diff options
author | vrk@google.com <vrk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-19 19:02:53 +0000 |
---|---|---|
committer | vrk@google.com <vrk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-19 19:02:53 +0000 |
commit | 948d3c6675d7e22c0fdf7d80ff5a1f8b5a8f2892 (patch) | |
tree | 7123343248409e61e6afd0cdaa756baa774c7269 /media | |
parent | 976327c504a0f80b09ce7403f8cd653c4a7576ae (diff) | |
download | chromium_src-948d3c6675d7e22c0fdf7d80ff5a1f8b5a8f2892.zip chromium_src-948d3c6675d7e22c0fdf7d80ff5a1f8b5a8f2892.tar.gz chromium_src-948d3c6675d7e22c0fdf7d80ff5a1f8b5a8f2892.tar.bz2 |
Consolidate AudioRendererAlgorithm classes into one class
There's no reason to have 2 derived classes of AudioRendererAlgorithmBase.
This CL deletes the derived classes and pushes the behavior into the base
class. It shouldn't change any existing logic; should be just pushing
things around.
BUG=106492
TEST=media_unittests
Review URL: http://codereview.chromium.org/8879041
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@115006 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/filters/audio_renderer_algorithm_base.cc | 215 | ||||
-rw-r--r-- | media/filters/audio_renderer_algorithm_base.h | 98 | ||||
-rw-r--r-- | media/filters/audio_renderer_algorithm_base_unittest.cc (renamed from media/filters/audio_renderer_algorithm_ola_unittest.cc) | 57 | ||||
-rw-r--r-- | media/filters/audio_renderer_algorithm_default.cc | 51 | ||||
-rw-r--r-- | media/filters/audio_renderer_algorithm_default.h | 31 | ||||
-rw-r--r-- | media/filters/audio_renderer_algorithm_ola.cc | 207 | ||||
-rw-r--r-- | media/filters/audio_renderer_algorithm_ola.h | 70 | ||||
-rw-r--r-- | media/filters/audio_renderer_base.cc | 5 | ||||
-rw-r--r-- | media/media.gyp | 17 | ||||
-rw-r--r-- | media/tools/wav_ola_test/wav_ola_test.cc | 174 |
10 files changed, 268 insertions, 657 deletions
diff --git a/media/filters/audio_renderer_algorithm_base.cc b/media/filters/audio_renderer_algorithm_base.cc index affebf5..2a05a24 100644 --- a/media/filters/audio_renderer_algorithm_base.cc +++ b/media/filters/audio_renderer_algorithm_base.cc @@ -4,6 +4,9 @@ #include "media/filters/audio_renderer_algorithm_base.h" +#include <algorithm> +#include <cmath> + #include "base/logging.h" #include "media/base/buffers.h" @@ -12,10 +15,21 @@ namespace media { // The size in bytes we try to maintain for the |queue_|. Previous usage // maintained a deque of 16 Buffers, each of size 4Kb. This worked well, so we // maintain this number of bytes (16 * 4096). -const int kDefaultMinQueueSizeInBytes = 65536; -const int kDefaultMinQueueSizeInMilliseconds = 372; // ~64kb @ 44.1k stereo -const int kDefaultMaxQueueSizeInBytes = 4608000; // 3 seconds @ 96kHz 7.1 -const int kDefaultMaxQueueSizeInMilliseconds = 3000; +static const int kDefaultMinQueueSizeInBytes = 65536; +// ~64kb @ 44.1k stereo. +static const int kDefaultMinQueueSizeInMilliseconds = 372; +// 3 seconds @ 96kHz 7.1. +static const int kDefaultMaxQueueSizeInBytes = 4608000; +static const int kDefaultMaxQueueSizeInMilliseconds = 3000; + +// Default window and crossfade lengths in seconds. +static const double kDefaultWindowLength = 0.08; +static const double kDefaultCrossfadeLength = 0.008; + +// Default mute ranges for fast/slow audio. These rates would sound better +// under a frequency domain algorithm. +static const float kMinRate = 0.5f; +static const float kMaxRate = 4.0f; AudioRendererAlgorithmBase::AudioRendererAlgorithmBase() : channels_(0), @@ -23,7 +37,179 @@ AudioRendererAlgorithmBase::AudioRendererAlgorithmBase() sample_bytes_(0), playback_rate_(0.0f), queue_(0, kDefaultMinQueueSizeInBytes), - max_queue_capacity_(kDefaultMaxQueueSizeInBytes) { + max_queue_capacity_(kDefaultMaxQueueSizeInBytes), + input_step_(0), + output_step_(0), + crossfade_size_(0), + window_size_(0) { +} + +uint32 AudioRendererAlgorithmBase::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 AudioRendererAlgorithmBase::SetPlaybackRate(float new_rate) { + DCHECK_GE(new_rate, 0.0); + 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 AudioRendererAlgorithmBase::AlignToSampleBoundary(uint32* value) { + (*value) -= ((*value) % (channels_ * sample_bytes_)); +} + +template <class Type> +void AudioRendererAlgorithmBase::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; + } + } } AudioRendererAlgorithmBase::~AudioRendererAlgorithmBase() {} @@ -58,7 +244,7 @@ void AudioRendererAlgorithmBase::Initialize( request_read_callback_ = callback; - set_playback_rate(initial_playback_rate); + SetPlaybackRate(initial_playback_rate); } void AudioRendererAlgorithmBase::FlushBuffers() { @@ -85,11 +271,6 @@ float AudioRendererAlgorithmBase::playback_rate() { return playback_rate_; } -void AudioRendererAlgorithmBase::set_playback_rate(float new_rate) { - DCHECK_GE(new_rate, 0.0); - playback_rate_ = new_rate; -} - bool AudioRendererAlgorithmBase::IsQueueEmpty() { return queue_.forward_bytes() == 0; } @@ -125,16 +306,4 @@ uint32 AudioRendererAlgorithmBase::CopyFromInput(uint8* dest, uint32 bytes) { return queue_.Peek(dest, bytes); } -int AudioRendererAlgorithmBase::channels() { - return channels_; -} - -int AudioRendererAlgorithmBase::sample_rate() { - return sample_rate_; -} - -int AudioRendererAlgorithmBase::sample_bytes() { - return sample_bytes_; -} - } // namespace media diff --git a/media/filters/audio_renderer_algorithm_base.h b/media/filters/audio_renderer_algorithm_base.h index af6381d..c6888c2 100644 --- a/media/filters/audio_renderer_algorithm_base.h +++ b/media/filters/audio_renderer_algorithm_base.h @@ -2,23 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// AudioRendererAlgorithmBase provides an interface for algorithms that modify -// playback speed. ARAB owns a BufferQueue which hides Buffer boundaries from -// subclasses and allows them to access data by byte. Subclasses must implement -// the following method: +// AudioRendererAlgorithmBase buffers and transforms audio data. The owner of +// this object provides audio data to the object through EnqueueBuffer() and +// requests data from the buffer via FillBuffer(). The owner also sets the +// playback rate, and the AudioRendererAlgorithm will stretch or compress the +// buffered audio as necessary to match the playback rate when fulfilling +// FillBuffer() requests. AudioRendererAlgorithm can request more data to be +// buffered via a read callback passed in during initialization. // -// FillBuffer() - fills the buffer passed to it & returns how many bytes -// copied. +// This class is *not* thread-safe. Calls to enqueue and retrieve data must be +// locked if called from multiple threads. // -// The general assumption is that the owner of this class will provide us with -// Buffers and a playback speed, and we will fill an output buffer when our -// owner requests it. If we needs more Buffers, we will query our owner via -// a callback passed during construction. This should be a nonblocking call. -// When the owner has a Buffer ready for us, it calls EnqueueBuffer(). +// AudioRendererAlgorithmBase uses a simple pitch-preservation algorithm to +// stretch and compress audio data to meet playback speeds less than and +// greater than the natural playback of the audio stream. // -// Exectution of ARAB is thread-unsafe. This class should be used as -// the guts of AudioRendererBase, which should lock calls into us so -// enqueues and processes do not cause an unpredictable |queue_| size. +// Audio at very low or very high playback rates are muted to preserve quality. #ifndef MEDIA_FILTERS_AUDIO_RENDERER_ALGORITHM_BASE_H_ #define MEDIA_FILTERS_AUDIO_RENDERER_ALGORITHM_BASE_H_ @@ -26,6 +25,7 @@ #include <deque> #include "base/callback.h" +#include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "media/base/seekable_buffer.h" @@ -37,48 +37,47 @@ class Buffer; class MEDIA_EXPORT AudioRendererAlgorithmBase { public: AudioRendererAlgorithmBase(); - virtual ~AudioRendererAlgorithmBase(); + ~AudioRendererAlgorithmBase(); // Checks validity of audio parameters and takes ownership of |callback|. - virtual void Initialize(int channels, + void Initialize(int channels, int sample_rate, int sample_bits, float initial_playback_rate, const base::Closure& callback); - // Implement this strategy method in derived classes. Tries to fill |length| - // bytes of |dest| with possibly scaled data from our |queue_|. Returns the - // number of bytes copied into |dest|. - virtual uint32 FillBuffer(uint8* dest, uint32 length) = 0; + // Tries to fill |length| bytes of |dest| with possibly scaled data from + // our |queue_|. Returns the number of bytes copied into |dest|. + uint32 FillBuffer(uint8* dest, uint32 length); // Clears |queue_|. - virtual void FlushBuffers(); + void FlushBuffers(); // Returns the time of the next byte in our data or kNoTimestamp if current // time is unknown. - virtual base::TimeDelta GetTime(); + base::TimeDelta GetTime(); // Enqueues a buffer. It is called from the owner of the algorithm after a // read completes. - virtual void EnqueueBuffer(Buffer* buffer_in); + void EnqueueBuffer(Buffer* buffer_in); // Getter/setter for |playback_rate_|. - virtual float playback_rate(); - virtual void set_playback_rate(float new_rate); + float playback_rate(); + void SetPlaybackRate(float new_rate); // Returns whether |queue_| is empty. - virtual bool IsQueueEmpty(); + bool IsQueueEmpty(); // Returns true if we have enough data - virtual bool IsQueueFull(); + bool IsQueueFull(); // Returns the number of bytes left in |queue_|. - virtual uint32 QueueSize(); + uint32 QueueSize(); // Increase the capacity of |queue_| if possible. - virtual void IncreaseQueueCapacity(); + void IncreaseQueueCapacity(); - protected: + private: // Advances |queue_|'s internal pointer by |bytes|. void AdvanceInputPosition(uint32 bytes); @@ -90,16 +89,25 @@ class MEDIA_EXPORT AudioRendererAlgorithmBase { // the current sample rate, channel count, and bytes per sample. int DurationToBytes(int duration_in_milliseconds) const; - // Number of audio channels. - virtual int channels(); - - // Sample rate in hertz. - virtual int sample_rate(); + FRIEND_TEST_ALL_PREFIXES(AudioRendererAlgorithmBaseTest, + FillBuffer_NormalRate); + FRIEND_TEST_ALL_PREFIXES(AudioRendererAlgorithmBaseTest, + FillBuffer_DoubleRate); + FRIEND_TEST_ALL_PREFIXES(AudioRendererAlgorithmBaseTest, + FillBuffer_HalfRate); + FRIEND_TEST_ALL_PREFIXES(AudioRendererAlgorithmBaseTest, + FillBuffer_QuarterRate); + + // Aligns |value| to a channel and sample boundary. + void AlignToSampleBoundary(uint32* value); + + // Crossfades |samples| samples of |dest| with the data in |src|. Assumes + // there is room in |dest| and enough data in |src|. Type is the datatype + // of a data point in the waveform (i.e. uint8, int16, int32, etc). Also, + // sizeof(one sample) == sizeof(Type) * channels. + template <class Type> + void Crossfade(int samples, const Type* src, Type* dest); - // Number of bytes per sample per channel. - virtual int sample_bytes(); - - private: // Audio properties. int channels_; int sample_rate_; @@ -117,6 +125,18 @@ class MEDIA_EXPORT AudioRendererAlgorithmBase { // Largest capacity queue_ can grow to. size_t max_queue_capacity_; + // Members for ease of calculation in FillBuffer(). These members are based + // on |playback_rate_|, but are stored separately so they don't have to be + // recalculated on every call to FillBuffer(). + uint32 input_step_; + uint32 output_step_; + + // Length for crossfade in bytes. + uint32 crossfade_size_; + + // Window size, in bytes (calculated from audio properties). + uint32 window_size_; + DISALLOW_COPY_AND_ASSIGN(AudioRendererAlgorithmBase); }; diff --git a/media/filters/audio_renderer_algorithm_ola_unittest.cc b/media/filters/audio_renderer_algorithm_base_unittest.cc index 39d89c3..489182e 100644 --- a/media/filters/audio_renderer_algorithm_ola_unittest.cc +++ b/media/filters/audio_renderer_algorithm_base_unittest.cc @@ -11,7 +11,7 @@ #include "base/bind.h" #include "base/callback.h" #include "media/base/data_buffer.h" -#include "media/filters/audio_renderer_algorithm_ola.h" +#include "media/filters/audio_renderer_algorithm_base.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -19,30 +19,16 @@ using ::testing::AnyNumber; namespace media { -class MockDataProvider { - public: - MockDataProvider() {} - - MOCK_METHOD0(Read, void()); - - private: - DISALLOW_COPY_AND_ASSIGN(MockDataProvider); -}; - static const int kChannels = 1; static const int kSampleRate = 1000; static const int kSampleBits = 8; +static void DoNothing() {} -TEST(AudioRendererAlgorithmOLATest, FillBuffer_NormalRate) { +TEST(AudioRendererAlgorithmBaseTest, FillBuffer_NormalRate) { // When playback rate == 1.0f: straight copy of whatever is in |queue_|. - MockDataProvider mock; - AudioRendererAlgorithmOLA algorithm; + AudioRendererAlgorithmBase algorithm; algorithm.Initialize(kChannels, kSampleRate, kSampleBits, 1.0f, - base::Bind(&MockDataProvider::Read, - base::Unretained(&mock))); - - // We won't reply to any read requests. - EXPECT_CALL(mock, Read()).Times(AnyNumber()); + base::Bind(&DoNothing)); // Enqueue a buffer of any size since it doesn't matter. const size_t kDataSize = 1024; @@ -55,16 +41,11 @@ TEST(AudioRendererAlgorithmOLATest, FillBuffer_NormalRate) { EXPECT_EQ(0u, algorithm.QueueSize()); } -TEST(AudioRendererAlgorithmOLATest, FillBuffer_DoubleRate) { +TEST(AudioRendererAlgorithmBaseTest, FillBuffer_DoubleRate) { // When playback rate > 1.0f: input is read faster than output is written. - MockDataProvider mock; - AudioRendererAlgorithmOLA algorithm; + AudioRendererAlgorithmBase algorithm; algorithm.Initialize(kChannels, kSampleRate, kSampleBits, 2.0f, - base::Bind(&MockDataProvider::Read, - base::Unretained(&mock))); - - // We won't reply to any read requests. - EXPECT_CALL(mock, Read()).Times(AnyNumber()); + base::Bind(&DoNothing)); // First parameter is the input buffer size, second parameter is how much data // we expect to consume in order to have no data left in the |algorithm|. @@ -92,16 +73,11 @@ TEST(AudioRendererAlgorithmOLATest, FillBuffer_DoubleRate) { } } -TEST(AudioRendererAlgorithmOLATest, FillBuffer_HalfRate) { +TEST(AudioRendererAlgorithmBaseTest, FillBuffer_HalfRate) { // When playback rate < 1.0f: input is read slower than output is written. - MockDataProvider mock; - AudioRendererAlgorithmOLA algorithm; + AudioRendererAlgorithmBase algorithm; algorithm.Initialize(kChannels, kSampleRate, kSampleBits, 0.5f, - base::Bind(&MockDataProvider::Read, - base::Unretained(&mock))); - - // We won't reply to any read requests. - EXPECT_CALL(mock, Read()).Times(AnyNumber()); + base::Bind(&DoNothing)); // First parameter is the input buffer size, second parameter is how much data // we expect to consume in order to have no data left in the |algorithm|. @@ -129,16 +105,11 @@ TEST(AudioRendererAlgorithmOLATest, FillBuffer_HalfRate) { } } -TEST(AudioRendererAlgorithmOLATest, FillBuffer_QuarterRate) { +TEST(AudioRendererAlgorithmBaseTest, FillBuffer_QuarterRate) { // When playback rate is very low the audio is simply muted. - MockDataProvider mock; - AudioRendererAlgorithmOLA algorithm; + AudioRendererAlgorithmBase algorithm; algorithm.Initialize(kChannels, kSampleRate, kSampleBits, 0.25f, - base::Bind(&MockDataProvider::Read, - base::Unretained(&mock))); - - // We won't reply to any read requests. - EXPECT_CALL(mock, Read()).Times(AnyNumber()); + base::Bind(&DoNothing)); // First parameter is the input buffer size, second parameter is how much data // we expect to consume in order to have no data left in the |algorithm|. diff --git a/media/filters/audio_renderer_algorithm_default.cc b/media/filters/audio_renderer_algorithm_default.cc deleted file mode 100644 index 7ebc10e..0000000 --- a/media/filters/audio_renderer_algorithm_default.cc +++ /dev/null @@ -1,51 +0,0 @@ -// 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_default.h" - -#include "media/base/buffers.h" - -namespace media { - -AudioRendererAlgorithmDefault::AudioRendererAlgorithmDefault() { -} - -AudioRendererAlgorithmDefault::~AudioRendererAlgorithmDefault() { -} - -uint32 AudioRendererAlgorithmDefault::FillBuffer(uint8* dest, uint32 length) { - if (playback_rate() == 0.0f) { - return 0; - } - - uint32 dest_written = 0; - - if (playback_rate() == 1.0f) { - // If we don't have enough data, copy what we have. - if (QueueSize() < length) - dest_written = CopyFromInput(dest, QueueSize()); - else - dest_written = CopyFromInput(dest, length); - AdvanceInputPosition(dest_written); - } else { - // Mute (we will write to the whole buffer, so set |dest_written| to the - // requested size). - dest_written = length; - memset(dest, 0, dest_written); - - // Calculate the number of bytes we "used". - uint32 scaled_dest_length = - static_cast<uint32>(dest_written * playback_rate()); - - // If this is more than we have, report the correct amount consumed. - if (QueueSize() < scaled_dest_length) { - scaled_dest_length = QueueSize(); - dest_written = static_cast<size_t>(scaled_dest_length / playback_rate()); - } - AdvanceInputPosition(scaled_dest_length); - } - return dest_written; -} - -} // namespace media diff --git a/media/filters/audio_renderer_algorithm_default.h b/media/filters/audio_renderer_algorithm_default.h deleted file mode 100644 index 7950542..0000000 --- a/media/filters/audio_renderer_algorithm_default.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2011 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. - -// AudioRendererAlgorithmDefault is the default implementation of -// AudioRendererAlgorithmBase. For speeds other than 1.0f, FillBuffer() -// fills |buffer_out| with 0s and returns the expected size. As ARAB is -// thread-unsafe, so is ARAD. - -#ifndef MEDIA_FILTERS_AUDIO_RENDERER_ALGORITHM_DEFAULT_H_ -#define MEDIA_FILTERS_AUDIO_RENDERER_ALGORITHM_DEFAULT_H_ - -#include "media/filters/audio_renderer_algorithm_base.h" - -namespace media { - -class AudioRendererAlgorithmDefault : public AudioRendererAlgorithmBase { - public: - AudioRendererAlgorithmDefault(); - virtual ~AudioRendererAlgorithmDefault(); - - // AudioRendererAlgorithmBase implementation - virtual uint32 FillBuffer(uint8* dest, uint32 length) OVERRIDE; - - private: - DISALLOW_COPY_AND_ASSIGN(AudioRendererAlgorithmDefault); -}; - -} // namespace media - -#endif // MEDIA_FILTERS_AUDIO_RENDERER_ALGORITHM_DEFAULT_H_ diff --git a/media/filters/audio_renderer_algorithm_ola.cc b/media/filters/audio_renderer_algorithm_ola.cc deleted file mode 100644 index db0f348..0000000 --- a/media/filters/audio_renderer_algorithm_ola.cc +++ /dev/null @@ -1,207 +0,0 @@ -// 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 diff --git a/media/filters/audio_renderer_algorithm_ola.h b/media/filters/audio_renderer_algorithm_ola.h deleted file mode 100644 index 8cefcbf..0000000 --- a/media/filters/audio_renderer_algorithm_ola.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2011 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. - -// AudioRendererAlgorithmOLA [ARAO] is the pitch-preservation implementation of -// AudioRendererAlgorithmBase [ARAB]. For speeds greater than 1.0f, FillBuffer() -// consumes more input data than output data requested and crossfades samples -// to fill |buffer_out|. For speeds less than 1.0f, FillBuffer() consumers less -// input data than output data requested and draws overlapping samples from the -// input data to fill |buffer_out|. ARAO will mute the audio for very high or -// very low playback rates to preserve quality. As ARAB is thread-unsafe, so is -// ARAO. - -#ifndef MEDIA_FILTERS_AUDIO_RENDERER_ALGORITHM_OLA_H_ -#define MEDIA_FILTERS_AUDIO_RENDERER_ALGORITHM_OLA_H_ - -#include "base/gtest_prod_util.h" -#include "media/filters/audio_renderer_algorithm_base.h" - -namespace media { - -class MEDIA_EXPORT AudioRendererAlgorithmOLA - : public AudioRendererAlgorithmBase { - public: - AudioRendererAlgorithmOLA(); - virtual ~AudioRendererAlgorithmOLA(); - - // AudioRendererAlgorithmBase implementation - virtual uint32 FillBuffer(uint8* dest, uint32 length) OVERRIDE; - - virtual void set_playback_rate(float new_rate) OVERRIDE; - - private: - FRIEND_TEST_ALL_PREFIXES(AudioRendererAlgorithmOLATest, - FillBuffer_NormalRate); - FRIEND_TEST_ALL_PREFIXES(AudioRendererAlgorithmOLATest, - FillBuffer_DoubleRate); - FRIEND_TEST_ALL_PREFIXES(AudioRendererAlgorithmOLATest, - FillBuffer_HalfRate); - FRIEND_TEST_ALL_PREFIXES(AudioRendererAlgorithmOLATest, - FillBuffer_QuarterRate); - - // Aligns |value| to a channel and sample boundary. - void AlignToSampleBoundary(uint32* value); - - // Crossfades |samples| samples of |dest| with the data in |src|. Assumes - // there is room in |dest| and enough data in |src|. Type is the datatype - // of a data point in the waveform (i.e. uint8, int16, int32, etc). Also, - // sizeof(one sample) == sizeof(Type) * channels. - template <class Type> - void Crossfade(int samples, const Type* src, Type* dest); - - // Members for ease of calculation in FillBuffer(). These members are based - // on |playback_rate_|, but are stored separately so they don't have to be - // recalculated on every call to FillBuffer(). - uint32 input_step_; - uint32 output_step_; - - // Length for crossfade in bytes. - uint32 crossfade_size_; - - // Window size, in bytes (calculated from audio properties). - uint32 window_size_; - - DISALLOW_COPY_AND_ASSIGN(AudioRendererAlgorithmOLA); -}; - -} // namespace media - -#endif // MEDIA_FILTERS_AUDIO_RENDERER_ALGORITHM_OLA_H_ diff --git a/media/filters/audio_renderer_base.cc b/media/filters/audio_renderer_base.cc index 7960b07..03c9ed0 100644 --- a/media/filters/audio_renderer_base.cc +++ b/media/filters/audio_renderer_base.cc @@ -11,7 +11,6 @@ #include "base/callback.h" #include "base/logging.h" #include "media/base/filter_host.h" -#include "media/filters/audio_renderer_algorithm_ola.h" namespace media { @@ -103,7 +102,7 @@ void AudioRendererBase::Initialize(AudioDecoder* decoder, base::Closure cb = base::Bind(&AudioRendererBase::ScheduleRead_Locked, this); // Construct the algorithm. - algorithm_.reset(new AudioRendererAlgorithmOLA()); + algorithm_.reset(new AudioRendererAlgorithmBase()); // Initialize our algorithm with media properties, initial playback rate, // and a callback to request more reads from the data source. @@ -286,7 +285,7 @@ void AudioRendererBase::ScheduleRead_Locked() { } void AudioRendererBase::SetPlaybackRate(float playback_rate) { - algorithm_->set_playback_rate(playback_rate); + algorithm_->SetPlaybackRate(playback_rate); } float AudioRendererBase::GetPlaybackRate() { diff --git a/media/media.gyp b/media/media.gyp index 3fba5d5..27b9dd2 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -175,10 +175,6 @@ 'filters/audio_file_reader.h', 'filters/audio_renderer_algorithm_base.cc', 'filters/audio_renderer_algorithm_base.h', - 'filters/audio_renderer_algorithm_default.cc', - 'filters/audio_renderer_algorithm_default.h', - 'filters/audio_renderer_algorithm_ola.cc', - 'filters/audio_renderer_algorithm_ola.h', 'filters/audio_renderer_base.cc', 'filters/audio_renderer_base.h', 'filters/bitstream_converter.cc', @@ -615,7 +611,7 @@ 'base/video_util_unittest.cc', 'base/yuv_convert_unittest.cc', 'ffmpeg/ffmpeg_common_unittest.cc', - 'filters/audio_renderer_algorithm_ola_unittest.cc', + 'filters/audio_renderer_algorithm_base_unittest.cc', 'filters/audio_renderer_base_unittest.cc', 'filters/bitstream_converter_unittest.cc', 'filters/chunk_demuxer_unittest.cc', @@ -705,17 +701,6 @@ ], }, { - 'target_name': 'wav_ola_test', - 'type': 'executable', - 'dependencies': [ - 'media', - '../base/base.gyp:base', - ], - 'sources': [ - 'tools/wav_ola_test/wav_ola_test.cc' - ], - }, - { 'target_name': 'qt_faststart', 'type': 'executable', 'sources': [ diff --git a/media/tools/wav_ola_test/wav_ola_test.cc b/media/tools/wav_ola_test/wav_ola_test.cc deleted file mode 100644 index 42517f1..0000000 --- a/media/tools/wav_ola_test/wav_ola_test.cc +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (c) 2011 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. - -// This application is a test for AudioRendererAlgorithmOLA. It reads in a -// specified wav file (so far only 8, 16 and 32 bit are supported) and uses -// ARAO to scale the playback by a specified rate. Then it outputs the result -// to the specified location. Command line calls should be as follows: -// -// wav_ola_test RATE INFILE OUTFILE - -#include <iostream> -#include <string> - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/file_util.h" -#include "base/memory/ref_counted.h" -#include "base/string_number_conversions.h" -#include "base/string_util.h" -#include "base/utf_string_conversions.h" -#include "media/base/data_buffer.h" -#include "media/filters/audio_renderer_algorithm_ola.h" - -using file_util::ScopedFILE; -using media::AudioRendererAlgorithmOLA; -using media::DataBuffer; - -const double kDefaultWindowLength = 0.08; - -struct WavHeader { - int32 riff; - int32 chunk_size; - char unused0[8]; - int32 subchunk1_size; - int16 audio_format; - int16 channels; - int32 sample_rate; - char unused1[6]; - int16 bit_rate; - char unused2[4]; - int32 subchunk2_size; -}; - -// Dummy class to feed data to OLA algorithm. Necessary to create callback. -class Dummy { - public: - Dummy(FILE* in, AudioRendererAlgorithmOLA* ola, size_t window_size) - : input_(in), - ola_(ola), - window_size_(window_size) { - } - - void ReadDataForAlg() { - scoped_refptr<DataBuffer> buffer(new DataBuffer(window_size_)); - buffer->SetDataSize(window_size_); - uint8* buf = buffer->GetWritableData(); - if (fread(buf, 1, window_size_, input_) > 0) { - ola_->EnqueueBuffer(buffer.get()); - } - } - - private: - FILE* input_; - AudioRendererAlgorithmOLA* ola_; - size_t window_size_; - - DISALLOW_COPY_AND_ASSIGN(Dummy); -}; - -int main(int argc, const char** argv) { - AudioRendererAlgorithmOLA ola; - CommandLine::Init(argc, argv); - const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); - - const CommandLine::StringVector& filenames = cmd_line->GetArgs(); - if (filenames.empty()) { - std::cerr << "Usage: " << argv[0] << " RATE INFILE OUTFILE\n" - << std::endl; - return 1; - } - - // Retrieve command line options. - FilePath in_path(filenames[1]); - FilePath out_path(filenames[2]); - double playback_rate = 0.0; - - // Determine speed of rerecord. -#if defined(OS_WIN) - std::string filename_str = WideToASCII(filenames[0]); -#else - const std::string& filename_str = filenames[0]; -#endif - if (!base::StringToDouble(filename_str, &playback_rate)) - playback_rate = 0.0; - - // Open input file. - ScopedFILE input(file_util::OpenFile(in_path, "rb")); - if (!(input.get())) { - LOG(ERROR) << "could not open input"; - return 1; - } - - // Open output file. - ScopedFILE output(file_util::OpenFile(out_path, "wb")); - if (!(output.get())) { - LOG(ERROR) << "could not open output"; - return 1; - } - - // Read in header. - WavHeader wav; - if (fread(&wav, sizeof(wav), 1, input.get()) < 1) { - LOG(ERROR) << "could not read WAV header"; - return 1; - } - - size_t window_size = static_cast<size_t>(wav.sample_rate - * (wav.bit_rate / 8) - * wav.channels - * kDefaultWindowLength); - - // Instantiate dummy class and callback to feed data to |ola|. - Dummy guy(input.get(), &ola, window_size); - base::Closure cb = base::Bind(&Dummy::ReadDataForAlg, base::Unretained(&guy)); - ola.Initialize(wav.channels, - wav.sample_rate, - wav.bit_rate, - static_cast<float>(playback_rate), - cb); - ola.FlushBuffers(); - - // Print out input format. - std::cout << in_path.value() << "\n" - << "Channels: " << wav.channels << "\n" - << "Sample Rate: " << wav.sample_rate << "\n" - << "Bit Rate: " << wav.bit_rate << "\n" - << "\n" - << "Scaling audio by " << playback_rate << "x..." << std::endl; - - // Write the header back out again. - if (fwrite(&wav, sizeof(wav), 1, output.get()) < 1) { - LOG(ERROR) << "could not write WAV header"; - return 1; - } - - // Create buffer to be filled by |ola|. - scoped_array<uint8> buf(new uint8[window_size]); - - CHECK(buf.get()); - - // Keep track of bytes written to disk and bytes copied to |b|. - size_t bytes_written = 0; - size_t bytes; - while ((bytes = ola.FillBuffer(buf.get(), window_size)) > 0) { - if (fwrite(buf.get(), 1, bytes, output.get()) != bytes) { - LOG(ERROR) << "could not write data after " << bytes_written; - } else { - bytes_written += bytes; - } - } - - // Seek back to the beginning of our output file and update the header. - wav.chunk_size = 36 + bytes_written; - wav.subchunk1_size = 16; - wav.subchunk2_size = bytes_written; - fseek(output.get(), 0, SEEK_SET); - if (fwrite(&wav, sizeof(wav), 1, output.get()) < 1) { - LOG(ERROR) << "could not write wav header."; - return 1; - } - CommandLine::Reset(); - return 0; -} |