From d32797f8530882bea873ab6a42079b9c1912e3b9 Mon Sep 17 00:00:00 2001 From: "andrew@webrtc.org" Date: Mon, 10 Mar 2014 18:51:42 +0000 Subject: [PATCH] Add a float interface to PushSincResampler. Provides a push interface to SincResampler without the int16->float overhead. This is required to support resampling in the new AudioProcessing float path. BUG=2894 TESTED=unit tests R=turaj@webrtc.org Review URL: https://webrtc-codereview.appspot.com/9529004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5673 4adac7df-926f-26a2-2b94-8c16560cd09d --- .../resampler/push_sinc_resampler.cc | 44 +++++--- .../resampler/push_sinc_resampler.h | 9 +- .../resampler/push_sinc_resampler_unittest.cc | 102 ++++++++++++------ 3 files changed, 109 insertions(+), 46 deletions(-) diff --git a/webrtc/common_audio/resampler/push_sinc_resampler.cc b/webrtc/common_audio/resampler/push_sinc_resampler.cc index cbf1210429..3469ff3f84 100644 --- a/webrtc/common_audio/resampler/push_sinc_resampler.cc +++ b/webrtc/common_audio/resampler/push_sinc_resampler.cc @@ -9,22 +9,22 @@ */ #include "webrtc/common_audio/include/audio_util.h" -#include "webrtc/common_audio/resampler/push_sinc_resampler.h" #include +#include "webrtc/common_audio/resampler/push_sinc_resampler.h" + namespace webrtc { -PushSincResampler::PushSincResampler(int source_frames, - int destination_frames) +PushSincResampler::PushSincResampler(int source_frames, int destination_frames) : resampler_(new SincResampler(source_frames * 1.0 / destination_frames, - source_frames, this)), - float_buffer_(new float[destination_frames]), + source_frames, + this)), source_ptr_(NULL), + source_ptr_int_(NULL), destination_frames_(destination_frames), first_pass_(true), - source_available_(0) { -} + source_available_(0) {} PushSincResampler::~PushSincResampler() { } @@ -33,6 +33,21 @@ int PushSincResampler::Resample(const int16_t* source, int source_length, int16_t* destination, int destination_capacity) { + if (!float_buffer_.get()) + float_buffer_.reset(new float[destination_frames_]); + + source_ptr_int_ = source; + // Pass NULL as the float source to have Run() read from the int16 source. + Resample(NULL, source_length, float_buffer_.get(), destination_frames_); + RoundToInt16(float_buffer_.get(), destination_frames_, destination); + source_ptr_int_ = NULL; + return destination_frames_; +} + +int PushSincResampler::Resample(const float* source, + int source_length, + float* destination, + int destination_capacity) { assert(source_length == resampler_->request_frames()); assert(destination_capacity >= destination_frames_); // Cache the source pointer. Calling Resample() will immediately trigger @@ -54,16 +69,14 @@ int PushSincResampler::Resample(const int16_t* source, // request in order to prime the buffer with a single Run() request for // |source_frames|. if (first_pass_) - resampler_->Resample(resampler_->ChunkSize(), float_buffer_.get()); + resampler_->Resample(resampler_->ChunkSize(), destination); - resampler_->Resample(destination_frames_, float_buffer_.get()); - RoundToInt16(float_buffer_.get(), destination_frames_, destination); + resampler_->Resample(destination_frames_, destination); source_ptr_ = NULL; return destination_frames_; } void PushSincResampler::Run(int frames, float* destination) { - assert(source_ptr_ != NULL); // Ensure we are only asked for the available samples. This would fail if // Run() was triggered more than once per Resample() call. assert(source_available_ == frames); @@ -73,11 +86,16 @@ void PushSincResampler::Run(int frames, float* destination) { // discarded, as described in Resample(). memset(destination, 0, frames * sizeof(float)); first_pass_ = false; + return; + } + + if (source_ptr_) { + memcpy(destination, source_ptr_, frames * sizeof(float)); } else { for (int i = 0; i < frames; ++i) - destination[i] = static_cast(source_ptr_[i]); - source_available_ -= frames; + destination[i] = static_cast(source_ptr_int_[i]); } + source_available_ -= frames; } } // namespace webrtc diff --git a/webrtc/common_audio/resampler/push_sinc_resampler.h b/webrtc/common_audio/resampler/push_sinc_resampler.h index 82b9045fce..fa1bb3e71d 100644 --- a/webrtc/common_audio/resampler/push_sinc_resampler.h +++ b/webrtc/common_audio/resampler/push_sinc_resampler.h @@ -35,6 +35,10 @@ class PushSincResampler : public SincResamplerCallback { // to |destination_frames|). int Resample(const int16_t* source, int source_frames, int16_t* destination, int destination_capacity); + int Resample(const float* source, + int source_frames, + float* destination, + int destination_capacity); // Implements SincResamplerCallback. virtual void Run(int frames, float* destination) OVERRIDE; @@ -43,8 +47,9 @@ class PushSincResampler : public SincResamplerCallback { private: scoped_ptr resampler_; - scoped_array float_buffer_; - const int16_t* source_ptr_; + scoped_ptr float_buffer_; + const float* source_ptr_; + const int16_t* source_ptr_int_; const int destination_frames_; // True on the first call to Resample(), to prime the SincResampler buffer. diff --git a/webrtc/common_audio/resampler/push_sinc_resampler_unittest.cc b/webrtc/common_audio/resampler/push_sinc_resampler_unittest.cc index d5005c6d05..1ca4fdf936 100644 --- a/webrtc/common_audio/resampler/push_sinc_resampler_unittest.cc +++ b/webrtc/common_audio/resampler/push_sinc_resampler_unittest.cc @@ -12,6 +12,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/common_audio/include/audio_util.h" #include "webrtc/common_audio/resampler/push_sinc_resampler.h" #include "webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" @@ -34,6 +35,9 @@ class PushSincResamplerTest virtual ~PushSincResamplerTest() {} protected: + void ResampleBenchmarkTest(bool int_format); + void ResampleTest(bool int_format); + int input_rate_; int output_rate_; double rms_error_; @@ -47,20 +51,18 @@ class ZeroSource : public SincResamplerCallback { } }; -// Disabled because it takes too long to run routinely. Use for performance -// benchmarking when needed. -TEST_P(PushSincResamplerTest, DISABLED_ResampleBenchmark) { +void PushSincResamplerTest::ResampleBenchmarkTest(bool int_format) { const int input_samples = input_rate_ / 100; const int output_samples = output_rate_ / 100; - const int kResampleIterations = 200000; + const int kResampleIterations = 500000; // Source for data to be resampled. ZeroSource resampler_source; - scoped_array resampled_destination(new float[output_samples]); - scoped_array source(new float[input_samples]); - scoped_array source_int(new int16_t[input_samples]); - scoped_array destination_int(new int16_t[output_samples]); + scoped_ptr resampled_destination(new float[output_samples]); + scoped_ptr source(new float[input_samples]); + scoped_ptr source_int(new int16_t[input_samples]); + scoped_ptr destination_int(new int16_t[output_samples]); resampler_source.Run(input_samples, source.get()); for (int i = 0; i < input_samples; ++i) { @@ -82,10 +84,22 @@ TEST_P(PushSincResamplerTest, DISABLED_ResampleBenchmark) { PushSincResampler resampler(input_samples, output_samples); start = TickTime::Now(); - for (int i = 0; i < kResampleIterations; ++i) { - EXPECT_EQ(output_samples, - resampler.Resample(source_int.get(), input_samples, - destination_int.get(), output_samples)); + if (int_format) { + for (int i = 0; i < kResampleIterations; ++i) { + EXPECT_EQ(output_samples, + resampler.Resample(source_int.get(), + input_samples, + destination_int.get(), + output_samples)); + } + } else { + for (int i = 0; i < kResampleIterations; ++i) { + EXPECT_EQ(output_samples, + resampler.Resample(source.get(), + input_samples, + resampled_destination.get(), + output_samples)); + } } double total_time_us = (TickTime::Now() - start).Microseconds(); printf("PushSincResampler took %.2f us per frame; which is a %.1f%% overhead " @@ -93,8 +107,18 @@ TEST_P(PushSincResamplerTest, DISABLED_ResampleBenchmark) { (total_time_us - total_time_sinc_us) / total_time_sinc_us * 100); } +// Disabled because it takes too long to run routinely. Use for performance +// benchmarking when needed. +TEST_P(PushSincResamplerTest, DISABLED_BenchmarkInt) { + ResampleBenchmarkTest(true); +} + +TEST_P(PushSincResamplerTest, DISABLED_BenchmarkFloat) { + ResampleBenchmarkTest(false); +} + // Tests resampling using a given input and output sample rate. -TEST_P(PushSincResamplerTest, Resample) { +void PushSincResamplerTest::ResampleTest(bool int_format) { // Make comparisons using one second of data. static const double kTestDurationSecs = 1; // 10 ms blocks. @@ -115,11 +139,11 @@ TEST_P(PushSincResamplerTest, Resample) { // 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 resampled_destination(new float[output_samples]); - scoped_array pure_destination(new float[output_samples]); - scoped_array source(new float[input_samples]); - scoped_array source_int(new int16_t[input_block_size]); - scoped_array destination_int(new int16_t[output_block_size]); + scoped_ptr resampled_destination(new float[output_samples]); + scoped_ptr pure_destination(new float[output_samples]); + scoped_ptr source(new float[input_samples]); + scoped_ptr source_int(new int16_t[input_block_size]); + scoped_ptr destination_int(new int16_t[output_block_size]); // The sinc resampler has an implicit delay of approximately half the kernel // size at the input sample rate. By moving to a push model, this delay @@ -134,17 +158,27 @@ TEST_P(PushSincResamplerTest, Resample) { // With the PushSincResampler, we produce the signal block-by-10ms-block // rather than in a single pass, to exercise how it will be used in WebRTC. resampler_source.Run(input_samples, source.get()); - for (int i = 0; i < kNumBlocks; ++i) { - for (int j = 0; j < input_block_size; ++j) { - source_int[j] = static_cast(floor(32767 * - source[i * input_block_size + j] + 0.5)); + if (int_format) { + for (int i = 0; i < kNumBlocks; ++i) { + ScaleAndRoundToInt16( + &source[i * input_block_size], input_block_size, source_int.get()); + EXPECT_EQ(output_block_size, + resampler.Resample(source_int.get(), + input_block_size, + destination_int.get(), + output_block_size)); + ScaleToFloat(destination_int.get(), + output_block_size, + &resampled_destination[i * output_block_size]); } - EXPECT_EQ(output_block_size, - resampler.Resample(source_int.get(), input_block_size, - destination_int.get(), output_block_size)); - for (int j = 0; j < output_block_size; ++j) { - resampled_destination[i * output_block_size + j] = - static_cast(destination_int[j]) / 32767; + } else { + for (int i = 0; i < kNumBlocks; ++i) { + EXPECT_EQ( + output_block_size, + resampler.Resample(&source[i * input_block_size], + input_block_size, + &resampled_destination[i * output_block_size], + output_block_size)); } } @@ -204,13 +238,19 @@ TEST_P(PushSincResamplerTest, Resample) { EXPECT_LE(high_freq_max_error, kHighFrequencyMaxError); } +TEST_P(PushSincResamplerTest, ResampleInt) { ResampleTest(true); } + +TEST_P(PushSincResamplerTest, ResampleFloat) { ResampleTest(false); } + // Almost all conversions have an RMS error of around -14 dbFS. static const double kResamplingRMSError = -14.42; // Thresholds chosen arbitrarily based on what each resampling reported during // testing. All thresholds are in dbFS, http://en.wikipedia.org/wiki/DBFS. INSTANTIATE_TEST_CASE_P( - PushSincResamplerTest, PushSincResamplerTest, testing::Values( + PushSincResamplerTest, + PushSincResamplerTest, + testing::Values( // First run through the rates tested in SincResamplerTest. The // thresholds are identical. // @@ -261,7 +301,7 @@ INSTANTIATE_TEST_CASE_P( // practice anyway. // To 8 kHz - std::tr1::make_tuple(8000, 8000, kResamplingRMSError, -75.51), + std::tr1::make_tuple(8000, 8000, kResamplingRMSError, -75.50), std::tr1::make_tuple(16000, 8000, -18.56, -28.79), std::tr1::make_tuple(32000, 8000, -20.36, -14.13), std::tr1::make_tuple(44100, 8000, -21.00, -11.39), @@ -278,7 +318,7 @@ INSTANTIATE_TEST_CASE_P( // To 32 kHz std::tr1::make_tuple(8000, 32000, kResamplingRMSError, -70.30), std::tr1::make_tuple(16000, 32000, kResamplingRMSError, -75.51), - std::tr1::make_tuple(32000, 32000, kResamplingRMSError, -75.56), + std::tr1::make_tuple(32000, 32000, kResamplingRMSError, -75.51), std::tr1::make_tuple(44100, 32000, -16.44, -51.10), std::tr1::make_tuple(48000, 32000, -16.90, -44.03), std::tr1::make_tuple(96000, 32000, -19.61, -18.04),