| // 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), |
| crossfade_size_(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 |