]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-11728 [core] add switch_ivr_play_and_collect_input(). This function plays a promp...
authorChris Rienzo <chris@signalwire.com>
Mon, 25 Mar 2019 20:12:55 +0000 (20:12 +0000)
committerAndrey Volk <andywolk@gmail.com>
Wed, 17 Jul 2019 15:58:45 +0000 (19:58 +0400)
src/include/switch_ivr.h
src/switch_ivr_play_say.c
tests/unit/Makefile.am
tests/unit/switch_ivr_play_say.c [new file with mode: 0644]

index 69cb038caf5d422dfe52e6d14cbe250a6181600d..061da0705b57095add97d552f54098112269da14 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
- * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
+ * Copyright (C) 2005-2019, Anthony Minessale II <anthm@freeswitch.org>
  *
  * 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
index 8063d5cb10bb615845341dd2673f7ac4c81c406b..2278c35e48a92ca41f2fecf533531ccc6d295ca7 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
- * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
+ * Copyright (C) 2005-2019, Anthony Minessale II <anthm@freeswitch.org>
  *
  * 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
index 6d6270208c5f32ca3f281103a476fe1a023ca29b..f0d3a06474fa1babaeb1a44ebdce39baf4b6b2c3 100644 (file)
@@ -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 (file)
index 0000000..90bcd9d
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2019, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * 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 <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris@signalwire.com>
+ *
+ *
+ * switch_ivr_play_say.c -- IVR tests
+ *
+ */
+#include <switch.h>
+#include <stdlib.h>
+
+#include <test/switch_test.h>
+
+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()
+