From 44fc26f7d4e4b02265ed4e506d655594b63c2fc9 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 9 Nov 2006 05:39:04 +0000 Subject: [PATCH] Finalization of speech detect interface and API This changes the core to have the necessary tools to create a speech detection interface. It also changes the code in javascript (mod_spidermonkey) there are a few api changes in how it handles callbacks It also adds grammars as a system dir to store asr grammars git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3291 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- scripts/js_modules/SpeechTools.jm | 433 +++++++++ src/include/switch_am_config.h.in | 2 +- src/include/switch_core.h | 119 ++- src/include/switch_ivr.h | 63 +- src/include/switch_loadable_module.h | 9 + src/include/switch_module_interfaces.h | 51 +- src/include/switch_types.h | 76 +- .../applications/mod_commands/mod_commands.c | 35 +- .../mod_conference/mod_conference.c | 8 +- .../applications/mod_dptools/mod_dptools.c | 39 +- .../applications/mod_ivrtest/mod_ivrtest.c | 126 ++- src/mod/applications/mod_rss/mod_rss.c | 2 +- src/mod/asr_tts/mod_cepstral/mod_cepstral.c | 105 +-- .../mod_event_socket/mod_event_socket.c | 9 +- .../mod_spidermonkey/mod_spidermonkey.c | 875 ++++++++++-------- src/switch_channel.c | 20 +- src/switch_core.c | 186 +++- src/switch_event.c | 19 +- src/switch_ivr.c | 324 ++++++- src/switch_loadable_module.c | 35 +- 20 files changed, 1996 insertions(+), 540 deletions(-) create mode 100644 scripts/js_modules/SpeechTools.jm diff --git a/scripts/js_modules/SpeechTools.jm b/scripts/js_modules/SpeechTools.jm new file mode 100644 index 0000000000..725e7fb787 --- /dev/null +++ b/scripts/js_modules/SpeechTools.jm @@ -0,0 +1,433 @@ +/* + * 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 + * + * + * SpeechTools.jm Speech Detection Interface + * + */ + +/* Constructor for Grammar Class (Class to identify a grammar entity) */ +function Grammar(grammar_name, path, obj_path, min_score, confirm_score, halt) { + this.grammar_name = grammar_name; + this.path = path; + this.min_score = min_score; + this.confirm_score = confirm_score; + this.halt = halt; + this.obj_path = obj_path; + + if (!this.min_score) { + this.min_score = 1; + } + + if (!this.confirm_score) { + this.confirm_score = 400; + } +} + +/* Constructor for SpeechDetect Class (Class to Detect Speech) */ +function SpeechDetect(session, mod, ip) { + this.ip = ip; + this.session = session; + this.mod = mod; + this.grammar_name = undefined; + this.grammar_hash = new Array(); + this.grammar_name = false; + this.audio_base = ""; + this.audio_ext = ".wav"; + this.tts_eng = false; + this.tts_voice = false; + this.AutoUnload = false; + this.debug = false; + + /* Set the TTS info*/ + this.setTTS = function (tts_eng, tts_voice) { + this.tts_eng = tts_eng; + this.tts_voice = tts_voice; + } + + /* Set the audio base */ + this.setAudioBase = function (audio_base) { + this.audio_base = audio_base; + } + + /* Set the audio extension */ + this.setAudioExt= function (audio_ext) { + this.audio_ext = audio_ext; + } + + /* Add a grammar to be used*/ + this.addGrammar = function(grammar_object) { + this.grammar_hash[grammar_object.grammar_name] = grammar_object; + } + + /* Play an audio file */ + this.streamFile = function(str) { + var rv; + if (!str) { + console_log("error", "No file specified!\n"); + return; + } + files = str.split(","); + for( x = 0; x < files.length; x++) { + if (!files[x] || files[x] == "noop") { + continue; + } + this.session.streamFile(this.audio_base + files[x] + this.audio_ext); + } + } + + /* Speak with TTS */ + this.speak = function(str) { + return this.session.speak(this.tts_eng, this.tts_voice, str); + } + + /* Set the current grammar */ + this.setGrammar = function (grammar_name) { + var grammar_object = this.grammar_hash[grammar_name]; + + if (!grammar_object) { + console_log("error", "Missing Grammar!\n"); + return false; + } + + if (this.grammar_name) { + if (this.AutoUnload) { + console_log("debug", "Unloading grammar " + this.grammar_name + "\n"); + this.session.execute("detect_speech", "nogrammar " + this.grammar_name); + } + if (grammar_object.path) { + this.session.execute("detect_speech", "grammar " + grammar_name + " " + grammar_object.path); + } else { + this.session.execute("detect_speech", "grammar " + grammar_name); + } + } else { + this.session.execute("detect_speech", this.mod + " " + grammar_name + " " + grammar_object.path + " " + this.ip); + } + + this.grammar_name = grammar_name; + } + + /* Pause speech detection */ + this.pause = function() { + this.session.execute("detect_speech", "pause"); + } + + /* Resume speech detection */ + this.resume = function() { + this.session.execute("detect_speech", "resume"); + } + + /* Stop speech detection */ + this.stop = function() { + this.session.execute("detect_speech", "stop"); + } + + /* Callback function for streaming,TTS or bridged calls */ + this.onInput = function(type, inputEvent, _this) { + if (type == "event") { + var speech_type = inputEvent.getHeader("Speech-Type"); + var rv = new Array(); + + if (!_this.grammar_name) { + console_log("error", "No Grammar name!\n"); + _this.session.hangup(); + return; + } + var grammar_object = _this.grammar_hash[_this.grammar_name]; + + if (!grammar_object) { + console_log("error", "Can't find grammar for " + _this.grammar_name + "\n"); + _this.session.hangup(); + return; + } + + if (speech_type == "begin-speaking") { + if (grammar_object.halt) { + return; + } + } else { + var body = inputEvent.getBody(); + var interp = new XML(body); + + _this.lastDetect = body; + + if (_this.debug) { + console_log("debug", "----XML:\n" + body); + console_log("debug", "----Heard [" + interp.input + "]\n"); + console_log("debug", "----Hit score " + interp.@score + "\n"); + } + + if (interp.@score < grammar_object.min_score) { + delete interp; + rv.push("_no_idea_"); + return rv; + } else { + if (interp.@score < grammar_object.confirm_score) { + rv.push("_confirm_"); + } + + eval("xo = interp." + grammar_object.obj_path + ";"); + + for (x = 0; x < xo.length(); x++) { + rv.push(xo[x]); + } + + delete interp; + return rv; + } + } + } + } +} + +/* Constructor for SpeechObtainer Class (Class to collect data from a SpeechDetect Class) */ +function SpeechObtainer(asr, req, wait_time) { + + this.items = new Array(); + this.collected_items = new Array(); + this.index = 0; + this.collected_index = 0; + this.req = req; + this.tts_eng = undefined; + this.tts_voice = false; + this.asr = asr; + this.top_sound = false; + this.add_sound = false; + this.dup_sound = false; + this.bad_sound = false; + this.needConfirm = false; + this.grammar_name = false; + this.audio_base = asr.audio_base; + this.audio_ext = asr.audio_ext; + this.tts_eng = asr.tts_eng; + this.tts_voice = asr.tts_voice; + this.debug = asr.debug; + + if (!req) { + req = 1; + } + + if (!wait_time) { + wait_time = 5000; + } + + this.waitTime = wait_time + 0; + + /* Set the TTS info*/ + this.setTTS = function (tts_eng, tts_voice) { + this.tts_eng = tts_eng; + this.tts_voice = tts_voice; + } + + /* Set the audio base */ + this.setAudioBase = function (audio_base) { + this.audio_base = audio_base; + } + + /* Set the audio extension */ + this.setAudioExt= function (audio_ext) { + this.audio_ext = audio_ext; + } + + /* Set the grammar to use */ + this.setGrammar = function (grammar_name, path, obj_path, min_score, confirm_score, halt) { + var grammar_object = new Grammar(grammar_name, path, obj_path, min_score, confirm_score, halt); + this.asr.addGrammar(grammar_object); + this.grammar_name = grammar_name; + } + + /* Set the top audio file or tts for the collection */ + this.setTopSound = function (top_sound) { + this.top_sound = top_sound; + } + + /* Set the audio file or tts for misunderstood input */ + this.setBadSound = function (bad_sound) { + this.bad_sound = bad_sound; + } + + /* Set the audio file or tts for duplicate input */ + this.setDupSound = function (dup_sound) { + this.dup_sound = dup_sound; + } + + /* Set the audio file or tts for accepted input */ + this.setAddSound = function (add_sound) { + this.add_sound = add_sound; + } + + /* Add acceptable items (comma sep list)*/ + this.addItem = function(item) { + ia = item.split(","); + var x; + for (x = 0; x < ia.length; x++) { + this.items[this.index++] = ia[x]; + } + } + + /* Add a regex */ + this.addRegEx = function(item) { + this.items[this.index++] = item; + } + + /* Reset the object and delete all collect items */ + this.reset = function() { + this.collected_index = 0; + delete this.collected_items; + this.collected_items = new Array(); + } + + /* Stream a file, collecting input */ + this.streamFile = function(str) { + var rv; + if (!str) { + console_log("error", "No file specified!\n"); + return; + } + files = str.split(","); + for( x = 0; x < files.length; x++) { + if (!files[x] || files[x] == "noop") { + continue; + } + rv = this.asr.session.streamFile(this.audio_base + files[x] + this.audio_ext , "", this.asr.onInput, this.asr); + if (rv) { + break; + } + } + + return rv; + } + + /* Speak some text, collecting input */ + this.speak = function(str) { + return this.asr.session.speak(this.tts_eng, this.tts_voice, str, this.asr.onInput, this.asr); + } + + /* Process collected input */ + this.react = function(say_str, play_str) { + var rv; + + this.asr.resume(); + if (this.tts_eng && this.tts_voice) { + rv = this.speak(say_str); + } else { + rv = this.streamFile(play_str); + } + if (!rv) { + rv = this.asr.session.collectInput(this.asr.onInput, this.asr, 500); + } + + return rv; + } + + /* Collect input */ + this.run = function() { + var rv; + var hit; + var dup; + + if (this.collected_index) { + this.reset(); + } + + if (!this.grammar_name) { + console_log("error", "No Grammar name!\n"); + this.session.hangup(); + return false; + } + + this.asr.setGrammar(this.grammar_name); + + while(this.asr.session.ready() && this.collected_index < this.req) { + var x; + this.needConfirm = false; + if (!rv) { + rv = this.react(this.top_sound, this.top_sound); + } + if (!rv) { + this.asr.resume(); + rv = this.asr.session.collectInput(this.asr.onInput, this.asr, this.waitTime); + } + hit = false; + if (rv) { + for (y = 0; y < rv.length; y++) { + if (rv[y] == "_confirm_") { + this.needConfirm = true; + if (this.debug) { + console_log("debug", "----We need to confirm this one\n"); + } + continue; + } + + for(x = 0 ; x < this.index; x++) { + if (this.debug) { + console_log("debug", "----Testing " + rv[y] + " =~ [" + this.items[x] + "]\n"); + } + var re = new RegExp(this.items[x]); + match = re.exec(rv[y]); + if (match) { + for (i = 0; i < match.length; i++) { + dup = false; + for(z = 0; z < this.collected_items.length; z++) { + if (this.collected_items[z] == match[i]) { + dup = true; + break; + } + } + if (dup) { + if (this.dup_sound) { + rv = this.react(this.dup_sound + " " + match[i], this.dup_sound + "," + match[i]); + } + } else { + if (this.debug) { + console_log("debug", "----Adding " + match[i] + "\n"); + } + this.collected_items[this.collected_index++] = match[i]; + hit = true; + if (this.add_sound) { + rv = this.react(this.add_sound + " " + match[i], this.add_sound + "," + match[i]); + } + } + } + } + } + } + } + + if (!rv) { + rv = this.asr.session.collectInput(this.asr.onInput, this.asr, 500); + } + + if (!rv && !hit && !dup) { + rv = this.react(this.bad_sound, this.bad_sound); + } + } + + return this.collected_items; + + } +} diff --git a/src/include/switch_am_config.h.in b/src/include/switch_am_config.h.in index 3e1a92ccaf..c2a949cebe 100644 --- a/src/include/switch_am_config.h.in +++ b/src/include/switch_am_config.h.in @@ -124,5 +124,5 @@ /* Define to rpl_malloc if the replacement function should be used. */ #undef malloc -/* Define to `unsigned int' if does not define. */ +/* Define to `unsigned' if does not define. */ #undef size_t diff --git a/src/include/switch_core.h b/src/include/switch_core.h index cf8697d621..d108242d90 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -119,14 +119,21 @@ struct switch_core_port_allocator; \param session the session to add the bug to \param callback a callback for events \param user_data arbitrary user data + \param flags flags to choose the stream \param new_bug pointer to assign new bug to \return SWITCH_STATUS_SUCCESS if the operation was a success */ SWITCH_DECLARE(switch_status_t) switch_core_media_bug_add(switch_core_session_t *session, switch_media_bug_callback_t callback, void *user_data, + switch_media_bug_flag_t flags, switch_media_bug_t **new_bug); - +/*! + \brief Obtain private data from a media bug + \param session the session to obtain the private data from + \return the private data +*/ +SWITCH_DECLARE(void *) switch_core_media_bug_get_user_data(switch_media_bug_t *bug); /*! \brief Remove a media bug from the session @@ -1060,7 +1067,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_file_close(switch_file_handle_t *fh) \param module_name the speech module to use \param voice_name the desired voice name \param rate the sampling rate - \param flags asr/tts flags + \param flags tts flags \param pool the pool to use (NULL for new pool) \return SWITCH_STATUS_SUCCESS if the handle is opened */ @@ -1070,28 +1077,6 @@ SWITCH_DECLARE(switch_status_t) switch_core_speech_open(switch_speech_handle_t * unsigned int rate, switch_speech_flag_t *flags, switch_memory_pool_t *pool); - -/*! - \brief Feed data to the ASR module - \param sh the speech handle to feed - \param data the buffer of audio data - \param len the in-use size of the buffer - \param rate the rate of the audio (in hz) - \param flags flags in/out for fine tuning - \return SWITCH_STATUS_SUCCESS with possible new flags on success -*/ -SWITCH_DECLARE(switch_status_t) switch_core_speech_feed_asr(switch_speech_handle_t *sh, void *data, unsigned int *len, int rate, switch_speech_flag_t *flags); - -/*! - \brief Get text back from the ASR module - \param sh the speech handle to read - \param buf the buffer to insert the text into - \param buflen the max size of the buffer - \param flags flags in/out for fine tuning - \return SWITCH_STATUS_SUCCESS with possible new flags on success -*/ -SWITCH_DECLARE(switch_status_t) switch_core_speech_interpret_asr(switch_speech_handle_t *sh, char *buf, unsigned int buflen, switch_speech_flag_t *flags); - /*! \brief Feed text to the TTS module \param sh the speech handle to feed @@ -1152,6 +1137,92 @@ SWITCH_DECLARE(switch_status_t) switch_core_speech_read_tts(switch_speech_handle \return SWITCH_STATUS_SUCCESS if the file handle was closed */ SWITCH_DECLARE(switch_status_t) switch_core_speech_close(switch_speech_handle_t *sh, switch_speech_flag_t *flags); + + +/*! + \brief Open an asr handle + \param ah the asr handle to open + \param module_name the name of the asr module + \param codec the preferred codec + \param rate the preferred rate + \param dest the destination address + \param flags flags to influence behaviour + \param pool the pool to use (NULL for new pool) + \return SWITCH_STATUS_SUCCESS if the asr handle was opened +*/ +SWITCH_DECLARE(switch_status_t) switch_core_asr_open(switch_asr_handle_t *ah, + char *module_name, + char *codec, + int rate, + char *dest, + switch_asr_flag_t *flags, + switch_memory_pool_t *pool); + +/*! + \brief Close an asr handle + \param ah the handle to close + \param flags flags to influence behaviour + \return SWITCH_STATUS_SUCCESS +*/ +SWITCH_DECLARE(switch_status_t) switch_core_asr_close(switch_asr_handle_t *ah, switch_asr_flag_t *flags); + +/*! + \brief Feed audio data to an asr handle + \param ah the handle to feed data to + \param data a pointer to the data + \param len the size in bytes of the data + \param flags flags to influence behaviour + \return SWITCH_STATUS_SUCCESS +*/ +SWITCH_DECLARE(switch_status_t) switch_core_asr_feed(switch_asr_handle_t *ah, void *data, unsigned int len, switch_asr_flag_t *flags); + +/*! + \brief Check an asr handle for results + \param ah the handle to check + \param flags flags to influence behaviour + \return SWITCH_STATUS_SUCCESS +*/ +SWITCH_DECLARE(switch_status_t) switch_core_asr_check_results(switch_asr_handle_t *ah, switch_asr_flag_t *flags); + +/*! + \brief Get results from an asr handle + \param ah the handle to get results from + \param xmlstr a pointer to dynamically allocate an xml result string to + \param flags flags to influence behaviour + \return SWITCH_STATUS_SUCCESS +*/ +SWITCH_DECLARE(switch_status_t) switch_core_asr_get_results(switch_asr_handle_t *ah, char **xmlstr, switch_asr_flag_t *flags); + +/*! + \brief Load a grammar to an asr handle + \param ah the handle to load to + \param grammar the name of the grammar + \param path the path to the grammaar file + \return SWITCH_STATUS_SUCCESS +*/ +SWITCH_DECLARE(switch_status_t) switch_core_asr_load_grammar(switch_asr_handle_t *ah, char *grammar, char *path); + +/*! + \brief Unload a grammar from an asr handle + \param ah the handle to unload the grammar from + \return SWITCH_STATUS_SUCCESS +*/ +SWITCH_DECLARE(switch_status_t) switch_core_asr_unload_grammar(switch_asr_handle_t *ah, char *grammar); + +/*! + \brief Pause detection on an asr handle + \param ah the handle to pause + \return SWITCH_STATUS_SUCCESS +*/ +SWITCH_DECLARE(switch_status_t) switch_core_asr_pause(switch_asr_handle_t *ah); + +/*! + \brief Resume detection on an asr handle + \param ah the handle to resume + \return SWITCH_STATUS_SUCCESS +*/ +SWITCH_DECLARE(switch_status_t) switch_core_asr_resume(switch_asr_handle_t *ah); + ///\} diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index df52f665cd..c8c23973f4 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -69,12 +69,14 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_park(switch_core_session_t *session); \param dtmf_callback code to execute if any dtmf is dialed during the recording \param buf an object to maintain across calls \param buflen the size of buf + \param timeout a timeout in milliseconds \return SWITCH_STATUS_SUCCESS to keep the collection moving. */ SWITCH_DECLARE(switch_status_t) switch_ivr_collect_digits_callback(switch_core_session_t *session, - switch_input_callback_function_t dtmf_callback, - void *buf, - unsigned int buflen); + switch_input_callback_function_t dtmf_callback, + void *buf, + unsigned int buflen, + unsigned int timeout); /*! \brief Wait for specified number of DTMF digits, untile terminator is received or until the channel hangs up. @@ -95,6 +97,61 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_collect_digits_count(switch_core_sess char *terminator, unsigned int timeout); +/*! + \brief Engage background Speech detection on a session + \param session the session to attach + \param mod_name the module name of the ASR library + \param grammar the grammar name + \param path the path to the grammar file + \param dest the destination address + \param ah an ASR handle to use (NULL to create one) + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech(switch_core_session_t *session, + char *mod_name, + char *grammar, + char *path, + char *dest, + switch_asr_handle_t *ah); + +/*! + \brief Stop background Speech detection on a session + \param session The session to stop detection on + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_stop_detect_speech(switch_core_session_t *session); + +/*! + \brief Pause background Speech detection on a session + \param session The session to pause detection on + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_pause_detect_speech(switch_core_session_t *session); + +/*! + \brief Resume background Speech detection on a session + \param session The session to resume detection on + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_resume_detect_speech(switch_core_session_t *session); + +/*! + \brief Load a grammar on a background speech detection handle + \param session The session to change the grammar on + \param grammar the grammar name + \param path the grammar path + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_load_grammar(switch_core_session_t *session, char *grammar, char *path); + +/*! + \brief Unload a grammar on a background speech detection handle + \param session The session to change the grammar on + \param grammar the grammar name + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_unload_grammar(switch_core_session_t *session, char *grammar); + /*! \brief Record a session to disk \param session the session to record diff --git a/src/include/switch_loadable_module.h b/src/include/switch_loadable_module.h index 2cfbeb4b11..e3d3a7c4e4 100644 --- a/src/include/switch_loadable_module.h +++ b/src/include/switch_loadable_module.h @@ -75,6 +75,8 @@ struct switch_loadable_module_interface { const switch_directory_interface_t *directory_interface; /*! the table of chat interfaces the module has implmented */ const switch_chat_interface_t *chat_interface; + /*! the table of asr interfaces the module has implmented */ + const switch_asr_interface_t *asr_interface; }; /*! @@ -158,6 +160,13 @@ SWITCH_DECLARE(switch_file_interface_t *) switch_loadable_module_get_file_interf */ SWITCH_DECLARE(switch_speech_interface_t *) switch_loadable_module_get_speech_interface(char *name); +/*! + \brief Retrieve the asr interface by it's registered name + \param name the name of the asr interface + \return the desired asr interface + */ +SWITCH_DECLARE(switch_asr_interface_t *) switch_loadable_module_get_asr_interface(char *name); + /*! \brief Retrieve the directory interface by it's registered name \param name the name of the directory interface diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h index 33940c142c..a1541e8895 100644 --- a/src/include/switch_module_interfaces.h +++ b/src/include/switch_module_interfaces.h @@ -315,6 +315,53 @@ struct switch_file_handle { switch_buffer_t *audio_buffer; }; +/*! \brief Abstract interface to an asr module */ +struct switch_asr_interface { + /*! the name of the interface */ + const char *interface_name; + /*! function to open the asr interface */ + switch_status_t (*asr_open)(switch_asr_handle_t *ah, + char *codec, + int rate, + char *dest, + switch_asr_flag_t *flags); + /*! function to load a grammar to the asr interface */ + switch_status_t (*asr_load_grammar)(switch_asr_handle_t *ah, char *grammar, char *path); + /*! function to unload a grammar to the asr interface */ + switch_status_t (*asr_unload_grammar)(switch_asr_handle_t *ah, char *grammar); + /*! function to close the asr interface */ + switch_status_t (*asr_close)(switch_asr_handle_t *ah, switch_asr_flag_t *flags); + /*! function to feed audio to the ASR*/ + switch_status_t (*asr_feed)(switch_asr_handle_t *ah, void *data, unsigned int len, switch_asr_flag_t *flags); + /*! function to resume the ASR*/ + switch_status_t (*asr_resume)(switch_asr_handle_t *ah); + /*! function to pause the ASR*/ + switch_status_t (*asr_pause)(switch_asr_handle_t *ah); + /*! function to read results from the ASR*/ + switch_status_t (*asr_check_results)(switch_asr_handle_t *ah, switch_asr_flag_t *flags); + /*! function to read results from the ASR*/ + switch_status_t (*asr_get_results)(switch_asr_handle_t *ah, char **xmlstr, switch_asr_flag_t *flags); + const struct switch_asr_interface *next; +}; + +/*! an abstract representation of an asr speech interface. */ +struct switch_asr_handle { + /*! the interface of the module that implemented the current speech interface */ + const switch_asr_interface_t *asr_interface; + /*! flags to control behaviour */ + uint32_t flags; + /*! The Name*/ + char *name; + /*! The Codec*/ + char *codec; + /*! The Rate*/ + uint32_t rate; + char *grammar; + /*! the handle's memory pool */ + switch_memory_pool_t *memory_pool; + /*! private data for the format module to store handle specific info */ + void *private_info; +}; /*! \brief Abstract interface to a speech module */ struct switch_speech_interface { @@ -328,10 +375,6 @@ struct switch_speech_interface { /*! function to close the speech interface */ switch_status_t (*speech_close)(switch_speech_handle_t *, switch_speech_flag_t *flags); /*! function to feed audio to the ASR*/ - switch_status_t (*speech_feed_asr)(switch_speech_handle_t *sh, void *data, unsigned int *len, int rate, switch_speech_flag_t *flags); - /*! function to read text from the ASR*/ - switch_status_t (*speech_interpret_asr)(switch_speech_handle_t *sh, char *buf, unsigned int buflen, switch_speech_flag_t *flags); - /*! function to feed text to the TTS*/ switch_status_t (*speech_feed_tts)(switch_speech_handle_t *sh, char *text, switch_speech_flag_t *flags); /*! function to read audio from the TTS*/ switch_status_t (*speech_read_tts)(switch_speech_handle_t *sh, diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 637ced38f1..02fb237229 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -72,6 +72,10 @@ SWITCH_BEGIN_EXTERN_C #define SWITCH_HTDOCS_DIR SWITCH_PREFIX_DIR SWITCH_PATH_SEPARATOR "htdocs" #endif +#ifndef SWITCH_GRAMMAR_DIR +#define SWITCH_GRAMMAR_DIR SWITCH_PREFIX_DIR SWITCH_PATH_SEPARATOR "grammar" +#endif + #define SWITCH_R_SDP_VARIABLE "_switch_r_sdp_" #define SWITCH_L_SDP_VARIABLE "_switch_l_sdp_" #define SWITCH_B_SDP_VARIABLE "_switch_m_sdp_" @@ -82,7 +86,7 @@ SWITCH_BEGIN_EXTERN_C #define SWITCH_LOCAL_MEDIA_PORT_VARIABLE "_local_media_port_" #define SWITCH_REMOTE_MEDIA_IP_VARIABLE "_remote_media_ip_" #define SWITCH_REMOTE_MEDIA_PORT_VARIABLE "_remote_media_port_" - +#define SWITCH_SPEECH_KEY "_speech_" #define SWITCH_BITS_PER_BYTE 8 typedef uint8_t switch_byte_t; @@ -132,6 +136,7 @@ struct switch_directories { char *script_dir; char *temp_dir; char *htdocs_dir; + char *grammar_dir; }; typedef struct switch_directories switch_directories; @@ -313,6 +318,7 @@ typedef enum { SWITCH_STATUS_SOCKERR - A socket error SWITCH_STATUS_MORE_DATA - Need More Data SWITCH_STATUS_NOTFOUND - Not Found + SWITCH_STATUS_UNLOAD - Unload */ typedef enum { @@ -330,7 +336,8 @@ typedef enum { SWITCH_STATUS_BREAK, SWITCH_STATUS_SOCKERR, SWITCH_STATUS_MORE_DATA, - SWITCH_STATUS_NOTFOUND + SWITCH_STATUS_NOTFOUND, + SWITCH_STATUS_UNLOAD } switch_status_t; @@ -522,26 +529,42 @@ typedef enum { \enum switch_speech_flag_t \brief Speech related flags
-SWITCH_SPEECH_FLAG_TTS =			(1 <<  0) - Interface can/should convert text to speech.
-SWITCH_SPEECH_FLAG_ASR =			(1 <<  1) - Interface can/should convert audio to text.
-SWITCH_SPEECH_FLAG_HASTEXT =		(1 <<  2) - Interface is has text to read.
-SWITCH_SPEECH_FLAG_PEEK =			(1 <<  3) - Read data but do not erase it.
-SWITCH_SPEECH_FLAG_FREE_POOL =		(1 <<  4) - Free interface's pool on destruction.
-SWITCH_SPEECH_FLAG_BLOCKING =       (1 <<  5) - Indicate that a blocking call is desired 
-SWITCH_SPEECH_FLAG_PAUSE = 			(1 <<  6) - Pause toggle for playback
+SWITCH_SPEECH_FLAG_HASTEXT =		(1 <<  0) - Interface is has text to read.
+SWITCH_SPEECH_FLAG_PEEK =			(1 <<  1) - Read data but do not erase it.
+SWITCH_SPEECH_FLAG_FREE_POOL =		(1 <<  2) - Free interface's pool on destruction.
+SWITCH_SPEECH_FLAG_BLOCKING =       (1 <<  3) - Indicate that a blocking call is desired 
+SWITCH_SPEECH_FLAG_PAUSE = 			(1 <<  4) - Pause toggle for playback
 
*/ typedef enum { - SWITCH_SPEECH_FLAG_TTS = (1 << 0), - SWITCH_SPEECH_FLAG_ASR = (1 << 1), - SWITCH_SPEECH_FLAG_HASTEXT = (1 << 2), - SWITCH_SPEECH_FLAG_PEEK = (1 << 3), - SWITCH_SPEECH_FLAG_FREE_POOL = (1 << 4), - SWITCH_SPEECH_FLAG_BLOCKING = (1 << 5), - SWITCH_SPEECH_FLAG_PAUSE = (1 << 6) + SWITCH_SPEECH_FLAG_NONE = 0, + SWITCH_SPEECH_FLAG_HASTEXT = (1 << 0), + SWITCH_SPEECH_FLAG_PEEK = (1 << 1), + SWITCH_SPEECH_FLAG_FREE_POOL = (1 << 2), + SWITCH_SPEECH_FLAG_BLOCKING = (1 << 3), + SWITCH_SPEECH_FLAG_PAUSE = (1 << 4) } switch_speech_flag_t; +/*! + \enum switch_asr_flag_t + \brief Asr related flags +
+SWITCH_ASR_FLAG_DATA =			(1 <<  0) - Interface has data
+SWITCH_ASR_FLAG_FREE_POOL =		(1 <<  1) - Pool needs to be freed
+SWITCH_ASR_FLAG_CLOSED = 		(1 <<  2) - Interface has been closed
+SWITCH_ASR_FLAG_FIRE_EVENTS =	(1 <<  3) - Fire all speech events
+SWITCH_ASR_FLAG_AUTO_RESUME =   (1 <<  4) - Auto Resume
+
+*/ +typedef enum { + SWITCH_ASR_FLAG_NONE = 0, + SWITCH_ASR_FLAG_DATA = (1 << 0), + SWITCH_ASR_FLAG_FREE_POOL = (1 << 1), + SWITCH_ASR_FLAG_CLOSED = (1 << 2), + SWITCH_ASR_FLAG_FIRE_EVENTS = (1 << 3), + SWITCH_ASR_FLAG_AUTO_RESUME = (1 << 4) +} switch_asr_flag_t; /*! \enum switch_directory_flag_t @@ -584,6 +607,21 @@ typedef enum { SWITCH_TIMER_FLAG_FREE_POOL = (1 << 0), } switch_timer_flag_t; + +/*! + \enum switch_timer_flag_t + \brief Timer related flags +
+SMBF_READ_STREAM - Include the Read Stream
+SMBF_WRITE_STREAM - Include the Write Stream
+
+*/ +typedef enum { + SMBF_BOTH = 0, + SMBF_READ_STREAM = (1 << 0), + SMBF_WRITE_STREAM = (1 << 1) +} switch_media_bug_flag_t; + /*! \enum switch_file_flag_t \brief File flags @@ -653,6 +691,7 @@ typedef enum { SWITCH_EVENT_PRESENCE - Presence Info SWITCH_EVENT_CODEC - Codec Change SWITCH_EVENT_BACKGROUND_JOB - Background Job + SWITCH_EVENT_DETECTED_SPEECH - Detected Speech SWITCH_EVENT_ALL - All events at once @@ -690,6 +729,7 @@ typedef enum { SWITCH_EVENT_ROSTER, SWITCH_EVENT_CODEC, SWITCH_EVENT_BACKGROUND_JOB, + SWITCH_EVENT_DETECTED_SPEECH, SWITCH_EVENT_ALL } switch_event_types_t; @@ -800,10 +840,9 @@ typedef struct switch_io_event_hook_waitfor_write switch_io_event_hook_waitfor_w typedef struct switch_io_event_hook_send_dtmf switch_io_event_hook_send_dtmf_t; typedef struct switch_io_routines switch_io_routines_t; typedef struct switch_io_event_hooks switch_io_event_hooks_t; - typedef struct switch_speech_handle switch_speech_handle_t; +typedef struct switch_asr_handle switch_asr_handle_t; typedef struct switch_directory_handle switch_directory_handle_t; - typedef struct switch_loadable_module_interface switch_loadable_module_interface_t; typedef struct switch_endpoint_interface switch_endpoint_interface_t; typedef struct switch_timer_interface switch_timer_interface_t; @@ -813,6 +852,7 @@ typedef struct switch_application_interface switch_application_interface_t; typedef struct switch_api_interface switch_api_interface_t; typedef struct switch_file_interface switch_file_interface_t; typedef struct switch_speech_interface switch_speech_interface_t; +typedef struct switch_asr_interface switch_asr_interface_t; typedef struct switch_directory_interface switch_directory_interface_t; typedef struct switch_chat_interface switch_chat_interface_t; typedef struct switch_core_port_allocator switch_core_port_allocator_t; diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index 4985501290..42b127baf1 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -393,10 +393,12 @@ static switch_status_t originate_function(char *cmd, switch_core_session_t *ises switch_channel_t *caller_channel; switch_core_session_t *caller_session; char *argv[7] = {0}; - int x, argc = 0; + int i = 0, x, argc = 0; char *aleg, *exten, *dp, *context, *cid_name, *cid_num; uint32_t timeout = 60; switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING; + uint8_t machine = 1; + if (isession) { stream->write_function(stream, "Illegal Usage\n"); return SWITCH_STATUS_SUCCESS; @@ -414,13 +416,18 @@ static switch_status_t originate_function(char *cmd, switch_core_session_t *ises argv[x] = NULL; } } + + if (!strcasecmp(argv[0], "machine")) { + machine = 1; + i++; + } - aleg = argv[0]; - exten = argv[1]; - dp = argv[2]; - context = argv[3]; - cid_name = argv[4]; - cid_num = argv[5]; + aleg = argv[i++]; + exten = argv[i++]; + dp = argv[i++]; + context = argv[i++]; + cid_name = argv[i++]; + cid_num = argv[i++]; if (!dp) { dp = "XML"; @@ -435,7 +442,11 @@ static switch_status_t originate_function(char *cmd, switch_core_session_t *ises } if (switch_ivr_originate(NULL, &caller_session, &cause, aleg, timeout, &noop_state_handler, cid_name, cid_num, NULL) != SWITCH_STATUS_SUCCESS) { - stream->write_function(stream, "Cannot Create Outgoing Channel! [%s]\n", aleg); + if (machine) { + stream->write_function(stream, "fail: %s", switch_channel_cause2str(cause)); + } else { + stream->write_function(stream, "Cannot Create Outgoing Channel! [%s] cause: %s\n", aleg, switch_channel_cause2str(cause)); + } return SWITCH_STATUS_SUCCESS; } @@ -467,7 +478,13 @@ static switch_status_t originate_function(char *cmd, switch_core_session_t *ises } else { switch_ivr_session_transfer(caller_session, exten, dp, context); } - stream->write_function(stream, "Created Session: %s\n", switch_core_session_get_uuid(caller_session)); + + if (machine) { + stream->write_function(stream, "success: %s\n", switch_core_session_get_uuid(caller_session)); + } else { + stream->write_function(stream, "Created Session: %s\n", switch_core_session_get_uuid(caller_session)); + } + return SWITCH_STATUS_SUCCESS;; } diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 9b9b14f2ed..5161957515 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -666,7 +666,7 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v switch_memory_pool_t *pool; if (conference->fnode->type == NODE_TYPE_SPEECH) { - switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_TTS; + switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE; switch_core_speech_close(&conference->fnode->sh, &flags); } else { switch_core_file_close(&conference->fnode->fh); @@ -957,7 +957,7 @@ static void conference_loop(conference_member_t *member) switch_memory_pool_t *pool; if (member->fnode->type == NODE_TYPE_SPEECH) { - switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_TTS; + switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE; switch_core_speech_close(&member->fnode->sh, &flags); } else { switch_core_file_close(&member->fnode->fh); @@ -1340,7 +1340,7 @@ static switch_status_t conference_member_say(conference_obj_t *conference, confe { confernce_file_node_t *fnode, *nptr; switch_memory_pool_t *pool; - switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_TTS; + switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE; if (!(conference->tts_engine && conference->tts_voice)) { return SWITCH_STATUS_SUCCESS; @@ -1399,7 +1399,7 @@ static switch_status_t conference_say(conference_obj_t *conference, char *text, { confernce_file_node_t *fnode, *nptr; switch_memory_pool_t *pool; - switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_TTS; + switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE; uint32_t count; switch_mutex_lock(conference->mutex); diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index 5bb8d5726a..0bdd81aaac 100644 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -34,6 +34,33 @@ static const char modname[] = "mod_dptools"; +static const switch_application_interface_t detect_speech_application_interface; + +static void detect_speech_function(switch_core_session_t *session, char *data) +{ + char *argv[4]; + int argc; + char *lbuf = NULL; + + if ((lbuf = switch_core_session_strdup(session, data)) && (argc = switch_separate_string(lbuf, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) { + if (!strcasecmp(argv[0], "grammar") && argc >= 1) { + switch_ivr_detect_speech_load_grammar(session, argv[1], argv[2]); + } else if (!strcasecmp(argv[0], "nogrammar")) { + switch_ivr_detect_speech_unload_grammar(session, argv[1]); + } else if (!strcasecmp(argv[0], "pause")) { + switch_ivr_pause_detect_speech(session); + } else if (!strcasecmp(argv[0], "resume")) { + switch_ivr_resume_detect_speech(session); + } else if (!strcasecmp(argv[0], "stop")) { + switch_ivr_stop_detect_speech(session); + } else if (argc >= 3) { + switch_ivr_detect_speech(session, argv[0], argv[1], argv[2], argv[3], NULL); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Usage: %s\n", detect_speech_application_interface.syntax); + } + +} static void ringback_function(switch_core_session_t *session, char *data) { @@ -253,7 +280,6 @@ static switch_api_interface_t dptools_api_interface = { /*.next */ &chat_api_interface }; - static switch_api_interface_t presence_api_interface = { /*.interface_name */ "presence", /*.desc */ "presence", @@ -262,6 +288,14 @@ static switch_api_interface_t presence_api_interface = { /*.next */ &dptools_api_interface }; +static const switch_application_interface_t detect_speech_application_interface = { + /*.interface_name */ "detect_speech", + /*.application_function */ detect_speech_function, + /* long_desc */ "Detect speech on a channel.", + /* short_desc */ "Detect speech", + /* syntax */ " [] OR grammar [] OR pause OR resume", + /*.next */ NULL +}; static const switch_application_interface_t ringback_application_interface = { /*.interface_name */ "ringback", @@ -269,8 +303,7 @@ static const switch_application_interface_t ringback_application_interface = { /* long_desc */ "Indicate Ringback on a channel.", /* short_desc */ "Indicate Ringback", /* syntax */ "", - /*.next */ NULL - + /*.next */ &detect_speech_application_interface }; static const switch_application_interface_t set_application_interface = { diff --git a/src/mod/applications/mod_ivrtest/mod_ivrtest.c b/src/mod/applications/mod_ivrtest/mod_ivrtest.c index 1ea827bd90..03b7abec92 100644 --- a/src/mod/applications/mod_ivrtest/mod_ivrtest.c +++ b/src/mod/applications/mod_ivrtest/mod_ivrtest.c @@ -167,6 +167,123 @@ static void tts_function(switch_core_session_t *session, char *data) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Done\n"); } +#if 1 +static void asrtest_function(switch_core_session_t *session, char *data) +{ + switch_ivr_detect_speech(session, + "lumenvox", + "demo", + data, + "127.0.0.1", + NULL); +} + +#else +static void asrtest_function(switch_core_session_t *session, char *data) +{ + switch_asr_handle_t ah = {0}; + switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; + switch_channel_t *channel = switch_core_session_get_channel(session); + char *codec_name = "L16"; + switch_codec_t codec = {0}, *read_codec; + switch_frame_t write_frame = {0}, *write_frame_p = NULL; + char xdata[1024] = ""; + + read_codec = switch_core_session_get_read_codec(session); + assert(read_codec != NULL); + + + if (switch_core_asr_open(&ah, "lumenvox", + read_codec->implementation->iananame, + 8000, + "127.0.0.1", + &flags, + switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) { + if (strcmp(ah.codec, read_codec->implementation->iananame)) { + if (switch_core_codec_init(&codec, + ah.codec, + NULL, + ah.rate, + read_codec->implementation->microseconds_per_frame / 1000, + read_codec->implementation->number_of_channels, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, + NULL, switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Raw Codec Activated\n"); + switch_core_session_set_read_codec(session, &codec); + write_frame.data = xdata; + write_frame.buflen = sizeof(xdata); + write_frame.codec = &codec; + write_frame_p = &write_frame; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec Activation Failed %s@%uhz %u channels %dms\n", + codec_name, read_codec->implementation->samples_per_second, read_codec->implementation->number_of_channels, + read_codec->implementation->microseconds_per_frame / 1000); + switch_core_session_reset(session); + return; + } + } + + + if (switch_core_asr_load_grammar(&ah, "demo", "/opt/lumenvox/engine_7.0/Lang/BuiltinGrammars/ABNFPhone.gram") != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Error loading Grammar\n"); + goto end; + } + + while(switch_channel_ready(channel)) { + switch_frame_t *read_frame; + switch_status_t status = switch_core_session_read_frame(session, &read_frame, -1, 0); + char *xmlstr = NULL; + switch_xml_t xml = NULL, result; + + if (!SWITCH_READ_ACCEPTABLE(status)) { + break; + } + + if (switch_test_flag(read_frame, SFF_CNG)) { + continue; + } + + if (switch_core_asr_feed(&ah, read_frame->data, read_frame->datalen, &flags) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Error Feeding Data\n"); + break; + } + + if (switch_core_asr_check_results(&ah, &flags) == SWITCH_STATUS_SUCCESS) { + if (switch_core_asr_get_results(&ah, &xmlstr, &flags) != SWITCH_STATUS_SUCCESS) { + break; + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "RAW XML\n========\n%s\n", xmlstr); + + if ((xml = switch_xml_parse_str(xmlstr, strlen(xmlstr))) && (result = switch_xml_child(xml, "result"))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Results [%s]\n", result->txt); + switch_xml_free(xml); + } + switch_safe_free(xmlstr); + } + + if (write_frame_p) { + write_frame.datalen = read_frame->datalen; + switch_core_session_write_frame(session, write_frame_p, -1, 0); + } else { + memset(read_frame->data, 0, read_frame->datalen); + switch_core_session_write_frame(session, read_frame, -1, 0); + } + } + + end: + if (write_frame_p) { + switch_core_session_set_read_codec(session, read_codec); + switch_core_codec_destroy(&codec); + } + + switch_core_asr_close(&ah, &flags); + switch_core_session_reset(session); + } + +} + +#endif + static void ivrtest_function(switch_core_session_t *session, char *data) { switch_channel_t *channel; @@ -272,13 +389,20 @@ static const switch_application_interface_t ivrtest_application_interface = { /*.next*/ &dirtest_application_interface }; +static const switch_application_interface_t asrtest_application_interface = { + /*.interface_name */ "asrtest", + /*.application_function */ asrtest_function, + NULL, NULL, NULL, + /*.next*/ &ivrtest_application_interface +}; + static const switch_loadable_module_interface_t mod_ivrtest_module_interface = { /*.module_name = */ modname, /*.endpoint_interface = */ NULL, /*.timer_interface = */ NULL, /*.dialplan_interface = */ NULL, /*.codec_interface = */ NULL, - /*.application_interface */ &ivrtest_application_interface + /*.application_interface */ &asrtest_application_interface }; SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_module_interface_t **module_interface, char *filename) diff --git a/src/mod/applications/mod_rss/mod_rss.c b/src/mod/applications/mod_rss/mod_rss.c index d71e1c8187..ccfb3323bc 100644 --- a/src/mod/applications/mod_rss/mod_rss.c +++ b/src/mod/applications/mod_rss/mod_rss.c @@ -169,7 +169,7 @@ static void rss_function(switch_core_session_t *session, char *data) char *voice = TTS_DEFAULT_VOICE; char *timer_name = NULL; switch_speech_handle_t sh; - switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_TTS; + switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE; switch_core_thread_session_t thread_session; uint32_t rate, interval = 20; int stream_id = 0; diff --git a/src/mod/asr_tts/mod_cepstral/mod_cepstral.c b/src/mod/asr_tts/mod_cepstral/mod_cepstral.c index 2814da34d9..573a235988 100644 --- a/src/mod/asr_tts/mod_cepstral/mod_cepstral.c +++ b/src/mod/asr_tts/mod_cepstral/mod_cepstral.c @@ -109,70 +109,65 @@ static swift_result_t write_audio(swift_event *event, swift_event_t type, void * static switch_status_t cepstral_speech_open(switch_speech_handle_t *sh, char *voice_name, int rate, switch_speech_flag_t *flags) { - if (*flags & SWITCH_SPEECH_FLAG_ASR) { - return SWITCH_STATUS_FALSE; + cepstral_t *cepstral = switch_core_alloc(sh->memory_pool, sizeof(*cepstral)); + char srate[25]; + + if (!cepstral) { + return SWITCH_STATUS_MEMERR; } - if (*flags & SWITCH_SPEECH_FLAG_TTS) { - cepstral_t *cepstral = switch_core_alloc(sh->memory_pool, sizeof(*cepstral)); - char srate[25]; - if (!cepstral) { - return SWITCH_STATUS_MEMERR; - } - - if (switch_buffer_create_dynamic(&cepstral->audio_buffer, MY_BLOCK_SIZE, MY_BUF_LEN, 0) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Write Buffer Failed!\n"); - return SWITCH_STATUS_MEMERR; - } + if (switch_buffer_create_dynamic(&cepstral->audio_buffer, MY_BLOCK_SIZE, MY_BUF_LEN, 0) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Write Buffer Failed!\n"); + return SWITCH_STATUS_MEMERR; + } - switch_mutex_init(&cepstral->audio_lock, SWITCH_MUTEX_NESTED, sh->memory_pool); + switch_mutex_init(&cepstral->audio_lock, SWITCH_MUTEX_NESTED, sh->memory_pool); - cepstral->params = swift_params_new(NULL); - swift_params_set_string(cepstral->params, "audio/encoding", "pcm16"); - snprintf(srate, sizeof(srate), "%d", rate); - swift_params_set_string(cepstral->params, "audio/sampling-rate", srate); + cepstral->params = swift_params_new(NULL); + swift_params_set_string(cepstral->params, "audio/encoding", "pcm16"); + snprintf(srate, sizeof(srate), "%d", rate); + swift_params_set_string(cepstral->params, "audio/sampling-rate", srate); - /* Open a Swift Port through which to make TTS calls */ - if (SWIFT_FAILED(cepstral->port = swift_port_open(engine, cepstral->params))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open Swift Port."); + /* Open a Swift Port through which to make TTS calls */ + if (SWIFT_FAILED(cepstral->port = swift_port_open(engine, cepstral->params))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open Swift Port."); + goto all_done; + } + + + if (voice_name && SWIFT_FAILED(swift_port_set_voice_by_name(cepstral->port, voice_name))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid voice %s!\n", voice_name); + voice_name = NULL; + } + + if (!voice_name) { + /* Find the first voice on the system */ + if ((cepstral->voice = swift_port_find_first_voice(cepstral->port, NULL, NULL)) == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to find any voices!\n"); goto all_done; } - - if (voice_name && SWIFT_FAILED(swift_port_set_voice_by_name(cepstral->port, voice_name))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid voice %s!\n", voice_name); - voice_name = NULL; + /* Set the voice found by find_first_voice() as the port's current voice */ + if ( SWIFT_FAILED(swift_port_set_voice(cepstral->port, cepstral->voice)) ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to set voice.\n"); + goto all_done; } - if (!voice_name) { - /* Find the first voice on the system */ - if ((cepstral->voice = swift_port_find_first_voice(cepstral->port, NULL, NULL)) == NULL) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to find any voices!\n"); - goto all_done; - } - - /* Set the voice found by find_first_voice() as the port's current voice */ - if ( SWIFT_FAILED(swift_port_set_voice(cepstral->port, cepstral->voice)) ) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to set voice.\n"); - goto all_done; - } - - voice_name = (char *) swift_voice_get_attribute(cepstral->voice, "name"); - } - - if (voice_name) { - switch_copy_string(sh->voice, voice_name, sizeof(sh->voice)); - } - - swift_port_set_callback(cepstral->port, &write_audio, SWIFT_EVENT_AUDIO, cepstral); - - sh->private_info = cepstral; - return SWITCH_STATUS_SUCCESS; + voice_name = (char *) swift_voice_get_attribute(cepstral->voice, "name"); } + if (voice_name) { + switch_copy_string(sh->voice, voice_name, sizeof(sh->voice)); + } + + swift_port_set_callback(cepstral->port, &write_audio, SWIFT_EVENT_AUDIO, cepstral); + + sh->private_info = cepstral; + return SWITCH_STATUS_SUCCESS; + all_done: return SWITCH_STATUS_FALSE; } @@ -227,7 +222,7 @@ static switch_status_t cepstral_speech_feed_tts(switch_speech_handle_t *sh, char return SWITCH_STATUS_FALSE; } - swift_port_speak_text(cepstral->port, "", 0, NULL, &cepstral->tts_stream, NULL); + swift_port_speak_text(cepstral->port, "", 0, NULL, &cepstral->tts_stream, NULL); swift_port_speak_text(cepstral->port, text, 0, NULL, &cepstral->tts_stream, NULL); } @@ -252,10 +247,10 @@ static void cepstral_speech_flush_tts(switch_speech_handle_t *sh) } static switch_status_t cepstral_speech_read_tts(switch_speech_handle_t *sh, - void *data, - size_t *datalen, - uint32_t *rate, - switch_speech_flag_t *flags) + void *data, + size_t *datalen, + uint32_t *rate, + switch_speech_flag_t *flags) { cepstral_t *cepstral; size_t desired = *datalen; @@ -404,8 +399,6 @@ static const switch_speech_interface_t cepstral_speech_interface = { /*.interface_name*/ "cepstral", /*.speech_open*/ cepstral_speech_open, /*.speech_close*/ cepstral_speech_close, - /*.speech_feed_asr*/ NULL, - /*.speech_interpret_asr*/ NULL, /*.speech_feed_tts*/ cepstral_speech_feed_tts, /*.speech_read_tts*/ cepstral_speech_read_tts, /*.speech_flush_tts*/ cepstral_speech_flush_tts, diff --git a/src/mod/event_handlers/mod_event_socket/mod_event_socket.c b/src/mod/event_handlers/mod_event_socket/mod_event_socket.c index e8bedd3b6b..a56e14931c 100644 --- a/src/mod/event_handlers/mod_event_socket/mod_event_socket.c +++ b/src/mod/event_handlers/mod_event_socket/mod_event_socket.c @@ -77,6 +77,7 @@ static struct { char *ip; uint16_t port; char *password; + int done; } prefs; SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_pref_ip, prefs.ip) @@ -166,6 +167,8 @@ SWITCH_MOD_DECLARE(switch_status_t) switch_module_shutdown(void) { listener_t *l; + prefs.done = 1; + close_socket(&listen_list.sock); switch_mutex_lock(listen_list.mutex); @@ -1003,7 +1006,11 @@ SWITCH_MOD_DECLARE(switch_status_t) switch_module_runtime(void) } if ((rv = switch_socket_accept(&inbound_socket, listen_list.sock, listener_pool))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Socket Error\n"); + if (prefs.done) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Shutting Down\n"); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Socket Error\n"); + } break; } diff --git a/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c b/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c index bcacacc570..1cf92206ea 100644 --- a/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c +++ b/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c @@ -97,13 +97,18 @@ typedef enum { S_HUP = (1 << 0) } session_flag_t; -struct dtmf_callback_state { +struct input_callback_state { struct js_session *session_state; char code_buffer[1024]; size_t code_buffer_len; char ret_buffer[1024]; int ret_buffer_len; int digit_count; + JSFunction *function; + jsval arg; + jsval ret; + JSContext *cx; + JSObject *obj; void *extra; }; @@ -125,8 +130,9 @@ struct teletone_obj { switch_memory_pool_t *pool; switch_timer_t *timer; switch_timer_t timer_base; - char code_buffer[1024]; - char ret_val[1024]; + JSFunction *function; + jsval arg; + jsval ret; unsigned int flags; }; @@ -141,52 +147,65 @@ struct fileio_obj { }; struct db_obj { - switch_memory_pool_t *pool; - switch_core_db_t *db; - switch_core_db_stmt_t *stmt; - char *dbname; - char code_buffer[2048]; - JSContext *cx; - JSObject *obj; + switch_memory_pool_t *pool; + switch_core_db_t *db; + switch_core_db_stmt_t *stmt; + char *dbname; + char code_buffer[2048]; + JSContext *cx; + JSObject *obj; }; + +struct event_obj { + switch_event_t *event; + int freed; +}; + /* Event Object */ /*********************************************************************************/ static JSBool event_construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (argc > 0) { switch_event_t *event; + struct event_obj *eo; switch_event_types_t etype; char *ename = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); - if (switch_name_event(ename, &etype) != SWITCH_STATUS_SUCCESS) { - *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); + if ((eo = malloc(sizeof(*eo)))) { + + if (switch_name_event(ename, &etype) != SWITCH_STATUS_SUCCESS) { + *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); + return JS_TRUE; + } + + if (etype == SWITCH_EVENT_CUSTOM) { + char *subclass_name; + if (argc < 1) { + *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); + return JS_TRUE; + } + + subclass_name = JS_GetStringBytes(JS_ValueToString(cx, argv[1])); + if (switch_event_create_subclass(&event, etype, subclass_name) != SWITCH_STATUS_SUCCESS) { + *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); + return JS_TRUE; + } + + } else { + if (!switch_event_create(&event, etype) != SWITCH_STATUS_SUCCESS) { + *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); + return JS_TRUE; + } + } + + eo->event = event; + eo->freed = 0; + + JS_SetPrivate(cx, obj, eo); return JS_TRUE; } - - if (etype == SWITCH_EVENT_CUSTOM) { - char *subclass_name; - if (argc < 1) { - *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); - return JS_TRUE; - } - - subclass_name = JS_GetStringBytes(JS_ValueToString(cx, argv[1])); - if (switch_event_create_subclass(&event, etype, subclass_name) != SWITCH_STATUS_SUCCESS) { - *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); - return JS_TRUE; - } - - } else { - if (!switch_event_create(&event, etype) != SWITCH_STATUS_SUCCESS) { - *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); - return JS_TRUE; - } - } - - JS_SetPrivate(cx, obj, event); - return JS_TRUE; } return JS_FALSE; @@ -194,18 +213,21 @@ static JSBool event_construct(JSContext *cx, JSObject *obj, uintN argc, jsval *a static void event_destroy(JSContext *cx, JSObject *obj) { - switch_event_t *event = JS_GetPrivate(cx, obj); + struct event_obj *eo = JS_GetPrivate(cx, obj); - if (event) { - switch_event_destroy(&event); + if (eo) { + if (!eo->freed && eo->event) { + switch_event_destroy(&eo->event); + } + switch_safe_free(eo); } } static JSBool event_add_header(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - switch_event_t *event = JS_GetPrivate(cx, obj); + struct event_obj *eo = JS_GetPrivate(cx, obj); - if (!event) { + if (!eo || eo->freed) { *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); return JS_TRUE; } @@ -213,7 +235,7 @@ static JSBool event_add_header(JSContext *cx, JSObject *obj, uintN argc, jsval * if (argc > 1) { char *hname = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); char *hval = JS_GetStringBytes(JS_ValueToString(cx, argv[1])); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, hname, hval); + switch_event_add_header(eo->event, SWITCH_STACK_BOTTOM, hname, hval); *rval = BOOLEAN_TO_JSVAL( JS_TRUE ); return JS_TRUE; } @@ -224,16 +246,16 @@ static JSBool event_add_header(JSContext *cx, JSObject *obj, uintN argc, jsval * static JSBool event_get_header(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - switch_event_t *event = JS_GetPrivate(cx, obj); + struct event_obj *eo = JS_GetPrivate(cx, obj); - if (!event) { + if (!eo) { *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); return JS_TRUE; } if (argc > 0) { char *hname = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); - char *val = switch_event_get_header(event, hname); + char *val = switch_event_get_header(eo->event, hname); *rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, val)); return JS_TRUE; } @@ -244,16 +266,16 @@ static JSBool event_get_header(JSContext *cx, JSObject *obj, uintN argc, jsval * static JSBool event_add_body(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - switch_event_t *event = JS_GetPrivate(cx, obj); + struct event_obj *eo = JS_GetPrivate(cx, obj); - if (!event) { + if (!eo || eo->freed) { *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); return JS_TRUE; } if (argc > 0) { char *body = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); - switch_event_add_body(event, body); + switch_event_add_body(eo->event, body); *rval = BOOLEAN_TO_JSVAL( JS_TRUE ); return JS_TRUE; } @@ -264,25 +286,39 @@ static JSBool event_add_body(JSContext *cx, JSObject *obj, uintN argc, jsval *ar static JSBool event_get_body(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - switch_event_t *event = JS_GetPrivate(cx, obj); + struct event_obj *eo = JS_GetPrivate(cx, obj); - if (!event) { + if (!eo) { *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); return JS_TRUE; } - *rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, switch_event_get_body(event))); + *rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, switch_event_get_body(eo->event))); return JS_TRUE; } +static JSBool event_get_type(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + struct event_obj *eo = JS_GetPrivate(cx, obj); + + if (!eo) { + *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); + return JS_TRUE; + } + + *rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, switch_event_name(eo->event->event_id))); + + return JS_TRUE; +} + static JSBool event_serialize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - switch_event_t *event = JS_GetPrivate(cx, obj); + struct event_obj *eo = JS_GetPrivate(cx, obj); char buf[1024]; uint8_t isxml = 0; - if (!event) { + if (!eo) { *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); return JS_TRUE; } @@ -297,7 +333,7 @@ static JSBool event_serialize(JSContext *cx, JSObject *obj, uintN argc, jsval *a if (isxml) { switch_xml_t xml; char *xmlstr; - if ((xml = switch_event_xmlize(event, NULL))) { + if ((xml = switch_event_xmlize(eo->event, NULL))) { xmlstr = switch_xml_toxml(xml); *rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, xmlstr)); switch_xml_free(xml); @@ -306,7 +342,7 @@ static JSBool event_serialize(JSContext *cx, JSObject *obj, uintN argc, jsval *a *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); } } else { - switch_event_serialize(event, buf, sizeof(buf), NULL); + switch_event_serialize(eo->event, buf, sizeof(buf), NULL); *rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, buf)); } @@ -315,11 +351,12 @@ static JSBool event_serialize(JSContext *cx, JSObject *obj, uintN argc, jsval *a static JSBool event_fire(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - switch_event_t *event = JS_GetPrivate(cx, obj); + struct event_obj *eo = JS_GetPrivate(cx, obj); - if (event) { - switch_event_fire(&event); + if (eo) { + switch_event_fire(&eo->event); JS_SetPrivate(cx, obj, NULL); + switch_safe_free(eo); *rval = BOOLEAN_TO_JSVAL( JS_TRUE ); return JS_TRUE; } @@ -330,11 +367,14 @@ static JSBool event_fire(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static JSBool event_destroy_(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - switch_event_t *event = JS_GetPrivate(cx, obj); - - if (event) { - switch_event_destroy(&event); + struct event_obj *eo = JS_GetPrivate(cx, obj); + + if (eo) { + if (!eo->freed) { + switch_event_destroy(&eo->event); + } JS_SetPrivate(cx, obj, NULL); + switch_safe_free(eo); *rval = BOOLEAN_TO_JSVAL( JS_TRUE ); return JS_TRUE; } @@ -354,6 +394,7 @@ static JSFunctionSpec event_methods[] = { {"getHeader", event_get_header, 1}, {"addBody", event_add_body, 1}, {"getBody", event_get_body, 1}, + {"getType", event_get_type, 1}, {"serialize", event_serialize, 0}, {"fire", event_fire, 0}, {"destroy", event_destroy_, 0}, @@ -410,7 +451,8 @@ JSClass event_class = { static void js_error(JSContext *cx, const char *message, JSErrorReport *report) { if (message) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s\n", message); + switch_log_printf(SWITCH_CHANNEL_ID_LOG, (char *)report->filename, modname, report->lineno, SWITCH_LOG_ERROR, + "%s %s%s\n", message, report->linebuf ? "near " : "", report->linebuf ? report->linebuf : ""); } } @@ -433,247 +475,204 @@ static switch_status_t init_js(void) return SWITCH_STATUS_SUCCESS; } -static switch_status_t js_stream_dtmf_callback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen) +JSObject *new_js_event(switch_event_t *event, char *name, JSContext *cx, JSObject *obj) { - switch (itype) { - case SWITCH_INPUT_TYPE_DTMF: { - char *dtmf = (char *) input; - char code[2048]; - struct dtmf_callback_state *cb_state = buf; - struct js_session *jss = cb_state->session_state; - switch_file_handle_t *fh = cb_state->extra; - jsval rval; - char *ret; - - if (!jss) { - return SWITCH_STATUS_FALSE; - } - - if (cb_state->digit_count || (cb_state->code_buffer[0] > 47 && cb_state->code_buffer[0] < 58)) { - char *d; - if (!cb_state->digit_count) { - cb_state->digit_count = atoi(cb_state->code_buffer); - } + struct event_obj *eo; + JSObject *Event = NULL; - for(d = dtmf; *d; d++) { - cb_state->ret_buffer[cb_state->ret_buffer_len++] = *d; - if ((cb_state->ret_buffer_len > cb_state->digit_count)|| - (cb_state->ret_buffer_len > (int32_t) sizeof(cb_state->ret_buffer))|| - (cb_state->ret_buffer_len >= cb_state->digit_count) - ) { - return SWITCH_STATUS_FALSE; - } - } - return SWITCH_STATUS_SUCCESS; - } else { - snprintf(code, sizeof(code), "~%s(\"%s\")", cb_state->code_buffer, dtmf); - eval_some_js(code, jss->cx, jss->obj, &rval); - ret = JS_GetStringBytes(JS_ValueToString(jss->cx, rval)); - - if (!strncasecmp(ret, "speed", 4)) { - char *p; - - if ((p = strchr(ret, ':'))) { - p++; - if (*p == '+' || *p == '-') { - int step; - if (!(step = atoi(p))) { - step = 1; - } - fh->speed += step; - } else { - int speed = atoi(p); - fh->speed = speed; - } - return SWITCH_STATUS_SUCCESS; - } - - return SWITCH_STATUS_FALSE; - } else if (!strcasecmp(ret, "pause")) { - if (switch_test_flag(fh, SWITCH_FILE_PAUSE)) { - switch_clear_flag(fh, SWITCH_FILE_PAUSE); - } else { - switch_set_flag(fh, SWITCH_FILE_PAUSE); - } - return SWITCH_STATUS_SUCCESS; - } else if (!strcasecmp(ret, "restart")) { - unsigned int pos = 0; - fh->speed = 0; - switch_core_file_seek(fh, &pos, 0, SEEK_SET); - return SWITCH_STATUS_SUCCESS; - } else if (!strncasecmp(ret, "seek", 4)) { - switch_codec_t *codec; - unsigned int samps = 0; - unsigned int pos = 0; - char *p; - codec = switch_core_session_get_read_codec(jss->session); - - if ((p = strchr(ret, ':'))) { - p++; - if (*p == '+' || *p == '-') { - int step; - if (!(step = atoi(p))) { - step = 1000; - } - if (step > 0) { - samps = step * (codec->implementation->samples_per_second / 1000); - switch_core_file_seek(fh, &pos, samps, SEEK_CUR); - } else { - samps = step * (codec->implementation->samples_per_second / 1000); - switch_core_file_seek(fh, &pos, fh->pos - samps, SEEK_SET); - } - } else { - samps = atoi(p) * (codec->implementation->samples_per_second / 1000); - switch_core_file_seek(fh, &pos, samps, SEEK_SET); - } - } - - return SWITCH_STATUS_SUCCESS; - } - - if (!strcmp(ret, "true") || !strcmp(ret, "undefined")) { - return SWITCH_STATUS_SUCCESS; - } - - if (ret) { - switch_copy_string(cb_state->ret_buffer, ret, sizeof(cb_state->ret_buffer)); + if ((eo = malloc(sizeof(*eo)))) { + eo->event = event; + eo->freed = 1; + if ((Event = JS_DefineObject(cx, obj, name, &event_class, NULL, 0))) { + if ((JS_SetPrivate(cx, Event, eo) && + JS_DefineProperties(cx, Event, event_props) && + JS_DefineFunctions(cx, Event, event_methods))) { } } - - return SWITCH_STATUS_FALSE; } - break; - default: - break; - } - return SWITCH_STATUS_SUCCESS; - + return Event; } -static switch_status_t js_record_dtmf_callback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen) + +static switch_status_t js_common_callback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen) { - switch (itype) { - case SWITCH_INPUT_TYPE_DTMF: { - char *dtmf = (char *) input; - char code[2048]; - struct dtmf_callback_state *cb_state = buf; - struct js_session *jss = cb_state->session_state; - switch_file_handle_t *fh = cb_state->extra; - jsval rval; - char *ret; - - if (!jss) { - return SWITCH_STATUS_FALSE; - } - - if (cb_state->digit_count || (cb_state->code_buffer[0] > 47 && cb_state->code_buffer[0] < 58)) { - char *d; - if (!cb_state->digit_count) { - cb_state->digit_count = atoi(cb_state->code_buffer); - } + char *dtmf = NULL; + switch_event_t *event = NULL; + struct input_callback_state *cb_state = buf; + struct js_session *jss = cb_state->session_state; + uintN argc = 0; + jsval argv[4]; + JSObject *Event = NULL; - for(d = dtmf; *d; d++) { - cb_state->ret_buffer[cb_state->ret_buffer_len++] = *d; - if ((cb_state->ret_buffer_len > cb_state->digit_count)|| - (cb_state->ret_buffer_len > (int32_t) sizeof(cb_state->ret_buffer))|| - (cb_state->ret_buffer_len >= cb_state->digit_count) - ) { - return SWITCH_STATUS_FALSE; - } - } - return SWITCH_STATUS_SUCCESS; - } else { - snprintf(code, sizeof(code), "~%s(\"%s\")", cb_state->code_buffer, dtmf); - eval_some_js(code, jss->cx, jss->obj, &rval); - ret = JS_GetStringBytes(JS_ValueToString(jss->cx, rval)); - - if (!strcasecmp(ret, "pause")) { - if (switch_test_flag(fh, SWITCH_FILE_PAUSE)) { - switch_clear_flag(fh, SWITCH_FILE_PAUSE); - } else { - switch_set_flag(fh, SWITCH_FILE_PAUSE); - } - return SWITCH_STATUS_SUCCESS; - } else if (!strcasecmp(ret, "restart")) { - unsigned int pos = 0; - fh->speed = 0; - switch_core_file_seek(fh, &pos, 0, SEEK_SET); - return SWITCH_STATUS_SUCCESS; - } - - if (!strcmp(ret, "true") || !strcmp(ret, "undefined")) { - return SWITCH_STATUS_SUCCESS; - } - - if (ret) { - switch_copy_string(cb_state->ret_buffer, ret, sizeof(cb_state->ret_buffer)); - } - } + if (!jss) { return SWITCH_STATUS_FALSE; } + + switch (itype) { + case SWITCH_INPUT_TYPE_EVENT: + if ((event = (switch_event_t *) input)) { + if ((Event = new_js_event(event, "_XX_EVENT_XX_", cb_state->cx, cb_state->obj))) { + argv[argc++] = STRING_TO_JSVAL (JS_NewStringCopyZ(cb_state->cx, "event")); + argv[argc++] = OBJECT_TO_JSVAL(Event); + } + } + if (!Event) { + return SWITCH_STATUS_FALSE; + } break; - default: + case SWITCH_INPUT_TYPE_DTMF: + dtmf = (char *) input; + argv[argc++] = STRING_TO_JSVAL (JS_NewStringCopyZ(cb_state->cx, "dtmf")); + argv[argc++] = STRING_TO_JSVAL (JS_NewStringCopyZ(cb_state->cx, dtmf)); break; } + if (cb_state->arg) { + argv[argc++] = cb_state->arg; + } + + JS_CallFunction(cb_state->cx, cb_state->obj, cb_state->function, argc, argv, &cb_state->ret); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t js_stream_input_callback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen) +{ + char *ret; + switch_status_t status; + struct input_callback_state *cb_state = buf; + switch_file_handle_t *fh = cb_state->extra; + struct js_session *jss = cb_state->session_state; + + if ((status = js_common_callback(session, input, itype, buf, buflen)) != SWITCH_STATUS_SUCCESS) { + return status; + } + + if ((ret = JS_GetStringBytes(JS_ValueToString(cb_state->cx, cb_state->ret)))) { + if (!strncasecmp(ret, "speed", 4)) { + char *p; + + if ((p = strchr(ret, ':'))) { + p++; + if (*p == '+' || *p == '-') { + int step; + if (!(step = atoi(p))) { + step = 1; + } + fh->speed += step; + } else { + int speed = atoi(p); + fh->speed = speed; + } + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; + } else if (!strcasecmp(ret, "pause")) { + if (switch_test_flag(fh, SWITCH_FILE_PAUSE)) { + switch_clear_flag(fh, SWITCH_FILE_PAUSE); + } else { + switch_set_flag(fh, SWITCH_FILE_PAUSE); + } + return SWITCH_STATUS_SUCCESS; + } else if (!strcasecmp(ret, "restart")) { + unsigned int pos = 0; + fh->speed = 0; + switch_core_file_seek(fh, &pos, 0, SEEK_SET); + return SWITCH_STATUS_SUCCESS; + } else if (!strncasecmp(ret, "seek", 4)) { + switch_codec_t *codec; + unsigned int samps = 0; + unsigned int pos = 0; + char *p; + codec = switch_core_session_get_read_codec(jss->session); + + if ((p = strchr(ret, ':'))) { + p++; + if (*p == '+' || *p == '-') { + int step; + if (!(step = atoi(p))) { + step = 1000; + } + if (step > 0) { + samps = step * (codec->implementation->samples_per_second / 1000); + switch_core_file_seek(fh, &pos, samps, SEEK_CUR); + } else { + samps = step * (codec->implementation->samples_per_second / 1000); + switch_core_file_seek(fh, &pos, fh->pos - samps, SEEK_SET); + } + } else { + samps = atoi(p) * (codec->implementation->samples_per_second / 1000); + switch_core_file_seek(fh, &pos, samps, SEEK_SET); + } + } + + return SWITCH_STATUS_SUCCESS; + } + + if (!strcmp(ret, "true") || !strcmp(ret, "undefined")) { + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_BREAK; + } + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t js_record_input_callback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen) +{ + char *ret; + switch_status_t status; + struct input_callback_state *cb_state = buf; + switch_file_handle_t *fh = cb_state->extra; + + if ((status = js_common_callback(session, input, itype, buf, buflen)) != SWITCH_STATUS_SUCCESS) { + return status; + } + + if ((ret = JS_GetStringBytes(JS_ValueToString(cb_state->cx, cb_state->ret)))) { + if (!strcasecmp(ret, "pause")) { + if (switch_test_flag(fh, SWITCH_FILE_PAUSE)) { + switch_clear_flag(fh, SWITCH_FILE_PAUSE); + } else { + switch_set_flag(fh, SWITCH_FILE_PAUSE); + } + return SWITCH_STATUS_SUCCESS; + } else if (!strcasecmp(ret, "restart")) { + unsigned int pos = 0; + fh->speed = 0; + switch_core_file_seek(fh, &pos, 0, SEEK_SET); + return SWITCH_STATUS_SUCCESS; + } + + if (!strcmp(ret, "true") || !strcmp(ret, "undefined")) { + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_BREAK; + } + return SWITCH_STATUS_SUCCESS; } - -static switch_status_t js_speak_dtmf_callback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen) +static switch_status_t js_collect_input_callback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen) { - switch (itype) { - case SWITCH_INPUT_TYPE_DTMF: { - char *dtmf = (char *) input; - char code[2048]; - struct dtmf_callback_state *cb_state = buf; - struct js_session *jss = cb_state->session_state; - jsval rval; - char *ret; - - if (!jss) { - return SWITCH_STATUS_FALSE; - } - - if (cb_state->digit_count || (cb_state->code_buffer[0] > 47 && cb_state->code_buffer[0] < 58)) { - char *d; - if (!cb_state->digit_count) { - cb_state->digit_count = atoi(cb_state->code_buffer); - } + char *ret; + switch_status_t status; + struct input_callback_state *cb_state = buf; - for(d = dtmf; *d; d++) { - cb_state->ret_buffer[cb_state->ret_buffer_len++] = *d; - if ((cb_state->ret_buffer_len > cb_state->digit_count)|| - (cb_state->ret_buffer_len > (int32_t) sizeof(cb_state->ret_buffer))|| - (cb_state->ret_buffer_len >= cb_state->digit_count) - ) { - return SWITCH_STATUS_FALSE; - } - } - return SWITCH_STATUS_SUCCESS; - } else { - snprintf(code, sizeof(code), "~%s(\"%s\")", cb_state->code_buffer, dtmf); - eval_some_js(code, jss->cx, jss->obj, &rval); - ret = JS_GetStringBytes(JS_ValueToString(jss->cx, rval)); - - if (!strcmp(ret, "true") || !strcmp(ret, "undefined")) { - return SWITCH_STATUS_SUCCESS; - } - - if (ret) { - switch_copy_string(cb_state->ret_buffer, ret, sizeof(cb_state->ret_buffer)); - } - } - - return SWITCH_STATUS_FALSE; + if ((status = js_common_callback(session, input, itype, buf, buflen)) != SWITCH_STATUS_SUCCESS) { + return status; } - break; - default: - break; + + if ((ret = JS_GetStringBytes(JS_ValueToString(cb_state->cx, cb_state->ret)))) { + if (!strcmp(ret, "true") || !strcmp(ret, "undefined")) { + return SWITCH_STATUS_SUCCESS; + } + return SWITCH_STATUS_BREAK; } return SWITCH_STATUS_SUCCESS; - } static JSBool session_flush_digits(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) @@ -713,12 +712,13 @@ static JSBool session_recordfile(JSContext *cx, JSObject *obj, uintN argc, jsval struct js_session *jss = JS_GetPrivate(cx, obj); switch_channel_t *channel; char *file_name = NULL; - char *dtmf_callback = NULL; + char *input_callback = NULL; void *bp = NULL; int len = 0; switch_input_callback_function_t dtmf_func = NULL; - struct dtmf_callback_state cb_state = {0}; + struct input_callback_state cb_state = {0}; switch_file_handle_t fh; + JSFunction *function; channel = switch_core_session_get_channel(jss->session); assert(channel != NULL); @@ -730,15 +730,18 @@ static JSBool session_recordfile(JSContext *cx, JSObject *obj, uintN argc, jsval } } if (argc > 1) { - dtmf_callback = JS_GetStringBytes(JS_ValueToString(cx, argv[1])); - if (switch_strlen_zero(dtmf_callback)) { - dtmf_callback = NULL; - } else { + if ((function = JS_ValueToFunction(cx, argv[1]))) { memset(&cb_state, 0, sizeof(cb_state)); - switch_copy_string(cb_state.code_buffer, dtmf_callback, sizeof(cb_state.code_buffer)); - cb_state.code_buffer_len = strlen(cb_state.code_buffer); + switch_copy_string(cb_state.code_buffer, input_callback, sizeof(cb_state.code_buffer)); cb_state.session_state = jss; - dtmf_func = js_record_dtmf_callback; + cb_state.function = function; + cb_state.cx = cx; + cb_state.obj = obj; + if (argc > 2) { + cb_state.arg = argv[2]; + } + + dtmf_func = js_record_input_callback; bp = &cb_state; len = sizeof(cb_state); } @@ -748,6 +751,49 @@ static JSBool session_recordfile(JSContext *cx, JSObject *obj, uintN argc, jsval cb_state.extra = &fh; switch_ivr_record_file(jss->session, &fh, file_name, dtmf_func, bp, len); + *rval = cb_state.ret; + + return (switch_channel_ready(channel)) ? JS_TRUE : JS_FALSE; +} + + +static JSBool session_collect_input(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + struct js_session *jss = JS_GetPrivate(cx, obj); + switch_channel_t *channel; + void *bp = NULL; + int len = 0; + int32 to = 0; + switch_input_callback_function_t dtmf_func = NULL; + struct input_callback_state cb_state = {0}; + JSFunction *function; + + channel = switch_core_session_get_channel(jss->session); + assert(channel != NULL); + + if (argc > 0) { + if ((function = JS_ValueToFunction(cx, argv[0]))) { + memset(&cb_state, 0, sizeof(cb_state)); + cb_state.function = function; + + if (argc > 1) { + cb_state.arg = argv[1]; + } + + cb_state.session_state = jss; + cb_state.cx = cx; + cb_state.obj = obj; + dtmf_func = js_collect_input_callback; + bp = &cb_state; + len = sizeof(cb_state); + } + } + + if (argc > 2) { + JS_ValueToInt32(jss->cx, argv[2], &to); + } + + switch_ivr_collect_digits_callback(jss->session, dtmf_func, bp, len, to); *rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, cb_state.ret_buffer)); return (switch_channel_ready(channel)) ? JS_TRUE : JS_FALSE; @@ -759,12 +805,13 @@ static JSBool session_streamfile(JSContext *cx, JSObject *obj, uintN argc, jsval switch_channel_t *channel; char *file_name = NULL; char *timer_name = NULL; - char *dtmf_callback = NULL; + //char *input_callback = NULL; void *bp = NULL; int len = 0; switch_input_callback_function_t dtmf_func = NULL; - struct dtmf_callback_state cb_state = {0}; + struct input_callback_state cb_state = {0}; switch_file_handle_t fh; + JSFunction *function; channel = switch_core_session_get_channel(jss->session); assert(channel != NULL); @@ -782,15 +829,18 @@ static JSBool session_streamfile(JSContext *cx, JSObject *obj, uintN argc, jsval } } if (argc > 2) { - dtmf_callback = JS_GetStringBytes(JS_ValueToString(cx, argv[2])); - if (switch_strlen_zero(dtmf_callback)) { - dtmf_callback = NULL; - } else { + if ((function = JS_ValueToFunction(cx, argv[2]))) { memset(&cb_state, 0, sizeof(cb_state)); - switch_copy_string(cb_state.code_buffer, dtmf_callback, sizeof(cb_state.code_buffer)); - cb_state.code_buffer_len = strlen(cb_state.code_buffer); + cb_state.function = function; + + if (argc > 3) { + cb_state.arg = argv[3]; + } + cb_state.session_state = jss; - dtmf_func = js_stream_dtmf_callback; + cb_state.cx = cx; + cb_state.obj = obj; + dtmf_func = js_stream_input_callback; bp = &cb_state; len = sizeof(cb_state); } @@ -800,8 +850,8 @@ static JSBool session_streamfile(JSContext *cx, JSObject *obj, uintN argc, jsval cb_state.extra = &fh; switch_ivr_play_file(jss->session, &fh, file_name, timer_name, dtmf_func, bp, len); - *rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, cb_state.ret_buffer)); - + *rval = cb_state.ret; + return (switch_channel_ready(channel)) ? JS_TRUE : JS_FALSE; } @@ -861,13 +911,13 @@ static JSBool session_speak(JSContext *cx, JSObject *obj, uintN argc, jsval *arg char *tts_name = NULL; char *voice_name = NULL; char *text = NULL; - char *dtmf_callback = NULL; char *timer_name = NULL; switch_codec_t *codec; void *bp = NULL; int len = 0; - struct dtmf_callback_state cb_state = {0}; + struct input_callback_state cb_state = {0}; switch_input_callback_function_t dtmf_func = NULL; + JSFunction *function; channel = switch_core_session_get_channel(jss->session); assert(channel != NULL); @@ -882,15 +932,17 @@ static JSBool session_speak(JSContext *cx, JSObject *obj, uintN argc, jsval *arg text = JS_GetStringBytes(JS_ValueToString(cx, argv[2])); } if (argc > 3) { - dtmf_callback = JS_GetStringBytes(JS_ValueToString(cx, argv[3])); - if (switch_strlen_zero(dtmf_callback)) { - dtmf_callback = NULL; - } else { + if ((function = JS_ValueToFunction(cx, argv[3]))) { memset(&cb_state, 0, sizeof(cb_state)); - switch_copy_string(cb_state.code_buffer, dtmf_callback, sizeof(cb_state.code_buffer)); - cb_state.code_buffer_len = strlen(cb_state.code_buffer); + cb_state.function = function; + if (argc > 4) { + cb_state.arg = argv[4]; + } + + cb_state.cx = cx; + cb_state.obj = obj; cb_state.session_state = jss; - dtmf_func = js_speak_dtmf_callback; + dtmf_func = js_collect_input_callback; bp = &cb_state; len = sizeof(cb_state); } @@ -915,7 +967,7 @@ static JSBool session_speak(JSContext *cx, JSObject *obj, uintN argc, jsval *arg bp, len); - *rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, cb_state.ret_buffer)); + *rval = cb_state.ret; return (switch_channel_ready(channel)) ? JS_TRUE : JS_FALSE; } @@ -971,6 +1023,41 @@ static JSBool session_ready(JSContext *cx, JSObject *obj, uintN argc, jsval *arg return JS_TRUE; } + + +static JSBool session_wait_for_media(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + struct js_session *jss = JS_GetPrivate(cx, obj); + switch_channel_t *channel; + switch_time_t started; + unsigned int elapsed; + int32 timeout = 60; + channel = switch_core_session_get_channel(jss->session); + assert(channel != NULL); + started = switch_time_now(); + + if (argc > 0) { + JS_ValueToInt32(cx, argv[0], &timeout); + } + + for(;;) { + if (((elapsed = (unsigned int)((switch_time_now() - started) / 1000)) > (switch_time_t)timeout) || switch_channel_get_state(channel) >= CS_HANGUP) { + *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); + break; + } + + if (switch_channel_ready(channel) && (switch_channel_test_flag(channel, CF_ANSWERED) || switch_channel_test_flag(channel, CF_EARLY_MEDIA))) { + *rval = BOOLEAN_TO_JSVAL( JS_TRUE ); + break; + } + + switch_yield(1000); + } + + return JS_TRUE; +} + + static JSBool session_wait_for_answer(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { struct js_session *jss = JS_GetPrivate(cx, obj); @@ -987,15 +1074,19 @@ static JSBool session_wait_for_answer(JSContext *cx, JSObject *obj, uintN argc, } for(;;) { - elapsed = (unsigned int)((switch_time_now() - started) / 1000); - if ((int32)elapsed > timeout || switch_channel_test_flag(channel, CF_ANSWERED) || - switch_channel_test_flag(channel, CF_EARLY_MEDIA)) { + if (((elapsed = (unsigned int)((switch_time_now() - started) / 1000)) > (switch_time_t)timeout) || switch_channel_get_state(channel) >= CS_HANGUP) { + *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); + break; + } + + if (switch_channel_ready(channel) && switch_channel_test_flag(channel, CF_ANSWERED)) { + *rval = BOOLEAN_TO_JSVAL( JS_TRUE ); break; } switch_yield(1000); } - + return JS_TRUE; } @@ -1027,12 +1118,19 @@ static JSBool session_get_event(JSContext *cx, JSObject *obj, uintN argc, jsval if (switch_core_session_dequeue_event(jss->session, &event) == SWITCH_STATUS_SUCCESS) { JSObject *Event; - if ((Event = JS_DefineObject(cx, obj, "Event", &event_class, NULL, 0))) { - if ((JS_SetPrivate(cx, Event, event) && - JS_DefineProperties(cx, Event, event_props) && - JS_DefineFunctions(cx, Event, event_methods))) { - *rval = OBJECT_TO_JSVAL ( Event ); - return JS_TRUE; + struct event_obj *eo; + + if ((eo = malloc(sizeof(*eo)))) { + eo->event = event; + eo->freed = 0; + + if ((Event = JS_DefineObject(cx, obj, "__event__", &event_class, NULL, 0))) { + if ((JS_SetPrivate(cx, Event, eo) && + JS_DefineProperties(cx, Event, event_props) && + JS_DefineFunctions(cx, Event, event_methods))) { + *rval = OBJECT_TO_JSVAL ( Event ); + return JS_TRUE; + } } } } @@ -1045,13 +1143,13 @@ static JSBool session_get_event(JSContext *cx, JSObject *obj, uintN argc, jsval static JSBool session_send_event(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { struct js_session *jss = JS_GetPrivate(cx, obj); - switch_event_t *event; JSObject *Event; + struct event_obj *eo; if (argc > 0) { if (JS_ValueToObject(cx, argv[0], &Event)) { - if ((event = JS_GetPrivate(cx, Event))) { - if (switch_core_session_receive_event(jss->session, &event) != SWITCH_STATUS_SUCCESS) { + if ((eo = JS_GetPrivate(cx, Event))) { + if (switch_core_session_receive_event(jss->session, &eo->event) != SWITCH_STATUS_SUCCESS) { *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); return JS_TRUE; } @@ -1240,6 +1338,7 @@ enum session_tinyid { static JSFunctionSpec session_methods[] = { {"streamFile", session_streamfile, 1}, + {"collectInput", session_collect_input, 1}, {"recordFile", session_recordfile, 1}, {"flushEvents", session_flush_events, 1}, {"flushDigits", session_flush_digits, 1}, @@ -1250,6 +1349,7 @@ static JSFunctionSpec session_methods[] = { {"answer", session_answer, 0}, {"ready", session_ready, 0}, {"waitForAnswer", session_wait_for_answer, 0}, + {"waitForMedia", session_wait_for_media, 0}, {"getEvent", session_get_event, 0}, {"sendEvent", session_send_event, 0}, {"hangup", session_hangup, 0}, @@ -1279,6 +1379,10 @@ static JSBool session_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval switch_channel_t *channel; switch_caller_profile_t *caller_profile; char *name; + + if (!jss || !jss->session) { + return JS_FALSE; + } channel = switch_core_session_get_channel(jss->session); assert(channel != NULL); @@ -1478,6 +1582,8 @@ static JSBool session_construct(JSContext *cx, JSObject *obj, uintN argc, jsval return JS_TRUE; } + *rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, switch_channel_cause2str(cause))); + jss = switch_core_session_alloc(peer_session, sizeof(*jss)); jss->session = peer_session; jss->flags = 0; @@ -1772,6 +1878,7 @@ static int db_callback(void *pArg, int argc, char **argv, char **columnNames) snprintf(code, sizeof(code), "~_Db_RoW_[\"%s\"] = \"%s\"", columnNames[x], argv[x]); eval_some_js(code, dbo->cx, dbo->obj, &rval); } + snprintf(code, sizeof(code), "~%s(_Db_RoW_)", dbo->code_buffer); eval_some_js(code, dbo->cx, dbo->obj, &rval); @@ -1783,33 +1890,34 @@ static int db_callback(void *pArg, int argc, char **argv, char **columnNames) static JSBool db_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - struct db_obj *dbo = JS_GetPrivate(cx, obj); - *rval = BOOLEAN_TO_JSVAL( JS_TRUE ); + struct db_obj *dbo = JS_GetPrivate(cx, obj); + *rval = BOOLEAN_TO_JSVAL( JS_TRUE ); - if (argc > 0) { - char *sql = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); - char *err = NULL; - void *arg = NULL; - switch_core_db_callback_func_t cb_func = NULL; + if (argc > 0) { + char *sql = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); + char *err = NULL; + void *arg = NULL; + switch_core_db_callback_func_t cb_func = NULL; - - if (argc > 1) { - char *js_func = JS_GetStringBytes(JS_ValueToString(cx, argv[1])); - switch_copy_string(dbo->code_buffer, js_func, sizeof(dbo->code_buffer)); - cb_func = db_callback; - arg = dbo; - } - switch_core_db_exec(dbo->db, sql, cb_func, arg, &err); - if (err) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error %s\n", err); - switch_core_db_free(err); - *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); - } - } - return JS_TRUE; + if (argc > 1) { + char *js_func = JS_GetStringBytes(JS_ValueToString(cx, argv[1])); + switch_copy_string(dbo->code_buffer, js_func, sizeof(dbo->code_buffer)); + cb_func = db_callback; + arg = dbo; + } + + switch_core_db_exec(dbo->db, sql, cb_func, arg, &err); + if (err) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error %s\n", err); + switch_core_db_free(err); + *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); + } + } + return JS_TRUE; } + static JSBool db_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { struct db_obj *dbo = JS_GetPrivate(cx, obj); @@ -2065,8 +2173,10 @@ static JSBool teletone_on_dtmf(JSContext *cx, JSObject *obj, uintN argc, jsval * { struct teletone_obj *tto = JS_GetPrivate(cx, obj); if (argc > 0) { - char *func = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); - switch_copy_string(tto->code_buffer, func, sizeof(tto->code_buffer)); + tto->function = JS_ValueToFunction(cx, argv[0]); + if (argc > 1) { + tto->arg = argv[1]; + } switch_set_flag(tto, TTF_DTMF); } return JS_TRUE; @@ -2126,17 +2236,18 @@ static JSBool teletone_generate(JSContext *cx, JSObject *obj, uintN argc, jsval if (switch_test_flag(tto, TTF_DTMF)) { char dtmf[128]; - char code[512]; char *ret; - jsval tt_rval; + if (switch_channel_has_dtmf(channel)) { + uintN aargc = 0; + jsval aargv[4]; + switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf)); - snprintf(code, sizeof(code), "~%s(\"%s\")", tto->code_buffer, dtmf); - eval_some_js(code, cx, obj, &tt_rval); - ret = JS_GetStringBytes(JS_ValueToString(cx, tt_rval)); + aargv[aargc++] = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, dtmf)); + JS_CallFunction(cx, obj, tto->function, aargc, aargv, &tto->ret); + ret = JS_GetStringBytes(JS_ValueToString(cx, tto->ret)); if (strcmp(ret, "true") && strcmp(ret, "undefined")) { - switch_copy_string(tto->ret_val, ret, sizeof(tto->ret_val)); - *rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, tto->ret_val)); + *rval = tto->ret; return JS_TRUE; } } @@ -2239,19 +2350,32 @@ static JSBool js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsva { char *level_str, *msg; switch_log_level_t level = SWITCH_LOG_DEBUG; + JSScript *script = NULL; + const char *file = __FILE__; + int line = __LINE__; + JSStackFrame *caller; + caller = JS_GetScriptedCaller(cx, NULL); + script = JS_GetFrameScript(cx, caller); + + if (script) { + file = JS_GetScriptFilename(cx, script); + line = JS_GetScriptBaseLineNumber(cx, script); + } + + if (argc > 1) { if ((level_str = JS_GetStringBytes(JS_ValueToString(cx, argv[0])))) { level = switch_log_str2level(level_str); } if ((msg = JS_GetStringBytes(JS_ValueToString(cx, argv[1])))) { - switch_log_printf(SWITCH_CHANNEL_LOG, level, "JS_LOG: %s", msg); + switch_log_printf(SWITCH_CHANNEL_ID_LOG, (char*) file, "console_log", line, level, "%s", msg); return JS_TRUE; } } else if (argc > 0) { if ((msg = JS_GetStringBytes(JS_ValueToString(cx, argv[0])))) { - switch_log_printf(SWITCH_CHANNEL_LOG, level, "JS_LOG: %s", msg); + switch_log_printf(SWITCH_CHANNEL_ID_LOG, (char*) file, "console_log", line, level, "%s", msg); return JS_TRUE; } } @@ -2323,6 +2447,11 @@ static JSBool js_bridge(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, j { struct js_session *jss_a = NULL, *jss_b = NULL; JSObject *session_obj_a = NULL, *session_obj_b = NULL; + void *bp = NULL; + int len = 0; + switch_input_callback_function_t dtmf_func = NULL; + struct input_callback_state cb_state = {0}; + JSFunction *function; if (argc > 1) { if (JS_ValueToObject(cx, argv[0], &session_obj_a)) { @@ -2344,7 +2473,28 @@ static JSBool js_bridge(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, j return JS_FALSE; } - switch_ivr_multi_threaded_bridge(jss_a->session, jss_b->session, NULL, NULL, NULL); + + if (argc > 2) { + if ((function = JS_ValueToFunction(cx, argv[2]))) { + memset(&cb_state, 0, sizeof(cb_state)); + cb_state.function = function; + + if (argc > 3) { + cb_state.arg = argv[3]; + } + + cb_state.cx = cx; + cb_state.obj = obj; + + cb_state.session_state = jss_a; + dtmf_func = js_collect_input_callback; + bp = &cb_state; + len = sizeof(cb_state); + } + } + + switch_ivr_multi_threaded_bridge(jss_a->session, jss_b->session, dtmf_func, bp, bp); + return JS_TRUE; } @@ -2402,7 +2552,10 @@ static JSBool js_email(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, js return JS_FALSE; if (file) { - snprintf(buf, B64BUFFLEN, "\n\n--%s\nContent-Type: application/octet-stream\nContent-Transfer-Encoding: base64\nContent-Description: Sound attachment.\nContent-Disposition: attachment; filename=\"%s\"\n\n", bound, file); + snprintf(buf, B64BUFFLEN, "\n\n--%s\nContent-Type: application/octet-stream\n" + "Content-Transfer-Encoding: base64\n" + "Content-Description: Sound attachment.\n" + "Content-Disposition: attachment; filename=\"%s\"\n\n", bound, file); if (!write_buf(fd, buf)) return JS_FALSE; @@ -2488,7 +2641,7 @@ static int eval_some_js(char *code, JSContext *cx, JSObject *obj, jsval *rval) { JSScript *script; char *cptr; - char path[512]; + char *path = NULL; int res = 0; JS_ClearPendingException(cx); @@ -2497,11 +2650,11 @@ static int eval_some_js(char *code, JSContext *cx, JSObject *obj, jsval *rval) cptr = code + 1; script = JS_CompileScript(cx, obj, cptr, strlen(cptr), "inline", 1); } else { - if (code[0] == '/') { + if (*code == '/') { script = JS_CompileFile(cx, obj, code); - } else { - snprintf(path, sizeof(path), "%s%s%s", SWITCH_GLOBAL_dirs.script_dir, SWITCH_PATH_SEPARATOR, code); + } else if ((path = switch_mprintf("%s%s%s", SWITCH_GLOBAL_dirs.script_dir, SWITCH_PATH_SEPARATOR, code))) { script = JS_CompileFile(cx, obj, path); + switch_safe_free(path); } } diff --git a/src/switch_channel.c b/src/switch_channel.c index 832aed1d91..06c60d9533 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -348,7 +348,11 @@ SWITCH_DECLARE(char *) switch_channel_get_variable(switch_channel_t *channel, ch assert(channel != NULL); if (!(v=switch_core_hash_find(channel->variables, varname))) { - v = switch_caller_get_field_by_name(channel->caller_profile, varname); + if (!(v = switch_caller_get_field_by_name(channel->caller_profile, varname))) { + if (!strcmp(varname, "base_dir")) { + return SWITCH_GLOBAL_dirs.base_dir; + } + } } return v; @@ -986,7 +990,8 @@ SWITCH_DECLARE(switch_channel_state_t) switch_channel_perform_hangup(switch_chan switch_channel_event_set_data(channel, event); switch_event_fire(&event); } - + + switch_channel_set_variable(channel, "hangup_cause", switch_channel_cause2str(channel->hangup_cause)); switch_channel_presence(channel, "unavailable", switch_channel_cause2str(channel->hangup_cause)); switch_core_session_kill_channel(channel->session, SWITCH_SIG_KILL); @@ -1190,7 +1195,7 @@ SWITCH_DECLARE(char *) switch_channel_expand_variables(switch_channel_t *channel return in; } } - nlen = sub_val ? strlen(sub_val) : 0; + nlen = strlen(sub_val); if (len + nlen >= olen) { olen += block; cpos = c - data; @@ -1205,11 +1210,10 @@ SWITCH_DECLARE(char *) switch_channel_expand_variables(switch_channel_t *channel vname = data + vvalpos; } - if (nlen) { - len += nlen; - strcat(c, sub_val); - c += nlen; - } + len += nlen; + strcat(c, sub_val); + c += nlen; + if (func_val) { free(func_val); func_val = NULL; diff --git a/src/switch_core.c b/src/switch_core.c index 32b85b1640..15db1b084e 100644 --- a/src/switch_core.c +++ b/src/switch_core.c @@ -64,6 +64,7 @@ struct switch_media_bug { switch_mutex_t *write_mutex; switch_core_session_t *session; void *user_data; + uint32_t flags; struct switch_media_bug *next; }; @@ -168,6 +169,11 @@ static void switch_core_media_bug_destroy(switch_media_bug_t *bug) switch_buffer_destroy(&bug->raw_write_buffer); } +SWITCH_DECLARE(void *) switch_core_media_bug_get_user_data(switch_media_bug_t *bug) +{ + return bug->user_data; +} + SWITCH_DECLARE(switch_status_t) switch_core_media_bug_read(switch_media_bug_t *bug, switch_frame_t *frame) { uint32_t bytes = 0; @@ -175,21 +181,35 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_bug_read(switch_media_bug_t *b uint32_t datalen = 0; int16_t *dp, *fp; uint32_t x; - size_t rlen = switch_buffer_inuse(bug->raw_read_buffer); - size_t wlen = switch_buffer_inuse(bug->raw_write_buffer); + size_t rlen = 0; + size_t wlen = 0; uint32_t blen; size_t rdlen = 0; uint32_t maxlen; - if (!rlen && !wlen) { + + if (bug->raw_read_buffer) { + rlen = switch_buffer_inuse(bug->raw_read_buffer); + } + + if (bug->raw_write_buffer) { + wlen = switch_buffer_inuse(bug->raw_write_buffer); + } + + if ((bug->raw_read_buffer && bug->raw_write_buffer) && (!rlen && !wlen)) { return SWITCH_STATUS_FALSE; } + maxlen = sizeof(data) > frame->buflen ? frame->buflen : sizeof(data); if ((rdlen = rlen > wlen ? wlen : rlen) > maxlen) { rdlen = maxlen; } - + + if (!rdlen) { + rdlen = maxlen; + } + frame->datalen = 0; if (rlen) { @@ -209,6 +229,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_bug_read(switch_media_bug_t *b switch_mutex_unlock(bug->write_mutex); } + bytes = (datalen > frame->datalen) ? datalen : frame->datalen; if (bytes) { @@ -233,6 +254,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_bug_read(switch_media_bug_t *b } frame->datalen = bytes; + + return SWITCH_STATUS_SUCCESS; } @@ -243,6 +266,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_bug_read(switch_media_bug_t *b SWITCH_DECLARE(switch_status_t) switch_core_media_bug_add(switch_core_session_t *session, switch_media_bug_callback_t callback, void *user_data, + switch_media_bug_flag_t flags, switch_media_bug_t **new_bug) { @@ -256,13 +280,25 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_bug_add(switch_core_session_t bug->callback = callback; bug->user_data = user_data; bug->session = session; + bug->flags = flags; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Attaching BUG to %s\n", switch_channel_get_name(session->channel)); bytes = session->read_codec->implementation->bytes_per_frame; - switch_buffer_create_dynamic(&bug->raw_read_buffer, bytes * SWITCH_BUFFER_BLOCK_FRAMES, bytes * SWITCH_BUFFER_START_FRAMES, MAX_BUG_BUFFER); + + if (!bug->flags) { + bug->flags = (SMBF_READ_STREAM | SMBF_WRITE_STREAM); + } + + if (switch_test_flag(bug, SMBF_READ_STREAM)) { + switch_buffer_create_dynamic(&bug->raw_read_buffer, bytes * SWITCH_BUFFER_BLOCK_FRAMES, bytes * SWITCH_BUFFER_START_FRAMES, MAX_BUG_BUFFER); + switch_mutex_init(&bug->read_mutex, SWITCH_MUTEX_NESTED, session->pool); + } + bytes = session->write_codec->implementation->bytes_per_frame; - switch_buffer_create_dynamic(&bug->raw_write_buffer, bytes * SWITCH_BUFFER_BLOCK_FRAMES, bytes * SWITCH_BUFFER_START_FRAMES, MAX_BUG_BUFFER); - switch_mutex_init(&bug->read_mutex, SWITCH_MUTEX_NESTED, session->pool); - switch_mutex_init(&bug->write_mutex, SWITCH_MUTEX_NESTED, session->pool); + + if (switch_test_flag(bug, SMBF_WRITE_STREAM)) { + switch_buffer_create_dynamic(&bug->raw_write_buffer, bytes * SWITCH_BUFFER_BLOCK_FRAMES, bytes * SWITCH_BUFFER_START_FRAMES, MAX_BUG_BUFFER); + switch_mutex_init(&bug->write_mutex, SWITCH_MUTEX_NESTED, session->pool); + } switch_thread_rwlock_wrlock(session->bug_rwlock); bug->next = session->bugs; @@ -292,6 +328,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_bug_remove_all(switch_core_ses switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing BUG from %s\n", switch_channel_get_name(session->channel)); } switch_thread_rwlock_unlock(session->bug_rwlock); + session->bugs = NULL; return SWITCH_STATUS_SUCCESS; } @@ -946,18 +983,108 @@ SWITCH_DECLARE(switch_status_t) switch_core_speech_open(switch_speech_handle_t * return sh->speech_interface->speech_open(sh, voice_name, rate, flags); } -SWITCH_DECLARE(switch_status_t) switch_core_speech_feed_asr(switch_speech_handle_t *sh, void *data, unsigned int *len, int rate, switch_speech_flag_t *flags) +SWITCH_DECLARE(switch_status_t) switch_core_asr_open(switch_asr_handle_t *ah, + char *module_name, + char *codec, + int rate, + char *dest, + switch_asr_flag_t *flags, + switch_memory_pool_t *pool) { - assert(sh != NULL); + switch_status_t status; + + assert(ah != NULL); + + if ((ah->asr_interface = switch_loadable_module_get_asr_interface(module_name)) == 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "invalid asr module [%s]!\n", module_name); + return SWITCH_STATUS_GENERR; + } - return sh->speech_interface->speech_feed_asr(sh, data, len, rate, flags); + ah->flags = *flags; + + if (pool) { + ah->memory_pool = pool; + } else { + if ((status = switch_core_new_memory_pool(&ah->memory_pool)) != SWITCH_STATUS_SUCCESS) { + return status; + } + switch_set_flag(ah, SWITCH_ASR_FLAG_FREE_POOL); + } + + ah->rate = rate; + ah->name = switch_core_strdup(ah->memory_pool, module_name); + + return ah->asr_interface->asr_open(ah, codec, rate, dest, flags); } -SWITCH_DECLARE(switch_status_t) switch_core_speech_interpret_asr(switch_speech_handle_t *sh, char *buf, unsigned int buflen, switch_speech_flag_t *flags) +SWITCH_DECLARE(switch_status_t) switch_core_asr_load_grammar(switch_asr_handle_t *ah, char *grammar, char *path) { - assert(sh != NULL); + char *epath = NULL; + switch_status_t status; + + assert(ah != NULL); + + if (*path != '/') { + epath = switch_mprintf("%s%s%s", SWITCH_GLOBAL_dirs.grammar_dir, SWITCH_PATH_SEPARATOR, path); + path = epath; + } + + status = ah->asr_interface->asr_load_grammar(ah, grammar, path); + switch_safe_free(epath); + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_asr_unload_grammar(switch_asr_handle_t *ah, char *grammar) +{ + switch_status_t status; + + assert(ah != NULL); + status = ah->asr_interface->asr_unload_grammar(ah, grammar); + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_asr_pause(switch_asr_handle_t *ah) +{ + assert(ah != NULL); + + return ah->asr_interface->asr_pause(ah); +} + +SWITCH_DECLARE(switch_status_t) switch_core_asr_resume(switch_asr_handle_t *ah) +{ + assert(ah != NULL); + + return ah->asr_interface->asr_resume(ah); +} + +SWITCH_DECLARE(switch_status_t) switch_core_asr_close(switch_asr_handle_t *ah, switch_asr_flag_t *flags) +{ + assert(ah != NULL); + + return ah->asr_interface->asr_close(ah, flags); +} + +SWITCH_DECLARE(switch_status_t) switch_core_asr_feed(switch_asr_handle_t *ah, void *data, unsigned int len, switch_asr_flag_t *flags) +{ + assert(ah != NULL); - return sh->speech_interface->speech_interpret_asr(sh, buf, buflen, flags); + return ah->asr_interface->asr_feed(ah, data, len, flags); +} + +SWITCH_DECLARE(switch_status_t) switch_core_asr_check_results(switch_asr_handle_t *ah, switch_asr_flag_t *flags) +{ + assert(ah != NULL); + + return ah->asr_interface->asr_check_results(ah, flags); +} + +SWITCH_DECLARE(switch_status_t) switch_core_asr_get_results(switch_asr_handle_t *ah, char **xmlstr, switch_asr_flag_t *flags) +{ + assert(ah != NULL); + + return ah->asr_interface->asr_get_results(ah, xmlstr, flags); } SWITCH_DECLARE(switch_status_t) switch_core_speech_feed_tts(switch_speech_handle_t *sh, char *text, switch_speech_flag_t *flags) @@ -1765,12 +1892,14 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi switch_media_bug_t *bp; switch_thread_rwlock_rdlock(session->bug_rwlock); for (bp = session->bugs; bp; bp = bp->next) { - switch_mutex_lock(bp->read_mutex); - switch_buffer_write(bp->raw_read_buffer, read_frame->data, read_frame->datalen); - if (bp->callback) { - bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_READ); + if (switch_test_flag(bp, SMBF_READ_STREAM)) { + switch_mutex_lock(bp->read_mutex); + switch_buffer_write(bp->raw_read_buffer, read_frame->data, read_frame->datalen); + if (bp->callback) { + bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_READ); + } + switch_mutex_unlock(bp->read_mutex); } - switch_mutex_unlock(bp->read_mutex); } switch_thread_rwlock_unlock(session->bug_rwlock); } @@ -1999,11 +2128,13 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess switch_media_bug_t *bp; switch_thread_rwlock_rdlock(session->bug_rwlock); for (bp = session->bugs; bp; bp = bp->next) { - switch_mutex_lock(bp->write_mutex); - switch_buffer_write(bp->raw_write_buffer, write_frame->data, write_frame->datalen); - switch_mutex_unlock(bp->write_mutex); - if (bp->callback) { - bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_WRITE); + if (switch_test_flag(bp, SMBF_WRITE_STREAM)) { + switch_mutex_lock(bp->write_mutex); + switch_buffer_write(bp->raw_write_buffer, write_frame->data, write_frame->datalen); + switch_mutex_unlock(bp->write_mutex); + if (bp->callback) { + bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_WRITE); + } } } switch_thread_rwlock_unlock(session->bug_rwlock); @@ -3193,7 +3324,7 @@ static void *SWITCH_THREAD_FUNC switch_core_session_thread(switch_thread_t *thre switch_mutex_unlock(runtime.session_table_mutex); switch_core_session_run(session); - + switch_core_media_bug_remove_all(session); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Session %u (%s) Locked, Waiting on external entities\n", session->id, switch_channel_get_name(session->channel)); switch_core_session_write_lock(session); switch_core_session_rwunlock(session); @@ -3609,6 +3740,9 @@ SWITCH_DECLARE(void) switch_core_set_globals(void) if (!SWITCH_GLOBAL_dirs.htdocs_dir && (SWITCH_GLOBAL_dirs.htdocs_dir = (char *) malloc(BUFSIZE))) { snprintf(SWITCH_GLOBAL_dirs.htdocs_dir, BUFSIZE, "%shtdocs", exePath); } + if (!SWITCH_GLOBAL_dirs.htdocs_dir && (SWITCH_GLOBAL_dirs.grammar_dir = (char *) malloc(BUFSIZE))) { + snprintf(SWITCH_GLOBAL_dirs.grammar_dir, BUFSIZE, "%sgrammar", exePath); + } #else SWITCH_GLOBAL_dirs.base_dir = SWITCH_PREFIX_DIR; SWITCH_GLOBAL_dirs.mod_dir = SWITCH_MOD_DIR; @@ -3617,6 +3751,7 @@ SWITCH_DECLARE(void) switch_core_set_globals(void) SWITCH_GLOBAL_dirs.db_dir = SWITCH_DB_DIR; SWITCH_GLOBAL_dirs.script_dir = SWITCH_SCRIPT_DIR; SWITCH_GLOBAL_dirs.htdocs_dir = SWITCH_HTDOCS_DIR; + SWITCH_GLOBAL_dirs.grammar_dir = SWITCH_GRAMMAR_DIR; #endif #ifdef SWITCH_TEMP_DIR SWITCH_GLOBAL_dirs.temp_dir = SWITCH_TEMP_DIR; @@ -3987,6 +4122,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_destroy(void) free(SWITCH_GLOBAL_dirs.db_dir); free(SWITCH_GLOBAL_dirs.script_dir); free(SWITCH_GLOBAL_dirs.htdocs_dir); + free(SWITCH_GLOBAL_dirs.grammar_dir); free(SWITCH_GLOBAL_dirs.temp_dir); #endif diff --git a/src/switch_event.c b/src/switch_event.c index 3620e33889..c53ff1dec0 100644 --- a/src/switch_event.c +++ b/src/switch_event.c @@ -128,6 +128,7 @@ static char *EVENT_NAMES[] = { "ROSTER", "CODEC", "BACKGROUND_JOB", + "DETECTED_SPEECH", "ALL" }; @@ -539,15 +540,17 @@ SWITCH_DECLARE(void) switch_event_destroy(switch_event_t **event) switch_event_t *ep = *event; switch_event_header_t *hp, *this; - for (hp = ep->headers; hp;) { - this = hp; - hp = hp->next; - FREE(this->name); - FREE(this->value); - FREE(this); + if (ep) { + for (hp = ep->headers; hp;) { + this = hp; + hp = hp->next; + FREE(this->name); + FREE(this->value); + FREE(this); + } + FREE(ep->body); + FREE(ep); } - FREE(ep->body); - FREE(ep); *event = NULL; } diff --git a/src/switch_ivr.c b/src/switch_ivr.c index aff0ef4586..bc0a534f99 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -46,7 +46,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_sleep(switch_core_session_t *session, switch_time_t start, now, done = switch_time_now() + (ms * 1000); switch_frame_t *read_frame; int32_t left, elapsed; - + channel = switch_core_session_get_channel(session); assert(channel != NULL); @@ -174,13 +174,16 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_park(switch_core_session_t *session) } SWITCH_DECLARE(switch_status_t) switch_ivr_collect_digits_callback(switch_core_session_t *session, - switch_input_callback_function_t input_callback, - void *buf, - unsigned int buflen) + switch_input_callback_function_t input_callback, + void *buf, + unsigned int buflen, + unsigned int timeout) { switch_channel_t *channel; switch_status_t status = SWITCH_STATUS_SUCCESS; - + switch_time_t started = 0; + unsigned int elapsed; + channel = switch_core_session_get_channel(session); assert(channel != NULL); @@ -188,12 +191,21 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_collect_digits_callback(switch_core_s return SWITCH_STATUS_GENERR; } + if (timeout) { + started = switch_time_now(); + } + while(switch_channel_ready(channel)) { switch_frame_t *read_frame; switch_event_t *event; - char dtmf[128]; + if (timeout) { + elapsed = (unsigned int)((switch_time_now() - started) / 1000); + if (elapsed >= timeout) { + break; + } + } if (switch_core_session_dequeue_private_event(session, &event) == SWITCH_STATUS_SUCCESS) { switch_ivr_parse_event(session, event); @@ -585,6 +597,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_session(switch_core_session_t if ((status = switch_core_media_bug_add(session, record_callback, fh, + SMBF_BOTH, &bug)) != SWITCH_STATUS_SUCCESS) { switch_core_file_close(fh); return status; @@ -595,6 +608,299 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_session(switch_core_session_t return SWITCH_STATUS_SUCCESS; } + +struct speech_thread_handle { + switch_core_session_t *session; + switch_asr_handle_t *ah; + switch_media_bug_t *bug; + switch_mutex_t *mutex; + switch_thread_cond_t *cond; + switch_memory_pool_t *pool; +}; + +static void *SWITCH_THREAD_FUNC speech_thread(switch_thread_t *thread, void *obj) +{ + struct speech_thread_handle *sth = (struct speech_thread_handle *) obj; + switch_channel_t *channel = switch_core_session_get_channel(sth->session); + switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; + switch_status_t status; + + switch_thread_cond_create(&sth->cond, sth->pool); + switch_mutex_init(&sth->mutex, SWITCH_MUTEX_NESTED, sth->pool); + + + switch_core_session_read_lock(sth->session); + switch_mutex_lock(sth->mutex); + + while (switch_channel_ready(channel) && !switch_test_flag(sth->ah, SWITCH_ASR_FLAG_CLOSED)) { + char *xmlstr = NULL; + + switch_thread_cond_wait(sth->cond, sth->mutex); + if (switch_core_asr_check_results(sth->ah, &flags) == SWITCH_STATUS_SUCCESS) { + switch_event_t *event; + + status = switch_core_asr_get_results(sth->ah, &xmlstr, &flags); + + if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) { + goto done; + } + + + if (switch_event_create(&event, SWITCH_EVENT_DETECTED_SPEECH) == SWITCH_STATUS_SUCCESS) { + if (status == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Speech-Type", "detected-speech"); + switch_event_add_body(event, xmlstr); + } else { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Speech-Type", "begin-speaking"); + } + + if (switch_test_flag(sth->ah, SWITCH_ASR_FLAG_FIRE_EVENTS)) { + switch_event_t *dup; + + if (switch_event_dup(&dup, event) == SWITCH_STATUS_SUCCESS) { + switch_event_fire(&dup); + } + + } + + if (switch_core_session_queue_event(sth->session, &event) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Event queue failed!\n"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "delivery-failure", "true"); + switch_event_fire(&event); + } + } + + switch_safe_free(xmlstr); + } + } + done: + + switch_mutex_unlock(sth->mutex); + switch_core_session_rwunlock(sth->session); + + return NULL; +} + +static void speech_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) +{ + struct speech_thread_handle *sth = (struct speech_thread_handle *) user_data; + uint8_t data[SWITCH_RECCOMMENDED_BUFFER_SIZE]; + switch_frame_t frame = {0}; + switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; + + frame.data = data; + frame.buflen = SWITCH_RECCOMMENDED_BUFFER_SIZE; + + switch(type) { + case SWITCH_ABC_TYPE_INIT: { + switch_thread_t *thread; + switch_threadattr_t *thd_attr = NULL; + + switch_threadattr_create(&thd_attr, sth->pool); + switch_threadattr_detach_set(thd_attr, 1); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&thread, thd_attr, speech_thread, sth, sth->pool); + + } + break; + case SWITCH_ABC_TYPE_CLOSE: + switch_core_asr_close(sth->ah, &flags); + switch_mutex_lock(sth->mutex); + switch_thread_cond_signal(sth->cond); + switch_mutex_unlock(sth->mutex); + case SWITCH_ABC_TYPE_READ: + if (sth->ah) { + if (switch_core_media_bug_read(bug, &frame) == SWITCH_STATUS_SUCCESS) { + if (switch_core_asr_feed(sth->ah, frame.data, frame.datalen, &flags) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Error Feeding Data\n"); + return; + } + if (switch_core_asr_check_results(sth->ah, &flags) == SWITCH_STATUS_SUCCESS) { + switch_mutex_lock(sth->mutex); + switch_thread_cond_signal(sth->cond); + switch_mutex_unlock(sth->mutex); + } + } + } + break; + case SWITCH_ABC_TYPE_WRITE: + break; + } +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_stop_detect_speech(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + struct speech_thread_handle *sth; + + assert(channel != NULL); + if ((sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY))) { + switch_channel_set_private(channel, SWITCH_SPEECH_KEY, NULL); + switch_core_media_bug_remove(session, &sth->bug); + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; + +} + + + +SWITCH_DECLARE(switch_status_t) switch_ivr_pause_detect_speech(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + struct speech_thread_handle *sth; + + assert(channel != NULL); + if ((sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY))) { + switch_core_asr_pause(sth->ah); + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; + +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_resume_detect_speech(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + struct speech_thread_handle *sth; + + assert(channel != NULL); + if ((sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY))) { + switch_core_asr_resume(sth->ah); + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; + +} + + +SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_load_grammar(switch_core_session_t *session, char *grammar, char *path) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; + struct speech_thread_handle *sth; + + assert(channel != NULL); + if ((sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY))) { + if (switch_core_asr_load_grammar(sth->ah, grammar, path) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Error loading Grammar\n"); + switch_core_asr_close(sth->ah, &flags); + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return SWITCH_STATUS_FALSE; + } + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; +} + + +SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_unload_grammar(switch_core_session_t *session, char *grammar) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; + struct speech_thread_handle *sth; + + assert(channel != NULL); + if ((sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY))) { + if (switch_core_asr_unload_grammar(sth->ah, grammar) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Error unloading Grammar\n"); + switch_core_asr_close(sth->ah, &flags); + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return SWITCH_STATUS_FALSE; + } + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech(switch_core_session_t *session, + char *mod_name, + char *grammar, + char *path, + char *dest, + switch_asr_handle_t *ah) +{ + switch_channel_t *channel; + switch_codec_t *read_codec; + switch_status_t status; + switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; + struct speech_thread_handle *sth; + char *val; + + if (!ah) { + if (!(ah = switch_core_session_alloc(session, sizeof(*ah)))) { + return SWITCH_STATUS_MEMERR; + } + } + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + read_codec = switch_core_session_get_read_codec(session); + assert(read_codec != NULL); + + + if ((val = switch_channel_get_variable(channel, "fire_asr_events"))) { + switch_set_flag(ah, SWITCH_ASR_FLAG_FIRE_EVENTS); + } + + if ((sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY))) { + if (switch_core_asr_load_grammar(sth->ah, grammar, path) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Error loading Grammar\n"); + switch_core_asr_close(sth->ah, &flags); + return SWITCH_STATUS_FALSE; + } + + return SWITCH_STATUS_SUCCESS; + } + + if (switch_core_asr_open(ah, + mod_name, + "L16", + read_codec->implementation->samples_per_second, + dest, + &flags, + switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) { + + if (switch_core_asr_load_grammar(ah, grammar, path) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Error loading Grammar\n"); + switch_core_asr_close(ah, &flags); + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return SWITCH_STATUS_FALSE; + } + } else { + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return SWITCH_STATUS_FALSE; + } + + + sth = switch_core_session_alloc(session, sizeof(*sth)); + sth->pool = switch_core_session_get_pool(session); + sth->session = session; + sth->ah = ah; + + if ((status = switch_core_media_bug_add(session, + speech_callback, + sth, + SMBF_READ_STREAM, + &sth->bug)) != SWITCH_STATUS_SUCCESS) { + switch_core_asr_close(ah, &flags); + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return status; + } + + switch_channel_set_private(channel, SWITCH_SPEECH_KEY, sth); + + return SWITCH_STATUS_SUCCESS; +} + #define FILE_STARTSAMPLES 1024 * 32 #define FILE_BLOCKSIZE 1024 * 8 #define FILE_BUFSIZE 1024 * 64 @@ -1130,7 +1436,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session int done = 0; int lead_in_out = 10; switch_status_t status = SWITCH_STATUS_SUCCESS; - switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_TTS; + switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE; uint32_t rate = 0, samples = 0; channel = switch_core_session_get_channel(session); @@ -1323,7 +1629,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text(switch_core_session_t *ses int stream_id; switch_status_t status = SWITCH_STATUS_SUCCESS; switch_speech_handle_t sh; - switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_TTS; + switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE; channel = switch_core_session_get_channel(session); @@ -1502,7 +1808,7 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj) status = input_callback(session_a, event, SWITCH_INPUT_TYPE_EVENT, user_data, 0); } - if (switch_core_session_receive_event(session_b, &event) != SWITCH_STATUS_SUCCESS) { + if (event->event_id != SWITCH_EVENT_MESSAGE || switch_core_session_receive_event(session_b, &event) != SWITCH_STATUS_SUCCESS) { switch_event_destroy(&event); } diff --git a/src/switch_loadable_module.c b/src/switch_loadable_module.c index 338b37576d..c530f11677 100644 --- a/src/switch_loadable_module.c +++ b/src/switch_loadable_module.c @@ -51,6 +51,7 @@ struct switch_loadable_module_container { switch_hash_t *api_hash; switch_hash_t *file_hash; switch_hash_t *speech_hash; + switch_hash_t *asr_hash; switch_hash_t *directory_hash; switch_hash_t *chat_hash; switch_memory_pool_t *pool; @@ -218,6 +219,20 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable } } + if (new_module->module_interface->asr_interface) { + const switch_asr_interface_t *ptr; + + for (ptr = new_module->module_interface->asr_interface; ptr; ptr = ptr->next) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Asr interface '%s'\n", ptr->interface_name); + if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "type", "asr"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "name", "%s", ptr->interface_name); + switch_event_fire(&event); + } + switch_core_hash_insert(loadable_modules.asr_hash, (char *) ptr->interface_name, (void *) ptr); + } + } + if (new_module->module_interface->directory_interface) { const switch_directory_interface_t *ptr; @@ -490,6 +505,7 @@ SWITCH_DECLARE(switch_status_t) switch_loadable_module_init() switch_core_hash_init(&loadable_modules.api_hash, loadable_modules.pool); switch_core_hash_init(&loadable_modules.file_hash, loadable_modules.pool); switch_core_hash_init(&loadable_modules.speech_hash, loadable_modules.pool); + switch_core_hash_init(&loadable_modules.asr_hash, loadable_modules.pool); switch_core_hash_init(&loadable_modules.directory_hash, loadable_modules.pool); switch_core_hash_init(&loadable_modules.chat_hash, loadable_modules.pool); switch_core_hash_init(&loadable_modules.dialplan_hash, loadable_modules.pool); @@ -582,12 +598,18 @@ SWITCH_DECLARE(void) switch_loadable_module_shutdown(void) for (hi = switch_hash_first(loadable_modules.pool, loadable_modules.module_hash); hi; hi = switch_hash_next(hi)) { switch_hash_this(hi, NULL, NULL, &val); module = (switch_loadable_module_t *) val; - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Checking %s\t", module->module_interface->module_name); + if (module->switch_module_shutdown) { - switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_CONSOLE, "(yes)\n"); - module->switch_module_shutdown(); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Stopping: %s\n", module->module_interface->module_name); + if (module->switch_module_shutdown() == SWITCH_STATUS_UNLOAD) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "%s unloaded.\n", module->module_interface->module_name); + apr_dso_unload(module->lib); + module->lib = NULL; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "%s shutdown.\n", module->module_interface->module_name); + } } else { - switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_CONSOLE, "(no)\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "%s has no shutdown routine\n", module->module_interface->module_name); } } @@ -648,6 +670,11 @@ SWITCH_DECLARE(switch_speech_interface_t *) switch_loadable_module_get_speech_in return switch_core_hash_find(loadable_modules.speech_hash, name); } +SWITCH_DECLARE(switch_asr_interface_t *) switch_loadable_module_get_asr_interface(char *name) +{ + return switch_core_hash_find(loadable_modules.asr_hash, name); +} + SWITCH_DECLARE(switch_directory_interface_t *) switch_loadable_module_get_directory_interface(char *name) { return switch_core_hash_find(loadable_modules.directory_hash, name);