From 534db1947eb890978cb7c1710e9827c75ef5dbdb Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 27 Jan 2006 21:33:45 +0000 Subject: [PATCH] Add more IVR stuff and a test app (mod_ivrtest) in dialplan (ivrtest ) if the file is valid it will play the file and you will be expected to dial digits until you dial 10 digits or press # or * (* will end the call) (# will repeat the test endlessly) git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@462 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- checkversion.sh | 4 +- modules.conf | 1 + src/include/switch_ivr.h | 41 +++- src/include/switch_types.h | 6 +- .../applications/mod_ivrtest/mod_ivrtest.c | 127 +++++++++++ .../mod_ivrtest/mod_ivrtest.vcproj | 207 ++++++++++++++++++ .../applications/mod_playback/mod_playback.c | 10 +- src/mod/dialplans/mod_pcre/mod_pcre.c | 6 +- src/mod/formats/mod_sndfile/mod_sndfile.c | 2 + src/switch_ivr.c | 129 +++++++++-- 10 files changed, 504 insertions(+), 29 deletions(-) create mode 100644 src/mod/applications/mod_ivrtest/mod_ivrtest.c create mode 100644 src/mod/applications/mod_ivrtest/mod_ivrtest.vcproj diff --git a/checkversion.sh b/checkversion.sh index ef6184141d..04f7cfc5f7 100755 --- a/checkversion.sh +++ b/checkversion.sh @@ -6,10 +6,12 @@ if [ ! -z $1 ] ; then fi fi +force=0 version=`svnversion . -n || echo hacked` oldversion=`cat .version 2>/dev/null || echo "0"` +grep "@SVN_VERSION@" src/include/switch_version.h && force=1 -if [ $oldversion != $version ] ; then +if [ $oldversion != $version ] || [ $force = 1 ] ; then cat src/include/switch_version.h.in | sed "s/@SVN_VERSION@/$version/g" > src/include/switch_version.h echo $version > .version make modclean diff --git a/modules.conf b/modules.conf index 3cb24fd49d..7f5ea66536 100644 --- a/modules.conf +++ b/modules.conf @@ -1,5 +1,6 @@ applications/mod_bridgecall applications/mod_playback +applications/mod_ivrtest #applications/mod_skel #codecs/mod_g729 codecs/mod_gsm diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 0f0209e090..9b0560c427 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -52,6 +52,34 @@ extern "C" { * @{ */ +/*! + \brief Wait for DTMF digits calling a pluggable callback function when digits are collected. + \param session the session to read. + \param dtmf_collection_callback code to execute if any dtmf is dialed during the recording + \return SWITCH_STATUS_SUCCESS to keep the collection moving. +*/ +SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_callback(switch_core_session *session, + switch_dtmf_callback_function dtmf_callback, + void *buf, + unsigned int buflen); + +/*! + \brief Wait for specified number of DTMF digits, untile terminator is received or until the channel hangs up. + \param session the session to read. + \param buf strig to write to + \param buflen max size of buf + \param maxdigits max number of digits to read + \param terminators digits to end the collection + \param terminator actual digit that caused the collection to end (if any) + \return SWITCH_STATUS_SUCCESS to keep the collection moving. +*/ +SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_count(switch_core_session *session, + char *buf, + unsigned int buflen, + int maxdigits, + const char *terminators, + char *terminator); + /*! \brief play a file from the disk to the session \param session the session to play the file too @@ -59,11 +87,14 @@ extern "C" { \param timer_name the name of a timer to use input will be absorbed (NULL to time off the session input). \param dtmf_callback code to execute if any dtmf is dialed during the playback \return SWITCH_STATUS_SUCCESS if all is well + \note passing a NULL dtmf_callback nad a not NULL buf indicates to copy any dtmf to buf and stop playback. */ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, char *file, char *timer_name, - switch_dtmf_callback_function dtmf_callback); + switch_dtmf_callback_function dtmf_callback, + void *buf, + unsigned int buflen); @@ -73,11 +104,15 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, \param file the path to the file \param dtmf_callback code to execute if any dtmf is dialed during the recording \return SWITCH_STATUS_SUCCESS if all is well + \note passing a NULL dtmf_callback nad a not NULL buf indicates to copy any dtmf to buf and stop recording. */ SWITCH_DECLARE(switch_status) switch_ivr_record_file(switch_core_session *session, char *file, - switch_dtmf_callback_function dtmf_callback); - + switch_dtmf_callback_function dtmf_callback, + void *buf, + unsigned int buflen); + + /** @} */ #ifdef __cplusplus diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 4c8a411762..aff3a6f4a8 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -102,6 +102,7 @@ typedef enum { SWITCH_STATUS_RESAMPLE - An indication that a resample has occured SWITCH_STATUS_GENERR - A general Error SWITCH_STATUS_INUSE - An indication that requested resource is in use + SWITCH_STATUS_BREAK - A non-fatal break of an operation */ typedef enum { @@ -115,7 +116,8 @@ typedef enum { SWITCH_STATUS_NOOP, SWITCH_STATUS_RESAMPLE, SWITCH_STATUS_GENERR, - SWITCH_STATUS_INUSE + SWITCH_STATUS_INUSE, + SWITCH_STATUS_BREAK, } switch_status; /*! @@ -392,7 +394,7 @@ typedef switch_status (*switch_waitfor_read_hook)(switch_core_session *, int, in typedef switch_status (*switch_waitfor_write_hook)(switch_core_session *, int, int); typedef switch_status (*switch_send_dtmf_hook)(switch_core_session *, char *); typedef switch_status (*switch_api_function)(char *in, char *out, size_t outlen); -typedef switch_status (*switch_dtmf_callback_function)(switch_core_session *session, char *dtmf); +typedef switch_status (*switch_dtmf_callback_function)(switch_core_session *session, char *dtmf, void *buf, unsigned int buflen); /* things we don't deserve to know about */ diff --git a/src/mod/applications/mod_ivrtest/mod_ivrtest.c b/src/mod/applications/mod_ivrtest/mod_ivrtest.c new file mode 100644 index 0000000000..31600fe98d --- /dev/null +++ b/src/mod/applications/mod_ivrtest/mod_ivrtest.c @@ -0,0 +1,127 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, 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): + * + * Anthony Minessale II + * + * + * mod_ivrtest.c -- Raw Audio File Streaming Application Module + * + */ +#include +#include + + + +static const char modname[] = "mod_ivrtest"; + + +/* + dtmf handler function you can hook up to be executed when a digit is dialed during playback + if you return anything but SWITCH_STATUS_SUCCESS the playback will stop. +*/ +static switch_status on_dtmf(switch_core_session *session, char *dtmf, void *buf, unsigned int buflen) +{ + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Digits %s\n", dtmf); + + switch_copy_string((char *)buf, dtmf, buflen); + return SWITCH_STATUS_BREAK; + +} + +static void ivrtest_function(switch_core_session *session, char *data) +{ + switch_channel *channel; + switch_status status = SWITCH_STATUS_SUCCESS; + char buf[10] = ""; + char term; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + switch_channel_answer(channel); + + + while (switch_channel_get_state(channel) == CS_EXECUTE) { + memset(buf, 0, sizeof(buf)); + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Enter up to 10 digits, press # to terminate, * to hangup\n"); + + if (data) { + /* you could have passed NULL instead of on_dtmf to get this exact behaviour (copy the digits to buf and stop playing) + but you may want to pass the function if you have something cooler to do... + */ + status = switch_ivr_play_file(session, data, NULL, on_dtmf, buf, sizeof(buf)); + + if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) { + switch_channel_hangup(channel); + break; + } + } + + if (switch_ivr_collect_digits_count(session, buf, sizeof(buf), 10, "#*", &term) != SWITCH_STATUS_SUCCESS) { + switch_channel_hangup(channel); + break; + } + + if (term && term == '*') { + break; + } + + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "You Dialed [%s]\n", buf); + } + +} + + +static const switch_application_interface ivrtest_application_interface = { + /*.interface_name */ "ivrtest", + /*.application_function */ ivrtest_function +}; + +static const switch_loadable_module_interface mod_ivrtest_module_interface = { + /*.module_name = */ modname, + /*.endpoint_interface = */ NULL, + /*.timer_interface = */ NULL, + /*.dialplan_interface = */ NULL, + /*.codec_interface = */ NULL, + /*.application_interface */ &ivrtest_application_interface +}; + +SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_module_interface **interface, char *filename) +{ + + /* connect my internal structure to the blank pointer passed to me */ + *interface = &mod_ivrtest_module_interface; + + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + +/* 'switch_module_runtime' will start up in a thread by itself just by having it exist + if it returns anything but SWITCH_STATUS_TERM it will be called again automaticly +*/ + + +//switch_status switch_module_runtime(void) diff --git a/src/mod/applications/mod_ivrtest/mod_ivrtest.vcproj b/src/mod/applications/mod_ivrtest/mod_ivrtest.vcproj new file mode 100644 index 0000000000..32d316ebf2 --- /dev/null +++ b/src/mod/applications/mod_ivrtest/mod_ivrtest.vcproj @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/applications/mod_playback/mod_playback.c b/src/mod/applications/mod_playback/mod_playback.c index 9f06cb52d4..1035ee4603 100644 --- a/src/mod/applications/mod_playback/mod_playback.c +++ b/src/mod/applications/mod_playback/mod_playback.c @@ -40,7 +40,7 @@ static const char modname[] = "mod_playback"; dtmf handler function you can hook up to be executed when a digit is dialed during playback if you return anything but SWITCH_STATUS_SUCCESS the playback will stop. */ -static switch_status on_dtmf(switch_core_session *session, char *dtmf) +static switch_status on_dtmf(switch_core_session *session, char *dtmf, void *buf, unsigned int buflen) { switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Digits %s\n", dtmf); @@ -52,7 +52,7 @@ static switch_status on_dtmf(switch_core_session *session, char *dtmf) } -void playback_function(switch_core_session *session, char *data) +static void playback_function(switch_core_session *session, char *data) { switch_channel *channel; char *timer_name = NULL; @@ -67,20 +67,20 @@ void playback_function(switch_core_session *session, char *data) channel = switch_core_session_get_channel(session); assert(channel != NULL); - if (switch_ivr_play_file(session, file_name, timer_name, on_dtmf) != SWITCH_STATUS_SUCCESS) { + if (switch_ivr_play_file(session, file_name, timer_name, on_dtmf, NULL, 0) != SWITCH_STATUS_SUCCESS) { switch_channel_hangup(channel); } } -void record_function(switch_core_session *session, char *data) +static void record_function(switch_core_session *session, char *data) { switch_channel *channel; channel = switch_core_session_get_channel(session); assert(channel != NULL); - if (switch_ivr_record_file(session, data, on_dtmf) != SWITCH_STATUS_SUCCESS) { + if (switch_ivr_record_file(session, data, on_dtmf, NULL, 0) != SWITCH_STATUS_SUCCESS) { switch_channel_hangup(channel); } diff --git a/src/mod/dialplans/mod_pcre/mod_pcre.c b/src/mod/dialplans/mod_pcre/mod_pcre.c index 3c5731428f..3539559a72 100644 --- a/src/mod/dialplans/mod_pcre/mod_pcre.c +++ b/src/mod/dialplans/mod_pcre/mod_pcre.c @@ -147,11 +147,7 @@ switch_caller_extension *dialplan_hunt(switch_core_session *session) if ((data = strchr(app, ' '))) { *data = '\0'; data++; - } else { - switch_console_printf(SWITCH_CHANNEL_CONSOLE, "invalid extension on line %d\n", cfg.lineno); - continue; - } - + } if (!extension) { if (! diff --git a/src/mod/formats/mod_sndfile/mod_sndfile.c b/src/mod/formats/mod_sndfile/mod_sndfile.c index 99faee6285..0feb5c2cad 100644 --- a/src/mod/formats/mod_sndfile/mod_sndfile.c +++ b/src/mod/formats/mod_sndfile/mod_sndfile.c @@ -89,6 +89,8 @@ switch_status sndfile_file_open(switch_file_handle *handle, char *path) } else { ready = 0; } + } else { + ready = 0; } diff --git a/src/switch_ivr.c b/src/switch_ivr.c index 9b2778865a..4bbdfca8f3 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -35,9 +35,95 @@ /* TBD (Lots! there are not very many functions in here lol) */ +SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_callback(switch_core_session *session, + switch_dtmf_callback_function dtmf_callback, + void *buf, + unsigned int buflen) +{ + switch_channel *channel; + switch_status status = SWITCH_STATUS_SUCCESS; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + if (!dtmf_callback) { + return SWITCH_STATUS_GENERR; + } + + while (switch_channel_get_state(channel) == CS_EXECUTE) { + switch_frame *read_frame; + char dtmf[128]; + + if (switch_channel_has_dtmf(channel)) { + switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf)); + status = dtmf_callback(session, dtmf, buf, buflen); + } + + if (status != SWITCH_STATUS_SUCCESS) { + break; + } + + if (switch_core_session_read_frame(session, &read_frame, -1, 0) != SWITCH_STATUS_SUCCESS) { + break; + } + } + + return status; +} + + +SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_count(switch_core_session *session, + char *buf, + unsigned int buflen, + int maxdigits, + const char *terminators, + char *terminator) +{ + int i = 0, x = strlen(buf); + switch_channel *channel; + switch_status status = SWITCH_STATUS_SUCCESS; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + *terminator = '\0'; + while (switch_channel_get_state(channel) == CS_EXECUTE) { + switch_frame *read_frame; + + if (switch_channel_has_dtmf(channel)) { + char dtmf[128]; + switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf)); + + for(i =0 ; i < strlen(dtmf); i++) { + + if (strchr(terminators, dtmf[i])) { + *terminator = dtmf[i]; + return SWITCH_STATUS_SUCCESS; + } + + buf[x++] = dtmf[i]; + buf[x] = '\0'; + if (x >= buflen || x >= maxdigits) { + return SWITCH_STATUS_SUCCESS; + } + } + } + + if ((status = switch_core_session_read_frame(session, &read_frame, -1, 0)) != SWITCH_STATUS_SUCCESS) { + break; + } + } + + return status; +} + + + SWITCH_DECLARE(switch_status) switch_ivr_record_file(switch_core_session *session, char *file, - switch_dtmf_callback_function dtmf_callback) + switch_dtmf_callback_function dtmf_callback, + void *buf, + unsigned int buflen) { switch_channel *channel; char dtmf[128]; @@ -91,22 +177,27 @@ SWITCH_DECLARE(switch_status) switch_ivr_record_file(switch_core_session *sessio while (switch_channel_get_state(channel) == CS_EXECUTE) { size_t len; - if (dtmf_callback) { + if (dtmf_callback || buf) { /* dtmf handler function you can hook up to be executed when a digit is dialed during playback if you return anything but SWITCH_STATUS_SUCCESS the playback will stop. */ if (switch_channel_has_dtmf(channel)) { switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf)); - status = dtmf_callback(session, dtmf); + if (dtmf_callback) { + status = dtmf_callback(session, dtmf, buf, buflen); + } else { + switch_copy_string((char *)buf, dtmf, buflen); + status = SWITCH_STATUS_BREAK; + } } if (status != SWITCH_STATUS_SUCCESS) { break; } } - - if (switch_core_session_read_frame(session, &read_frame, -1, 0) != SWITCH_STATUS_SUCCESS) { + + if ((status = switch_core_session_read_frame(session, &read_frame, -1, 0)) != SWITCH_STATUS_SUCCESS) { break; } @@ -123,10 +214,12 @@ SWITCH_DECLARE(switch_status) switch_ivr_record_file(switch_core_session *sessio SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, char *file, char *timer_name, - switch_dtmf_callback_function dtmf_callback) + switch_dtmf_callback_function dtmf_callback, + void *buf, + unsigned int buflen) { switch_channel *channel; - short buf[960]; + short abuf[960]; char dtmf[128]; int interval = 0, samples = 0; size_t len = 0, ilen = 0; @@ -156,8 +249,8 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, switch_channel_answer(channel); - write_frame.data = buf; - write_frame.buflen = sizeof(buf); + write_frame.data = abuf; + write_frame.buflen = sizeof(abuf); switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OPEN FILE %s %dhz %d channels\n", file, fh.samplerate, fh.channels); @@ -206,23 +299,30 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, while (switch_channel_get_state(channel) == CS_EXECUTE) { int done = 0; - if (dtmf_callback) { + if (dtmf_callback || buf) { + + /* dtmf handler function you can hook up to be executed when a digit is dialed during playback if you return anything but SWITCH_STATUS_SUCCESS the playback will stop. */ if (switch_channel_has_dtmf(channel)) { switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf)); - status = dtmf_callback(session, dtmf); + if (dtmf_callback) { + status = dtmf_callback(session, dtmf, buf, buflen); + } else { + switch_copy_string((char *)buf, dtmf, buflen); + status = SWITCH_STATUS_BREAK; + } } - + if (status != SWITCH_STATUS_SUCCESS) { done = 1; break; } } - switch_core_file_read(&fh, buf, &ilen); + switch_core_file_read(&fh, abuf, &ilen); if (done || ilen <= 0) { break; @@ -270,3 +370,6 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, return status; } + + +