speech_input_manager.cc revision ac1e49eb6695f711d72215fcdf9388548942a00d
1bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen// Copyright (c) 2010 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 5bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "chrome/browser/speech/speech_input_manager.h" 6bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 7bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "app/l10n_util.h" 8ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch#include "base/command_line.h" 9201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "base/lock.h" 10bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "base/ref_counted.h" 11bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "base/singleton.h" 12ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch#include "base/thread_restrictions.h" 13201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "base/utf_string_conversions.h" 14201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "chrome/browser/browser_process.h" 15731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "chrome/browser/browser_thread.h" 16ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch#include "chrome/common/chrome_switches.h" 17ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch#include "chrome/browser/platform_util.h" 18201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "chrome/browser/prefs/pref_service.h" 19bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "chrome/browser/speech/speech_input_bubble_controller.h" 20bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "chrome/browser/speech/speech_recognizer.h" 21bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "chrome/browser/tab_contents/infobar_delegate.h" 22bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "chrome/browser/tab_contents/tab_contents.h" 23bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "chrome/browser/tab_contents/tab_util.h" 24201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "chrome/common/pref_names.h" 25bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "grit/generated_resources.h" 26bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "media/audio/audio_manager.h" 27bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include <map> 28bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 29201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#if defined(OS_WIN) 30201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "chrome/installer/util/wmi.h" 31201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#endif 32201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 33201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochnamespace { 34201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 35201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch// Asynchronously fetches the PC and audio hardware/driver info on windows if 36201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch// the user has opted into UMA. This information is sent with speech input 37201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch// requests to the server for identifying and improving quality issues with 38201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch// specific device configurations. 39201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochclass HardwareInfo : public base::RefCountedThreadSafe<HardwareInfo> { 40201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch public: 41201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch HardwareInfo() {} 42201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 43201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#if defined(OS_WIN) 44201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch void Refresh() { 45201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 46201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // UMA opt-in can be checked only from the UI thread, so switch to that. 47201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 48201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch NewRunnableMethod(this, &HardwareInfo::CheckUMAAndGetHardwareInfo)); 49201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 50201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 51201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch void CheckUMAAndGetHardwareInfo() { 52201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 53201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (g_browser_process->local_state()->GetBoolean( 54201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch prefs::kMetricsReportingEnabled)) { 55201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // Access potentially slow OS calls from the FILE thread. 56201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 57201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch NewRunnableMethod(this, &HardwareInfo::GetHardwareInfo)); 58201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 59201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 60201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 61201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch void GetHardwareInfo() { 62201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 63201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch AutoLock lock(lock_); 64201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch value_ = UTF16ToUTF8( 65201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch installer::WMIComputerSystem::GetModel() + L"|" + 66201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch AudioManager::GetAudioManager()->GetAudioInputDeviceModel()); 67201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 68201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 69201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch std::string value() { 70201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch AutoLock lock(lock_); 71201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch return value_; 72201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 73201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 74201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch private: 75201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch Lock lock_; 76201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch std::string value_; 77201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 78201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#else // defined(OS_WIN) 79201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch void Refresh() {} 80201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch std::string value() { return std::string(); } 81201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#endif // defined(OS_WIN) 82201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 83201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch DISALLOW_COPY_AND_ASSIGN(HardwareInfo); 84201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch}; 85201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 86201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch} 87201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 88bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsennamespace speech_input { 89bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 90bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenclass SpeechInputManagerImpl : public SpeechInputManager, 91bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen public SpeechInputBubbleControllerDelegate, 92bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen public SpeechRecognizerDelegate { 93bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen public: 94bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // SpeechInputManager methods. 95bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void StartRecognition(SpeechInputManagerDelegate* delegate, 96bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int caller_id, 97bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int render_process_id, 98bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int render_view_id, 99513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const gfx::Rect& element_rect, 100513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const std::string& language, 101513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const std::string& grammar); 102bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void CancelRecognition(int caller_id); 103bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void StopRecording(int caller_id); 104bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 105bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // SpeechRecognizer::Delegate methods. 106513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch virtual void SetRecognitionResult(int caller_id, 107513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch bool error, 108513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const SpeechInputResultArray& result); 109bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void DidCompleteRecording(int caller_id); 110bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void DidCompleteRecognition(int caller_id); 111bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void OnRecognizerError(int caller_id, 112bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechRecognizer::ErrorCode error); 113bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void DidCompleteEnvironmentEstimation(int caller_id); 114bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void SetInputVolume(int caller_id, float volume); 115bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 116bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // SpeechInputBubbleController::Delegate methods. 117bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void InfoBubbleButtonClicked(int caller_id, 118bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechInputBubble::Button button); 119bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual void InfoBubbleFocusChanged(int caller_id); 120bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 121bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen private: 122bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen struct SpeechInputRequest { 123bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechInputManagerDelegate* delegate; 124bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen scoped_refptr<SpeechRecognizer> recognizer; 125bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bool is_active; // Set to true when recording or recognition is going on. 126bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen }; 127bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 128bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // Private constructor to enforce singleton. 129bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen friend struct DefaultSingletonTraits<SpeechInputManagerImpl>; 130bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechInputManagerImpl(); 131bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen virtual ~SpeechInputManagerImpl(); 132bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 133bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bool HasPendingRequest(int caller_id) const; 134bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechInputManagerDelegate* GetDelegate(int caller_id) const; 135bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 136bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen void CancelRecognitionAndInformDelegate(int caller_id); 137bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 138bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // Starts/restarts recognition for an existing request. 139bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen void StartRecognitionForRequest(int caller_id); 140bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 141bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen typedef std::map<int, SpeechInputRequest> SpeechRecognizerMap; 142bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechRecognizerMap requests_; 143bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int recording_caller_id_; 144bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen scoped_refptr<SpeechInputBubbleController> bubble_controller_; 145201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch scoped_refptr<HardwareInfo> hardware_info_; 146bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}; 147bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 148bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian MonsenSpeechInputManager* SpeechInputManager::Get() { 149bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen return Singleton<SpeechInputManagerImpl>::get(); 150bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 151bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 152ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdochbool SpeechInputManager::IsFeatureEnabled() { 153ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch bool enabled = true; 154ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 155ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch 156ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch if (command_line.HasSwitch(switches::kDisableSpeechInput)) { 157ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch enabled = false; 158ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch#if defined(GOOGLE_CHROME_BUILD) 159ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch } else if (!command_line.HasSwitch(switches::kEnableSpeechInput)) { 160ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch // We need to evaluate whether IO is OK here. http://crbug.com/63335. 161ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch base::ThreadRestrictions::ScopedAllowIO allow_io; 162ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch // Official Chrome builds have speech input enabled by default only in the 163ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch // dev channel. 164ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch std::string channel = platform_util::GetVersionStringModifier(); 165ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch enabled = (channel == "dev"); 166ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch#endif 167ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch } 168ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch 169ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch return enabled; 170ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch} 171ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch 172bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian MonsenSpeechInputManagerImpl::SpeechInputManagerImpl() 173bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen : recording_caller_id_(0), 174bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bubble_controller_(new SpeechInputBubbleController( 175bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen ALLOW_THIS_IN_INITIALIZER_LIST(this))) { 176bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 177bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 178bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian MonsenSpeechInputManagerImpl::~SpeechInputManagerImpl() { 179bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen while (requests_.begin() != requests_.end()) 180bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen CancelRecognition(requests_.begin()->first); 181bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 182bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 183bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenbool SpeechInputManagerImpl::HasPendingRequest(int caller_id) const { 184bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen return requests_.find(caller_id) != requests_.end(); 185bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 186bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 187bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian MonsenSpeechInputManagerDelegate* SpeechInputManagerImpl::GetDelegate( 188bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int caller_id) const { 189bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen return requests_.find(caller_id)->second.delegate; 190bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 191bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 192bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::StartRecognition( 193bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechInputManagerDelegate* delegate, 194bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int caller_id, 195bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int render_process_id, 196bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int render_view_id, 197513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const gfx::Rect& element_rect, 198513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const std::string& language, 199513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const std::string& grammar) { 200bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(!HasPendingRequest(caller_id)); 201bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 202bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bubble_controller_->CreateBubble(caller_id, render_process_id, render_view_id, 203bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen element_rect); 204bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 205201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (!hardware_info_.get()) { 206201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch hardware_info_ = new HardwareInfo(); 207201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // Since hardware info is optional with speech input requests, we start an 208201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // asynchronous fetch here and move on with recording audio. This first 209201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // speech input request would send an empty string for hardware info and 210201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // subsequent requests may have the hardware info available if the fetch 211201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // completed before them. This way we don't end up stalling the user with 212201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // a long wait and disk seeks when they click on a UI element and start 213201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // speaking. 214201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch hardware_info_->Refresh(); 215201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 216201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 217bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechInputRequest* request = &requests_[caller_id]; 218bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen request->delegate = delegate; 219513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch request->recognizer = new SpeechRecognizer(this, caller_id, language, 220201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch grammar, hardware_info_->value()); 221bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen request->is_active = false; 222bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 223bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen StartRecognitionForRequest(caller_id); 224bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 225bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 226bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::StartRecognitionForRequest(int caller_id) { 227bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(HasPendingRequest(caller_id)); 228bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 229bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // If we are currently recording audio for another caller, abort that cleanly. 230bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (recording_caller_id_) 231bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen CancelRecognitionAndInformDelegate(recording_caller_id_); 232bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 233bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (!AudioManager::GetAudioManager()->HasAudioInputDevices()) { 234bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bubble_controller_->SetBubbleMessage( 235bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen caller_id, l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_NO_MIC)); 236bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen } else { 237bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen recording_caller_id_ = caller_id; 238bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen requests_[caller_id].is_active = true; 239bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen requests_[caller_id].recognizer->StartRecording(); 240bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen } 241bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 242bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 243bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::CancelRecognition(int caller_id) { 244bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(HasPendingRequest(caller_id)); 245bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (requests_[caller_id].is_active) 246bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen requests_[caller_id].recognizer->CancelRecognition(); 247bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen requests_.erase(caller_id); 248bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (recording_caller_id_ == caller_id) 249bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen recording_caller_id_ = 0; 250bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bubble_controller_->CloseBubble(caller_id); 251bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 252bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 253bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::StopRecording(int caller_id) { 254bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(HasPendingRequest(caller_id)); 255bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen requests_[caller_id].recognizer->StopRecording(); 256bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 257bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 258513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid SpeechInputManagerImpl::SetRecognitionResult( 259513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch int caller_id, bool error, const SpeechInputResultArray& result) { 260bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(HasPendingRequest(caller_id)); 261513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch GetDelegate(caller_id)->SetRecognitionResult(caller_id, result); 262bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 263bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 264bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::DidCompleteRecording(int caller_id) { 265bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(recording_caller_id_ == caller_id); 266bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(HasPendingRequest(caller_id)); 267bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen recording_caller_id_ = 0; 268bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen GetDelegate(caller_id)->DidCompleteRecording(caller_id); 269bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bubble_controller_->SetBubbleRecognizingMode(caller_id); 270bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 271bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 272bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::DidCompleteRecognition(int caller_id) { 273bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen GetDelegate(caller_id)->DidCompleteRecognition(caller_id); 274bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen requests_.erase(caller_id); 275bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bubble_controller_->CloseBubble(caller_id); 276bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 277bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 278bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::OnRecognizerError( 279bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int caller_id, SpeechRecognizer::ErrorCode error) { 280bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (caller_id == recording_caller_id_) 281bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen recording_caller_id_ = 0; 282bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 283bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen requests_[caller_id].is_active = false; 284bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 285bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int message_id; 286bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen switch (error) { 287bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen case SpeechRecognizer::RECOGNIZER_ERROR_CAPTURE: 288bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen message_id = IDS_SPEECH_INPUT_ERROR; 289bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen break; 290bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen case SpeechRecognizer::RECOGNIZER_ERROR_NO_SPEECH: 291bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen message_id = IDS_SPEECH_INPUT_NO_SPEECH; 292bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen break; 293bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen case SpeechRecognizer::RECOGNIZER_ERROR_NO_RESULTS: 294bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen message_id = IDS_SPEECH_INPUT_NO_RESULTS; 295bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen break; 296bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen default: 297bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen NOTREACHED() << "unknown error " << error; 298bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen return; 299bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen } 300bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bubble_controller_->SetBubbleMessage(caller_id, 301bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen l10n_util::GetStringUTF16(message_id)); 302bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 303bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 304bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::DidCompleteEnvironmentEstimation(int caller_id) { 305bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(HasPendingRequest(caller_id)); 306bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(recording_caller_id_ == caller_id); 307bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 308bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // Speech recognizer has gathered enough background audio so we can ask the 309bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // user to start speaking. 310bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bubble_controller_->SetBubbleRecordingMode(caller_id); 311bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 312bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 313bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::SetInputVolume(int caller_id, float volume) { 314bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK(HasPendingRequest(caller_id)); 315bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen DCHECK_EQ(recording_caller_id_, caller_id); 316bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 317bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen bubble_controller_->SetBubbleInputVolume(caller_id, volume); 318bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 319bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 320bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::CancelRecognitionAndInformDelegate(int caller_id) { 321bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SpeechInputManagerDelegate* cur_delegate = GetDelegate(caller_id); 322bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen CancelRecognition(caller_id); 323bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen cur_delegate->DidCompleteRecording(caller_id); 324bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen cur_delegate->DidCompleteRecognition(caller_id); 325bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 326bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 327bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::InfoBubbleButtonClicked( 328bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int caller_id, SpeechInputBubble::Button button) { 329731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 330bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // Ignore if the caller id was not in our active recognizers list because the 331bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // user might have clicked more than once, or recognition could have been 332bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // cancelled due to other reasons before the user click was processed. 333bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (!HasPendingRequest(caller_id)) 334bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen return; 335bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 336bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (button == SpeechInputBubble::BUTTON_CANCEL) { 337bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen CancelRecognitionAndInformDelegate(caller_id); 338bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen } else if (button == SpeechInputBubble::BUTTON_TRY_AGAIN) { 339bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen StartRecognitionForRequest(caller_id); 340bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen } 341bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 342bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 343bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputManagerImpl::InfoBubbleFocusChanged(int caller_id) { 344731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 345bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // Ignore if the caller id was not in our active recognizers list because the 346bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // user might have clicked more than once, or recognition could have been 347bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // ended due to other reasons before the user click was processed. 348bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (HasPendingRequest(caller_id)) { 349bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // If this is an ongoing recording or if we were displaying an error message 350bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // to the user, abort it since user has switched focus. Otherwise 351bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // recognition has started and keep that going so user can start speaking to 352bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // another element while this gets the results in parallel. 353bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (recording_caller_id_ == caller_id || !requests_[caller_id].is_active) { 354bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen CancelRecognitionAndInformDelegate(caller_id); 355bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen } 356bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen } 357bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 358bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 359bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} // namespace speech_input 360