1 // Copyright (c) 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 #include "content/browser/speech/speech_recognition_manager_impl.h" 6 7 #include "base/bind.h" 8 #include "content/browser/browser_main_loop.h" 9 #include "content/browser/renderer_host/media/media_stream_manager.h" 10 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h" 11 #include "content/browser/speech/google_one_shot_remote_engine.h" 12 #include "content/browser/speech/google_streaming_remote_engine.h" 13 #include "content/browser/speech/speech_recognition_engine.h" 14 #include "content/browser/speech/speech_recognizer_impl.h" 15 #include "content/public/browser/browser_thread.h" 16 #include "content/public/browser/content_browser_client.h" 17 #include "content/public/browser/resource_context.h" 18 #include "content/public/browser/speech_recognition_event_listener.h" 19 #include "content/public/browser/speech_recognition_manager_delegate.h" 20 #include "content/public/browser/speech_recognition_session_config.h" 21 #include "content/public/browser/speech_recognition_session_context.h" 22 #include "content/public/common/speech_recognition_error.h" 23 #include "content/public/common/speech_recognition_result.h" 24 #include "media/audio/audio_manager.h" 25 #include "media/audio/audio_manager_base.h" 26 27 #if defined(OS_ANDROID) 28 #include "content/browser/speech/speech_recognizer_impl_android.h" 29 #endif 30 31 using base::Callback; 32 33 namespace content { 34 35 SpeechRecognitionManager* SpeechRecognitionManager::manager_for_tests_; 36 37 namespace { 38 39 SpeechRecognitionManagerImpl* g_speech_recognition_manager_impl; 40 41 void ShowAudioInputSettingsOnFileThread(media::AudioManager* audio_manager) { 42 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 43 audio_manager->ShowAudioInputSettings(); 44 } 45 46 } // namespace 47 48 SpeechRecognitionManager* SpeechRecognitionManager::GetInstance() { 49 if (manager_for_tests_) 50 return manager_for_tests_; 51 return SpeechRecognitionManagerImpl::GetInstance(); 52 } 53 54 void SpeechRecognitionManager::SetManagerForTesting( 55 SpeechRecognitionManager* manager) { 56 manager_for_tests_ = manager; 57 } 58 59 SpeechRecognitionManagerImpl* SpeechRecognitionManagerImpl::GetInstance() { 60 return g_speech_recognition_manager_impl; 61 } 62 63 SpeechRecognitionManagerImpl::SpeechRecognitionManagerImpl( 64 media::AudioManager* audio_manager, 65 MediaStreamManager* media_stream_manager) 66 : audio_manager_(audio_manager), 67 media_stream_manager_(media_stream_manager), 68 primary_session_id_(kSessionIDInvalid), 69 last_session_id_(kSessionIDInvalid), 70 is_dispatching_event_(false), 71 delegate_(GetContentClient()->browser()-> 72 GetSpeechRecognitionManagerDelegate()), 73 weak_factory_(this) { 74 DCHECK(!g_speech_recognition_manager_impl); 75 g_speech_recognition_manager_impl = this; 76 } 77 78 SpeechRecognitionManagerImpl::~SpeechRecognitionManagerImpl() { 79 DCHECK(g_speech_recognition_manager_impl); 80 g_speech_recognition_manager_impl = NULL; 81 82 for (SessionsTable::iterator it = sessions_.begin(); it != sessions_.end(); 83 ++it) { 84 // MediaStreamUIProxy must be deleted on the IO thread. 85 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, 86 it->second->ui.release()); 87 delete it->second; 88 } 89 sessions_.clear(); 90 } 91 92 int SpeechRecognitionManagerImpl::CreateSession( 93 const SpeechRecognitionSessionConfig& config) { 94 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 95 96 const int session_id = GetNextSessionID(); 97 DCHECK(!SessionExists(session_id)); 98 // Set-up the new session. 99 Session* session = new Session(); 100 sessions_[session_id] = session; 101 session->id = session_id; 102 session->config = config; 103 session->context = config.initial_context; 104 105 std::string hardware_info; 106 bool can_report_metrics = false; 107 if (delegate_) 108 delegate_->GetDiagnosticInformation(&can_report_metrics, &hardware_info); 109 110 // The legacy api cannot use continuous mode. 111 DCHECK(!config.is_legacy_api || !config.continuous); 112 113 #if !defined(OS_ANDROID) 114 // A SpeechRecognitionEngine (and corresponding Config) is required only 115 // when using SpeechRecognizerImpl, which performs the audio capture and 116 // endpointing in the browser. This is not the case of Android where, not 117 // only the speech recognition, but also the audio capture and endpointing 118 // activities performed outside of the browser (delegated via JNI to the 119 // Android API implementation). 120 121 SpeechRecognitionEngineConfig remote_engine_config; 122 remote_engine_config.language = config.language; 123 remote_engine_config.grammars = config.grammars; 124 remote_engine_config.audio_sample_rate = 125 SpeechRecognizerImpl::kAudioSampleRate; 126 remote_engine_config.audio_num_bits_per_sample = 127 SpeechRecognizerImpl::kNumBitsPerAudioSample; 128 remote_engine_config.filter_profanities = config.filter_profanities; 129 remote_engine_config.continuous = config.continuous; 130 remote_engine_config.interim_results = config.interim_results; 131 remote_engine_config.max_hypotheses = config.max_hypotheses; 132 remote_engine_config.hardware_info = hardware_info; 133 remote_engine_config.origin_url = 134 can_report_metrics ? config.origin_url : std::string(); 135 136 SpeechRecognitionEngine* google_remote_engine; 137 if (config.is_legacy_api) { 138 google_remote_engine = 139 new GoogleOneShotRemoteEngine(config.url_request_context_getter.get()); 140 } else { 141 google_remote_engine = new GoogleStreamingRemoteEngine( 142 config.url_request_context_getter.get()); 143 } 144 145 google_remote_engine->SetConfig(remote_engine_config); 146 147 session->recognizer = new SpeechRecognizerImpl( 148 this, 149 session_id, 150 config.continuous, 151 config.interim_results, 152 google_remote_engine); 153 #else 154 session->recognizer = new SpeechRecognizerImplAndroid(this, session_id); 155 #endif 156 return session_id; 157 } 158 159 void SpeechRecognitionManagerImpl::StartSession(int session_id) { 160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 161 if (!SessionExists(session_id)) 162 return; 163 164 // If there is another active session, abort that. 165 if (primary_session_id_ != kSessionIDInvalid && 166 primary_session_id_ != session_id) { 167 AbortSession(primary_session_id_); 168 } 169 170 primary_session_id_ = session_id; 171 172 if (delegate_) { 173 delegate_->CheckRecognitionIsAllowed( 174 session_id, 175 base::Bind(&SpeechRecognitionManagerImpl::RecognitionAllowedCallback, 176 weak_factory_.GetWeakPtr(), 177 session_id)); 178 } 179 } 180 181 void SpeechRecognitionManagerImpl::RecognitionAllowedCallback(int session_id, 182 bool ask_user, 183 bool is_allowed) { 184 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 185 if (!SessionExists(session_id)) 186 return; 187 188 SessionsTable::iterator iter = sessions_.find(session_id); 189 DCHECK(iter != sessions_.end()); 190 Session* session = iter->second; 191 192 if (session->abort_requested) 193 return; 194 195 if (ask_user) { 196 SpeechRecognitionSessionContext& context = session->context; 197 context.label = media_stream_manager_->MakeMediaAccessRequest( 198 context.render_process_id, 199 context.render_frame_id, 200 context.request_id, 201 StreamOptions(true, false), 202 GURL(context.context_name), 203 base::Bind( 204 &SpeechRecognitionManagerImpl::MediaRequestPermissionCallback, 205 weak_factory_.GetWeakPtr(), session_id)); 206 return; 207 } 208 209 if (is_allowed) { 210 base::MessageLoop::current()->PostTask( 211 FROM_HERE, 212 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent, 213 weak_factory_.GetWeakPtr(), 214 session_id, 215 EVENT_START)); 216 } else { 217 OnRecognitionError(session_id, SpeechRecognitionError( 218 SPEECH_RECOGNITION_ERROR_NOT_ALLOWED)); 219 base::MessageLoop::current()->PostTask( 220 FROM_HERE, 221 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent, 222 weak_factory_.GetWeakPtr(), 223 session_id, 224 EVENT_ABORT)); 225 } 226 } 227 228 void SpeechRecognitionManagerImpl::MediaRequestPermissionCallback( 229 int session_id, 230 const MediaStreamDevices& devices, 231 scoped_ptr<MediaStreamUIProxy> stream_ui) { 232 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 233 234 SessionsTable::iterator iter = sessions_.find(session_id); 235 if (iter == sessions_.end()) 236 return; 237 238 bool is_allowed = !devices.empty(); 239 if (is_allowed) { 240 // Copy the approved devices array to the context for UI indication. 241 iter->second->context.devices = devices; 242 243 // Save the UI object. 244 iter->second->ui = stream_ui.Pass(); 245 } 246 247 // Clear the label to indicate the request has been done. 248 iter->second->context.label.clear(); 249 250 // Notify the recognition about the request result. 251 RecognitionAllowedCallback(iter->first, false, is_allowed); 252 } 253 254 void SpeechRecognitionManagerImpl::AbortSession(int session_id) { 255 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 256 if (!SessionExists(session_id)) 257 return; 258 259 SessionsTable::iterator iter = sessions_.find(session_id); 260 iter->second->ui.reset(); 261 262 if (iter->second->abort_requested) 263 return; 264 265 iter->second->abort_requested = true; 266 267 base::MessageLoop::current()->PostTask( 268 FROM_HERE, 269 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent, 270 weak_factory_.GetWeakPtr(), 271 session_id, 272 EVENT_ABORT)); 273 } 274 275 void SpeechRecognitionManagerImpl::StopAudioCaptureForSession(int session_id) { 276 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 277 if (!SessionExists(session_id)) 278 return; 279 280 SessionsTable::iterator iter = sessions_.find(session_id); 281 iter->second->ui.reset(); 282 283 base::MessageLoop::current()->PostTask( 284 FROM_HERE, 285 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent, 286 weak_factory_.GetWeakPtr(), 287 session_id, 288 EVENT_STOP_CAPTURE)); 289 } 290 291 // Here begins the SpeechRecognitionEventListener interface implementation, 292 // which will simply relay the events to the proper listener registered for the 293 // particular session and to the catch-all listener provided by the delegate 294 // (if any). 295 296 void SpeechRecognitionManagerImpl::OnRecognitionStart(int session_id) { 297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 298 if (!SessionExists(session_id)) 299 return; 300 301 SessionsTable::iterator iter = sessions_.find(session_id); 302 if (iter->second->ui) { 303 // Notify the UI that the devices are being used. 304 iter->second->ui->OnStarted(base::Closure(), 305 MediaStreamUIProxy::WindowIdCallback()); 306 } 307 308 DCHECK_EQ(primary_session_id_, session_id); 309 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 310 delegate_listener->OnRecognitionStart(session_id); 311 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 312 listener->OnRecognitionStart(session_id); 313 } 314 315 void SpeechRecognitionManagerImpl::OnAudioStart(int session_id) { 316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 317 if (!SessionExists(session_id)) 318 return; 319 320 DCHECK_EQ(primary_session_id_, session_id); 321 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 322 delegate_listener->OnAudioStart(session_id); 323 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 324 listener->OnAudioStart(session_id); 325 } 326 327 void SpeechRecognitionManagerImpl::OnEnvironmentEstimationComplete( 328 int session_id) { 329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 330 if (!SessionExists(session_id)) 331 return; 332 333 DCHECK_EQ(primary_session_id_, session_id); 334 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 335 delegate_listener->OnEnvironmentEstimationComplete(session_id); 336 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 337 listener->OnEnvironmentEstimationComplete(session_id); 338 } 339 340 void SpeechRecognitionManagerImpl::OnSoundStart(int session_id) { 341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 342 if (!SessionExists(session_id)) 343 return; 344 345 DCHECK_EQ(primary_session_id_, session_id); 346 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 347 delegate_listener->OnSoundStart(session_id); 348 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 349 listener->OnSoundStart(session_id); 350 } 351 352 void SpeechRecognitionManagerImpl::OnSoundEnd(int session_id) { 353 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 354 if (!SessionExists(session_id)) 355 return; 356 357 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 358 delegate_listener->OnSoundEnd(session_id); 359 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 360 listener->OnSoundEnd(session_id); 361 } 362 363 void SpeechRecognitionManagerImpl::OnAudioEnd(int session_id) { 364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 365 if (!SessionExists(session_id)) 366 return; 367 368 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 369 delegate_listener->OnAudioEnd(session_id); 370 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 371 listener->OnAudioEnd(session_id); 372 base::MessageLoop::current()->PostTask( 373 FROM_HERE, 374 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent, 375 weak_factory_.GetWeakPtr(), 376 session_id, 377 EVENT_AUDIO_ENDED)); 378 } 379 380 void SpeechRecognitionManagerImpl::OnRecognitionResults( 381 int session_id, const SpeechRecognitionResults& results) { 382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 383 if (!SessionExists(session_id)) 384 return; 385 386 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 387 delegate_listener->OnRecognitionResults(session_id, results); 388 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 389 listener->OnRecognitionResults(session_id, results); 390 } 391 392 void SpeechRecognitionManagerImpl::OnRecognitionError( 393 int session_id, const SpeechRecognitionError& error) { 394 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 395 if (!SessionExists(session_id)) 396 return; 397 398 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 399 delegate_listener->OnRecognitionError(session_id, error); 400 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 401 listener->OnRecognitionError(session_id, error); 402 } 403 404 void SpeechRecognitionManagerImpl::OnAudioLevelsChange( 405 int session_id, float volume, float noise_volume) { 406 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 407 if (!SessionExists(session_id)) 408 return; 409 410 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 411 delegate_listener->OnAudioLevelsChange(session_id, volume, noise_volume); 412 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 413 listener->OnAudioLevelsChange(session_id, volume, noise_volume); 414 } 415 416 void SpeechRecognitionManagerImpl::OnRecognitionEnd(int session_id) { 417 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 418 if (!SessionExists(session_id)) 419 return; 420 421 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 422 delegate_listener->OnRecognitionEnd(session_id); 423 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 424 listener->OnRecognitionEnd(session_id); 425 base::MessageLoop::current()->PostTask( 426 FROM_HERE, 427 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent, 428 weak_factory_.GetWeakPtr(), 429 session_id, 430 EVENT_RECOGNITION_ENDED)); 431 } 432 433 int SpeechRecognitionManagerImpl::GetSession( 434 int render_process_id, int render_view_id, int request_id) const { 435 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 436 SessionsTable::const_iterator iter; 437 for(iter = sessions_.begin(); iter != sessions_.end(); ++iter) { 438 const int session_id = iter->first; 439 const SpeechRecognitionSessionContext& context = iter->second->context; 440 if (context.render_process_id == render_process_id && 441 context.render_view_id == render_view_id && 442 context.request_id == request_id) { 443 return session_id; 444 } 445 } 446 return kSessionIDInvalid; 447 } 448 449 SpeechRecognitionSessionContext 450 SpeechRecognitionManagerImpl::GetSessionContext(int session_id) const { 451 return GetSession(session_id)->context; 452 } 453 454 void SpeechRecognitionManagerImpl::AbortAllSessionsForRenderProcess( 455 int render_process_id) { 456 // This method gracefully destroys sessions for the listener. However, since 457 // the listener itself is likely to be destroyed after this call, we avoid 458 // dispatching further events to it, marking the |listener_is_active| flag. 459 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 460 for (SessionsTable::iterator it = sessions_.begin(); it != sessions_.end(); 461 ++it) { 462 Session* session = it->second; 463 if (session->context.render_process_id == render_process_id) { 464 AbortSession(session->id); 465 session->listener_is_active = false; 466 } 467 } 468 } 469 470 void SpeechRecognitionManagerImpl::AbortAllSessionsForRenderView( 471 int render_process_id, 472 int render_view_id) { 473 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 474 for (SessionsTable::iterator it = sessions_.begin(); it != sessions_.end(); 475 ++it) { 476 Session* session = it->second; 477 if (session->context.render_process_id == render_process_id && 478 session->context.render_view_id == render_view_id) { 479 AbortSession(session->id); 480 } 481 } 482 } 483 484 // ----------------------- Core FSM implementation --------------------------- 485 void SpeechRecognitionManagerImpl::DispatchEvent(int session_id, 486 FSMEvent event) { 487 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 488 489 // There are some corner cases in which the session might be deleted (due to 490 // an EndRecognition event) between a request (e.g. Abort) and its dispatch. 491 if (!SessionExists(session_id)) 492 return; 493 494 Session* session = GetSession(session_id); 495 FSMState session_state = GetSessionState(session_id); 496 DCHECK_LE(session_state, SESSION_STATE_MAX_VALUE); 497 DCHECK_LE(event, EVENT_MAX_VALUE); 498 499 // Event dispatching must be sequential, otherwise it will break all the rules 500 // and the assumptions of the finite state automata model. 501 DCHECK(!is_dispatching_event_); 502 is_dispatching_event_ = true; 503 ExecuteTransitionAndGetNextState(session, session_state, event); 504 is_dispatching_event_ = false; 505 } 506 507 // This FSM handles the evolution of each session, from the viewpoint of the 508 // interaction with the user (that may be either the browser end-user which 509 // interacts with UI bubbles, or JS developer intracting with JS methods). 510 // All the events received by the SpeechRecognizer instances (one for each 511 // session) are always routed to the SpeechRecognitionEventListener(s) 512 // regardless the choices taken in this FSM. 513 void SpeechRecognitionManagerImpl::ExecuteTransitionAndGetNextState( 514 Session* session, FSMState session_state, FSMEvent event) { 515 // Note: since we're not tracking the state of the recognizer object, rather 516 // we're directly retrieving it (through GetSessionState), we see its events 517 // (that are AUDIO_ENDED and RECOGNITION_ENDED) after its state evolution 518 // (e.g., when we receive the AUDIO_ENDED event, the recognizer has just 519 // completed the transition from CAPTURING_AUDIO to WAITING_FOR_RESULT, thus 520 // we perceive the AUDIO_ENDED event in WAITING_FOR_RESULT). 521 // This makes the code below a bit tricky but avoids a lot of code for 522 // tracking and reconstructing asynchronously the state of the recognizer. 523 switch (session_state) { 524 case SESSION_STATE_IDLE: 525 switch (event) { 526 case EVENT_START: 527 return SessionStart(*session); 528 case EVENT_ABORT: 529 return SessionAbort(*session); 530 case EVENT_RECOGNITION_ENDED: 531 return SessionDelete(session); 532 case EVENT_STOP_CAPTURE: 533 return SessionStopAudioCapture(*session); 534 case EVENT_AUDIO_ENDED: 535 return; 536 } 537 break; 538 case SESSION_STATE_CAPTURING_AUDIO: 539 switch (event) { 540 case EVENT_STOP_CAPTURE: 541 return SessionStopAudioCapture(*session); 542 case EVENT_ABORT: 543 return SessionAbort(*session); 544 case EVENT_START: 545 return; 546 case EVENT_AUDIO_ENDED: 547 case EVENT_RECOGNITION_ENDED: 548 return NotFeasible(*session, event); 549 } 550 break; 551 case SESSION_STATE_WAITING_FOR_RESULT: 552 switch (event) { 553 case EVENT_ABORT: 554 return SessionAbort(*session); 555 case EVENT_AUDIO_ENDED: 556 return ResetCapturingSessionId(*session); 557 case EVENT_START: 558 case EVENT_STOP_CAPTURE: 559 return; 560 case EVENT_RECOGNITION_ENDED: 561 return NotFeasible(*session, event); 562 } 563 break; 564 } 565 return NotFeasible(*session, event); 566 } 567 568 SpeechRecognitionManagerImpl::FSMState 569 SpeechRecognitionManagerImpl::GetSessionState(int session_id) const { 570 Session* session = GetSession(session_id); 571 if (!session->recognizer.get() || !session->recognizer->IsActive()) 572 return SESSION_STATE_IDLE; 573 if (session->recognizer->IsCapturingAudio()) 574 return SESSION_STATE_CAPTURING_AUDIO; 575 return SESSION_STATE_WAITING_FOR_RESULT; 576 } 577 578 // ----------- Contract for all the FSM evolution functions below ------------- 579 // - Are guaranteed to be executed in the IO thread; 580 // - Are guaranteed to be not reentrant (themselves and each other); 581 582 void SpeechRecognitionManagerImpl::SessionStart(const Session& session) { 583 DCHECK_EQ(primary_session_id_, session.id); 584 const MediaStreamDevices& devices = session.context.devices; 585 std::string device_id; 586 if (devices.empty()) { 587 // From the ask_user=false path, use the default device. 588 // TODO(xians): Abort the session after we do not need to support this path 589 // anymore. 590 device_id = media::AudioManagerBase::kDefaultDeviceId; 591 } else { 592 // From the ask_user=true path, use the selected device. 593 DCHECK_EQ(1u, devices.size()); 594 DCHECK_EQ(MEDIA_DEVICE_AUDIO_CAPTURE, devices.front().type); 595 device_id = devices.front().id; 596 } 597 598 session.recognizer->StartRecognition(device_id); 599 } 600 601 void SpeechRecognitionManagerImpl::SessionAbort(const Session& session) { 602 if (primary_session_id_ == session.id) 603 primary_session_id_ = kSessionIDInvalid; 604 DCHECK(session.recognizer.get()); 605 session.recognizer->AbortRecognition(); 606 } 607 608 void SpeechRecognitionManagerImpl::SessionStopAudioCapture( 609 const Session& session) { 610 DCHECK(session.recognizer.get()); 611 session.recognizer->StopAudioCapture(); 612 } 613 614 void SpeechRecognitionManagerImpl::ResetCapturingSessionId( 615 const Session& session) { 616 DCHECK_EQ(primary_session_id_, session.id); 617 primary_session_id_ = kSessionIDInvalid; 618 } 619 620 void SpeechRecognitionManagerImpl::SessionDelete(Session* session) { 621 DCHECK(session->recognizer.get() == NULL || !session->recognizer->IsActive()); 622 if (primary_session_id_ == session->id) 623 primary_session_id_ = kSessionIDInvalid; 624 if (!session->context.label.empty()) 625 media_stream_manager_->CancelRequest(session->context.label); 626 sessions_.erase(session->id); 627 delete session; 628 } 629 630 void SpeechRecognitionManagerImpl::NotFeasible(const Session& session, 631 FSMEvent event) { 632 NOTREACHED() << "Unfeasible event " << event 633 << " in state " << GetSessionState(session.id) 634 << " for session " << session.id; 635 } 636 637 int SpeechRecognitionManagerImpl::GetNextSessionID() { 638 ++last_session_id_; 639 // Deal with wrapping of last_session_id_. (How civilized). 640 if (last_session_id_ <= 0) 641 last_session_id_ = 1; 642 return last_session_id_; 643 } 644 645 bool SpeechRecognitionManagerImpl::SessionExists(int session_id) const { 646 return sessions_.find(session_id) != sessions_.end(); 647 } 648 649 SpeechRecognitionManagerImpl::Session* 650 SpeechRecognitionManagerImpl::GetSession(int session_id) const { 651 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 652 SessionsTable::const_iterator iter = sessions_.find(session_id); 653 DCHECK(iter != sessions_.end()); 654 return iter->second; 655 } 656 657 SpeechRecognitionEventListener* SpeechRecognitionManagerImpl::GetListener( 658 int session_id) const { 659 Session* session = GetSession(session_id); 660 if (session->listener_is_active && session->config.event_listener) 661 return session->config.event_listener.get(); 662 return NULL; 663 } 664 665 SpeechRecognitionEventListener* 666 SpeechRecognitionManagerImpl::GetDelegateListener() const { 667 return delegate_.get() ? delegate_->GetEventListener() : NULL; 668 } 669 670 const SpeechRecognitionSessionConfig& 671 SpeechRecognitionManagerImpl::GetSessionConfig(int session_id) const { 672 return GetSession(session_id)->config; 673 } 674 675 bool SpeechRecognitionManagerImpl::HasAudioInputDevices() { 676 return audio_manager_->HasAudioInputDevices(); 677 } 678 679 base::string16 SpeechRecognitionManagerImpl::GetAudioInputDeviceModel() { 680 return audio_manager_->GetAudioInputDeviceModel(); 681 } 682 683 void SpeechRecognitionManagerImpl::ShowAudioInputSettings() { 684 // Since AudioManager::ShowAudioInputSettings can potentially launch external 685 // processes, do that in the FILE thread to not block the calling threads. 686 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 687 base::Bind(&ShowAudioInputSettingsOnFileThread, 688 audio_manager_)); 689 } 690 691 SpeechRecognitionManagerImpl::Session::Session() 692 : id(kSessionIDInvalid), 693 abort_requested(false), 694 listener_is_active(true) { 695 } 696 697 SpeechRecognitionManagerImpl::Session::~Session() { 698 } 699 700 } // namespace content 701