1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 * @fileoverview The manager of offline hotword speech recognizer plugin.
9 cr.define('speech', function() {
12 /** The timeout milliseconds to load the model file. */
13 var MODEL_LOAD_TIMEOUT = 2000;
16 * The type of the plugin state.
27 * The command names of the plugin.
30 var pluginCommands = {
31 SET_SAMPLING_RATE: 'h',
33 START_RECOGNIZING: 'r',
38 * The regexp pattern of the hotword recognition result.
40 var recognitionPattern = /^(HotwordFiredEvent:|hotword)/;
45 function PluginManager(prefix, onReady, onRecognized, onError) {
46 this.state = PluginState.UNINITIALIZED;
47 this.onReady_ = onReady;
48 this.onRecognized_ = onRecognized;
49 this.onError_ = onError;
50 this.samplingRate_ = null;
52 this.modelLoadTimeoutId_ = null;
53 var recognizer = $('recognizer');
55 recognizer = document.createElement('EMBED');
56 recognizer.id = 'recognizer';
57 recognizer.type = 'application/x-nacl';
58 recognizer.src = 'chrome://app-list/hotword_' + prefix + '.nmf';
59 recognizer.width = '1';
60 recognizer.height = '1';
61 document.body.appendChild(recognizer);
63 recognizer.addEventListener('error', onError);
64 recognizer.addEventListener('message', this.onMessage_.bind(this));
65 recognizer.addEventListener('load', this.onLoad_.bind(this));
69 * The event handler of the plugin status.
71 * @param {Event} messageEvent the event object from the plugin.
74 PluginManager.prototype.onMessage_ = function(messageEvent) {
75 if (messageEvent.data == 'audio') {
76 var wasNotReady = this.state < PluginState.READY;
77 this.state = PluginState.RECOGNIZING;
79 window.clearTimeout(this.modelLoadTimeoutId_);
80 this.modelLoadTimeoutId_ = null;
83 } else if (messageEvent.data == 'stopped' &&
84 this.state == PluginState.RECOGNIZING) {
85 this.state = PluginState.READY;
86 } else if (recognitionPattern.exec(messageEvent.data)) {
92 * The event handler when the plugin is loaded.
96 PluginManager.prototype.onLoad_ = function() {
97 if (this.state == PluginState.UNINITIALIZED) {
98 this.state = PluginState.LOADED;
99 if (this.samplingRate_ && this.config_)
100 this.initialize_(this.samplingRate_, this.config_);
101 // Sets the timeout for initialization in case that NaCl module failed to
102 // respond during the initialization.
103 this.modelLoadTimeoutId_ = window.setTimeout(
104 this.onError_, MODEL_LOAD_TIMEOUT);
109 * Sends the initialization messages to the plugin. This method is private.
110 * The initialization will happen from onLoad_ or scheduleInitialize.
112 * @param {number} samplingRate the sampling rate the plugin accepts.
113 * @param {string} config the url of the config file.
116 PluginManager.prototype.initialize_ = function(samplingRate, config) {
117 $('recognizer').postMessage(
118 pluginCommands.SET_SAMPLING_RATE + samplingRate);
119 $('recognizer').postMessage(pluginCommands.SET_CONFIG + config);
123 * Initializes the plugin with the specified parameter, or schedules the
124 * initialization if the plugin is not ready.
126 * @param {number} samplingRate the sampling rate the plugin accepts.
127 * @param {string} config the url of the config file.
129 PluginManager.prototype.scheduleInitialize = function(samplingRate, config) {
130 if (this.state == PluginState.UNINITIALIZED) {
131 this.samplingRate_ = samplingRate;
132 this.config_ = config;
134 this.initialize_(samplingRate, config);
139 * Asks the plugin to start recognizing the hotword.
141 PluginManager.prototype.startRecognizer = function() {
142 if (this.state == PluginState.READY)
143 $('recognizer').postMessage(pluginCommands.START_RECOGNIZING);
147 * Asks the plugin to stop recognizing the hotword.
149 PluginManager.prototype.stopRecognizer = function() {
150 if (this.state == PluginState.RECOGNIZING)
151 $('recognizer').postMessage(pluginCommands.STOP_RECOGNIZING);
155 * Sends the actual audio wave data.
157 * @param {cr.event.Event} event The event for the audio data.
159 PluginManager.prototype.sendAudioData = function(event) {
160 if (this.state == PluginState.RECOGNIZING)
161 $('recognizer').postMessage(event.data.buffer);
165 PluginManager: PluginManager,
166 PluginState: PluginState,