1 // Copyright 2013 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.
5 #include "content/browser/speech/speech_recognizer_impl_android.h"
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_array.h"
9 #include "base/android/jni_string.h"
10 #include "base/android/scoped_java_ref.h"
11 #include "base/bind.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/speech_recognition_event_listener.h"
15 #include "content/public/browser/speech_recognition_manager.h"
16 #include "content/public/browser/speech_recognition_session_config.h"
17 #include "content/public/common/speech_recognition_grammar.h"
18 #include "content/public/common/speech_recognition_result.h"
19 #include "jni/SpeechRecognition_jni.h"
21 using base::android::AppendJavaStringArrayToStringVector
;
22 using base::android::AttachCurrentThread
;
23 using base::android::ConvertUTF8ToJavaString
;
24 using base::android::GetApplicationContext
;
25 using base::android::JavaFloatArrayToFloatVector
;
29 SpeechRecognizerImplAndroid::SpeechRecognizerImplAndroid(
30 SpeechRecognitionEventListener
* listener
,
32 : SpeechRecognizer(listener
, session_id
),
36 SpeechRecognizerImplAndroid::~SpeechRecognizerImplAndroid() { }
38 void SpeechRecognizerImplAndroid::StartRecognition(
39 const std::string
& device_id
) {
40 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
41 // TODO(xians): Open the correct device for speech on Android.
42 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
, base::Bind(
43 &SpeechRecognitionEventListener::OnRecognitionStart
,
44 base::Unretained(listener()),
46 SpeechRecognitionSessionConfig config
=
47 SpeechRecognitionManager::GetInstance()->GetSessionConfig(session_id());
48 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, base::Bind(
49 &content::SpeechRecognizerImplAndroid::StartRecognitionOnUIThread
, this,
50 config
.language
, config
.continuous
, config
.interim_results
));
53 void SpeechRecognizerImplAndroid::StartRecognitionOnUIThread(
54 std::string language
, bool continuous
, bool interim_results
) {
55 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
56 JNIEnv
* env
= AttachCurrentThread();
57 j_recognition_
.Reset(Java_SpeechRecognition_createSpeechRecognition(env
,
58 GetApplicationContext(), reinterpret_cast<intptr_t>(this)));
59 Java_SpeechRecognition_startRecognition(env
, j_recognition_
.obj(),
60 ConvertUTF8ToJavaString(env
, language
).obj(), continuous
,
64 void SpeechRecognizerImplAndroid::AbortRecognition() {
65 if (BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
67 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, base::Bind(
68 &content::SpeechRecognizerImplAndroid::AbortRecognition
, this));
71 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
72 JNIEnv
* env
= AttachCurrentThread();
73 if (!j_recognition_
.is_null())
74 Java_SpeechRecognition_abortRecognition(env
, j_recognition_
.obj());
77 void SpeechRecognizerImplAndroid::StopAudioCapture() {
78 if (BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
79 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, base::Bind(
80 &content::SpeechRecognizerImplAndroid::StopAudioCapture
, this));
83 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
84 JNIEnv
* env
= AttachCurrentThread();
85 if (!j_recognition_
.is_null())
86 Java_SpeechRecognition_stopRecognition(env
, j_recognition_
.obj());
89 bool SpeechRecognizerImplAndroid::IsActive() const {
90 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
91 return state_
!= STATE_IDLE
;
94 bool SpeechRecognizerImplAndroid::IsCapturingAudio() const {
95 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
96 return state_
== STATE_CAPTURING_AUDIO
;
99 void SpeechRecognizerImplAndroid::OnAudioStart(JNIEnv
* env
, jobject obj
) {
100 if (BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
101 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
, base::Bind(
102 &SpeechRecognizerImplAndroid::OnAudioStart
, this,
103 static_cast<JNIEnv
*>(NULL
), static_cast<jobject
>(NULL
)));
106 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
107 state_
= STATE_CAPTURING_AUDIO
;
108 listener()->OnAudioStart(session_id());
111 void SpeechRecognizerImplAndroid::OnSoundStart(JNIEnv
* env
, jobject obj
) {
112 if (BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
113 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
, base::Bind(
114 &SpeechRecognizerImplAndroid::OnSoundStart
, this,
115 static_cast<JNIEnv
*>(NULL
), static_cast<jobject
>(NULL
)));
118 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
119 listener()->OnSoundStart(session_id());
122 void SpeechRecognizerImplAndroid::OnSoundEnd(JNIEnv
* env
, jobject obj
) {
123 if (BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
124 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
, base::Bind(
125 &SpeechRecognizerImplAndroid::OnSoundEnd
, this,
126 static_cast<JNIEnv
*>(NULL
), static_cast<jobject
>(NULL
)));
129 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
130 listener()->OnSoundEnd(session_id());
133 void SpeechRecognizerImplAndroid::OnAudioEnd(JNIEnv
* env
, jobject obj
) {
134 if (BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
135 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
, base::Bind(
136 &SpeechRecognizerImplAndroid::OnAudioEnd
, this,
137 static_cast<JNIEnv
*>(NULL
), static_cast<jobject
>(NULL
)));
140 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
141 if (state_
== STATE_CAPTURING_AUDIO
)
142 state_
= STATE_AWAITING_FINAL_RESULT
;
143 listener()->OnAudioEnd(session_id());
146 void SpeechRecognizerImplAndroid::OnRecognitionResults(JNIEnv
* env
, jobject obj
,
147 jobjectArray strings
, jfloatArray floats
, jboolean provisional
) {
148 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
149 std::vector
<base::string16
> options
;
150 AppendJavaStringArrayToStringVector(env
, strings
, &options
);
151 std::vector
<float> scores(options
.size(), 0.0);
153 JavaFloatArrayToFloatVector(env
, floats
, &scores
);
154 SpeechRecognitionResults results
;
155 results
.push_back(SpeechRecognitionResult());
156 SpeechRecognitionResult
& result
= results
.back();
157 CHECK_EQ(options
.size(), scores
.size());
158 for (size_t i
= 0; i
< options
.size(); ++i
) {
159 result
.hypotheses
.push_back(SpeechRecognitionHypothesis(
160 options
[i
], static_cast<double>(scores
[i
])));
162 result
.is_provisional
= provisional
;
163 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
, base::Bind(
164 &SpeechRecognizerImplAndroid::OnRecognitionResultsOnIOThread
,
168 void SpeechRecognizerImplAndroid::OnRecognitionResultsOnIOThread(
169 SpeechRecognitionResults
const &results
) {
170 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
171 listener()->OnRecognitionResults(session_id(), results
);
174 void SpeechRecognizerImplAndroid::OnRecognitionError(JNIEnv
* env
,
175 jobject obj
, jint error
) {
176 if (BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
177 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
, base::Bind(
178 &SpeechRecognizerImplAndroid::OnRecognitionError
, this,
179 static_cast<JNIEnv
*>(NULL
), static_cast<jobject
>(NULL
), error
));
182 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
183 SpeechRecognitionErrorCode code
=
184 static_cast<SpeechRecognitionErrorCode
>(error
);
185 listener()->OnRecognitionError(session_id(), SpeechRecognitionError(code
));
188 void SpeechRecognizerImplAndroid::OnRecognitionEnd(JNIEnv
* env
,
190 if (BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
191 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
, base::Bind(
192 &SpeechRecognizerImplAndroid::OnRecognitionEnd
, this,
193 static_cast<JNIEnv
*>(NULL
), static_cast<jobject
>(NULL
)));
196 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
198 listener()->OnRecognitionEnd(session_id());
202 bool SpeechRecognizerImplAndroid::RegisterSpeechRecognizer(JNIEnv
* env
) {
203 return RegisterNativesImpl(env
);
206 } // namespace content