From acd56d2fe2a0458801010cc6b03c524023cbc597 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sun, 22 Aug 2010 18:00:40 -0500 Subject: [PATCH 1/9] expose ASR start_input_timers on the IVR abstraction level --- src/include/switch_ivr.h | 8 ++++++++ src/switch_ivr_async.c | 13 +++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 40ba1dc16f..692ad8d61b 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -26,6 +26,7 @@ * Anthony Minessale II * Neal Horman * Bret McDanel + * Luke Dashjr (OpenMethods, LLC) * * switch_ivr.h -- IVR Library * @@ -200,6 +201,13 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_unload_grammar(switch_c SWITCH_DECLARE(switch_status_t) switch_ivr_set_param_detect_speech(switch_core_session_t *session, const char *name, const char *val); +/*! + \brief Start input timers on a background speech detection handle + \param session The session to start the timers on + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_start_input_timers(switch_core_session_t *session); + /*! \brief Record a session to disk \param session the session to record diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index 4fe5732f82..ae23d0a9f4 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -26,6 +26,7 @@ * Anthony Minessale II * Michael Jerris * Bret McDanel + * Luke Dashjr (OpenMethods, LLC) * * switch_ivr_async.c -- IVR Library (async operations) * @@ -2728,6 +2729,18 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_set_param_detect_speech(switch_core_s return status; } +SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_start_input_timers(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY); + + if (sth) { + switch_core_asr_start_input_timers(sth->ah); + return SWITCH_STATUS_SUCCESS; + } + return SWITCH_STATUS_FALSE; +} + SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_unload_grammar(switch_core_session_t *session, const char *name) { switch_channel_t *channel = switch_core_session_get_channel(session); From d395b654fb0abab4889d5b8c239728683e772959 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sun, 22 Aug 2010 18:07:24 -0500 Subject: [PATCH 2/9] expose ASR start_input_timers to dialplan via mod_dptools --- src/mod/applications/mod_dptools/mod_dptools.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index 5a1b8c5d14..2f496300e8 100755 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -28,6 +28,7 @@ * Michael Murdock * Neal Horman * Bret McDanel + * Luke Dashjr (OpenMethods, LLC) * * mod_dptools.c -- Raw Audio File Streaming Application Module * @@ -95,7 +96,7 @@ SWITCH_STANDARD_DIALPLAN(inline_dialplan_hunt) return extension; } -#define DETECT_SPEECH_SYNTAX " [] OR grammar [] OR pause OR resume" +#define DETECT_SPEECH_SYNTAX " [] OR grammar [] OR pause OR resume OR start_input_timers" SWITCH_STANDARD_APP(detect_speech_function) { char *argv[4]; @@ -116,6 +117,8 @@ SWITCH_STANDARD_APP(detect_speech_function) switch_ivr_stop_detect_speech(session); } else if (!strcasecmp(argv[0], "param")) { switch_ivr_set_param_detect_speech(session, argv[1], argv[2]); + } else if (!strcasecmp(argv[0], "start_input_timers")) { + switch_ivr_detect_speech_start_input_timers(session); } else if (argc >= 3) { switch_ivr_detect_speech(session, argv[0], argv[1], argv[2], argv[3], NULL); } From 439df43cae04a053a1c22befdd7f1009f6cb6dfa Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sun, 22 Aug 2010 18:17:36 -0500 Subject: [PATCH 3/9] document all of detect_speech's valid syntax --- src/mod/applications/mod_dptools/mod_dptools.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index 5a1b8c5d14..6d014faf41 100755 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -28,6 +28,7 @@ * Michael Murdock * Neal Horman * Bret McDanel + * Luke Dashjr (OpenMethods, LLC) * * mod_dptools.c -- Raw Audio File Streaming Application Module * @@ -95,7 +96,7 @@ SWITCH_STANDARD_DIALPLAN(inline_dialplan_hunt) return extension; } -#define DETECT_SPEECH_SYNTAX " [] OR grammar [] OR pause OR resume" +#define DETECT_SPEECH_SYNTAX " [] OR grammar [] OR nogrammar OR pause OR resume OR stop OR param " SWITCH_STANDARD_APP(detect_speech_function) { char *argv[4]; From e719ae662b7cb08ebd7f8e732a140e9aeb502960 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sat, 21 Aug 2010 17:44:42 -0500 Subject: [PATCH 4/9] Allow loading grammars without sending RECOGNIZE with start-recognize=false parameter --- src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c b/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c index 41c0ccdaa4..a27ddf01a2 100644 --- a/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c +++ b/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c @@ -26,6 +26,7 @@ * * Brian West * Christopher M. Rienzo + * Luke Dashjr (OpenMethods, LLC) * * mod_unimrcp.c -- UniMRCP module (MRCP client) * @@ -2451,6 +2452,8 @@ static switch_status_t recog_channel_set_params(speech_channel_t *schannel, mrcp if (id) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) \"%s\": \"%s\"\n", schannel->name, param_name, param_val); recog_channel_set_header(schannel, id->id, param_val, msg, recog_hdr); + } else if (!strcasecmp(param_name, "start-recognize")) { + // This parameter is used internally only, not in MRCP headers } else { /* this is probably a vendor-specific MRCP param */ apt_str_t apt_param_name = { 0 }; @@ -2782,6 +2785,7 @@ static switch_status_t recog_asr_load_grammar(switch_asr_handle_t *ah, const cha speech_channel_t *schannel = (speech_channel_t *) ah->private_info; const char *grammar_data = NULL; char *grammar_file_data = NULL; + char *start_recognize; switch_file_t *grammar_file = NULL; switch_size_t grammar_file_size = 0, to_read = 0; grammar_type_t type = GRAMMAR_TYPE_UNKNOWN; @@ -2886,7 +2890,9 @@ static switch_status_t recog_asr_load_grammar(switch_asr_handle_t *ah, const cha goto done; } - status = recog_channel_start(schannel, name); + 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); done: From 4cbdfbe481d7f9b448663418dccc88b017f9ce69 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sat, 21 Aug 2010 19:43:53 -0500 Subject: [PATCH 5/9] switch_core_asr interfaces for enable_grammar, disable_grammar, and disable_all_grammars --- src/include/switch_core.h | 24 ++++++++++++++++ src/include/switch_module_interfaces.h | 7 +++++ src/switch_core_asr.c | 40 ++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/src/include/switch_core.h b/src/include/switch_core.h index 650ffc15fb..26a048cdb2 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -24,6 +24,7 @@ * Contributor(s): * * Anthony Minessale II + * Luke Dashjr (OpenMethods, LLC) * * * switch_core.h -- Core Library @@ -1747,6 +1748,29 @@ SWITCH_DECLARE(switch_status_t) switch_core_asr_load_grammar(switch_asr_handle_t */ SWITCH_DECLARE(switch_status_t) switch_core_asr_unload_grammar(switch_asr_handle_t *ah, const char *name); +/*! + \brief Enable a grammar from an asr handle + \param ah the handle to enable the grammar from + \param name the name of the grammar to enable + \return SWITCH_STATUS_SUCCESS +*/ +SWITCH_DECLARE(switch_status_t) switch_core_asr_enable_grammar(switch_asr_handle_t *ah, const char *name); + +/*! + \brief Disable a grammar from an asr handle + \param ah the handle to disable the grammar from + \param name the name of the grammar to disable + \return SWITCH_STATUS_SUCCESS +*/ +SWITCH_DECLARE(switch_status_t) switch_core_asr_disable_grammar(switch_asr_handle_t *ah, const char *name); + +/*! + \brief Disable all grammars from an asr handle + \param ah the handle to disable the grammars from + \return SWITCH_STATUS_SUCCESS +*/ +SWITCH_DECLARE(switch_status_t) switch_core_asr_disable_all_grammars(switch_asr_handle_t *ah); + /*! \brief Pause detection on an asr handle \param ah the handle to pause diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h index 078b83d2fd..e0119b4d15 100644 --- a/src/include/switch_module_interfaces.h +++ b/src/include/switch_module_interfaces.h @@ -24,6 +24,7 @@ * Contributor(s): * * Anthony Minessale II + * Luke Dashjr (OpenMethods, LLC) * * * switch_module_interfaces.h -- Module Interface Definitions @@ -393,6 +394,12 @@ struct switch_asr_interface { switch_mutex_t *reflock; switch_loadable_module_interface_t *parent; struct switch_asr_interface *next; + /*! function to enable a grammar to the asr interface */ + switch_status_t (*asr_enable_grammar) (switch_asr_handle_t *ah, const char *name); + /*! function to disable a grammar to the asr interface */ + switch_status_t (*asr_disable_grammar) (switch_asr_handle_t *ah, const char *name); + /*! function to disable all grammars to the asr interface */ + switch_status_t (*asr_disable_all_grammars) (switch_asr_handle_t *ah); }; /*! an abstract representation of an asr speech interface. */ diff --git a/src/switch_core_asr.c b/src/switch_core_asr.c index fa4446ec46..691011a285 100644 --- a/src/switch_core_asr.c +++ b/src/switch_core_asr.c @@ -27,6 +27,7 @@ * Michael Jerris * Paul D. Tinsley * Christopher M. Rienzo + * Luke Dashjr (OpenMethods, LLC) * * * switch_core_asr.c -- Main Core Library (Speech Detection Interface) @@ -160,6 +161,45 @@ SWITCH_DECLARE(switch_status_t) switch_core_asr_unload_grammar(switch_asr_handle return status; } +SWITCH_DECLARE(switch_status_t) switch_core_asr_enable_grammar(switch_asr_handle_t *ah, const char *name) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + + switch_assert(ah != NULL); + + if (ah->asr_interface->asr_enable_grammar) { + status = ah->asr_interface->asr_enable_grammar(ah, name); + } + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_asr_disable_grammar(switch_asr_handle_t *ah, const char *name) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + + switch_assert(ah != NULL); + + if (ah->asr_interface->asr_disable_grammar) { + status = ah->asr_interface->asr_disable_grammar(ah, name); + } + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_asr_disable_all_grammars(switch_asr_handle_t *ah) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + + switch_assert(ah != NULL); + + if (ah->asr_interface->asr_disable_all_grammars) { + status = ah->asr_interface->asr_disable_all_grammars(ah); + } + + return status; +} + SWITCH_DECLARE(switch_status_t) switch_core_asr_pause(switch_asr_handle_t *ah) { switch_assert(ah != NULL); From 128d53c2e6a73a6136efcfffa4e880e6635e43f1 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sat, 21 Aug 2010 19:46:35 -0500 Subject: [PATCH 6/9] Implement UniMRCP asr_enable_grammar, asr_disable_grammar, and asr_disable_all_grammars which allow for multiple grammar recognition --- src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c | 234 +++++++++++++++++++--- 1 file changed, 208 insertions(+), 26 deletions(-) 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 From 6d7e019b5c0244f53c16361d2667049b430adb55 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sun, 22 Aug 2010 00:26:21 -0500 Subject: [PATCH 7/9] switch_ivr interfaces to enable/disable grammar: switch_ivr_detect_speech_enable_grammar, switch_ivr_detect_speech_disable_grammar, and switch_ivr_detect_speech_disable_all_grammars --- src/include/switch_ivr.h | 24 +++++++++++++++++++ src/switch_ivr_async.c | 52 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 40ba1dc16f..8641766760 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -26,6 +26,7 @@ * Anthony Minessale II * Neal Horman * Bret McDanel + * Luke Dashjr (OpenMethods, LLC) * * switch_ivr.h -- IVR Library * @@ -198,6 +199,29 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_load_grammar(switch_cor */ SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_unload_grammar(switch_core_session_t *session, const char *name); +/*! + \brief Enable a grammar on a background speech detection handle + \param session The session to change the grammar on + \param name the grammar name + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_enable_grammar(switch_core_session_t *session, const char *name); + +/*! + \brief Disable a grammar on a background speech detection handle + \param session The session to change the grammar on + \param name the grammar name + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_disable_grammar(switch_core_session_t *session, const char *name); + +/*! + \brief Disable all grammars on a background speech detection handle + \param session The session to change the grammar on + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_disable_all_grammars(switch_core_session_t *session); + SWITCH_DECLARE(switch_status_t) switch_ivr_set_param_detect_speech(switch_core_session_t *session, const char *name, const char *val); /*! diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index 4fe5732f82..672750a4c3 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -26,6 +26,7 @@ * Anthony Minessale II * Michael Jerris * Bret McDanel + * Luke Dashjr (OpenMethods, LLC) * * switch_ivr_async.c -- IVR Library (async operations) * @@ -2745,6 +2746,57 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_unload_grammar(switch_c return SWITCH_STATUS_FALSE; } +SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_enable_grammar(switch_core_session_t *session, const char *name) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; + struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY); + switch_status_t status; + + if (sth) { + if ((status = switch_core_asr_enable_grammar(sth->ah, name)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Error enabling Grammar\n"); + switch_core_asr_close(sth->ah, &flags); + } + return status; + } + return SWITCH_STATUS_FALSE; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_disable_grammar(switch_core_session_t *session, const char *name) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; + struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY); + switch_status_t status; + + if (sth) { + if ((status = switch_core_asr_disable_grammar(sth->ah, name)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Error disabling Grammar\n"); + switch_core_asr_close(sth->ah, &flags); + } + return status; + } + return SWITCH_STATUS_FALSE; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_disable_all_grammars(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; + struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY); + switch_status_t status; + + if (sth) { + if ((status = switch_core_asr_disable_all_grammars(sth->ah)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Error disabling all Grammars\n"); + switch_core_asr_close(sth->ah, &flags); + } + return status; + } + return SWITCH_STATUS_FALSE; +} + SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech(switch_core_session_t *session, const char *mod_name, const char *grammar, const char *name, const char *dest, switch_asr_handle_t *ah) From 0f39c8f9c3d8e8bce05c551e0168e476ce29437a Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sun, 22 Aug 2010 18:12:18 -0500 Subject: [PATCH 8/9] expose ASR enable/disable grammar (and disable all grammars) to dialplan via mod_dptools --- src/mod/applications/mod_dptools/mod_dptools.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index 5a1b8c5d14..e942bc612d 100755 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -28,6 +28,7 @@ * Michael Murdock * Neal Horman * Bret McDanel + * Luke Dashjr (OpenMethods, LLC) * * mod_dptools.c -- Raw Audio File Streaming Application Module * @@ -95,7 +96,7 @@ SWITCH_STANDARD_DIALPLAN(inline_dialplan_hunt) return extension; } -#define DETECT_SPEECH_SYNTAX " [] OR grammar [] OR pause OR resume" +#define DETECT_SPEECH_SYNTAX " [] OR grammar [] OR pause OR resume OR grammaron/grammaroff OR grammarsalloff" SWITCH_STANDARD_APP(detect_speech_function) { char *argv[4]; @@ -108,6 +109,12 @@ SWITCH_STANDARD_APP(detect_speech_function) switch_ivr_detect_speech_load_grammar(session, argv[1], argv[2]); } else if (!strcasecmp(argv[0], "nogrammar")) { switch_ivr_detect_speech_unload_grammar(session, argv[1]); + } else if (!strcasecmp(argv[0], "grammaron")) { + switch_ivr_detect_speech_enable_grammar(session, argv[1]); + } else if (!strcasecmp(argv[0], "grammaroff")) { + switch_ivr_detect_speech_disable_grammar(session, argv[1]); + } else if (!strcasecmp(argv[0], "grammarsalloff")) { + switch_ivr_detect_speech_disable_all_grammars(session); } else if (!strcasecmp(argv[0], "pause")) { switch_ivr_pause_detect_speech(session); } else if (!strcasecmp(argv[0], "resume")) { From 15e65cfafb8a46caca8bb8a63d55edf817f17f2d Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Wed, 26 Jan 2011 14:40:35 -0600 Subject: [PATCH 9/9] MERGE: DTMF recognition via ASR modules (implemented in UniMRCP) --- src/include/switch_core.h | 9 +++ src/include/switch_module_interfaces.h | 2 + src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c | 73 ++++++++++++++++++++++- src/switch_core_asr.c | 13 ++++ src/switch_ivr_async.c | 19 ++++++ 5 files changed, 115 insertions(+), 1 deletion(-) diff --git a/src/include/switch_core.h b/src/include/switch_core.h index 26a048cdb2..a3d74e97b3 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -1714,6 +1714,15 @@ SWITCH_DECLARE(switch_status_t) switch_core_asr_close(switch_asr_handle_t *ah, s */ SWITCH_DECLARE(switch_status_t) switch_core_asr_feed(switch_asr_handle_t *ah, void *data, unsigned int len, switch_asr_flag_t *flags); +/*! + \brief Feed DTMF to an asr handle + \param ah the handle to feed data to + \param dtmf a string of DTMF digits + \param flags flags to influence behaviour + \return SWITCH_STATUS_SUCCESS +*/ +SWITCH_DECLARE(switch_status_t) switch_core_asr_feed_dtmf(switch_asr_handle_t *ah, const switch_dtmf_t *dtmf, switch_asr_flag_t *flags); + /*! \brief Check an asr handle for results \param ah the handle to check diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h index e0119b4d15..8303af75de 100644 --- a/src/include/switch_module_interfaces.h +++ b/src/include/switch_module_interfaces.h @@ -400,6 +400,8 @@ struct switch_asr_interface { switch_status_t (*asr_disable_grammar) (switch_asr_handle_t *ah, const char *name); /*! function to disable all grammars to the asr interface */ switch_status_t (*asr_disable_all_grammars) (switch_asr_handle_t *ah); + /*! function to feed DTMF to the ASR */ + switch_status_t (*asr_feed_dtmf) (switch_asr_handle_t *ah, const switch_dtmf_t *dtmf, switch_asr_flag_t *flags); }; /*! an abstract representation of an asr speech interface. */ diff --git a/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c b/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c index b9cecd10bc..b3679cf516 100644 --- a/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c +++ b/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c @@ -49,6 +49,7 @@ #include "mrcp_resource_loader.h" #include "mpf_engine.h" #include "mpf_codec_manager.h" +#include "mpf_dtmf_generator.h" #include "mpf_rtp_termination_factory.h" #include "mrcp_sofiasip_client_agent.h" #include "mrcp_unirtsp_client_agent.h" @@ -442,6 +443,12 @@ struct recognizer_data { int start_of_input; /** true, if input timers have started */ int timers_started; + /** UniMRCP mpf stream */ + mpf_audio_stream_t *unimrcp_stream; + /** DTMF generator */ + mpf_dtmf_generator_t *dtmf_generator; + /** true, if presently transmitting DTMF */ + char dtmf_generator_active; }; typedef struct recognizer_data recognizer_data_t; @@ -457,6 +464,7 @@ static switch_status_t recog_asr_disable_grammar(switch_asr_handle_t *ah, const 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); +static switch_status_t recog_asr_feed_dtmf(switch_asr_handle_t *ah, const switch_dtmf_t *dtmf, switch_asr_flag_t *flags); #if 0 static switch_status_t recog_asr_start(switch_asr_handle_t *ah, const char *name); #endif @@ -472,6 +480,7 @@ static void recog_asr_float_param(switch_asr_handle_t *ah, char *param, double v /* recognizer's interface for UniMRCP */ static apt_bool_t recog_message_handler(const mrcp_app_message_t *app_message); static apt_bool_t recog_on_message_receive(mrcp_application_t *application, mrcp_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message); +static apt_bool_t recog_stream_open(mpf_audio_stream_t *stream, mpf_codec_t *codec); static apt_bool_t recog_stream_read(mpf_audio_stream_t *stream, mpf_frame_t *frame); /* recognizer specific speech_channel_funcs */ @@ -3114,6 +3123,9 @@ static switch_status_t recog_asr_close(switch_asr_handle_t *ah, switch_asr_flag_ speech_channel_destroy(schannel); switch_core_hash_destroy(&r->grammars); switch_core_hash_destroy(&r->enabled_grammars); + if (r->dtmf_generator) { + mpf_dtmf_generator_destroy(r->dtmf_generator); + } /* this lets FreeSWITCH's speech_thread know the handle is closed */ switch_set_flag(ah, SWITCH_ASR_FLAG_CLOSED); @@ -3134,6 +3146,39 @@ static switch_status_t recog_asr_feed(switch_asr_handle_t *ah, void *data, unsig return speech_channel_write(schannel, data, &slen); } +/** + * Process asr_feed_dtmf request from FreeSWITCH + * + * @param ah the FreeSWITCH speech recognition handle + * @return SWITCH_STATUS_SUCCESS if successful + */ +static switch_status_t recog_asr_feed_dtmf(switch_asr_handle_t *ah, const switch_dtmf_t *dtmf, switch_asr_flag_t *flags) +{ + speech_channel_t *schannel = (speech_channel_t *) ah->private_info; + recognizer_data_t *r = (recognizer_data_t *) schannel->data; + char digits[2]; + + if (!r->dtmf_generator) { + if (!r->unimrcp_stream) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "(%s) Cannot queue DTMF: No UniMRCP stream object open\n", schannel->name); + return SWITCH_STATUS_FALSE; + } + r->dtmf_generator = mpf_dtmf_generator_create(r->unimrcp_stream, schannel->unimrcp_session->pool); + if (!r->dtmf_generator) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "(%s) Cannot queue DTMF: Failed to create DTMF generator\n", schannel->name); + return SWITCH_STATUS_FALSE; + } + } + + digits[0] = dtmf->digit; + digits[1] = '\0'; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) Queued DTMF: %s\n", schannel->name, digits); + mpf_dtmf_generator_enqueue(r->dtmf_generator, digits); + r->dtmf_generator_active = 1; + + return SWITCH_STATUS_SUCCESS; +} + #if 0 /** * Process asr_start request from FreeSWITCH @@ -3379,6 +3424,23 @@ static apt_bool_t recog_on_message_receive(mrcp_application_t *application, mrcp return TRUE; } +/** + * UniMRCP callback requesting open for speech recognition + * + * @param stream the UniMRCP stream + * @param codec the codec + * @return TRUE + */ +static apt_bool_t recog_stream_open(mpf_audio_stream_t *stream, mpf_codec_t *codec) +{ + speech_channel_t *schannel = (speech_channel_t *) stream->obj; + recognizer_data_t *r = (recognizer_data_t *) schannel->data; + + r->unimrcp_stream = stream; + + return TRUE; +} + /** * UniMRCP callback requesting next frame for speech recognition * @@ -3389,6 +3451,7 @@ 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) { speech_channel_t *schannel = (speech_channel_t *) stream->obj; + recognizer_data_t *r = (recognizer_data_t *) schannel->data; switch_size_t to_read = frame->codec_frame.size; /* grab the data. pad it if there isn't enough */ @@ -3398,6 +3461,13 @@ static apt_bool_t recog_stream_read(mpf_audio_stream_t *stream, mpf_frame_t *fra } frame->type |= MEDIA_FRAME_TYPE_AUDIO; } + + if (r->dtmf_generator_active) { + if (!mpf_dtmf_generator_put_frame(r->dtmf_generator, frame)) { + if (!mpf_dtmf_generator_sending(r->dtmf_generator)) + r->dtmf_generator_active = 0; + } + } return TRUE; } @@ -3421,6 +3491,7 @@ static switch_status_t recog_load(switch_loadable_module_interface_t *module_int 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; + asr_interface->asr_feed_dtmf = recog_asr_feed_dtmf; #if 0 asr_interface->asr_start = recog_asr_start; #endif @@ -3443,7 +3514,7 @@ static switch_status_t recog_load(switch_loadable_module_interface_t *module_int globals.recog.dispatcher.on_channel_remove = speech_on_channel_remove; globals.recog.dispatcher.on_message_receive = recog_on_message_receive; globals.recog.audio_stream_vtable.destroy = NULL; - globals.recog.audio_stream_vtable.open_rx = NULL; + globals.recog.audio_stream_vtable.open_rx = recog_stream_open; globals.recog.audio_stream_vtable.close_rx = NULL; globals.recog.audio_stream_vtable.read_frame = recog_stream_read; globals.recog.audio_stream_vtable.open_tx = NULL; diff --git a/src/switch_core_asr.c b/src/switch_core_asr.c index 691011a285..7f231ef651 100644 --- a/src/switch_core_asr.c +++ b/src/switch_core_asr.c @@ -239,6 +239,19 @@ SWITCH_DECLARE(switch_status_t) switch_core_asr_feed(switch_asr_handle_t *ah, vo return ah->asr_interface->asr_feed(ah, data, len, flags); } +SWITCH_DECLARE(switch_status_t) switch_core_asr_feed_dtmf(switch_asr_handle_t *ah, const switch_dtmf_t *dtmf, switch_asr_flag_t *flags) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + + switch_assert(ah != NULL); + + if (ah->asr_interface->asr_feed_dtmf) { + status = ah->asr_interface->asr_feed_dtmf(ah, dtmf, flags); + } + + return status; +} + SWITCH_DECLARE(switch_status_t) switch_core_asr_check_results(switch_asr_handle_t *ah, switch_asr_flag_t *flags) { switch_assert(ah != NULL); diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index 66cd36963b..5df7af3bff 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -2660,6 +2660,20 @@ static switch_bool_t speech_callback(switch_media_bug_t *bug, void *user_data, s return SWITCH_TRUE; } +static switch_status_t speech_on_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf, switch_dtmf_direction_t direction) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY); + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; + + if (switch_core_asr_feed_dtmf(sth->ah, dtmf, &flags) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error Feeding DTMF\n"); + } + + return status; +} + SWITCH_DECLARE(switch_status_t) switch_ivr_stop_detect_speech(switch_core_session_t *session) { switch_channel_t *channel = switch_core_session_get_channel(session); @@ -2875,6 +2889,11 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech(switch_core_session_t * return status; } + if ((status = switch_core_event_hook_add_recv_dtmf(session, speech_on_dtmf)) != SWITCH_STATUS_SUCCESS) { + switch_ivr_stop_detect_speech(session); + return status; + } + switch_channel_set_private(channel, SWITCH_SPEECH_KEY, sth); return SWITCH_STATUS_SUCCESS;