Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / app_list / speech_manager.js
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 class to Manage both offline / online speech recognition.
7  */
8
9 <include src="plugin_manager.js"/>
10 <include src="audio_manager.js"/>
11 <include src="speech_recognition_manager.js"/>
12
13 cr.define('speech', function() {
14   'use strict';
15
16   /**
17    * The state of speech recognition.
18    *
19    * @enum {string}
20    */
21   var SpeechState = {
22     READY: 'READY',
23     HOTWORD_RECOGNIZING: 'HOTWORD_RECOGNIZING',
24     RECOGNIZING: 'RECOGNIZING',
25     IN_SPEECH: 'IN_SPEECH',
26     STOPPING: 'STOPPING'
27   };
28
29   /**
30    * Checks the prefix for the hotword module based on the language. This is
31    * fragile if the file structure has changed.
32    */
33   function getHotwordPrefix() {
34     var prefix = navigator.language.toLowerCase();
35     if (prefix == 'en-gb')
36       return prefix;
37     var hyphen = prefix.indexOf('-');
38     if (hyphen >= 0)
39       prefix = prefix.substr(0, hyphen);
40     if (prefix == 'en')
41       prefix = '';
42     return prefix;
43   }
44
45   /**
46    * @constructor
47    */
48   function SpeechManager() {
49     this.audioManager_ = new speech.AudioManager();
50     this.audioManager_.addEventListener('audio', this.onAudioLevel_.bind(this));
51     this.speechRecognitionManager_ = new speech.SpeechRecognitionManager(this);
52   }
53
54   /**
55    * Updates the state.
56    *
57    * @param {SpeechState} newState The new state.
58    * @private
59    */
60   SpeechManager.prototype.setState_ = function(newState) {
61     if (this.state == newState)
62       return;
63
64     this.state = newState;
65     chrome.send('setSpeechRecognitionState', [this.state]);
66   };
67
68   /**
69    * Called with the mean audio level when audio data arrives.
70    *
71    * @param {cr.event.Event} event The event object for the audio data.
72    * @private
73    */
74   SpeechManager.prototype.onAudioLevel_ = function(event) {
75     var data = event.data;
76     var level = 0;
77     for (var i = 0; i < data.length; ++i)
78       level += Math.abs(data[i]);
79     level /= data.length;
80     chrome.send('speechSoundLevel', [level]);
81   };
82
83   /**
84    * Called when the hotword recognizer is ready.
85    *
86    * @param {PluginManager} pluginManager The hotword plugin manager which gets
87    *   ready.
88    * @private
89    */
90   SpeechManager.prototype.onHotwordRecognizerReady_ = function(pluginManager) {
91     this.pluginManager_ = pluginManager;
92     this.audioManager_.addEventListener(
93         'audio', pluginManager.sendAudioData.bind(pluginManager));
94     this.pluginManager_.startRecognizer();
95     this.audioManager_.start();
96     this.setState_(SpeechState.HOTWORD_RECOGNIZING);
97   };
98
99   /**
100    * Called when an error happens for loading the hotword recognizer.
101    *
102    * @private
103    */
104   SpeechManager.prototype.onHotwordRecognizerLoadError_ = function() {
105     this.setHotwordEnabled(false);
106     this.setState_(SpeechState.READY);
107   };
108
109   /**
110    * Called when the hotword is recognized.
111    *
112    * @private
113    */
114   SpeechManager.prototype.onHotwordRecognized_ = function() {
115     if (this.state != SpeechState.HOTWORD_RECOGNIZING)
116       return;
117     this.pluginManager_.stopRecognizer();
118     this.speechRecognitionManager_.start();
119   };
120
121   /**
122    * Called when the speech recognition has happened.
123    *
124    * @param {string} result The speech recognition result.
125    * @param {boolean} isFinal Whether the result is final or not.
126    */
127   SpeechManager.prototype.onSpeechRecognized = function(result, isFinal) {
128     chrome.send('speechResult', [result, isFinal]);
129     if (isFinal)
130       this.speechRecognitionManager_.stop();
131   };
132
133   /**
134    * Called when the speech recognition has started.
135    */
136   SpeechManager.prototype.onSpeechRecognitionStarted = function() {
137     this.setState_(SpeechState.RECOGNIZING);
138   };
139
140   /**
141    * Called when the speech recognition has ended.
142    */
143   SpeechManager.prototype.onSpeechRecognitionEnded = function() {
144     // Restarts the hotword recognition.
145     if (this.state != SpeechState.STOPPING && this.pluginManager_) {
146       this.pluginManager_.startRecognizer();
147       this.audioManager_.start();
148       this.setState_(SpeechState.HOTWORD_RECOGNIZING);
149     } else {
150       this.audioManager_.stop();
151       this.setState_(SpeechState.READY);
152     }
153   };
154
155   /**
156    * Called when a speech has started.
157    */
158   SpeechManager.prototype.onSpeechStarted = function() {
159     if (this.state == SpeechState.RECOGNIZING)
160       this.setState_(SpeechState.IN_SPEECH);
161   };
162
163   /**
164    * Called when a speech has ended.
165    */
166   SpeechManager.prototype.onSpeechEnded = function() {
167     if (this.state == SpeechState.IN_SPEECH)
168       this.setState_(SpeechState.RECOGNIZING);
169   };
170
171   /**
172    * Called when an error happened during the speech recognition.
173    *
174    * @param {SpeechRecognitionError} e The error object.
175    */
176   SpeechManager.prototype.onSpeechRecognitionError = function(e) {
177     if (this.state != SpeechState.STOPPING)
178       this.setState_(SpeechState.READY);
179   };
180
181   /**
182    * Changes the availability of the hotword plugin.
183    *
184    * @param {boolean} enabled Whether enabled or not.
185    */
186   SpeechManager.prototype.setHotwordEnabled = function(enabled) {
187     var recognizer = $('recognizer');
188     if (enabled) {
189       if (recognizer)
190         return;
191       if (!this.naclArch)
192         return;
193
194       var prefix = getHotwordPrefix();
195       var pluginManager = new speech.PluginManager(
196           prefix,
197           this.onHotwordRecognizerReady_.bind(this),
198           this.onHotwordRecognized_.bind(this),
199           this.onHotwordRecognizerLoadError_.bind(this));
200       var modelUrl = 'chrome://app-list/_platform_specific/' + this.naclArch +
201           '_' + prefix + '/hotword.data';
202       pluginManager.scheduleInitialize(this.audioManager_.sampleRate, modelUrl);
203     } else {
204       if (!recognizer)
205         return;
206       document.body.removeChild(recognizer);
207       this.pluginManager_ = null;
208       if (this.state == SpeechState.HOTWORD_RECOGNIZING) {
209         this.audioManager_.stop();
210         this.setState_(SpeechState.READY);
211       }
212     }
213   };
214
215   /**
216    * Sets the NaCl architecture for the hotword module.
217    *
218    * @param {string} arch The architecture.
219    */
220   SpeechManager.prototype.setNaclArch = function(arch) {
221     this.naclArch = arch;
222   };
223
224   /**
225    * Called when the app-list bubble is shown.
226    *
227    * @param {boolean} hotwordEnabled Whether the hotword is enabled or not.
228    */
229   SpeechManager.prototype.onShown = function(hotwordEnabled) {
230     this.setHotwordEnabled(hotwordEnabled);
231
232     // No one sets the state if the content is initialized on shown but hotword
233     // is not enabled. Sets the state in such case.
234     if (!this.state && !hotwordEnabled)
235       this.setState_(SpeechState.READY);
236   };
237
238   /**
239    * Called when the app-list bubble is hidden.
240    */
241   SpeechManager.prototype.onHidden = function() {
242     this.setHotwordEnabled(false);
243
244     // SpeechRecognition is asynchronous.
245     this.audioManager_.stop();
246     if (this.state == SpeechState.RECOGNIZING ||
247         this.state == SpeechState.IN_SPEECH) {
248       this.setState_(SpeechState.STOPPING);
249       this.speechRecognitionManager_.stop();
250     } else {
251       this.setState_(SpeechState.READY);
252     }
253   };
254
255   /**
256    * Toggles the current state of speech recognition.
257    */
258   SpeechManager.prototype.toggleSpeechRecognition = function() {
259     if (this.state == SpeechState.RECOGNIZING ||
260         this.state == SpeechState.IN_SPEECH) {
261       this.audioManager_.stop();
262       this.speechRecognitionManager_.stop();
263     } else {
264       if (this.pluginManager_)
265         this.pluginManager_.stopRecognizer();
266       if (this.audioManager_.state == speech.AudioState.STOPPED)
267         this.audioManager_.start();
268       this.speechRecognitionManager_.start();
269     }
270   };
271
272   return {
273     SpeechManager: SpeechManager
274   };
275 });