Chromium Code Reviews| 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 |