Index: media/filters/audio_renderer_algorithm_ola.cc |
=================================================================== |
--- media/filters/audio_renderer_algorithm_ola.cc (revision 0) |
+++ media/filters/audio_renderer_algorithm_ola.cc (revision 0) |
@@ -0,0 +1,212 @@ |
+// Copyright (c) 2009 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 <cmath> |
+ |
+#include "media/base/buffers.h" |
+#include "media/base/data_buffer.h" |
+ |
+namespace media { |
+ |
+// Default window size in bytes. |
+// TODO(kylep): base the window size in seconds, not bytes. |
+const size_t kDefaultWindowSize = 4096; |
+ |
+AudioRendererAlgorithmOLA::AudioRendererAlgorithmOLA() |
+ : data_offset_(0), |
+ input_step_(0), |
+ output_step_(0) { |
+} |
+ |
+AudioRendererAlgorithmOLA::~AudioRendererAlgorithmOLA() { |
+} |
+ |
+size_t AudioRendererAlgorithmOLA::FillBuffer(DataBuffer* buffer_out) { |
+ if (IsInputFinished()) |
+ return 0; |
+ if (playback_rate() == 0.0f) |
+ return 0; |
+ |
+ // Grab info from |buffer_out| and handle the simple case of normal playback. |
+ size_t dest_remaining = buffer_out->GetDataSize(); |
+ uint8* dest = buffer_out->GetWritableData(dest_remaining); |
+ size_t dest_written = 0; |
+ if (playback_rate() == 1.0f) { |
+ dest_written = CopyInput(dest, dest_remaining); |
+ AdvanceInput(dest_written); |
+ return dest_written; |
+ } |
+ |
+ // For other playback rates, OLA with crossfade! |
+ // TODO(kylep): Limit the rates to reasonable values. We may want to do this |
+ // on the UI side or in set_playback_rate(). |
+ while (dest_remaining >= output_step_ + crossfade_size_) { |
+ // 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 remaing requested. |
+ size_t copied = CopyInput(dest, output_step_ + crossfade_size_); |
+ dest_written += copied; |
+ dest_remaining -= copied; |
+ |
+ // Advance pointers for crossfade. |
+ dest += output_step_; |
+ AdvanceInput(input_step_); |
+ |
+ // Prepare intermediate buffer. |
+ size_t crossfade_size; |
+ scoped_array<uint8> src(new uint8[crossfade_size_]); |
+ crossfade_size = CopyInput(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. |
+ AdvanceInput(crossfade_size_); |
+ dest += crossfade_size_; |
+ } |
+ return dest_written; |
+} |
+ |
+void AudioRendererAlgorithmOLA::FlushBuffers() { |
+ AudioRendererAlgorithmBase::FlushBuffers(); |
+ saved_buf_ = NULL; |
+} |
+ |
+void AudioRendererAlgorithmOLA::set_playback_rate(float new_rate) { |
+ AudioRendererAlgorithmBase::set_playback_rate(new_rate); |
+ |
+ // Adjusting step sizes to accomodate requested playback rate. |
+ if (playback_rate() > 1.0f) { |
+ input_step_ = kDefaultWindowSize; |
+ output_step_ = static_cast<size_t>(ceil( |
+ static_cast<float>(kDefaultWindowSize / playback_rate()))); |
+ } else { |
+ input_step_ = static_cast<size_t>(ceil( |
+ static_cast<float>(kDefaultWindowSize * playback_rate()))); |
+ output_step_ = kDefaultWindowSize; |
+ } |
+ AlignToSampleBoundary(&input_step_); |
+ AlignToSampleBoundary(&output_step_); |
+ |
+ // Calculate length for crossfading. |
+ crossfade_size_ = kDefaultWindowSize / 10; |
+ AlignToSampleBoundary(&crossfade_size_); |
+ |
+ // To keep true to playback rate, modify the steps. |
+ input_step_ -= crossfade_size_; |
+ output_step_ -= crossfade_size_; |
+} |
+ |
+void AudioRendererAlgorithmOLA::AdvanceInput(size_t bytes) { |
+ if (IsInputFinished()) |
+ return; |
+ |
+ DCHECK(saved_buf_) << "Did you forget to call CopyInput()?"; |
+ |
+ // Calculate number of usable bytes in |saved_buf_|. |
+ size_t saved_buf_remaining = saved_buf_->GetDataSize() - data_offset_; |
+ |
+ // If there is enough data in |saved_buf_| to advance into it, do so. |
+ // Otherwise, advance into the queue. |
+ if (saved_buf_remaining > bytes) { |
+ data_offset_ += bytes; |
+ } else { |
+ if (!IsQueueEmpty()) { |
+ saved_buf_ = FrontQueue(); |
+ PopFrontQueue(); |
+ } else { |
+ saved_buf_ = NULL; |
+ } |
+ // TODO(kylep): Make this function loop to eliminate the DCHECK. |
+ DCHECK_GE(bytes, saved_buf_remaining); |
+ |
+ data_offset_ = bytes - saved_buf_remaining; |
+ } |
+} |
+ |
+void AudioRendererAlgorithmOLA::AlignToSampleBoundary(size_t* value) { |
+ (*value) -= ((*value) % (channels() * sample_bytes())); |
+} |
+ |
+// TODO(kylep): Make this function loop to satisfy requests better. |
+size_t AudioRendererAlgorithmOLA::CopyInput(uint8* dest, size_t length) { |
+ if (IsInputFinished()) |
+ return 0; |
+ |
+ // Lazy initialization. |
+ if (!saved_buf_) { |
+ saved_buf_ = FrontQueue(); |
+ PopFrontQueue(); |
+ } |
+ |
+ size_t dest_written = 0; |
+ size_t data_length = saved_buf_->GetDataSize() - data_offset_; |
+ |
+ // Prevent writing past end of the buffer. |
+ if (data_length > length) |
+ data_length = length; |
+ memcpy(dest, saved_buf_->GetData() + data_offset_, data_length); |
+ |
+ dest += data_length; |
+ length -= data_length; |
+ dest_written += data_length; |
+ |
+ if (length > 0) { |
+ // We should have enough data in the next buffer so long as the |
+ // queue is not empty. |
+ if (IsQueueEmpty()) |
+ return dest_written; |
+ DCHECK_LE(length, FrontQueue()->GetDataSize()); |
+ |
+ memcpy(dest, FrontQueue()->GetData(), length); |
+ dest_written += length; |
+ } |
+ |
+ return dest_written; |
+} |
+ |
+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; |
+ } |
+ } |
+} |
+ |
+bool AudioRendererAlgorithmOLA::IsInputFinished() { |
+ return !saved_buf_ && IsQueueEmpty(); |
+} |
+ |
+} // namespace media |
Property changes on: media\filters\audio_renderer_algorithm_ola.cc |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |