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

Unified Diff: media/base/audio_renderer_mixer_unittest.cc

Issue 10698066: Switch to pcm_low_latency and enable resampling. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Comments! Remove RefCount. Squash ARMM changes. Created 8 years, 5 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/base/audio_renderer_mixer_unittest.cc
diff --git a/media/base/audio_renderer_mixer_unittest.cc b/media/base/audio_renderer_mixer_unittest.cc
index e897bbf9399c8216fa94faf227c1d56a6e390a37..fc2267df130b64e864860069c7d19a696481fdba 100644
--- a/media/base/audio_renderer_mixer_unittest.cc
+++ b/media/base/audio_renderer_mixer_unittest.cc
@@ -2,99 +2,117 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+// MSVC++ requires this to be set before any other includes to get M_PI.
+#define _USE_MATH_DEFINES
#include <cmath>
+#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/memory/scoped_ptr.h"
-#include "media/audio/audio_util.h"
+#include "base/memory/scoped_vector.h"
#include "media/base/audio_renderer_mixer.h"
#include "media/base/audio_renderer_mixer_input.h"
#include "media/base/fake_audio_render_callback.h"
+#include "media/base/mock_audio_renderer_sink.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
// Parameters which control the many input case tests.
-static const int kMixerInputs = 64;
-static const int kMixerCycles = 32;
+static const int kMixerInputs = 8;
+static const int kMixerCycles = 3;
+// Parameters used for testing.
static const int kBitsPerChannel = 16;
-static const int kSampleRate = 48000;
static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO;
+static const int kHighLatencyBufferSize = 8192;
+static const int kLowLatencyBufferSize = 256;
-// Multiple rounds of addition result in precision loss with float values, so we
-// need an epsilon such that if for all x f(x) = sum(x, m) and g(x) = m * x then
-// fabs(f - g) < kEpsilon. The kEpsilon below has been tested with m < 128.
-static const float kEpsilon = 0.00015f;
+// Number of full sine wave cycles for each Render() call.
+static const int kSineCycles = 4;
-class MockAudioRendererSink : public AudioRendererSink {
+// Tuple of <input sampling rate, output sampling rate, epsilon>.
+typedef std::tr1::tuple<int, int, double> AudioRendererMixerTestData;
+class AudioRendererMixerTestCase
scherkus (not reviewing) 2012/07/14 01:45:02 this should be ARMTest, not ARMTestCase the only
DaleCurtis 2012/07/14 02:10:03 Done.
+ : public testing::TestWithParam<AudioRendererMixerTestData> {
public:
- MOCK_METHOD0(Start, void());
- MOCK_METHOD0(Stop, void());
- MOCK_METHOD1(Pause, void(bool flush));
- MOCK_METHOD0(Play, void());
- MOCK_METHOD1(SetPlaybackRate, void(float rate));
- MOCK_METHOD1(SetVolume, bool(double volume));
- MOCK_METHOD1(GetVolume, void(double* volume));
-
- void Initialize(const media::AudioParameters& params,
- AudioRendererSink::RenderCallback* renderer) OVERRIDE {
- // TODO(dalecurtis): Once we have resampling we need to ensure we are given
- // an AudioParameters which reflects the hardware settings.
- callback_ = renderer;
- };
-
- AudioRendererSink::RenderCallback* callback() {
- return callback_;
- }
-
- void SimulateRenderError() {
- callback_->OnRenderError();
- }
+ AudioRendererMixerTestCase()
+ : epsilon_(std::tr1::get<2>(GetParam())),
+ half_fill_(false) {
+ // Create input and output parameters based on test parameters.
+ input_parameters_ = AudioParameters(
+ AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout,
+ std::tr1::get<0>(GetParam()), kBitsPerChannel, kHighLatencyBufferSize);
+ output_parameters_ = AudioParameters(
+ AudioParameters::AUDIO_PCM_LOW_LATENCY, kChannelLayout,
+ std::tr1::get<1>(GetParam()), 16, kLowLatencyBufferSize);
- protected:
- virtual ~MockAudioRendererSink() {}
-
- AudioRendererSink::RenderCallback* callback_;
-};
-
-class AudioRendererMixerTest : public ::testing::Test {
- public:
- AudioRendererMixerTest() {
- audio_parameters_ = AudioParameters(
- AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate,
- kBitsPerChannel, GetHighLatencyOutputBufferSize(kSampleRate));
sink_ = new MockAudioRendererSink();
EXPECT_CALL(*sink_, Start());
EXPECT_CALL(*sink_, Stop());
- mixer_ = new AudioRendererMixer(audio_parameters_, sink_);
+ mixer_.reset(new AudioRendererMixer(
+ input_parameters_, output_parameters_, sink_));
mixer_callback_ = sink_->callback();
// TODO(dalecurtis): If we switch to AVX/SSE optimization, we'll need to
// allocate these on 32-byte boundaries and ensure they're sized % 32 bytes.
- audio_data_.reserve(audio_parameters_.channels());
- for (int i = 0; i < audio_parameters_.channels(); ++i)
- audio_data_.push_back(new float[audio_parameters_.frames_per_buffer()]);
+ audio_data_.reserve(output_parameters_.channels());
+ for (int i = 0; i < output_parameters_.channels(); ++i)
+ audio_data_.push_back(new float[output_parameters_.frames_per_buffer()]);
+
+ // TODO(dalecurtis): If we switch to AVX/SSE optimization, we'll need to
+ // allocate these on 32-byte boundaries and ensure they're sized % 32 bytes.
+ expected_audio_data_.reserve(output_parameters_.channels());
+ for (int i = 0; i < output_parameters_.channels(); ++i) {
+ expected_audio_data_.push_back(
+ new float[output_parameters_.frames_per_buffer()]);
+ }
+
+ // Allocate one callback for generating expected results.
+ double step = kSineCycles / static_cast<double>(
+ output_parameters_.frames_per_buffer());
+ expected_callback_.reset(new FakeAudioRenderCallback(step));
+ }
- fake_callback_.reset(new FakeAudioRenderCallback(audio_parameters_));
+ AudioRendererMixer* GetMixer(const AudioParameters& params) {
+ return mixer_.get();
}
+ MOCK_METHOD1(RemoveMixer, void(const AudioParameters&));
+
void InitializeInputs(int count) {
+ mixer_inputs_.reserve(count);
+ fake_callbacks_.reserve(count);
+
+ // Setup FakeAudioRenderCallback step to compensate for resampling.
+ double scale_factor = input_parameters_.sample_rate()
+ / static_cast<double>(output_parameters_.sample_rate());
+ double step = kSineCycles / (scale_factor *
+ static_cast<double>(output_parameters_.frames_per_buffer()));
+
for (int i = 0; i < count; ++i) {
- scoped_refptr<AudioRendererMixerInput> mixer_input(
- new AudioRendererMixerInput(mixer_));
- mixer_input->Initialize(audio_parameters_, fake_callback_.get());
- mixer_input->SetVolume(1.0f);
- mixer_inputs_.push_back(mixer_input);
+ fake_callbacks_.push_back(new FakeAudioRenderCallback(step));
+ mixer_inputs_.push_back(new AudioRendererMixerInput(
+ base::Bind(&AudioRendererMixerTestCase::GetMixer,
+ base::Unretained(this)),
+ base::Bind(&AudioRendererMixerTestCase::RemoveMixer,
+ base::Unretained(this))));
+ mixer_inputs_[i]->Initialize(input_parameters_, fake_callbacks_[i]);
+ mixer_inputs_[i]->SetVolume(1.0f);
}
+ EXPECT_CALL(*this, RemoveMixer(testing::_)).Times(count);
}
- bool ValidateAudioData(int start_index, int frames, float check_value) {
+ bool ValidateAudioData(int index, int frames, float scale) {
for (size_t i = 0; i < audio_data_.size(); ++i) {
- for (int j = start_index; j < frames; j++) {
- if (fabs(audio_data_[i][j] - check_value) > kEpsilon) {
- EXPECT_NEAR(check_value, audio_data_[i][j], kEpsilon)
+ for (int j = index; j < frames; j++) {
+ double error = fabs(
+ audio_data_[i][j] - expected_audio_data_[i][j] * scale);
+ if (error > epsilon_) {
+ EXPECT_NEAR(
+ expected_audio_data_[i][j] * scale, audio_data_[i][j], epsilon_)
<< " i=" << i << ", j=" << j;
return false;
}
@@ -103,20 +121,47 @@ class AudioRendererMixerTest : public ::testing::Test {
return true;
}
- // Render audio_parameters_.frames_per_buffer() frames into |audio_data_| and
- // verify the result against |check_value|.
- bool RenderAndValidateAudioData(float check_value) {
- int frames = mixer_callback_->Render(
- audio_data_, audio_parameters_.frames_per_buffer(), 0);
- return frames == audio_parameters_.frames_per_buffer() && ValidateAudioData(
- 0, audio_parameters_.frames_per_buffer(), check_value);
+ bool RenderAndValidateAudioData(float scale) {
+ int request_frames = output_parameters_.frames_per_buffer();
+
+ // Half fill won't be exactly half when resampling since the resampler
+ // will have enough data to fill out more of the buffer based on its
+ // internal buffer and kernel size. So special case some of the checks.
+ bool resampling = input_parameters_.sample_rate()
+ != output_parameters_.sample_rate();
+
+ if (half_fill_) {
+ for (size_t i = 0; i < fake_callbacks_.size(); ++i)
+ fake_callbacks_[i]->set_half_fill(true);
+ expected_callback_->set_half_fill(true);
+ }
+
+ // Render actual audio data.
+ int frames = mixer_callback_->Render(audio_data_, request_frames, 0);
+ if (frames != request_frames)
+ return false;
+
+ // Render expected audio data (without scaling).
+ expected_callback_->Render(expected_audio_data_, request_frames, 0);
+
+ if (half_fill_) {
+ // Verify first half of audio data for both resampling and non-resampling.
+ if (!ValidateAudioData(0, frames / 2, scale))
+ return false;
+ // Verify silence in the second half if we're not resampling.
+ if (!resampling)
+ return ValidateAudioData(frames / 2, frames, 0);
+ return true;
+ } else {
+ return ValidateAudioData(0, frames, scale);
+ }
}
// Fill |audio_data_| fully with |value|.
void FillAudioData(float value) {
for (size_t i = 0; i < audio_data_.size(); ++i)
std::fill(audio_data_[i],
- audio_data_[i] + audio_parameters_.frames_per_buffer(), value);
+ audio_data_[i] + output_parameters_.frames_per_buffer(), value);
}
// Verify silence when mixer inputs are in pre-Start() and post-Start().
@@ -148,27 +193,14 @@ class AudioRendererMixerTest : public ::testing::Test {
void PlayTest(int inputs) {
InitializeInputs(inputs);
- for (size_t i = 0; i < mixer_inputs_.size(); ++i)
+ // Play() all mixer inputs and ensure we get the right values.
+ for (size_t i = 0; i < mixer_inputs_.size(); ++i) {
mixer_inputs_[i]->Start();
-
- // Play() all even numbered mixer inputs and ensure we get the right value.
- for (size_t i = 0; i < mixer_inputs_.size(); i += 2)
mixer_inputs_[i]->Play();
- for (int i = 0; i < kMixerCycles; ++i) {
- fake_callback_->NextFillValue();
- ASSERT_TRUE(RenderAndValidateAudioData(
- fake_callback_->fill_value() * std::max(
- mixer_inputs_.size() / 2, static_cast<size_t>(1))));
}
- // Play() all mixer inputs and ensure we still get the right values.
- for (size_t i = 1; i < mixer_inputs_.size(); i += 2)
- mixer_inputs_[i]->Play();
- for (int i = 0; i < kMixerCycles; ++i) {
- fake_callback_->NextFillValue();
- ASSERT_TRUE(RenderAndValidateAudioData(
- fake_callback_->fill_value() * mixer_inputs_.size()));
- }
+ for (int i = 0; i < kMixerCycles; ++i)
+ ASSERT_TRUE(RenderAndValidateAudioData(mixer_inputs_.size()));
for (size_t i = 0; i < mixer_inputs_.size(); ++i)
mixer_inputs_[i]->Stop();
@@ -190,11 +222,8 @@ class AudioRendererMixerTest : public ::testing::Test {
total_scale += volume;
EXPECT_TRUE(mixer_inputs_[i]->SetVolume(volume));
}
- for (int i = 0; i < kMixerCycles; ++i) {
- fake_callback_->NextFillValue();
- ASSERT_TRUE(RenderAndValidateAudioData(
- fake_callback_->fill_value() * total_scale));
- }
+ for (int i = 0; i < kMixerCycles; ++i)
+ ASSERT_TRUE(RenderAndValidateAudioData(total_scale));
for (size_t i = 0; i < mixer_inputs_.size(); ++i)
mixer_inputs_[i]->Stop();
@@ -203,7 +232,6 @@ class AudioRendererMixerTest : public ::testing::Test {
// Verify output when mixer inputs can only partially fulfill a Render().
void PlayPartialRenderTest(int inputs) {
InitializeInputs(inputs);
- int frames = audio_parameters_.frames_per_buffer();
for (size_t i = 0; i < mixer_inputs_.size(); ++i) {
mixer_inputs_[i]->Start();
@@ -211,16 +239,8 @@ class AudioRendererMixerTest : public ::testing::Test {
}
// Verify a properly filled buffer when half filled (remainder zeroed).
- fake_callback_->set_half_fill(true);
- for (int i = 0; i < kMixerCycles; ++i) {
- fake_callback_->NextFillValue();
- ASSERT_EQ(mixer_callback_->Render(audio_data_, frames, 0), frames);
- ASSERT_TRUE(ValidateAudioData(
- 0, frames / 2, fake_callback_->fill_value() * mixer_inputs_.size()));
- ASSERT_TRUE(ValidateAudioData(
- frames / 2, frames, 0.0f));
- }
- fake_callback_->set_half_fill(false);
+ half_fill_ = true;
+ ASSERT_TRUE(RenderAndValidateAudioData(mixer_inputs_.size()));
for (size_t i = 0; i < mixer_inputs_.size(); ++i)
mixer_inputs_[i]->Stop();
@@ -238,17 +258,8 @@ class AudioRendererMixerTest : public ::testing::Test {
// Pause() all even numbered mixer inputs and ensure we get the right value.
for (size_t i = 0; i < mixer_inputs_.size(); i += 2)
mixer_inputs_[i]->Pause(false);
- for (int i = 0; i < kMixerCycles; ++i) {
- fake_callback_->NextFillValue();
- ASSERT_TRUE(RenderAndValidateAudioData(
- fake_callback_->fill_value() * (mixer_inputs_.size() / 2)));
- }
-
- // Pause() all the inputs and verify we get silence back.
- for (size_t i = 1; i < mixer_inputs_.size(); i += 2)
- mixer_inputs_[i]->Pause(false);
- FillAudioData(1.0f);
- EXPECT_TRUE(RenderAndValidateAudioData(0.0f));
+ for (int i = 0; i < kMixerCycles; ++i)
+ ASSERT_TRUE(RenderAndValidateAudioData(mixer_inputs_.size() / 2));
for (size_t i = 0; i < mixer_inputs_.size(); ++i)
mixer_inputs_[i]->Stop();
@@ -270,90 +281,95 @@ class AudioRendererMixerTest : public ::testing::Test {
}
protected:
- virtual ~AudioRendererMixerTest() {
+ virtual ~AudioRendererMixerTestCase() {
for (size_t i = 0; i < audio_data_.size(); ++i)
delete [] audio_data_[i];
}
scoped_refptr<MockAudioRendererSink> sink_;
- scoped_refptr<AudioRendererMixer> mixer_;
+ scoped_ptr<AudioRendererMixer> mixer_;
AudioRendererSink::RenderCallback* mixer_callback_;
- scoped_ptr<FakeAudioRenderCallback> fake_callback_;
- AudioParameters audio_parameters_;
+ AudioParameters input_parameters_;
+ AudioParameters output_parameters_;
std::vector<float*> audio_data_;
+ std::vector<float*> expected_audio_data_;
std::vector< scoped_refptr<AudioRendererMixerInput> > mixer_inputs_;
+ ScopedVector<FakeAudioRenderCallback> fake_callbacks_;
+ scoped_ptr<FakeAudioRenderCallback> expected_callback_;
+ double epsilon_;
+ bool half_fill_;
- DISALLOW_COPY_AND_ASSIGN(AudioRendererMixerTest);
+ DISALLOW_COPY_AND_ASSIGN(AudioRendererMixerTestCase);
};
// Verify a mixer with no inputs returns silence for all requested frames.
-TEST_F(AudioRendererMixerTest, NoInputs) {
+TEST_P(AudioRendererMixerTestCase, NoInputs) {
FillAudioData(1.0f);
EXPECT_TRUE(RenderAndValidateAudioData(0.0f));
}
// Test mixer output with one input in the pre-Start() and post-Start() state.
-TEST_F(AudioRendererMixerTest, OneInputStart) {
+TEST_P(AudioRendererMixerTestCase, OneInputStart) {
StartTest(1);
}
// Test mixer output with many inputs in the pre-Start() and post-Start() state.
-TEST_F(AudioRendererMixerTest, ManyInputStart) {
+TEST_P(AudioRendererMixerTestCase, ManyInputStart) {
StartTest(kMixerInputs);
}
// Test mixer output with one input in the post-Play() state.
-TEST_F(AudioRendererMixerTest, OneInputPlay) {
+TEST_P(AudioRendererMixerTestCase, OneInputPlay) {
PlayTest(1);
}
// Test mixer output with many inputs in the post-Play() state.
-TEST_F(AudioRendererMixerTest, ManyInputPlay) {
+TEST_P(AudioRendererMixerTestCase, ManyInputPlay) {
PlayTest(kMixerInputs);
}
// Test volume adjusted mixer output with one input in the post-Play() state.
-TEST_F(AudioRendererMixerTest, OneInputPlayVolumeAdjusted) {
+TEST_P(AudioRendererMixerTestCase, OneInputPlayVolumeAdjusted) {
PlayVolumeAdjustedTest(1);
}
// Test volume adjusted mixer output with many inputs in the post-Play() state.
-TEST_F(AudioRendererMixerTest, ManyInputPlayVolumeAdjusted) {
+TEST_P(AudioRendererMixerTestCase, ManyInputPlayVolumeAdjusted) {
PlayVolumeAdjustedTest(kMixerInputs);
}
// Test mixer output with one input and partial Render() in post-Play() state.
-TEST_F(AudioRendererMixerTest, OneInputPlayPartialRender) {
+TEST_P(AudioRendererMixerTestCase, OneInputPlayPartialRender) {
PlayPartialRenderTest(1);
}
// Test mixer output with many inputs and partial Render() in post-Play() state.
-TEST_F(AudioRendererMixerTest, ManyInputPlayPartialRender) {
+TEST_P(AudioRendererMixerTestCase, ManyInputPlayPartialRender) {
PlayPartialRenderTest(kMixerInputs);
}
// Test mixer output with one input in the post-Pause() state.
-TEST_F(AudioRendererMixerTest, OneInputPause) {
+TEST_P(AudioRendererMixerTestCase, OneInputPause) {
PauseTest(1);
}
// Test mixer output with many inputs in the post-Pause() state.
-TEST_F(AudioRendererMixerTest, ManyInputPause) {
+TEST_P(AudioRendererMixerTestCase, ManyInputPause) {
PauseTest(kMixerInputs);
}
// Test mixer output with one input in the post-Stop() state.
-TEST_F(AudioRendererMixerTest, OneInputStop) {
+TEST_P(AudioRendererMixerTestCase, OneInputStop) {
StopTest(1);
}
// Test mixer output with many inputs in the post-Stop() state.
-TEST_F(AudioRendererMixerTest, ManyInputStop) {
+TEST_P(AudioRendererMixerTestCase, ManyInputStop) {
StopTest(kMixerInputs);
}
// Test mixer with many inputs in mixed post-Stop() and post-Play() states.
-TEST_F(AudioRendererMixerTest, ManyInputMixedStopPlay) {
+TEST_P(AudioRendererMixerTestCase, ManyInputMixedStopPlay) {
InitializeInputs(kMixerInputs);
// Start() all inputs.
@@ -366,32 +382,34 @@ TEST_F(AudioRendererMixerTest, ManyInputMixedStopPlay) {
mixer_inputs_[i - 1]->Stop();
mixer_inputs_[i]->Play();
}
- for (int i = 0; i < kMixerCycles; ++i) {
- fake_callback_->NextFillValue();
- ASSERT_TRUE(RenderAndValidateAudioData(
- fake_callback_->fill_value() * std::max(
- mixer_inputs_.size() / 2, static_cast<size_t>(1))));
- }
+ ASSERT_TRUE(RenderAndValidateAudioData(std::max(
+ mixer_inputs_.size() / 2, static_cast<size_t>(1))));
for (size_t i = 1; i < mixer_inputs_.size(); i += 2)
mixer_inputs_[i]->Stop();
}
-TEST_F(AudioRendererMixerTest, OnRenderError) {
- std::vector< scoped_refptr<AudioRendererMixerInput> > mixer_inputs;
- for (int i = 0; i < kMixerInputs; ++i) {
- scoped_refptr<AudioRendererMixerInput> mixer_input(
- new AudioRendererMixerInput(mixer_));
- mixer_input->Initialize(audio_parameters_, fake_callback_.get());
- mixer_input->SetVolume(1.0f);
- mixer_input->Start();
- mixer_inputs_.push_back(mixer_input);
+TEST_P(AudioRendererMixerTestCase, OnRenderError) {
+ InitializeInputs(kMixerInputs);
+ for (size_t i = 0; i < mixer_inputs_.size(); ++i) {
+ mixer_inputs_[i]->Start();
+ EXPECT_CALL(*fake_callbacks_[i], OnRenderError()).Times(1);
}
- EXPECT_CALL(*fake_callback_, OnRenderError()).Times(kMixerInputs);
- sink_->SimulateRenderError();
- for (int i = 0; i < kMixerInputs; ++i)
+ mixer_callback_->OnRenderError();
+ for (size_t i = 0; i < mixer_inputs_.size(); ++i)
mixer_inputs_[i]->Stop();
}
+INSTANTIATE_TEST_CASE_P(
+ AudioRendererMixerTest, AudioRendererMixerTestCase, testing::Values(
+ // No resampling.
+ std::tr1::make_tuple(44100, 44100, 0.000000477),
+
+ // Upsampling.
+ std::tr1::make_tuple(44100, 48000, 0.0329405),
+
+ // Downsampling.
+ std::tr1::make_tuple(48000, 41000, 0.0410239)));
+
} // namespace media

Powered by Google App Engine
This is Rietveld 408576698