diff --git a/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c b/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c index a27ddf01a2..b9cecd10bc 100644 --- a/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c +++ b/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c @@ -434,8 +434,8 @@ static const char *grammar_type_to_mime(grammar_type_t type, profile_t *profile) struct recognizer_data { /** the available grammars */ switch_hash_t *grammars; - /** the last grammar used (for pause/resume) */ - grammar_t *last_grammar; + /** the enabled grammars */ + switch_hash_t *enabled_grammars; /** recognize result */ char *result; /** true, if voice has started */ @@ -452,6 +452,9 @@ static switch_status_t recog_shutdown(); static switch_status_t recog_asr_open(switch_asr_handle_t *ah, const char *codec, int rate, const char *dest, switch_asr_flag_t *flags); static switch_status_t recog_asr_load_grammar(switch_asr_handle_t *ah, const char *grammar, const char *name); static switch_status_t recog_asr_unload_grammar(switch_asr_handle_t *ah, const char *name); +static switch_status_t recog_asr_enable_grammar(switch_asr_handle_t *ah, const char *name); +static switch_status_t recog_asr_disable_grammar(switch_asr_handle_t *ah, const char *name); +static switch_status_t recog_asr_disable_all_grammars(switch_asr_handle_t *ah); static switch_status_t recog_asr_close(switch_asr_handle_t *ah, switch_asr_flag_t *flags); static switch_status_t recog_asr_feed(switch_asr_handle_t *ah, void *data, unsigned int len, switch_asr_flag_t *flags); #if 0 @@ -472,9 +475,12 @@ static apt_bool_t recog_on_message_receive(mrcp_application_t *application, mrcp static apt_bool_t recog_stream_read(mpf_audio_stream_t *stream, mpf_frame_t *frame); /* recognizer specific speech_channel_funcs */ -static switch_status_t recog_channel_start(speech_channel_t *schannel, const char *name); +static switch_status_t recog_channel_start(speech_channel_t *schannel); static switch_status_t recog_channel_load_grammar(speech_channel_t *schannel, const char *name, grammar_type_t type, const char *data); static switch_status_t recog_channel_unload_grammar(speech_channel_t *schannel, const char *name); +static switch_status_t recog_channel_enable_grammar(speech_channel_t *schannel, const char *name); +static switch_status_t recog_channel_disable_grammar(speech_channel_t *schannel, const char *name); +static switch_status_t recog_channel_disable_all_grammars(speech_channel_t *schannel); static switch_status_t recog_channel_check_results(speech_channel_t *schannel); static switch_status_t recog_channel_set_start_of_input(speech_channel_t *schannel); static switch_status_t recog_channel_start_input_timers(speech_channel_t *schannel); @@ -2056,19 +2062,24 @@ static const char *grammar_type_to_mime(grammar_type_t type, profile_t *profile) * Start RECOGNIZE request * * @param schannel the channel to start - * @param name the name of the grammar to use or NULL if to reuse the last grammar * @return SWITCH_STATUS_SUCCESS if successful */ -static switch_status_t recog_channel_start(speech_channel_t *schannel, const char *name) +static switch_status_t recog_channel_start(speech_channel_t *schannel) { switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_hash_index_t *egk; mrcp_message_t *mrcp_message; mrcp_recog_header_t *recog_header; mrcp_generic_header_t *generic_header; recognizer_data_t *r; char *start_input_timers; const char *mime_type; - grammar_t *grammar = NULL; + char *key; + switch_size_t len; + grammar_t *grammar; + switch_size_t grammar_uri_count = 0; + switch_size_t grammar_uri_list_len = 0; + char *grammar_uri_list = NULL; switch_mutex_lock(schannel->mutex); if (schannel->state != SPEECH_CHANNEL_READY) { @@ -2089,21 +2100,55 @@ static switch_status_t recog_channel_start(speech_channel_t *schannel, const cha start_input_timers = (char *) switch_core_hash_find(schannel->params, "start-input-timers"); r->timers_started = zstr(start_input_timers) || strcasecmp(start_input_timers, "false"); - /* get the cached grammar */ - if (zstr(name)) { - grammar = r->last_grammar; - } else { - grammar = (grammar_t *) switch_core_hash_find(r->grammars, name); - r->last_grammar = grammar; - } - if (grammar == NULL) { - if (name) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "(%s) Undefined grammar, %s\n", schannel->name, name); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "(%s) No grammar specified\n", schannel->name); + /* count enabled grammars */ + for (egk = switch_hash_first(NULL, r->enabled_grammars); egk; egk = switch_hash_next(egk)) { + // NOTE: This postponed type check is necessary to allow a non-URI-list grammar to execute alone + if (grammar_uri_count == 1 && grammar->type != GRAMMAR_TYPE_URI) + goto no_grammar_alone; + ++grammar_uri_count; + switch_hash_this(egk, (void *) &key, NULL, (void *) &grammar); + if (grammar->type != GRAMMAR_TYPE_URI && grammar_uri_count != 1) { + no_grammar_alone: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "(%s) Grammar '%s' can only be used alone (not a URI list)\n", schannel->name, key); + status = SWITCH_STATUS_FALSE; + goto done; } + len = strlen(grammar->data); + if (!len) + continue; + grammar_uri_list_len += len; + if (grammar->data[len - 1] != '\n') + grammar_uri_list_len += 2; + } + + switch (grammar_uri_count) { + case 0: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "(%s) No grammar specified\n", schannel->name); status = SWITCH_STATUS_FALSE; goto done; + case 1: + /* grammar should already be the unique grammar */ + break; + default: + /* get the enabled grammars list */ + grammar_uri_list = switch_core_alloc(schannel->memory_pool, grammar_uri_list_len + 1); + grammar_uri_list_len = 0; + for (egk = switch_hash_first(NULL, r->enabled_grammars); egk; egk = switch_hash_next(egk)) { + switch_hash_this(egk, (void *) &key, NULL, (void *) &grammar); + len = strlen(grammar->data); + if (!len) + continue; + memcpy(&(grammar_uri_list[grammar_uri_list_len]), grammar->data, len); + grammar_uri_list_len += len; + if (grammar_uri_list[grammar_uri_list_len - 1] != '\n') + { + grammar_uri_list_len += 2; + grammar_uri_list[grammar_uri_list_len - 2] = '\r'; + grammar_uri_list[grammar_uri_list_len - 1] = '\n'; + } + } + grammar_uri_list[grammar_uri_list_len++] = '\0'; + grammar = NULL; } /* create MRCP message */ @@ -2121,7 +2166,7 @@ static switch_status_t recog_channel_start(speech_channel_t *schannel, const cha } /* set Content-Type */ - mime_type = grammar_type_to_mime(grammar->type, schannel->profile); + mime_type = grammar_type_to_mime(grammar ? grammar->type : GRAMMAR_TYPE_URI, schannel->profile); if (zstr(mime_type)) { status = SWITCH_STATUS_FALSE; goto done; @@ -2130,7 +2175,7 @@ static switch_status_t recog_channel_start(speech_channel_t *schannel, const cha mrcp_generic_header_property_add(mrcp_message, GENERIC_HEADER_CONTENT_TYPE); /* set Content-ID for inline grammars */ - if (grammar->type != GRAMMAR_TYPE_URI) { + if (grammar && grammar->type != GRAMMAR_TYPE_URI) { apt_string_assign(&generic_header->content_id, grammar->name, mrcp_message->pool); mrcp_generic_header_property_add(mrcp_message, GENERIC_HEADER_CONTENT_ID); } @@ -2152,7 +2197,7 @@ static switch_status_t recog_channel_start(speech_channel_t *schannel, const cha recog_channel_set_params(schannel, mrcp_message, generic_header, recog_header); /* set message body */ - apt_string_assign(&mrcp_message->body, grammar->data, mrcp_message->pool); + apt_string_assign(&mrcp_message->body, grammar ? grammar->data : grammar_uri_list, mrcp_message->pool); /* Empty audio queue and send RECOGNIZE to MRCP server */ audio_queue_clear(schannel->audio_queue); @@ -2287,12 +2332,84 @@ static switch_status_t recog_channel_unload_grammar(speech_channel_t *schannel, } else { recognizer_data_t *r = (recognizer_data_t *) schannel->data; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) Unloading grammar %s\n", schannel->name, grammar_name); + switch_core_hash_delete(r->enabled_grammars, grammar_name); switch_core_hash_delete(r->grammars, grammar_name); } return status; } +/** + * Enable speech recognition grammar + * + * @param schannel the recognizer channel + * @param grammar_name the name of the grammar to enable + * @return SWITCH_STATUS_SUCCESS if successful + */ +static switch_status_t recog_channel_enable_grammar(speech_channel_t *schannel, const char *grammar_name) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + + if (zstr(grammar_name)) { + status = SWITCH_STATUS_FALSE; + } else { + recognizer_data_t *r = (recognizer_data_t *) schannel->data; + grammar_t *grammar; + grammar = (grammar_t *) switch_core_hash_find(r->grammars, grammar_name); + if (grammar == NULL) + { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "(%s) Undefined grammar, %s\n", schannel->name, grammar_name); + status = SWITCH_STATUS_FALSE; + } + else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) Enabling grammar %s\n", schannel->name, grammar_name); + switch_core_hash_insert(r->enabled_grammars, grammar_name, grammar); + } + } + + return status; +} + +/** + * Disable speech recognition grammar + * + * @param schannel the recognizer channel + * @param grammar_name the name of the grammar to disable + * @return SWITCH_STATUS_SUCCESS if successful + */ +static switch_status_t recog_channel_disable_grammar(speech_channel_t *schannel, const char *grammar_name) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + + if (zstr(grammar_name)) { + status = SWITCH_STATUS_FALSE; + } else { + recognizer_data_t *r = (recognizer_data_t *) schannel->data; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) Disabling grammar %s\n", schannel->name, grammar_name); + switch_core_hash_delete(r->enabled_grammars, grammar_name); + } + + return status; +} + +/** + * Disable all speech recognition grammars + * + * @param schannel the recognizer channel + * @return SWITCH_STATUS_SUCCESS if successful + */ +static switch_status_t recog_channel_disable_all_grammars(speech_channel_t *schannel) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + + recognizer_data_t *r = (recognizer_data_t *) schannel->data; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) Disabling all grammars\n", schannel->name); + switch_core_hash_destroy(&r->enabled_grammars); + switch_core_hash_init(&r->enabled_grammars, schannel->memory_pool); + + return status; +} + /** * Check if recognition is complete * @@ -2740,6 +2857,7 @@ static switch_status_t recog_asr_open(switch_asr_handle_t *ah, const char *codec schannel->data = r; memset(r, 0, sizeof(recognizer_data_t)); switch_core_hash_init(&r->grammars, ah->memory_pool); + switch_core_hash_init(&r->enabled_grammars, ah->memory_pool); /* Open the channel */ if (zstr(profile_name)) { @@ -2892,7 +3010,17 @@ static switch_status_t recog_asr_load_grammar(switch_asr_handle_t *ah, const cha start_recognize = (char *) switch_core_hash_find(schannel->params, "start-recognize"); if (zstr(start_recognize) || strcasecmp(start_recognize, "false")) - status = recog_channel_start(schannel, name); + { + if (recog_channel_disable_all_grammars(schannel) != SWITCH_STATUS_SUCCESS) { + status = SWITCH_STATUS_FALSE; + goto done; + } + if (recog_channel_enable_grammar(schannel, name) != SWITCH_STATUS_SUCCESS) { + status = SWITCH_STATUS_FALSE; + goto done; + } + status = recog_channel_start(schannel); + } done: @@ -2920,6 +3048,57 @@ static switch_status_t recog_asr_unload_grammar(switch_asr_handle_t *ah, const c return status; } +/** + * Process asr_enable_grammar request from FreeSWITCH. + * + * FreeSWITCH sends this request to enable recognition on this grammar. + * @param ah the FreeSWITCH speech recognition handle + * @param name the grammar name. + */ +static switch_status_t recog_asr_enable_grammar(switch_asr_handle_t *ah, const char *name) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + speech_channel_t *schannel = (speech_channel_t *) ah->private_info; + if (zstr(name) || speech_channel_stop(schannel) != SWITCH_STATUS_SUCCESS || recog_channel_enable_grammar(schannel, name) != SWITCH_STATUS_SUCCESS) { + status = SWITCH_STATUS_FALSE; + } + return status; +} + +/** + * Process asr_disable_grammar request from FreeSWITCH. + * + * FreeSWITCH sends this request to disable recognition on this grammar. + * @param ah the FreeSWITCH speech recognition handle + * @param name the grammar name. + */ +static switch_status_t recog_asr_disable_grammar(switch_asr_handle_t *ah, const char *name) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + speech_channel_t *schannel = (speech_channel_t *) ah->private_info; + if (zstr(name) || speech_channel_stop(schannel) != SWITCH_STATUS_SUCCESS || recog_channel_disable_grammar(schannel, name) != SWITCH_STATUS_SUCCESS) { + status = SWITCH_STATUS_FALSE; + } + return status; +} + +/** + * Process asr_disable_all_grammars request from FreeSWITCH. + * + * FreeSWITCH sends this request to disable recognition of all grammars. + * @param ah the FreeSWITCH speech recognition handle + * @param name the grammar name. + */ +static switch_status_t recog_asr_disable_all_grammars(switch_asr_handle_t *ah) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + speech_channel_t *schannel = (speech_channel_t *) ah->private_info; + if (speech_channel_stop(schannel) != SWITCH_STATUS_SUCCESS || recog_channel_disable_all_grammars(schannel) != SWITCH_STATUS_SUCCESS) { + status = SWITCH_STATUS_FALSE; + } + return status; +} + /** * Process asr_close request from FreeSWITCH * @@ -2934,6 +3113,7 @@ static switch_status_t recog_asr_close(switch_asr_handle_t *ah, switch_asr_flag_ speech_channel_stop(schannel); speech_channel_destroy(schannel); switch_core_hash_destroy(&r->grammars); + switch_core_hash_destroy(&r->enabled_grammars); /* this lets FreeSWITCH's speech_thread know the handle is closed */ switch_set_flag(ah, SWITCH_ASR_FLAG_CLOSED); @@ -2958,14 +3138,13 @@ static switch_status_t recog_asr_feed(switch_asr_handle_t *ah, void *data, unsig /** * Process asr_start request from FreeSWITCH * @param ah the FreeSWITCH speech recognition handle - * @param name name of the grammar to use * @return SWITCH_STATUS_SUCCESS if successful */ -static switch_status_t recog_asr_start(switch_asr_handle_t *ah, const char *name) +static switch_status_t recog_asr_start(switch_asr_handle_t *ah) { switch_status_t status; speech_channel_t *schannel = (speech_channel_t *) ah->private_info; - status = recog_channel_start(schannel, name); + status = recog_channel_start(schannel); return status; } #endif @@ -2978,7 +3157,7 @@ static switch_status_t recog_asr_start(switch_asr_handle_t *ah, const char *name static switch_status_t recog_asr_resume(switch_asr_handle_t *ah) { speech_channel_t *schannel = (speech_channel_t *) ah->private_info; - return recog_channel_start(schannel, NULL); + return recog_channel_start(schannel); } /** @@ -3237,6 +3416,9 @@ static switch_status_t recog_load(switch_loadable_module_interface_t *module_int asr_interface->asr_open = recog_asr_open; asr_interface->asr_load_grammar = recog_asr_load_grammar; asr_interface->asr_unload_grammar = recog_asr_unload_grammar; + asr_interface->asr_enable_grammar = recog_asr_enable_grammar; + asr_interface->asr_disable_grammar = recog_asr_disable_grammar; + asr_interface->asr_disable_all_grammars = recog_asr_disable_all_grammars; asr_interface->asr_close = recog_asr_close; asr_interface->asr_feed = recog_asr_feed; #if 0