OLD | NEW |
| (Empty) |
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. | |
4 | |
5 /** | |
6 * @fileoverview The manager of offline hotword speech recognizer plugin. | |
7 */ | |
8 | |
9 cr.define('speech', function() { | |
10 'use strict'; | |
11 | |
12 /** The timeout milliseconds to load the model file. */ | |
13 var MODEL_LOAD_TIMEOUT = 2000; | |
14 | |
15 /** | |
16 * The type of the plugin state. | |
17 ** @enum {number} | |
18 */ | |
19 var PluginState = { | |
20 UNINITIALIZED: 0, | |
21 LOADED: 1, | |
22 READY: 2, | |
23 RECOGNIZING: 3 | |
24 }; | |
25 | |
26 /** | |
27 * The command names of the plugin. | |
28 * @enum {string} | |
29 */ | |
30 var pluginCommands = { | |
31 SET_SAMPLING_RATE: 'h', | |
32 SET_CONFIG: 'm', | |
33 START_RECOGNIZING: 'r', | |
34 STOP_RECOGNIZING: 's' | |
35 }; | |
36 | |
37 /** | |
38 * The regexp pattern of the hotword recognition result. | |
39 */ | |
40 var recognitionPattern = /^(HotwordFiredEvent:|hotword)/; | |
41 | |
42 /** | |
43 * @constructor | |
44 */ | |
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; | |
51 this.config_ = null; | |
52 this.modelLoadTimeoutId_ = null; | |
53 var recognizer = $('recognizer'); | |
54 if (!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); | |
62 } | |
63 recognizer.addEventListener('error', onError); | |
64 recognizer.addEventListener('message', this.onMessage_.bind(this)); | |
65 recognizer.addEventListener('load', this.onLoad_.bind(this)); | |
66 }; | |
67 | |
68 /** | |
69 * The event handler of the plugin status. | |
70 * | |
71 * @param {Event} messageEvent the event object from the plugin. | |
72 * @private | |
73 */ | |
74 PluginManager.prototype.onMessage_ = function(messageEvent) { | |
75 if (messageEvent.data == 'audio') { | |
76 var wasNotReady = this.state < PluginState.READY; | |
77 this.state = PluginState.RECOGNIZING; | |
78 if (wasNotReady) { | |
79 window.clearTimeout(this.modelLoadTimeoutId_); | |
80 this.modelLoadTimeoutId_ = null; | |
81 this.onReady_(this); | |
82 } | |
83 } else if (messageEvent.data == 'stopped' && | |
84 this.state == PluginState.RECOGNIZING) { | |
85 this.state = PluginState.READY; | |
86 } else if (recognitionPattern.exec(messageEvent.data)) { | |
87 this.onRecognized_(); | |
88 } | |
89 }; | |
90 | |
91 /** | |
92 * The event handler when the plugin is loaded. | |
93 * | |
94 * @private | |
95 */ | |
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); | |
105 } | |
106 }; | |
107 | |
108 /** | |
109 * Sends the initialization messages to the plugin. This method is private. | |
110 * The initialization will happen from onLoad_ or scheduleInitialize. | |
111 * | |
112 * @param {number} samplingRate the sampling rate the plugin accepts. | |
113 * @param {string} config the url of the config file. | |
114 * @private | |
115 */ | |
116 PluginManager.prototype.initialize_ = function(samplingRate, config) { | |
117 $('recognizer').postMessage( | |
118 pluginCommands.SET_SAMPLING_RATE + samplingRate); | |
119 $('recognizer').postMessage(pluginCommands.SET_CONFIG + config); | |
120 }; | |
121 | |
122 /** | |
123 * Initializes the plugin with the specified parameter, or schedules the | |
124 * initialization if the plugin is not ready. | |
125 * | |
126 * @param {number} samplingRate the sampling rate the plugin accepts. | |
127 * @param {string} config the url of the config file. | |
128 */ | |
129 PluginManager.prototype.scheduleInitialize = function(samplingRate, config) { | |
130 if (this.state == PluginState.UNINITIALIZED) { | |
131 this.samplingRate_ = samplingRate; | |
132 this.config_ = config; | |
133 } else { | |
134 this.initialize_(samplingRate, config); | |
135 } | |
136 }; | |
137 | |
138 /** | |
139 * Asks the plugin to start recognizing the hotword. | |
140 */ | |
141 PluginManager.prototype.startRecognizer = function() { | |
142 if (this.state == PluginState.READY) | |
143 $('recognizer').postMessage(pluginCommands.START_RECOGNIZING); | |
144 }; | |
145 | |
146 /** | |
147 * Asks the plugin to stop recognizing the hotword. | |
148 */ | |
149 PluginManager.prototype.stopRecognizer = function() { | |
150 if (this.state == PluginState.RECOGNIZING) | |
151 $('recognizer').postMessage(pluginCommands.STOP_RECOGNIZING); | |
152 }; | |
153 | |
154 /** | |
155 * Sends the actual audio wave data. | |
156 * | |
157 * @param {cr.event.Event} event The event for the audio data. | |
158 */ | |
159 PluginManager.prototype.sendAudioData = function(event) { | |
160 if (this.state == PluginState.RECOGNIZING) | |
161 $('recognizer').postMessage(event.data.buffer); | |
162 }; | |
163 | |
164 return { | |
165 PluginManager: PluginManager, | |
166 PluginState: PluginState, | |
167 }; | |
168 }); | |
OLD | NEW |