blob: 4f2f9bd04fb84a4619766597777c75665b5641c6 [file] [log] [blame]
// Copyright (c) 2012 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 "media/audio/virtual_audio_input_stream.h"
#include <algorithm>
#include <utility>
#include "base/bind.h"
#include "base/message_loop.h"
#include "media/audio/virtual_audio_output_stream.h"
namespace media {
// LoopbackAudioConverter works similar to AudioConverter and converts input
// streams to different audio parameters. Then, the LoopbackAudioConverter can
// be used as an input to another AudioConverter. This allows us to
// use converted audio from AudioOutputStreams as input to an AudioConverter.
// For example, this allows converting multiple streams into a common format and
// using the converted audio as input to another AudioConverter (i.e. a mixer).
class LoopbackAudioConverter : public AudioConverter::InputCallback {
public:
LoopbackAudioConverter(const AudioParameters& input_params,
const AudioParameters& output_params)
: audio_converter_(input_params, output_params, false) {}
virtual ~LoopbackAudioConverter() {}
void AddInput(AudioConverter::InputCallback* input) {
audio_converter_.AddInput(input);
}
void RemoveInput(AudioConverter::InputCallback* input) {
audio_converter_.RemoveInput(input);
}
private:
virtual double ProvideInput(AudioBus* audio_bus,
base::TimeDelta buffer_delay) OVERRIDE {
audio_converter_.Convert(audio_bus);
return 1.0;
}
AudioConverter audio_converter_;
DISALLOW_COPY_AND_ASSIGN(LoopbackAudioConverter);
};
VirtualAudioInputStream* VirtualAudioInputStream::MakeStream(
AudioManagerBase* manager, const AudioParameters& params,
base::MessageLoopProxy* message_loop) {
return new VirtualAudioInputStream(manager, params, message_loop);
}
VirtualAudioInputStream::VirtualAudioInputStream(
AudioManagerBase* manager, const AudioParameters& params,
base::MessageLoopProxy* message_loop)
: audio_manager_(manager),
message_loop_(message_loop),
callback_(NULL),
buffer_duration_ms_(base::TimeDelta::FromMilliseconds(
params.frames_per_buffer() * base::Time::kMillisecondsPerSecond /
static_cast<float>(params.sample_rate()))),
buffer_(new uint8[params.GetBytesPerBuffer()]),
params_(params),
audio_bus_(AudioBus::Create(params_)),
mixer_(params_, params_, false),
num_attached_outputs_streams_(0) {
}
VirtualAudioInputStream::~VirtualAudioInputStream() {
for (AudioConvertersMap::iterator it = converters_.begin();
it != converters_.end(); ++it)
delete it->second;
DCHECK_EQ(0, num_attached_outputs_streams_);
}
bool VirtualAudioInputStream::Open() {
memset(buffer_.get(), 0, params_.GetBytesPerBuffer());
return true;
}
void VirtualAudioInputStream::Start(AudioInputCallback* callback) {
DCHECK(message_loop_->BelongsToCurrentThread());
callback_ = callback;
on_more_data_cb_.Reset(base::Bind(&VirtualAudioInputStream::ReadAudio,
base::Unretained(this)));
audio_manager_->GetMessageLoop()->PostTask(FROM_HERE,
on_more_data_cb_.callback());
}
void VirtualAudioInputStream::Stop() {
DCHECK(message_loop_->BelongsToCurrentThread());
on_more_data_cb_.Cancel();
}
void VirtualAudioInputStream::AddOutputStream(
VirtualAudioOutputStream* stream, const AudioParameters& output_params) {
DCHECK(message_loop_->BelongsToCurrentThread());
AudioConvertersMap::iterator converter = converters_.find(output_params);
if (converter == converters_.end()) {
std::pair<AudioConvertersMap::iterator, bool> result = converters_.insert(
std::make_pair(output_params,
new LoopbackAudioConverter(output_params, params_)));
converter = result.first;
// Add to main mixer if we just added a new AudioTransform.
mixer_.AddInput(converter->second);
}
converter->second->AddInput(stream);
++num_attached_outputs_streams_;
}
void VirtualAudioInputStream::RemoveOutputStream(
VirtualAudioOutputStream* stream, const AudioParameters& output_params) {
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(converters_.find(output_params) != converters_.end());
converters_[output_params]->RemoveInput(stream);
--num_attached_outputs_streams_;
}
void VirtualAudioInputStream::ReadAudio() {
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(callback_);
mixer_.Convert(audio_bus_.get());
audio_bus_->ToInterleaved(params_.frames_per_buffer(),
params_.bits_per_sample() / 8,
buffer_.get());
callback_->OnData(this,
buffer_.get(),
params_.GetBytesPerBuffer(),
params_.GetBytesPerBuffer(),
1.0);
message_loop_->PostDelayedTask(FROM_HERE,
on_more_data_cb_.callback(),
buffer_duration_ms_);
}
void VirtualAudioInputStream::Close() {
DCHECK(message_loop_->BelongsToCurrentThread());
if (callback_) {
DCHECK(on_more_data_cb_.IsCancelled());
callback_->OnClose(this);
callback_ = NULL;
}
audio_manager_->ReleaseInputStream(this);
}
double VirtualAudioInputStream::GetMaxVolume() {
return 1.0;
}
void VirtualAudioInputStream::SetVolume(double volume) {}
double VirtualAudioInputStream::GetVolume() {
return 1.0;
}
void VirtualAudioInputStream::SetAutomaticGainControl(bool enabled) {}
bool VirtualAudioInputStream::GetAutomaticGainControl() {
return false;
}
} // namespace media