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

Side by Side Diff: chrome/browser/ui/app_list/speech_recognizer.cc

Issue 676593003: Implement native speech recognition for the launcher. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove refcount and better use WeakPtr. Created 6 years, 1 month 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
OLDNEW
(Empty)
1 // Copyright 2014 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 "chrome/browser/ui/app_list/speech_recognizer.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/strings/string16.h"
11 #include "base/timer/timer.h"
12 #include "chrome/browser/ui/app_list/speech_recognizer_delegate.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/render_process_host.h"
15 #include "content/public/browser/speech_recognition_event_listener.h"
16 #include "content/public/browser/speech_recognition_manager.h"
17 #include "content/public/browser/speech_recognition_session_config.h"
18 #include "content/public/browser/web_contents.h"
19 #include "content/public/common/speech_recognition_error.h"
20 #include "net/url_request/url_request_context_getter.h"
21 #include "ui/app_list/speech_ui_model_observer.h"
22
23 namespace app_list {
24
25 // Length of timeout to cancel recognition if there's no speech heard.
26 static const int kNoSpeechTimeoutInSeconds = 5;
27
28 // Speech recognizer listener. This is separate from SpeechRecognizer because
29 // the speech recognition engine must function from the IO thread. Because of
30 // this, the lifecycle of this class must be decoupled from the lifecycle of
31 // SpeechRecognizer. To avoid circular references, this class has no reference
32 // to SpeechRecognizer. Instead, it has a reference to the
33 // SpeechRecognizerDelegate via a weak pointer that is only ever referenced from
34 // the UI thread.
35 class SpeechRecognizer::EventListener
36 : public base::RefCountedThreadSafe<SpeechRecognizer::EventListener>,
37 public content::SpeechRecognitionEventListener {
38 public:
39 EventListener(base::WeakPtr<SpeechRecognizerDelegate> delegate,
40 net::URLRequestContextGetter* url_request_context_getter,
41 const std::string& locale);
42
43 void StartOnIOThread(int render_process_id);
44 void StopOnIOThread();
45
46 private:
47 friend class base::RefCountedThreadSafe<SpeechRecognizer::EventListener>;
48 ~EventListener();
49
50 void NotifyRecognitionStateChanged(SpeechRecognitionState new_state);
51
52 void StartSpeechTimeout();
53 void StopSpeechTimeout();
54 void SpeechTimeout();
55
56 // Overidden from content::SpeechRecognitionEventListener:
57 // These are always called on the IO thread.
58 void OnRecognitionStart(int session_id) override;
59 void OnRecognitionEnd(int session_id) override;
60 void OnRecognitionResults(
61 int session_id,
62 const content::SpeechRecognitionResults& results) override;
63 void OnRecognitionError(
64 int session_id, const content::SpeechRecognitionError& error) override;
65 void OnSoundStart(int session_id) override;
66 void OnSoundEnd(int session_id) override;
67 void OnAudioLevelsChange(
68 int session_id, float volume, float noise_volume) override;
69 void OnEnvironmentEstimationComplete(int session_id) override;
70 void OnAudioStart(int session_id) override;
71 void OnAudioEnd(int session_id) override;
72
73 // Only access from the UI thread.
74 base::WeakPtr<SpeechRecognizerDelegate> delegate_;
75
76 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
Lei Zhang 2014/11/05 03:44:38 And everything here down is only accessed from the
Anand Mistry (off Chromium) 2014/11/05 04:44:11 Yes. Added comment.
77 std::string locale_;
78 base::Timer speech_timeout_;
79 int session_;
80
81 base::WeakPtrFactory<EventListener> weak_factory_;
82
83 DISALLOW_COPY_AND_ASSIGN(EventListener);
84 };
85
86 SpeechRecognizer::EventListener::EventListener(
87 base::WeakPtr<SpeechRecognizerDelegate> delegate,
Lei Zhang 2014/11/05 03:44:37 can this be const ref?
Anand Mistry (off Chromium) 2014/11/05 04:44:11 Done.
88 net::URLRequestContextGetter* url_request_context_getter,
89 const std::string& locale)
90 : delegate_(delegate),
91 url_request_context_getter_(url_request_context_getter),
92 locale_(locale),
93 speech_timeout_(false, false),
94 session_(-1),
Lei Zhang 2014/11/05 03:44:38 Maybe define a kInvalidSession constant?
Anand Mistry (off Chromium) 2014/11/05 04:44:11 Done.
95 weak_factory_(this) {
96 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
97 }
98
99 SpeechRecognizer::EventListener::~EventListener() {
100 DCHECK(!speech_timeout_.IsRunning());
101 }
102
103 void SpeechRecognizer::EventListener::StartOnIOThread(int render_process_id) {
104 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
105 if (session_ != -1)
106 StopOnIOThread();
107
108 content::SpeechRecognitionSessionConfig config;
109 config.language = locale_;
110 config.is_legacy_api = false;
111 config.continuous = true;
112 config.interim_results = true;
113 config.max_hypotheses = 1;
114 config.filter_profanities = true;
115 config.url_request_context_getter = url_request_context_getter_;
116 config.event_listener = weak_factory_.GetWeakPtr();
117 config.initial_context.render_process_id = render_process_id;
118
119 auto speech_instance = content::SpeechRecognitionManager::GetInstance();
120 session_ = speech_instance->CreateSession(config);
121 speech_instance->StartSession(session_);
122 }
123
124 void SpeechRecognizer::EventListener::StopOnIOThread() {
125 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
126 if (session_ == -1)
127 return;
128
129 // Prevent recursion.
130 int session = session_;
131 session_ = -1;
132 StopSpeechTimeout();
133 content::SpeechRecognitionManager::GetInstance()->StopAudioCaptureForSession(
134 session);
135 }
136
137 void SpeechRecognizer::EventListener::NotifyRecognitionStateChanged(
138 SpeechRecognitionState new_state) {
139 content::BrowserThread::PostTask(
140 content::BrowserThread::UI,
141 FROM_HERE,
142 base::Bind(&SpeechRecognizerDelegate::OnSpeechRecognitionStateChanged,
143 delegate_,
144 new_state));
145 }
146
147 void SpeechRecognizer::EventListener::StartSpeechTimeout() {
148 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
149 speech_timeout_.Start(
150 FROM_HERE,
151 base::TimeDelta::FromSeconds(kNoSpeechTimeoutInSeconds),
152 base::Bind(&SpeechRecognizer::EventListener::SpeechTimeout, this));
153 }
154
155 void SpeechRecognizer::EventListener::StopSpeechTimeout() {
156 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
157 speech_timeout_.Stop();
158 }
159
160 void SpeechRecognizer::EventListener::SpeechTimeout() {
161 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
162 StopOnIOThread();
163 }
164
165 void SpeechRecognizer::EventListener::OnRecognitionStart(int session_id) {
166 NotifyRecognitionStateChanged(SPEECH_RECOGNITION_RECOGNIZING);
167 }
168
169 void SpeechRecognizer::EventListener::OnRecognitionEnd(int session_id) {
170 StopOnIOThread();
171 NotifyRecognitionStateChanged(SPEECH_RECOGNITION_READY);
172 }
173
174 void SpeechRecognizer::EventListener::OnRecognitionResults(
175 int session_id, const content::SpeechRecognitionResults& results) {
176 base::string16 result_str;
177 size_t final_count = 0;
178 for (const auto& result : results) {
179 if (!result.is_provisional)
180 final_count++;
181 result_str += result.hypotheses[0].utterance;
182 }
183 StopSpeechTimeout();
184 content::BrowserThread::PostTask(
185 content::BrowserThread::UI,
186 FROM_HERE,
187 base::Bind(&SpeechRecognizerDelegate::OnSpeechResult,
188 delegate_,
189 result_str,
190 final_count == results.size()));
191
192 // Stop the moment we have a final result.
193 if (final_count == results.size())
194 StopOnIOThread();
195 }
196
197 void SpeechRecognizer::EventListener::OnRecognitionError(
198 int session_id, const content::SpeechRecognitionError& error) {
199 StopOnIOThread();
200 if (error.code == content::SPEECH_RECOGNITION_ERROR_NETWORK) {
201 NotifyRecognitionStateChanged(SPEECH_RECOGNITION_NETWORK_ERROR);
202 }
203 NotifyRecognitionStateChanged(SPEECH_RECOGNITION_READY);
204 }
205
206 void SpeechRecognizer::EventListener::OnSoundStart(int session_id) {
207 StartSpeechTimeout();
208 NotifyRecognitionStateChanged(SPEECH_RECOGNITION_IN_SPEECH);
209 }
210
211 void SpeechRecognizer::EventListener::OnSoundEnd(int session_id) {
212 StopOnIOThread();
213 NotifyRecognitionStateChanged(SPEECH_RECOGNITION_RECOGNIZING);
214 }
215
216 void SpeechRecognizer::EventListener::OnAudioLevelsChange(
217 int session_id, float volume, float noise_volume) {
218 DCHECK_LE(0.0, volume);
219 DCHECK_GE(1.0, volume);
220 DCHECK_LE(0.0, noise_volume);
221 DCHECK_GE(1.0, noise_volume);
222 volume = std::max(0.0f, volume - noise_volume);
223 // Both |volume| and |noise_volume| are defined to be in the range [0.0, 1.0].
224 // See: content/public/browser/speech_recognition_event_listener.h
225 int16_t sound_level = static_cast<int16_t>(INT16_MAX * volume);
226 content::BrowserThread::PostTask(
227 content::BrowserThread::UI,
228 FROM_HERE,
229 base::Bind(&SpeechRecognizerDelegate::OnSpeechSoundLevelChanged,
230 delegate_,
231 sound_level));
232 }
233
234 void SpeechRecognizer::EventListener::OnEnvironmentEstimationComplete(
235 int session_id) {
236 }
237
238 void SpeechRecognizer::EventListener::OnAudioStart(int session_id) {
239 }
240
241 void SpeechRecognizer::EventListener::OnAudioEnd(int session_id) {
242 }
243
244 SpeechRecognizer::SpeechRecognizer(
245 SpeechRecognizerDelegate* delegate,
246 net::URLRequestContextGetter* url_request_context_getter,
247 const std::string& locale)
248 : delegate_(delegate),
249 delegate_weak_factory_(delegate) {
250 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
251
252 // Must be done after delegate_weak_factory_ has been constructed.
253 speech_event_listener_ = new EventListener(
254 delegate_weak_factory_.GetWeakPtr(), url_request_context_getter, locale);
255 }
256
257 SpeechRecognizer::~SpeechRecognizer() {
258 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
259 Stop();
260 }
261
262 void SpeechRecognizer::Start() {
263 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
264 // The speech recognizer checks to see if the request is allowed by looking
265 // up the renderer process. A renderer containing the app-list is hard-coded
266 // to be allowed.
267 content::WebContents* contents = delegate_->GetSpeechContents();
268 if (!contents)
269 return;
270
271 content::BrowserThread::PostTask(
272 content::BrowserThread::IO,
273 FROM_HERE,
274 base::Bind(&SpeechRecognizer::EventListener::StartOnIOThread,
275 speech_event_listener_,
276 contents->GetRenderProcessHost()->GetID()));
277 }
278
279 void SpeechRecognizer::Stop() {
280 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
281 content::BrowserThread::PostTask(
282 content::BrowserThread::IO,
283 FROM_HERE,
284 base::Bind(&SpeechRecognizer::EventListener::StopOnIOThread,
285 speech_event_listener_));
286 }
287
288 } // namespace app_list
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698