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/webrtc@5673 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/common_audio/resampler/push_sinc_resampler.cc b/common_audio/resampler/push_sinc_resampler.cc
index cbf1210..3469ff3 100644
--- a/common_audio/resampler/push_sinc_resampler.cc
+++ b/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 <string.h>
+#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 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 @@
// 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 @@
// 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<float>(source_ptr_[i]);
- source_available_ -= frames;
+ destination[i] = static_cast<float>(source_ptr_int_[i]);
}
+ source_available_ -= frames;
}
} // namespace webrtc
diff --git a/common_audio/resampler/push_sinc_resampler.h b/common_audio/resampler/push_sinc_resampler.h
index 82b9045..fa1bb3e 100644
--- a/common_audio/resampler/push_sinc_resampler.h
+++ b/common_audio/resampler/push_sinc_resampler.h
@@ -35,6 +35,10 @@
// 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 @@
private:
scoped_ptr<SincResampler> resampler_;
- scoped_array<float> float_buffer_;
- const int16_t* source_ptr_;
+ scoped_ptr<float[]> 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/common_audio/resampler/push_sinc_resampler_unittest.cc b/common_audio/resampler/push_sinc_resampler_unittest.cc
index d5005c6..1ca4fdf 100644
--- a/common_audio/resampler/push_sinc_resampler_unittest.cc
+++ b/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 @@
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 @@
}
};
-// 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<float> resampled_destination(new float[output_samples]);
- scoped_array<float> source(new float[input_samples]);
- scoped_array<int16_t> source_int(new int16_t[input_samples]);
- scoped_array<int16_t> destination_int(new int16_t[output_samples]);
+ scoped_ptr<float[]> resampled_destination(new float[output_samples]);
+ scoped_ptr<float[]> source(new float[input_samples]);
+ scoped_ptr<int16_t[]> source_int(new int16_t[input_samples]);
+ scoped_ptr<int16_t[]> 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 @@
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 @@
(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 @@
// 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]);
- scoped_array<float> source(new float[input_samples]);
- scoped_array<int16_t> source_int(new int16_t[input_block_size]);
- scoped_array<int16_t> destination_int(new int16_t[output_block_size]);
+ scoped_ptr<float[]> resampled_destination(new float[output_samples]);
+ scoped_ptr<float[]> pure_destination(new float[output_samples]);
+ scoped_ptr<float[]> source(new float[input_samples]);
+ scoped_ptr<int16_t[]> source_int(new int16_t[input_block_size]);
+ scoped_ptr<int16_t[]> 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 @@
// 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<int16_t>(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<float>(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 @@
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 @@
// 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 @@
// 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),