diff options
Diffstat (limited to 'chromium/chrome/browser/resources/app_list/speech_manager.js')
-rw-r--r-- | chromium/chrome/browser/resources/app_list/speech_manager.js | 171 |
1 files changed, 135 insertions, 36 deletions
diff --git a/chromium/chrome/browser/resources/app_list/speech_manager.js b/chromium/chrome/browser/resources/app_list/speech_manager.js index 90f099e4e5b..31c9812452a 100644 --- a/chromium/chrome/browser/resources/app_list/speech_manager.js +++ b/chromium/chrome/browser/resources/app_list/speech_manager.js @@ -22,25 +22,42 @@ cr.define('speech', function() { READY: 'READY', HOTWORD_RECOGNIZING: 'HOTWORD_RECOGNIZING', RECOGNIZING: 'RECOGNIZING', - STOPPING: 'STOPPING' + IN_SPEECH: 'IN_SPEECH', + STOPPING: 'STOPPING', + NETWORK_ERROR: 'NETWORK_ERROR' }; /** + * The time to show the network error message in seconds. + * + * @const {number} + */ + var SPEECH_ERROR_TIMEOUT = 3; + + /** + * Checks the prefix for the hotword module based on the language. This is + * fragile if the file structure has changed. + */ + function getHotwordPrefix() { + var prefix = navigator.language.toLowerCase(); + if (prefix == 'en-gb') + return prefix; + var hyphen = prefix.indexOf('-'); + if (hyphen >= 0) + prefix = prefix.substr(0, hyphen); + if (prefix == 'en') + prefix = ''; + return prefix; + } + + /** * @constructor */ function SpeechManager() { this.audioManager_ = new speech.AudioManager(); this.audioManager_.addEventListener('audio', this.onAudioLevel_.bind(this)); - if (speech.isPluginAvailable()) { - var pluginManager = new speech.PluginManager( - this.onHotwordRecognizerReady_.bind(this), - this.onHotwordRecognized_.bind(this)); - pluginManager.scheduleInitialize( - this.audioManager_.getSampleRate(), - 'chrome://app-list/okgoogle_hotword.config'); - } this.speechRecognitionManager_ = new speech.SpeechRecognitionManager(this); - this.setState_(SpeechState.READY); + this.errorTimeoutId_ = null; } /** @@ -50,7 +67,11 @@ cr.define('speech', function() { * @private */ SpeechManager.prototype.setState_ = function(newState) { + if (this.state == newState) + return; + this.state = newState; + chrome.send('setSpeechRecognitionState', [this.state]); }; /** @@ -79,19 +100,29 @@ cr.define('speech', function() { this.pluginManager_ = pluginManager; this.audioManager_.addEventListener( 'audio', pluginManager.sendAudioData.bind(pluginManager)); + this.pluginManager_.startRecognizer(); + this.audioManager_.start(); + this.setState_(SpeechState.HOTWORD_RECOGNIZING); + }; + + /** + * Called when an error happens for loading the hotword recognizer. + * + * @private + */ + SpeechManager.prototype.onHotwordRecognizerLoadError_ = function() { + this.setHotwordEnabled(false); this.setState_(SpeechState.READY); }; /** * Called when the hotword is recognized. * - * @param {number} confidence The confidence store of the recognition. * @private */ - SpeechManager.prototype.onHotwordRecognized_ = function(confidence) { + SpeechManager.prototype.onHotwordRecognized_ = function() { if (this.state != SpeechState.HOTWORD_RECOGNIZING) return; - this.setState_(SpeechState.READY); this.pluginManager_.stopRecognizer(); this.speechRecognitionManager_.start(); }; @@ -113,13 +144,18 @@ cr.define('speech', function() { */ SpeechManager.prototype.onSpeechRecognitionStarted = function() { this.setState_(SpeechState.RECOGNIZING); - chrome.send('setSpeechRecognitionState', ['on']); }; /** * Called when the speech recognition has ended. */ SpeechManager.prototype.onSpeechRecognitionEnded = function() { + // Do not handle the speech recognition ends if it ends due to an error + // because an error message should be shown for a while. + // See onSpeechRecognitionError. + if (this.state == SpeechState.NETWORK_ERROR) + return; + // Restarts the hotword recognition. if (this.state != SpeechState.STOPPING && this.pluginManager_) { this.pluginManager_.startRecognizer(); @@ -127,8 +163,8 @@ cr.define('speech', function() { this.setState_(SpeechState.HOTWORD_RECOGNIZING); } else { this.audioManager_.stop(); + this.setState_(SpeechState.READY); } - chrome.send('setSpeechRecognitionState', ['off']); }; /** @@ -136,15 +172,26 @@ cr.define('speech', function() { */ SpeechManager.prototype.onSpeechStarted = function() { if (this.state == SpeechState.RECOGNIZING) - chrome.send('setSpeechRecognitionState', ['in-speech']); + this.setState_(SpeechState.IN_SPEECH); }; /** * Called when a speech has ended. */ SpeechManager.prototype.onSpeechEnded = function() { - if (this.state == SpeechState.RECOGNIZING) - chrome.send('setSpeechRecognitionState', ['on']); + if (this.state == SpeechState.IN_SPEECH) + this.setState_(SpeechState.RECOGNIZING); + }; + + /** + * Called when the speech manager should recover from the error state. + * + * @private + */ + SpeechManager.prototype.onSpeechRecognitionErrorTimeout_ = function() { + this.errorTimeoutId_ = null; + this.setState_(SpeechState.READY); + this.onSpeechRecognitionEnded(); }; /** @@ -153,37 +200,84 @@ cr.define('speech', function() { * @param {SpeechRecognitionError} e The error object. */ SpeechManager.prototype.onSpeechRecognitionError = function(e) { - if (this.state != SpeechState.STOPPING) - this.setState_(SpeechState.READY); + if (e.error == 'network') { + this.setState_(SpeechState.NETWORK_ERROR); + this.errorTimeoutId_ = window.setTimeout( + this.onSpeechRecognitionErrorTimeout_.bind(this), + SPEECH_ERROR_TIMEOUT * 1000); + } else { + if (this.state != SpeechState.STOPPING) + this.setState_(SpeechState.READY); + } }; /** - * Starts the speech recognition session. + * Changes the availability of the hotword plugin. + * + * @param {boolean} enabled Whether enabled or not. */ - SpeechManager.prototype.start = function() { - if (!this.pluginManager_) - return; + SpeechManager.prototype.setHotwordEnabled = function(enabled) { + var recognizer = $('recognizer'); + if (enabled) { + if (recognizer) + return; + if (!this.naclArch) + return; - if (this.state == SpeechState.HOTWORD_RECOGNIZING) { - console.warn('Already in recognition state...'); - return; + var prefix = getHotwordPrefix(); + var pluginManager = new speech.PluginManager( + prefix, + this.onHotwordRecognizerReady_.bind(this), + this.onHotwordRecognized_.bind(this), + this.onHotwordRecognizerLoadError_.bind(this)); + var modelUrl = 'chrome://app-list/_platform_specific/' + this.naclArch + + '_' + prefix + '/hotword.data'; + pluginManager.scheduleInitialize(this.audioManager_.sampleRate, modelUrl); + } else { + if (!recognizer) + return; + document.body.removeChild(recognizer); + this.pluginManager_ = null; + if (this.state == SpeechState.HOTWORD_RECOGNIZING) { + this.audioManager_.stop(); + this.setState_(SpeechState.READY); + } } + }; - this.pluginManager_.startRecognizer(); - this.audioManager_.start(); - this.setState_(SpeechState.HOTWORD_RECOGNIZING); + /** + * Sets the NaCl architecture for the hotword module. + * + * @param {string} arch The architecture. + */ + SpeechManager.prototype.setNaclArch = function(arch) { + this.naclArch = arch; + }; + + /** + * Called when the app-list bubble is shown. + * + * @param {boolean} hotwordEnabled Whether the hotword is enabled or not. + */ + SpeechManager.prototype.onShown = function(hotwordEnabled) { + this.setHotwordEnabled(hotwordEnabled); + + // No one sets the state if the content is initialized on shown but hotword + // is not enabled. Sets the state in such case. + if (!this.state && !hotwordEnabled) + this.setState_(SpeechState.READY); }; /** - * Stops the speech recognition session. + * Called when the app-list bubble is hidden. */ - SpeechManager.prototype.stop = function() { - if (this.pluginManager_) - this.pluginManager_.stopRecognizer(); + SpeechManager.prototype.onHidden = function() { + this.setHotwordEnabled(false); // SpeechRecognition is asynchronous. this.audioManager_.stop(); - if (this.state == SpeechState.RECOGNIZING) { + if (this.state == SpeechState.RECOGNIZING || + this.state == SpeechState.IN_SPEECH) { this.setState_(SpeechState.STOPPING); this.speechRecognitionManager_.stop(); } else { @@ -195,7 +289,12 @@ cr.define('speech', function() { * Toggles the current state of speech recognition. */ SpeechManager.prototype.toggleSpeechRecognition = function() { - if (this.state == SpeechState.RECOGNIZING) { + if (this.state == SpeechState.NETWORK_ERROR) { + if (this.errorTimeoutId_) + window.clearTimeout(this.errorTimeoutId_); + this.onSpeechRecognitionErrorTimeout_(); + } else if (this.state == SpeechState.RECOGNIZING || + this.state == SpeechState.IN_SPEECH) { this.audioManager_.stop(); this.speechRecognitionManager_.stop(); } else { |