172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen// Use of this source code is governed by a BSD-style license that can be 3bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen// found in the LICENSE file. 4bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 5dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/speech/speech_input_manager.h" 6bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include <map> 821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include <string> 921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen 103f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/lazy_instance.h" 11ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/ref_counted.h" 1272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/synchronization/lock.h" 133f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/threading/thread_restrictions.h" 14201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "base/utf_string_conversions.h" 1572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/browser_process.h" 163f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "chrome/browser/platform_util.h" 17201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "chrome/browser/prefs/pref_service.h" 18bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "chrome/browser/speech/speech_input_bubble_controller.h" 19bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "chrome/browser/tab_contents/tab_util.h" 203f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "chrome/common/chrome_switches.h" 21201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "chrome/common/pref_names.h" 22dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/browser_thread.h" 23dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/speech/speech_recognizer.h" 24bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "grit/generated_resources.h" 25bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "media/audio/audio_manager.h" 2672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/l10n/l10n_util.h" 27bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 28201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#if defined(OS_WIN) 29201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "chrome/installer/util/wmi.h" 30201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#endif 31201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 3272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsennamespace speech_input { 3372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 34201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochnamespace { 35201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 3672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Asynchronously fetches the PC and audio hardware/driver info if 37201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch// the user has opted into UMA. This information is sent with speech input 38201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch// requests to the server for identifying and improving quality issues with 39201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch// specific device configurations. 4072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenclass OptionalRequestInfo 4172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen : public base::RefCountedThreadSafe<OptionalRequestInfo> { 42201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch public: 4372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen OptionalRequestInfo() : can_report_metrics_(false) {} 44201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 45201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch void Refresh() { 46201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 47201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // UMA opt-in can be checked only from the UI thread, so switch to that. 48201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 4972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen NewRunnableMethod(this, 5072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen &OptionalRequestInfo::CheckUMAAndGetHardwareInfo)); 51201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 52201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 53201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch void CheckUMAAndGetHardwareInfo() { 54201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 55201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (g_browser_process->local_state()->GetBoolean( 56201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch prefs::kMetricsReportingEnabled)) { 57201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // Access potentially slow OS calls from the FILE thread. 58201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 5972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen NewRunnableMethod(this, &OptionalRequestInfo::GetHardwareInfo)); 60201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 61201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 62201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 63201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch void GetHardwareInfo() { 64201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 6572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(lock_); 6672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen can_report_metrics_ = true; 6772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#if defined(OS_WIN) 68201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch value_ = UTF16ToUTF8( 69201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch installer::WMIComputerSystem::GetModel() + L"|" + 70201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch AudioManager::GetAudioManager()->GetAudioInputDeviceModel()); 7172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#else // defined(OS_WIN) 7272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen value_ = UTF16ToUTF8( 7372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen AudioManager::GetAudioManager()->GetAudioInputDeviceModel()); 7472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#endif // defined(OS_WIN) 75201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 76201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 77201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch std::string value() { 7872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(lock_); 79201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch return value_; 80201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 81201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 8272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen bool can_report_metrics() { 8372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(lock_); 8472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return can_report_metrics_; 8572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 8672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 87201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch private: 8872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::Lock lock_; 89201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch std::string value_; 9072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen bool can_report_metrics_; 91201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 9272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen DISALLOW_COPY_AND_ASSIGN(OptionalRequestInfo); 93201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch}; 94201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 95bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenclass SpeechInputManagerImpl : public SpeechInputManager, 96bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen public SpeechInputBubbleControllerDelegate, 97bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen public SpeechRecognizerDelegate { 98bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen public: 99bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // SpeechInputManager methods. 100bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void StartRecognition(SpeechInputManagerDelegate* delegate, 101bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int caller_id, 102bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int render_process_id, 103bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int render_view_id, 104513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const gfx::Rect& element_rect, 105513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const std::string& language, 10672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const std::string& grammar, 10772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const std::string& origin_url); 108bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void CancelRecognition(int caller_id); 109bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void StopRecording(int caller_id); 11072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen virtual void CancelAllRequestsWithDelegate( 11172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen SpeechInputManagerDelegate* delegate); 112bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 113bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // SpeechRecognizer::Delegate methods. 114ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen virtual void DidStartReceivingAudio(int caller_id); 115513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch virtual void SetRecognitionResult(int caller_id, 116513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch bool error, 117513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const SpeechInputResultArray& result); 118bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void DidCompleteRecording(int caller_id); 119bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void DidCompleteRecognition(int caller_id); 120bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void OnRecognizerError(int caller_id, 121bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechRecognizer::ErrorCode error); 122bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void DidCompleteEnvironmentEstimation(int caller_id); 123dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen virtual void SetInputVolume(int caller_id, float volume, float noise_volume); 124bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 125bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // SpeechInputBubbleController::Delegate methods. 126bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void InfoBubbleButtonClicked(int caller_id, 127bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechInputBubble::Button button); 128bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void InfoBubbleFocusChanged(int caller_id); 129bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 130bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen private: 131bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen struct SpeechInputRequest { 132bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechInputManagerDelegate* delegate; 133bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen scoped_refptr<SpeechRecognizer> recognizer; 134bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bool is_active; // Set to true when recording or recognition is going on. 135bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen }; 136bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 137bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // Private constructor to enforce singleton. 13821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen friend struct base::DefaultLazyInstanceTraits<SpeechInputManagerImpl>; 139bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechInputManagerImpl(); 140bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual ~SpeechInputManagerImpl(); 141bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 142bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bool HasPendingRequest(int caller_id) const; 143bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechInputManagerDelegate* GetDelegate(int caller_id) const; 144bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 145bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen void CancelRecognitionAndInformDelegate(int caller_id); 146bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 147bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // Starts/restarts recognition for an existing request. 148bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen void StartRecognitionForRequest(int caller_id); 149bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 150bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen typedef std::map<int, SpeechInputRequest> SpeechRecognizerMap; 151bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechRecognizerMap requests_; 152bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int recording_caller_id_; 153bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen scoped_refptr<SpeechInputBubbleController> bubble_controller_; 15472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen scoped_refptr<OptionalRequestInfo> optional_request_info_; 155bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}; 156bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 15772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbase::LazyInstance<SpeechInputManagerImpl> g_speech_input_manager_impl( 15821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen base::LINKER_INITIALIZED); 159bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 16072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} // namespace 16172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 16221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian MonsenSpeechInputManager* SpeechInputManager::Get() { 16321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen return g_speech_input_manager_impl.Pointer(); 164ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch} 165ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch 166dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid SpeechInputManager::ShowAudioInputSettings() { 167dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Since AudioManager::ShowAudioInputSettings can potentially launch external 168dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // processes, do that in the FILE thread to not block the calling threads. 169dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { 170dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen BrowserThread::PostTask( 171dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen BrowserThread::FILE, FROM_HERE, 172dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen NewRunnableFunction(&SpeechInputManager::ShowAudioInputSettings)); 173dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return; 1743f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen } 1753f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen 176dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DCHECK(AudioManager::GetAudioManager()->CanShowAudioInputSettings()); 177dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (AudioManager::GetAudioManager()->CanShowAudioInputSettings()) 178dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen AudioManager::GetAudioManager()->ShowAudioInputSettings(); 1793f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen} 1803f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen 181bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian MonsenSpeechInputManagerImpl::SpeechInputManagerImpl() 182bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen : recording_caller_id_(0), 183bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bubble_controller_(new SpeechInputBubbleController( 184bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen ALLOW_THIS_IN_INITIALIZER_LIST(this))) { 185bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 186bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 187bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian MonsenSpeechInputManagerImpl::~SpeechInputManagerImpl() { 188bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen while (requests_.begin() != requests_.end()) 189bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen CancelRecognition(requests_.begin()->first); 190bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 191bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 192bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenbool SpeechInputManagerImpl::HasPendingRequest(int caller_id) const { 193bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen return requests_.find(caller_id) != requests_.end(); 194bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 195bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 196bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian MonsenSpeechInputManagerDelegate* SpeechInputManagerImpl::GetDelegate( 197bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int caller_id) const { 198bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen return requests_.find(caller_id)->second.delegate; 199bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 200bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 201bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::StartRecognition( 202bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechInputManagerDelegate* delegate, 203bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int caller_id, 204bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int render_process_id, 205bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int render_view_id, 206513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const gfx::Rect& element_rect, 207513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const std::string& language, 20872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const std::string& grammar, 20972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const std::string& origin_url) { 210bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(!HasPendingRequest(caller_id)); 211bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 212bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bubble_controller_->CreateBubble(caller_id, render_process_id, render_view_id, 213bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen element_rect); 214bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 21572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!optional_request_info_.get()) { 21672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen optional_request_info_ = new OptionalRequestInfo(); 217201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // Since hardware info is optional with speech input requests, we start an 218201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // asynchronous fetch here and move on with recording audio. This first 219201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // speech input request would send an empty string for hardware info and 220201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // subsequent requests may have the hardware info available if the fetch 221201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // completed before them. This way we don't end up stalling the user with 222201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // a long wait and disk seeks when they click on a UI element and start 223201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // speaking. 22472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen optional_request_info_->Refresh(); 225201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 226201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 227bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechInputRequest* request = &requests_[caller_id]; 228bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen request->delegate = delegate; 22972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen request->recognizer = new SpeechRecognizer( 23072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen this, caller_id, language, grammar, optional_request_info_->value(), 23172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen optional_request_info_->can_report_metrics() ? origin_url : ""); 232bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen request->is_active = false; 233bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 234bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen StartRecognitionForRequest(caller_id); 235bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 236bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 237bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::StartRecognitionForRequest(int caller_id) { 238bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(HasPendingRequest(caller_id)); 239bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 240bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // If we are currently recording audio for another caller, abort that cleanly. 241bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (recording_caller_id_) 242bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen CancelRecognitionAndInformDelegate(recording_caller_id_); 243bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 244bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (!AudioManager::GetAudioManager()->HasAudioInputDevices()) { 245bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bubble_controller_->SetBubbleMessage( 246bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen caller_id, l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_NO_MIC)); 247bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen } else { 248bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen recording_caller_id_ = caller_id; 249bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen requests_[caller_id].is_active = true; 250bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen requests_[caller_id].recognizer->StartRecording(); 251ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen bubble_controller_->SetBubbleWarmUpMode(caller_id); 252bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen } 253bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 254bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 255bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::CancelRecognition(int caller_id) { 256bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(HasPendingRequest(caller_id)); 257bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (requests_[caller_id].is_active) 258bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen requests_[caller_id].recognizer->CancelRecognition(); 259bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen requests_.erase(caller_id); 260bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (recording_caller_id_ == caller_id) 261bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen recording_caller_id_ = 0; 262bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bubble_controller_->CloseBubble(caller_id); 263bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 264bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 26572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid SpeechInputManagerImpl::CancelAllRequestsWithDelegate( 26672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen SpeechInputManagerDelegate* delegate) { 26772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen SpeechRecognizerMap::iterator it = requests_.begin(); 26872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen while (it != requests_.end()) { 26972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (it->second.delegate == delegate) { 27072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen CancelRecognition(it->first); 27172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // This map will have very few elements so it is simpler to restart. 27272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen it = requests_.begin(); 27372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } else { 27472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ++it; 27572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 27672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 27772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 27872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 279bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::StopRecording(int caller_id) { 280bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(HasPendingRequest(caller_id)); 281bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen requests_[caller_id].recognizer->StopRecording(); 282bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 283bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 284513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid SpeechInputManagerImpl::SetRecognitionResult( 285513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch int caller_id, bool error, const SpeechInputResultArray& result) { 286bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(HasPendingRequest(caller_id)); 287513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch GetDelegate(caller_id)->SetRecognitionResult(caller_id, result); 288bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 289bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 290bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::DidCompleteRecording(int caller_id) { 291bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(recording_caller_id_ == caller_id); 292bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(HasPendingRequest(caller_id)); 293bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen recording_caller_id_ = 0; 294bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen GetDelegate(caller_id)->DidCompleteRecording(caller_id); 295bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bubble_controller_->SetBubbleRecognizingMode(caller_id); 296bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 297bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 298bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::DidCompleteRecognition(int caller_id) { 299bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen GetDelegate(caller_id)->DidCompleteRecognition(caller_id); 300bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen requests_.erase(caller_id); 301bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bubble_controller_->CloseBubble(caller_id); 302bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 303bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 304bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::OnRecognizerError( 305bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int caller_id, SpeechRecognizer::ErrorCode error) { 306bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (caller_id == recording_caller_id_) 307bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen recording_caller_id_ = 0; 308bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 309bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen requests_[caller_id].is_active = false; 310bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 311dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen struct ErrorMessageMapEntry { 312dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen SpeechRecognizer::ErrorCode error; 313dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int message_id; 314dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen }; 315dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen ErrorMessageMapEntry error_message_map[] = { 316dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen { 317dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen SpeechRecognizer::RECOGNIZER_ERROR_CAPTURE, IDS_SPEECH_INPUT_MIC_ERROR 318dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen }, { 319dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen SpeechRecognizer::RECOGNIZER_ERROR_NO_SPEECH, IDS_SPEECH_INPUT_NO_SPEECH 320dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen }, { 321dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen SpeechRecognizer::RECOGNIZER_ERROR_NO_RESULTS, IDS_SPEECH_INPUT_NO_RESULTS 322dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen }, { 323dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen SpeechRecognizer::RECOGNIZER_ERROR_NETWORK, IDS_SPEECH_INPUT_NET_ERROR 324dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 325dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen }; 326dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen for (size_t i = 0; i < ARRAYSIZE_UNSAFE(error_message_map); ++i) { 327dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (error_message_map[i].error == error) { 328dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen bubble_controller_->SetBubbleMessage( 329dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen caller_id, 330dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen l10n_util::GetStringUTF16(error_message_map[i].message_id)); 331bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen return; 332dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 333bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen } 334dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 335dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen NOTREACHED() << "unknown error " << error; 336bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 337bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 338ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid SpeechInputManagerImpl::DidStartReceivingAudio(int caller_id) { 339bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(HasPendingRequest(caller_id)); 340bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(recording_caller_id_ == caller_id); 341bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bubble_controller_->SetBubbleRecordingMode(caller_id); 342bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 343bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 344ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid SpeechInputManagerImpl::DidCompleteEnvironmentEstimation(int caller_id) { 345ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DCHECK(HasPendingRequest(caller_id)); 346ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DCHECK(recording_caller_id_ == caller_id); 347ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 348ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 349dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid SpeechInputManagerImpl::SetInputVolume(int caller_id, float volume, 350dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen float noise_volume) { 351bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(HasPendingRequest(caller_id)); 352bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK_EQ(recording_caller_id_, caller_id); 353bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 354dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen bubble_controller_->SetBubbleInputVolume(caller_id, volume, noise_volume); 355bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 356bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 357bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::CancelRecognitionAndInformDelegate(int caller_id) { 358bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechInputManagerDelegate* cur_delegate = GetDelegate(caller_id); 359bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen CancelRecognition(caller_id); 360bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen cur_delegate->DidCompleteRecording(caller_id); 361bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen cur_delegate->DidCompleteRecognition(caller_id); 362bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 363bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 364bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::InfoBubbleButtonClicked( 365bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int caller_id, SpeechInputBubble::Button button) { 366731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 367bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // Ignore if the caller id was not in our active recognizers list because the 368bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // user might have clicked more than once, or recognition could have been 369bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // cancelled due to other reasons before the user click was processed. 370bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (!HasPendingRequest(caller_id)) 371bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen return; 372bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 373bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (button == SpeechInputBubble::BUTTON_CANCEL) { 374bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen CancelRecognitionAndInformDelegate(caller_id); 375bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen } else if (button == SpeechInputBubble::BUTTON_TRY_AGAIN) { 376bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen StartRecognitionForRequest(caller_id); 377bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen } 378bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 379bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 380bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::InfoBubbleFocusChanged(int caller_id) { 381731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 382bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // Ignore if the caller id was not in our active recognizers list because the 383bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // user might have clicked more than once, or recognition could have been 384bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // ended due to other reasons before the user click was processed. 385bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (HasPendingRequest(caller_id)) { 386bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // If this is an ongoing recording or if we were displaying an error message 387bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // to the user, abort it since user has switched focus. Otherwise 388bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // recognition has started and keep that going so user can start speaking to 389bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // another element while this gets the results in parallel. 390bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (recording_caller_id_ == caller_id || !requests_[caller_id].is_active) { 391bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen CancelRecognitionAndInformDelegate(caller_id); 392bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen } 393bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen } 394bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 395bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 396bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} // namespace speech_input 397