Index: content/browser/speech/speech_recognizer_impl_android.cc |
diff --git a/content/browser/speech/speech_recognizer_impl_android.cc b/content/browser/speech/speech_recognizer_impl_android.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..df2b95e869c44e82de071d9c16af930e882c772d |
--- /dev/null |
+++ b/content/browser/speech/speech_recognizer_impl_android.cc |
@@ -0,0 +1,207 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "content/browser/speech/speech_recognizer_impl_android.h" |
+ |
+#include "base/android/jni_android.h" |
+#include "base/android/jni_array.h" |
+#include "base/android/jni_string.h" |
+#include "base/android/scoped_java_ref.h" |
+#include "base/bind.h" |
+#include "base/utf_string_conversions.h" |
+#include "content/public/browser/browser_thread.h" |
+#include "content/public/browser/speech_recognition_event_listener.h" |
+#include "content/public/common/speech_recognition_grammar.h" |
+#include "content/public/common/speech_recognition_result.h" |
+#include "jni/SpeechRecognition_jni.h" |
+ |
+using base::android::AppendJavaStringArrayToStringVector; |
+using base::android::AttachCurrentThread; |
+using base::android::GetApplicationContext; |
+using base::android::JavaFloatArrayToFloatVector; |
+ |
+namespace content { |
+ |
+void SpeechRecognizerImplAndroid::Init(JNIEnv* env) { |
+ RegisterNativesImpl(env); |
+} |
+ |
+SpeechRecognizerImplAndroid::SpeechRecognizerImplAndroid( |
+ SpeechRecognitionEventListener* listener, |
+ const SpeechRecognitionSessionConfig& config, |
+ int session_id) |
+ : SpeechRecognizer(listener, session_id), |
+ config_(config) { |
+} |
+ |
+SpeechRecognizerImplAndroid::~SpeechRecognizerImplAndroid() { } |
+ |
+void SpeechRecognizerImplAndroid::StartRecognition() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ listener_->OnRecognitionStart(session_id_); |
Primiano Tucci (use gerrit)
2013/06/03 16:24:11
I'd like to have feedback by some other reviewer o
|
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
+ base::Bind(&content::SpeechRecognizerImplAndroid::StartRecognitionJNI, |
+ this, config_.continuous, config_.interim_results)); |
+} |
+void SpeechRecognizerImplAndroid::StartRecognitionJNI(bool continuous, |
Primiano Tucci (use gerrit)
2013/06/03 16:24:11
Nit: \n
janx
2013/06/05 13:51:00
I was collating coupled functions together, but si
|
+ bool partial_results) { |
Primiano Tucci (use gerrit)
2013/06/03 16:24:11
Why not keeping the interim_results name?
I unders
janx
2013/06/05 13:51:00
Let's go with "interim" then.
|
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ j_recognition_.Reset(Java_SpeechRecognition_createSpeechRecognition( |
+ AttachCurrentThread(), |
+ GetApplicationContext(), |
+ reinterpret_cast<jlong>(this))); |
Primiano Tucci (use gerrit)
2013/06/03 16:24:11
I am definitely not a JNI expert, so I'd like some
janx
2013/06/05 13:51:00
Fixed both occurences (this CL and http://crrev.co
|
+ Java_SpeechRecognition_StartRecognition(AttachCurrentThread(), |
+ j_recognition_.obj(), continuous, partial_results); |
+} |
+ |
+void SpeechRecognizerImplAndroid::AbortRecognition() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
+ base::Bind(&content::SpeechRecognizerImplAndroid::CancelRecognitionJNI, |
+ this)); |
+ OnRecognitionError(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_ABORTED)); |
+ listener_->OnRecognitionEnd(session_id_); |
Primiano Tucci (use gerrit)
2013/06/03 16:24:11
Keep in mind that OnRecognitionEnd is the final ev
|
+} |
+void SpeechRecognizerImplAndroid::CancelRecognitionJNI() { |
Primiano Tucci (use gerrit)
2013/06/03 16:24:11
Similarly to my previous comment (on partial_resul
Primiano Tucci (use gerrit)
2013/06/03 16:24:11
Nit: \n (here and similarly below)
janx
2013/06/05 13:51:00
Done.
|
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ Java_SpeechRecognition_CancelRecognition(AttachCurrentThread(), |
+ j_recognition_.obj()); |
Primiano Tucci (use gerrit)
2013/06/03 16:24:11
At this point we must be sure that the JNI object
|
+} |
+ |
+void SpeechRecognizerImplAndroid::StopAudioCapture() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
+ base::Bind(&content::SpeechRecognizerImplAndroid::StopRecognitionJNI, |
+ this)); |
+ listener_->OnRecognitionEnd(session_id_); |
Primiano Tucci (use gerrit)
2013/06/03 16:24:11
Again: OnRecognitionEnd will cause the manager to
|
+} |
+void SpeechRecognizerImplAndroid::StopRecognitionJNI() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ Java_SpeechRecognition_StopRecognition(AttachCurrentThread(), |
+ j_recognition_.obj()); |
+} |
+ |
+bool SpeechRecognizerImplAndroid::IsActive() const { |
+ return false; // FIXME |
janx
2013/05/31 17:22:24
Recognition seems to work when IsActive() and IsCa
Primiano Tucci (use gerrit)
2013/06/03 16:24:11
Hmm, actually I think you should implement this. I
janx
2013/06/05 13:51:00
Thanks for your input, I'll go ahead and reimpleme
|
+} |
+ |
+bool SpeechRecognizerImplAndroid::IsCapturingAudio() const { |
+ return false; // FIXME |
janx
2013/05/31 17:22:24
ditto
Primiano Tucci (use gerrit)
2013/06/03 16:24:11
Ditto :)
Btw, you should be able to implement the
janx
2013/06/05 13:51:00
Done.
|
+} |
+ |
+void SpeechRecognizerImplAndroid::OnAudioStartJNI(JNIEnv* env, jobject obj) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
+ base::Bind(&SpeechRecognizerImplAndroid::OnAudioStart, this)); |
+} |
+void SpeechRecognizerImplAndroid::OnAudioStart() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ listener_->OnAudioStart(session_id_); |
+} |
+ |
+void SpeechRecognizerImplAndroid::OnSoundStartJNI(JNIEnv* env, jobject obj) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
+ base::Bind(&SpeechRecognizerImplAndroid::OnSoundStart, this)); |
+} |
+void SpeechRecognizerImplAndroid::OnSoundStart() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ listener_->OnSoundStart(session_id_); |
+} |
+ |
+void SpeechRecognizerImplAndroid::OnSoundEndJNI(JNIEnv* env, jobject obj) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
+ base::Bind(&SpeechRecognizerImplAndroid::OnSoundEnd, this)); |
+} |
+void SpeechRecognizerImplAndroid::OnSoundEnd() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ listener_->OnSoundEnd(session_id_); |
+ // Since Android's speech API doesn't have an AudioEnd event, we emulate it. |
Primiano Tucci (use gerrit)
2013/06/03 16:24:11
It's ok emulating the event but not here. The same
janx
2013/06/05 13:51:00
Ok, I'll emulate the AudioEnd event in the Java fi
|
+ listener_->OnAudioEnd(session_id_); |
+} |
+ |
+void SpeechRecognizerImplAndroid::OnRecognitionResultsJNI(JNIEnv* env, |
+ jobject obj, jobjectArray strings, jfloatArray floats, |
+ jboolean partial) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ std::vector<string16> options; |
+ AppendJavaStringArrayToStringVector(env, strings, &options); |
+ std::vector<float> scores; |
+ JavaFloatArrayToFloatVector(env, floats, &scores); |
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
+ base::Bind(&SpeechRecognizerImplAndroid::OnRecognitionResults, |
+ this, options, scores, partial)); |
+} |
+void SpeechRecognizerImplAndroid::OnRecognitionResults( |
+ std::vector<string16> options, std::vector<float> scores, |
+ bool partial) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ SpeechRecognitionResults results; |
+ results.push_back(SpeechRecognitionResult()); |
+ SpeechRecognitionResult& result = results.back(); |
+ for (unsigned int i = 0; i < options.size(); i++) { |
+ result.hypotheses.push_back( |
+ SpeechRecognitionHypothesis(options[i], |
+ static_cast<double>(scores[i]))); |
+ } |
+ result.is_provisional = partial; |
+ listener_->OnRecognitionResults(session_id_, results); |
+} |
+ |
+void SpeechRecognizerImplAndroid::OnRecognitionErrorJNI(JNIEnv* env, |
+ jobject obj, jint error) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ SpeechRecognitionErrorCode code = SPEECH_RECOGNITION_ERROR_NONE; |
Primiano Tucci (use gerrit)
2013/06/03 16:24:11
Is it actually possible to get another error code
janx
2013/06/05 13:51:00
SPEECH_RECOGNITION_ERROR_NONE is a hack and should
|
+ |
+ // Translate Android speech recognition errors to Web Speech API errors. |
Primiano Tucci (use gerrit)
2013/06/03 16:24:11
Can we move this translation in the .java file ?
I
|
+ switch(error) { |
+ case 3: // ERROR_AUDIO |
+ code = SPEECH_RECOGNITION_ERROR_AUDIO; |
+ break; |
+ case 5: // ERROR_CLIENT |
+ code = SPEECH_RECOGNITION_ERROR_ABORTED; |
+ break; |
+ case 8: // ERROR_RECOGNIZER_BUSY |
+ case 9: // ERROR_INSUFFICIENT_PERMISSIONS |
+ code = SPEECH_RECOGNITION_ERROR_NOT_ALLOWED; |
+ break; |
+ case 1: // ERROR_NETWORK_TIMEOUT |
+ case 2: // ERROR_NETWORK |
+ case 4: // ERROR_SERVER |
+ code = SPEECH_RECOGNITION_ERROR_NETWORK; |
+ break; |
+ case 7: // ERROR_NO_MATCH |
+ code = SPEECH_RECOGNITION_ERROR_NO_MATCH; |
+ break; |
+ case 6: // ERROR_SPEECH_TIMEOUT |
+ code = SPEECH_RECOGNITION_ERROR_NO_SPEECH; |
+ break; |
+ } |
+ |
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
+ base::Bind(&SpeechRecognizerImplAndroid::OnRecognitionError, this, |
+ SpeechRecognitionError(code))); |
+} |
+void SpeechRecognizerImplAndroid::OnRecognitionError( |
+ SpeechRecognitionError error) { |
+ if (error.code != SPEECH_RECOGNITION_ERROR_NONE) |
Primiano Tucci (use gerrit)
2013/06/03 16:24:11
See previous comment, this shouldn't be required.
janx
2013/06/05 13:51:00
Removed.
|
+ listener_->OnRecognitionError(session_id_, error); |
+} |
+ |
+void SpeechRecognizerImplAndroid::OnAudioLevelsChangeJNI(JNIEnv* env, |
+ jobject obj, jfloat rms) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
+ base::Bind(&SpeechRecognizerImplAndroid::OnAudioLevelsChange, this, rms)); |
+} |
+void SpeechRecognizerImplAndroid::OnAudioLevelsChange(float rms) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ // FIXME set audio_level_ and noise_level from rms |
janx
2013/05/31 17:22:24
The audio and noise levels are calculated from the
Primiano Tucci (use gerrit)
2013/06/03 16:24:11
I thinks that you can safely remove completely thi
janx
2013/06/05 13:51:00
Agreed, let's completely remove OnAudioLevelsChang
|
+ //listener_->OnAudioLevelsChange(session_id_, |
+ // clip_detected ? 1.0f : audio_level_, |
+ // noise_level); |
+} |
+ |
+} // namespace content |