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