From c796723dd7495dd8e11cca7d9f52035a4d438771 Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Mon, 25 Mar 2019 20:12:55 +0000 Subject: [PATCH] FS-11728 [core] add switch_ivr_play_and_collect_input(). This function plays a prompt while looking for DTMF or speech and returns whatever was heard. --- src/include/switch_ivr.h | 15 +- src/switch_ivr_play_say.c | 323 ++++++++++++++++++++++++++++++- tests/unit/Makefile.am | 3 +- tests/unit/switch_ivr_play_say.c | 266 +++++++++++++++++++++++++ 4 files changed, 604 insertions(+), 3 deletions(-) create mode 100644 tests/unit/switch_ivr_play_say.c diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 69cb038caf..061da0705b 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -1,6 +1,6 @@ /* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application - * Copyright (C) 2005-2014, Anthony Minessale II + * Copyright (C) 2005-2019, Anthony Minessale II * * Version: MPL 1.1 * @@ -1051,6 +1051,19 @@ SWITCH_DECLARE(switch_event_t *) switch_dial_leg_get_vars(switch_dial_leg_t *leg SWITCH_DECLARE(int) switch_dial_handle_get_total(switch_dial_handle_t *handle); SWITCH_DECLARE(void) switch_ivr_orig_and_bridge(switch_core_session_t *session, const char *data, switch_dial_handle_t *dh); +SWITCH_DECLARE(switch_status_t) switch_ivr_play_and_collect_input(switch_core_session_t *session, + const char *prompt, + const char *recognizer_mod_name, + const char *recognizer_grammar, + int min_digits, + int max_digits, + const char *terminators, + uint32_t digit_timeout, + cJSON **recognition_result, + char **digits_collected, + char *terminator_collected, + switch_input_args_t *args); + /** @} */ SWITCH_END_EXTERN_C diff --git a/src/switch_ivr_play_say.c b/src/switch_ivr_play_say.c index 8063d5cb10..2278c35e48 100644 --- a/src/switch_ivr_play_say.c +++ b/src/switch_ivr_play_say.c @@ -1,6 +1,6 @@ /* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application - * Copyright (C) 2005-2014, Anthony Minessale II + * Copyright (C) 2005-2019, Anthony Minessale II * * Version: MPL 1.1 * @@ -3171,6 +3171,327 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_soft_hold(switch_core_session_t *sess } + + +typedef enum { + /** playing initial prompt - allow barge in */ + SWITCH_COLLECT_INPUT_PROMPT = (1 << 0), + /** looking for speech input */ + SWITCH_COLLECT_INPUT_SPEECH = (1 << 1), + /** finished looking for speech input */ + SWITCH_COLLECT_INPUT_SPEECH_DONE = (1 << 2), + /** looking for digits */ + SWITCH_COLLECT_INPUT_DIGITS = (1 << 3), + /** finished looking for digits */ + SWITCH_COLLECT_INPUT_DIGITS_DONE = (1 << 4) +} switch_collect_input_flags_t; + +typedef struct { + int flags; + cJSON *recognition_result; + char *digits; + int min_digits; + int max_digits; + const char *terminators; + char terminator; + switch_time_t last_digit_time; +} switch_collect_input_state_t; + +static switch_status_t switch_collect_input_callback(switch_core_session_t *session, void *input, switch_input_type_t input_type, void *data, unsigned int len) +{ + switch_collect_input_state_t *state = (switch_collect_input_state_t *)data; + switch_channel_t *channel = switch_core_session_get_channel(session); + + if (switch_test_flag(state, SWITCH_COLLECT_INPUT_SPEECH) && input_type == SWITCH_INPUT_TYPE_EVENT) { + const char *speech_type = NULL; + switch_event_t *event = (switch_event_t *)input; + + if (event->event_id != SWITCH_EVENT_DETECTED_SPEECH) return SWITCH_STATUS_SUCCESS; + + speech_type = switch_event_get_header(event, "Speech-Type"); + + if (zstr(speech_type)) return SWITCH_STATUS_SUCCESS; + + if (!strcasecmp(speech_type, "detected-speech")) { + const char *result = switch_event_get_body(event); + + /* stop waiting for speech */ + switch_set_flag(state, SWITCH_COLLECT_INPUT_SPEECH_DONE); + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "(%s) DETECTED SPEECH\n", switch_channel_get_name(channel)); + + if (!zstr(result)) { + state->recognition_result = cJSON_Parse(result); + + if (state->recognition_result) { + const char *text = cJSON_GetObjectCstr(state->recognition_result, "text"); + + if (!zstr(text)) { + /* stop waiting for digits */ + switch_set_flag(state, SWITCH_COLLECT_INPUT_DIGITS_DONE); + } + } + } + return SWITCH_STATUS_BREAK; + } + + if (!strcasecmp("closed", speech_type)) { + /* stop waiting for speech */ + switch_set_flag(state, SWITCH_COLLECT_INPUT_SPEECH_DONE); + return SWITCH_STATUS_BREAK; + } + + if (!strcasecmp(speech_type, "begin-speaking")) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "(%s) START OF SPEECH\n", switch_channel_get_name(channel)); + + if (switch_test_flag(state, SWITCH_COLLECT_INPUT_PROMPT)) { + /* barge in on prompt */ + return SWITCH_STATUS_BREAK; + } + } + + } + + if (switch_test_flag(state, SWITCH_COLLECT_INPUT_DIGITS) && input_type == SWITCH_INPUT_TYPE_DTMF) { + switch_dtmf_t *dtmf = (switch_dtmf_t *) input; + state->last_digit_time = switch_micro_time_now(); + + if (!zstr(state->terminators) && strchr(state->terminators, dtmf->digit)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "(%s) ACCEPT TERMINATOR %c\n", + switch_channel_get_name(channel), dtmf->digit); + + state->terminator = dtmf->digit; + + /* stop waiting for digits */ + switch_set_flag(state, SWITCH_COLLECT_INPUT_DIGITS_DONE); + + if (switch_test_flag(state, SWITCH_COLLECT_INPUT_DIGITS) && !zstr(state->digits)) { + /* stop waiting for speech */ + switch_set_flag(state, SWITCH_COLLECT_INPUT_SPEECH_DONE); + } + + /* barge-in and break playback on terminator */ + return SWITCH_STATUS_BREAK; + } + + if (!switch_test_flag(state, SWITCH_COLLECT_INPUT_DIGITS_DONE)) { + int digits_collected = strlen(state->digits); + + if (digits_collected < state->max_digits) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "(%s) ACCEPT DIGIT %c\n", + switch_channel_get_name(channel), dtmf->digit); + state->digits[digits_collected] = dtmf->digit; + } + + if (digits_collected + 1 >= state->max_digits) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "(%s) MAX DIGITS COLLECTED\n", switch_channel_get_name(channel)); + switch_set_flag(state, SWITCH_COLLECT_INPUT_DIGITS_DONE); // stop waiting for digits + switch_set_flag(state, SWITCH_COLLECT_INPUT_SPEECH_DONE); // stop waiting for speech, too + } + } + return SWITCH_STATUS_BREAK; // got a digit- break for inter-digit timeout / checking results / barge-in + } + + return SWITCH_STATUS_SUCCESS; +} + + + +/*!\brief Play prompt and collect input +* +* Returns collect status +* +* \param[in] session the current session +* +* \return Returns status +* SWITCH_STATUS_SUCCESS when success +* SWITCH_STATUS_FALSE when error +* SWITCH_STATUS_GENERR when error +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_play_and_collect_input(switch_core_session_t *session, + const char *prompt, + const char *recognizer_mod_name, + const char *recognizer_grammar, + int min_digits, + int max_digits, + const char *terminators, + uint32_t digit_timeout, + cJSON **recognition_result, + char **digits_collected, + char *terminator_collected, + switch_input_args_t *args) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + switch_input_args_t myargs = { 0 }; + switch_collect_input_state_t state = { 0 }; + switch_channel_t *channel = switch_core_session_get_channel(session); + + arg_recursion_check_start(args); + + /* configure digit collection */ + if (digit_timeout <= 0) digit_timeout = 5000; + if (min_digits < 0) { + min_digits = 0; + } + + /* check if digit collection is enabled */ + if (min_digits > 0) { + if (max_digits < min_digits) { + max_digits = min_digits; + } + if (max_digits > 100) { + max_digits = 100; + } + state.digits = switch_core_session_alloc(session, sizeof(char) * (max_digits + 1)); + switch_set_flag(&state, SWITCH_COLLECT_INPUT_DIGITS); + } else { + switch_set_flag(&state, SWITCH_COLLECT_INPUT_DIGITS_DONE); + } + + state.min_digits = min_digits; + state.max_digits = max_digits; + if (!zstr(terminators)) { + if (!strcasecmp(terminators, "any")) { + state.terminators = "1234567890*#"; + } else if (!strcasecmp(terminators, "none")) { + state.terminators = NULL; + } else { + state.terminators = terminators; + } + } + + if (!args) { + args = &myargs; + } + + /* start speech recognition, if enabled */ + if (recognizer_grammar && recognizer_mod_name) { + if ((status = switch_ivr_detect_speech(session, recognizer_mod_name, recognizer_grammar, "", NULL, NULL)) != SWITCH_STATUS_SUCCESS) { + /* map SWITCH_STATUS_FALSE to SWITCH_STATUS_GENERR to indicate grammar load failed + SWITCH_STATUS_NOT_INITALIZED will be passed back to indicate ASR resource problem */ + if (status == SWITCH_STATUS_FALSE) { + status = SWITCH_STATUS_GENERR; + } + goto done; + } + switch_set_flag(&state, SWITCH_COLLECT_INPUT_SPEECH); + } else { + switch_set_flag(&state, SWITCH_COLLECT_INPUT_SPEECH_DONE); + } + + /* play the prompt, looking for input result */ + args->input_callback = switch_collect_input_callback; + args->buf = &state; + args->buflen = sizeof(state); + switch_set_flag(&state, SWITCH_COLLECT_INPUT_PROMPT); + status = switch_ivr_play_file(session, NULL, prompt, args); + switch_clear_flag(&state, SWITCH_COLLECT_INPUT_PROMPT); + + if (args->dmachine && switch_ivr_dmachine_last_ping(args->dmachine) != SWITCH_STATUS_SUCCESS) { + switch_set_flag(&state, SWITCH_COLLECT_INPUT_DIGITS_DONE); + switch_set_flag(&state, SWITCH_COLLECT_INPUT_SPEECH_DONE); + status = SWITCH_STATUS_SUCCESS; + } + + if (status != SWITCH_STATUS_BREAK && status != SWITCH_STATUS_SUCCESS) { + status = SWITCH_STATUS_FALSE; + goto done; + } + + // wait for final result if not done + if (!switch_test_flag(&state, SWITCH_COLLECT_INPUT_DIGITS_DONE) || !switch_test_flag(&state, SWITCH_COLLECT_INPUT_SPEECH_DONE)) { + int sleep_time = digit_timeout; + + if (switch_test_flag(&state, SWITCH_COLLECT_INPUT_SPEECH)) { + switch_ivr_detect_speech_start_input_timers(session); + } + state.last_digit_time = switch_micro_time_now(); + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "(%s) WAITING FOR RESULT\n", switch_channel_get_name(channel)); + + while ((!switch_test_flag(&state, SWITCH_COLLECT_INPUT_DIGITS_DONE) || !switch_test_flag(&state, SWITCH_COLLECT_INPUT_SPEECH_DONE)) + && switch_channel_ready(channel)) { + + status = switch_ivr_sleep(session, sleep_time, SWITCH_FALSE, args); + + if (args->dmachine && switch_ivr_dmachine_last_ping(args->dmachine) != SWITCH_STATUS_SUCCESS) { + // dmachine done + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "(%s) DMACHINE DONE\n", switch_channel_get_name(channel)); + switch_set_flag(&state, SWITCH_COLLECT_INPUT_DIGITS_DONE); + switch_set_flag(&state, SWITCH_COLLECT_INPUT_SPEECH_DONE); + status = SWITCH_STATUS_SUCCESS; + goto done; + } + + if (state.terminator != 0) { + switch_set_flag(&state, SWITCH_COLLECT_INPUT_SPEECH_DONE); + status = SWITCH_STATUS_SUCCESS; + sleep_time = digit_timeout; + continue; + } + + sleep_time = (switch_micro_time_now() - state.last_digit_time) / 1000; + if (sleep_time >= digit_timeout) { + // too much time since last digit + if (!switch_test_flag(&state, SWITCH_COLLECT_INPUT_DIGITS_DONE)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "(%s) INTER-DIGIT TIMEOUT\n", switch_channel_get_name(channel)); + switch_set_flag(&state, SWITCH_COLLECT_INPUT_DIGITS_DONE); + } + status = SWITCH_STATUS_SUCCESS; + sleep_time = digit_timeout; + } else { + // woke up early, sleep for remaining digit timeout + sleep_time = digit_timeout - sleep_time; + } + + if (status != SWITCH_STATUS_BREAK && status != SWITCH_STATUS_SUCCESS) { + // error of some sort + status = SWITCH_STATUS_FALSE; + goto done; + } + + } + } + +done: + + if (status == SWITCH_STATUS_BREAK) { + status = SWITCH_STATUS_SUCCESS; + } + + if (switch_test_flag(&state, SWITCH_COLLECT_INPUT_SPEECH)) { + switch_ivr_pause_detect_speech(session); + } + + if (!zstr(state.digits) && strlen(state.digits) >= state.min_digits) { + /* got DTMF result */ + if (digits_collected) { + *digits_collected = state.digits; + } + + if (state.recognition_result) { + cJSON_Delete(state.recognition_result); + } + } else if (state.recognition_result) { + /* have some kind of recognition result or error */ + if (recognition_result) { + *recognition_result = state.recognition_result; + } else { + cJSON_Delete(state.recognition_result); + } + } + + if (terminator_collected && state.terminator != 0) { + *terminator_collected = state.terminator; + } + + arg_recursion_check_stop(args); + + return status; +} + + + /* For Emacs: * Local Variables: * mode:c diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 6d6270208c..f0d3a06474 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -1,6 +1,7 @@ include $(top_srcdir)/build/modmake.rulesam -bin_PROGRAMS = switch_event switch_hash switch_ivr_originate switch_utils switch_core switch_console switch_vpx +bin_PROGRAMS = switch_event switch_hash switch_ivr_originate switch_utils switch_core switch_console switch_vpx \ + switch_ivr_play_say AM_LDFLAGS = -avoid-version -no-undefined $(SWITCH_AM_LDFLAGS) $(openssl_LIBS) AM_LDFLAGS += $(FREESWITCH_LIBS) $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS) AM_CFLAGS = $(SWITCH_AM_CPPFLAGS) diff --git a/tests/unit/switch_ivr_play_say.c b/tests/unit/switch_ivr_play_say.c new file mode 100644 index 0000000000..90bcd9d1fb --- /dev/null +++ b/tests/unit/switch_ivr_play_say.c @@ -0,0 +1,266 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2019, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Rienzo + * + * + * switch_ivr_play_say.c -- IVR tests + * + */ +#include +#include + +#include + +FST_CORE_BEGIN("./conf_playsay") +{ + FST_SUITE_BEGIN(switch_ivr_play_say) + { + FST_SETUP_BEGIN() + { + } + FST_SETUP_END() + + FST_TEARDOWN_BEGIN() + { + } + FST_TEARDOWN_END() + + FST_SESSION_BEGIN(play_and_collect_input_failure) + { + char terminator_collected = 0; + char *digits_collected = NULL; + cJSON *recognition_result = NULL; + + // args + const char *play_files = "silence_stream://2000"; + const char *speech_engine = "test"; + const char *terminators = "#"; + int min_digits = 1; + int max_digits = 3; + int digit_timeout = 15000; + int no_input_timeout = digit_timeout; + int speech_complete_timeout = digit_timeout; + int speech_recognition_timeout = digit_timeout; + char *speech_grammar_args = switch_core_session_sprintf(fst_session, "{start-input-timers=false,no-input-timeout=%d,vad-silence-ms=%d,speech-timeout=%d,language=en-US}default", + no_input_timeout, speech_complete_timeout, speech_recognition_timeout); + + switch_status_t status; + + // collect input - 1# + fst_sched_recv_dtmf("+1", "1"); + fst_sched_recv_dtmf("+2", "2"); + fst_sched_recv_dtmf("+3", "3"); + status = switch_ivr_play_and_collect_input(fst_session, play_files, speech_engine, speech_grammar_args, min_digits, max_digits, terminators, digit_timeout, &recognition_result, &digits_collected, &terminator_collected, NULL); + + fst_check(status == SWITCH_STATUS_SUCCESS); // might be break? + fst_check_string_equals(cJSON_GetObjectCstr(recognition_result, "text"), NULL); + fst_check_string_equals(digits_collected, "123"); + fst_check(terminator_collected == 0); + } + FST_SESSION_END() + + FST_SESSION_BEGIN(play_and_collect_input) + { + char terminator_collected = 0; + char *digits_collected = NULL; + cJSON *recognition_result = NULL; + + // args + const char *play_files = "silence_stream://1000"; + const char *speech_engine = "test"; + const char *terminators = "#"; + int min_digits = 1; + int max_digits = 99; + int digit_timeout = 5000; + int no_input_timeout = digit_timeout; + int speech_complete_timeout = digit_timeout; + int speech_recognition_timeout = 60000; + char *speech_grammar_args = switch_core_session_sprintf(fst_session, "{start-input-timers=false,no-input-timeout=%d,vad-silence-ms=%d,speech-timeout=%d,language=en-US}default", + no_input_timeout, speech_complete_timeout, speech_recognition_timeout); + + switch_status_t status; + + // collect input - 1# + fst_sched_recv_dtmf("+2", "1#"); + terminator_collected = 0; + digits_collected = NULL; + recognition_result = NULL; + fst_time_mark(); + status = switch_ivr_play_and_collect_input(fst_session, play_files, speech_engine, speech_grammar_args, min_digits, max_digits, terminators, digit_timeout, &recognition_result, &digits_collected, &terminator_collected, NULL); + + // check results + fst_check_duration(2500, 1000); // should return immediately when term digit is received + fst_check(status == SWITCH_STATUS_SUCCESS); // might be break? + fst_check_string_equals(cJSON_GetObjectCstr(recognition_result, "text"), NULL); + fst_check_string_equals(digits_collected, "1"); + fst_check(terminator_collected == '#'); + + // collect input - 1# again, same session + fst_sched_recv_dtmf("+2", "1#"); + terminator_collected = 0; + digits_collected = NULL; + if (recognition_result) cJSON_Delete(recognition_result); + recognition_result = NULL; + fst_time_mark(); + status = switch_ivr_play_and_collect_input(fst_session, play_files, speech_engine, speech_grammar_args, min_digits, max_digits, terminators, digit_timeout, &recognition_result, &digits_collected, &terminator_collected, NULL); + + // check results + fst_check(status == SWITCH_STATUS_SUCCESS); // might be break? + fst_check_duration(2500, 1000); // should return immediately when term digit is received + fst_check_string_equals(cJSON_GetObjectCstr(recognition_result, "text"), NULL); + fst_check_string_equals(digits_collected, "1"); + fst_check(terminator_collected == '#'); + + // collect input - 1 + fst_sched_recv_dtmf("+2", "1"); + terminator_collected = 0; + digits_collected = NULL; + if (recognition_result) cJSON_Delete(recognition_result); + recognition_result = NULL; + fst_time_mark(); + status = switch_ivr_play_and_collect_input(fst_session, play_files, speech_engine, speech_grammar_args, min_digits, max_digits, terminators, digit_timeout, &recognition_result, &digits_collected, &terminator_collected, NULL); + + // check results + fst_check(status == SWITCH_STATUS_SUCCESS); // might be break? + fst_check_duration(7000, 1000); // should return after timeout when prompt finishes playing + fst_check_string_equals(cJSON_GetObjectCstr(recognition_result, "text"), NULL); + fst_check_string_equals(digits_collected, "1"); + fst_check(terminator_collected == 0); + + // collect input - 12# + fst_sched_recv_dtmf("+2", "12#"); + terminator_collected = 0; + digits_collected = NULL; + if (recognition_result) cJSON_Delete(recognition_result); + recognition_result = NULL; + fst_time_mark(); + status = switch_ivr_play_and_collect_input(fst_session, play_files, speech_engine, speech_grammar_args, min_digits, max_digits, terminators, digit_timeout, &recognition_result, &digits_collected, &terminator_collected, NULL); + + // check results + fst_check(status == SWITCH_STATUS_SUCCESS); // might be break? + fst_check_duration(2500, 1000); // should return after timeout when prompt finishes playing + fst_check_string_equals(cJSON_GetObjectCstr(recognition_result, "text"), NULL); + fst_check_string_equals(digits_collected, "12"); + fst_check(terminator_collected == '#'); + + // collect input - 12# - long spacing + fst_sched_recv_dtmf("+2", "1"); + fst_sched_recv_dtmf("+4", "2"); + fst_sched_recv_dtmf("+6", "3"); + fst_sched_recv_dtmf("+8", "4"); + fst_sched_recv_dtmf("+10", "#"); + + terminator_collected = 0; + digits_collected = NULL; + if (recognition_result) cJSON_Delete(recognition_result); + recognition_result = NULL; + fst_time_mark(); + status = switch_ivr_play_and_collect_input(fst_session, play_files, speech_engine, speech_grammar_args, min_digits, max_digits, terminators, digit_timeout, &recognition_result, &digits_collected, &terminator_collected, NULL); + + // check results + fst_check(status == SWITCH_STATUS_SUCCESS); // might be break? + fst_check_duration(10000, 1000); // should return when dtmf terminator is pressed + fst_check_string_equals(cJSON_GetObjectCstr(recognition_result, "text"), NULL); + fst_check_string_equals(digits_collected, "1234"); + fst_check(terminator_collected == '#'); + + // collect input - make an utterance + speech_complete_timeout = 500; // 'auto' mode... + speech_grammar_args = switch_core_session_sprintf(fst_session, "{start-input-timers=false,no-input-timeout=%d,vad-silence-ms=%d,speech-timeout=%d,language=en-US}default", + no_input_timeout, speech_complete_timeout, speech_recognition_timeout); + switch_ivr_displace_session(fst_session, "file_string://silence_stream://500,0!tone_stream://%%(2000,0,350,440)", 0, "r"); + terminator_collected = 0; + digits_collected = NULL; + if (recognition_result) cJSON_Delete(recognition_result); + recognition_result = NULL; + fst_time_mark(); + status = switch_ivr_play_and_collect_input(fst_session, play_files, speech_engine, speech_grammar_args, min_digits, max_digits, terminators, digit_timeout, &recognition_result, &digits_collected, &terminator_collected, NULL); + + // check results + fst_check(status == SWITCH_STATUS_SUCCESS); // might be break? + fst_check_duration(2500, 1000); // returns when utterance is done + fst_check_string_equals(cJSON_GetObjectCstr(recognition_result, "text"), "agent"); + fst_check_string_equals(digits_collected, NULL); + fst_check(terminator_collected == 0); + + // single digit test + fst_sched_recv_dtmf("+2", "2"); + max_digits = 1; + terminator_collected = 0; + digits_collected = NULL; + if (recognition_result) cJSON_Delete(recognition_result); + recognition_result = NULL; + fst_time_mark(); + status = switch_ivr_play_and_collect_input(fst_session, play_files, speech_engine, speech_grammar_args, min_digits, max_digits, terminators, digit_timeout, &recognition_result, &digits_collected, &terminator_collected, NULL); + + // check results + fst_check(status == SWITCH_STATUS_SUCCESS); // might be break? + fst_check_duration(2500, 1000); // returns when single digit is pressed + fst_check_string_equals(cJSON_GetObjectCstr(recognition_result, "text"), NULL); + fst_check_string_equals(digits_collected, "2"); + fst_check(terminator_collected == 0); + + // three digit test + fst_sched_recv_dtmf("+2", "259"); + min_digits = 1; + max_digits = 3; + terminator_collected = 0; + digits_collected = NULL; + if (recognition_result) cJSON_Delete(recognition_result); + recognition_result = NULL; + fst_time_mark(); + status = switch_ivr_play_and_collect_input(fst_session, play_files, speech_engine, speech_grammar_args, min_digits, max_digits, terminators, digit_timeout, &recognition_result, &digits_collected, &terminator_collected, NULL); + + // check results + fst_check(status == SWITCH_STATUS_SUCCESS); // might be break? + fst_check_duration(2000, 1000); // returns when single digit is pressed + fst_check_string_equals(cJSON_GetObjectCstr(recognition_result, "text"), NULL); + fst_check_string_equals(digits_collected, "259"); + fst_check(terminator_collected == 0); + + // min digit test + fst_sched_recv_dtmf("+2", "25"); + min_digits = 3; + max_digits = 3; + terminator_collected = 0; + digits_collected = NULL; + if (recognition_result) cJSON_Delete(recognition_result); + recognition_result = NULL; + fst_time_mark(); + status = switch_ivr_play_and_collect_input(fst_session, play_files, speech_engine, speech_grammar_args, min_digits, max_digits, terminators, digit_timeout, &recognition_result, &digits_collected, &terminator_collected, NULL); + + // check results + fst_check(status == SWITCH_STATUS_SUCCESS); // might be break? + fst_check_duration(7000, 1000); // inter-digit timeout after 2nd digit pressed + fst_check_string_equals(cJSON_GetObjectCstr(recognition_result, "text"), ""); + fst_check_string_equals(digits_collected, NULL); + fst_check(terminator_collected == 0); + } + FST_SESSION_END() + } + FST_SUITE_END() +} +FST_CORE_END() +