freeswitch/scripts/js_modules/SpeechTools.jm

451 lines
11 KiB
Plaintext

/*
* 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 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 interp = new XML(body);
_this.lastDetect = body;
if (_this.debug) {
console_log("debug", "----XML:\n" + body + "\n");
console_log("debug", "----Heard [" + interp.input + "]\n");
console_log("debug", "----Hit score " + interp.@score + "/" + grammar_object.min_score + "/" + grammar_object.confirm_score + "\n");
}
if (interp.@score >= grammar_object.min_score) {
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]);
}
} else {
rv.push("_no_idea_");
}
console_log("debug", "dammit: " + rv + "\n");
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;
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");
}
var re = new RegExp(this.items[x]);
match = re.exec(items[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, 1000);
}
if (!rv && !hit && !dup) {
rv = this.react(this.bad_sound, this.bad_sound);
}
}
return this.collected_items;
}
}