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;