]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
Finalization of speech detect interface and API
authorAnthony Minessale <anthony.minessale@gmail.com>
Thu, 9 Nov 2006 05:39:04 +0000 (05:39 +0000)
committerAnthony Minessale <anthony.minessale@gmail.com>
Thu, 9 Nov 2006 05:39:04 +0000 (05:39 +0000)
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

20 files changed:
scripts/js_modules/SpeechTools.jm [new file with mode: 0644]
src/include/switch_am_config.h.in
src/include/switch_core.h
src/include/switch_ivr.h
src/include/switch_loadable_module.h
src/include/switch_module_interfaces.h
src/include/switch_types.h
src/mod/applications/mod_commands/mod_commands.c
src/mod/applications/mod_conference/mod_conference.c
src/mod/applications/mod_dptools/mod_dptools.c
src/mod/applications/mod_ivrtest/mod_ivrtest.c
src/mod/applications/mod_rss/mod_rss.c
src/mod/asr_tts/mod_cepstral/mod_cepstral.c
src/mod/event_handlers/mod_event_socket/mod_event_socket.c
src/mod/languages/mod_spidermonkey/mod_spidermonkey.c
src/switch_channel.c
src/switch_core.c
src/switch_event.c
src/switch_ivr.c
src/switch_loadable_module.c

diff --git a/scripts/js_modules/SpeechTools.jm b/scripts/js_modules/SpeechTools.jm
new file mode 100644 (file)
index 0000000..725e7fb
--- /dev/null
@@ -0,0 +1,433 @@
+/* 
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
+ *
+ * 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 <anthmct@yahoo.com>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * 
+ * Anthony Minessale II <anthmct@yahoo.com>
+ *
+ *
+ * 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;
+
+       }
+}
index 3e1a92ccaf65c2286357c9d425fa5f513a7ad27a..c2a949cebefad34dbeedbfa1536589dd6bd88616 100644 (file)
 /* Define to rpl_malloc if the replacement function should be used. */
 #undef malloc
 
-/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* Define to `unsigned' if <sys/types.h> does not define. */
 #undef size_t
index cf8697d6211bef29937e2805a767e8eeffe3c9ba..d108242d902cec6e58b0a620616ec13e4e48af98 100644 (file)
@@ -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);
+
 ///\}
 
 
index df52f665cd30ee851ba2ce1493ef503c524e2310..c8c23973f454a2b7ef712a3fc889d5de6a563bd6 100644 (file)
@@ -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
index 2cfbeb4b11d62b6180f48051efc0b443bc20faa0..e3d3a7c4e45f1fe19be16077452d25b96ec0fc94 100644 (file)
@@ -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
index 33940c142c5ec8921c82eb9c83b5920b20a53b76..a1541e8895f3b0351ab24557964843cca6f8096b 100644 (file)
@@ -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,
index 637ced38f1f75f89d90551f053242e2723452245..02fb2372290d6fee47fc7e0f58b469a85248f557 100644 (file)
@@ -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
 </pre>
  */
 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
 <pre>
-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
 </pre>
 */
 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
+<pre>
+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
+</pre>
+*/
+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
+<pre>
+SMBF_READ_STREAM - Include the Read Stream
+SMBF_WRITE_STREAM - Include the Write Stream
+</pre>
+*/
+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
 </pre>
 
@@ -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;
index 4985501290cb519dcb42add66af65f9d08beb673..42b127baf1c68d58f96f5c0bce4b02501c919e45 100644 (file)
@@ -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;;
 }
 
index 9b9b14f2ed882f0f6324bc28759d5caa05fe117a..516195751553a3110b33d15b939018365b94e07e 100644 (file)
@@ -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);
index 5bb8d5726aef6ad64121643fefcad1f8c4a42a9a..0bdd81aaaca25189c692b98fa5c5b53dda1725a6 100644 (file)
 
 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 */ "<mod_name> <gram_name> <gram_path> [<addr>] OR grammar <gram_name> [<path>] 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 = {
index 1ea827bd901d77d49540f2764c7c1169d2dcfcbc..03b7abec926d67dd3f8d35792b2e6f1a8cf2ebe2 100644 (file)
@@ -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)
index d71e1c818729357720bbc55d04d6c7489374e9b4..ccfb3323bc7e0993499b2cd3dd244df50dfcea3a 100644 (file)
@@ -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;
index 2814da34d9c142026c58cfba2a98143bdba9117e..573a235988ff922d0570904d23dbb5c62eb1b4b4 100644 (file)
@@ -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;
-       }
-       if (*flags & SWITCH_SPEECH_FLAG_TTS) {
-               cepstral_t *cepstral = switch_core_alloc(sh->memory_pool, sizeof(*cepstral));
-               char srate[25];
+       cepstral_t *cepstral = switch_core_alloc(sh->memory_pool, sizeof(*cepstral));
+       char srate[25];
 
-               if (!cepstral) {
-                       return SWITCH_STATUS_MEMERR;
-               }
+       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.");
-                       goto all_done;
-               }
+       /* 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;
-                       }
-
-                       /* 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 && 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;
+       }
 
-                       voice_name = (char *) swift_voice_get_attribute(cepstral->voice, "name");
+       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) {
-            switch_copy_string(sh->voice, voice_name, sizeof(sh->voice));
-        }
+               /* 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;
+               }
 
-               swift_port_set_callback(cepstral->port, &write_audio, SWIFT_EVENT_AUDIO, cepstral);
+               voice_name = (char *) swift_voice_get_attribute(cepstral->voice, "name");
+       }
 
-               sh->private_info = cepstral;
-               return SWITCH_STATUS_SUCCESS;
+       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, "<break time=\"400ms\"/>", 0, NULL, &cepstral->tts_stream, NULL); 
+               swift_port_speak_text(cepstral->port, "<break time=\"500ms\"/>", 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,
index e8bedd3b6ba4b2ff25ba7eba744e2ea887ebed46..a56e14931c5c7f5054f45b0c5d72b748264ce8ca 100644 (file)
@@ -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;
                }
                
index bcacacc570f5016cf3ba4997268643bc685ea476..1cf92206ea143f611efe4c0987ccca60b90a36e4 100644 (file)
@@ -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 );
-                       return JS_TRUE;
-               }
+               if ((eo = malloc(sizeof(*eo)))) {
 
-               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) {
+                       if (switch_name_event(ename, &etype) != 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;
+                       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;
+                       eo->event = event;
+                       eo->freed = 0;
+
+                       JS_SetPrivate(cx, obj, eo);
+                       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)
+{
+       struct event_obj *eo;
+       JSObject *Event = NULL;
+
+       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 Event;
+}
+
+
+static switch_status_t js_common_callback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
 {
+       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;
+
+       if (!jss) {
+               return SWITCH_STATUS_FALSE;
+       }
+
        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) {
+       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;
                }
-       
-               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);
-                       }
+               break;
+       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;
+       }
 
-                       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 (cb_state->arg) {
+               argv[argc++] = cb_state->arg;
+       }
+
+       JS_CallFunction(cb_state->cx, cb_state->obj, cb_state->function, argc, argv, &cb_state->ret);
 
-                       if (!strncasecmp(ret, "speed", 4)) {
-                               char *p;
+       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;
+                       if ((p = strchr(ret, ':'))) {
+                               p++;
+                               if (*p == '+' || *p == '-') {
+                                       int step;
+                                       if (!(step = atoi(p))) {
+                                               step = 1;
                                        }
-                                       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);
+                                       fh->speed += step;
                                } else {
-                                       switch_set_flag(fh, SWITCH_FILE_PAUSE);
+                                       int speed = atoi(p);
+                                       fh->speed = speed;
                                }
                                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);
-                                               }
+                       }
+                       
+                       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 = atoi(p) * (codec->implementation->samples_per_second / 1000);
-                                               switch_core_file_seek(fh, &pos, samps, SEEK_SET);
+                                               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));
-                       }
+                       return SWITCH_STATUS_SUCCESS;
                }
 
-               return SWITCH_STATUS_FALSE;
-       }
-               break;
-       default:
-               break;
+               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_dtmf_callback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
+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)
 {
-       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 *ret;
+       switch_status_t status;
+       struct input_callback_state *cb_state = buf;
+       switch_file_handle_t *fh = cb_state->extra;
 
-                       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;
-                               }
+       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 {
-                       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;
-                       }
+               } 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 (!strcmp(ret, "true") || !strcmp(ret, "undefined")) {
+                       return SWITCH_STATUS_SUCCESS;
                }
-               return SWITCH_STATUS_FALSE;
-       }
-               break;
-       default:
-               break;
+       
+               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 ((status = js_common_callback(session, input, itype, buf, buflen)) != SWITCH_STATUS_SUCCESS) {
+               return status;
+       }
 
-                       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 ((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_FALSE;
-       }
-               break;
-       default:
-               break;
+               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,7 +1023,9 @@ static JSBool session_ready(JSContext *cx, JSObject *obj, uintN argc, jsval *arg
        return JS_TRUE;
 }
 
-static JSBool session_wait_for_answer(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+
+
+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;
@@ -987,15 +1041,52 @@ 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) || 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);
+       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)) {
+                       *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 );    
-
-       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;
+    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 > 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);
                }
        }
 
index 832aed1d9195a2df31816215d3b75488e73455c0..06c60d9533b9b6a620b1ea7745785ff531bea80f 100644 (file)
@@ -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;
index 32b85b1640ed80d83b20e67349c7f3fe5627fd91..15db1b084e25a83b212b813b1cd49e110d1ebfb7 100644 (file)
@@ -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 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 sh->speech_interface->speech_interpret_asr(sh, buf, buflen, flags);
+       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
 
index 3620e3388947f44677d1f0ba30cde665995b3164..c53ff1dec080937fe818e97b0d40df7964ccc310 100644 (file)
@@ -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;
 }
 
index aff0ef45860d0ad5fdd78b3969de2687e07657a3..bc0a534f997cf5114a4629271c5e7af09bd327d7 100644 (file)
@@ -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);
                        }
 
index 338b37576d1e4fec7dc1a074ae02ff5cb53309e3..c530f1167752893e2b1e3bcb6db420a77787c987 100644 (file)
@@ -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);