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),