| // Copyright 2016 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/speech/microphone_manager.h" |
| |
| #include "cobalt/speech/speech_recognition_error.h" |
| |
| namespace cobalt { |
| namespace speech { |
| |
| namespace { |
| // Size of an audio buffer. |
| const int kBufferSizeInBytes = 8 * 1024; |
| // The frequency which we read the data from devices. |
| const float kMicReadRateInHertz = 60.0f; |
| } // namespace |
| |
| MicrophoneManager::MicrophoneManager( |
| const DataReceivedCallback& data_received, |
| const CompletionCallback& completion, const ErrorCallback& error, |
| const MicrophoneCreator& microphone_creator) |
| : data_received_callback_(data_received), |
| completion_callback_(completion), |
| error_callback_(error), |
| microphone_creator_(microphone_creator), |
| state_(kStopped), |
| thread_("microphone_thread") { |
| thread_.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO, 0)); |
| } |
| |
| MicrophoneManager::~MicrophoneManager() { |
| thread_.message_loop()->PostTask( |
| FROM_HERE, |
| base::Bind(&MicrophoneManager::DestroyInternal, base::Unretained(this))); |
| } |
| |
| void MicrophoneManager::Open() { |
| thread_.message_loop()->PostTask( |
| FROM_HERE, |
| base::Bind(&MicrophoneManager::OpenInternal, base::Unretained(this))); |
| } |
| |
| void MicrophoneManager::Close() { |
| thread_.message_loop()->PostTask( |
| FROM_HERE, |
| base::Bind(&MicrophoneManager::CloseInternal, base::Unretained(this))); |
| } |
| |
| bool MicrophoneManager::CreateIfNecessary() { |
| DCHECK(thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| |
| if (microphone_) { |
| return true; |
| } |
| |
| microphone_ = microphone_creator_.Run(kBufferSizeInBytes); |
| if (microphone_ && microphone_->IsValid()) { |
| state_ = kStopped; |
| return true; |
| } else { |
| DLOG(WARNING) << "Microphone creation failed."; |
| microphone_.reset(); |
| state_ = kError; |
| error_callback_.Run(new SpeechRecognitionError( |
| kSpeechRecognitionErrorCodeAudioCapture, "No microphone available.")); |
| return false; |
| } |
| } |
| |
| void MicrophoneManager::OpenInternal() { |
| DCHECK(thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| |
| // Try to create a valid microphone if necessary. |
| if (state_ == kStarted || !CreateIfNecessary()) { |
| return; |
| } |
| |
| DCHECK(microphone_); |
| if (!microphone_->Open()) { |
| state_ = kError; |
| error_callback_.Run(new SpeechRecognitionError( |
| kSpeechRecognitionErrorCodeAborted, "Microphone open failed.")); |
| return; |
| } |
| |
| poll_mic_events_timer_.emplace(); |
| // Setup a timer to poll for input events. |
| poll_mic_events_timer_->Start( |
| FROM_HERE, base::TimeDelta::FromMicroseconds(static_cast<int64>( |
| base::Time::kMicrosecondsPerSecond / kMicReadRateInHertz)), |
| this, &MicrophoneManager::Read); |
| state_ = kStarted; |
| } |
| |
| void MicrophoneManager::CloseInternal() { |
| DCHECK(thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| |
| if (state_ == kStopped) { |
| return; |
| } |
| |
| if (poll_mic_events_timer_) { |
| poll_mic_events_timer_->Stop(); |
| } |
| |
| if (microphone_) { |
| if (!microphone_->Close()) { |
| state_ = kError; |
| error_callback_.Run(new SpeechRecognitionError( |
| kSpeechRecognitionErrorCodeAborted, "Microphone close failed.")); |
| return; |
| } |
| completion_callback_.Run(); |
| state_ = kStopped; |
| } |
| } |
| |
| void MicrophoneManager::Read() { |
| DCHECK(thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| |
| DCHECK(state_ == kStarted); |
| DCHECK(microphone_); |
| DCHECK(microphone_->MinMicrophoneReadInBytes() <= kBufferSizeInBytes); |
| |
| int16_t samples[kBufferSizeInBytes / sizeof(int16_t)]; |
| int read_bytes = |
| microphone_->Read(reinterpret_cast<char*>(samples), kBufferSizeInBytes); |
| // If |read_bytes| is zero, nothing should happen. |
| if (read_bytes > 0 && read_bytes % sizeof(int16_t) == 0) { |
| size_t frames = read_bytes / sizeof(int16_t); |
| scoped_ptr<ShellAudioBus> output_audio_bus(new ShellAudioBus( |
| 1, frames, ShellAudioBus::kInt16, ShellAudioBus::kInterleaved)); |
| ShellAudioBus source(1, frames, samples); |
| output_audio_bus->Assign(source); |
| data_received_callback_.Run(output_audio_bus.Pass()); |
| } else if (read_bytes != 0) { |
| state_ = kError; |
| error_callback_.Run(new SpeechRecognitionError( |
| kSpeechRecognitionErrorCodeAborted, "Microphone read failed.")); |
| poll_mic_events_timer_->Stop(); |
| } |
| } |
| |
| void MicrophoneManager::DestroyInternal() { |
| DCHECK(thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| |
| microphone_.reset(); |
| state_ = kStopped; |
| poll_mic_events_timer_ = base::nullopt; |
| } |
| |
| } // namespace speech |
| } // namespace cobalt |