blob: ea3ba7d76ac6e64e88a260ed67ea6ca9a1581322 [file] [log] [blame]
/*
* 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"
#if defined(ENABLE_FAKE_MICROPHONE)
#include "cobalt/speech/microphone_fake.h"
#endif // defined(ENABLE_FAKE_MICROPHONE)
#if defined(SB_USE_SB_MICROPHONE)
#include "cobalt/speech/microphone_starboard.h"
#endif // defined(SB_USE_SB_MICROPHONE)
#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(int sample_rate,
const DataReceivedCallback& data_received,
const CompletionCallback& completion,
const ErrorCallback& error,
const Microphone::Options& options)
: sample_rate_(sample_rate),
data_received_callback_(data_received),
completion_callback_(completion),
error_callback_(error),
#if defined(ENABLE_FAKE_MICROPHONE)
microphone_options_(options),
#endif // defined(ENABLE_FAKE_MICROPHONE)
state_(kStopped),
thread_("microphone_thread") {
UNREFERENCED_PARAMETER(sample_rate_);
#if defined(ENABLE_FAKE_MICROPHONE)
UNREFERENCED_PARAMETER(microphone_options_);
#else
UNREFERENCED_PARAMETER(options);
#endif // defined(ENABLE_FAKE_MICROPHONE)
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;
}
#if defined(SB_USE_SB_MICROPHONE)
#if defined(ENABLE_FAKE_MICROPHONE)
if (microphone_options_.enable_fake_microphone) {
microphone_.reset(new MicrophoneFake(microphone_options_));
} else {
microphone_.reset(
new MicrophoneStarboard(sample_rate_, kBufferSizeInBytes));
}
#else
microphone_.reset(new MicrophoneStarboard(sample_rate_, kBufferSizeInBytes));
#endif // defined(ENABLE_FAKE_MICROPHONE)
#endif // defined(SB_USE_SB_MICROPHONE)
if (microphone_ && microphone_->IsValid()) {
state_ = kStopped;
return true;
} else {
DLOG(WARNING) << "Microphone creation failed.";
microphone_.reset();
state_ = kError;
error_callback_.Run(new SpeechRecognitionError(
SpeechRecognitionError::kAudioCapture, "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(
SpeechRecognitionError::kAborted, "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(
SpeechRecognitionError::kAborted, "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);
static 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));
output_audio_bus->Assign(ShellAudioBus(1, frames, samples));
data_received_callback_.Run(output_audio_bus.Pass());
} else if (read_bytes != 0) {
state_ = kError;
error_callback_.Run(new SpeechRecognitionError(
SpeechRecognitionError::kAborted, "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