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
This commit is contained in:
@ -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 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<float>(source_ptr_[i]);
|
||||
source_available_ -= frames;
|
||||
destination[i] = static_cast<float>(source_ptr_int_[i]);
|
||||
}
|
||||
source_available_ -= frames;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -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<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.
|
||||
|
@ -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<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 @@ 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<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 @@ 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<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 @@ 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),
|
||||
|
Reference in New Issue
Block a user