Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/speech/speech_recognizer_impl.h" | 5 #include "content/browser/speech/speech_recognizer_impl.h" |
| 6 | 6 |
| 7 #include "base/basictypes.h" | 7 #include "base/basictypes.h" |
| 8 #include "base/bind.h" | 8 #include "base/bind.h" |
| 9 #include "base/memory/shared_memory.h" | |
| 10 #include "base/process/process_handle.h" | |
| 11 #include "base/threading/thread.h" | |
| 9 #include "base/time/time.h" | 12 #include "base/time/time.h" |
| 10 #include "content/browser/browser_main_loop.h" | 13 #include "content/browser/browser_main_loop.h" |
| 11 #include "content/browser/media/media_internals.h" | 14 #include "content/browser/media/media_internals.h" |
| 12 #include "content/browser/speech/audio_buffer.h" | 15 #include "content/browser/speech/audio_buffer.h" |
| 13 #include "content/browser/speech/google_one_shot_remote_engine.h" | 16 #include "content/browser/speech/google_one_shot_remote_engine.h" |
| 17 #include "content/public/browser/browser_message_filter.h" | |
| 14 #include "content/public/browser/speech_recognition_event_listener.h" | 18 #include "content/public/browser/speech_recognition_event_listener.h" |
| 15 #include "media/base/audio_converter.h" | 19 #include "media/base/audio_converter.h" |
| 16 #include "net/url_request/url_request_context_getter.h" | 20 #include "net/url_request/url_request_context_getter.h" |
| 17 | 21 |
| 18 #if defined(OS_WIN) | 22 #if defined(OS_WIN) |
| 19 #include "media/audio/win/core_audio_util_win.h" | 23 #include "media/audio/win/core_audio_util_win.h" |
| 20 #endif | 24 #endif |
| 21 | 25 |
| 22 using media::AudioBus; | 26 using media::AudioBus; |
| 23 using media::AudioConverter; | 27 using media::AudioConverter; |
| 28 using media::AudioInputBufferParameters; | |
| 24 using media::AudioInputController; | 29 using media::AudioInputController; |
| 25 using media::AudioManager; | 30 using media::AudioManager; |
| 26 using media::AudioParameters; | 31 using media::AudioParameters; |
| 27 using media::ChannelLayout; | 32 using media::ChannelLayout; |
| 28 | 33 |
| 29 namespace content { | 34 namespace content { |
| 30 | 35 |
| 31 // Private class which encapsulates the audio converter and the | 36 // Private class which encapsulates the audio converter and the |
| 32 // AudioConverter::InputCallback. It handles resampling, buffering and | 37 // AudioConverter::InputCallback. It handles resampling, buffering and |
| 33 // channel mixing between input and output parameters. | 38 // channel mixing between input and output parameters. |
| 34 class SpeechRecognizerImpl::OnDataConverter | 39 class SpeechRecognizerImpl::OnDataConverter |
| 35 : public media::AudioConverter::InputCallback { | 40 : public media::AudioConverter::InputCallback { |
| 36 public: | 41 public: |
| 37 OnDataConverter(const AudioParameters& input_params, | 42 OnDataConverter(const AudioParameters& input_params, |
| 38 const AudioParameters& output_params); | 43 const AudioParameters& output_params); |
| 39 virtual ~OnDataConverter(); | 44 virtual ~OnDataConverter(); |
| 40 | 45 |
| 41 // Converts input audio |data| bus into an AudioChunk where the input format | 46 // Converts input audio |data| bus into an AudioChunk where the input format |
| 42 // is given by |input_parameters_| and the output format by | 47 // is given by |input_parameters_| and the output format by |
| 43 // |output_parameters_|. | 48 // |output_parameters_|. |
| 49 // Skips conversion on |audio_converter_| when input and output formats match. | |
| 44 scoped_refptr<AudioChunk> Convert(const AudioBus* data); | 50 scoped_refptr<AudioChunk> Convert(const AudioBus* data); |
| 45 | 51 |
| 46 private: | 52 private: |
| 47 // media::AudioConverter::InputCallback implementation. | 53 // media::AudioConverter::InputCallback implementation. |
| 48 virtual double ProvideInput(AudioBus* dest, | 54 virtual double ProvideInput(AudioBus* dest, |
| 49 base::TimeDelta buffer_delay) OVERRIDE; | 55 base::TimeDelta buffer_delay) OVERRIDE; |
| 50 | 56 |
| 51 // Handles resampling, buffering, and channel mixing between input and output | 57 // Handles resampling, buffering, and channel mixing between input and output |
| 52 // parameters. | 58 // parameters. |
| 53 AudioConverter audio_converter_; | 59 AudioConverter audio_converter_; |
| 54 | 60 |
| 55 scoped_ptr<AudioBus> input_bus_; | 61 scoped_ptr<AudioBus> input_bus_; |
| 56 scoped_ptr<AudioBus> output_bus_; | 62 scoped_ptr<AudioBus> output_bus_; |
| 57 const AudioParameters input_parameters_; | 63 const AudioParameters input_parameters_; |
| 58 const AudioParameters output_parameters_; | 64 const AudioParameters output_parameters_; |
| 59 bool waiting_for_input_; | 65 bool waiting_for_input_; |
| 66 bool need_conversion_; | |
| 60 scoped_ptr<uint8[]> converted_data_; | 67 scoped_ptr<uint8[]> converted_data_; |
| 61 | 68 |
| 62 DISALLOW_COPY_AND_ASSIGN(OnDataConverter); | 69 DISALLOW_COPY_AND_ASSIGN(OnDataConverter); |
| 63 }; | 70 }; |
| 64 | 71 |
| 65 namespace { | 72 namespace { |
| 66 | 73 |
| 67 // The following constants are related to the volume level indicator shown in | 74 // The following constants are related to the volume level indicator shown in |
| 68 // the UI for recorded audio. | 75 // the UI for recorded audio. |
| 69 // Multiplier used when new volume is greater than previous level. | 76 // Multiplier used when new volume is greater than previous level. |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 117 SpeechRecognizerImpl::OnDataConverter::OnDataConverter( | 124 SpeechRecognizerImpl::OnDataConverter::OnDataConverter( |
| 118 const AudioParameters& input_params, const AudioParameters& output_params) | 125 const AudioParameters& input_params, const AudioParameters& output_params) |
| 119 : audio_converter_(input_params, output_params, false), | 126 : audio_converter_(input_params, output_params, false), |
| 120 input_bus_(AudioBus::Create(input_params)), | 127 input_bus_(AudioBus::Create(input_params)), |
| 121 output_bus_(AudioBus::Create(output_params)), | 128 output_bus_(AudioBus::Create(output_params)), |
| 122 input_parameters_(input_params), | 129 input_parameters_(input_params), |
| 123 output_parameters_(output_params), | 130 output_parameters_(output_params), |
| 124 waiting_for_input_(false), | 131 waiting_for_input_(false), |
| 125 converted_data_(new uint8[output_parameters_.GetBytesPerBuffer()]) { | 132 converted_data_(new uint8[output_parameters_.GetBytesPerBuffer()]) { |
| 126 audio_converter_.AddInput(this); | 133 audio_converter_.AddInput(this); |
| 134 need_conversion_ = !input_parameters_.Equals(output_parameters_); | |
| 127 } | 135 } |
| 128 | 136 |
| 129 SpeechRecognizerImpl::OnDataConverter::~OnDataConverter() { | 137 SpeechRecognizerImpl::OnDataConverter::~OnDataConverter() { |
| 130 // It should now be safe to unregister the converter since no more OnData() | 138 // It should now be safe to unregister the converter since no more OnData() |
| 131 // callbacks are outstanding at this point. | 139 // callbacks are outstanding at this point. |
| 132 audio_converter_.RemoveInput(this); | 140 audio_converter_.RemoveInput(this); |
| 133 } | 141 } |
| 134 | 142 |
| 135 scoped_refptr<AudioChunk> SpeechRecognizerImpl::OnDataConverter::Convert( | 143 scoped_refptr<AudioChunk> SpeechRecognizerImpl::OnDataConverter::Convert( |
| 136 const AudioBus* data) { | 144 const AudioBus* data) { |
| 137 CHECK_EQ(data->frames(), input_parameters_.frames_per_buffer()); | 145 CHECK_EQ(data->frames(), input_parameters_.frames_per_buffer()); |
| 138 | 146 |
| 139 data->CopyTo(input_bus_.get()); | 147 // Do conversion if needed - only when |input_parameters_| differ from |
| 140 | 148 // |output_parameters_|. |
| 141 waiting_for_input_ = true; | 149 if (need_conversion_) { |
| 142 audio_converter_.Convert(output_bus_.get()); | 150 data->CopyTo(input_bus_.get()); |
| 151 waiting_for_input_ = true; | |
| 152 audio_converter_.Convert(output_bus_.get()); | |
| 153 } else { | |
| 154 data->CopyTo(output_bus_.get()); | |
| 155 } | |
| 143 | 156 |
| 144 output_bus_->ToInterleaved( | 157 output_bus_->ToInterleaved( |
| 145 output_bus_->frames(), output_parameters_.bits_per_sample() / 8, | 158 output_bus_->frames(), output_parameters_.bits_per_sample() / 8, |
| 146 converted_data_.get()); | 159 converted_data_.get()); |
| 147 | 160 |
| 148 // TODO(primiano): Refactor AudioChunk to avoid the extra-copy here | 161 // TODO(primiano): Refactor AudioChunk to avoid the extra-copy here |
| 149 // (see http://crbug.com/249316 for details). | 162 // (see http://crbug.com/249316 for details). |
| 150 return scoped_refptr<AudioChunk>(new AudioChunk( | 163 return scoped_refptr<AudioChunk>(new AudioChunk( |
| 151 converted_data_.get(), | 164 converted_data_.get(), |
| 152 output_parameters_.GetBytesPerBuffer(), | 165 output_parameters_.GetBytesPerBuffer(), |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 168 return 1; | 181 return 1; |
| 169 } | 182 } |
| 170 | 183 |
| 171 // SpeechRecognizerImpl implementation | 184 // SpeechRecognizerImpl implementation |
| 172 | 185 |
| 173 SpeechRecognizerImpl::SpeechRecognizerImpl( | 186 SpeechRecognizerImpl::SpeechRecognizerImpl( |
| 174 SpeechRecognitionEventListener* listener, | 187 SpeechRecognitionEventListener* listener, |
| 175 int session_id, | 188 int session_id, |
| 176 bool continuous, | 189 bool continuous, |
| 177 bool provisional_results, | 190 bool provisional_results, |
| 191 bool using_audio_track, | |
| 192 base::ProcessHandle peer_process_handle, | |
| 178 SpeechRecognitionEngine* engine) | 193 SpeechRecognitionEngine* engine) |
| 179 : SpeechRecognizer(listener, session_id), | 194 : SpeechRecognizer(listener, session_id), |
| 180 recognition_engine_(engine), | 195 recognition_engine_(engine), |
| 181 endpointer_(kAudioSampleRate), | 196 endpointer_(kAudioSampleRate), |
| 182 audio_log_(MediaInternals::GetInstance()->CreateAudioLog( | 197 audio_log_(MediaInternals::GetInstance()->CreateAudioLog( |
| 183 media::AudioLogFactory::AUDIO_INPUT_CONTROLLER)), | 198 media::AudioLogFactory::AUDIO_INPUT_CONTROLLER)), |
| 184 is_dispatching_event_(false), | 199 is_dispatching_event_(false), |
| 185 provisional_results_(provisional_results), | 200 provisional_results_(provisional_results), |
| 201 using_audio_track_(using_audio_track), | |
| 202 peer_process_handle_(peer_process_handle), | |
| 186 state_(STATE_IDLE) { | 203 state_(STATE_IDLE) { |
| 187 DCHECK(recognition_engine_ != NULL); | 204 DCHECK(recognition_engine_ != NULL); |
| 188 if (!continuous) { | 205 if (!continuous) { |
| 189 // In single shot (non-continous) recognition, | 206 // In single shot (non-continous) recognition, |
| 190 // the session is automatically ended after: | 207 // the session is automatically ended after: |
| 191 // - 0.5 seconds of silence if time < 3 seconds | 208 // - 0.5 seconds of silence if time < 3 seconds |
| 192 // - 1 seconds of silence if time >= 3 seconds | 209 // - 1 seconds of silence if time >= 3 seconds |
| 193 endpointer_.set_speech_input_complete_silence_length( | 210 endpointer_.set_speech_input_complete_silence_length( |
| 194 base::Time::kMicrosecondsPerSecond / 2); | 211 base::Time::kMicrosecondsPerSecond / 2); |
| 195 endpointer_.set_long_speech_input_complete_silence_length( | 212 endpointer_.set_long_speech_input_complete_silence_length( |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 207 } | 224 } |
| 208 | 225 |
| 209 // ------- Methods that trigger Finite State Machine (FSM) events ------------ | 226 // ------- Methods that trigger Finite State Machine (FSM) events ------------ |
| 210 | 227 |
| 211 // NOTE:all the external events and requests should be enqueued (PostTask), even | 228 // NOTE:all the external events and requests should be enqueued (PostTask), even |
| 212 // if they come from the same (IO) thread, in order to preserve the relationship | 229 // if they come from the same (IO) thread, in order to preserve the relationship |
| 213 // of causality between events and avoid interleaved event processing due to | 230 // of causality between events and avoid interleaved event processing due to |
| 214 // synchronous callbacks. | 231 // synchronous callbacks. |
| 215 | 232 |
| 216 void SpeechRecognizerImpl::StartRecognition(const std::string& device_id) { | 233 void SpeechRecognizerImpl::StartRecognition(const std::string& device_id) { |
| 217 DCHECK(!device_id.empty()); | 234 DCHECK(!device_id.empty() ^ using_audio_track_); |
| 218 device_id_ = device_id; | 235 device_id_ = device_id; |
| 219 | 236 |
| 220 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | 237 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| 221 base::Bind(&SpeechRecognizerImpl::DispatchEvent, | 238 base::Bind(&SpeechRecognizerImpl::DispatchEvent, |
| 222 this, FSMEventArgs(EVENT_START))); | 239 this, FSMEventArgs(EVENT_START))); |
| 223 } | 240 } |
| 224 | 241 |
| 225 void SpeechRecognizerImpl::AbortRecognition() { | 242 void SpeechRecognizerImpl::AbortRecognition() { |
| 226 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | 243 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| 227 base::Bind(&SpeechRecognizerImpl::DispatchEvent, | 244 base::Bind(&SpeechRecognizerImpl::DispatchEvent, |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 238 // Checking the FSM state from another thread (thus, while the FSM is | 255 // Checking the FSM state from another thread (thus, while the FSM is |
| 239 // potentially concurrently evolving) is meaningless. | 256 // potentially concurrently evolving) is meaningless. |
| 240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 241 return state_ != STATE_IDLE && state_ != STATE_ENDED; | 258 return state_ != STATE_IDLE && state_ != STATE_ENDED; |
| 242 } | 259 } |
| 243 | 260 |
| 244 bool SpeechRecognizerImpl::IsCapturingAudio() const { | 261 bool SpeechRecognizerImpl::IsCapturingAudio() const { |
| 245 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); // See IsActive(). | 262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); // See IsActive(). |
| 246 const bool is_capturing_audio = state_ >= STATE_STARTING && | 263 const bool is_capturing_audio = state_ >= STATE_STARTING && |
| 247 state_ <= STATE_RECOGNIZING; | 264 state_ <= STATE_RECOGNIZING; |
| 248 DCHECK((is_capturing_audio && (audio_controller_.get() != NULL)) || | 265 DCHECK((is_capturing_audio && |
| 266 (using_audio_track_ || (audio_controller_.get() != NULL))) || | |
| 249 (!is_capturing_audio && audio_controller_.get() == NULL)); | 267 (!is_capturing_audio && audio_controller_.get() == NULL)); |
| 250 return is_capturing_audio; | 268 return is_capturing_audio; |
| 251 } | 269 } |
| 252 | 270 |
| 253 const SpeechRecognitionEngine& | 271 const SpeechRecognitionEngine& |
| 254 SpeechRecognizerImpl::recognition_engine() const { | 272 SpeechRecognizerImpl::recognition_engine() const { |
| 255 return *(recognition_engine_.get()); | 273 return *(recognition_engine_.get()); |
| 256 } | 274 } |
| 257 | 275 |
| 258 SpeechRecognizerImpl::~SpeechRecognizerImpl() { | 276 SpeechRecognizerImpl::~SpeechRecognizerImpl() { |
| 259 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 277 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 260 endpointer_.EndSession(); | 278 endpointer_.EndSession(); |
| 261 if (audio_controller_.get()) { | 279 if (audio_controller_.get()) { |
| 262 audio_controller_->Close( | 280 audio_controller_->Close( |
| 263 base::Bind(&KeepAudioControllerRefcountedForDtor, audio_controller_)); | 281 base::Bind(&KeepAudioControllerRefcountedForDtor, audio_controller_)); |
| 264 audio_log_->OnClosed(0); | 282 audio_log_->OnClosed(0); |
| 265 } | 283 } |
| 284 | |
| 285 if (audio_sync_thread_.get()) { | |
| 286 base::ThreadRestrictions::SetIOAllowed(true); | |
|
tommi (sloooow) - chröme
2014/10/02 11:23:19
can we avoid this synchronous operation by e.g. pa
burnik
2014/10/02 16:04:11
Ok, but what would happen if the object goes away
no longer working on chromium
2014/10/03 08:57:53
I think there are two ways doing that:
1) in brows
burnik
2014/10/06 08:32:50
I've tried a few different approaches to avoid thi
| |
| 287 audio_sync_socket_->Shutdown(); | |
| 288 audio_sync_thread_->Stop(); | |
| 289 } | |
| 266 } | 290 } |
| 267 | 291 |
| 268 // Invoked in the audio thread. | 292 // Invoked in the audio thread. |
| 269 void SpeechRecognizerImpl::OnError(AudioInputController* controller, | 293 void SpeechRecognizerImpl::OnError(AudioInputController* controller, |
| 270 media::AudioInputController::ErrorCode error_code) { | 294 media::AudioInputController::ErrorCode error_code) { |
| 271 FSMEventArgs event_args(EVENT_AUDIO_ERROR); | 295 FSMEventArgs event_args(EVENT_AUDIO_ERROR); |
| 272 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | 296 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| 273 base::Bind(&SpeechRecognizerImpl::DispatchEvent, | 297 base::Bind(&SpeechRecognizerImpl::DispatchEvent, |
| 274 this, event_args)); | 298 this, event_args)); |
| 275 } | 299 } |
| (...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 475 state_ <= STATE_RECOGNIZING; | 499 state_ <= STATE_RECOGNIZING; |
| 476 const bool clip_detected = DetectClipping(raw_audio); | 500 const bool clip_detected = DetectClipping(raw_audio); |
| 477 float rms = 0.0f; | 501 float rms = 0.0f; |
| 478 | 502 |
| 479 num_samples_recorded_ += raw_audio.NumSamples(); | 503 num_samples_recorded_ += raw_audio.NumSamples(); |
| 480 | 504 |
| 481 if (route_to_endpointer) | 505 if (route_to_endpointer) |
| 482 endpointer_.ProcessAudio(raw_audio, &rms); | 506 endpointer_.ProcessAudio(raw_audio, &rms); |
| 483 | 507 |
| 484 if (route_to_vumeter) { | 508 if (route_to_vumeter) { |
| 485 DCHECK(route_to_endpointer); // Depends on endpointer due to |rms|. | 509 DCHECK(route_to_endpointer); // Depends on endpointer due to |rms|. |
| 486 UpdateSignalAndNoiseLevels(rms, clip_detected); | 510 UpdateSignalAndNoiseLevels(rms, clip_detected); |
| 487 } | 511 } |
| 488 if (route_to_sr_engine) { | 512 if (route_to_sr_engine) { |
| 489 DCHECK(recognition_engine_.get() != NULL); | 513 DCHECK(recognition_engine_.get() != NULL); |
| 490 recognition_engine_->TakeAudioChunk(raw_audio); | 514 recognition_engine_->TakeAudioChunk(raw_audio); |
| 491 } | 515 } |
| 492 } | 516 } |
| 493 | 517 |
| 494 SpeechRecognizerImpl::FSMState | 518 SpeechRecognizerImpl::FSMState |
| 495 SpeechRecognizerImpl::StartRecording(const FSMEventArgs&) { | 519 SpeechRecognizerImpl::StartRecording(const FSMEventArgs&) { |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 522 return Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO)); | 546 return Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO)); |
| 523 } | 547 } |
| 524 | 548 |
| 525 // Audio converter shall provide audio based on these parameters as output. | 549 // Audio converter shall provide audio based on these parameters as output. |
| 526 // Hard coded, WebSpeech specific parameters are utilized here. | 550 // Hard coded, WebSpeech specific parameters are utilized here. |
| 527 int frames_per_buffer = (kAudioSampleRate * chunk_duration_ms) / 1000; | 551 int frames_per_buffer = (kAudioSampleRate * chunk_duration_ms) / 1000; |
| 528 AudioParameters output_parameters = AudioParameters( | 552 AudioParameters output_parameters = AudioParameters( |
| 529 AudioParameters::AUDIO_PCM_LOW_LATENCY, kChannelLayout, kAudioSampleRate, | 553 AudioParameters::AUDIO_PCM_LOW_LATENCY, kChannelLayout, kAudioSampleRate, |
| 530 kNumBitsPerAudioSample, frames_per_buffer); | 554 kNumBitsPerAudioSample, frames_per_buffer); |
| 531 | 555 |
| 556 // Check if using audio track as source (audio data processed on renderer). | |
| 557 // If |StartRecordingUsingAudioTrack()| fails, we abort the recognition. | |
| 558 if (using_audio_track_) | |
|
tommi (sloooow) - chröme
2014/10/02 11:23:19
{}
burnik
2014/10/02 16:04:11
Done.
| |
| 559 return StartRecordingUsingAudioTrack(output_parameters) | |
| 560 ? STATE_STARTING | |
| 561 : Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO)); | |
| 562 | |
| 563 // Get audio data from microphone on the browser. | |
| 564 | |
| 532 // Audio converter will receive audio based on these parameters as input. | 565 // Audio converter will receive audio based on these parameters as input. |
| 533 // On Windows we start by verifying that Core Audio is supported. If not, | 566 // On Windows we start by verifying that Core Audio is supported. If not, |
| 534 // the WaveIn API is used and we might as well avoid all audio conversations | 567 // the WaveIn API is used and we might as well avoid all audio conversations |
| 535 // since WaveIn does the conversion for us. | 568 // since WaveIn does the conversion for us. |
| 536 // TODO(henrika): this code should be moved to platform dependent audio | 569 // TODO(henrika): this code should be moved to platform dependent audio |
| 537 // managers. | 570 // managers. |
| 538 bool use_native_audio_params = true; | 571 bool use_native_audio_params = true; |
| 539 #if defined(OS_WIN) | 572 #if defined(OS_WIN) |
| 540 use_native_audio_params = media::CoreAudioUtil::IsSupported(); | 573 use_native_audio_params = media::CoreAudioUtil::IsSupported(); |
| 541 DVLOG_IF(1, !use_native_audio_params) << "Reverting to WaveIn for WebSpeech"; | 574 DVLOG_IF(1, !use_native_audio_params) << "Reverting to WaveIn for WebSpeech"; |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 578 // The endpointer needs to estimate the environment/background noise before | 611 // The endpointer needs to estimate the environment/background noise before |
| 579 // starting to treat the audio as user input. We wait in the state | 612 // starting to treat the audio as user input. We wait in the state |
| 580 // ESTIMATING_ENVIRONMENT until such interval has elapsed before switching | 613 // ESTIMATING_ENVIRONMENT until such interval has elapsed before switching |
| 581 // to user input mode. | 614 // to user input mode. |
| 582 endpointer_.SetEnvironmentEstimationMode(); | 615 endpointer_.SetEnvironmentEstimationMode(); |
| 583 audio_controller_->Record(); | 616 audio_controller_->Record(); |
| 584 audio_log_->OnStarted(0); | 617 audio_log_->OnStarted(0); |
| 585 return STATE_STARTING; | 618 return STATE_STARTING; |
| 586 } | 619 } |
| 587 | 620 |
| 621 bool SpeechRecognizerImpl::StartRecordingUsingAudioTrack( | |
| 622 const media::AudioParameters& params) { | |
| 623 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 624 // Create the shared memory and share with the renderer process. | |
| 625 uint32 shared_memory_size = sizeof(AudioInputBufferParameters) + | |
| 626 AudioBus::CalculateMemorySize(params); | |
| 627 shared_memory_.reset(new base::SharedMemory()); | |
| 628 if (!shared_memory_->CreateAndMapAnonymous(shared_memory_size)) { | |
| 629 DLOG(ERROR) << "Failed to create shared memory for the speech recognizer"; | |
| 630 return false; | |
| 631 } | |
| 632 base::SharedMemoryHandle foreign_memory_handle; | |
|
no longer working on chromium
2014/10/02 11:55:40
nit, add an empty line
burnik
2014/10/02 16:04:11
Done.
| |
| 633 if (!shared_memory_->ShareToProcess(base::GetCurrentProcessHandle(), | |
| 634 &foreign_memory_handle)) { | |
| 635 DLOG(ERROR) << "Failed to map and share the shared memory"; | |
| 636 return false; | |
|
tommi (sloooow) - chröme
2014/10/02 11:23:19
free shared_memory_?
burnik
2014/10/02 16:04:11
Added:
shared_memory_.reset();
| |
| 637 } | |
| 638 media::AudioInputBuffer* buffer = | |
|
no longer working on chromium
2014/10/02 11:55:40
nit add an empty line.
burnik
2014/10/02 16:04:11
Done.
| |
| 639 static_cast<media::AudioInputBuffer*>(shared_memory_->memory()); | |
| 640 audio_track_bus_ = AudioBus::WrapMemory(params, buffer->audio); | |
| 641 // Use |buffer->params.size| from shared memory to synchronize by counting | |
| 642 // audio buffers when receiving audio data from the renderer. | |
| 643 buffer_index_ = &(buffer->params.size); | |
| 644 *buffer_index_ = 0U; | |
| 645 // Reusing the audio converter only to create chunks from the bus. | |
|
no longer working on chromium
2014/10/02 11:55:40
empty line.
burnik
2014/10/02 16:04:11
Done.
| |
| 646 // Same input and output format indicate no additional conversion here. | |
| 647 audio_converter_.reset(new OnDataConverter(params, params)); | |
| 648 | |
| 649 // The endpointer needs to estimate the environment/background noise before | |
| 650 // starting to treat the audio as user input. We wait in the state | |
| 651 // ESTIMATING_ENVIRONMENT until such interval has elapsed before switching | |
| 652 // to user input mode. | |
| 653 endpointer_.SetEnvironmentEstimationMode(); | |
| 654 | |
| 655 foreign_sync_socket_.reset(new base::CancelableSyncSocket()); | |
| 656 audio_sync_socket_.reset(new base::CancelableSyncSocket()); | |
| 657 if (!base::SyncSocket::CreatePair(audio_sync_socket_.get(), | |
|
no longer working on chromium
2014/10/02 11:55:40
move these sockets code right after the shared mem
burnik
2014/10/02 16:04:11
Done.
| |
| 658 foreign_sync_socket_.get())) { | |
| 659 DLOG(ERROR) << "Cannot create SyncSocket pair"; | |
| 660 return false; | |
|
tommi (sloooow) - chröme
2014/10/02 11:23:19
free resources hitherto allocated?
same for all o
burnik
2014/10/02 16:04:12
Done.
| |
| 661 } | |
| 662 | |
| 663 base::SyncSocket::TransitDescriptor foreign_socket_descriptor; | |
| 664 if (!foreign_sync_socket_->PrepareTransitDescriptor( | |
| 665 peer_process_handle_, &foreign_socket_descriptor)) { | |
| 666 DLOG(ERROR) << "Cannot prepare syncsocket transit descriptor"; | |
| 667 return false; | |
| 668 } | |
| 669 | |
| 670 // The thread check will be first invoked in |OnAudioTrackData()|. | |
| 671 audio_sync_thread_checker_.DetachFromThread(); | |
| 672 audio_sync_thread_.reset( | |
| 673 new base::Thread("SpeechRecognitionAudioTrackThread")); | |
| 674 audio_sync_thread_->Start(); | |
|
no longer working on chromium
2014/10/02 11:55:40
why is it called audio_sync_thread_? how about aud
burnik
2014/10/02 16:04:12
Done. audio_thread_.
I think keyword "track" shou
| |
| 675 | |
| 676 DCHECK_EQ(0U, *buffer_index_); | |
| 677 if (!audio_sync_thread_->message_loop_proxy() | |
|
no longer working on chromium
2014/10/02 11:55:40
add a DCHECK, like:
bool ret = audio_sync_thread_-
burnik
2014/10/02 16:04:11
This will then be an unused variable in the Relase
| |
| 678 ->PostTask(FROM_HERE, | |
| 679 base::Bind(&SpeechRecognizerImpl::OnAudioTrackData, | |
| 680 base::Unretained(this)))) { | |
| 681 DLOG(ERROR) << "Cannot post audio sync thread task"; | |
| 682 return false; | |
| 683 } | |
| 684 | |
| 685 listener()->OnAudioTrackReady(session_id(), params, foreign_memory_handle, | |
|
no longer working on chromium
2014/10/02 11:55:40
OnAudioTrackReady sounds a wrong name, change it t
burnik
2014/10/02 16:04:11
How about OnSharedAudioBusReady or OnSharedBusRead
| |
| 686 foreign_socket_descriptor); | |
| 687 | |
| 688 return true; | |
|
burnik
2014/10/02 16:04:12
Regardin the "bool ret":
I could return ret; here.
| |
| 689 } | |
| 690 | |
| 691 void SpeechRecognizerImpl::OnAudioTrackData() { | |
| 692 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 693 DCHECK(audio_sync_thread_checker_.CalledOnValidThread()); | |
| 694 base::ThreadRestrictions::SetIOAllowed(true); | |
| 695 | |
| 696 // TODO(burnik): We should receive with a timeout here. And when it occurs, | |
| 697 // we should stop the recognition assuming there is no more audio data. | |
| 698 size_t bytes_received = | |
| 699 audio_sync_socket_->Receive(buffer_index_, sizeof(*buffer_index_)); | |
|
no longer working on chromium
2014/10/02 11:55:40
Receive will block the thread. In case there is no
burnik
2014/10/02 16:04:11
This would indicate the renderer is not sending da
no longer working on chromium
2014/10/03 09:13:18
It is better for you do some tests here. Like clos
burnik
2014/10/06 08:32:50
Reimplemented as ReceiveWithTimeout in patchset 2.
| |
| 700 | |
| 701 if (bytes_received != sizeof(*buffer_index_)) { | |
| 702 DCHECK_EQ(bytes_received, 0U); | |
| 703 return; | |
|
no longer working on chromium
2014/10/02 11:55:40
I think this is wrong, you silently stop the audio
burnik
2014/10/02 16:04:11
Are you sure about this? Can a socket receive agai
no longer working on chromium
2014/10/03 08:57:53
Yes, I can sure that once you return here, the OnA
no longer working on chromium
2014/10/03 09:13:18
hmm, I actually answer my own question, if the soc
burnik
2014/10/06 08:32:50
Acknowledged.
| |
| 704 } | |
| 705 | |
| 706 FSMEventArgs event_args(EVENT_AUDIO_DATA); | |
| 707 // Convert audio from native format to fixed format used by WebSpeech. | |
| 708 event_args.audio_data = audio_converter_->Convert(audio_track_bus_.get()); | |
| 709 // Process on browser thread. | |
| 710 BrowserThread::PostTask( | |
| 711 BrowserThread::IO, FROM_HERE, | |
| 712 base::Bind(&SpeechRecognizerImpl::DispatchEvent, this, event_args)); | |
| 713 | |
| 714 // Notify host that the buffer was consumed via shared memory. | |
| 715 ++(*buffer_index_); | |
| 716 | |
| 717 // Post this task for next round of data. | |
| 718 audio_sync_thread_->message_loop_proxy()->PostTask( | |
| 719 FROM_HERE, base::Bind(&SpeechRecognizerImpl::OnAudioTrackData, | |
| 720 base::Unretained(this))); | |
| 721 } | |
| 722 | |
| 588 SpeechRecognizerImpl::FSMState | 723 SpeechRecognizerImpl::FSMState |
| 589 SpeechRecognizerImpl::StartRecognitionEngine(const FSMEventArgs& event_args) { | 724 SpeechRecognizerImpl::StartRecognitionEngine(const FSMEventArgs& event_args) { |
| 590 // This is the first audio packet captured, so the recognition engine is | 725 // This is the first audio packet captured, so the recognition engine is |
| 591 // started and the delegate notified about the event. | 726 // started and the delegate notified about the event. |
| 592 DCHECK(recognition_engine_.get() != NULL); | 727 DCHECK(recognition_engine_.get() != NULL); |
| 593 recognition_engine_->StartRecognition(); | 728 recognition_engine_->StartRecognition(); |
| 594 listener()->OnAudioStart(session_id()); | 729 listener()->OnAudioStart(session_id()); |
| 595 | 730 |
| 596 // This is a little hack, since TakeAudioChunk() is already called by | 731 // This is a little hack, since TakeAudioChunk() is already called by |
| 597 // ProcessAudioPipeline(). It is the best tradeoff, unless we allow dropping | 732 // ProcessAudioPipeline(). It is the best tradeoff, unless we allow dropping |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 628 if (endpointer_.speech_input_complete()) | 763 if (endpointer_.speech_input_complete()) |
| 629 return StopCaptureAndWaitForResult(event_args); | 764 return StopCaptureAndWaitForResult(event_args); |
| 630 return STATE_RECOGNIZING; | 765 return STATE_RECOGNIZING; |
| 631 } | 766 } |
| 632 | 767 |
| 633 SpeechRecognizerImpl::FSMState | 768 SpeechRecognizerImpl::FSMState |
| 634 SpeechRecognizerImpl::StopCaptureAndWaitForResult(const FSMEventArgs&) { | 769 SpeechRecognizerImpl::StopCaptureAndWaitForResult(const FSMEventArgs&) { |
| 635 DCHECK(state_ >= STATE_ESTIMATING_ENVIRONMENT && state_ <= STATE_RECOGNIZING); | 770 DCHECK(state_ >= STATE_ESTIMATING_ENVIRONMENT && state_ <= STATE_RECOGNIZING); |
| 636 | 771 |
| 637 DVLOG(1) << "Concluding recognition"; | 772 DVLOG(1) << "Concluding recognition"; |
| 638 CloseAudioControllerAsynchronously(); | 773 if (!using_audio_track_) CloseAudioControllerAsynchronously(); |
|
tommi (sloooow) - chröme
2014/10/02 11:23:19
two lines
burnik
2014/10/02 16:04:11
Done.
| |
| 639 recognition_engine_->AudioChunksEnded(); | 774 recognition_engine_->AudioChunksEnded(); |
| 640 | 775 |
| 641 if (state_ > STATE_WAITING_FOR_SPEECH) | 776 if (state_ > STATE_WAITING_FOR_SPEECH) |
| 642 listener()->OnSoundEnd(session_id()); | 777 listener()->OnSoundEnd(session_id()); |
| 643 | 778 |
| 644 listener()->OnAudioEnd(session_id()); | 779 listener()->OnAudioEnd(session_id()); |
| 645 return STATE_WAITING_FINAL_RESULT; | 780 return STATE_WAITING_FINAL_RESULT; |
| 646 } | 781 } |
| 647 | 782 |
| 648 SpeechRecognizerImpl::FSMState | 783 SpeechRecognizerImpl::FSMState |
| 649 SpeechRecognizerImpl::AbortSilently(const FSMEventArgs& event_args) { | 784 SpeechRecognizerImpl::AbortSilently(const FSMEventArgs& event_args) { |
| 650 DCHECK_NE(event_args.event, EVENT_AUDIO_ERROR); | 785 DCHECK_NE(event_args.event, EVENT_AUDIO_ERROR); |
| 651 DCHECK_NE(event_args.event, EVENT_ENGINE_ERROR); | 786 DCHECK_NE(event_args.event, EVENT_ENGINE_ERROR); |
| 652 return Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_NONE)); | 787 return Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_NONE)); |
| 653 } | 788 } |
| 654 | 789 |
| 655 SpeechRecognizerImpl::FSMState | 790 SpeechRecognizerImpl::FSMState |
| 656 SpeechRecognizerImpl::AbortWithError(const FSMEventArgs& event_args) { | 791 SpeechRecognizerImpl::AbortWithError(const FSMEventArgs& event_args) { |
| 657 if (event_args.event == EVENT_AUDIO_ERROR) { | 792 if (event_args.event == EVENT_AUDIO_ERROR) { |
| 658 return Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO)); | 793 return Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO)); |
| 659 } else if (event_args.event == EVENT_ENGINE_ERROR) { | 794 } else if (event_args.event == EVENT_ENGINE_ERROR) { |
| 660 return Abort(event_args.engine_error); | 795 return Abort(event_args.engine_error); |
| 661 } | 796 } |
| 662 return Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_ABORTED)); | 797 return Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_ABORTED)); |
| 663 } | 798 } |
| 664 | 799 |
| 665 SpeechRecognizerImpl::FSMState SpeechRecognizerImpl::Abort( | 800 SpeechRecognizerImpl::FSMState SpeechRecognizerImpl::Abort( |
| 666 const SpeechRecognitionError& error) { | 801 const SpeechRecognitionError& error) { |
| 667 if (IsCapturingAudio()) | 802 if (IsCapturingAudio() && !using_audio_track_) |
| 668 CloseAudioControllerAsynchronously(); | 803 CloseAudioControllerAsynchronously(); |
| 669 | 804 |
| 670 DVLOG(1) << "SpeechRecognizerImpl canceling recognition. "; | 805 DVLOG(1) << "SpeechRecognizerImpl canceling recognition. "; |
| 671 | 806 |
| 672 // The recognition engine is initialized only after STATE_STARTING. | 807 // The recognition engine is initialized only after STATE_STARTING. |
| 673 if (state_ > STATE_STARTING) { | 808 if (state_ > STATE_STARTING) { |
| 674 DCHECK(recognition_engine_.get() != NULL); | 809 DCHECK(recognition_engine_.get() != NULL); |
| 675 recognition_engine_->EndRecognition(); | 810 recognition_engine_->EndRecognition(); |
| 676 } | 811 } |
| 677 | 812 |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 763 | 898 |
| 764 SpeechRecognizerImpl::FSMState | 899 SpeechRecognizerImpl::FSMState |
| 765 SpeechRecognizerImpl::NotFeasible(const FSMEventArgs& event_args) { | 900 SpeechRecognizerImpl::NotFeasible(const FSMEventArgs& event_args) { |
| 766 NOTREACHED() << "Unfeasible event " << event_args.event | 901 NOTREACHED() << "Unfeasible event " << event_args.event |
| 767 << " in state " << state_; | 902 << " in state " << state_; |
| 768 return state_; | 903 return state_; |
| 769 } | 904 } |
| 770 | 905 |
| 771 void SpeechRecognizerImpl::CloseAudioControllerAsynchronously() { | 906 void SpeechRecognizerImpl::CloseAudioControllerAsynchronously() { |
| 772 DCHECK(IsCapturingAudio()); | 907 DCHECK(IsCapturingAudio()); |
| 908 DCHECK(!using_audio_track_); | |
|
no longer working on chromium
2014/10/02 11:55:40
what is the alternative of CloseAudioControllerAsy
burnik
2014/10/02 16:04:11
The audio track is managed by the renderer and the
no longer working on chromium
2014/10/03 08:57:53
The audio thread and its relevant resource like sh
burnik
2014/10/06 08:32:50
Acknowledged.
| |
| 773 DVLOG(1) << "SpeechRecognizerImpl closing audio controller."; | 909 DVLOG(1) << "SpeechRecognizerImpl closing audio controller."; |
| 774 // Issues a Close on the audio controller, passing an empty callback. The only | 910 // Issues a Close on the audio controller, passing an empty callback. The only |
| 775 // purpose of such callback is to keep the audio controller refcounted until | 911 // purpose of such callback is to keep the audio controller refcounted until |
| 776 // Close has completed (in the audio thread) and automatically destroy it | 912 // Close has completed (in the audio thread) and automatically destroy it |
| 777 // afterwards (upon return from OnAudioClosed). | 913 // afterwards (upon return from OnAudioClosed). |
| 914 | |
| 778 audio_controller_->Close(base::Bind(&SpeechRecognizerImpl::OnAudioClosed, | 915 audio_controller_->Close(base::Bind(&SpeechRecognizerImpl::OnAudioClosed, |
| 779 this, audio_controller_)); | 916 this, audio_controller_)); |
| 780 audio_controller_ = NULL; // The controller is still refcounted by Bind. | 917 audio_controller_ = NULL; // The controller is still refcounted by Bind. |
| 781 audio_log_->OnClosed(0); | 918 audio_log_->OnClosed(0); |
| 782 } | 919 } |
| 783 | 920 |
| 784 int SpeechRecognizerImpl::GetElapsedTimeMs() const { | 921 int SpeechRecognizerImpl::GetElapsedTimeMs() const { |
| 785 return (num_samples_recorded_ * 1000) / kAudioSampleRate; | 922 return (num_samples_recorded_ * 1000) / kAudioSampleRate; |
| 786 } | 923 } |
| 787 | 924 |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 815 SpeechRecognizerImpl::FSMEventArgs::FSMEventArgs(FSMEvent event_value) | 952 SpeechRecognizerImpl::FSMEventArgs::FSMEventArgs(FSMEvent event_value) |
| 816 : event(event_value), | 953 : event(event_value), |
| 817 audio_data(NULL), | 954 audio_data(NULL), |
| 818 engine_error(SPEECH_RECOGNITION_ERROR_NONE) { | 955 engine_error(SPEECH_RECOGNITION_ERROR_NONE) { |
| 819 } | 956 } |
| 820 | 957 |
| 821 SpeechRecognizerImpl::FSMEventArgs::~FSMEventArgs() { | 958 SpeechRecognizerImpl::FSMEventArgs::~FSMEventArgs() { |
| 822 } | 959 } |
| 823 | 960 |
| 824 } // namespace content | 961 } // namespace content |
| OLD | NEW |