mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-08-13 17:38:59 +00:00
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.
This commit is contained in:
committed by
Andrey Volk
parent
1b430c76d5
commit
c796723dd7
@@ -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
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user