Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(46)

Side by Side Diff: chrome/browser/speech/speech_input_manager.cc

Issue 7729001: Get rid of link dependency from content to chrome. Make it get the SpeechInputManager through the... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/speech/speech_input_manager.h"
6
7 #include <map>
8 #include <string>
9
10 #include "base/lazy_instance.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/synchronization/lock.h"
13 #include "base/threading/thread_restrictions.h"
14 #include "base/utf_string_conversions.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/prefs/pref_service.h"
17 #include "chrome/browser/speech/speech_input_bubble_controller.h"
18 #include "chrome/browser/tab_contents/tab_util.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/pref_names.h"
21 #include "content/browser/browser_thread.h"
22 #include "content/browser/speech/speech_recognizer.h"
23 #include "grit/generated_resources.h"
24 #include "media/audio/audio_manager.h"
25 #include "ui/base/l10n/l10n_util.h"
26
27 #if defined(OS_WIN)
28 #include "chrome/installer/util/wmi.h"
29 #endif
30
31 namespace speech_input {
32
33 namespace {
34
35 // Asynchronously fetches the PC and audio hardware/driver info if
36 // the user has opted into UMA. This information is sent with speech input
37 // requests to the server for identifying and improving quality issues with
38 // specific device configurations.
39 class OptionalRequestInfo
40 : public base::RefCountedThreadSafe<OptionalRequestInfo> {
41 public:
42 OptionalRequestInfo() : can_report_metrics_(false) {}
43
44 void Refresh() {
45 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
46 // UMA opt-in can be checked only from the UI thread, so switch to that.
47 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
48 NewRunnableMethod(this,
49 &OptionalRequestInfo::CheckUMAAndGetHardwareInfo));
50 }
51
52 void CheckUMAAndGetHardwareInfo() {
53 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
54 if (g_browser_process->local_state()->GetBoolean(
55 prefs::kMetricsReportingEnabled)) {
56 // Access potentially slow OS calls from the FILE thread.
57 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
58 NewRunnableMethod(this, &OptionalRequestInfo::GetHardwareInfo));
59 }
60 }
61
62 void GetHardwareInfo() {
63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
64 base::AutoLock lock(lock_);
65 can_report_metrics_ = true;
66 #if defined(OS_WIN)
67 value_ = UTF16ToUTF8(
68 installer::WMIComputerSystem::GetModel() + L"|" +
69 AudioManager::GetAudioManager()->GetAudioInputDeviceModel());
70 #else // defined(OS_WIN)
71 value_ = UTF16ToUTF8(
72 AudioManager::GetAudioManager()->GetAudioInputDeviceModel());
73 #endif // defined(OS_WIN)
74 }
75
76 std::string value() {
77 base::AutoLock lock(lock_);
78 return value_;
79 }
80
81 bool can_report_metrics() {
82 base::AutoLock lock(lock_);
83 return can_report_metrics_;
84 }
85
86 private:
87 base::Lock lock_;
88 std::string value_;
89 bool can_report_metrics_;
90
91 DISALLOW_COPY_AND_ASSIGN(OptionalRequestInfo);
92 };
93
94 class SpeechInputManagerImpl : public SpeechInputManager,
95 public SpeechInputBubbleControllerDelegate,
96 public SpeechRecognizerDelegate {
97 public:
98 // SpeechInputManager methods.
99 virtual void StartRecognition(SpeechInputManagerDelegate* delegate,
100 int caller_id,
101 int render_process_id,
102 int render_view_id,
103 const gfx::Rect& element_rect,
104 const std::string& language,
105 const std::string& grammar,
106 const std::string& origin_url);
107 virtual void CancelRecognition(int caller_id);
108 virtual void StopRecording(int caller_id);
109 virtual void CancelAllRequestsWithDelegate(
110 SpeechInputManagerDelegate* delegate);
111
112 // SpeechRecognizer::Delegate methods.
113 virtual void DidStartReceivingAudio(int caller_id);
114 virtual void SetRecognitionResult(int caller_id,
115 bool error,
116 const SpeechInputResultArray& result);
117 virtual void DidCompleteRecording(int caller_id);
118 virtual void DidCompleteRecognition(int caller_id);
119 virtual void OnRecognizerError(int caller_id,
120 SpeechRecognizer::ErrorCode error);
121 virtual void DidCompleteEnvironmentEstimation(int caller_id);
122 virtual void SetInputVolume(int caller_id, float volume, float noise_volume);
123
124 // SpeechInputBubbleController::Delegate methods.
125 virtual void InfoBubbleButtonClicked(int caller_id,
126 SpeechInputBubble::Button button);
127 virtual void InfoBubbleFocusChanged(int caller_id);
128
129 private:
130 struct SpeechInputRequest {
131 SpeechInputManagerDelegate* delegate;
132 scoped_refptr<SpeechRecognizer> recognizer;
133 bool is_active; // Set to true when recording or recognition is going on.
134 };
135
136 // Private constructor to enforce singleton.
137 friend struct base::DefaultLazyInstanceTraits<SpeechInputManagerImpl>;
138 SpeechInputManagerImpl();
139 virtual ~SpeechInputManagerImpl();
140
141 bool HasPendingRequest(int caller_id) const;
142 SpeechInputManagerDelegate* GetDelegate(int caller_id) const;
143
144 void CancelRecognitionAndInformDelegate(int caller_id);
145
146 // Starts/restarts recognition for an existing request.
147 void StartRecognitionForRequest(int caller_id);
148
149 typedef std::map<int, SpeechInputRequest> SpeechRecognizerMap;
150 SpeechRecognizerMap requests_;
151 int recording_caller_id_;
152 scoped_refptr<SpeechInputBubbleController> bubble_controller_;
153 scoped_refptr<OptionalRequestInfo> optional_request_info_;
154 };
155
156 base::LazyInstance<SpeechInputManagerImpl> g_speech_input_manager_impl(
157 base::LINKER_INITIALIZED);
158
159 } // namespace
160
161 SpeechInputManager* SpeechInputManager::Get() {
162 return g_speech_input_manager_impl.Pointer();
163 }
164
165 void SpeechInputManager::ShowAudioInputSettings() {
166 // Since AudioManager::ShowAudioInputSettings can potentially launch external
167 // processes, do that in the FILE thread to not block the calling threads.
168 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
169 BrowserThread::PostTask(
170 BrowserThread::FILE, FROM_HERE,
171 NewRunnableFunction(&SpeechInputManager::ShowAudioInputSettings));
172 return;
173 }
174
175 DCHECK(AudioManager::GetAudioManager()->CanShowAudioInputSettings());
176 if (AudioManager::GetAudioManager()->CanShowAudioInputSettings())
177 AudioManager::GetAudioManager()->ShowAudioInputSettings();
178 }
179
180 SpeechInputManagerImpl::SpeechInputManagerImpl()
181 : recording_caller_id_(0),
182 bubble_controller_(new SpeechInputBubbleController(
183 ALLOW_THIS_IN_INITIALIZER_LIST(this))) {
184 }
185
186 SpeechInputManagerImpl::~SpeechInputManagerImpl() {
187 while (requests_.begin() != requests_.end())
188 CancelRecognition(requests_.begin()->first);
189 }
190
191 bool SpeechInputManagerImpl::HasPendingRequest(int caller_id) const {
192 return requests_.find(caller_id) != requests_.end();
193 }
194
195 SpeechInputManagerDelegate* SpeechInputManagerImpl::GetDelegate(
196 int caller_id) const {
197 return requests_.find(caller_id)->second.delegate;
198 }
199
200 void SpeechInputManagerImpl::StartRecognition(
201 SpeechInputManagerDelegate* delegate,
202 int caller_id,
203 int render_process_id,
204 int render_view_id,
205 const gfx::Rect& element_rect,
206 const std::string& language,
207 const std::string& grammar,
208 const std::string& origin_url) {
209 DCHECK(!HasPendingRequest(caller_id));
210
211 bubble_controller_->CreateBubble(caller_id, render_process_id, render_view_id,
212 element_rect);
213
214 if (!optional_request_info_.get()) {
215 optional_request_info_ = new OptionalRequestInfo();
216 // Since hardware info is optional with speech input requests, we start an
217 // asynchronous fetch here and move on with recording audio. This first
218 // speech input request would send an empty string for hardware info and
219 // subsequent requests may have the hardware info available if the fetch
220 // completed before them. This way we don't end up stalling the user with
221 // a long wait and disk seeks when they click on a UI element and start
222 // speaking.
223 optional_request_info_->Refresh();
224 }
225
226 SpeechInputRequest* request = &requests_[caller_id];
227 request->delegate = delegate;
228 request->recognizer = new SpeechRecognizer(
229 this, caller_id, language, grammar, censor_results(),
230 optional_request_info_->value(),
231 optional_request_info_->can_report_metrics() ? origin_url : "");
232 request->is_active = false;
233
234 StartRecognitionForRequest(caller_id);
235 }
236
237 void SpeechInputManagerImpl::StartRecognitionForRequest(int caller_id) {
238 DCHECK(HasPendingRequest(caller_id));
239
240 // If we are currently recording audio for another caller, abort that cleanly.
241 if (recording_caller_id_)
242 CancelRecognitionAndInformDelegate(recording_caller_id_);
243
244 if (!AudioManager::GetAudioManager()->HasAudioInputDevices()) {
245 bubble_controller_->SetBubbleMessage(
246 caller_id, l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_NO_MIC));
247 } else {
248 recording_caller_id_ = caller_id;
249 requests_[caller_id].is_active = true;
250 requests_[caller_id].recognizer->StartRecording();
251 bubble_controller_->SetBubbleWarmUpMode(caller_id);
252 }
253 }
254
255 void SpeechInputManagerImpl::CancelRecognition(int caller_id) {
256 DCHECK(HasPendingRequest(caller_id));
257 if (requests_[caller_id].is_active)
258 requests_[caller_id].recognizer->CancelRecognition();
259 requests_.erase(caller_id);
260 if (recording_caller_id_ == caller_id)
261 recording_caller_id_ = 0;
262 bubble_controller_->CloseBubble(caller_id);
263 }
264
265 void SpeechInputManagerImpl::CancelAllRequestsWithDelegate(
266 SpeechInputManagerDelegate* delegate) {
267 SpeechRecognizerMap::iterator it = requests_.begin();
268 while (it != requests_.end()) {
269 if (it->second.delegate == delegate) {
270 CancelRecognition(it->first);
271 // This map will have very few elements so it is simpler to restart.
272 it = requests_.begin();
273 } else {
274 ++it;
275 }
276 }
277 }
278
279 void SpeechInputManagerImpl::StopRecording(int caller_id) {
280 DCHECK(HasPendingRequest(caller_id));
281 requests_[caller_id].recognizer->StopRecording();
282 }
283
284 void SpeechInputManagerImpl::SetRecognitionResult(
285 int caller_id, bool error, const SpeechInputResultArray& result) {
286 DCHECK(HasPendingRequest(caller_id));
287 GetDelegate(caller_id)->SetRecognitionResult(caller_id, result);
288 }
289
290 void SpeechInputManagerImpl::DidCompleteRecording(int caller_id) {
291 DCHECK(recording_caller_id_ == caller_id);
292 DCHECK(HasPendingRequest(caller_id));
293 recording_caller_id_ = 0;
294 GetDelegate(caller_id)->DidCompleteRecording(caller_id);
295 bubble_controller_->SetBubbleRecognizingMode(caller_id);
296 }
297
298 void SpeechInputManagerImpl::DidCompleteRecognition(int caller_id) {
299 GetDelegate(caller_id)->DidCompleteRecognition(caller_id);
300 requests_.erase(caller_id);
301 bubble_controller_->CloseBubble(caller_id);
302 }
303
304 void SpeechInputManagerImpl::OnRecognizerError(
305 int caller_id, SpeechRecognizer::ErrorCode error) {
306 if (caller_id == recording_caller_id_)
307 recording_caller_id_ = 0;
308
309 requests_[caller_id].is_active = false;
310
311 struct ErrorMessageMapEntry {
312 SpeechRecognizer::ErrorCode error;
313 int message_id;
314 };
315 ErrorMessageMapEntry error_message_map[] = {
316 {
317 SpeechRecognizer::RECOGNIZER_ERROR_CAPTURE, IDS_SPEECH_INPUT_MIC_ERROR
318 }, {
319 SpeechRecognizer::RECOGNIZER_ERROR_NO_SPEECH, IDS_SPEECH_INPUT_NO_SPEECH
320 }, {
321 SpeechRecognizer::RECOGNIZER_ERROR_NO_RESULTS, IDS_SPEECH_INPUT_NO_RESULTS
322 }, {
323 SpeechRecognizer::RECOGNIZER_ERROR_NETWORK, IDS_SPEECH_INPUT_NET_ERROR
324 }
325 };
326 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(error_message_map); ++i) {
327 if (error_message_map[i].error == error) {
328 bubble_controller_->SetBubbleMessage(
329 caller_id,
330 l10n_util::GetStringUTF16(error_message_map[i].message_id));
331 return;
332 }
333 }
334
335 NOTREACHED() << "unknown error " << error;
336 }
337
338 void SpeechInputManagerImpl::DidStartReceivingAudio(int caller_id) {
339 DCHECK(HasPendingRequest(caller_id));
340 DCHECK(recording_caller_id_ == caller_id);
341 bubble_controller_->SetBubbleRecordingMode(caller_id);
342 }
343
344 void SpeechInputManagerImpl::DidCompleteEnvironmentEstimation(int caller_id) {
345 DCHECK(HasPendingRequest(caller_id));
346 DCHECK(recording_caller_id_ == caller_id);
347 }
348
349 void SpeechInputManagerImpl::SetInputVolume(int caller_id, float volume,
350 float noise_volume) {
351 DCHECK(HasPendingRequest(caller_id));
352 DCHECK_EQ(recording_caller_id_, caller_id);
353
354 bubble_controller_->SetBubbleInputVolume(caller_id, volume, noise_volume);
355 }
356
357 void SpeechInputManagerImpl::CancelRecognitionAndInformDelegate(int caller_id) {
358 SpeechInputManagerDelegate* cur_delegate = GetDelegate(caller_id);
359 CancelRecognition(caller_id);
360 cur_delegate->DidCompleteRecording(caller_id);
361 cur_delegate->DidCompleteRecognition(caller_id);
362 }
363
364 void SpeechInputManagerImpl::InfoBubbleButtonClicked(
365 int caller_id, SpeechInputBubble::Button button) {
366 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
367 // Ignore if the caller id was not in our active recognizers list because the
368 // user might have clicked more than once, or recognition could have been
369 // cancelled due to other reasons before the user click was processed.
370 if (!HasPendingRequest(caller_id))
371 return;
372
373 if (button == SpeechInputBubble::BUTTON_CANCEL) {
374 CancelRecognitionAndInformDelegate(caller_id);
375 } else if (button == SpeechInputBubble::BUTTON_TRY_AGAIN) {
376 StartRecognitionForRequest(caller_id);
377 }
378 }
379
380 void SpeechInputManagerImpl::InfoBubbleFocusChanged(int caller_id) {
381 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
382 // Ignore if the caller id was not in our active recognizers list because the
383 // user might have clicked more than once, or recognition could have been
384 // ended due to other reasons before the user click was processed.
385 if (HasPendingRequest(caller_id)) {
386 // If this is an ongoing recording or if we were displaying an error message
387 // to the user, abort it since user has switched focus. Otherwise
388 // recognition has started and keep that going so user can start speaking to
389 // another element while this gets the results in parallel.
390 if (recording_caller_id_ == caller_id || !requests_[caller_id].is_active) {
391 CancelRecognitionAndInformDelegate(caller_id);
392 }
393 }
394 }
395
396 } // namespace speech_input
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698