Home | History | Annotate | Download | only in base
      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 
     18 namespace media {
     19 
     20 // Parameters which control the many input case tests.
     21 static const int kConvertInputs = 8;
     22 static const int kConvertCycles = 3;
     23 
     24 // Parameters used for testing.
     25 static const int kBitsPerChannel = 32;
     26 static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO;
     27 static const int kHighLatencyBufferSize = 2048;
     28 static const int kLowLatencyBufferSize = 256;
     29 static const int kSampleRate = 48000;
     30 
     31 // Number of full sine wave cycles for each Render() call.
     32 static const int kSineCycles = 4;
     33 
     34 // Tuple of <input rate, output rate, output channel layout, epsilon>.
     35 typedef std::tr1::tuple<int, int, ChannelLayout, double> AudioConverterTestData;
     36 class 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.
    197 TEST(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 
    230 TEST_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 
    237 TEST_P(AudioConverterTest, NoInputs) {
    238   FillAudioData(1.0f);
    239   EXPECT_TRUE(RenderAndValidateAudioData(0.0f));
    240 }
    241 
    242 TEST_P(AudioConverterTest, OneInput) {
    243   RunTest(1);
    244 }
    245 
    246 TEST_P(AudioConverterTest, ManyInputs) {
    247   RunTest(kConvertInputs);
    248 }
    249 
    250 INSTANTIATE_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