OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 cr.define('hotword', function() { | 5 cr.define('hotword', function() { |
6 'use strict'; | 6 'use strict'; |
7 | 7 |
8 /** | 8 /** |
9 * Trivial container class for session information. | 9 * Trivial container class for session information. |
10 * @param {!hotword.constants.SessionSource} source Source of the hotword | 10 * @param {!hotword.constants.SessionSource} source Source of the hotword |
11 * session. | 11 * session. |
12 * @param {!function()} triggerCb Callback invoked when the hotword has | 12 * @param {!function()} triggerCb Callback invoked when the hotword has |
13 * triggered. | 13 * triggered. |
14 * @param {!function()} startedCb Callback invoked when the session has | 14 * @param {!function()} startedCb Callback invoked when the session has |
15 * been started successfully. | 15 * been started successfully. |
16 * @param {function()=} opt_modelSavedCb Callback invoked when the speaker | 16 * @param {function()=} opt_modelSavedCb Callback invoked when the speaker |
17 * model has been saved successfully. | 17 * model has been saved successfully. |
18 * @constructor | 18 * @constructor |
19 * @struct | 19 * @struct |
20 * @private | 20 * @private |
21 */ | 21 */ |
22 function Session_(source, triggerCb, startedCb, opt_modelSavedCb) { | 22 function Session_(source, triggerCb, startedCb, opt_modelSavedCb) { |
23 /** | 23 /** |
24 * Source of the hotword session request. | 24 * Source of the hotword session request. |
25 * @private {!hotword.constants.SessionSource} | 25 * @private {!hotword.constants.SessionSource} |
26 */ | 26 */ |
27 this.source_ = source; | 27 this.source_ = source; |
28 | 28 |
29 /** | 29 /** |
30 * Callback invoked when the hotword has triggered. | 30 * Callback invoked when the hotword has triggered. |
31 * @private {!function()} | 31 * @private {!function()} |
32 */ | 32 */ |
33 this.triggerCb_ = triggerCb; | 33 this.triggerCb_ = triggerCb; |
34 | 34 |
35 /** | 35 /** |
36 * Callback invoked when the session has been started successfully. | 36 * Callback invoked when the session has been started successfully. |
37 * @private {?function()} | 37 * @private {?function()} |
38 */ | 38 */ |
39 this.startedCb_ = startedCb; | 39 this.startedCb_ = startedCb; |
40 | 40 |
41 /** | 41 /** |
42 * Callback invoked when the session has been started successfully. | 42 * Callback invoked when the session has been started successfully. |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
86 * Event that fires when the hotwording status has changed. | 86 * Event that fires when the hotwording status has changed. |
87 * @type {!ChromeEvent} | 87 * @type {!ChromeEvent} |
88 */ | 88 */ |
89 this.onStatusChanged = new chrome.Event(); | 89 this.onStatusChanged = new chrome.Event(); |
90 | 90 |
91 /** | 91 /** |
92 * Hotword trigger audio notification... a.k.a The Chime (tm). | 92 * Hotword trigger audio notification... a.k.a The Chime (tm). |
93 * @private {!HTMLAudioElement} | 93 * @private {!HTMLAudioElement} |
94 */ | 94 */ |
95 this.chime_ = | 95 this.chime_ = |
96 /** @type {!HTMLAudioElement} */(document.createElement('audio')); | 96 /** @type {!HTMLAudioElement} */ (document.createElement('audio')); |
97 | 97 |
98 /** | 98 /** |
99 * Chrome event listeners. Saved so that they can be de-registered when | 99 * Chrome event listeners. Saved so that they can be de-registered when |
100 * hotwording is disabled. | 100 * hotwording is disabled. |
101 * @private | 101 * @private |
102 */ | 102 */ |
103 this.idleStateChangedListener_ = this.handleIdleStateChanged_.bind(this); | 103 this.idleStateChangedListener_ = this.handleIdleStateChanged_.bind(this); |
104 this.startupListener_ = this.handleStartup_.bind(this); | 104 this.startupListener_ = this.handleStartup_.bind(this); |
105 | 105 |
106 /** | 106 /** |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
209 * hotwordPrivate.onEnabledChanged() event. | 209 * hotwordPrivate.onEnabledChanged() event. |
210 */ | 210 */ |
211 updateStatus: function() { | 211 updateStatus: function() { |
212 chrome.hotwordPrivate.getStatus(this.handleStatus_.bind(this)); | 212 chrome.hotwordPrivate.getStatus(this.handleStatus_.bind(this)); |
213 }, | 213 }, |
214 | 214 |
215 /** | 215 /** |
216 * @return {boolean} True if google.com/NTP/launcher hotwording is enabled. | 216 * @return {boolean} True if google.com/NTP/launcher hotwording is enabled. |
217 */ | 217 */ |
218 isSometimesOnEnabled: function() { | 218 isSometimesOnEnabled: function() { |
219 assert(this.hotwordStatus_, | 219 assert( |
220 'No hotwording status (isSometimesOnEnabled)'); | 220 this.hotwordStatus_, 'No hotwording status (isSometimesOnEnabled)'); |
221 // Although the two settings are supposed to be mutually exclusive, it's | 221 // Although the two settings are supposed to be mutually exclusive, it's |
222 // possible for both to be set. In that case, always-on takes precedence. | 222 // possible for both to be set. In that case, always-on takes precedence. |
223 return this.hotwordStatus_.enabled && | 223 return this.hotwordStatus_.enabled && |
224 !this.hotwordStatus_.alwaysOnEnabled; | 224 !this.hotwordStatus_.alwaysOnEnabled; |
225 }, | 225 }, |
226 | 226 |
227 /** | 227 /** |
228 * @return {boolean} True if always-on hotwording is enabled. | 228 * @return {boolean} True if always-on hotwording is enabled. |
229 */ | 229 */ |
230 isAlwaysOnEnabled: function() { | 230 isAlwaysOnEnabled: function() { |
(...skipping 25 matching lines...) Expand all Loading... |
256 }, | 256 }, |
257 | 257 |
258 /** | 258 /** |
259 * Updates state based on the current status. | 259 * Updates state based on the current status. |
260 * @private | 260 * @private |
261 */ | 261 */ |
262 updateStateFromStatus_: function() { | 262 updateStateFromStatus_: function() { |
263 if (!this.hotwordStatus_) | 263 if (!this.hotwordStatus_) |
264 return; | 264 return; |
265 | 265 |
266 if (this.hotwordStatus_.enabled || | 266 if (this.hotwordStatus_.enabled || this.hotwordStatus_.alwaysOnEnabled || |
267 this.hotwordStatus_.alwaysOnEnabled || | |
268 this.hotwordStatus_.trainingEnabled) { | 267 this.hotwordStatus_.trainingEnabled) { |
269 // Detect changes to audio logging and kill the detector if that setting | 268 // Detect changes to audio logging and kill the detector if that setting |
270 // has changed. | 269 // has changed. |
271 if (this.hotwordStatus_.audioLoggingEnabled != this.loggingEnabled_) | 270 if (this.hotwordStatus_.audioLoggingEnabled != this.loggingEnabled_) |
272 this.shutdownDetector_(); | 271 this.shutdownDetector_(); |
273 this.loggingEnabled_ = this.hotwordStatus_.audioLoggingEnabled; | 272 this.loggingEnabled_ = this.hotwordStatus_.audioLoggingEnabled; |
274 | 273 |
275 // If the training state has changed, we need to first shut down the | 274 // If the training state has changed, we need to first shut down the |
276 // detector so that we can restart in a different mode. | 275 // detector so that we can restart in a different mode. |
277 if (this.hotwordStatus_.trainingEnabled != this.trainingEnabled_) | 276 if (this.hotwordStatus_.trainingEnabled != this.trainingEnabled_) |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
315 // Last attempt to start detector resulted in an error. | 314 // Last attempt to start detector resulted in an error. |
316 if (this.state_ == State_.ERROR) { | 315 if (this.state_ == State_.ERROR) { |
317 // TODO(amistry): Do some error rate tracking here and disable the | 316 // TODO(amistry): Do some error rate tracking here and disable the |
318 // extension if we error too often. | 317 // extension if we error too often. |
319 } | 318 } |
320 | 319 |
321 if (!this.pluginManager_) { | 320 if (!this.pluginManager_) { |
322 this.state_ = State_.STARTING; | 321 this.state_ = State_.STARTING; |
323 var isHotwordStream = this.isAlwaysOnEnabled() && | 322 var isHotwordStream = this.isAlwaysOnEnabled() && |
324 this.hotwordStatus_.hotwordHardwareAvailable; | 323 this.hotwordStatus_.hotwordHardwareAvailable; |
325 this.pluginManager_ = new hotword.NaClManager(this.loggingEnabled_, | 324 this.pluginManager_ = |
326 isHotwordStream); | 325 new hotword.NaClManager(this.loggingEnabled_, isHotwordStream); |
327 this.pluginManager_.addEventListener(hotword.constants.Event.READY, | 326 this.pluginManager_.addEventListener( |
328 this.onReady_.bind(this)); | 327 hotword.constants.Event.READY, this.onReady_.bind(this)); |
329 this.pluginManager_.addEventListener(hotword.constants.Event.ERROR, | 328 this.pluginManager_.addEventListener( |
330 this.onError_.bind(this)); | 329 hotword.constants.Event.ERROR, this.onError_.bind(this)); |
331 this.pluginManager_.addEventListener(hotword.constants.Event.TRIGGER, | 330 this.pluginManager_.addEventListener( |
332 this.onTrigger_.bind(this)); | 331 hotword.constants.Event.TRIGGER, this.onTrigger_.bind(this)); |
333 this.pluginManager_.addEventListener(hotword.constants.Event.TIMEOUT, | 332 this.pluginManager_.addEventListener( |
334 this.onTimeout_.bind(this)); | 333 hotword.constants.Event.TIMEOUT, this.onTimeout_.bind(this)); |
335 this.pluginManager_.addEventListener( | 334 this.pluginManager_.addEventListener( |
336 hotword.constants.Event.SPEAKER_MODEL_SAVED, | 335 hotword.constants.Event.SPEAKER_MODEL_SAVED, |
337 this.onSpeakerModelSaved_.bind(this)); | 336 this.onSpeakerModelSaved_.bind(this)); |
338 chrome.runtime.getPlatformInfo(function(platform) { | 337 chrome.runtime.getPlatformInfo(function(platform) { |
339 var naclArch = platform.nacl_arch; | 338 var naclArch = platform.nacl_arch; |
340 | 339 |
341 // googDucking set to false so that audio output level from other tabs | 340 // googDucking set to false so that audio output level from other tabs |
342 // is not affected when hotword is enabled. https://crbug.com/357773 | 341 // is not affected when hotword is enabled. https://crbug.com/357773 |
343 // content/common/media/media_stream_options.cc | 342 // content/common/media/media_stream_options.cc |
344 // When always-on is enabled, request the hotword stream. | 343 // When always-on is enabled, request the hotword stream. |
345 // Optional because we allow power users to bypass the hardware | 344 // Optional because we allow power users to bypass the hardware |
346 // detection via a flag, and hence the hotword stream may not be | 345 // detection via a flag, and hence the hotword stream may not be |
347 // available. | 346 // available. |
348 var constraints = /** @type {googMediaStreamConstraints} */ | 347 var constraints = /** @type {googMediaStreamConstraints} */ |
349 ({audio: {optional: [ | 348 ({ |
350 { googDucking: false }, | 349 audio: { |
351 { googHotword: this.isAlwaysOnEnabled() } | 350 optional: [ |
352 ]}}); | 351 {googDucking: false}, |
| 352 {googHotword: this.isAlwaysOnEnabled()} |
| 353 ] |
| 354 } |
| 355 }); |
353 navigator.webkitGetUserMedia( | 356 navigator.webkitGetUserMedia( |
354 /** @type {MediaStreamConstraints} */ (constraints), | 357 /** @type {MediaStreamConstraints} */ (constraints), |
355 function(stream) { | 358 function(stream) { |
356 hotword.metrics.recordEnum( | 359 hotword.metrics.recordEnum( |
357 hotword.constants.UmaMetrics.MEDIA_STREAM_RESULT, | 360 hotword.constants.UmaMetrics.MEDIA_STREAM_RESULT, |
358 hotword.constants.UmaMediaStreamOpenResult.SUCCESS, | 361 hotword.constants.UmaMediaStreamOpenResult.SUCCESS, |
359 hotword.constants.UmaMediaStreamOpenResult.MAX); | 362 hotword.constants.UmaMediaStreamOpenResult.MAX); |
360 // The detector could have been shut down before the stream | 363 // The detector could have been shut down before the stream |
361 // finishes opening. | 364 // finishes opening. |
362 if (this.pluginManager_ == null) { | 365 if (this.pluginManager_ == null) { |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
445 shutdownDetector_: function() { | 448 shutdownDetector_: function() { |
446 this.state_ = State_.STOPPED; | 449 this.state_ = State_.STOPPED; |
447 this.shutdownPluginManager_(); | 450 this.shutdownPluginManager_(); |
448 }, | 451 }, |
449 | 452 |
450 /** | 453 /** |
451 * Finalizes the speaker model. Assumes the plugin has been loaded and | 454 * Finalizes the speaker model. Assumes the plugin has been loaded and |
452 * started. | 455 * started. |
453 */ | 456 */ |
454 finalizeSpeakerModel: function() { | 457 finalizeSpeakerModel: function() { |
455 assert(this.pluginManager_, | 458 assert( |
456 'Cannot finalize speaker model: No NaCl plugin loaded'); | 459 this.pluginManager_, |
| 460 'Cannot finalize speaker model: No NaCl plugin loaded'); |
457 if (this.state_ != State_.RUNNING) { | 461 if (this.state_ != State_.RUNNING) { |
458 hotword.debug('Cannot finalize speaker model: NaCl plugin not started'); | 462 hotword.debug('Cannot finalize speaker model: NaCl plugin not started'); |
459 return; | 463 return; |
460 } | 464 } |
461 this.pluginManager_.finalizeSpeakerModel(); | 465 this.pluginManager_.finalizeSpeakerModel(); |
462 }, | 466 }, |
463 | 467 |
464 /** | 468 /** |
465 * Handle the hotword plugin being ready to start. | 469 * Handle the hotword plugin being ready to start. |
466 * @private | 470 * @private |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
570 * @param {!hotword.constants.SessionSource} source Source of the hotword | 574 * @param {!hotword.constants.SessionSource} source Source of the hotword |
571 * session request. | 575 * session request. |
572 * @param {!function()} startedCb Callback invoked when the session has | 576 * @param {!function()} startedCb Callback invoked when the session has |
573 * been started successfully. | 577 * been started successfully. |
574 * @param {!function()} triggerCb Callback invoked when the hotword has | 578 * @param {!function()} triggerCb Callback invoked when the hotword has |
575 * @param {function()=} modelSavedCb Callback invoked when the speaker model | 579 * @param {function()=} modelSavedCb Callback invoked when the speaker model |
576 * has been saved. | 580 * has been saved. |
577 * @param {hotword.constants.RecognizerStartMode=} opt_mode The mode to | 581 * @param {hotword.constants.RecognizerStartMode=} opt_mode The mode to |
578 * start the recognizer in. | 582 * start the recognizer in. |
579 */ | 583 */ |
580 startSession: function(source, startedCb, triggerCb, | 584 startSession: function( |
581 opt_modelSavedCb, opt_mode) { | 585 source, startedCb, triggerCb, opt_modelSavedCb, opt_mode) { |
582 if (this.isTrainingEnabled() && opt_mode) { | 586 if (this.isTrainingEnabled() && opt_mode) { |
583 this.startMode_ = opt_mode; | 587 this.startMode_ = opt_mode; |
584 } else { | 588 } else { |
585 this.startMode_ = hotword.constants.RecognizerStartMode.NORMAL; | 589 this.startMode_ = hotword.constants.RecognizerStartMode.NORMAL; |
586 } | 590 } |
587 hotword.debug('Starting session for source: ' + source); | 591 hotword.debug('Starting session for source: ' + source); |
588 this.removeSession_(source); | 592 this.removeSession_(source); |
589 this.sessions_.push(new Session_(source, triggerCb, startedCb, | 593 this.sessions_.push( |
590 opt_modelSavedCb)); | 594 new Session_(source, triggerCb, startedCb, opt_modelSavedCb)); |
591 this.updateStateFromStatus_(); | 595 this.updateStateFromStatus_(); |
592 }, | 596 }, |
593 | 597 |
594 /** | 598 /** |
595 * Stops a hotwording session. | 599 * Stops a hotwording session. |
596 * @param {!hotword.constants.SessionSource} source Source of the hotword | 600 * @param {!hotword.constants.SessionSource} source Source of the hotword |
597 * session request. | 601 * session request. |
598 */ | 602 */ |
599 stopSession: function(source) { | 603 stopSession: function(source) { |
600 hotword.debug('Stopping session for source: ' + source); | 604 hotword.debug('Stopping session for source: ' + source); |
(...skipping 25 matching lines...) Expand all Loading... |
626 /** | 630 /** |
627 * Handles a chrome.runtime.onStartup event. | 631 * Handles a chrome.runtime.onStartup event. |
628 * @private | 632 * @private |
629 */ | 633 */ |
630 handleStartup_: function() { | 634 handleStartup_: function() { |
631 // Nothing specific needs to be done here. This function exists solely to | 635 // Nothing specific needs to be done here. This function exists solely to |
632 // be registered on the startup event. | 636 // be registered on the startup event. |
633 } | 637 } |
634 }; | 638 }; |
635 | 639 |
636 return { | 640 return {StateManager: StateManager}; |
637 StateManager: StateManager | |
638 }; | |
639 }); | 641 }); |
OLD | NEW |