blob: 2d47c9115e5fa24dedeb2028545e6fe5a764a23e [file] [log] [blame]
// Copyright (c) 2010 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/filters/audio_renderer_algorithm_ola.h"
#include <algorithm>
#include <cmath>
#include "base/logging.h"
#include "media/base/buffers.h"
namespace media {
// Default window and crossfade lengths in seconds.
const double kDefaultWindowLength = 0.08;
const double kDefaultCrossfadeLength = 0.008;
// Default mute ranges for fast/slow audio. These rates would sound better
// under a frequency domain algorithm.
const float kMinRate = 0.5f;
const float kMaxRate = 4.0f;
AudioRendererAlgorithmOLA::AudioRendererAlgorithmOLA()
: input_step_(0),
output_step_(0),
window_size_(0) {
}
AudioRendererAlgorithmOLA::~AudioRendererAlgorithmOLA() {
}
uint32 AudioRendererAlgorithmOLA::FillBuffer(uint8* dest, uint32 length) {
if (IsQueueEmpty())
return 0;
if (playback_rate() == 0.0f)
return 0;
uint32 dest_written = 0;
// Handle the simple case of normal playback.
if (playback_rate() == 1.0f) {
if (QueueSize() < length)
dest_written = CopyFromInput(dest, QueueSize());
else
dest_written = CopyFromInput(dest, length);
AdvanceInputPosition(dest_written);
return dest_written;
}
// For other playback rates, OLA with crossfade!
while (true) {
// Mute when out of acceptable quality range or when we don't have enough
// data to completely finish this loop.
//
// Note: This may not play at the speed requested as we can only consume as
// much data as we have, and audio timestamps drive the pipeline clock.
//
// Furthermore, we won't end up scaling the very last bit of audio, but
// we're talking about <8ms of audio data.
if (playback_rate() < kMinRate || playback_rate() > kMaxRate ||
QueueSize() < window_size_) {
// Calculate the ideal input/output steps based on the size of the
// destination buffer.
uint32 input_step = static_cast<uint32>(ceil(
static_cast<float>(length * playback_rate())));
uint32 output_step = length;
// If the ideal size is too big, recalculate based on how much is left in
// the queue.
if (input_step > QueueSize()) {
input_step = QueueSize();
output_step = static_cast<uint32>(ceil(
static_cast<float>(input_step / playback_rate())));
}
// Stay aligned and sanity check before writing out zeros.
AlignToSampleBoundary(&input_step);
AlignToSampleBoundary(&output_step);
DCHECK_LE(output_step, length);
if (output_step > length) {
LOG(ERROR) << "OLA: output_step (" << output_step << ") calculated to "
<< "be larger than destination length (" << length << ")";
output_step = length;
}
memset(dest, 0, output_step);
AdvanceInputPosition(input_step);
dest_written += output_step;
break;
}
// Break if we don't have enough room left in our buffer to do a full
// OLA iteration.
if (length < (output_step_ + crossfade_size_)) {
break;
}
// Copy bulk of data to output (including some to crossfade to the next
// copy), then add to our running sum of written data and subtract from
// our tally of remaining requested.
uint32 copied = CopyFromInput(dest, output_step_ + crossfade_size_);
dest_written += copied;
length -= copied;
// Advance pointers for crossfade.
dest += output_step_;
AdvanceInputPosition(input_step_);
// Prepare intermediate buffer.
uint32 crossfade_size;
scoped_array<uint8> src(new uint8[crossfade_size_]);
crossfade_size = CopyFromInput(src.get(), crossfade_size_);
// Calculate number of samples to crossfade, then do so.
int samples = static_cast<int>(crossfade_size / sample_bytes()
/ channels());
switch (sample_bytes()) {
case 4:
Crossfade(samples,
reinterpret_cast<const int32*>(src.get()),
reinterpret_cast<int32*>(dest));
break;
case 2:
Crossfade(samples,
reinterpret_cast<const int16*>(src.get()),
reinterpret_cast<int16*>(dest));
break;
case 1:
Crossfade(samples, src.get(), dest);
break;
default:
NOTREACHED() << "Unsupported audio bit depth sent to OLA algorithm";
}
// Advance pointers again.
AdvanceInputPosition(crossfade_size);
dest += crossfade_size_;
}
return dest_written;
}
void AudioRendererAlgorithmOLA::set_playback_rate(float new_rate) {
AudioRendererAlgorithmBase::set_playback_rate(new_rate);
// Calculate the window size from our default length and our audio properties.
// Precision is not an issue because we will round this to a sample boundary.
// This will not overflow because each parameter is checked in Initialize().
window_size_ = static_cast<uint32>(sample_rate()
* sample_bytes()
* channels()
* kDefaultWindowLength);
// Adjusting step sizes to accommodate requested playback rate.
if (playback_rate() > 1.0f) {
input_step_ = window_size_;
output_step_ = static_cast<uint32>(ceil(
static_cast<float>(window_size_ / playback_rate())));
} else {
input_step_ = static_cast<uint32>(ceil(
static_cast<float>(window_size_ * playback_rate())));
output_step_ = window_size_;
}
AlignToSampleBoundary(&input_step_);
AlignToSampleBoundary(&output_step_);
// Calculate length for crossfading.
crossfade_size_ = static_cast<uint32>(sample_rate()
* sample_bytes()
* channels()
* kDefaultCrossfadeLength);
AlignToSampleBoundary(&crossfade_size_);
if (crossfade_size_ > std::min(input_step_, output_step_)) {
crossfade_size_ = 0;
return;
}
// To keep true to playback rate, modify the steps.
input_step_ -= crossfade_size_;
output_step_ -= crossfade_size_;
}
void AudioRendererAlgorithmOLA::AlignToSampleBoundary(uint32* value) {
(*value) -= ((*value) % (channels() * sample_bytes()));
}
template <class Type>
void AudioRendererAlgorithmOLA::Crossfade(int samples,
const Type* src,
Type* dest) {
Type* dest_end = dest + samples * channels();
const Type* src_end = src + samples * channels();
for (int i = 0; i < samples; ++i) {
double x_ratio = static_cast<double>(i) / static_cast<double>(samples);
for (int j = 0; j < channels(); ++j) {
DCHECK(dest < dest_end);
DCHECK(src < src_end);
(*dest) = static_cast<Type>((*dest) * (1.0 - x_ratio) +
(*src) * x_ratio);
++src;
++dest;
}
}
}
} // namespace media