OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/speech/chrome_speech_input_manager.h" | 5 #include "chrome/browser/speech/chrome_speech_input_manager.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "base/synchronization/lock.h" | 9 #include "base/synchronization/lock.h" |
10 #include "base/threading/thread_restrictions.h" | 10 #include "base/threading/thread_restrictions.h" |
11 #include "base/utf_string_conversions.h" | 11 #include "base/utf_string_conversions.h" |
12 #include "chrome/browser/browser_process.h" | 12 #include "chrome/browser/browser_process.h" |
13 #include "chrome/browser/prefs/pref_service.h" | 13 #include "chrome/browser/prefs/pref_service.h" |
14 #include "chrome/browser/tab_contents/tab_util.h" | 14 #include "chrome/browser/tab_contents/tab_util.h" |
15 #include "chrome/common/chrome_switches.h" | |
16 #include "chrome/common/pref_names.h" | 15 #include "chrome/common/pref_names.h" |
17 #include "content/browser/browser_thread.h" | 16 #include "content/browser/browser_thread.h" |
18 #include "grit/generated_resources.h" | 17 #include "grit/generated_resources.h" |
19 #include "media/audio/audio_manager.h" | 18 #include "media/audio/audio_manager.h" |
20 #include "ui/base/l10n/l10n_util.h" | 19 #include "ui/base/l10n/l10n_util.h" |
21 | 20 |
22 #if defined(OS_WIN) | 21 #if defined(OS_WIN) |
23 #include "chrome/installer/util/wmi.h" | 22 #include "chrome/installer/util/wmi.h" |
24 #endif | 23 #endif |
25 | 24 |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
70 base::AutoLock lock(lock_); | 69 base::AutoLock lock(lock_); |
71 return value_; | 70 return value_; |
72 } | 71 } |
73 | 72 |
74 bool can_report_metrics() { | 73 bool can_report_metrics() { |
75 base::AutoLock lock(lock_); | 74 base::AutoLock lock(lock_); |
76 return can_report_metrics_; | 75 return can_report_metrics_; |
77 } | 76 } |
78 | 77 |
79 private: | 78 private: |
79 friend class base::RefCountedThreadSafe<OptionalRequestInfo>; | |
80 | |
81 ~OptionalRequestInfo() {} | |
82 | |
80 base::Lock lock_; | 83 base::Lock lock_; |
81 std::string value_; | 84 std::string value_; |
82 bool can_report_metrics_; | 85 bool can_report_metrics_; |
83 | 86 |
84 DISALLOW_COPY_AND_ASSIGN(OptionalRequestInfo); | 87 DISALLOW_COPY_AND_ASSIGN(OptionalRequestInfo); |
85 }; | 88 }; |
86 | 89 |
87 ChromeSpeechInputManager::SpeechInputRequest::SpeechInputRequest() { | 90 ChromeSpeechInputManager::SpeechInputRequest::SpeechInputRequest() { |
88 } | 91 } |
89 | 92 |
90 ChromeSpeechInputManager::SpeechInputRequest::~SpeechInputRequest() { | 93 ChromeSpeechInputManager::SpeechInputRequest::~SpeechInputRequest() { |
91 } | 94 } |
92 | 95 |
93 ChromeSpeechInputManager* ChromeSpeechInputManager::GetInstance() { | 96 ChromeSpeechInputManager* ChromeSpeechInputManager::GetInstance() { |
94 return Singleton<ChromeSpeechInputManager>::get(); | 97 return Singleton<ChromeSpeechInputManager>::get(); |
95 } | 98 } |
96 | 99 |
97 ChromeSpeechInputManager::ChromeSpeechInputManager() | 100 ChromeSpeechInputManager::ChromeSpeechInputManager() |
98 : recording_caller_id_(0), | 101 : bubble_controller_(new SpeechInputBubbleController( |
99 bubble_controller_(new SpeechInputBubbleController( | |
100 ALLOW_THIS_IN_INITIALIZER_LIST(this))) { | 102 ALLOW_THIS_IN_INITIALIZER_LIST(this))) { |
101 } | 103 } |
102 | 104 |
103 ChromeSpeechInputManager::~ChromeSpeechInputManager() { | 105 ChromeSpeechInputManager::~ChromeSpeechInputManager() { |
104 while (requests_.begin() != requests_.end()) | |
105 CancelRecognition(requests_.begin()->first); | |
106 } | 106 } |
107 | 107 |
108 bool ChromeSpeechInputManager::HasPendingRequest(int caller_id) const { | 108 void ChromeSpeechInputManager::CreateBubble( |
109 return requests_.find(caller_id) != requests_.end(); | |
110 } | |
111 | |
112 SpeechInputManagerDelegate* ChromeSpeechInputManager::GetDelegate( | |
113 int caller_id) const { | |
114 return requests_.find(caller_id)->second.delegate; | |
115 } | |
116 | |
117 void ChromeSpeechInputManager::StartRecognition( | |
118 SpeechInputManagerDelegate* delegate, | |
119 int caller_id, | 109 int caller_id, |
120 int render_process_id, | 110 int render_process_id, |
121 int render_view_id, | 111 int render_view_id, |
122 const gfx::Rect& element_rect, | 112 const gfx::Rect& element_rect) { |
123 const std::string& language, | 113 bubble_controller_->CreateBubble(caller_id, render_process_id, |
124 const std::string& grammar, | 114 render_view_id, element_rect); |
125 const std::string& origin_url) { | 115 } |
126 DCHECK(!HasPendingRequest(caller_id)); | |
127 | 116 |
128 bubble_controller_->CreateBubble(caller_id, render_process_id, render_view_id, | 117 void ChromeSpeechInputManager::FetchRequestInfo() { |
129 element_rect); | |
130 | |
131 if (!optional_request_info_.get()) { | 118 if (!optional_request_info_.get()) { |
132 optional_request_info_ = new OptionalRequestInfo(); | 119 optional_request_info_ = new OptionalRequestInfo(); |
133 // Since hardware info is optional with speech input requests, we start an | 120 // Since hardware info is optional with speech input requests, we start an |
134 // asynchronous fetch here and move on with recording audio. This first | 121 // asynchronous fetch here and move on with recording audio. This first |
135 // speech input request would send an empty string for hardware info and | 122 // speech input request would send an empty string for hardware info and |
136 // subsequent requests may have the hardware info available if the fetch | 123 // subsequent requests may have the hardware info available if the fetch |
137 // completed before them. This way we don't end up stalling the user with | 124 // completed before them. This way we don't end up stalling the user with |
138 // a long wait and disk seeks when they click on a UI element and start | 125 // a long wait and disk seeks when they click on a UI element and start |
139 // speaking. | 126 // speaking. |
140 optional_request_info_->Refresh(); | 127 optional_request_info_->Refresh(); |
141 } | 128 } |
142 | 129 request_info_ = optional_request_info_->value(); |
Satish
2011/09/12 12:23:05
instead of directly setting the member vars of the
allanwoj
2011/09/19 18:30:38
Done.
| |
143 SpeechInputRequest* request = &requests_[caller_id]; | 130 can_report_metrics_ = optional_request_info_->can_report_metrics(); |
144 request->delegate = delegate; | |
145 request->recognizer = new SpeechRecognizer( | |
146 this, caller_id, language, grammar, censor_results(), | |
147 optional_request_info_->value(), | |
148 optional_request_info_->can_report_metrics() ? origin_url : ""); | |
149 request->is_active = false; | |
150 | |
151 StartRecognitionForRequest(caller_id); | |
152 } | 131 } |
153 | 132 |
154 void ChromeSpeechInputManager::StartRecognitionForRequest(int caller_id) { | 133 void ChromeSpeechInputManager::NoMicBubbleMessage(int caller_id) { |
155 DCHECK(HasPendingRequest(caller_id)); | 134 bubble_controller_->SetBubbleMessage( |
156 | 135 caller_id, l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_NO_MIC)); |
157 // If we are currently recording audio for another caller, abort that cleanly. | |
158 if (recording_caller_id_) | |
159 CancelRecognitionAndInformDelegate(recording_caller_id_); | |
160 | |
161 if (!AudioManager::GetAudioManager()->HasAudioInputDevices()) { | |
162 bubble_controller_->SetBubbleMessage( | |
163 caller_id, l10n_util::GetStringUTF16(IDS_SPEECH_INPUT_NO_MIC)); | |
164 } else { | |
165 recording_caller_id_ = caller_id; | |
166 requests_[caller_id].is_active = true; | |
167 requests_[caller_id].recognizer->StartRecording(); | |
168 bubble_controller_->SetBubbleWarmUpMode(caller_id); | |
169 } | |
170 } | 136 } |
171 | 137 |
172 void ChromeSpeechInputManager::CancelRecognition(int caller_id) { | 138 void ChromeSpeechInputManager::SetBubbleWarmUpMode(int caller_id) { |
173 DCHECK(HasPendingRequest(caller_id)); | 139 bubble_controller_->SetBubbleWarmUpMode(caller_id); |
174 if (requests_[caller_id].is_active) | 140 } |
175 requests_[caller_id].recognizer->CancelRecognition(); | 141 |
176 requests_.erase(caller_id); | 142 void ChromeSpeechInputManager::CloseBubble(int caller_id) { |
177 if (recording_caller_id_ == caller_id) | |
178 recording_caller_id_ = 0; | |
179 bubble_controller_->CloseBubble(caller_id); | 143 bubble_controller_->CloseBubble(caller_id); |
180 } | 144 } |
181 | 145 |
182 void ChromeSpeechInputManager::CancelAllRequestsWithDelegate( | 146 void ChromeSpeechInputManager::SetBubbleRecognizingMode(int caller_id) { |
183 SpeechInputManagerDelegate* delegate) { | |
184 SpeechRecognizerMap::iterator it = requests_.begin(); | |
185 while (it != requests_.end()) { | |
186 if (it->second.delegate == delegate) { | |
187 CancelRecognition(it->first); | |
188 // This map will have very few elements so it is simpler to restart. | |
189 it = requests_.begin(); | |
190 } else { | |
191 ++it; | |
192 } | |
193 } | |
194 } | |
195 | |
196 void ChromeSpeechInputManager::StopRecording(int caller_id) { | |
197 DCHECK(HasPendingRequest(caller_id)); | |
198 requests_[caller_id].recognizer->StopRecording(); | |
199 } | |
200 | |
201 void ChromeSpeechInputManager::SetRecognitionResult( | |
202 int caller_id, bool error, const SpeechInputResultArray& result) { | |
203 DCHECK(HasPendingRequest(caller_id)); | |
204 GetDelegate(caller_id)->SetRecognitionResult(caller_id, result); | |
205 } | |
206 | |
207 void ChromeSpeechInputManager::DidCompleteRecording(int caller_id) { | |
208 DCHECK(recording_caller_id_ == caller_id); | |
209 DCHECK(HasPendingRequest(caller_id)); | |
210 recording_caller_id_ = 0; | |
211 GetDelegate(caller_id)->DidCompleteRecording(caller_id); | |
212 bubble_controller_->SetBubbleRecognizingMode(caller_id); | 147 bubble_controller_->SetBubbleRecognizingMode(caller_id); |
213 } | 148 } |
214 | 149 |
215 void ChromeSpeechInputManager::DidCompleteRecognition(int caller_id) { | 150 void ChromeSpeechInputManager::SetBubbleRecordingMode(int caller_id) { |
216 GetDelegate(caller_id)->DidCompleteRecognition(caller_id); | 151 bubble_controller_->SetBubbleRecordingMode(caller_id); |
217 requests_.erase(caller_id); | |
218 bubble_controller_->CloseBubble(caller_id); | |
219 } | 152 } |
220 | 153 |
221 void ChromeSpeechInputManager::OnRecognizerError( | 154 void ChromeSpeechInputManager::SetBubbleInputVolume( |
155 int caller_id, float volume, float noise_volume) { | |
156 bubble_controller_->SetBubbleInputVolume(caller_id, volume, noise_volume); | |
157 } | |
158 | |
159 void ChromeSpeechInputManager::HandleRecognizerError( | |
222 int caller_id, SpeechRecognizer::ErrorCode error) { | 160 int caller_id, SpeechRecognizer::ErrorCode error) { |
223 if (caller_id == recording_caller_id_) | |
224 recording_caller_id_ = 0; | |
225 | |
226 requests_[caller_id].is_active = false; | |
227 | |
228 struct ErrorMessageMapEntry { | 161 struct ErrorMessageMapEntry { |
229 SpeechRecognizer::ErrorCode error; | 162 SpeechRecognizer::ErrorCode error; |
230 int message_id; | 163 int message_id; |
231 }; | 164 }; |
232 ErrorMessageMapEntry error_message_map[] = { | 165 ErrorMessageMapEntry error_message_map[] = { |
233 { | 166 { |
234 SpeechRecognizer::RECOGNIZER_ERROR_CAPTURE, IDS_SPEECH_INPUT_MIC_ERROR | 167 SpeechRecognizer::RECOGNIZER_ERROR_CAPTURE, IDS_SPEECH_INPUT_MIC_ERROR |
235 }, { | 168 }, { |
236 SpeechRecognizer::RECOGNIZER_ERROR_NO_SPEECH, IDS_SPEECH_INPUT_NO_SPEECH | 169 SpeechRecognizer::RECOGNIZER_ERROR_NO_SPEECH, IDS_SPEECH_INPUT_NO_SPEECH |
237 }, { | 170 }, { |
238 SpeechRecognizer::RECOGNIZER_ERROR_NO_RESULTS, IDS_SPEECH_INPUT_NO_RESULTS | 171 SpeechRecognizer::RECOGNIZER_ERROR_NO_RESULTS, IDS_SPEECH_INPUT_NO_RESULTS |
239 }, { | 172 }, { |
240 SpeechRecognizer::RECOGNIZER_ERROR_NETWORK, IDS_SPEECH_INPUT_NET_ERROR | 173 SpeechRecognizer::RECOGNIZER_ERROR_NETWORK, IDS_SPEECH_INPUT_NET_ERROR |
241 } | 174 } |
242 }; | 175 }; |
243 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(error_message_map); ++i) { | 176 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(error_message_map); ++i) { |
244 if (error_message_map[i].error == error) { | 177 if (error_message_map[i].error == error) { |
245 bubble_controller_->SetBubbleMessage( | 178 bubble_controller_->SetBubbleMessage( |
246 caller_id, | 179 caller_id, |
247 l10n_util::GetStringUTF16(error_message_map[i].message_id)); | 180 l10n_util::GetStringUTF16(error_message_map[i].message_id)); |
248 return; | 181 return; |
249 } | 182 } |
250 } | 183 } |
251 | 184 |
252 NOTREACHED() << "unknown error " << error; | 185 NOTREACHED() << "unknown error " << error; |
253 } | 186 } |
254 | 187 |
255 void ChromeSpeechInputManager::DidStartReceivingAudio(int caller_id) { | |
256 DCHECK(HasPendingRequest(caller_id)); | |
257 DCHECK(recording_caller_id_ == caller_id); | |
258 bubble_controller_->SetBubbleRecordingMode(caller_id); | |
259 } | |
260 | |
261 void ChromeSpeechInputManager::DidCompleteEnvironmentEstimation(int caller_id) { | |
262 DCHECK(HasPendingRequest(caller_id)); | |
263 DCHECK(recording_caller_id_ == caller_id); | |
264 } | |
265 | |
266 void ChromeSpeechInputManager::SetInputVolume(int caller_id, float volume, | |
267 float noise_volume) { | |
268 DCHECK(HasPendingRequest(caller_id)); | |
269 DCHECK_EQ(recording_caller_id_, caller_id); | |
270 | |
271 bubble_controller_->SetBubbleInputVolume(caller_id, volume, noise_volume); | |
272 } | |
273 | |
274 void ChromeSpeechInputManager::CancelRecognitionAndInformDelegate( | |
275 int caller_id) { | |
276 SpeechInputManagerDelegate* cur_delegate = GetDelegate(caller_id); | |
277 CancelRecognition(caller_id); | |
278 cur_delegate->DidCompleteRecording(caller_id); | |
279 cur_delegate->DidCompleteRecognition(caller_id); | |
280 } | |
281 | |
282 void ChromeSpeechInputManager::InfoBubbleButtonClicked( | 188 void ChromeSpeechInputManager::InfoBubbleButtonClicked( |
283 int caller_id, SpeechInputBubble::Button button) { | 189 int caller_id, SpeechInputBubble::Button button) { |
284 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
285 // Ignore if the caller id was not in our active recognizers list because the | 191 // Ignore if the caller id was not in our active recognizers list because the |
286 // user might have clicked more than once, or recognition could have been | 192 // user might have clicked more than once, or recognition could have been |
287 // cancelled due to other reasons before the user click was processed. | 193 // cancelled due to other reasons before the user click was processed. |
288 if (!HasPendingRequest(caller_id)) | 194 if (!HasPendingRequest(caller_id)) |
289 return; | 195 return; |
290 | 196 |
291 if (button == SpeechInputBubble::BUTTON_CANCEL) { | 197 if (button == SpeechInputBubble::BUTTON_CANCEL) { |
292 CancelRecognitionAndInformDelegate(caller_id); | 198 CancelRecognitionAndInformDelegate(caller_id); |
293 } else if (button == SpeechInputBubble::BUTTON_TRY_AGAIN) { | 199 } else if (button == SpeechInputBubble::BUTTON_TRY_AGAIN) { |
294 StartRecognitionForRequest(caller_id); | 200 StartRecognitionForRequest(caller_id); |
295 } | 201 } |
296 } | 202 } |
297 | 203 |
298 void ChromeSpeechInputManager::InfoBubbleFocusChanged(int caller_id) { | 204 void ChromeSpeechInputManager::InfoBubbleFocusChanged(int caller_id) { |
299 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
300 // Ignore if the caller id was not in our active recognizers list because the | 206 OnFocusChanged(caller_id); |
301 // user might have clicked more than once, or recognition could have been | |
302 // ended due to other reasons before the user click was processed. | |
303 if (HasPendingRequest(caller_id)) { | |
304 // If this is an ongoing recording or if we were displaying an error message | |
305 // to the user, abort it since user has switched focus. Otherwise | |
306 // recognition has started and keep that going so user can start speaking to | |
307 // another element while this gets the results in parallel. | |
308 if (recording_caller_id_ == caller_id || !requests_[caller_id].is_active) { | |
309 CancelRecognitionAndInformDelegate(caller_id); | |
310 } | |
311 } | |
312 } | 207 } |
313 | 208 |
314 } // namespace speech_input | 209 } // namespace speech_input |
OLD | NEW |