| 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 |