Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1169)

Unified Diff: media/filters/audio_renderer_algorithm_base_unittest.cc

Issue 9395057: Fix muted audio when playback rate != 1.0 or 0.0 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: media/filters/audio_renderer_algorithm_base_unittest.cc
diff --git a/media/filters/audio_renderer_algorithm_base_unittest.cc b/media/filters/audio_renderer_algorithm_base_unittest.cc
index 75d8c52cd3be9819fe6d76d269eb9eec81ac5bfc..13a6e029760499f49da8261d8559eb18a1b1218e 100644
--- a/media/filters/audio_renderer_algorithm_base_unittest.cc
+++ b/media/filters/audio_renderer_algorithm_base_unittest.cc
@@ -8,136 +8,285 @@
// correct rate. We always pass in a very large destination buffer with the
// expectation that FillBuffer() will fill as much as it can but no more.
+#include <cmath>
+
#include "base/bind.h"
#include "base/callback.h"
#include "media/base/data_buffer.h"
#include "media/filters/audio_renderer_algorithm_base.h"
-#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-using ::testing::AnyNumber;
+static const size_t kRawDataSize = 10 * 1024;
+static const int kSamplesPerSecond = 44100;
+static const int kDefaultChannels = 2;
+static const int kDefaultSampleBits = 16;
namespace media {
-static const int kChannels = 1;
-static const int kSampleRate = 1000;
-static const int kSampleBits = 8;
-
-TEST(AudioRendererAlgorithmBaseTest, FillBuffer_NormalRate) {
- // When playback rate == 1.0f: straight copy of whatever is in |queue_|.
- AudioRendererAlgorithmBase algorithm;
- algorithm.Initialize(kChannels, kSampleRate, kSampleBits, 1.0f,
- base::Bind(&base::DoNothing));
-
- // Enqueue a buffer of any size since it doesn't matter.
- const size_t kDataSize = 1024;
- algorithm.EnqueueBuffer(new DataBuffer(
- scoped_array<uint8>(new uint8[kDataSize]), kDataSize));
- EXPECT_EQ(kDataSize, algorithm.bytes_buffered());
-
- // Read the same sized amount.
- scoped_array<uint8> data(new uint8[kDataSize]);
- EXPECT_EQ(kDataSize, algorithm.FillBuffer(data.get(), kDataSize));
- EXPECT_EQ(0u, algorithm.bytes_buffered());
-}
+class AudioRendererAlgorithmBaseTest : public testing::Test {
+ public:
+ AudioRendererAlgorithmBaseTest()
+ : bytes_enqueued_(0),
+ bytes_per_frame_(0),
acolwell GONE FROM CHROMIUM 2012/02/29 00:31:38 nit: Do we really need these? How about just calli
vrk (LEFT CHROMIUM) 2012/02/29 20:41:39 Done.
+ bytes_per_channel_(0) {
+ }
+
+ ~AudioRendererAlgorithmBaseTest() {}
-TEST(AudioRendererAlgorithmBaseTest, FillBuffer_DoubleRate) {
- // When playback rate > 1.0f: input is read faster than output is written.
- AudioRendererAlgorithmBase algorithm;
- algorithm.Initialize(kChannels, kSampleRate, kSampleBits, 2.0f,
- base::Bind(&base::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|.
- //
- // For rate == 0.5f, reading half the input size should consume all enqueued
- // data.
- const size_t kBufferSize = 16 * 1024;
- scoped_array<uint8> data(new uint8[kBufferSize]);
- const size_t kTestData[][2] = {
- { algorithm.window_size(), algorithm.window_size() / 2},
- { algorithm.window_size() / 2, algorithm.window_size() / 4},
- { 4u, 2u },
- { 0u, 0u },
- };
-
- for (size_t i = 0u; i < arraysize(kTestData); ++i) {
- const size_t kDataSize = kTestData[i][0];
- algorithm.EnqueueBuffer(new DataBuffer(
- scoped_array<uint8>(new uint8[kDataSize]), kDataSize));
- EXPECT_EQ(kDataSize, algorithm.bytes_buffered());
-
- const size_t kExpectedSize = kTestData[i][1];
- ASSERT_LE(kExpectedSize, kBufferSize);
- EXPECT_EQ(kExpectedSize, algorithm.FillBuffer(data.get(), kBufferSize));
- EXPECT_EQ(0u, algorithm.bytes_buffered());
+ void Initialize() {
+ Initialize(kDefaultChannels, kDefaultSampleBits);
}
-}
-TEST(AudioRendererAlgorithmBaseTest, FillBuffer_HalfRate) {
- // When playback rate < 1.0f: input is read slower than output is written.
- AudioRendererAlgorithmBase algorithm;
- algorithm.Initialize(kChannels, kSampleRate, kSampleBits, 0.5f,
- base::Bind(&base::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|.
- //
- // For rate == 0.5f, reading double the input size should consume all enqueued
- // data.
- const size_t kBufferSize = 16 * 1024;
- scoped_array<uint8> data(new uint8[kBufferSize]);
- const size_t kTestData[][2] = {
- { algorithm.window_size(), algorithm.window_size() * 2 },
- { algorithm.window_size() / 2, algorithm.window_size() },
- { 2u, 4u },
- { 0u, 0u },
- };
-
- for (size_t i = 0u; i < arraysize(kTestData); ++i) {
- const size_t kDataSize = kTestData[i][0];
- algorithm.EnqueueBuffer(new DataBuffer(
- scoped_array<uint8>(new uint8[kDataSize]), kDataSize));
- EXPECT_EQ(kDataSize, algorithm.bytes_buffered());
-
- const size_t kExpectedSize = kTestData[i][1];
- ASSERT_LE(kExpectedSize, kBufferSize);
- EXPECT_EQ(kExpectedSize, algorithm.FillBuffer(data.get(), kBufferSize));
- EXPECT_EQ(0u, algorithm.bytes_buffered());
+ void Initialize(int channels, int bits_per_channel) {
+ algorithm_.Initialize(
+ channels, kSamplesPerSecond, bits_per_channel, 1.0f,
+ base::Bind(&AudioRendererAlgorithmBaseTest::EnqueueData,
+ base::Unretained(this)));
+ bytes_per_frame_ = algorithm_.bytes_per_frame();
+ bytes_per_channel_ = algorithm_.bytes_per_channel();
+ EnqueueData();
+ }
+
+ void EnqueueData() {
+ scoped_array<uint8> audio_data(new uint8[kRawDataSize]);
+ size_t length = kRawDataSize / bytes_per_channel_;
acolwell GONE FROM CHROMIUM 2012/02/29 00:31:38 I think this might break if kRawDataSize isn't an
vrk (LEFT CHROMIUM) 2012/02/29 20:41:39 Done.
+ switch (bytes_per_channel_) {
+ case 4:
+ WriteFakeData<int32>(audio_data.get(), length);
+ break;
+ case 2:
+ WriteFakeData<int16>(audio_data.get(), length);
+ break;
+ case 1:
+ WriteFakeData<uint8>(audio_data.get(), length);
+ break;
+ default:
+ NOTREACHED() << "Unsupported audio bit depth in crossfade.";
+ }
+ algorithm_.EnqueueBuffer(new DataBuffer(audio_data.Pass(), kRawDataSize));
+ bytes_enqueued_ += kRawDataSize;
+ }
+
+ template <class Type>
+ void WriteFakeData(uint8* audio_data, size_t length) {
+ Type* output = reinterpret_cast<Type*>(audio_data);
+ for (size_t i = 0; i < length; i++) {
+ // The value of the data is meaningless; we just want non-zero data to
+ // differentiate it from muted data.
+ output[i] = i % 5 + 10;
+ }
+ }
+
+ void CheckFakeData(uint8* audio_data, int frames_written,
+ float playback_rate) {
+ // Check to see if the audio data was muted.
+ bool expect_muted = playback_rate < media::kMinPlaybackRate ||
+ playback_rate > media::kMaxPlaybackRate;
+
+ size_t length = (frames_written * bytes_per_frame_) / bytes_per_channel_;
+ switch (bytes_per_channel_) {
+ case 4:
+ DoCheckFakeData<int32>(audio_data, length, expect_muted);
+ break;
+ case 2:
+ DoCheckFakeData<int16>(audio_data, length, expect_muted);
+ break;
+ case 1:
+ DoCheckFakeData<uint8>(audio_data, length, expect_muted);
+ break;
+ default:
+ NOTREACHED() << "Unsupported audio bit depth in crossfade.";
+ }
+ }
+
+ template <class Type>
+ void DoCheckFakeData(uint8* audio_data, size_t length, bool expect_muted) {
+ Type* output = reinterpret_cast<Type*>(audio_data);
+ for (size_t i = 0; i < length; i++) {
+ EXPECT_TRUE(expect_muted || output[i] != 0);
+ }
}
-}
-TEST(AudioRendererAlgorithmBaseTest, FillBuffer_QuarterRate) {
- // When playback rate is very low the audio is simply muted.
- AudioRendererAlgorithmBase algorithm;
- algorithm.Initialize(kChannels, kSampleRate, kSampleBits, 0.25f,
- base::Bind(&base::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|.
- //
- // For rate == 0.25f, reading four times the input size should consume all
- // enqueued data but without executing OLA.
- const size_t kBufferSize = 16 * 1024;
- scoped_array<uint8> data(new uint8[kBufferSize]);
- const size_t kTestData[][2] = {
- { algorithm.window_size(), algorithm.window_size() * 4},
- { algorithm.window_size() / 2, algorithm.window_size() * 2},
- { 1u, 4u },
- { 0u, 0u },
- };
-
- for (size_t i = 0u; i < arraysize(kTestData); ++i) {
- const size_t kDataSize = kTestData[i][0];
- algorithm.EnqueueBuffer(new DataBuffer(scoped_array<uint8>(
- new uint8[kDataSize]), kDataSize));
- EXPECT_EQ(kDataSize, algorithm.bytes_buffered());
-
- const size_t kExpectedSize = kTestData[i][1];
- ASSERT_LE(kExpectedSize, kBufferSize);
- EXPECT_EQ(kExpectedSize, algorithm.FillBuffer(data.get(), kBufferSize));
- EXPECT_EQ(0u, algorithm.bytes_buffered());
+ int ComputeConsumedBytes(int initial_bytes_enqueued,
+ int initial_bytes_buffered) {
+ int byte_delta = bytes_enqueued_ - initial_bytes_enqueued;
+ int buffered_delta = algorithm_.bytes_buffered() - initial_bytes_buffered;
+ int consumed = byte_delta - buffered_delta;
+ CHECK_GE(consumed, 0);
+ return consumed;
}
+
+ void TestPlaybackRate(float playback_rate) {
+ static const int kDefaultBufferSize = kSamplesPerSecond / 10;
+ static const int kDefaultFramesRequested = 5 * kSamplesPerSecond;
+
+ TestPlaybackRate(playback_rate, kDefaultBufferSize,
+ kDefaultFramesRequested);
+ }
+
+ void TestPlaybackRate(float playback_rate,
+ int buffer_size_in_frames,
+ int total_frames_requested) {
+ int initial_bytes_enqueued = bytes_enqueued_;
+ int initial_bytes_buffered = algorithm_.bytes_buffered();
+
+ algorithm_.SetPlaybackRate(playback_rate);
+
+ scoped_array<uint8> buffer(
+ new uint8[buffer_size_in_frames * bytes_per_frame_]);
+
+ if (playback_rate == 0.0) {
+ int frames_written =
+ algorithm_.FillBuffer(buffer.get(), buffer_size_in_frames);
+ EXPECT_EQ(0, frames_written);
+ return;
+ }
+
+ int frames_remaining = total_frames_requested;
+ while (frames_remaining > 0) {
+ int frames_requested = std::min(buffer_size_in_frames, frames_remaining);
+ int frames_written =
+ algorithm_.FillBuffer(buffer.get(), frames_requested);
+ CHECK_GT(frames_written, 0);
+ CheckFakeData(buffer.get(), frames_written, playback_rate);
+ frames_remaining -= frames_written;
+ }
+
+ int bytes_requested = total_frames_requested * bytes_per_frame_;
+ int bytes_consumed = ComputeConsumedBytes(initial_bytes_enqueued,
+ initial_bytes_buffered);
+
+ // If playing back at normal speed, we should always get back the same
+ // number of bytes requested.
+ if (playback_rate == 1.0) {
+ EXPECT_EQ(bytes_requested, bytes_consumed);
+ return;
+ }
+
+ // Otherwise, allow |kMaxAcceptableDelta| difference between the target and
+ // actual playback rate.
+ // When |kSamplesPerSecond| and |total_frames_requested| are reasonably
+ // large, one can expect less than a 1% difference in most cases. In our
+ // current implementation, sped up playback is less accurate than slowed
+ // down playback, and for playback_rate > 1, playback rate generally gets
+ // less and less accurate the farther it drifts from 1 (though this is
+ // nonlinear).
+ static const float kMaxAcceptableDelta = 0.01;
+ float actual_playback_rate = 1.0 * bytes_consumed / bytes_requested;
+
+ // Calculate the percentage difference from the target |playback_rate| as a
+ // fraction from 0.0 to 1.0.
+ float delta = std::abs(1.0 - (actual_playback_rate / playback_rate));
+
+ EXPECT_LE(delta, kMaxAcceptableDelta);
+ }
+
+ protected:
+ AudioRendererAlgorithmBase algorithm_;
+ int bytes_enqueued_;
+ int bytes_per_frame_;
+ int bytes_per_channel_;
+};
+
+TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_NormalRate) {
+ Initialize();
+ TestPlaybackRate(1.0);
+}
+
+TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_OneAndAQuarterRate) {
+ Initialize();
+ TestPlaybackRate(1.25);
+}
+
+TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_OneAndAHalfRate) {
+ Initialize();
+ TestPlaybackRate(1.5);
+}
+
+TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_DoubleRate) {
+ Initialize();
+ TestPlaybackRate(2.0);
+}
+
+TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_EightTimesRate) {
+ Initialize();
+ TestPlaybackRate(8.0);
+}
+
+TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_ThreeQuartersRate) {
+ Initialize();
+ TestPlaybackRate(0.75);
+}
+
+TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_HalfRate) {
+ Initialize();
+ TestPlaybackRate(0.5);
+}
+
+TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_QuarterRate) {
+ Initialize();
+ TestPlaybackRate(0.25);
+}
+
+TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_Pause) {
+ Initialize();
+ TestPlaybackRate(0.0);
+}
+
+TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_SlowDown) {
+ Initialize();
+ TestPlaybackRate(4.5);
+ TestPlaybackRate(3.0);
+ TestPlaybackRate(2.0);
+ TestPlaybackRate(1.0);
+ TestPlaybackRate(0.5);
+ TestPlaybackRate(0.25);
+}
+
+TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_SpeedUp) {
+ Initialize();
+ TestPlaybackRate(0.25);
+ TestPlaybackRate(0.5);
+ TestPlaybackRate(1.0);
+ TestPlaybackRate(2.0);
+ TestPlaybackRate(3.0);
+ TestPlaybackRate(4.5);
+}
+
+TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_JumpAroundSpeeds) {
+ Initialize();
+ TestPlaybackRate(2.1);
+ TestPlaybackRate(0.9);
+ TestPlaybackRate(0.6);
+ TestPlaybackRate(1.4);
+ TestPlaybackRate(0.3);
+}
+
+TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_SmallBufferSize) {
+ Initialize();
+ static const int kBufferSizeInFrames = 1;
+ static const int kFramesRequested = 2 * kSamplesPerSecond;
+ TestPlaybackRate(1.0, kBufferSizeInFrames, kFramesRequested);
+ TestPlaybackRate(0.5, kBufferSizeInFrames, kFramesRequested);
+ TestPlaybackRate(1.5, kBufferSizeInFrames, kFramesRequested);
+}
+
+TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_LowerQualityAudio) {
+ static const int kChannels = 1;
+ static const int kSampleBits = 8;
+ Initialize(kChannels, kSampleBits);
+ TestPlaybackRate(1.0);
+ TestPlaybackRate(0.5);
+ TestPlaybackRate(1.5);
+}
+
+TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_HigherQualityAudio) {
+ static const int kChannels = 2;
+ static const int kSampleBits = 32;
+ Initialize(kChannels, kSampleBits);
+ TestPlaybackRate(1.0);
+ TestPlaybackRate(0.5);
+ TestPlaybackRate(1.5);
}
} // namespace media

Powered by Google App Engine
This is Rietveld 408576698