forked from Mirrors/freeswitch
484 lines
12 KiB
Plaintext
484 lines
12 KiB
Plaintext
/*
|
|
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
* Copyright (C) 2005/2012, Anthony Minessale II <anthm@freeswitch.org>
|
|
*
|
|
* Version: MPL 1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Anthony Minessale II <anthm@freeswitch.org>
|
|
* Portions created by the Initial Developer are Copyright (C)
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Anthony Minessale II <anthm@freeswitch.org>
|
|
*
|
|
*
|
|
* 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(s, type, inputEvent, _this) {
|
|
try {
|
|
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 false;
|
|
}
|
|
|
|
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 false;
|
|
}
|
|
|
|
if (speech_type == "begin-speaking") {
|
|
if (grammar_object.halt) {
|
|
return false;
|
|
}
|
|
} else {
|
|
var body = inputEvent.getBody();
|
|
var result;
|
|
var xml;
|
|
|
|
body = body.replace(/<\?.*?\?>/g, '');
|
|
xml = new XML("<xml>" + body + "</xml>");
|
|
result = xml.result;
|
|
|
|
_this.lastDetect = body;
|
|
|
|
if (_this.debug) {
|
|
console_log("debug", "----XML:\n" + body + "\n");
|
|
console_log("debug", "----Heard [" + result.interpretation.input + "]\n");
|
|
console_log("debug", "----Hit score " + result.interpretation.@confidence + "/" +
|
|
grammar_object.min_score + "/" + grammar_object.confirm_score + "\n");
|
|
}
|
|
|
|
if (result.interpretation.@confidence >= grammar_object.min_score) {
|
|
if (result.interpretation.@confidence < grammar_object.confirm_score) {
|
|
rv.push("_confirm_");
|
|
}
|
|
|
|
eval("xo = " + grammar_object.obj_path + ";");
|
|
for (x = 0; x < xo.length(); x++) {
|
|
rv.push(xo[x]);
|
|
console_log("info", "----" +xo[x] + "\n");
|
|
}
|
|
} else {
|
|
rv.push("_no_idea_");
|
|
}
|
|
|
|
}
|
|
delete interp;
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
catch(err) {
|
|
console_log("crit", "----ERROR:\n" + err + "\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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];
|
|
}
|
|
}
|
|
|
|
this.addItemAlias = function(item,alias) {
|
|
ia = item.split(",");
|
|
var x;
|
|
for (x = 0; x < ia.length; x++) {
|
|
this.items[this.index++] = ia[x] + ":::" + alias;
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
|
|
|
|
if (!rv) {
|
|
rv = this.asr.session.collectInput(this.asr.onInput, this.asr, 500);
|
|
}
|
|
if (!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);
|
|
}
|
|
|
|
if (rv && !rv[0]) {
|
|
rv = false;
|
|
}
|
|
|
|
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) {
|
|
var items = rv;
|
|
rv = undefined;
|
|
for (y = 0; y < items.length; y++) {
|
|
if (items[y] == "_no_idea_") {
|
|
if (this.debug) {
|
|
console_log("debug", "----We don't understand this\n");
|
|
}
|
|
break;
|
|
}
|
|
if (items[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 [" + y + "] [" + x + "] " + items[y] + " =~ [" + this.items[x] + "]\n");
|
|
}
|
|
str = this.items[x];
|
|
|
|
ab = str.split(":::");
|
|
|
|
var re = new RegExp(ab[0], "i");
|
|
match = re.exec(items[y]);
|
|
|
|
if (match) {
|
|
|
|
for (i = 0; i < match.length; i++) {
|
|
if (ab.length == 1) {
|
|
rep = match[i];
|
|
} else {
|
|
rep = ab[1];
|
|
}
|
|
|
|
dup = false;
|
|
for(z = 0; z < this.collected_items.length; z++) {
|
|
if (this.collected_items[z] == rep) {
|
|
dup = true;
|
|
break;
|
|
}
|
|
}
|
|
if (dup) {
|
|
if (this.dup_sound) {
|
|
rv = this.react(this.dup_sound + " " + rep, this.dup_sound + "," + rep);
|
|
}
|
|
} else {
|
|
if (this.debug) {
|
|
console_log("debug", "----Adding " + rep + "\n");
|
|
}
|
|
this.collected_items[this.collected_index++] = rep;
|
|
hit = true;
|
|
if (this.add_sound) {
|
|
rv = this.react(this.add_sound + " " + rep, this.add_sound + "," + rep);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!rv) {
|
|
rv = this.asr.session.collectInput(this.asr.onInput, this.asr, 1000);
|
|
}
|
|
|
|
if (!rv && !hit && !dup) {
|
|
rv = this.react(this.bad_sound, this.bad_sound);
|
|
}
|
|
}
|
|
|
|
return this.collected_items;
|
|
|
|
}
|
|
}
|