Index: media/base/sinc_resampler_unittest.cc |
diff --git a/media/base/sinc_resampler_unittest.cc b/media/base/sinc_resampler_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5e3f45cc868daa85e8a3a09afa63dd620055282b |
--- /dev/null |
+++ b/media/base/sinc_resampler_unittest.cc |
@@ -0,0 +1,175 @@ |
+// 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/bind.h" |
+#include "base/bind_helpers.h" |
+#include "base/logging.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/stringprintf.h" |
+#include "media/base/sinc_resampler.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace media { |
+ |
+class SweptSineSource { |
Ami GONE FROM CHROMIUM
2012/07/03 20:54:42
How about some commentary?
DaleCurtis
2012/07/10 01:00:25
Done.
|
+ public: |
+ SweptSineSource(int sample_rate, int output_samples, double max_frequency) |
+ : sample_rate_(sample_rate), |
+ total_output_samples_(output_samples), |
+ min_frequency_(kMinFrequency), |
+ max_frequency_(max_frequency), |
+ frequency_(min_frequency_), |
+ phase_(0) { |
+ } |
+ |
+ virtual ~SweptSineSource() {} |
+ |
+ void ProvideInput(float* destination, int frames) { |
+ for (int i = 0; i < frames; ++i) { |
+ // Filter out frequencies higher than Nyquist. |
+ if (frequency_ > 0.5 * sample_rate_) { |
Ami GONE FROM CHROMIUM
2012/07/03 20:54:42
is this not always max_frequency_?
DaleCurtis
2012/07/10 01:00:25
No, per Chris max_frequency is the nyquist limit o
|
+ destination[i] = 0; |
+ } else { |
+ destination[i] = sin(phase_); |
+ phase_ += 2 * M_PI * frequency_ / sample_rate_; |
+ frequency_ += (max_frequency_ - min_frequency_) / total_output_samples_; |
Ami GONE FROM CHROMIUM
2012/07/03 20:54:42
FWIW, ISTM the RHS here is a ctor-time constant, s
DaleCurtis
2012/07/10 01:00:25
Cleaned this up a bit using Chris's new method.
|
+ } |
+ } |
+ } |
+ |
+ private: |
+ static const int kMinFrequency = 5; |
Ami GONE FROM CHROMIUM
2012/07/03 20:54:42
enum
DaleCurtis
2012/07/10 01:00:25
Done.
|
+ |
+ double sample_rate_; |
+ int total_output_samples_; |
+ double min_frequency_; |
+ double max_frequency_; |
+ double frequency_; |
+ double phase_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(SweptSineSource); |
+}; |
+ |
+typedef std::tr1::tuple<int, int, double, double, double> SincResamplerTestData; |
Ami GONE FROM CHROMIUM
2012/07/03 20:54:42
Chromium's Tuple class is slightly less ugly to wo
DaleCurtis
2012/07/10 01:00:25
I thought of that, but I think this ends up being
|
+class SincResamplerTestCase |
+ : public testing::TestWithParam<SincResamplerTestData> { |
+ public: |
+ SincResamplerTestCase() |
+ : input_rate_(std::tr1::get<0>(GetParam())), |
Ami GONE FROM CHROMIUM
2012/07/03 20:54:42
using?
DaleCurtis
2012/07/10 01:00:25
Clearer this way I think.
|
+ output_rate_(std::tr1::get<1>(GetParam())), |
+ rms_error_(std::tr1::get<2>(GetParam())), |
+ low_freq_error_(std::tr1::get<3>(GetParam())), |
+ high_freq_error_(std::tr1::get<4>(GetParam())) { |
+ } |
+ |
+ virtual ~SincResamplerTestCase() {} |
+ |
+ protected: |
+ int input_rate_; |
+ int output_rate_; |
+ double rms_error_; |
+ double low_freq_error_; |
+ double high_freq_error_; |
+}; |
+ |
+// Tests resampling using a given input and output sample rate. |
+TEST_P(SincResamplerTestCase, Resample) { |
+ // Make comparisons using one second of data. |
+ static const int kTestDurationSecs = 1; |
+ int input_samples = kTestDurationSecs * input_rate_; |
+ int output_samples = kTestDurationSecs * output_rate_; |
+ |
+ // Nyquist frequency for the input sampling rate. |
+ double input_nyquist_freq = 0.5 * input_rate_; |
+ |
+ // Source for data to be resampled. |
+ SweptSineSource resampler_source( |
+ input_rate_, input_samples, input_nyquist_freq); |
+ |
+ SincResampler resampler( |
+ base::Bind( |
+ &SweptSineSource::ProvideInput, base::Unretained(&resampler_source)), |
+ input_rate_ / static_cast<double>(output_rate_)); |
+ |
+ // 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. |
+ scoped_array<float> resampled_destination(new float[output_samples]); |
+ scoped_array<float> pure_destination(new float[output_samples]); |
+ |
+ // Generate resampled signal. |
+ resampler.Resample(resampled_destination.get(), output_samples); |
+ |
+ // Generate pure signal. |
+ SweptSineSource pure_source( |
+ output_rate_, output_samples, input_nyquist_freq); |
+ pure_source.ProvideInput(pure_destination.get(), output_samples); |
+ |
+ // TODO(dalecurtis): Figure out what we need to do here w/ crogers. |
+ // // Calculate Root-Mean-Square-Error for the resampling. |
+ // double sum_of_squares = 0; |
+ // double max_error = 0; |
+ // for (int i = 0; i < output_samples; ++i) { |
+ // double error = fabs(resampled_destination[i] - pure_destination[i]); |
+ // max_error = std::max(error, max_error); |
+ // sum_of_squares += error * error; |
+ // } |
+ |
+ // double rms_error = sqrt(sum_of_squares / output_samples); |
+ |
+ // EXPECT_LT(rms_error, rms_error_); |
+ // EXPECT_LT(max_error, low_freq_error_); |
+ // EXPECT_LT(max_error, high_freq_error_); |
+} |
+ |
+INSTANTIATE_TEST_CASE_P( |
+ SincResamplerTest, SincResamplerTestCase, testing::Values( |
+ // To 44.1kHz |
+ std::tr1::make_tuple(8000, 44100, 0, 0, 0), |
Ami GONE FROM CHROMIUM
2012/07/03 20:54:42
I guess these 0,0,0's are placeholders that won't
DaleCurtis
2012/07/10 01:00:25
Correct.
|
+ std::tr1::make_tuple(11025, 44100, 0, 0, 0), |
+ std::tr1::make_tuple(16000, 44100, 0, 0, 0), |
+ std::tr1::make_tuple(22050, 44100, 0, 0, 0), |
+ std::tr1::make_tuple(32000, 44100, 0, 0, 0), |
+ std::tr1::make_tuple(44100, 44100, 0, 0, 0), |
+ std::tr1::make_tuple(48000, 44100, 0, 0, 0), |
+ std::tr1::make_tuple(96000, 44100, 0, 0, 0), |
+ std::tr1::make_tuple(192000, 44100, 0, 0, 0), |
+ |
+ // To 48kHz |
+ std::tr1::make_tuple(8000, 48000, 0, 0, 0), |
+ std::tr1::make_tuple(11025, 48000, 0, 0, 0), |
+ std::tr1::make_tuple(16000, 48000, 0, 0, 0), |
+ std::tr1::make_tuple(22050, 48000, 0, 0, 0), |
+ std::tr1::make_tuple(32000, 48000, 0, 0, 0), |
+ std::tr1::make_tuple(44100, 48000, 0, 0, 0), |
+ std::tr1::make_tuple(48000, 48000, 0, 0, 0), |
+ std::tr1::make_tuple(96000, 48000, 0, 0, 0), |
+ std::tr1::make_tuple(192000, 48000, 0, 0, 0), |
+ |
+ // To 96kHz |
+ std::tr1::make_tuple(8000, 96000, 0, 0, 0), |
+ std::tr1::make_tuple(11025, 96000, 0, 0, 0), |
+ std::tr1::make_tuple(16000, 96000, 0, 0, 0), |
+ std::tr1::make_tuple(22050, 96000, 0, 0, 0), |
+ std::tr1::make_tuple(32000, 96000, 0, 0, 0), |
+ std::tr1::make_tuple(44100, 96000, 0, 0, 0), |
+ std::tr1::make_tuple(48000, 96000, 0, 0, 0), |
+ std::tr1::make_tuple(96000, 96000, 0, 0, 0), |
+ std::tr1::make_tuple(192000, 96000, 0, 0, 0), |
+ |
+ // To 192kHz |
+ std::tr1::make_tuple(8000, 192000, 0, 0, 0), |
+ std::tr1::make_tuple(11025, 192000, 0, 0, 0), |
+ std::tr1::make_tuple(16000, 192000, 0, 0, 0), |
+ std::tr1::make_tuple(22050, 192000, 0, 0, 0), |
+ std::tr1::make_tuple(32000, 192000, 0, 0, 0), |
+ std::tr1::make_tuple(44100, 192000, 0, 0, 0), |
+ std::tr1::make_tuple(48000, 192000, 0, 0, 0), |
+ std::tr1::make_tuple(96000, 192000, 0, 0, 0), |
+ std::tr1::make_tuple(192000, 192000, 0, 0, 0))); |
+ |
+} // namespace media |