Index: chrome/browser/resources/hotword/nacl_manager.js |
diff --git a/chrome/browser/resources/hotword/nacl_manager.js b/chrome/browser/resources/hotword/nacl_manager.js |
index 5a1dd556523b477838dc7c0d2543c8e7f54e45fd..ad4a8896d414637c5be9e69e09d1a4836ca9a6b0 100644 |
--- a/chrome/browser/resources/hotword/nacl_manager.js |
+++ b/chrome/browser/resources/hotword/nacl_manager.js |
@@ -3,599 +3,609 @@ |
// found in the LICENSE file. |
cr.define('hotword', function() { |
-'use strict'; |
- |
-/** |
- * Class used to manage the state of the NaCl recognizer plugin. Handles all |
- * control of the NaCl plugin, including creation, start, stop, trigger, and |
- * shutdown. |
- * |
- * @param {boolean} loggingEnabled Whether audio logging is enabled. |
- * @param {boolean} hotwordStream Whether the audio input stream is from a |
- * hotword stream. |
- * @constructor |
- * @extends {cr.EventTarget} |
- */ |
-function NaClManager(loggingEnabled, hotwordStream) { |
- /** |
- * Current state of this manager. |
- * @private {hotword.NaClManager.ManagerState_} |
- */ |
- this.recognizerState_ = ManagerState_.UNINITIALIZED; |
+ 'use strict'; |
/** |
- * The window.timeout ID associated with a pending message. |
- * @private {?number} |
+ * Class used to manage the state of the NaCl recognizer plugin. Handles all |
+ * control of the NaCl plugin, including creation, start, stop, trigger, and |
+ * shutdown. |
+ * |
+ * @param {boolean} loggingEnabled Whether audio logging is enabled. |
+ * @param {boolean} hotwordStream Whether the audio input stream is from a |
+ * hotword stream. |
+ * @constructor |
+ * @extends {cr.EventTarget} |
*/ |
- this.naclTimeoutId_ = null; |
+ function NaClManager(loggingEnabled, hotwordStream) { |
+ /** |
+ * Current state of this manager. |
+ * @private {hotword.NaClManager.ManagerState_} |
+ */ |
+ this.recognizerState_ = ManagerState_.UNINITIALIZED; |
+ |
+ /** |
+ * The window.timeout ID associated with a pending message. |
+ * @private {?number} |
+ */ |
+ this.naclTimeoutId_ = null; |
+ |
+ /** |
+ * The expected message that will cancel the current timeout. |
+ * @private {?string} |
+ */ |
+ this.expectingMessage_ = null; |
- /** |
- * The expected message that will cancel the current timeout. |
- * @private {?string} |
- */ |
- this.expectingMessage_ = null; |
+ /** |
+ * Whether the plugin will be started as soon as it stops. |
+ * @private {boolean} |
+ */ |
+ this.restartOnStop_ = false; |
+ |
+ /** |
+ * NaCl plugin element on extension background page. |
+ * @private {?HTMLEmbedElement} |
+ */ |
+ this.plugin_ = null; |
+ |
+ /** |
+ * URL containing hotword-model data file. |
+ * @private {string} |
+ */ |
+ this.modelUrl_ = ''; |
+ |
+ /** |
+ * Media stream containing an audio input track. |
+ * @private {?MediaStream} |
+ */ |
+ this.stream_ = null; |
+ |
+ /** |
+ * The mode to start the recognizer in. |
+ * @private {?chrome.hotwordPrivate.RecognizerStartMode} |
+ */ |
+ this.startMode_ = hotword.constants.RecognizerStartMode.NORMAL; |
+ |
+ /** |
+ * Whether audio logging is enabled. |
+ * @private {boolean} |
+ */ |
+ this.loggingEnabled_ = loggingEnabled; |
+ |
+ /** |
+ * Whether the audio input stream is from a hotword stream. |
+ * @private {boolean} |
+ */ |
+ this.hotwordStream_ = hotwordStream; |
+ |
+ /** |
+ * Audio log of X seconds before hotword triggered. |
+ * @private {?Object} |
+ */ |
+ this.preambleLog_ = null; |
+ }; |
/** |
- * Whether the plugin will be started as soon as it stops. |
- * @private {boolean} |
+ * States this manager can be in. Since messages to/from the plugin are |
+ * asynchronous (and potentially queued), it's not possible to know what state |
+ * the plugin is in. However, track a state machine for NaClManager based on |
+ * what messages are sent/received. |
+ * @enum {number} |
+ * @private |
*/ |
- this.restartOnStop_ = false; |
+ NaClManager.ManagerState_ = { |
+ UNINITIALIZED: 0, |
+ LOADING: 1, |
+ STOPPING: 2, |
+ STOPPED: 3, |
+ STARTING: 4, |
+ RUNNING: 5, |
+ ERROR: 6, |
+ SHUTDOWN: 7, |
+ }; |
+ var ManagerState_ = NaClManager.ManagerState_; |
+ var Error_ = hotword.constants.Error; |
+ var UmaNaClMessageTimeout_ = hotword.constants.UmaNaClMessageTimeout; |
+ var UmaNaClPluginLoadResult_ = hotword.constants.UmaNaClPluginLoadResult; |
+ |
+ NaClManager.prototype.__proto__ = cr.EventTarget.prototype; |
/** |
- * NaCl plugin element on extension background page. |
- * @private {?HTMLEmbedElement} |
+ * Called when an error occurs. Dispatches an event. |
+ * @param {!hotword.constants.Error} error |
+ * @private |
*/ |
- this.plugin_ = null; |
+ NaClManager.prototype.handleError_ = function(error) { |
+ var event = new Event(hotword.constants.Event.ERROR); |
+ event.data = error; |
+ this.dispatchEvent(event); |
+ }; |
/** |
- * URL containing hotword-model data file. |
- * @private {string} |
+ * Record the result of loading the NaCl plugin to UMA. |
+ * @param {!hotword.constants.UmaNaClPluginLoadResult} error |
+ * @private |
*/ |
- this.modelUrl_ = ''; |
+ NaClManager.prototype.logPluginLoadResult_ = function(error) { |
+ hotword.metrics.recordEnum( |
+ hotword.constants.UmaMetrics.NACL_PLUGIN_LOAD_RESULT, error, |
+ UmaNaClPluginLoadResult_.MAX); |
+ }; |
/** |
- * Media stream containing an audio input track. |
- * @private {?MediaStream} |
+ * Set a timeout. Only allow one timeout to exist at any given time. |
+ * @param {!function()} func |
+ * @param {number} timeout |
+ * @private |
*/ |
- this.stream_ = null; |
+ NaClManager.prototype.setTimeout_ = function(func, timeout) { |
+ assert(!this.naclTimeoutId_, 'Timeout already exists'); |
+ this.naclTimeoutId_ = window.setTimeout(function() { |
+ this.naclTimeoutId_ = null; |
+ func(); |
+ }.bind(this), timeout); |
+ }; |
/** |
- * The mode to start the recognizer in. |
- * @private {?chrome.hotwordPrivate.RecognizerStartMode} |
+ * Clears the current timeout. |
+ * @private |
*/ |
- this.startMode_ = hotword.constants.RecognizerStartMode.NORMAL; |
+ NaClManager.prototype.clearTimeout_ = function() { |
+ window.clearTimeout(this.naclTimeoutId_); |
+ this.naclTimeoutId_ = null; |
+ }; |
/** |
- * Whether audio logging is enabled. |
- * @private {boolean} |
+ * Starts a stopped or stopping hotword recognizer (NaCl plugin). |
+ * @param {hotword.constants.RecognizerStartMode} mode The mode to start the |
+ * recognizer in. |
*/ |
- this.loggingEnabled_ = loggingEnabled; |
+ NaClManager.prototype.startRecognizer = function(mode) { |
+ this.startMode_ = mode; |
+ if (this.recognizerState_ == ManagerState_.STOPPED) { |
+ this.preambleLog_ = null; |
+ this.recognizerState_ = ManagerState_.STARTING; |
+ if (mode == hotword.constants.RecognizerStartMode.NEW_MODEL) { |
+ hotword.debug('Starting Recognizer in START training mode'); |
+ this.sendDataToPlugin_( |
+ hotword.constants.NaClPlugin.BEGIN_SPEAKER_MODEL); |
+ } else if (mode == hotword.constants.RecognizerStartMode.ADAPT_MODEL) { |
+ hotword.debug('Starting Recognizer in ADAPT training mode'); |
+ this.sendDataToPlugin_( |
+ hotword.constants.NaClPlugin.ADAPT_SPEAKER_MODEL); |
+ } else { |
+ hotword.debug('Starting Recognizer in NORMAL mode'); |
+ this.sendDataToPlugin_(hotword.constants.NaClPlugin.RESTART); |
+ } |
+ // Normally, there would be a waitForMessage_(READY_FOR_AUDIO) here. |
+ // However, this message is sent the first time audio data is read and in |
+ // some cases (ie. using the hotword stream), this won't happen until a |
+ // potential hotword trigger is seen. Having a waitForMessage_() would |
+ // time |
+ // out in this case, so just leave it out. This ends up sacrificing a bit |
+ // of |
+ // error detection in the non-hotword-stream case, but I think we can live |
+ // with that. |
+ } else if (this.recognizerState_ == ManagerState_.STOPPING) { |
+ // Wait until the plugin is stopped before trying to start it. |
+ this.restartOnStop_ = true; |
+ } else { |
+ throw 'Attempting to start NaCl recogniser not in STOPPED or STOPPING ' + |
+ 'state'; |
+ } |
+ }; |
/** |
- * Whether the audio input stream is from a hotword stream. |
- * @private {boolean} |
+ * Stops the hotword recognizer. |
*/ |
- this.hotwordStream_ = hotwordStream; |
+ NaClManager.prototype.stopRecognizer = function() { |
+ if (this.recognizerState_ == ManagerState_.STARTING) { |
+ // If the recognizer is stopped before it finishes starting, it causes an |
+ // assertion to be raised in waitForMessage_() since we're waiting for the |
+ // READY_FOR_AUDIO message. Clear the current timeout and expecting |
+ // message |
+ // since we no longer expect it and may never receive it. |
+ this.clearTimeout_(); |
+ this.expectingMessage_ = null; |
+ } |
+ this.sendDataToPlugin_(hotword.constants.NaClPlugin.STOP); |
+ this.recognizerState_ = ManagerState_.STOPPING; |
+ this.waitForMessage_( |
+ hotword.constants.TimeoutMs.NORMAL, |
+ hotword.constants.NaClPlugin.STOPPED); |
+ }; |
/** |
- * Audio log of X seconds before hotword triggered. |
- * @private {?Object} |
+ * Saves the speaker model. |
*/ |
- this.preambleLog_ = null; |
-}; |
- |
-/** |
- * States this manager can be in. Since messages to/from the plugin are |
- * asynchronous (and potentially queued), it's not possible to know what state |
- * the plugin is in. However, track a state machine for NaClManager based on |
- * what messages are sent/received. |
- * @enum {number} |
- * @private |
- */ |
-NaClManager.ManagerState_ = { |
- UNINITIALIZED: 0, |
- LOADING: 1, |
- STOPPING: 2, |
- STOPPED: 3, |
- STARTING: 4, |
- RUNNING: 5, |
- ERROR: 6, |
- SHUTDOWN: 7, |
-}; |
-var ManagerState_ = NaClManager.ManagerState_; |
-var Error_ = hotword.constants.Error; |
-var UmaNaClMessageTimeout_ = hotword.constants.UmaNaClMessageTimeout; |
-var UmaNaClPluginLoadResult_ = hotword.constants.UmaNaClPluginLoadResult; |
- |
-NaClManager.prototype.__proto__ = cr.EventTarget.prototype; |
- |
-/** |
- * Called when an error occurs. Dispatches an event. |
- * @param {!hotword.constants.Error} error |
- * @private |
- */ |
-NaClManager.prototype.handleError_ = function(error) { |
- var event = new Event(hotword.constants.Event.ERROR); |
- event.data = error; |
- this.dispatchEvent(event); |
-}; |
- |
-/** |
- * Record the result of loading the NaCl plugin to UMA. |
- * @param {!hotword.constants.UmaNaClPluginLoadResult} error |
- * @private |
- */ |
-NaClManager.prototype.logPluginLoadResult_ = function(error) { |
- hotword.metrics.recordEnum( |
- hotword.constants.UmaMetrics.NACL_PLUGIN_LOAD_RESULT, |
- error, |
- UmaNaClPluginLoadResult_.MAX); |
-}; |
- |
-/** |
- * Set a timeout. Only allow one timeout to exist at any given time. |
- * @param {!function()} func |
- * @param {number} timeout |
- * @private |
- */ |
-NaClManager.prototype.setTimeout_ = function(func, timeout) { |
- assert(!this.naclTimeoutId_, 'Timeout already exists'); |
- this.naclTimeoutId_ = window.setTimeout( |
- function() { |
- this.naclTimeoutId_ = null; |
- func(); |
- }.bind(this), timeout); |
-}; |
- |
-/** |
- * Clears the current timeout. |
- * @private |
- */ |
-NaClManager.prototype.clearTimeout_ = function() { |
- window.clearTimeout(this.naclTimeoutId_); |
- this.naclTimeoutId_ = null; |
-}; |
- |
-/** |
- * Starts a stopped or stopping hotword recognizer (NaCl plugin). |
- * @param {hotword.constants.RecognizerStartMode} mode The mode to start the |
- * recognizer in. |
- */ |
-NaClManager.prototype.startRecognizer = function(mode) { |
- this.startMode_ = mode; |
- if (this.recognizerState_ == ManagerState_.STOPPED) { |
- this.preambleLog_ = null; |
- this.recognizerState_ = ManagerState_.STARTING; |
- if (mode == hotword.constants.RecognizerStartMode.NEW_MODEL) { |
- hotword.debug('Starting Recognizer in START training mode'); |
- this.sendDataToPlugin_(hotword.constants.NaClPlugin.BEGIN_SPEAKER_MODEL); |
- } else if (mode == hotword.constants.RecognizerStartMode.ADAPT_MODEL) { |
- hotword.debug('Starting Recognizer in ADAPT training mode'); |
- this.sendDataToPlugin_(hotword.constants.NaClPlugin.ADAPT_SPEAKER_MODEL); |
- } else { |
- hotword.debug('Starting Recognizer in NORMAL mode'); |
- this.sendDataToPlugin_(hotword.constants.NaClPlugin.RESTART); |
+ NaClManager.prototype.finalizeSpeakerModel = function() { |
+ if (this.recognizerState_ == ManagerState_.UNINITIALIZED || |
+ this.recognizerState_ == ManagerState_.ERROR || |
+ this.recognizerState_ == ManagerState_.SHUTDOWN || |
+ this.recognizerState_ == ManagerState_.LOADING) { |
+ return; |
} |
- // Normally, there would be a waitForMessage_(READY_FOR_AUDIO) here. |
- // However, this message is sent the first time audio data is read and in |
- // some cases (ie. using the hotword stream), this won't happen until a |
- // potential hotword trigger is seen. Having a waitForMessage_() would time |
- // out in this case, so just leave it out. This ends up sacrificing a bit of |
- // error detection in the non-hotword-stream case, but I think we can live |
- // with that. |
- } else if (this.recognizerState_ == ManagerState_.STOPPING) { |
- // Wait until the plugin is stopped before trying to start it. |
- this.restartOnStop_ = true; |
- } else { |
- throw 'Attempting to start NaCl recogniser not in STOPPED or STOPPING ' + |
- 'state'; |
- } |
-}; |
- |
-/** |
- * Stops the hotword recognizer. |
- */ |
-NaClManager.prototype.stopRecognizer = function() { |
- if (this.recognizerState_ == ManagerState_.STARTING) { |
- // If the recognizer is stopped before it finishes starting, it causes an |
- // assertion to be raised in waitForMessage_() since we're waiting for the |
- // READY_FOR_AUDIO message. Clear the current timeout and expecting message |
- // since we no longer expect it and may never receive it. |
- this.clearTimeout_(); |
- this.expectingMessage_ = null; |
- } |
- this.sendDataToPlugin_(hotword.constants.NaClPlugin.STOP); |
- this.recognizerState_ = ManagerState_.STOPPING; |
- this.waitForMessage_(hotword.constants.TimeoutMs.NORMAL, |
- hotword.constants.NaClPlugin.STOPPED); |
-}; |
- |
-/** |
- * Saves the speaker model. |
- */ |
-NaClManager.prototype.finalizeSpeakerModel = function() { |
- if (this.recognizerState_ == ManagerState_.UNINITIALIZED || |
- this.recognizerState_ == ManagerState_.ERROR || |
- this.recognizerState_ == ManagerState_.SHUTDOWN || |
- this.recognizerState_ == ManagerState_.LOADING) { |
- return; |
- } |
- this.sendDataToPlugin_(hotword.constants.NaClPlugin.FINISH_SPEAKER_MODEL); |
-}; |
- |
-/** |
+ this.sendDataToPlugin_(hotword.constants.NaClPlugin.FINISH_SPEAKER_MODEL); |
+ }; |
+ |
+ /** |
* Checks whether the file at the given path exists. |
* @param {!string} path Path to a file. Can be any valid URL. |
* @return {boolean} True if the patch exists. |
* @private |
*/ |
-NaClManager.prototype.fileExists_ = function(path) { |
- var xhr = new XMLHttpRequest(); |
- xhr.open('HEAD', path, false); |
- try { |
- xhr.send(); |
- } catch (err) { |
- return false; |
- } |
- if (xhr.readyState != xhr.DONE || xhr.status != 200) { |
- return false; |
- } |
- return true; |
-}; |
+ NaClManager.prototype.fileExists_ = function(path) { |
+ var xhr = new XMLHttpRequest(); |
+ xhr.open('HEAD', path, false); |
+ try { |
+ xhr.send(); |
+ } catch (err) { |
+ return false; |
+ } |
+ if (xhr.readyState != xhr.DONE || xhr.status != 200) { |
+ return false; |
+ } |
+ return true; |
+ }; |
-/** |
+ /** |
* Creates and returns a list of possible languages to check for hotword |
* support. |
* @return {!Array<string>} Array of languages. |
* @private |
*/ |
-NaClManager.prototype.getPossibleLanguages_ = function() { |
- // Create array used to search first for language-country, if not found then |
- // search for language, if not found then no language (empty string). |
- // For example, search for 'en-us', then 'en', then ''. |
- var langs = new Array(); |
- if (hotword.constants.UI_LANGUAGE) { |
- // Chrome webstore doesn't support uppercase path: crbug.com/353407 |
- var language = hotword.constants.UI_LANGUAGE.toLowerCase(); |
- langs.push(language); // Example: 'en-us'. |
- // Remove country to add just the language to array. |
- var hyphen = language.lastIndexOf('-'); |
- if (hyphen >= 0) { |
- langs.push(language.substr(0, hyphen)); // Example: 'en'. |
+ NaClManager.prototype.getPossibleLanguages_ = function() { |
+ // Create array used to search first for language-country, if not found then |
+ // search for language, if not found then no language (empty string). |
+ // For example, search for 'en-us', then 'en', then ''. |
+ var langs = new Array(); |
+ if (hotword.constants.UI_LANGUAGE) { |
+ // Chrome webstore doesn't support uppercase path: crbug.com/353407 |
+ var language = hotword.constants.UI_LANGUAGE.toLowerCase(); |
+ langs.push(language); // Example: 'en-us'. |
+ // Remove country to add just the language to array. |
+ var hyphen = language.lastIndexOf('-'); |
+ if (hyphen >= 0) { |
+ langs.push(language.substr(0, hyphen)); // Example: 'en'. |
+ } |
} |
- } |
- langs.push(''); |
- return langs; |
-}; |
+ langs.push(''); |
+ return langs; |
+ }; |
-/** |
+ /** |
* Creates a NaCl plugin object and attaches it to the page. |
* @param {!string} src Location of the plugin. |
* @return {!HTMLEmbedElement} NaCl plugin DOM object. |
* @private |
*/ |
-NaClManager.prototype.createPlugin_ = function(src) { |
- var plugin = /** @type {HTMLEmbedElement} */(document.createElement('embed')); |
- plugin.src = src; |
- plugin.type = 'application/x-nacl'; |
- document.body.appendChild(plugin); |
- return plugin; |
-}; |
- |
-/** |
+ NaClManager.prototype.createPlugin_ = function(src) { |
+ var plugin = |
+ /** @type {HTMLEmbedElement} */ (document.createElement('embed')); |
+ plugin.src = src; |
+ plugin.type = 'application/x-nacl'; |
+ document.body.appendChild(plugin); |
+ return plugin; |
+ }; |
+ |
+ /** |
* Initializes the NaCl manager. |
* @param {!string} naclArch Either 'arm', 'x86-32' or 'x86-64'. |
* @param {!MediaStream} stream A stream containing an audio source track. |
* @return {boolean} True if the successful. |
*/ |
-NaClManager.prototype.initialize = function(naclArch, stream) { |
- assert(this.recognizerState_ == ManagerState_.UNINITIALIZED, |
- 'Recognizer not in uninitialized state. State: ' + |
- this.recognizerState_); |
- assert(this.plugin_ == null); |
- var langs = this.getPossibleLanguages_(); |
- var i, j; |
- // For country-lang variations. For example, when combined with path it will |
- // attempt to find: '/x86-32_en-gb/', else '/x86-32_en/', else '/x86-32_/'. |
- for (i = 0; i < langs.length; i++) { |
- var folder = hotword.constants.SHARED_MODULE_ROOT + '/_platform_specific/' + |
- naclArch + '_' + langs[i] + '/'; |
- var dataSrc = folder + hotword.constants.File.RECOGNIZER_CONFIG; |
- var pluginSrc = hotword.constants.SHARED_MODULE_ROOT + '/hotword_' + |
- langs[i] + '.nmf'; |
- var dataExists = this.fileExists_(dataSrc) && this.fileExists_(pluginSrc); |
- if (!dataExists) { |
- continue; |
+ NaClManager.prototype.initialize = function(naclArch, stream) { |
+ assert( |
+ this.recognizerState_ == ManagerState_.UNINITIALIZED, |
+ 'Recognizer not in uninitialized state. State: ' + |
+ this.recognizerState_); |
+ assert(this.plugin_ == null); |
+ var langs = this.getPossibleLanguages_(); |
+ var i, j; |
+ // For country-lang variations. For example, when combined with path it will |
+ // attempt to find: '/x86-32_en-gb/', else '/x86-32_en/', else '/x86-32_/'. |
+ for (i = 0; i < langs.length; i++) { |
+ var folder = hotword.constants.SHARED_MODULE_ROOT + |
+ '/_platform_specific/' + naclArch + '_' + langs[i] + '/'; |
+ var dataSrc = folder + hotword.constants.File.RECOGNIZER_CONFIG; |
+ var pluginSrc = hotword.constants.SHARED_MODULE_ROOT + '/hotword_' + |
+ langs[i] + '.nmf'; |
+ var dataExists = this.fileExists_(dataSrc) && this.fileExists_(pluginSrc); |
+ if (!dataExists) { |
+ continue; |
+ } |
+ |
+ var plugin = this.createPlugin_(pluginSrc); |
+ if (!plugin || !plugin.postMessage) { |
+ document.body.removeChild(plugin); |
+ this.recognizerState_ = ManagerState_.ERROR; |
+ return false; |
+ } |
+ this.plugin_ = plugin; |
+ this.modelUrl_ = chrome.extension.getURL(dataSrc); |
+ this.stream_ = stream; |
+ this.recognizerState_ = ManagerState_.LOADING; |
+ |
+ plugin.addEventListener( |
+ 'message', this.handlePluginMessage_.bind(this), false); |
+ |
+ plugin.addEventListener('crash', function() { |
+ this.handleError_(Error_.NACL_CRASH); |
+ this.logPluginLoadResult_(UmaNaClPluginLoadResult_.CRASH); |
+ }.bind(this), false); |
+ return true; |
} |
+ this.recognizerState_ = ManagerState_.ERROR; |
+ this.logPluginLoadResult_(UmaNaClPluginLoadResult_.NO_MODULE_FOUND); |
+ return false; |
+ }; |
- var plugin = this.createPlugin_(pluginSrc); |
- if (!plugin || !plugin.postMessage) { |
- document.body.removeChild(plugin); |
- this.recognizerState_ = ManagerState_.ERROR; |
- return false; |
+ /** |
+ * Shuts down the NaCl plugin and frees all resources. |
+ */ |
+ NaClManager.prototype.shutdown = function() { |
+ if (this.plugin_ != null) { |
+ document.body.removeChild(this.plugin_); |
+ this.plugin_ = null; |
} |
- this.plugin_ = plugin; |
- this.modelUrl_ = chrome.extension.getURL(dataSrc); |
- this.stream_ = stream; |
- this.recognizerState_ = ManagerState_.LOADING; |
- |
- plugin.addEventListener('message', |
- this.handlePluginMessage_.bind(this), |
- false); |
- |
- plugin.addEventListener('crash', |
- function() { |
- this.handleError_(Error_.NACL_CRASH); |
- this.logPluginLoadResult_( |
- UmaNaClPluginLoadResult_.CRASH); |
- }.bind(this), |
- false); |
- return true; |
- } |
- this.recognizerState_ = ManagerState_.ERROR; |
- this.logPluginLoadResult_(UmaNaClPluginLoadResult_.NO_MODULE_FOUND); |
- return false; |
-}; |
- |
-/** |
- * Shuts down the NaCl plugin and frees all resources. |
- */ |
-NaClManager.prototype.shutdown = function() { |
- if (this.plugin_ != null) { |
- document.body.removeChild(this.plugin_); |
- this.plugin_ = null; |
- } |
- this.clearTimeout_(); |
- this.recognizerState_ = ManagerState_.SHUTDOWN; |
- if (this.stream_) |
- this.stream_.getAudioTracks()[0].stop(); |
- this.stream_ = null; |
-}; |
- |
-/** |
- * Sends data to the NaCl plugin. |
- * @param {!string|!MediaStreamTrack} data Command to be sent to NaCl plugin. |
- * @private |
- */ |
-NaClManager.prototype.sendDataToPlugin_ = function(data) { |
- assert(this.recognizerState_ != ManagerState_.UNINITIALIZED, |
- 'Recognizer in uninitialized state'); |
- this.plugin_.postMessage(data); |
-}; |
- |
-/** |
- * Waits, with a timeout, for a message to be received from the plugin. If the |
- * message is not seen within the timeout, dispatch an 'error' event and go into |
- * the ERROR state. |
- * @param {number} timeout Timeout, in milliseconds, to wait for the message. |
- * @param {!string} message Message to wait for. |
- * @private |
- */ |
-NaClManager.prototype.waitForMessage_ = function(timeout, message) { |
- assert(this.expectingMessage_ == null, 'Cannot wait for message: ' + |
- message + ', already waiting for message ' + this.expectingMessage_); |
- this.setTimeout_( |
- function() { |
- this.recognizerState_ = ManagerState_.ERROR; |
- this.handleError_(Error_.TIMEOUT); |
- switch (this.expectingMessage_) { |
- case hotword.constants.NaClPlugin.REQUEST_MODEL: |
- var metricValue = UmaNaClMessageTimeout_.REQUEST_MODEL; |
- break; |
- case hotword.constants.NaClPlugin.MODEL_LOADED: |
- var metricValue = UmaNaClMessageTimeout_.MODEL_LOADED; |
- break; |
- case hotword.constants.NaClPlugin.READY_FOR_AUDIO: |
- var metricValue = UmaNaClMessageTimeout_.READY_FOR_AUDIO; |
- break; |
- case hotword.constants.NaClPlugin.STOPPED: |
- var metricValue = UmaNaClMessageTimeout_.STOPPED; |
- break; |
- case hotword.constants.NaClPlugin.HOTWORD_DETECTED: |
- var metricValue = UmaNaClMessageTimeout_.HOTWORD_DETECTED; |
- break; |
- case hotword.constants.NaClPlugin.MS_CONFIGURED: |
- var metricValue = UmaNaClMessageTimeout_.MS_CONFIGURED; |
- break; |
- } |
- hotword.metrics.recordEnum( |
- hotword.constants.UmaMetrics.NACL_MESSAGE_TIMEOUT, |
- metricValue, |
- UmaNaClMessageTimeout_.MAX); |
- }.bind(this), timeout); |
- this.expectingMessage_ = message; |
-}; |
- |
-/** |
- * Called when a message is received from the plugin. If we're waiting for that |
- * message, cancel the pending timeout. |
- * @param {string} message Message received. |
- * @private |
- */ |
-NaClManager.prototype.receivedMessage_ = function(message) { |
- if (message == this.expectingMessage_) { |
this.clearTimeout_(); |
- this.expectingMessage_ = null; |
- } |
-}; |
+ this.recognizerState_ = ManagerState_.SHUTDOWN; |
+ if (this.stream_) |
+ this.stream_.getAudioTracks()[0].stop(); |
+ this.stream_ = null; |
+ }; |
-/** |
- * Handle a REQUEST_MODEL message from the plugin. |
- * The plugin sends this message immediately after starting. |
- * @private |
- */ |
-NaClManager.prototype.handleRequestModel_ = function() { |
- if (this.recognizerState_ != ManagerState_.LOADING) { |
- return; |
- } |
- this.logPluginLoadResult_(UmaNaClPluginLoadResult_.SUCCESS); |
- this.sendDataToPlugin_( |
- hotword.constants.NaClPlugin.MODEL_PREFIX + this.modelUrl_); |
- this.waitForMessage_(hotword.constants.TimeoutMs.LONG, |
- hotword.constants.NaClPlugin.MODEL_LOADED); |
- |
- // Configure logging in the plugin. This can be configured any time before |
- // starting the recognizer, and now is as good a time as any. |
- if (this.loggingEnabled_) { |
- this.sendDataToPlugin_( |
- hotword.constants.NaClPlugin.LOG + ':' + |
- hotword.constants.AUDIO_LOG_SECONDS); |
- } |
+ /** |
+ * Sends data to the NaCl plugin. |
+ * @param {!string|!MediaStreamTrack} data Command to be sent to NaCl plugin. |
+ * @private |
+ */ |
+ NaClManager.prototype.sendDataToPlugin_ = function(data) { |
+ assert( |
+ this.recognizerState_ != ManagerState_.UNINITIALIZED, |
+ 'Recognizer in uninitialized state'); |
+ this.plugin_.postMessage(data); |
+ }; |
+ |
+ /** |
+ * Waits, with a timeout, for a message to be received from the plugin. If the |
+ * message is not seen within the timeout, dispatch an 'error' event and go |
+ * into |
+ * the ERROR state. |
+ * @param {number} timeout Timeout, in milliseconds, to wait for the message. |
+ * @param {!string} message Message to wait for. |
+ * @private |
+ */ |
+ NaClManager.prototype.waitForMessage_ = function(timeout, message) { |
+ assert( |
+ this.expectingMessage_ == null, 'Cannot wait for message: ' + message + |
+ ', already waiting for message ' + this.expectingMessage_); |
+ this.setTimeout_(function() { |
+ this.recognizerState_ = ManagerState_.ERROR; |
+ this.handleError_(Error_.TIMEOUT); |
+ switch (this.expectingMessage_) { |
+ case hotword.constants.NaClPlugin.REQUEST_MODEL: |
+ var metricValue = UmaNaClMessageTimeout_.REQUEST_MODEL; |
+ break; |
+ case hotword.constants.NaClPlugin.MODEL_LOADED: |
+ var metricValue = UmaNaClMessageTimeout_.MODEL_LOADED; |
+ break; |
+ case hotword.constants.NaClPlugin.READY_FOR_AUDIO: |
+ var metricValue = UmaNaClMessageTimeout_.READY_FOR_AUDIO; |
+ break; |
+ case hotword.constants.NaClPlugin.STOPPED: |
+ var metricValue = UmaNaClMessageTimeout_.STOPPED; |
+ break; |
+ case hotword.constants.NaClPlugin.HOTWORD_DETECTED: |
+ var metricValue = UmaNaClMessageTimeout_.HOTWORD_DETECTED; |
+ break; |
+ case hotword.constants.NaClPlugin.MS_CONFIGURED: |
+ var metricValue = UmaNaClMessageTimeout_.MS_CONFIGURED; |
+ break; |
+ } |
+ hotword.metrics.recordEnum( |
+ hotword.constants.UmaMetrics.NACL_MESSAGE_TIMEOUT, metricValue, |
+ UmaNaClMessageTimeout_.MAX); |
+ }.bind(this), timeout); |
+ this.expectingMessage_ = message; |
+ }; |
+ |
+ /** |
+ * Called when a message is received from the plugin. If we're waiting for |
+ * that |
+ * message, cancel the pending timeout. |
+ * @param {string} message Message received. |
+ * @private |
+ */ |
+ NaClManager.prototype.receivedMessage_ = function(message) { |
+ if (message == this.expectingMessage_) { |
+ this.clearTimeout_(); |
+ this.expectingMessage_ = null; |
+ } |
+ }; |
- // If the audio stream is from a hotword stream, tell the plugin. |
- if (this.hotwordStream_) { |
+ /** |
+ * Handle a REQUEST_MODEL message from the plugin. |
+ * The plugin sends this message immediately after starting. |
+ * @private |
+ */ |
+ NaClManager.prototype.handleRequestModel_ = function() { |
+ if (this.recognizerState_ != ManagerState_.LOADING) { |
+ return; |
+ } |
+ this.logPluginLoadResult_(UmaNaClPluginLoadResult_.SUCCESS); |
this.sendDataToPlugin_( |
- hotword.constants.NaClPlugin.DSP + ':' + |
- hotword.constants.HOTWORD_STREAM_TIMEOUT_SECONDS); |
- } |
-}; |
- |
-/** |
- * Handle a MODEL_LOADED message from the plugin. |
- * The plugin sends this message after successfully loading the language model. |
- * @private |
- */ |
-NaClManager.prototype.handleModelLoaded_ = function() { |
- if (this.recognizerState_ != ManagerState_.LOADING) { |
- return; |
- } |
- this.sendDataToPlugin_(this.stream_.getAudioTracks()[0]); |
- // The plugin will send a MS_CONFIGURED, but don't set a timeout waiting for |
- // it. MediaStreamAudioTrack::Configure() will remain pending until the first |
- // audio buffer is received. When the audio source is a DSP for always-on |
- // detection, no audio sample is sent until the DSP detects a potential |
- // hotword trigger. Thus, Configure would remain pending indefinitely if we |
- // were to wait here. See https://crbug.com/616203 |
-}; |
- |
-/** |
- * Handle a MS_CONFIGURED message from the plugin. |
- * The plugin sends this message after successfully configuring the audio input |
- * stream. |
- * @private |
- */ |
-NaClManager.prototype.handleMsConfigured_ = function() { |
- if (this.recognizerState_ != ManagerState_.LOADING) { |
- return; |
- } |
- this.recognizerState_ = ManagerState_.STOPPED; |
- this.dispatchEvent(new Event(hotword.constants.Event.READY)); |
-}; |
- |
-/** |
- * Handle a READY_FOR_AUDIO message from the plugin. |
- * The plugin sends this message after the recognizer is started and |
- * successfully receives and processes audio data. |
- * @private |
- */ |
-NaClManager.prototype.handleReadyForAudio_ = function() { |
- if (this.recognizerState_ != ManagerState_.STARTING) { |
- return; |
- } |
- this.recognizerState_ = ManagerState_.RUNNING; |
-}; |
- |
-/** |
- * Handle a HOTWORD_DETECTED message from the plugin. |
- * The plugin sends this message after detecting the hotword. |
- * @private |
- */ |
-NaClManager.prototype.handleHotwordDetected_ = function() { |
- if (this.recognizerState_ != ManagerState_.RUNNING) { |
- return; |
- } |
- // We'll receive a STOPPED message very soon. |
- this.recognizerState_ = ManagerState_.STOPPING; |
- this.waitForMessage_(hotword.constants.TimeoutMs.NORMAL, |
- hotword.constants.NaClPlugin.STOPPED); |
- var event = new Event(hotword.constants.Event.TRIGGER); |
- event.log = this.preambleLog_; |
- this.dispatchEvent(event); |
-}; |
- |
-/** |
- * Handle a STOPPED message from the plugin. |
- * This plugin sends this message after stopping the recognizer. This can happen |
- * either in response to a stop request, or after the hotword is detected. |
- * @private |
- */ |
-NaClManager.prototype.handleStopped_ = function() { |
- this.recognizerState_ = ManagerState_.STOPPED; |
- if (this.restartOnStop_) { |
- this.restartOnStop_ = false; |
- this.startRecognizer(this.startMode_); |
- } |
-}; |
- |
-/** |
- * Handle a TIMEOUT message from the plugin. |
- * The plugin sends this message when it thinks the stream is from a DSP and |
- * a hotword wasn't detected within a timeout period after arrival of the first |
- * audio samples. |
- * @private |
- */ |
-NaClManager.prototype.handleTimeout_ = function() { |
- if (this.recognizerState_ != ManagerState_.RUNNING) { |
- return; |
- } |
- this.recognizerState_ = ManagerState_.STOPPED; |
- this.dispatchEvent(new Event(hotword.constants.Event.TIMEOUT)); |
-}; |
- |
-/** |
- * Handle a SPEAKER_MODEL_SAVED message from the plugin. |
- * The plugin sends this message after writing the model to a file. |
- * @private |
- */ |
-NaClManager.prototype.handleSpeakerModelSaved_ = function() { |
- this.dispatchEvent(new Event(hotword.constants.Event.SPEAKER_MODEL_SAVED)); |
-}; |
+ hotword.constants.NaClPlugin.MODEL_PREFIX + this.modelUrl_); |
+ this.waitForMessage_( |
+ hotword.constants.TimeoutMs.LONG, |
+ hotword.constants.NaClPlugin.MODEL_LOADED); |
+ |
+ // Configure logging in the plugin. This can be configured any time before |
+ // starting the recognizer, and now is as good a time as any. |
+ if (this.loggingEnabled_) { |
+ this.sendDataToPlugin_( |
+ hotword.constants.NaClPlugin.LOG + ':' + |
+ hotword.constants.AUDIO_LOG_SECONDS); |
+ } |
-/** |
- * Handles a message from the NaCl plugin. |
- * @param {!Event} msg Message from NaCl plugin. |
- * @private |
- */ |
-NaClManager.prototype.handlePluginMessage_ = function(msg) { |
- if (msg['data']) { |
- if (typeof(msg['data']) == 'object') { |
- // Save the preamble for delivery to the trigger handler when the trigger |
- // message arrives. |
- this.preambleLog_ = msg['data']; |
+ // If the audio stream is from a hotword stream, tell the plugin. |
+ if (this.hotwordStream_) { |
+ this.sendDataToPlugin_( |
+ hotword.constants.NaClPlugin.DSP + ':' + |
+ hotword.constants.HOTWORD_STREAM_TIMEOUT_SECONDS); |
+ } |
+ }; |
+ |
+ /** |
+ * Handle a MODEL_LOADED message from the plugin. |
+ * The plugin sends this message after successfully loading the language |
+ * model. |
+ * @private |
+ */ |
+ NaClManager.prototype.handleModelLoaded_ = function() { |
+ if (this.recognizerState_ != ManagerState_.LOADING) { |
+ return; |
+ } |
+ this.sendDataToPlugin_(this.stream_.getAudioTracks()[0]); |
+ // The plugin will send a MS_CONFIGURED, but don't set a timeout waiting for |
+ // it. MediaStreamAudioTrack::Configure() will remain pending until the |
+ // first |
+ // audio buffer is received. When the audio source is a DSP for always-on |
+ // detection, no audio sample is sent until the DSP detects a potential |
+ // hotword trigger. Thus, Configure would remain pending indefinitely if we |
+ // were to wait here. See https://crbug.com/616203 |
+ }; |
+ |
+ /** |
+ * Handle a MS_CONFIGURED message from the plugin. |
+ * The plugin sends this message after successfully configuring the audio |
+ * input |
+ * stream. |
+ * @private |
+ */ |
+ NaClManager.prototype.handleMsConfigured_ = function() { |
+ if (this.recognizerState_ != ManagerState_.LOADING) { |
+ return; |
+ } |
+ this.recognizerState_ = ManagerState_.STOPPED; |
+ this.dispatchEvent(new Event(hotword.constants.Event.READY)); |
+ }; |
+ |
+ /** |
+ * Handle a READY_FOR_AUDIO message from the plugin. |
+ * The plugin sends this message after the recognizer is started and |
+ * successfully receives and processes audio data. |
+ * @private |
+ */ |
+ NaClManager.prototype.handleReadyForAudio_ = function() { |
+ if (this.recognizerState_ != ManagerState_.STARTING) { |
+ return; |
+ } |
+ this.recognizerState_ = ManagerState_.RUNNING; |
+ }; |
+ |
+ /** |
+ * Handle a HOTWORD_DETECTED message from the plugin. |
+ * The plugin sends this message after detecting the hotword. |
+ * @private |
+ */ |
+ NaClManager.prototype.handleHotwordDetected_ = function() { |
+ if (this.recognizerState_ != ManagerState_.RUNNING) { |
+ return; |
+ } |
+ // We'll receive a STOPPED message very soon. |
+ this.recognizerState_ = ManagerState_.STOPPING; |
+ this.waitForMessage_( |
+ hotword.constants.TimeoutMs.NORMAL, |
+ hotword.constants.NaClPlugin.STOPPED); |
+ var event = new Event(hotword.constants.Event.TRIGGER); |
+ event.log = this.preambleLog_; |
+ this.dispatchEvent(event); |
+ }; |
+ |
+ /** |
+ * Handle a STOPPED message from the plugin. |
+ * This plugin sends this message after stopping the recognizer. This can |
+ * happen |
+ * either in response to a stop request, or after the hotword is detected. |
+ * @private |
+ */ |
+ NaClManager.prototype.handleStopped_ = function() { |
+ this.recognizerState_ = ManagerState_.STOPPED; |
+ if (this.restartOnStop_) { |
+ this.restartOnStop_ = false; |
+ this.startRecognizer(this.startMode_); |
+ } |
+ }; |
+ |
+ /** |
+ * Handle a TIMEOUT message from the plugin. |
+ * The plugin sends this message when it thinks the stream is from a DSP and |
+ * a hotword wasn't detected within a timeout period after arrival of the |
+ * first |
+ * audio samples. |
+ * @private |
+ */ |
+ NaClManager.prototype.handleTimeout_ = function() { |
+ if (this.recognizerState_ != ManagerState_.RUNNING) { |
return; |
} |
- this.receivedMessage_(msg['data']); |
- switch (msg['data']) { |
- case hotword.constants.NaClPlugin.REQUEST_MODEL: |
- this.handleRequestModel_(); |
- break; |
- case hotword.constants.NaClPlugin.MODEL_LOADED: |
- this.handleModelLoaded_(); |
- break; |
- case hotword.constants.NaClPlugin.MS_CONFIGURED: |
- this.handleMsConfigured_(); |
- break; |
- case hotword.constants.NaClPlugin.READY_FOR_AUDIO: |
- this.handleReadyForAudio_(); |
- break; |
- case hotword.constants.NaClPlugin.HOTWORD_DETECTED: |
- this.handleHotwordDetected_(); |
- break; |
- case hotword.constants.NaClPlugin.STOPPED: |
- this.handleStopped_(); |
- break; |
- case hotword.constants.NaClPlugin.TIMEOUT: |
- this.handleTimeout_(); |
- break; |
- case hotword.constants.NaClPlugin.SPEAKER_MODEL_SAVED: |
- this.handleSpeakerModelSaved_(); |
- break; |
+ this.recognizerState_ = ManagerState_.STOPPED; |
+ this.dispatchEvent(new Event(hotword.constants.Event.TIMEOUT)); |
+ }; |
+ |
+ /** |
+ * Handle a SPEAKER_MODEL_SAVED message from the plugin. |
+ * The plugin sends this message after writing the model to a file. |
+ * @private |
+ */ |
+ NaClManager.prototype.handleSpeakerModelSaved_ = function() { |
+ this.dispatchEvent(new Event(hotword.constants.Event.SPEAKER_MODEL_SAVED)); |
+ }; |
+ |
+ /** |
+ * Handles a message from the NaCl plugin. |
+ * @param {!Event} msg Message from NaCl plugin. |
+ * @private |
+ */ |
+ NaClManager.prototype.handlePluginMessage_ = function(msg) { |
+ if (msg['data']) { |
+ if (typeof(msg['data']) == 'object') { |
+ // Save the preamble for delivery to the trigger handler when the |
+ // trigger |
+ // message arrives. |
+ this.preambleLog_ = msg['data']; |
+ return; |
+ } |
+ this.receivedMessage_(msg['data']); |
+ switch (msg['data']) { |
+ case hotword.constants.NaClPlugin.REQUEST_MODEL: |
+ this.handleRequestModel_(); |
+ break; |
+ case hotword.constants.NaClPlugin.MODEL_LOADED: |
+ this.handleModelLoaded_(); |
+ break; |
+ case hotword.constants.NaClPlugin.MS_CONFIGURED: |
+ this.handleMsConfigured_(); |
+ break; |
+ case hotword.constants.NaClPlugin.READY_FOR_AUDIO: |
+ this.handleReadyForAudio_(); |
+ break; |
+ case hotword.constants.NaClPlugin.HOTWORD_DETECTED: |
+ this.handleHotwordDetected_(); |
+ break; |
+ case hotword.constants.NaClPlugin.STOPPED: |
+ this.handleStopped_(); |
+ break; |
+ case hotword.constants.NaClPlugin.TIMEOUT: |
+ this.handleTimeout_(); |
+ break; |
+ case hotword.constants.NaClPlugin.SPEAKER_MODEL_SAVED: |
+ this.handleSpeakerModelSaved_(); |
+ break; |
+ } |
} |
- } |
-}; |
+ }; |
-return { |
- NaClManager: NaClManager |
-}; |
+ return {NaClManager: NaClManager}; |
}); |