| // 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. | 
 |  | 
 | #include <cmath> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/bind_helpers.h" | 
 | #include "base/logging.h" | 
 | #include "base/memory/scoped_ptr.h" | 
 | #include "media/base/audio_bus.h" | 
 | #include "media/base/multi_channel_resampler.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 |  | 
 | namespace media { | 
 |  | 
 | // Just test a basic resampling case.  The SincResampler unit test will take | 
 | // care of accuracy testing; we just need to check that multichannel works as | 
 | // expected within some tolerance. | 
 | static const float kScaleFactor = 192000.0f / 44100.0f; | 
 |  | 
 | // Simulate large and small sample requests used by the different audio paths. | 
 | static const int kHighLatencySize = 8192; | 
 | // Low latency buffers show a larger error than high latency ones.  Which makes | 
 | // sense since each error represents a larger portion of the total request. | 
 | static const int kLowLatencySize = 128; | 
 |  | 
 | // Test fill value. | 
 | static const float kFillValue = 0.1f; | 
 |  | 
 | // Chosen arbitrarily based on what each resampler reported during testing. | 
 | static const double kLowLatencyMaxRMSError = 0.0036; | 
 | static const double kLowLatencyMaxError = 0.04; | 
 | static const double kHighLatencyMaxRMSError = 0.0036; | 
 | static const double kHighLatencyMaxError = 0.04; | 
 |  | 
 | class MultiChannelResamplerTest | 
 |     : public testing::TestWithParam<int> { | 
 |  public: | 
 |   MultiChannelResamplerTest() | 
 |       : last_frame_delay_(-1) { | 
 |   } | 
 |   virtual ~MultiChannelResamplerTest() {} | 
 |  | 
 |   void InitializeAudioData(int channels, int frames) { | 
 |     frames_ = frames; | 
 |     audio_bus_ = AudioBus::Create(channels, frames); | 
 |   } | 
 |  | 
 |   // MultiChannelResampler::MultiChannelAudioSourceProvider implementation, just | 
 |   // fills the provided audio_data with |kFillValue|. | 
 |   virtual void ProvideInput(int frame_delay, AudioBus* audio_bus) { | 
 |     EXPECT_GT(frame_delay, last_frame_delay_); | 
 |     last_frame_delay_ = frame_delay; | 
 |  | 
 |     float fill_value = fill_junk_values_ ? (1 / kFillValue) : kFillValue; | 
 |     EXPECT_EQ(audio_bus->channels(), audio_bus_->channels()); | 
 |     for (int i = 0; i < audio_bus->channels(); ++i) | 
 |       for (int j = 0; j < audio_bus->frames(); ++j) | 
 |         audio_bus->channel(i)[j] = fill_value; | 
 |   } | 
 |  | 
 |   void MultiChannelTest(int channels, int frames, double expected_max_rms_error, | 
 |                         double expected_max_error) { | 
 |     InitializeAudioData(channels, frames); | 
 |     MultiChannelResampler resampler(channels, kScaleFactor, base::Bind( | 
 |         &MultiChannelResamplerTest::ProvideInput, base::Unretained(this))); | 
 |  | 
 |     // First prime the resampler with some junk data, so we can verify Flush(). | 
 |     fill_junk_values_ = true; | 
 |     resampler.Resample(audio_bus_.get(), 1); | 
 |     resampler.Flush(); | 
 |     fill_junk_values_ = false; | 
 |  | 
 |     // The last frame delay should be strictly less than the total frame count. | 
 |     EXPECT_LT(last_frame_delay_, audio_bus_->frames()); | 
 |     last_frame_delay_ = -1; | 
 |  | 
 |     // If Flush() didn't work, the rest of the tests will fail. | 
 |     resampler.Resample(audio_bus_.get(), frames); | 
 |     TestValues(expected_max_rms_error, expected_max_error); | 
 |   } | 
 |  | 
 |   void HighLatencyTest(int channels) { | 
 |     MultiChannelTest(channels, kHighLatencySize, kHighLatencyMaxRMSError, | 
 |                      kHighLatencyMaxError); | 
 |   } | 
 |  | 
 |   void LowLatencyTest(int channels) { | 
 |     MultiChannelTest(channels, kLowLatencySize, kLowLatencyMaxRMSError, | 
 |                      kLowLatencyMaxError); | 
 |   } | 
 |  | 
 |   void TestValues(double expected_max_rms_error, double expected_max_error ) { | 
 |     // Calculate Root-Mean-Square-Error for the resampling. | 
 |     double max_error = 0.0; | 
 |     double sum_of_squares = 0.0; | 
 |     for (int i = 0; i < audio_bus_->channels(); ++i) { | 
 |       for (int j = 0; j < frames_; ++j) { | 
 |         // Ensure all values are accounted for. | 
 |         ASSERT_NE(audio_bus_->channel(i)[j], 0); | 
 |  | 
 |         double error = fabs(audio_bus_->channel(i)[j] - kFillValue); | 
 |         max_error = std::max(max_error, error); | 
 |         sum_of_squares += error * error; | 
 |       } | 
 |     } | 
 |  | 
 |     double rms_error = sqrt( | 
 |         sum_of_squares / (frames_ * audio_bus_->channels())); | 
 |  | 
 |     EXPECT_LE(rms_error, expected_max_rms_error); | 
 |     EXPECT_LE(max_error, expected_max_error); | 
 |   } | 
 |  | 
 |  protected: | 
 |   int frames_; | 
 |   bool fill_junk_values_; | 
 |   scoped_ptr<AudioBus> audio_bus_; | 
 |   int last_frame_delay_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(MultiChannelResamplerTest); | 
 | }; | 
 |  | 
 | TEST_P(MultiChannelResamplerTest, HighLatency) { | 
 |   HighLatencyTest(GetParam()); | 
 | } | 
 |  | 
 | TEST_P(MultiChannelResamplerTest, LowLatency) { | 
 |   LowLatencyTest(GetParam()); | 
 | } | 
 |  | 
 | // Test common channel layouts: mono, stereo, 5.1, 7.1. | 
 | INSTANTIATE_TEST_CASE_P( | 
 |     MultiChannelResamplerTest, MultiChannelResamplerTest, | 
 |     testing::Values(1, 2, 6, 8)); | 
 |  | 
 | }  // namespace media |