| Index: media/base/audio_transform_unittest.cc
|
| diff --git a/media/base/audio_transform_unittest.cc b/media/base/audio_transform_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6d077d5da810bed9541b95184da26b76bd9dd9b3
|
| --- /dev/null
|
| +++ b/media/base/audio_transform_unittest.cc
|
| @@ -0,0 +1,225 @@
|
| +// Copyright (c) 2012 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.
|
| +
|
| +// MSVC++ requires this to be set before any other includes to get M_PI.
|
| +#define _USE_MATH_DEFINES
|
| +
|
| +#include <cmath>
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "base/memory/scoped_vector.h"
|
| +#include "media/base/audio_transform.h"
|
| +#include "media/base/fake_audio_render_callback.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 kTransformInputs = 8;
|
| +static const int kTransformCycles = 3;
|
| +
|
| +// Parameters used for testing.
|
| +static const int kBitsPerChannel = 32;
|
| +static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO;
|
| +static const int kHighLatencyBufferSize = 2048;
|
| +static const int kLowLatencyBufferSize = 256;
|
| +static const int kSampleRate = 48000;
|
| +
|
| +// Number of full sine wave cycles for each Render() call.
|
| +static const int kSineCycles = 4;
|
| +
|
| +// Tuple of <input sampling rate, output sampling rate, epsilon>.
|
| +typedef std::tr1::tuple<int, int, double> AudioTransformTestData;
|
| +class AudioTransformTest
|
| + : public testing::TestWithParam<AudioTransformTestData> {
|
| + public:
|
| + AudioTransformTest()
|
| + : epsilon_(std::tr1::get<2>(GetParam())) {
|
| + // 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);
|
| +
|
| + transform_.reset(new AudioTransform(input_parameters_, output_parameters_));
|
| +
|
| + audio_bus_ = AudioBus::Create(output_parameters_);
|
| + expected_audio_bus_ = AudioBus::Create(output_parameters_);
|
| +
|
| + // Allocate one callback for generating expected results.
|
| + double step = kSineCycles / static_cast<double>(
|
| + output_parameters_.frames_per_buffer());
|
| + expected_callback_.reset(new FakeAudioRenderCallback(step));
|
| + }
|
| +
|
| + void InitializeInputs(int 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) {
|
| + fake_callbacks_.push_back(new FakeAudioRenderCallback(step));
|
| + transform_->AddInput(fake_callbacks_[i]);
|
| + }
|
| + }
|
| +
|
| + void Reset() {
|
| + transform_->Reset();
|
| + for (size_t i = 0; i < fake_callbacks_.size(); ++i)
|
| + fake_callbacks_[i]->reset();
|
| + expected_callback_->reset();
|
| + }
|
| +
|
| + void SetVolume(float volume) {
|
| + for (size_t i = 0; i < fake_callbacks_.size(); ++i)
|
| + fake_callbacks_[i]->set_volume(volume);
|
| + }
|
| +
|
| + bool ValidateAudioData(int index, int frames, float scale) {
|
| + for (int i = 0; i < audio_bus_->channels(); ++i) {
|
| + for (int j = index; j < frames; j++) {
|
| + double error = fabs(audio_bus_->channel(i)[j] -
|
| + expected_audio_bus_->channel(i)[j] * scale);
|
| + if (error > epsilon_) {
|
| + EXPECT_NEAR(expected_audio_bus_->channel(i)[j] * scale,
|
| + audio_bus_->channel(i)[j], epsilon_)
|
| + << " i=" << i << ", j=" << j;
|
| + return false;
|
| + }
|
| + }
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + bool RenderAndValidateAudioData(float scale) {
|
| + // Render actual audio data.
|
| + transform_->Transform(audio_bus_.get());
|
| +
|
| + // Render expected audio data.
|
| + expected_callback_->Render(expected_audio_bus_.get(), 0);
|
| +
|
| + return ValidateAudioData(0, audio_bus_->frames(), scale);
|
| + }
|
| +
|
| + // Fill |audio_bus_| fully with |value|.
|
| + void FillAudioData(float value) {
|
| + for (int i = 0; i < audio_bus_->channels(); ++i) {
|
| + std::fill(audio_bus_->channel(i),
|
| + audio_bus_->channel(i) + audio_bus_->frames(), value);
|
| + }
|
| + }
|
| +
|
| + // Verify output with a number of transform inputs.
|
| + void RunTest(int inputs) {
|
| + InitializeInputs(inputs);
|
| +
|
| + SetVolume(0);
|
| + for (int i = 0; i < kTransformCycles; ++i)
|
| + ASSERT_TRUE(RenderAndValidateAudioData(0));
|
| +
|
| + Reset();
|
| +
|
| + // Set a different volume for each input and verify the results.
|
| + float total_scale = 0;
|
| + for (size_t i = 0; i < fake_callbacks_.size(); ++i) {
|
| + float volume = static_cast<float>(i) / fake_callbacks_.size();
|
| + total_scale += volume;
|
| + fake_callbacks_[i]->set_volume(volume);
|
| + }
|
| + for (int i = 0; i < kTransformCycles; ++i)
|
| + ASSERT_TRUE(RenderAndValidateAudioData(total_scale));
|
| +
|
| + Reset();
|
| +
|
| + // Remove every other input.
|
| + for (size_t i = 1; i < fake_callbacks_.size(); i += 2)
|
| + transform_->RemoveInput(fake_callbacks_[i]);
|
| +
|
| + SetVolume(1);
|
| + float scale = inputs > 1 ? inputs / 2.0f : inputs;
|
| + for (int i = 0; i < kTransformCycles; ++i)
|
| + ASSERT_TRUE(RenderAndValidateAudioData(scale));
|
| + }
|
| +
|
| + protected:
|
| + virtual ~AudioTransformTest() {}
|
| +
|
| + scoped_ptr<AudioTransform> transform_;
|
| + AudioParameters input_parameters_;
|
| + AudioParameters output_parameters_;
|
| + scoped_ptr<AudioBus> audio_bus_;
|
| + scoped_ptr<AudioBus> expected_audio_bus_;
|
| + ScopedVector<FakeAudioRenderCallback> fake_callbacks_;
|
| + scoped_ptr<FakeAudioRenderCallback> expected_callback_;
|
| + double epsilon_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(AudioTransformTest);
|
| +};
|
| +
|
| +// Ensure the buffer delay provided by AudioTransform is accurate.
|
| +TEST(AudioTransformTest, AudioDelay) {
|
| + // Choose input and output parameters such that the transform must make
|
| + // multiple calls to fill the buffer.
|
| + AudioParameters input_parameters = AudioParameters(
|
| + AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate,
|
| + kBitsPerChannel, kLowLatencyBufferSize);
|
| + AudioParameters output_parameters = AudioParameters(
|
| + AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate * 2,
|
| + kBitsPerChannel, kHighLatencyBufferSize);
|
| +
|
| + AudioTransform transform(input_parameters, output_parameters);
|
| + FakeAudioRenderCallback callback(0.2);
|
| + scoped_ptr<AudioBus> audio_bus = AudioBus::Create(output_parameters);
|
| + transform.AddInput(&callback);
|
| + transform.Transform(audio_bus.get());
|
| +
|
| + // Calculate the expected buffer delay for given AudioParameters.
|
| + double input_sample_rate = input_parameters.sample_rate();
|
| + int fill_count =
|
| + (output_parameters.frames_per_buffer() * input_sample_rate /
|
| + output_parameters.sample_rate()) / input_parameters.frames_per_buffer();
|
| +
|
| + base::TimeDelta input_frame_duration = base::TimeDelta::FromMicroseconds(
|
| + base::Time::kMicrosecondsPerSecond / input_sample_rate);
|
| +
|
| + int expected_last_delay_milliseconds =
|
| + fill_count * input_parameters.frames_per_buffer() *
|
| + input_frame_duration.InMillisecondsF();
|
| +
|
| + EXPECT_EQ(expected_last_delay_milliseconds,
|
| + callback.last_audio_delay_milliseconds());
|
| +}
|
| +
|
| +TEST_P(AudioTransformTest, NoInputs) {
|
| + FillAudioData(1.0f);
|
| + EXPECT_TRUE(RenderAndValidateAudioData(0.0f));
|
| +}
|
| +
|
| +TEST_P(AudioTransformTest, OneInput) {
|
| + RunTest(1);
|
| +}
|
| +
|
| +TEST_P(AudioTransformTest, ManyInputs) {
|
| + RunTest(kTransformInputs);
|
| +}
|
| +
|
| +INSTANTIATE_TEST_CASE_P(
|
| + // TODO(dalecurtis): Add test cases for channel transforms.
|
| + AudioTransformTest, AudioTransformTest, testing::Values(
|
| + // No resampling.
|
| + std::tr1::make_tuple(44100, 44100, 0.00000048),
|
| +
|
| + // Upsampling.
|
| + std::tr1::make_tuple(44100, 48000, 0.033),
|
| +
|
| + // Downsampling.
|
| + std::tr1::make_tuple(48000, 41000, 0.042)));
|
| +
|
| +} // namespace media
|
|
|