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 google_remote_engine); 152 #else 153 session->recognizer = new SpeechRecognizerImplAndroid(this, session_id); 154 #endif 155 return session_id; 156 } 157 158 void SpeechRecognitionManagerImpl::StartSession(int session_id) { 159 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 160 if (!SessionExists(session_id)) 161 return; 162 163 // If there is another active session, abort that. 164 if (primary_session_id_ != kSessionIDInvalid && 165 primary_session_id_ != session_id) { 166 AbortSession(primary_session_id_); 167 } 168 169 primary_session_id_ = session_id; 170 171 if (delegate_) { 172 delegate_->CheckRecognitionIsAllowed( 173 session_id, 174 base::Bind(&SpeechRecognitionManagerImpl::RecognitionAllowedCallback, 175 weak_factory_.GetWeakPtr(), 176 session_id)); 177 } 178 } 179 180 void SpeechRecognitionManagerImpl::RecognitionAllowedCallback(int session_id, 181 bool ask_user, 182 bool is_allowed) { 183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 184 if (!SessionExists(session_id)) 185 return; 186 187 SessionsTable::iterator iter = sessions_.find(session_id); 188 DCHECK(iter != sessions_.end()); 189 Session* session = iter->second; 190 191 if (session->abort_requested) 192 return; 193 194 if (ask_user) { 195 SpeechRecognitionSessionContext& context = session->context; 196 context.label = media_stream_manager_->MakeMediaAccessRequest( 197 context.render_process_id, 198 context.render_view_id, 199 context.request_id, 200 StreamOptions(true, false), 201 GURL(context.context_name), 202 base::Bind( 203 &SpeechRecognitionManagerImpl::MediaRequestPermissionCallback, 204 weak_factory_.GetWeakPtr(), session_id)); 205 return; 206 } 207 208 if (is_allowed) { 209 base::MessageLoop::current()->PostTask( 210 FROM_HERE, 211 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent, 212 weak_factory_.GetWeakPtr(), 213 session_id, 214 EVENT_START)); 215 } else { 216 OnRecognitionError(session_id, SpeechRecognitionError( 217 SPEECH_RECOGNITION_ERROR_NOT_ALLOWED)); 218 base::MessageLoop::current()->PostTask( 219 FROM_HERE, 220 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent, 221 weak_factory_.GetWeakPtr(), 222 session_id, 223 EVENT_ABORT)); 224 } 225 } 226 227 void SpeechRecognitionManagerImpl::MediaRequestPermissionCallback( 228 int session_id, 229 const MediaStreamDevices& devices, 230 scoped_ptr<MediaStreamUIProxy> stream_ui) { 231 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 232 233 SessionsTable::iterator iter = sessions_.find(session_id); 234 if (iter == sessions_.end()) 235 return; 236 237 bool is_allowed = !devices.empty(); 238 if (is_allowed) { 239 // Copy the approved devices array to the context for UI indication. 240 iter->second->context.devices = devices; 241 242 // Save the UI object. 243 iter->second->ui = stream_ui.Pass(); 244 } 245 246 // Clear the label to indicate the request has been done. 247 iter->second->context.label.clear(); 248 249 // Notify the recognition about the request result. 250 RecognitionAllowedCallback(iter->first, false, is_allowed); 251 } 252 253 void SpeechRecognitionManagerImpl::AbortSession(int session_id) { 254 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 255 if (!SessionExists(session_id)) 256 return; 257 258 SessionsTable::iterator iter = sessions_.find(session_id); 259 iter->second->ui.reset(); 260 261 if (iter->second->abort_requested) 262 return; 263 264 iter->second->abort_requested = true; 265 266 base::MessageLoop::current()->PostTask( 267 FROM_HERE, 268 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent, 269 weak_factory_.GetWeakPtr(), 270 session_id, 271 EVENT_ABORT)); 272 } 273 274 void SpeechRecognitionManagerImpl::StopAudioCaptureForSession(int session_id) { 275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 276 if (!SessionExists(session_id)) 277 return; 278 279 SessionsTable::iterator iter = sessions_.find(session_id); 280 iter->second->ui.reset(); 281 282 base::MessageLoop::current()->PostTask( 283 FROM_HERE, 284 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent, 285 weak_factory_.GetWeakPtr(), 286 session_id, 287 EVENT_STOP_CAPTURE)); 288 } 289 290 // Here begins the SpeechRecognitionEventListener interface implementation, 291 // which will simply relay the events to the proper listener registered for the 292 // particular session (most likely InputTagSpeechDispatcherHost) and to the 293 // catch-all listener provided by the delegate (if any). 294 295 void SpeechRecognitionManagerImpl::OnRecognitionStart(int session_id) { 296 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 297 if (!SessionExists(session_id)) 298 return; 299 300 SessionsTable::iterator iter = sessions_.find(session_id); 301 if (iter->second->ui) { 302 // Notify the UI that the devices are being used. 303 iter->second->ui->OnStarted(base::Closure()); 304 } 305 306 DCHECK_EQ(primary_session_id_, session_id); 307 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 308 delegate_listener->OnRecognitionStart(session_id); 309 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 310 listener->OnRecognitionStart(session_id); 311 } 312 313 void SpeechRecognitionManagerImpl::OnAudioStart(int session_id) { 314 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 315 if (!SessionExists(session_id)) 316 return; 317 318 DCHECK_EQ(primary_session_id_, session_id); 319 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 320 delegate_listener->OnAudioStart(session_id); 321 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 322 listener->OnAudioStart(session_id); 323 } 324 325 void SpeechRecognitionManagerImpl::OnEnvironmentEstimationComplete( 326 int session_id) { 327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 328 if (!SessionExists(session_id)) 329 return; 330 331 DCHECK_EQ(primary_session_id_, session_id); 332 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 333 delegate_listener->OnEnvironmentEstimationComplete(session_id); 334 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 335 listener->OnEnvironmentEstimationComplete(session_id); 336 } 337 338 void SpeechRecognitionManagerImpl::OnSoundStart(int session_id) { 339 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 340 if (!SessionExists(session_id)) 341 return; 342 343 DCHECK_EQ(primary_session_id_, session_id); 344 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 345 delegate_listener->OnSoundStart(session_id); 346 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 347 listener->OnSoundStart(session_id); 348 } 349 350 void SpeechRecognitionManagerImpl::OnSoundEnd(int session_id) { 351 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 352 if (!SessionExists(session_id)) 353 return; 354 355 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 356 delegate_listener->OnSoundEnd(session_id); 357 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 358 listener->OnSoundEnd(session_id); 359 } 360 361 void SpeechRecognitionManagerImpl::OnAudioEnd(int session_id) { 362 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 363 if (!SessionExists(session_id)) 364 return; 365 366 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 367 delegate_listener->OnAudioEnd(session_id); 368 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 369 listener->OnAudioEnd(session_id); 370 base::MessageLoop::current()->PostTask( 371 FROM_HERE, 372 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent, 373 weak_factory_.GetWeakPtr(), 374 session_id, 375 EVENT_AUDIO_ENDED)); 376 } 377 378 void SpeechRecognitionManagerImpl::OnRecognitionResults( 379 int session_id, const SpeechRecognitionResults& results) { 380 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 381 if (!SessionExists(session_id)) 382 return; 383 384 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 385 delegate_listener->OnRecognitionResults(session_id, results); 386 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 387 listener->OnRecognitionResults(session_id, results); 388 } 389 390 void SpeechRecognitionManagerImpl::OnRecognitionError( 391 int session_id, const SpeechRecognitionError& error) { 392 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 393 if (!SessionExists(session_id)) 394 return; 395 396 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 397 delegate_listener->OnRecognitionError(session_id, error); 398 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 399 listener->OnRecognitionError(session_id, error); 400 } 401 402 void SpeechRecognitionManagerImpl::OnAudioLevelsChange( 403 int session_id, float volume, float noise_volume) { 404 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 405 if (!SessionExists(session_id)) 406 return; 407 408 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 409 delegate_listener->OnAudioLevelsChange(session_id, volume, noise_volume); 410 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 411 listener->OnAudioLevelsChange(session_id, volume, noise_volume); 412 } 413 414 void SpeechRecognitionManagerImpl::OnRecognitionEnd(int session_id) { 415 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 416 if (!SessionExists(session_id)) 417 return; 418 419 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) 420 delegate_listener->OnRecognitionEnd(session_id); 421 if (SpeechRecognitionEventListener* listener = GetListener(session_id)) 422 listener->OnRecognitionEnd(session_id); 423 base::MessageLoop::current()->PostTask( 424 FROM_HERE, 425 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent, 426 weak_factory_.GetWeakPtr(), 427 session_id, 428 EVENT_RECOGNITION_ENDED)); 429 } 430 431 int SpeechRecognitionManagerImpl::GetSession( 432 int render_process_id, int render_view_id, int request_id) const { 433 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 434 SessionsTable::const_iterator iter; 435 for(iter = sessions_.begin(); iter != sessions_.end(); ++iter) { 436 const int session_id = iter->first; 437 const SpeechRecognitionSessionContext& context = iter->second->context; 438 if (context.render_process_id == render_process_id && 439 context.render_view_id == render_view_id && 440 context.request_id == request_id) { 441 return session_id; 442 } 443 } 444 return kSessionIDInvalid; 445 } 446 447 SpeechRecognitionSessionContext 448 SpeechRecognitionManagerImpl::GetSessionContext(int session_id) const { 449 return GetSession(session_id)->context; 450 } 451 452 void SpeechRecognitionManagerImpl::AbortAllSessionsForRenderProcess( 453 int render_process_id) { 454 // This method gracefully destroys sessions for the listener. However, since 455 // the listener itself is likely to be destroyed after this call, we avoid 456 // dispatching further events to it, marking the |listener_is_active| flag. 457 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 458 for (SessionsTable::iterator it = sessions_.begin(); it != sessions_.end(); 459 ++it) { 460 Session* session = it->second; 461 if (session->context.render_process_id == render_process_id) { 462 AbortSession(session->id); 463 session->listener_is_active = false; 464 } 465 } 466 } 467 468 void SpeechRecognitionManagerImpl::AbortAllSessionsForRenderView( 469 int render_process_id, 470 int render_view_id) { 471 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 472 for (SessionsTable::iterator it = sessions_.begin(); it != sessions_.end(); 473 ++it) { 474 Session* session = it->second; 475 if (session->context.render_process_id == render_process_id && 476 session->context.render_view_id == render_view_id) { 477 AbortSession(session->id); 478 } 479 } 480 } 481 482 // ----------------------- Core FSM implementation --------------------------- 483 void SpeechRecognitionManagerImpl::DispatchEvent(int session_id, 484 FSMEvent event) { 485 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 486 487 // There are some corner cases in which the session might be deleted (due to 488 // an EndRecognition event) between a request (e.g. Abort) and its dispatch. 489 if (!SessionExists(session_id)) 490 return; 491 492 Session* session = GetSession(session_id); 493 FSMState session_state = GetSessionState(session_id); 494 DCHECK_LE(session_state, SESSION_STATE_MAX_VALUE); 495 DCHECK_LE(event, EVENT_MAX_VALUE); 496 497 // Event dispatching must be sequential, otherwise it will break all the rules 498 // and the assumptions of the finite state automata model. 499 DCHECK(!is_dispatching_event_); 500 is_dispatching_event_ = true; 501 ExecuteTransitionAndGetNextState(session, session_state, event); 502 is_dispatching_event_ = false; 503 } 504 505 // This FSM handles the evolution of each session, from the viewpoint of the 506 // interaction with the user (that may be either the browser end-user which 507 // interacts with UI bubbles, or JS developer intracting with JS methods). 508 // All the events received by the SpeechRecognizer instances (one for each 509 // session) are always routed to the SpeechRecognitionEventListener(s) 510 // regardless the choices taken in this FSM. 511 void SpeechRecognitionManagerImpl::ExecuteTransitionAndGetNextState( 512 Session* session, FSMState session_state, FSMEvent event) { 513 // Note: since we're not tracking the state of the recognizer object, rather 514 // we're directly retrieving it (through GetSessionState), we see its events 515 // (that are AUDIO_ENDED and RECOGNITION_ENDED) after its state evolution 516 // (e.g., when we receive the AUDIO_ENDED event, the recognizer has just 517 // completed the transition from CAPTURING_AUDIO to WAITING_FOR_RESULT, thus 518 // we perceive the AUDIO_ENDED event in WAITING_FOR_RESULT). 519 // This makes the code below a bit tricky but avoids a lot of code for 520 // tracking and reconstructing asynchronously the state of the recognizer. 521 switch (session_state) { 522 case SESSION_STATE_IDLE: 523 switch (event) { 524 case EVENT_START: 525 return SessionStart(*session); 526 case EVENT_ABORT: 527 return SessionAbort(*session); 528 case EVENT_RECOGNITION_ENDED: 529 return SessionDelete(session); 530 case EVENT_STOP_CAPTURE: 531 return SessionStopAudioCapture(*session); 532 case EVENT_AUDIO_ENDED: 533 return; 534 } 535 break; 536 case SESSION_STATE_CAPTURING_AUDIO: 537 switch (event) { 538 case EVENT_STOP_CAPTURE: 539 return SessionStopAudioCapture(*session); 540 case EVENT_ABORT: 541 return SessionAbort(*session); 542 case EVENT_START: 543 return; 544 case EVENT_AUDIO_ENDED: 545 case EVENT_RECOGNITION_ENDED: 546 return NotFeasible(*session, event); 547 } 548 break; 549 case SESSION_STATE_WAITING_FOR_RESULT: 550 switch (event) { 551 case EVENT_ABORT: 552 return SessionAbort(*session); 553 case EVENT_AUDIO_ENDED: 554 return ResetCapturingSessionId(*session); 555 case EVENT_START: 556 case EVENT_STOP_CAPTURE: 557 return; 558 case EVENT_RECOGNITION_ENDED: 559 return NotFeasible(*session, event); 560 } 561 break; 562 } 563 return NotFeasible(*session, event); 564 } 565 566 SpeechRecognitionManagerImpl::FSMState 567 SpeechRecognitionManagerImpl::GetSessionState(int session_id) const { 568 Session* session = GetSession(session_id); 569 if (!session->recognizer.get() || !session->recognizer->IsActive()) 570 return SESSION_STATE_IDLE; 571 if (session->recognizer->IsCapturingAudio()) 572 return SESSION_STATE_CAPTURING_AUDIO; 573 return SESSION_STATE_WAITING_FOR_RESULT; 574 } 575 576 // ----------- Contract for all the FSM evolution functions below ------------- 577 // - Are guaranteed to be executed in the IO thread; 578 // - Are guaranteed to be not reentrant (themselves and each other); 579 580 void SpeechRecognitionManagerImpl::SessionStart(const Session& session) { 581 DCHECK_EQ(primary_session_id_, session.id); 582 const MediaStreamDevices& devices = session.context.devices; 583 std::string device_id; 584 if (devices.empty()) { 585 // From the ask_user=false path, use the default device. 586 // TODO(xians): Abort the session after we do not need to support this path 587 // anymore. 588 device_id = media::AudioManagerBase::kDefaultDeviceId; 589 } else { 590 // From the ask_user=true path, use the selected device. 591 DCHECK_EQ(1u, devices.size()); 592 DCHECK_EQ(MEDIA_DEVICE_AUDIO_CAPTURE, devices.front().type); 593 device_id = devices.front().id; 594 } 595 596 session.recognizer->StartRecognition(device_id); 597 } 598 599 void SpeechRecognitionManagerImpl::SessionAbort(const Session& session) { 600 if (primary_session_id_ == session.id) 601 primary_session_id_ = kSessionIDInvalid; 602 DCHECK(session.recognizer.get()); 603 session.recognizer->AbortRecognition(); 604 } 605 606 void SpeechRecognitionManagerImpl::SessionStopAudioCapture( 607 const Session& session) { 608 DCHECK(session.recognizer.get()); 609 session.recognizer->StopAudioCapture(); 610 } 611 612 void SpeechRecognitionManagerImpl::ResetCapturingSessionId( 613 const Session& session) { 614 DCHECK_EQ(primary_session_id_, session.id); 615 primary_session_id_ = kSessionIDInvalid; 616 } 617 618 void SpeechRecognitionManagerImpl::SessionDelete(Session* session) { 619 DCHECK(session->recognizer.get() == NULL || !session->recognizer->IsActive()); 620 if (primary_session_id_ == session->id) 621 primary_session_id_ = kSessionIDInvalid; 622 if (!session->context.label.empty()) 623 media_stream_manager_->CancelRequest(session->context.label); 624 sessions_.erase(session->id); 625 delete session; 626 } 627 628 void SpeechRecognitionManagerImpl::NotFeasible(const Session& session, 629 FSMEvent event) { 630 NOTREACHED() << "Unfeasible event " << event 631 << " in state " << GetSessionState(session.id) 632 << " for session " << session.id; 633 } 634 635 int SpeechRecognitionManagerImpl::GetNextSessionID() { 636 ++last_session_id_; 637 // Deal with wrapping of last_session_id_. (How civilized). 638 if (last_session_id_ <= 0) 639 last_session_id_ = 1; 640 return last_session_id_; 641 } 642 643 bool SpeechRecognitionManagerImpl::SessionExists(int session_id) const { 644 return sessions_.find(session_id) != sessions_.end(); 645 } 646 647 SpeechRecognitionManagerImpl::Session* 648 SpeechRecognitionManagerImpl::GetSession(int session_id) const { 649 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 650 SessionsTable::const_iterator iter = sessions_.find(session_id); 651 DCHECK(iter != sessions_.end()); 652 return iter->second; 653 } 654 655 SpeechRecognitionEventListener* SpeechRecognitionManagerImpl::GetListener( 656 int session_id) const { 657 Session* session = GetSession(session_id); 658 if (session->listener_is_active && session->config.event_listener) 659 return session->config.event_listener.get(); 660 return NULL; 661 } 662 663 SpeechRecognitionEventListener* 664 SpeechRecognitionManagerImpl::GetDelegateListener() const { 665 return delegate_.get() ? delegate_->GetEventListener() : NULL; 666 } 667 668 const SpeechRecognitionSessionConfig& 669 SpeechRecognitionManagerImpl::GetSessionConfig(int session_id) const { 670 return GetSession(session_id)->config; 671 } 672 673 bool SpeechRecognitionManagerImpl::HasAudioInputDevices() { 674 return audio_manager_->HasAudioInputDevices(); 675 } 676 677 base::string16 SpeechRecognitionManagerImpl::GetAudioInputDeviceModel() { 678 return audio_manager_->GetAudioInputDeviceModel(); 679 } 680 681 void SpeechRecognitionManagerImpl::ShowAudioInputSettings() { 682 // Since AudioManager::ShowAudioInputSettings can potentially launch external 683 // processes, do that in the FILE thread to not block the calling threads. 684 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 685 base::Bind(&ShowAudioInputSettingsOnFileThread, 686 audio_manager_)); 687 } 688 689 SpeechRecognitionManagerImpl::Session::Session() 690 : id(kSessionIDInvalid), 691 abort_requested(false), 692 listener_is_active(true) { 693 } 694 695 SpeechRecognitionManagerImpl::Session::~Session() { 696 } 697 698 } // namespace content 699