summaryrefslogtreecommitdiffstats
path: root/chromium/chrome/browser/resources/app_list/speech_manager.js
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/chrome/browser/resources/app_list/speech_manager.js')
-rw-r--r--chromium/chrome/browser/resources/app_list/speech_manager.js171
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 {