| Index: content/browser/speech/speech_recognizer_impl.cc
|
| diff --git a/content/browser/speech/speech_recognizer_impl.cc b/content/browser/speech/speech_recognizer_impl.cc
|
| index f6fb3900a5e03cdfa08e0c89542c663021e288c4..821f2e1176be3a9da44f84331647fcacd46a569f 100644
|
| --- a/content/browser/speech/speech_recognizer_impl.cc
|
| +++ b/content/browser/speech/speech_recognizer_impl.cc
|
| @@ -6,11 +6,16 @@
|
|
|
| #include "base/basictypes.h"
|
| #include "base/bind.h"
|
| +#include "base/memory/shared_memory.h"
|
| +#include "base/threading/thread.h"
|
| +#include "base/threading/thread_restrictions.h"
|
| #include "base/time/time.h"
|
| #include "content/browser/browser_main_loop.h"
|
| #include "content/browser/media/media_internals.h"
|
| #include "content/browser/speech/audio_buffer.h"
|
| #include "content/browser/speech/google_one_shot_remote_engine.h"
|
| +#include "content/browser/speech/speech_recognition_audio_receiver.h"
|
| +#include "content/public/browser/browser_message_filter.h"
|
| #include "content/public/browser/speech_recognition_event_listener.h"
|
| #include "media/base/audio_converter.h"
|
| #include "net/url_request/url_request_context_getter.h"
|
| @@ -41,6 +46,7 @@ class SpeechRecognizerImpl::OnDataConverter
|
| // Converts input audio |data| bus into an AudioChunk where the input format
|
| // is given by |input_parameters_| and the output format by
|
| // |output_parameters_|.
|
| + // Skips conversion on |audio_converter_| when input and output formats match.
|
| scoped_refptr<AudioChunk> Convert(const AudioBus* data);
|
|
|
| private:
|
| @@ -49,13 +55,14 @@ class SpeechRecognizerImpl::OnDataConverter
|
|
|
| // Handles resampling, buffering, and channel mixing between input and output
|
| // parameters.
|
| - AudioConverter audio_converter_;
|
| + scoped_ptr<AudioConverter> audio_converter_;
|
|
|
| scoped_ptr<AudioBus> input_bus_;
|
| scoped_ptr<AudioBus> output_bus_;
|
| const AudioParameters input_parameters_;
|
| const AudioParameters output_parameters_;
|
| bool waiting_for_input_;
|
| + bool need_conversion_;
|
| scoped_ptr<uint8[]> converted_data_;
|
|
|
| DISALLOW_COPY_AND_ASSIGN(OnDataConverter);
|
| @@ -115,30 +122,40 @@ COMPILE_ASSERT(SpeechRecognizerImpl::kNumBitsPerAudioSample % 8 == 0,
|
|
|
| SpeechRecognizerImpl::OnDataConverter::OnDataConverter(
|
| const AudioParameters& input_params, const AudioParameters& output_params)
|
| - : audio_converter_(input_params, output_params, false),
|
| - input_bus_(AudioBus::Create(input_params)),
|
| + : input_bus_(AudioBus::Create(input_params)),
|
| output_bus_(AudioBus::Create(output_params)),
|
| input_parameters_(input_params),
|
| output_parameters_(output_params),
|
| waiting_for_input_(false),
|
| converted_data_(new uint8[output_parameters_.GetBytesPerBuffer()]) {
|
| - audio_converter_.AddInput(this);
|
| + if (!input_parameters_.Equals(output_parameters_)) {
|
| + audio_converter_.reset(new AudioConverter(input_params,
|
| + output_params,
|
| + false));
|
| + audio_converter_->AddInput(this);
|
| + }
|
| }
|
|
|
| SpeechRecognizerImpl::OnDataConverter::~OnDataConverter() {
|
| // It should now be safe to unregister the converter since no more OnData()
|
| // callbacks are outstanding at this point.
|
| - audio_converter_.RemoveInput(this);
|
| + if (audio_converter_.get())
|
| + audio_converter_->RemoveInput(this);
|
| }
|
|
|
| scoped_refptr<AudioChunk> SpeechRecognizerImpl::OnDataConverter::Convert(
|
| const AudioBus* data) {
|
| CHECK_EQ(data->frames(), input_parameters_.frames_per_buffer());
|
|
|
| - data->CopyTo(input_bus_.get());
|
| -
|
| - waiting_for_input_ = true;
|
| - audio_converter_.Convert(output_bus_.get());
|
| + // Do conversion if needed - only when |input_parameters_| differ from
|
| + // |output_parameters_|.
|
| + if (audio_converter_.get()) {
|
| + data->CopyTo(input_bus_.get());
|
| + waiting_for_input_ = true;
|
| + audio_converter_->Convert(output_bus_.get());
|
| + } else {
|
| + data->CopyTo(output_bus_.get());
|
| + }
|
|
|
| output_bus_->ToInterleaved(
|
| output_bus_->frames(), output_parameters_.bits_per_sample() / 8,
|
| @@ -174,6 +191,8 @@ SpeechRecognizerImpl::SpeechRecognizerImpl(
|
| int session_id,
|
| bool continuous,
|
| bool provisional_results,
|
| + bool using_audio_receiver,
|
| + const base::ProcessHandle peer_process_handle,
|
| SpeechRecognitionEngine* engine)
|
| : SpeechRecognizer(listener, session_id),
|
| recognition_engine_(engine),
|
| @@ -182,6 +201,8 @@ SpeechRecognizerImpl::SpeechRecognizerImpl(
|
| media::AudioLogFactory::AUDIO_INPUT_CONTROLLER)),
|
| is_dispatching_event_(false),
|
| provisional_results_(provisional_results),
|
| + using_audio_receiver_(using_audio_receiver),
|
| + peer_process_handle_(peer_process_handle),
|
| state_(STATE_IDLE) {
|
| DCHECK(recognition_engine_ != NULL);
|
| if (!continuous) {
|
| @@ -213,7 +234,7 @@ SpeechRecognizerImpl::SpeechRecognizerImpl(
|
| // synchronous callbacks.
|
|
|
| void SpeechRecognizerImpl::StartRecognition(const std::string& device_id) {
|
| - DCHECK(!device_id.empty());
|
| + DCHECK(!device_id.empty() ^ using_audio_receiver_);
|
| device_id_ = device_id;
|
|
|
| BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
|
| @@ -244,7 +265,8 @@ bool SpeechRecognizerImpl::IsCapturingAudio() const {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); // See IsActive().
|
| const bool is_capturing_audio = state_ >= STATE_STARTING &&
|
| state_ <= STATE_RECOGNIZING;
|
| - DCHECK((is_capturing_audio && (audio_controller_.get() != NULL)) ||
|
| + DCHECK((is_capturing_audio &&
|
| + (using_audio_receiver_ || (audio_controller_.get() != NULL))) ||
|
| (!is_capturing_audio && audio_controller_.get() == NULL));
|
| return is_capturing_audio;
|
| }
|
| @@ -261,6 +283,8 @@ SpeechRecognizerImpl::~SpeechRecognizerImpl() {
|
| audio_controller_->Close(
|
| base::Bind(&KeepAudioControllerRefcountedForDtor, audio_controller_));
|
| audio_log_->OnClosed(0);
|
| + } else if (audio_receiver_.get()) {
|
| + ResetAudioReceiver();
|
| }
|
| }
|
|
|
| @@ -481,7 +505,7 @@ void SpeechRecognizerImpl::ProcessAudioPipeline(const AudioChunk& raw_audio) {
|
| endpointer_.ProcessAudio(raw_audio, &rms);
|
|
|
| if (route_to_vumeter) {
|
| - DCHECK(route_to_endpointer); // Depends on endpointer due to |rms|.
|
| + DCHECK(route_to_endpointer); // Depends on endpointer due to |rms|.
|
| UpdateSignalAndNoiseLevels(rms, clip_detected);
|
| }
|
| if (route_to_sr_engine) {
|
| @@ -528,6 +552,16 @@ SpeechRecognizerImpl::StartRecording(const FSMEventArgs&) {
|
| AudioParameters::AUDIO_PCM_LOW_LATENCY, kChannelLayout, kAudioSampleRate,
|
| kNumBitsPerAudioSample, frames_per_buffer);
|
|
|
| + // Check if using audio receiver as source (audio data processed on renderer).
|
| + // If |StartRecordingUsingAudioReceiver()| fails, we abort the recognition.
|
| + if (using_audio_receiver_) {
|
| + return StartRecordingUsingAudioReceiver(output_parameters) ?
|
| + STATE_STARTING :
|
| + Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO));
|
| + }
|
| +
|
| + // Get audio data from microphone on the browser.
|
| +
|
| // Audio converter will receive audio based on these parameters as input.
|
| // On Windows we start by verifying that Core Audio is supported. If not,
|
| // the WaveIn API is used and we might as well avoid all audio conversations
|
| @@ -584,6 +618,77 @@ SpeechRecognizerImpl::StartRecording(const FSMEventArgs&) {
|
| return STATE_STARTING;
|
| }
|
|
|
| +bool SpeechRecognizerImpl::StartRecordingUsingAudioReceiver(
|
| + const media::AudioParameters& params) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + DCHECK(!audio_thread_.get());
|
| + DCHECK(!audio_receiver_.get());
|
| +
|
| + audio_thread_.reset(new base::Thread("AudioReceiverThread"));
|
| + if (!audio_thread_->Start()) {
|
| + DLOG(ERROR) << "Cannot start new audio thread";
|
| + audio_thread_.reset();
|
| + return false;
|
| + }
|
| +
|
| + // Start receiving audio, incoming data will be posted to |OnAudioTrackData|.
|
| + base::SharedMemoryHandle foreign_memory_handle;
|
| + base::SyncSocket::TransitDescriptor foreign_socket_descriptor;
|
| + audio_receiver_ = SpeechRecognitionAudioReceiver::Create(
|
| + params,
|
| + peer_process_handle_,
|
| + this, // Trigger OnData() and OnError() events here.
|
| + audio_thread_->task_runner().get(),
|
| + &foreign_memory_handle,
|
| + &foreign_socket_descriptor);
|
| +
|
| + if (!audio_receiver_.get()) {
|
| + ResetAudioReceiver();
|
| + return false;
|
| + }
|
| +
|
| + // Reusing the audio converter only to create chunks from the bus.
|
| + // Same input and output format indicate no additional conversion here.
|
| + audio_converter_.reset(new OnDataConverter(params, params));
|
| +
|
| + // The endpointer needs to estimate the environment/background noise before
|
| + // starting to treat the audio as user input. We wait in the state
|
| + // ESTIMATING_ENVIRONMENT until such interval has elapsed before switching
|
| + // to user input mode.
|
| + endpointer_.SetEnvironmentEstimationMode();
|
| +
|
| + listener()->OnAudioReceiverReady(session_id(),
|
| + params,
|
| + foreign_memory_handle,
|
| + foreign_socket_descriptor);
|
| +
|
| + // Start listening on the receiver's audio thread.
|
| + audio_receiver_->StartListening();
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void SpeechRecognizerImpl::ResetAudioReceiver() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| +
|
| + // We stop listening not to post any new tasks to the audio thread.
|
| + if (audio_receiver_.get())
|
| + audio_receiver_->Stop();
|
| +
|
| + if (!audio_thread_.get())
|
| + return;
|
| +
|
| + // This synchronous operation is usually instantaneous since the receiver
|
| + // closes the socket on Stop() and no new tasks will be posted to the
|
| + // |audio_thread_|.
|
| + const bool previous_io_allowed = base::ThreadRestrictions::SetIOAllowed(true);
|
| + audio_thread_->Stop();
|
| + base::ThreadRestrictions::SetIOAllowed(previous_io_allowed);
|
| +
|
| + audio_thread_.reset();
|
| + audio_receiver_.reset();
|
| +}
|
| +
|
| SpeechRecognizerImpl::FSMState
|
| SpeechRecognizerImpl::StartRecognitionEngine(const FSMEventArgs& event_args) {
|
| // This is the first audio packet captured, so the recognition engine is
|
| @@ -769,6 +874,12 @@ SpeechRecognizerImpl::NotFeasible(const FSMEventArgs& event_args) {
|
|
|
| void SpeechRecognizerImpl::CloseAudioControllerAsynchronously() {
|
| DCHECK(IsCapturingAudio());
|
| +
|
| + if (using_audio_receiver_) {
|
| + ResetAudioReceiver();
|
| + return;
|
| + }
|
| +
|
| DVLOG(1) << "SpeechRecognizerImpl closing audio controller.";
|
| // Issues a Close on the audio controller, passing an empty callback. The only
|
| // purpose of such callback is to keep the audio controller refcounted until
|
|
|