1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// MSVC++ requires this to be set before any other includes to get M_PI.
6#define _USE_MATH_DEFINES
7
8#include <cmath>
9
10#include "base/memory/scoped_ptr.h"
11#include "base/memory/scoped_vector.h"
12#include "base/strings/string_number_conversions.h"
13#include "media/base/audio_converter.h"
14#include "media/base/fake_audio_render_callback.h"
15#include "testing/gmock/include/gmock/gmock.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18namespace media {
19
20// Parameters which control the many input case tests.
21static const int kConvertInputs = 8;
22static const int kConvertCycles = 3;
23
24// Parameters used for testing.
25static const int kBitsPerChannel = 32;
26static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO;
27static const int kHighLatencyBufferSize = 2048;
28static const int kLowLatencyBufferSize = 256;
29static const int kSampleRate = 48000;
30
31// Number of full sine wave cycles for each Render() call.
32static const int kSineCycles = 4;
33
34// Tuple of <input rate, output rate, output channel layout, epsilon>.
35typedef std::tr1::tuple<int, int, ChannelLayout, double> AudioConverterTestData;
36class AudioConverterTest
37    : public testing::TestWithParam<AudioConverterTestData> {
38 public:
39  AudioConverterTest()
40      : epsilon_(std::tr1::get<3>(GetParam())) {
41    // Create input and output parameters based on test parameters.
42    input_parameters_ = AudioParameters(
43        AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout,
44        std::tr1::get<0>(GetParam()), kBitsPerChannel, kHighLatencyBufferSize);
45    output_parameters_ = AudioParameters(
46        AudioParameters::AUDIO_PCM_LOW_LATENCY, std::tr1::get<2>(GetParam()),
47        std::tr1::get<1>(GetParam()), 16, kLowLatencyBufferSize);
48
49    converter_.reset(new AudioConverter(
50        input_parameters_, output_parameters_, false));
51
52    audio_bus_ = AudioBus::Create(output_parameters_);
53    expected_audio_bus_ = AudioBus::Create(output_parameters_);
54
55    // Allocate one callback for generating expected results.
56    double step = kSineCycles / static_cast<double>(
57        output_parameters_.frames_per_buffer());
58    expected_callback_.reset(new FakeAudioRenderCallback(step));
59  }
60
61  // Creates |count| input callbacks to be used for conversion testing.
62  void InitializeInputs(int count) {
63    // Setup FakeAudioRenderCallback step to compensate for resampling.
64    double scale_factor = input_parameters_.sample_rate() /
65        static_cast<double>(output_parameters_.sample_rate());
66    double step = kSineCycles / (scale_factor *
67        static_cast<double>(output_parameters_.frames_per_buffer()));
68
69    for (int i = 0; i < count; ++i) {
70      fake_callbacks_.push_back(new FakeAudioRenderCallback(step));
71      converter_->AddInput(fake_callbacks_[i]);
72    }
73  }
74
75  // Resets all input callbacks to a pristine state.
76  void Reset() {
77    converter_->Reset();
78    for (size_t i = 0; i < fake_callbacks_.size(); ++i)
79      fake_callbacks_[i]->reset();
80    expected_callback_->reset();
81  }
82
83  // Sets the volume on all input callbacks to |volume|.
84  void SetVolume(float volume) {
85    for (size_t i = 0; i < fake_callbacks_.size(); ++i)
86      fake_callbacks_[i]->set_volume(volume);
87  }
88
89  // Validates audio data between |audio_bus_| and |expected_audio_bus_| from
90  // |index|..|frames| after |scale| is applied to the expected audio data.
91  bool ValidateAudioData(int index, int frames, float scale) {
92    for (int i = 0; i < audio_bus_->channels(); ++i) {
93      for (int j = index; j < frames; ++j) {
94        double error = fabs(audio_bus_->channel(i)[j] -
95            expected_audio_bus_->channel(i)[j] * scale);
96        if (error > epsilon_) {
97          EXPECT_NEAR(expected_audio_bus_->channel(i)[j] * scale,
98                      audio_bus_->channel(i)[j], epsilon_)
99              << " i=" << i << ", j=" << j;
100          return false;
101        }
102      }
103    }
104    return true;
105  }
106
107  // Runs a single Convert() stage, fills |expected_audio_bus_| appropriately,
108  // and validates equality with |audio_bus_| after |scale| is applied.
109  bool RenderAndValidateAudioData(float scale) {
110    // Render actual audio data.
111    converter_->Convert(audio_bus_.get());
112
113    // Render expected audio data.
114    expected_callback_->Render(expected_audio_bus_.get(), 0);
115
116    // Zero out unused channels in the expected AudioBus just as AudioConverter
117    // would during channel mixing.
118    for (int i = input_parameters_.channels();
119         i < output_parameters_.channels(); ++i) {
120      memset(expected_audio_bus_->channel(i), 0,
121             audio_bus_->frames() * sizeof(*audio_bus_->channel(i)));
122    }
123
124    return ValidateAudioData(0, audio_bus_->frames(), scale);
125  }
126
127  // Fills |audio_bus_| fully with |value|.
128  void FillAudioData(float value) {
129    for (int i = 0; i < audio_bus_->channels(); ++i) {
130      std::fill(audio_bus_->channel(i),
131                audio_bus_->channel(i) + audio_bus_->frames(), value);
132    }
133  }
134
135  // Verifies converter output with a |inputs| number of transform inputs.
136  void RunTest(int inputs) {
137    InitializeInputs(inputs);
138
139    SetVolume(0);
140    for (int i = 0; i < kConvertCycles; ++i)
141      ASSERT_TRUE(RenderAndValidateAudioData(0));
142
143    Reset();
144
145    // Set a different volume for each input and verify the results.
146    float total_scale = 0;
147    for (size_t i = 0; i < fake_callbacks_.size(); ++i) {
148      float volume = static_cast<float>(i) / fake_callbacks_.size();
149      total_scale += volume;
150      fake_callbacks_[i]->set_volume(volume);
151    }
152    for (int i = 0; i < kConvertCycles; ++i)
153      ASSERT_TRUE(RenderAndValidateAudioData(total_scale));
154
155    Reset();
156
157    // Remove every other input.
158    for (size_t i = 1; i < fake_callbacks_.size(); i += 2)
159      converter_->RemoveInput(fake_callbacks_[i]);
160
161    SetVolume(1);
162    float scale = inputs > 1 ? inputs / 2.0f : inputs;
163    for (int i = 0; i < kConvertCycles; ++i)
164      ASSERT_TRUE(RenderAndValidateAudioData(scale));
165  }
166
167 protected:
168  virtual ~AudioConverterTest() {}
169
170  // Converter under test.
171  scoped_ptr<AudioConverter> converter_;
172
173  // Input and output parameters used for AudioConverter construction.
174  AudioParameters input_parameters_;
175  AudioParameters output_parameters_;
176
177  // Destination AudioBus for AudioConverter output.
178  scoped_ptr<AudioBus> audio_bus_;
179
180  // AudioBus containing expected results for comparison with |audio_bus_|.
181  scoped_ptr<AudioBus> expected_audio_bus_;
182
183  // Vector of all input callbacks used to drive AudioConverter::Convert().
184  ScopedVector<FakeAudioRenderCallback> fake_callbacks_;
185
186  // Parallel input callback which generates the expected output.
187  scoped_ptr<FakeAudioRenderCallback> expected_callback_;
188
189  // Epsilon value with which to perform comparisons between |audio_bus_| and
190  // |expected_audio_bus_|.
191  double epsilon_;
192
193  DISALLOW_COPY_AND_ASSIGN(AudioConverterTest);
194};
195
196// Ensure the buffer delay provided by AudioConverter is accurate.
197TEST(AudioConverterTest, AudioDelay) {
198  // Choose input and output parameters such that the transform must make
199  // multiple calls to fill the buffer.
200  AudioParameters input_parameters = AudioParameters(
201      AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate,
202      kBitsPerChannel, kLowLatencyBufferSize);
203  AudioParameters output_parameters = AudioParameters(
204      AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate * 2,
205      kBitsPerChannel, kHighLatencyBufferSize);
206
207  AudioConverter converter(input_parameters, output_parameters, false);
208  FakeAudioRenderCallback callback(0.2);
209  scoped_ptr<AudioBus> audio_bus = AudioBus::Create(output_parameters);
210  converter.AddInput(&callback);
211  converter.Convert(audio_bus.get());
212
213  // Calculate the expected buffer delay for given AudioParameters.
214  double input_sample_rate = input_parameters.sample_rate();
215  int fill_count =
216      (output_parameters.frames_per_buffer() * input_sample_rate /
217       output_parameters.sample_rate()) / input_parameters.frames_per_buffer();
218
219  base::TimeDelta input_frame_duration = base::TimeDelta::FromMicroseconds(
220      base::Time::kMicrosecondsPerSecond / input_sample_rate);
221
222  int expected_last_delay_milliseconds =
223      fill_count * input_parameters.frames_per_buffer() *
224      input_frame_duration.InMillisecondsF();
225
226  EXPECT_EQ(expected_last_delay_milliseconds,
227            callback.last_audio_delay_milliseconds());
228}
229
230TEST_P(AudioConverterTest, ArbitraryOutputRequestSize) {
231  // Resize output bus to be half of |output_parameters_|'s frames_per_buffer().
232  audio_bus_ = AudioBus::Create(output_parameters_.channels(),
233                                output_parameters_.frames_per_buffer() / 2);
234  RunTest(1);
235}
236
237TEST_P(AudioConverterTest, NoInputs) {
238  FillAudioData(1.0f);
239  EXPECT_TRUE(RenderAndValidateAudioData(0.0f));
240}
241
242TEST_P(AudioConverterTest, OneInput) {
243  RunTest(1);
244}
245
246TEST_P(AudioConverterTest, ManyInputs) {
247  RunTest(kConvertInputs);
248}
249
250INSTANTIATE_TEST_CASE_P(
251    AudioConverterTest, AudioConverterTest, testing::Values(
252        // No resampling. No channel mixing.
253        std::tr1::make_tuple(44100, 44100, CHANNEL_LAYOUT_STEREO, 0.00000048),
254
255        // Upsampling. Channel upmixing.
256        std::tr1::make_tuple(44100, 48000, CHANNEL_LAYOUT_QUAD, 0.033),
257
258        // Downsampling. Channel downmixing.
259        std::tr1::make_tuple(48000, 41000, CHANNEL_LAYOUT_MONO, 0.042)));
260
261}  // namespace media
262