aboutsummaryrefslogtreecommitdiff
path: root/webrtc/common_audio/resampler
diff options
context:
space:
mode:
authorChih-hung Hsieh <chh@google.com>2015-12-01 17:07:48 +0000
committerandroid-build-merger <android-build-merger@google.com>2015-12-01 17:07:48 +0000
commita4acd9d6bc9b3b033d7d274316e75ee067df8d20 (patch)
tree672a185b294789cf991f385c3e395dd63bea9063 /webrtc/common_audio/resampler
parent3681b90ba4fe7a27232dd3e27897d5d7ed9d651c (diff)
parentfe8b4a657979b49e1701bd92f6d5814a99e0b2be (diff)
downloadwebrtc-a4acd9d6bc9b3b033d7d274316e75ee067df8d20.tar.gz
Merge changes I7bbf776e,I1b827825
am: fe8b4a6579 * commit 'fe8b4a657979b49e1701bd92f6d5814a99e0b2be': (7237 commits) WIP: Changes after merge commit 'cb3f9bd' Make the nonlinear beamformer steerable Utilize bitrate above codec max to protect video. Enable VP9 internal resize by default. Filter overlapping RTP header extensions. Make VCMEncodedFrameCallback const. MediaCodecVideoEncoder: Add number of quality resolution downscales to Encoded callback. Remove redudant encoder rate calls. Create isolate files for nonparallel tests. Register header extensions in RtpRtcpObserver to avoid log spam. Make an enum class out of NetEqDecoder, and hide the neteq_decoders_ table ACM: Move NACK functionality inside NetEq Fix chromium-style warnings in webrtc/sound/. Create a 'webrtc_nonparallel_tests' target. Update scalability structure data according to updates in the RTP payload profile. audio_coding: rename interface -> include Rewrote perform_action_on_all_files to be parallell. Update reference indices according to updates in the RTP payload profile. Disable P2PTransport...TestFailoverControlledSide on Memcheck pass clangcl compile options to ignore warnings in gflags.cc ...
Diffstat (limited to 'webrtc/common_audio/resampler')
-rw-r--r--webrtc/common_audio/resampler/Android.mk51
-rw-r--r--webrtc/common_audio/resampler/include/push_resampler.h52
-rw-r--r--webrtc/common_audio/resampler/include/resampler.h95
-rw-r--r--webrtc/common_audio/resampler/push_resampler.cc110
-rw-r--r--webrtc/common_audio/resampler/push_resampler_unittest.cc28
-rw-r--r--webrtc/common_audio/resampler/push_sinc_resampler.cc103
-rw-r--r--webrtc/common_audio/resampler/push_sinc_resampler.h76
-rw-r--r--webrtc/common_audio/resampler/push_sinc_resampler_unittest.cc335
-rw-r--r--webrtc/common_audio/resampler/resampler.cc959
-rw-r--r--webrtc/common_audio/resampler/resampler_unittest.cc139
-rw-r--r--webrtc/common_audio/resampler/sinc_resampler.cc378
-rw-r--r--webrtc/common_audio/resampler/sinc_resampler.h170
-rw-r--r--webrtc/common_audio/resampler/sinc_resampler_neon.cc47
-rw-r--r--webrtc/common_audio/resampler/sinc_resampler_sse.cc59
-rw-r--r--webrtc/common_audio/resampler/sinc_resampler_unittest.cc389
-rw-r--r--webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.cc58
-rw-r--r--webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h55
17 files changed, 3104 insertions, 0 deletions
diff --git a/webrtc/common_audio/resampler/Android.mk b/webrtc/common_audio/resampler/Android.mk
new file mode 100644
index 0000000000..60b55c5d58
--- /dev/null
+++ b/webrtc/common_audio/resampler/Android.mk
@@ -0,0 +1,51 @@
+# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+include $(LOCAL_PATH)/../../../android-webrtc.mk
+
+LOCAL_ARM_MODE := arm
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_MODULE := libwebrtc_resampler
+LOCAL_MODULE_TAGS := optional
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_SRC_FILES := \
+ push_sinc_resampler.cc \
+ resampler.cc \
+ sinc_resampler.cc \
+
+ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH),x86 x86_64))
+LOCAL_SRC_FILES += sinc_resampler_sse.cc
+endif
+
+# Flags passed to both C and C++ files.
+LOCAL_CFLAGS := \
+ $(MY_WEBRTC_COMMON_DEFS)
+
+LOCAL_CFLAGS_arm := $(MY_WEBRTC_COMMON_DEFS_arm)
+LOCAL_CFLAGS_x86 := $(MY_WEBRTC_COMMON_DEFS_x86)
+LOCAL_CFLAGS_mips := $(MY_WEBRTC_COMMON_DEFS_mips)
+LOCAL_CFLAGS_arm64 := $(MY_WEBRTC_COMMON_DEFS_arm64)
+LOCAL_CFLAGS_x86_64 := $(MY_WEBRTC_COMMON_DEFS_x86_64)
+LOCAL_CFLAGS_mips64 := $(MY_WEBRTC_COMMON_DEFS_mips64)
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include \
+ $(LOCAL_PATH)/../../.. \
+ $(LOCAL_PATH)/../signal_processing/include
+
+ifdef WEBRTC_STL
+LOCAL_NDK_STL_VARIANT := $(WEBRTC_STL)
+LOCAL_SDK_VERSION := 14
+LOCAL_MODULE := $(LOCAL_MODULE)_$(WEBRTC_STL)
+endif
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/webrtc/common_audio/resampler/include/push_resampler.h b/webrtc/common_audio/resampler/include/push_resampler.h
new file mode 100644
index 0000000000..b5c0003615
--- /dev/null
+++ b/webrtc/common_audio/resampler/include/push_resampler.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_
+#define WEBRTC_COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_
+
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+class PushSincResampler;
+
+// Wraps PushSincResampler to provide stereo support.
+// TODO(ajm): add support for an arbitrary number of channels.
+template <typename T>
+class PushResampler {
+ public:
+ PushResampler();
+ virtual ~PushResampler();
+
+ // Must be called whenever the parameters change. Free to be called at any
+ // time as it is a no-op if parameters have not changed since the last call.
+ int InitializeIfNeeded(int src_sample_rate_hz, int dst_sample_rate_hz,
+ int num_channels);
+
+ // Returns the total number of samples provided in destination (e.g. 32 kHz,
+ // 2 channel audio gives 640 samples).
+ int Resample(const T* src, size_t src_length, T* dst, size_t dst_capacity);
+
+ private:
+ rtc::scoped_ptr<PushSincResampler> sinc_resampler_;
+ rtc::scoped_ptr<PushSincResampler> sinc_resampler_right_;
+ int src_sample_rate_hz_;
+ int dst_sample_rate_hz_;
+ int num_channels_;
+ rtc::scoped_ptr<T[]> src_left_;
+ rtc::scoped_ptr<T[]> src_right_;
+ rtc::scoped_ptr<T[]> dst_left_;
+ rtc::scoped_ptr<T[]> dst_right_;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_
diff --git a/webrtc/common_audio/resampler/include/resampler.h b/webrtc/common_audio/resampler/include/resampler.h
new file mode 100644
index 0000000000..0d4c1afe4e
--- /dev/null
+++ b/webrtc/common_audio/resampler/include/resampler.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+
+/*
+ * A wrapper for resampling a numerous amount of sampling combinations.
+ */
+
+#ifndef WEBRTC_RESAMPLER_RESAMPLER_H_
+#define WEBRTC_RESAMPLER_RESAMPLER_H_
+
+#include <stddef.h>
+
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+// All methods return 0 on success and -1 on failure.
+class Resampler
+{
+
+public:
+ Resampler();
+ Resampler(int inFreq, int outFreq, int num_channels);
+ ~Resampler();
+
+ // Reset all states
+ int Reset(int inFreq, int outFreq, int num_channels);
+
+ // Reset all states if any parameter has changed
+ int ResetIfNeeded(int inFreq, int outFreq, int num_channels);
+
+ // Resample samplesIn to samplesOut.
+ int Push(const int16_t* samplesIn, size_t lengthIn, int16_t* samplesOut,
+ size_t maxLen, size_t &outLen);
+
+private:
+ enum ResamplerMode
+ {
+ kResamplerMode1To1,
+ kResamplerMode1To2,
+ kResamplerMode1To3,
+ kResamplerMode1To4,
+ kResamplerMode1To6,
+ kResamplerMode1To12,
+ kResamplerMode2To3,
+ kResamplerMode2To11,
+ kResamplerMode4To11,
+ kResamplerMode8To11,
+ kResamplerMode11To16,
+ kResamplerMode11To32,
+ kResamplerMode2To1,
+ kResamplerMode3To1,
+ kResamplerMode4To1,
+ kResamplerMode6To1,
+ kResamplerMode12To1,
+ kResamplerMode3To2,
+ kResamplerMode11To2,
+ kResamplerMode11To4,
+ kResamplerMode11To8
+ };
+
+ // Generic pointers since we don't know what states we'll need
+ void* state1_;
+ void* state2_;
+ void* state3_;
+
+ // Storage if needed
+ int16_t* in_buffer_;
+ int16_t* out_buffer_;
+ size_t in_buffer_size_;
+ size_t out_buffer_size_;
+ size_t in_buffer_size_max_;
+ size_t out_buffer_size_max_;
+
+ int my_in_frequency_khz_;
+ int my_out_frequency_khz_;
+ ResamplerMode my_mode_;
+ int num_channels_;
+
+ // Extra instance for stereo
+ Resampler* slave_left_;
+ Resampler* slave_right_;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_RESAMPLER_RESAMPLER_H_
diff --git a/webrtc/common_audio/resampler/push_resampler.cc b/webrtc/common_audio/resampler/push_resampler.cc
new file mode 100644
index 0000000000..566acdeaa3
--- /dev/null
+++ b/webrtc/common_audio/resampler/push_resampler.cc
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/common_audio/resampler/include/push_resampler.h"
+
+#include <string.h>
+
+#include "webrtc/common_audio/include/audio_util.h"
+#include "webrtc/common_audio/resampler/include/resampler.h"
+#include "webrtc/common_audio/resampler/push_sinc_resampler.h"
+
+namespace webrtc {
+
+template <typename T>
+PushResampler<T>::PushResampler()
+ : src_sample_rate_hz_(0),
+ dst_sample_rate_hz_(0),
+ num_channels_(0) {
+}
+
+template <typename T>
+PushResampler<T>::~PushResampler() {
+}
+
+template <typename T>
+int PushResampler<T>::InitializeIfNeeded(int src_sample_rate_hz,
+ int dst_sample_rate_hz,
+ int num_channels) {
+ if (src_sample_rate_hz == src_sample_rate_hz_ &&
+ dst_sample_rate_hz == dst_sample_rate_hz_ &&
+ num_channels == num_channels_)
+ // No-op if settings haven't changed.
+ return 0;
+
+ if (src_sample_rate_hz <= 0 || dst_sample_rate_hz <= 0 ||
+ num_channels <= 0 || num_channels > 2)
+ return -1;
+
+ src_sample_rate_hz_ = src_sample_rate_hz;
+ dst_sample_rate_hz_ = dst_sample_rate_hz;
+ num_channels_ = num_channels;
+
+ const size_t src_size_10ms_mono =
+ static_cast<size_t>(src_sample_rate_hz / 100);
+ const size_t dst_size_10ms_mono =
+ static_cast<size_t>(dst_sample_rate_hz / 100);
+ sinc_resampler_.reset(new PushSincResampler(src_size_10ms_mono,
+ dst_size_10ms_mono));
+ if (num_channels_ == 2) {
+ src_left_.reset(new T[src_size_10ms_mono]);
+ src_right_.reset(new T[src_size_10ms_mono]);
+ dst_left_.reset(new T[dst_size_10ms_mono]);
+ dst_right_.reset(new T[dst_size_10ms_mono]);
+ sinc_resampler_right_.reset(new PushSincResampler(src_size_10ms_mono,
+ dst_size_10ms_mono));
+ }
+
+ return 0;
+}
+
+template <typename T>
+int PushResampler<T>::Resample(const T* src, size_t src_length, T* dst,
+ size_t dst_capacity) {
+ const size_t src_size_10ms =
+ static_cast<size_t>(src_sample_rate_hz_ * num_channels_ / 100);
+ const size_t dst_size_10ms =
+ static_cast<size_t>(dst_sample_rate_hz_ * num_channels_ / 100);
+ if (src_length != src_size_10ms || dst_capacity < dst_size_10ms)
+ return -1;
+
+ if (src_sample_rate_hz_ == dst_sample_rate_hz_) {
+ // The old resampler provides this memcpy facility in the case of matching
+ // sample rates, so reproduce it here for the sinc resampler.
+ memcpy(dst, src, src_length * sizeof(T));
+ return static_cast<int>(src_length);
+ }
+ if (num_channels_ == 2) {
+ const size_t src_length_mono = src_length / num_channels_;
+ const size_t dst_capacity_mono = dst_capacity / num_channels_;
+ T* deinterleaved[] = {src_left_.get(), src_right_.get()};
+ Deinterleave(src, src_length_mono, num_channels_, deinterleaved);
+
+ size_t dst_length_mono =
+ sinc_resampler_->Resample(src_left_.get(), src_length_mono,
+ dst_left_.get(), dst_capacity_mono);
+ sinc_resampler_right_->Resample(src_right_.get(), src_length_mono,
+ dst_right_.get(), dst_capacity_mono);
+
+ deinterleaved[0] = dst_left_.get();
+ deinterleaved[1] = dst_right_.get();
+ Interleave(deinterleaved, dst_length_mono, num_channels_, dst);
+ return static_cast<int>(dst_length_mono * num_channels_);
+ } else {
+ return static_cast<int>(
+ sinc_resampler_->Resample(src, src_length, dst, dst_capacity));
+ }
+}
+
+// Explictly generate required instantiations.
+template class PushResampler<int16_t>;
+template class PushResampler<float>;
+
+} // namespace webrtc
diff --git a/webrtc/common_audio/resampler/push_resampler_unittest.cc b/webrtc/common_audio/resampler/push_resampler_unittest.cc
new file mode 100644
index 0000000000..4449f4c633
--- /dev/null
+++ b/webrtc/common_audio/resampler/push_resampler_unittest.cc
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/common_audio/resampler/include/push_resampler.h"
+
+// Quality testing of PushResampler is handled through output_mixer_unittest.cc.
+
+namespace webrtc {
+
+TEST(PushResamplerTest, VerifiesInputParameters) {
+ PushResampler<int16_t> resampler;
+ EXPECT_EQ(-1, resampler.InitializeIfNeeded(-1, 16000, 1));
+ EXPECT_EQ(-1, resampler.InitializeIfNeeded(16000, -1, 1));
+ EXPECT_EQ(-1, resampler.InitializeIfNeeded(16000, 16000, 0));
+ EXPECT_EQ(-1, resampler.InitializeIfNeeded(16000, 16000, 3));
+ EXPECT_EQ(0, resampler.InitializeIfNeeded(16000, 16000, 1));
+ EXPECT_EQ(0, resampler.InitializeIfNeeded(16000, 16000, 2));
+}
+
+} // namespace webrtc
diff --git a/webrtc/common_audio/resampler/push_sinc_resampler.cc b/webrtc/common_audio/resampler/push_sinc_resampler.cc
new file mode 100644
index 0000000000..a740423eec
--- /dev/null
+++ b/webrtc/common_audio/resampler/push_sinc_resampler.cc
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/common_audio/resampler/push_sinc_resampler.h"
+
+#include <cstring>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/common_audio/include/audio_util.h"
+
+namespace webrtc {
+
+PushSincResampler::PushSincResampler(size_t source_frames,
+ size_t destination_frames)
+ : resampler_(new SincResampler(source_frames * 1.0 / destination_frames,
+ source_frames,
+ this)),
+ source_ptr_(nullptr),
+ source_ptr_int_(nullptr),
+ destination_frames_(destination_frames),
+ first_pass_(true),
+ source_available_(0) {}
+
+PushSincResampler::~PushSincResampler() {
+}
+
+size_t PushSincResampler::Resample(const int16_t* source,
+ size_t source_length,
+ int16_t* destination,
+ size_t destination_capacity) {
+ if (!float_buffer_.get())
+ float_buffer_.reset(new float[destination_frames_]);
+
+ source_ptr_int_ = source;
+ // Pass nullptr as the float source to have Run() read from the int16 source.
+ Resample(nullptr, source_length, float_buffer_.get(), destination_frames_);
+ FloatS16ToS16(float_buffer_.get(), destination_frames_, destination);
+ source_ptr_int_ = nullptr;
+ return destination_frames_;
+}
+
+size_t PushSincResampler::Resample(const float* source,
+ size_t source_length,
+ float* destination,
+ size_t destination_capacity) {
+ RTC_CHECK_EQ(source_length, resampler_->request_frames());
+ RTC_CHECK_GE(destination_capacity, destination_frames_);
+ // Cache the source pointer. Calling Resample() will immediately trigger
+ // the Run() callback whereupon we provide the cached value.
+ source_ptr_ = source;
+ source_available_ = source_length;
+
+ // On the first pass, we call Resample() twice. During the first call, we
+ // provide dummy input and discard the output. This is done to prime the
+ // SincResampler buffer with the correct delay (half the kernel size), thereby
+ // ensuring that all later Resample() calls will only result in one input
+ // request through Run().
+ //
+ // If this wasn't done, SincResampler would call Run() twice on the first
+ // pass, and we'd have to introduce an entire |source_frames| of delay, rather
+ // than the minimum half kernel.
+ //
+ // It works out that ChunkSize() is exactly the amount of output we need to
+ // request in order to prime the buffer with a single Run() request for
+ // |source_frames|.
+ if (first_pass_)
+ resampler_->Resample(resampler_->ChunkSize(), destination);
+
+ resampler_->Resample(destination_frames_, destination);
+ source_ptr_ = nullptr;
+ return destination_frames_;
+}
+
+void PushSincResampler::Run(size_t frames, float* destination) {
+ // Ensure we are only asked for the available samples. This would fail if
+ // Run() was triggered more than once per Resample() call.
+ RTC_CHECK_EQ(source_available_, frames);
+
+ if (first_pass_) {
+ // Provide dummy input on the first pass, the output of which will be
+ // discarded, as described in Resample().
+ std::memset(destination, 0, frames * sizeof(*destination));
+ first_pass_ = false;
+ return;
+ }
+
+ if (source_ptr_) {
+ std::memcpy(destination, source_ptr_, frames * sizeof(*destination));
+ } else {
+ for (size_t i = 0; i < frames; ++i)
+ destination[i] = static_cast<float>(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
new file mode 100644
index 0000000000..cefc62aa2a
--- /dev/null
+++ b/webrtc/common_audio/resampler/push_sinc_resampler.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_
+#define WEBRTC_COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/common_audio/resampler/sinc_resampler.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+// A thin wrapper over SincResampler to provide a push-based interface as
+// required by WebRTC. SincResampler uses a pull-based interface, and will
+// use SincResamplerCallback::Run() to request data upon a call to Resample().
+// These Run() calls will happen on the same thread Resample() is called on.
+class PushSincResampler : public SincResamplerCallback {
+ public:
+ // Provide the size of the source and destination blocks in samples. These
+ // must correspond to the same time duration (typically 10 ms) as the sample
+ // ratio is inferred from them.
+ PushSincResampler(size_t source_frames, size_t destination_frames);
+ ~PushSincResampler() override;
+
+ // Perform the resampling. |source_frames| must always equal the
+ // |source_frames| provided at construction. |destination_capacity| must be
+ // at least as large as |destination_frames|. Returns the number of samples
+ // provided in destination (for convenience, since this will always be equal
+ // to |destination_frames|).
+ size_t Resample(const int16_t* source, size_t source_frames,
+ int16_t* destination, size_t destination_capacity);
+ size_t Resample(const float* source,
+ size_t source_frames,
+ float* destination,
+ size_t destination_capacity);
+
+ // Delay due to the filter kernel. Essentially, the time after which an input
+ // sample will appear in the resampled output.
+ static float AlgorithmicDelaySeconds(int source_rate_hz) {
+ return 1.f / source_rate_hz * SincResampler::kKernelSize / 2;
+ }
+
+ protected:
+ // Implements SincResamplerCallback.
+ void Run(size_t frames, float* destination) override;
+
+ private:
+ friend class PushSincResamplerTest;
+ SincResampler* get_resampler_for_testing() { return resampler_.get(); }
+
+ rtc::scoped_ptr<SincResampler> resampler_;
+ rtc::scoped_ptr<float[]> float_buffer_;
+ const float* source_ptr_;
+ const int16_t* source_ptr_int_;
+ const size_t destination_frames_;
+
+ // True on the first call to Resample(), to prime the SincResampler buffer.
+ bool first_pass_;
+
+ // Used to assert we are only requested for as much data as is available.
+ size_t source_available_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(PushSincResampler);
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_
diff --git a/webrtc/common_audio/resampler/push_sinc_resampler_unittest.cc b/webrtc/common_audio/resampler/push_sinc_resampler_unittest.cc
new file mode 100644
index 0000000000..17e3dba1e2
--- /dev/null
+++ b/webrtc/common_audio/resampler/push_sinc_resampler_unittest.cc
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <cmath>
+#include <cstring>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/base/scoped_ptr.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/include/tick_util.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+namespace {
+
+// Almost all conversions have an RMS error of around -14 dbFS.
+const double kResamplingRMSError = -14.42;
+
+// Used to convert errors to dbFS.
+template <typename T>
+T DBFS(T x) {
+ return 20 * std::log10(x);
+}
+
+} // namespace
+
+class PushSincResamplerTest : public ::testing::TestWithParam<
+ ::testing::tuple<int, int, double, double>> {
+ public:
+ PushSincResamplerTest()
+ : input_rate_(::testing::get<0>(GetParam())),
+ output_rate_(::testing::get<1>(GetParam())),
+ rms_error_(::testing::get<2>(GetParam())),
+ low_freq_error_(::testing::get<3>(GetParam())) {
+ }
+
+ ~PushSincResamplerTest() override {}
+
+ protected:
+ void ResampleBenchmarkTest(bool int_format);
+ void ResampleTest(bool int_format);
+
+ int input_rate_;
+ int output_rate_;
+ double rms_error_;
+ double low_freq_error_;
+};
+
+class ZeroSource : public SincResamplerCallback {
+ public:
+ void Run(size_t frames, float* destination) {
+ std::memset(destination, 0, sizeof(float) * frames);
+ }
+};
+
+void PushSincResamplerTest::ResampleBenchmarkTest(bool int_format) {
+ const size_t input_samples = static_cast<size_t>(input_rate_ / 100);
+ const size_t output_samples = static_cast<size_t>(output_rate_ / 100);
+ const int kResampleIterations = 500000;
+
+ // Source for data to be resampled.
+ ZeroSource resampler_source;
+
+ rtc::scoped_ptr<float[]> resampled_destination(new float[output_samples]);
+ rtc::scoped_ptr<float[]> source(new float[input_samples]);
+ rtc::scoped_ptr<int16_t[]> source_int(new int16_t[input_samples]);
+ rtc::scoped_ptr<int16_t[]> destination_int(new int16_t[output_samples]);
+
+ resampler_source.Run(input_samples, source.get());
+ for (size_t i = 0; i < input_samples; ++i) {
+ source_int[i] = static_cast<int16_t>(floor(32767 * source[i] + 0.5));
+ }
+
+ printf("Benchmarking %d iterations of %d Hz -> %d Hz:\n",
+ kResampleIterations, input_rate_, output_rate_);
+ const double io_ratio = input_rate_ / static_cast<double>(output_rate_);
+ SincResampler sinc_resampler(io_ratio, SincResampler::kDefaultRequestSize,
+ &resampler_source);
+ TickTime start = TickTime::Now();
+ for (int i = 0; i < kResampleIterations; ++i) {
+ sinc_resampler.Resample(output_samples, resampled_destination.get());
+ }
+ double total_time_sinc_us = (TickTime::Now() - start).Microseconds();
+ printf("SincResampler took %.2f us per frame.\n",
+ total_time_sinc_us / kResampleIterations);
+
+ PushSincResampler resampler(input_samples, output_samples);
+ start = TickTime::Now();
+ 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 "
+ "on SincResampler.\n\n", total_time_us / kResampleIterations,
+ (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.
+void PushSincResamplerTest::ResampleTest(bool int_format) {
+ // Make comparisons using one second of data.
+ static const double kTestDurationSecs = 1;
+ // 10 ms blocks.
+ const size_t kNumBlocks = static_cast<size_t>(kTestDurationSecs * 100);
+ const size_t input_block_size = static_cast<size_t>(input_rate_ / 100);
+ const size_t output_block_size = static_cast<size_t>(output_rate_ / 100);
+ const size_t input_samples =
+ static_cast<size_t>(kTestDurationSecs * input_rate_);
+ const size_t output_samples =
+ static_cast<size_t>(kTestDurationSecs * output_rate_);
+
+ // Nyquist frequency for the input sampling rate.
+ const double input_nyquist_freq = 0.5 * input_rate_;
+
+ // Source for data to be resampled.
+ SinusoidalLinearChirpSource resampler_source(
+ input_rate_, input_samples, input_nyquist_freq, 0);
+
+ PushSincResampler resampler(input_block_size, output_block_size);
+
+ // 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.
+ rtc::scoped_ptr<float[]> resampled_destination(new float[output_samples]);
+ rtc::scoped_ptr<float[]> pure_destination(new float[output_samples]);
+ rtc::scoped_ptr<float[]> source(new float[input_samples]);
+ rtc::scoped_ptr<int16_t[]> source_int(new int16_t[input_block_size]);
+ rtc::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
+ // becomes explicit and is managed by zero-stuffing in PushSincResampler. We
+ // deal with it in the test by delaying the "pure" source to match. It must be
+ // checked before the first call to Resample(), because ChunkSize() will
+ // change afterwards.
+ const size_t output_delay_samples = output_block_size -
+ resampler.get_resampler_for_testing()->ChunkSize();
+
+ // Generate resampled signal.
+ // 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());
+ if (int_format) {
+ for (size_t i = 0; i < kNumBlocks; ++i) {
+ FloatToS16(&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));
+ S16ToFloat(destination_int.get(), output_block_size,
+ &resampled_destination[i * output_block_size]);
+ }
+ } else {
+ for (size_t 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));
+ }
+ }
+
+ // Generate pure signal.
+ SinusoidalLinearChirpSource pure_source(
+ output_rate_, output_samples, input_nyquist_freq, output_delay_samples);
+ pure_source.Run(output_samples, pure_destination.get());
+
+ // Range of the Nyquist frequency (0.5 * min(input rate, output_rate)) which
+ // we refer to as low and high.
+ static const double kLowFrequencyNyquistRange = 0.7;
+ static const double kHighFrequencyNyquistRange = 0.9;
+
+ // Calculate Root-Mean-Square-Error and maximum error for the resampling.
+ double sum_of_squares = 0;
+ double low_freq_max_error = 0;
+ double high_freq_max_error = 0;
+ int minimum_rate = std::min(input_rate_, output_rate_);
+ double low_frequency_range = kLowFrequencyNyquistRange * 0.5 * minimum_rate;
+ double high_frequency_range = kHighFrequencyNyquistRange * 0.5 * minimum_rate;
+
+ for (size_t i = 0; i < output_samples; ++i) {
+ double error = fabs(resampled_destination[i] - pure_destination[i]);
+
+ if (pure_source.Frequency(i) < low_frequency_range) {
+ if (error > low_freq_max_error)
+ low_freq_max_error = error;
+ } else if (pure_source.Frequency(i) < high_frequency_range) {
+ if (error > high_freq_max_error)
+ high_freq_max_error = error;
+ }
+ // TODO(dalecurtis): Sanity check frequencies > kHighFrequencyNyquistRange.
+
+ sum_of_squares += error * error;
+ }
+
+ double rms_error = sqrt(sum_of_squares / output_samples);
+
+ rms_error = DBFS(rms_error);
+ // In order to keep the thresholds in this test identical to SincResamplerTest
+ // we must account for the quantization error introduced by truncating from
+ // float to int. This happens twice (once at input and once at output) and we
+ // allow for the maximum possible error (1 / 32767) for each step.
+ //
+ // The quantization error is insignificant in the RMS calculation so does not
+ // need to be accounted for there.
+ low_freq_max_error = DBFS(low_freq_max_error - 2.0 / 32767);
+ high_freq_max_error = DBFS(high_freq_max_error - 2.0 / 32767);
+
+ EXPECT_LE(rms_error, rms_error_);
+ EXPECT_LE(low_freq_max_error, low_freq_error_);
+
+ // All conversions currently have a high frequency error around -6 dbFS.
+ static const double kHighFrequencyMaxError = -6.02;
+ EXPECT_LE(high_freq_max_error, kHighFrequencyMaxError);
+}
+
+TEST_P(PushSincResamplerTest, ResampleInt) { ResampleTest(true); }
+
+TEST_P(PushSincResamplerTest, ResampleFloat) { ResampleTest(false); }
+
+// 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(
+ // First run through the rates tested in SincResamplerTest. The
+ // thresholds are identical.
+ //
+ // We don't test rates which fail to provide an integer number of
+ // samples in a 10 ms block (22050 and 11025 Hz). WebRTC doesn't support
+ // these rates in any case (for the same reason).
+
+ // To 44.1kHz
+ ::testing::make_tuple(8000, 44100, kResamplingRMSError, -62.73),
+ ::testing::make_tuple(16000, 44100, kResamplingRMSError, -62.54),
+ ::testing::make_tuple(32000, 44100, kResamplingRMSError, -63.32),
+ ::testing::make_tuple(44100, 44100, kResamplingRMSError, -73.53),
+ ::testing::make_tuple(48000, 44100, -15.01, -64.04),
+ ::testing::make_tuple(96000, 44100, -18.49, -25.51),
+ ::testing::make_tuple(192000, 44100, -20.50, -13.31),
+
+ // To 48kHz
+ ::testing::make_tuple(8000, 48000, kResamplingRMSError, -63.43),
+ ::testing::make_tuple(16000, 48000, kResamplingRMSError, -63.96),
+ ::testing::make_tuple(32000, 48000, kResamplingRMSError, -64.04),
+ ::testing::make_tuple(44100, 48000, kResamplingRMSError, -62.63),
+ ::testing::make_tuple(48000, 48000, kResamplingRMSError, -73.52),
+ ::testing::make_tuple(96000, 48000, -18.40, -28.44),
+ ::testing::make_tuple(192000, 48000, -20.43, -14.11),
+
+ // To 96kHz
+ ::testing::make_tuple(8000, 96000, kResamplingRMSError, -63.19),
+ ::testing::make_tuple(16000, 96000, kResamplingRMSError, -63.39),
+ ::testing::make_tuple(32000, 96000, kResamplingRMSError, -63.95),
+ ::testing::make_tuple(44100, 96000, kResamplingRMSError, -62.63),
+ ::testing::make_tuple(48000, 96000, kResamplingRMSError, -73.52),
+ ::testing::make_tuple(96000, 96000, kResamplingRMSError, -73.52),
+ ::testing::make_tuple(192000, 96000, kResamplingRMSError, -28.41),
+
+ // To 192kHz
+ ::testing::make_tuple(8000, 192000, kResamplingRMSError, -63.10),
+ ::testing::make_tuple(16000, 192000, kResamplingRMSError, -63.14),
+ ::testing::make_tuple(32000, 192000, kResamplingRMSError, -63.38),
+ ::testing::make_tuple(44100, 192000, kResamplingRMSError, -62.63),
+ ::testing::make_tuple(48000, 192000, kResamplingRMSError, -73.44),
+ ::testing::make_tuple(96000, 192000, kResamplingRMSError, -73.52),
+ ::testing::make_tuple(192000, 192000, kResamplingRMSError, -73.52),
+
+ // Next run through some additional cases interesting for WebRTC.
+ // We skip some extreme downsampled cases (192 -> {8, 16}, 96 -> 8)
+ // because they violate |kHighFrequencyMaxError|, which is not
+ // unexpected. It's very unlikely that we'll see these conversions in
+ // practice anyway.
+
+ // To 8 kHz
+ ::testing::make_tuple(8000, 8000, kResamplingRMSError, -75.50),
+ ::testing::make_tuple(16000, 8000, -18.56, -28.79),
+ ::testing::make_tuple(32000, 8000, -20.36, -14.13),
+ ::testing::make_tuple(44100, 8000, -21.00, -11.39),
+ ::testing::make_tuple(48000, 8000, -20.96, -11.04),
+
+ // To 16 kHz
+ ::testing::make_tuple(8000, 16000, kResamplingRMSError, -70.30),
+ ::testing::make_tuple(16000, 16000, kResamplingRMSError, -75.51),
+ ::testing::make_tuple(32000, 16000, -18.48, -28.59),
+ ::testing::make_tuple(44100, 16000, -19.30, -19.67),
+ ::testing::make_tuple(48000, 16000, -19.81, -18.11),
+ ::testing::make_tuple(96000, 16000, -20.95, -10.96),
+
+ // To 32 kHz
+ ::testing::make_tuple(8000, 32000, kResamplingRMSError, -70.30),
+ ::testing::make_tuple(16000, 32000, kResamplingRMSError, -75.51),
+ ::testing::make_tuple(32000, 32000, kResamplingRMSError, -75.51),
+ ::testing::make_tuple(44100, 32000, -16.44, -51.10),
+ ::testing::make_tuple(48000, 32000, -16.90, -44.03),
+ ::testing::make_tuple(96000, 32000, -19.61, -18.04),
+ ::testing::make_tuple(192000, 32000, -21.02, -10.94)));
+
+} // namespace webrtc
diff --git a/webrtc/common_audio/resampler/resampler.cc b/webrtc/common_audio/resampler/resampler.cc
new file mode 100644
index 0000000000..c9e7a1fb96
--- /dev/null
+++ b/webrtc/common_audio/resampler/resampler.cc
@@ -0,0 +1,959 @@
+/*
+ * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+
+/*
+ * A wrapper for resampling a numerous amount of sampling combinations.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "webrtc/common_audio/resampler/include/resampler.h"
+#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"
+
+namespace webrtc {
+
+Resampler::Resampler()
+ : state1_(nullptr),
+ state2_(nullptr),
+ state3_(nullptr),
+ in_buffer_(nullptr),
+ out_buffer_(nullptr),
+ in_buffer_size_(0),
+ out_buffer_size_(0),
+ in_buffer_size_max_(0),
+ out_buffer_size_max_(0),
+ my_in_frequency_khz_(0),
+ my_out_frequency_khz_(0),
+ my_mode_(kResamplerMode1To1),
+ num_channels_(0),
+ slave_left_(nullptr),
+ slave_right_(nullptr) {
+}
+
+Resampler::Resampler(int inFreq, int outFreq, int num_channels)
+ : Resampler() {
+ Reset(inFreq, outFreq, num_channels);
+}
+
+Resampler::~Resampler()
+{
+ if (state1_)
+ {
+ free(state1_);
+ }
+ if (state2_)
+ {
+ free(state2_);
+ }
+ if (state3_)
+ {
+ free(state3_);
+ }
+ if (in_buffer_)
+ {
+ free(in_buffer_);
+ }
+ if (out_buffer_)
+ {
+ free(out_buffer_);
+ }
+ if (slave_left_)
+ {
+ delete slave_left_;
+ }
+ if (slave_right_)
+ {
+ delete slave_right_;
+ }
+}
+
+int Resampler::ResetIfNeeded(int inFreq, int outFreq, int num_channels)
+{
+ int tmpInFreq_kHz = inFreq / 1000;
+ int tmpOutFreq_kHz = outFreq / 1000;
+
+ if ((tmpInFreq_kHz != my_in_frequency_khz_) || (tmpOutFreq_kHz != my_out_frequency_khz_)
+ || (num_channels != num_channels_))
+ {
+ return Reset(inFreq, outFreq, num_channels);
+ } else
+ {
+ return 0;
+ }
+}
+
+int Resampler::Reset(int inFreq, int outFreq, int num_channels)
+{
+ if (num_channels != 1 && num_channels != 2) {
+ return -1;
+ }
+ num_channels_ = num_channels;
+
+ if (state1_)
+ {
+ free(state1_);
+ state1_ = NULL;
+ }
+ if (state2_)
+ {
+ free(state2_);
+ state2_ = NULL;
+ }
+ if (state3_)
+ {
+ free(state3_);
+ state3_ = NULL;
+ }
+ if (in_buffer_)
+ {
+ free(in_buffer_);
+ in_buffer_ = NULL;
+ }
+ if (out_buffer_)
+ {
+ free(out_buffer_);
+ out_buffer_ = NULL;
+ }
+ if (slave_left_)
+ {
+ delete slave_left_;
+ slave_left_ = NULL;
+ }
+ if (slave_right_)
+ {
+ delete slave_right_;
+ slave_right_ = NULL;
+ }
+
+ in_buffer_size_ = 0;
+ out_buffer_size_ = 0;
+ in_buffer_size_max_ = 0;
+ out_buffer_size_max_ = 0;
+
+ // Start with a math exercise, Euclid's algorithm to find the gcd:
+ int a = inFreq;
+ int b = outFreq;
+ int c = a % b;
+ while (c != 0)
+ {
+ a = b;
+ b = c;
+ c = a % b;
+ }
+ // b is now the gcd;
+
+ // We need to track what domain we're in.
+ my_in_frequency_khz_ = inFreq / 1000;
+ my_out_frequency_khz_ = outFreq / 1000;
+
+ // Scale with GCD
+ inFreq = inFreq / b;
+ outFreq = outFreq / b;
+
+ if (num_channels_ == 2)
+ {
+ // Create two mono resamplers.
+ slave_left_ = new Resampler(inFreq, outFreq, 1);
+ slave_right_ = new Resampler(inFreq, outFreq, 1);
+ }
+
+ if (inFreq == outFreq)
+ {
+ my_mode_ = kResamplerMode1To1;
+ } else if (inFreq == 1)
+ {
+ switch (outFreq)
+ {
+ case 2:
+ my_mode_ = kResamplerMode1To2;
+ break;
+ case 3:
+ my_mode_ = kResamplerMode1To3;
+ break;
+ case 4:
+ my_mode_ = kResamplerMode1To4;
+ break;
+ case 6:
+ my_mode_ = kResamplerMode1To6;
+ break;
+ case 12:
+ my_mode_ = kResamplerMode1To12;
+ break;
+ default:
+ return -1;
+ }
+ } else if (outFreq == 1)
+ {
+ switch (inFreq)
+ {
+ case 2:
+ my_mode_ = kResamplerMode2To1;
+ break;
+ case 3:
+ my_mode_ = kResamplerMode3To1;
+ break;
+ case 4:
+ my_mode_ = kResamplerMode4To1;
+ break;
+ case 6:
+ my_mode_ = kResamplerMode6To1;
+ break;
+ case 12:
+ my_mode_ = kResamplerMode12To1;
+ break;
+ default:
+ return -1;
+ }
+ } else if ((inFreq == 2) && (outFreq == 3))
+ {
+ my_mode_ = kResamplerMode2To3;
+ } else if ((inFreq == 2) && (outFreq == 11))
+ {
+ my_mode_ = kResamplerMode2To11;
+ } else if ((inFreq == 4) && (outFreq == 11))
+ {
+ my_mode_ = kResamplerMode4To11;
+ } else if ((inFreq == 8) && (outFreq == 11))
+ {
+ my_mode_ = kResamplerMode8To11;
+ } else if ((inFreq == 3) && (outFreq == 2))
+ {
+ my_mode_ = kResamplerMode3To2;
+ } else if ((inFreq == 11) && (outFreq == 2))
+ {
+ my_mode_ = kResamplerMode11To2;
+ } else if ((inFreq == 11) && (outFreq == 4))
+ {
+ my_mode_ = kResamplerMode11To4;
+ } else if ((inFreq == 11) && (outFreq == 16))
+ {
+ my_mode_ = kResamplerMode11To16;
+ } else if ((inFreq == 11) && (outFreq == 32))
+ {
+ my_mode_ = kResamplerMode11To32;
+ } else if ((inFreq == 11) && (outFreq == 8))
+ {
+ my_mode_ = kResamplerMode11To8;
+ } else
+ {
+ return -1;
+ }
+
+ // Now create the states we need
+ switch (my_mode_)
+ {
+ case kResamplerMode1To1:
+ // No state needed;
+ break;
+ case kResamplerMode1To2:
+ state1_ = malloc(8 * sizeof(int32_t));
+ memset(state1_, 0, 8 * sizeof(int32_t));
+ break;
+ case kResamplerMode1To3:
+ state1_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz));
+ WebRtcSpl_ResetResample16khzTo48khz((WebRtcSpl_State16khzTo48khz *)state1_);
+ break;
+ case kResamplerMode1To4:
+ // 1:2
+ state1_ = malloc(8 * sizeof(int32_t));
+ memset(state1_, 0, 8 * sizeof(int32_t));
+ // 2:4
+ state2_ = malloc(8 * sizeof(int32_t));
+ memset(state2_, 0, 8 * sizeof(int32_t));
+ break;
+ case kResamplerMode1To6:
+ // 1:2
+ state1_ = malloc(8 * sizeof(int32_t));
+ memset(state1_, 0, 8 * sizeof(int32_t));
+ // 2:6
+ state2_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz));
+ WebRtcSpl_ResetResample16khzTo48khz((WebRtcSpl_State16khzTo48khz *)state2_);
+ break;
+ case kResamplerMode1To12:
+ // 1:2
+ state1_ = malloc(8 * sizeof(int32_t));
+ memset(state1_, 0, 8 * sizeof(int32_t));
+ // 2:4
+ state2_ = malloc(8 * sizeof(int32_t));
+ memset(state2_, 0, 8 * sizeof(int32_t));
+ // 4:12
+ state3_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz));
+ WebRtcSpl_ResetResample16khzTo48khz(
+ (WebRtcSpl_State16khzTo48khz*) state3_);
+ break;
+ case kResamplerMode2To3:
+ // 2:6
+ state1_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz));
+ WebRtcSpl_ResetResample16khzTo48khz((WebRtcSpl_State16khzTo48khz *)state1_);
+ // 6:3
+ state2_ = malloc(8 * sizeof(int32_t));
+ memset(state2_, 0, 8 * sizeof(int32_t));
+ break;
+ case kResamplerMode2To11:
+ state1_ = malloc(8 * sizeof(int32_t));
+ memset(state1_, 0, 8 * sizeof(int32_t));
+
+ state2_ = malloc(sizeof(WebRtcSpl_State8khzTo22khz));
+ WebRtcSpl_ResetResample8khzTo22khz((WebRtcSpl_State8khzTo22khz *)state2_);
+ break;
+ case kResamplerMode4To11:
+ state1_ = malloc(sizeof(WebRtcSpl_State8khzTo22khz));
+ WebRtcSpl_ResetResample8khzTo22khz((WebRtcSpl_State8khzTo22khz *)state1_);
+ break;
+ case kResamplerMode8To11:
+ state1_ = malloc(sizeof(WebRtcSpl_State16khzTo22khz));
+ WebRtcSpl_ResetResample16khzTo22khz((WebRtcSpl_State16khzTo22khz *)state1_);
+ break;
+ case kResamplerMode11To16:
+ state1_ = malloc(8 * sizeof(int32_t));
+ memset(state1_, 0, 8 * sizeof(int32_t));
+
+ state2_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz));
+ WebRtcSpl_ResetResample22khzTo16khz((WebRtcSpl_State22khzTo16khz *)state2_);
+ break;
+ case kResamplerMode11To32:
+ // 11 -> 22
+ state1_ = malloc(8 * sizeof(int32_t));
+ memset(state1_, 0, 8 * sizeof(int32_t));
+
+ // 22 -> 16
+ state2_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz));
+ WebRtcSpl_ResetResample22khzTo16khz((WebRtcSpl_State22khzTo16khz *)state2_);
+
+ // 16 -> 32
+ state3_ = malloc(8 * sizeof(int32_t));
+ memset(state3_, 0, 8 * sizeof(int32_t));
+
+ break;
+ case kResamplerMode2To1:
+ state1_ = malloc(8 * sizeof(int32_t));
+ memset(state1_, 0, 8 * sizeof(int32_t));
+ break;
+ case kResamplerMode3To1:
+ state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz));
+ WebRtcSpl_ResetResample48khzTo16khz((WebRtcSpl_State48khzTo16khz *)state1_);
+ break;
+ case kResamplerMode4To1:
+ // 4:2
+ state1_ = malloc(8 * sizeof(int32_t));
+ memset(state1_, 0, 8 * sizeof(int32_t));
+ // 2:1
+ state2_ = malloc(8 * sizeof(int32_t));
+ memset(state2_, 0, 8 * sizeof(int32_t));
+ break;
+ case kResamplerMode6To1:
+ // 6:2
+ state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz));
+ WebRtcSpl_ResetResample48khzTo16khz((WebRtcSpl_State48khzTo16khz *)state1_);
+ // 2:1
+ state2_ = malloc(8 * sizeof(int32_t));
+ memset(state2_, 0, 8 * sizeof(int32_t));
+ break;
+ case kResamplerMode12To1:
+ // 12:4
+ state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz));
+ WebRtcSpl_ResetResample48khzTo16khz(
+ (WebRtcSpl_State48khzTo16khz*) state1_);
+ // 4:2
+ state2_ = malloc(8 * sizeof(int32_t));
+ memset(state2_, 0, 8 * sizeof(int32_t));
+ // 2:1
+ state3_ = malloc(8 * sizeof(int32_t));
+ memset(state3_, 0, 8 * sizeof(int32_t));
+ break;
+ case kResamplerMode3To2:
+ // 3:6
+ state1_ = malloc(8 * sizeof(int32_t));
+ memset(state1_, 0, 8 * sizeof(int32_t));
+ // 6:2
+ state2_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz));
+ WebRtcSpl_ResetResample48khzTo16khz((WebRtcSpl_State48khzTo16khz *)state2_);
+ break;
+ case kResamplerMode11To2:
+ state1_ = malloc(sizeof(WebRtcSpl_State22khzTo8khz));
+ WebRtcSpl_ResetResample22khzTo8khz((WebRtcSpl_State22khzTo8khz *)state1_);
+
+ state2_ = malloc(8 * sizeof(int32_t));
+ memset(state2_, 0, 8 * sizeof(int32_t));
+
+ break;
+ case kResamplerMode11To4:
+ state1_ = malloc(sizeof(WebRtcSpl_State22khzTo8khz));
+ WebRtcSpl_ResetResample22khzTo8khz((WebRtcSpl_State22khzTo8khz *)state1_);
+ break;
+ case kResamplerMode11To8:
+ state1_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz));
+ WebRtcSpl_ResetResample22khzTo16khz((WebRtcSpl_State22khzTo16khz *)state1_);
+ break;
+
+ }
+
+ return 0;
+}
+
+// Synchronous resampling, all output samples are written to samplesOut
+int Resampler::Push(const int16_t * samplesIn, size_t lengthIn,
+ int16_t* samplesOut, size_t maxLen, size_t &outLen)
+{
+ if (num_channels_ == 2)
+ {
+ // Split up the signal and call the slave object for each channel
+ int16_t* left = (int16_t*)malloc(lengthIn * sizeof(int16_t) / 2);
+ int16_t* right = (int16_t*)malloc(lengthIn * sizeof(int16_t) / 2);
+ int16_t* out_left = (int16_t*)malloc(maxLen / 2 * sizeof(int16_t));
+ int16_t* out_right =
+ (int16_t*)malloc(maxLen / 2 * sizeof(int16_t));
+ int res = 0;
+ for (size_t i = 0; i < lengthIn; i += 2)
+ {
+ left[i >> 1] = samplesIn[i];
+ right[i >> 1] = samplesIn[i + 1];
+ }
+
+ // It's OK to overwrite the local parameter, since it's just a copy
+ lengthIn = lengthIn / 2;
+
+ size_t actualOutLen_left = 0;
+ size_t actualOutLen_right = 0;
+ // Do resampling for right channel
+ res |= slave_left_->Push(left, lengthIn, out_left, maxLen / 2, actualOutLen_left);
+ res |= slave_right_->Push(right, lengthIn, out_right, maxLen / 2, actualOutLen_right);
+ if (res || (actualOutLen_left != actualOutLen_right))
+ {
+ free(left);
+ free(right);
+ free(out_left);
+ free(out_right);
+ return -1;
+ }
+
+ // Reassemble the signal
+ for (size_t i = 0; i < actualOutLen_left; i++)
+ {
+ samplesOut[i * 2] = out_left[i];
+ samplesOut[i * 2 + 1] = out_right[i];
+ }
+ outLen = 2 * actualOutLen_left;
+
+ free(left);
+ free(right);
+ free(out_left);
+ free(out_right);
+
+ return 0;
+ }
+
+ // Containers for temp samples
+ int16_t* tmp;
+ int16_t* tmp_2;
+ // tmp data for resampling routines
+ int32_t* tmp_mem;
+
+ switch (my_mode_)
+ {
+ case kResamplerMode1To1:
+ memcpy(samplesOut, samplesIn, lengthIn * sizeof(int16_t));
+ outLen = lengthIn;
+ break;
+ case kResamplerMode1To2:
+ if (maxLen < (lengthIn * 2))
+ {
+ return -1;
+ }
+ WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut, (int32_t*)state1_);
+ outLen = lengthIn * 2;
+ return 0;
+ case kResamplerMode1To3:
+
+ // We can only handle blocks of 160 samples
+ // Can be fixed, but I don't think it's needed
+ if ((lengthIn % 160) != 0)
+ {
+ return -1;
+ }
+ if (maxLen < (lengthIn * 3))
+ {
+ return -1;
+ }
+ tmp_mem = (int32_t*)malloc(336 * sizeof(int32_t));
+
+ for (size_t i = 0; i < lengthIn; i += 160)
+ {
+ WebRtcSpl_Resample16khzTo48khz(samplesIn + i, samplesOut + i * 3,
+ (WebRtcSpl_State16khzTo48khz *)state1_,
+ tmp_mem);
+ }
+ outLen = lengthIn * 3;
+ free(tmp_mem);
+ return 0;
+ case kResamplerMode1To4:
+ if (maxLen < (lengthIn * 4))
+ {
+ return -1;
+ }
+
+ tmp = (int16_t*)malloc(sizeof(int16_t) * 2 * lengthIn);
+ // 1:2
+ WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_);
+ // 2:4
+ WebRtcSpl_UpsampleBy2(tmp, lengthIn * 2, samplesOut, (int32_t*)state2_);
+ outLen = lengthIn * 4;
+ free(tmp);
+ return 0;
+ case kResamplerMode1To6:
+ // We can only handle blocks of 80 samples
+ // Can be fixed, but I don't think it's needed
+ if ((lengthIn % 80) != 0)
+ {
+ return -1;
+ }
+ if (maxLen < (lengthIn * 6))
+ {
+ return -1;
+ }
+
+ //1:2
+
+ tmp_mem = (int32_t*)malloc(336 * sizeof(int32_t));
+ tmp = (int16_t*)malloc(sizeof(int16_t) * 2 * lengthIn);
+
+ WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_);
+ outLen = lengthIn * 2;
+
+ for (size_t i = 0; i < outLen; i += 160)
+ {
+ WebRtcSpl_Resample16khzTo48khz(tmp + i, samplesOut + i * 3,
+ (WebRtcSpl_State16khzTo48khz *)state2_,
+ tmp_mem);
+ }
+ outLen = outLen * 3;
+ free(tmp_mem);
+ free(tmp);
+
+ return 0;
+ case kResamplerMode1To12:
+ // We can only handle blocks of 40 samples
+ // Can be fixed, but I don't think it's needed
+ if ((lengthIn % 40) != 0) {
+ return -1;
+ }
+ if (maxLen < (lengthIn * 12)) {
+ return -1;
+ }
+
+ tmp_mem = (int32_t*) malloc(336 * sizeof(int32_t));
+ tmp = (int16_t*) malloc(sizeof(int16_t) * 4 * lengthIn);
+ //1:2
+ WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut,
+ (int32_t*) state1_);
+ outLen = lengthIn * 2;
+ //2:4
+ WebRtcSpl_UpsampleBy2(samplesOut, outLen, tmp, (int32_t*) state2_);
+ outLen = outLen * 2;
+ // 4:12
+ for (size_t i = 0; i < outLen; i += 160) {
+ // WebRtcSpl_Resample16khzTo48khz() takes a block of 160 samples
+ // as input and outputs a resampled block of 480 samples. The
+ // data is now actually in 32 kHz sampling rate, despite the
+ // function name, and with a resampling factor of three becomes
+ // 96 kHz.
+ WebRtcSpl_Resample16khzTo48khz(tmp + i, samplesOut + i * 3,
+ (WebRtcSpl_State16khzTo48khz*) state3_,
+ tmp_mem);
+ }
+ outLen = outLen * 3;
+ free(tmp_mem);
+ free(tmp);
+
+ return 0;
+ case kResamplerMode2To3:
+ if (maxLen < (lengthIn * 3 / 2))
+ {
+ return -1;
+ }
+ // 2:6
+ // We can only handle blocks of 160 samples
+ // Can be fixed, but I don't think it's needed
+ if ((lengthIn % 160) != 0)
+ {
+ return -1;
+ }
+ tmp = static_cast<int16_t*> (malloc(sizeof(int16_t) * lengthIn * 3));
+ tmp_mem = (int32_t*)malloc(336 * sizeof(int32_t));
+ for (size_t i = 0; i < lengthIn; i += 160)
+ {
+ WebRtcSpl_Resample16khzTo48khz(samplesIn + i, tmp + i * 3,
+ (WebRtcSpl_State16khzTo48khz *)state1_,
+ tmp_mem);
+ }
+ lengthIn = lengthIn * 3;
+ // 6:3
+ WebRtcSpl_DownsampleBy2(tmp, lengthIn, samplesOut, (int32_t*)state2_);
+ outLen = lengthIn / 2;
+ free(tmp);
+ free(tmp_mem);
+ return 0;
+ case kResamplerMode2To11:
+
+ // We can only handle blocks of 80 samples
+ // Can be fixed, but I don't think it's needed
+ if ((lengthIn % 80) != 0)
+ {
+ return -1;
+ }
+ if (maxLen < ((lengthIn * 11) / 2))
+ {
+ return -1;
+ }
+ tmp = (int16_t*)malloc(sizeof(int16_t) * 2 * lengthIn);
+ // 1:2
+ WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_);
+ lengthIn *= 2;
+
+ tmp_mem = (int32_t*)malloc(98 * sizeof(int32_t));
+
+ for (size_t i = 0; i < lengthIn; i += 80)
+ {
+ WebRtcSpl_Resample8khzTo22khz(tmp + i, samplesOut + (i * 11) / 4,
+ (WebRtcSpl_State8khzTo22khz *)state2_,
+ tmp_mem);
+ }
+ outLen = (lengthIn * 11) / 4;
+ free(tmp_mem);
+ free(tmp);
+ return 0;
+ case kResamplerMode4To11:
+
+ // We can only handle blocks of 80 samples
+ // Can be fixed, but I don't think it's needed
+ if ((lengthIn % 80) != 0)
+ {
+ return -1;
+ }
+ if (maxLen < ((lengthIn * 11) / 4))
+ {
+ return -1;
+ }
+ tmp_mem = (int32_t*)malloc(98 * sizeof(int32_t));
+
+ for (size_t i = 0; i < lengthIn; i += 80)
+ {
+ WebRtcSpl_Resample8khzTo22khz(samplesIn + i, samplesOut + (i * 11) / 4,
+ (WebRtcSpl_State8khzTo22khz *)state1_,
+ tmp_mem);
+ }
+ outLen = (lengthIn * 11) / 4;
+ free(tmp_mem);
+ return 0;
+ case kResamplerMode8To11:
+ // We can only handle blocks of 160 samples
+ // Can be fixed, but I don't think it's needed
+ if ((lengthIn % 160) != 0)
+ {
+ return -1;
+ }
+ if (maxLen < ((lengthIn * 11) / 8))
+ {
+ return -1;
+ }
+ tmp_mem = (int32_t*)malloc(88 * sizeof(int32_t));
+
+ for (size_t i = 0; i < lengthIn; i += 160)
+ {
+ WebRtcSpl_Resample16khzTo22khz(samplesIn + i, samplesOut + (i * 11) / 8,
+ (WebRtcSpl_State16khzTo22khz *)state1_,
+ tmp_mem);
+ }
+ outLen = (lengthIn * 11) / 8;
+ free(tmp_mem);
+ return 0;
+
+ case kResamplerMode11To16:
+ // We can only handle blocks of 110 samples
+ if ((lengthIn % 110) != 0)
+ {
+ return -1;
+ }
+ if (maxLen < ((lengthIn * 16) / 11))
+ {
+ return -1;
+ }
+
+ tmp_mem = (int32_t*)malloc(104 * sizeof(int32_t));
+ tmp = (int16_t*)malloc((sizeof(int16_t) * lengthIn * 2));
+
+ WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_);
+
+ for (size_t i = 0; i < (lengthIn * 2); i += 220)
+ {
+ WebRtcSpl_Resample22khzTo16khz(tmp + i, samplesOut + (i / 220) * 160,
+ (WebRtcSpl_State22khzTo16khz *)state2_,
+ tmp_mem);
+ }
+
+ outLen = (lengthIn * 16) / 11;
+
+ free(tmp_mem);
+ free(tmp);
+ return 0;
+
+ case kResamplerMode11To32:
+
+ // We can only handle blocks of 110 samples
+ if ((lengthIn % 110) != 0)
+ {
+ return -1;
+ }
+ if (maxLen < ((lengthIn * 32) / 11))
+ {
+ return -1;
+ }
+
+ tmp_mem = (int32_t*)malloc(104 * sizeof(int32_t));
+ tmp = (int16_t*)malloc((sizeof(int16_t) * lengthIn * 2));
+
+ // 11 -> 22 kHz in samplesOut
+ WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut, (int32_t*)state1_);
+
+ // 22 -> 16 in tmp
+ for (size_t i = 0; i < (lengthIn * 2); i += 220)
+ {
+ WebRtcSpl_Resample22khzTo16khz(samplesOut + i, tmp + (i / 220) * 160,
+ (WebRtcSpl_State22khzTo16khz *)state2_,
+ tmp_mem);
+ }
+
+ // 16 -> 32 in samplesOut
+ WebRtcSpl_UpsampleBy2(tmp, (lengthIn * 16) / 11, samplesOut,
+ (int32_t*)state3_);
+
+ outLen = (lengthIn * 32) / 11;
+
+ free(tmp_mem);
+ free(tmp);
+ return 0;
+
+ case kResamplerMode2To1:
+ if (maxLen < (lengthIn / 2))
+ {
+ return -1;
+ }
+ WebRtcSpl_DownsampleBy2(samplesIn, lengthIn, samplesOut, (int32_t*)state1_);
+ outLen = lengthIn / 2;
+ return 0;
+ case kResamplerMode3To1:
+ // We can only handle blocks of 480 samples
+ // Can be fixed, but I don't think it's needed
+ if ((lengthIn % 480) != 0)
+ {
+ return -1;
+ }
+ if (maxLen < (lengthIn / 3))
+ {
+ return -1;
+ }
+ tmp_mem = (int32_t*)malloc(496 * sizeof(int32_t));
+
+ for (size_t i = 0; i < lengthIn; i += 480)
+ {
+ WebRtcSpl_Resample48khzTo16khz(samplesIn + i, samplesOut + i / 3,
+ (WebRtcSpl_State48khzTo16khz *)state1_,
+ tmp_mem);
+ }
+ outLen = lengthIn / 3;
+ free(tmp_mem);
+ return 0;
+ case kResamplerMode4To1:
+ if (maxLen < (lengthIn / 4))
+ {
+ return -1;
+ }
+ tmp = (int16_t*)malloc(sizeof(int16_t) * lengthIn / 2);
+ // 4:2
+ WebRtcSpl_DownsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_);
+ // 2:1
+ WebRtcSpl_DownsampleBy2(tmp, lengthIn / 2, samplesOut, (int32_t*)state2_);
+ outLen = lengthIn / 4;
+ free(tmp);
+ return 0;
+
+ case kResamplerMode6To1:
+ // We can only handle blocks of 480 samples
+ // Can be fixed, but I don't think it's needed
+ if ((lengthIn % 480) != 0)
+ {
+ return -1;
+ }
+ if (maxLen < (lengthIn / 6))
+ {
+ return -1;
+ }
+
+ tmp_mem = (int32_t*)malloc(496 * sizeof(int32_t));
+ tmp = (int16_t*)malloc((sizeof(int16_t) * lengthIn) / 3);
+
+ for (size_t i = 0; i < lengthIn; i += 480)
+ {
+ WebRtcSpl_Resample48khzTo16khz(samplesIn + i, tmp + i / 3,
+ (WebRtcSpl_State48khzTo16khz *)state1_,
+ tmp_mem);
+ }
+ outLen = lengthIn / 3;
+ free(tmp_mem);
+ WebRtcSpl_DownsampleBy2(tmp, outLen, samplesOut, (int32_t*)state2_);
+ free(tmp);
+ outLen = outLen / 2;
+ return 0;
+ case kResamplerMode12To1:
+ // We can only handle blocks of 480 samples
+ // Can be fixed, but I don't think it's needed
+ if ((lengthIn % 480) != 0) {
+ return -1;
+ }
+ if (maxLen < (lengthIn / 12)) {
+ return -1;
+ }
+
+ tmp_mem = (int32_t*) malloc(496 * sizeof(int32_t));
+ tmp = (int16_t*) malloc((sizeof(int16_t) * lengthIn) / 3);
+ tmp_2 = (int16_t*) malloc((sizeof(int16_t) * lengthIn) / 6);
+ // 12:4
+ for (size_t i = 0; i < lengthIn; i += 480) {
+ // WebRtcSpl_Resample48khzTo16khz() takes a block of 480 samples
+ // as input and outputs a resampled block of 160 samples. The
+ // data is now actually in 96 kHz sampling rate, despite the
+ // function name, and with a resampling factor of 1/3 becomes
+ // 32 kHz.
+ WebRtcSpl_Resample48khzTo16khz(samplesIn + i, tmp + i / 3,
+ (WebRtcSpl_State48khzTo16khz*) state1_,
+ tmp_mem);
+ }
+ outLen = lengthIn / 3;
+ free(tmp_mem);
+ // 4:2
+ WebRtcSpl_DownsampleBy2(tmp, outLen, tmp_2, (int32_t*) state2_);
+ outLen = outLen / 2;
+ free(tmp);
+ // 2:1
+ WebRtcSpl_DownsampleBy2(tmp_2, outLen, samplesOut,
+ (int32_t*) state3_);
+ free(tmp_2);
+ outLen = outLen / 2;
+ return 0;
+ case kResamplerMode3To2:
+ if (maxLen < (lengthIn * 2 / 3))
+ {
+ return -1;
+ }
+ // 3:6
+ tmp = static_cast<int16_t*> (malloc(sizeof(int16_t) * lengthIn * 2));
+ WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_);
+ lengthIn *= 2;
+ // 6:2
+ // We can only handle blocks of 480 samples
+ // Can be fixed, but I don't think it's needed
+ if ((lengthIn % 480) != 0)
+ {
+ free(tmp);
+ return -1;
+ }
+ tmp_mem = (int32_t*)malloc(496 * sizeof(int32_t));
+ for (size_t i = 0; i < lengthIn; i += 480)
+ {
+ WebRtcSpl_Resample48khzTo16khz(tmp + i, samplesOut + i / 3,
+ (WebRtcSpl_State48khzTo16khz *)state2_,
+ tmp_mem);
+ }
+ outLen = lengthIn / 3;
+ free(tmp);
+ free(tmp_mem);
+ return 0;
+ case kResamplerMode11To2:
+ // We can only handle blocks of 220 samples
+ // Can be fixed, but I don't think it's needed
+ if ((lengthIn % 220) != 0)
+ {
+ return -1;
+ }
+ if (maxLen < ((lengthIn * 2) / 11))
+ {
+ return -1;
+ }
+ tmp_mem = (int32_t*)malloc(126 * sizeof(int32_t));
+ tmp = (int16_t*)malloc((lengthIn * 4) / 11 * sizeof(int16_t));
+
+ for (size_t i = 0; i < lengthIn; i += 220)
+ {
+ WebRtcSpl_Resample22khzTo8khz(samplesIn + i, tmp + (i * 4) / 11,
+ (WebRtcSpl_State22khzTo8khz *)state1_,
+ tmp_mem);
+ }
+ lengthIn = (lengthIn * 4) / 11;
+
+ WebRtcSpl_DownsampleBy2(tmp, lengthIn, samplesOut,
+ (int32_t*)state2_);
+ outLen = lengthIn / 2;
+
+ free(tmp_mem);
+ free(tmp);
+ return 0;
+ case kResamplerMode11To4:
+ // We can only handle blocks of 220 samples
+ // Can be fixed, but I don't think it's needed
+ if ((lengthIn % 220) != 0)
+ {
+ return -1;
+ }
+ if (maxLen < ((lengthIn * 4) / 11))
+ {
+ return -1;
+ }
+ tmp_mem = (int32_t*)malloc(126 * sizeof(int32_t));
+
+ for (size_t i = 0; i < lengthIn; i += 220)
+ {
+ WebRtcSpl_Resample22khzTo8khz(samplesIn + i, samplesOut + (i * 4) / 11,
+ (WebRtcSpl_State22khzTo8khz *)state1_,
+ tmp_mem);
+ }
+ outLen = (lengthIn * 4) / 11;
+ free(tmp_mem);
+ return 0;
+ case kResamplerMode11To8:
+ // We can only handle blocks of 160 samples
+ // Can be fixed, but I don't think it's needed
+ if ((lengthIn % 220) != 0)
+ {
+ return -1;
+ }
+ if (maxLen < ((lengthIn * 8) / 11))
+ {
+ return -1;
+ }
+ tmp_mem = (int32_t*)malloc(104 * sizeof(int32_t));
+
+ for (size_t i = 0; i < lengthIn; i += 220)
+ {
+ WebRtcSpl_Resample22khzTo16khz(samplesIn + i, samplesOut + (i * 8) / 11,
+ (WebRtcSpl_State22khzTo16khz *)state1_,
+ tmp_mem);
+ }
+ outLen = (lengthIn * 8) / 11;
+ free(tmp_mem);
+ return 0;
+ break;
+
+ }
+ return 0;
+}
+
+} // namespace webrtc
diff --git a/webrtc/common_audio/resampler/resampler_unittest.cc b/webrtc/common_audio/resampler/resampler_unittest.cc
new file mode 100644
index 0000000000..c5953d030b
--- /dev/null
+++ b/webrtc/common_audio/resampler/resampler_unittest.cc
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "webrtc/common_audio/resampler/include/resampler.h"
+
+// TODO(andrew): this is a work-in-progress. Many more tests are needed.
+
+namespace webrtc {
+namespace {
+
+const int kNumChannels[] = {1, 2};
+const size_t kNumChannelsSize = sizeof(kNumChannels) / sizeof(*kNumChannels);
+
+// Rates we must support.
+const int kMaxRate = 96000;
+const int kRates[] = {
+ 8000,
+ 16000,
+ 32000,
+ 44000,
+ 48000,
+ kMaxRate
+};
+const size_t kRatesSize = sizeof(kRates) / sizeof(*kRates);
+const int kMaxChannels = 2;
+const size_t kDataSize = static_cast<size_t> (kMaxChannels * kMaxRate / 100);
+
+// TODO(andrew): should we be supporting these combinations?
+bool ValidRates(int in_rate, int out_rate) {
+ // Not the most compact notation, for clarity.
+ if ((in_rate == 44000 && (out_rate == 48000 || out_rate == 96000)) ||
+ (out_rate == 44000 && (in_rate == 48000 || in_rate == 96000))) {
+ return false;
+ }
+
+ return true;
+}
+
+class ResamplerTest : public testing::Test {
+ protected:
+ ResamplerTest();
+ virtual void SetUp();
+ virtual void TearDown();
+
+ Resampler rs_;
+ int16_t data_in_[kDataSize];
+ int16_t data_out_[kDataSize];
+};
+
+ResamplerTest::ResamplerTest() {}
+
+void ResamplerTest::SetUp() {
+ // Initialize input data with anything. The tests are content independent.
+ memset(data_in_, 1, sizeof(data_in_));
+}
+
+void ResamplerTest::TearDown() {}
+
+TEST_F(ResamplerTest, Reset) {
+ // The only failure mode for the constructor is if Reset() fails. For the
+ // time being then (until an Init function is added), we rely on Reset()
+ // to test the constructor.
+
+ // Check that all required combinations are supported.
+ for (size_t i = 0; i < kRatesSize; ++i) {
+ for (size_t j = 0; j < kRatesSize; ++j) {
+ for (size_t k = 0; k < kNumChannelsSize; ++k) {
+ std::ostringstream ss;
+ ss << "Input rate: " << kRates[i] << ", output rate: " << kRates[j]
+ << ", channels: " << kNumChannels[k];
+ SCOPED_TRACE(ss.str());
+ if (ValidRates(kRates[i], kRates[j]))
+ EXPECT_EQ(0, rs_.Reset(kRates[i], kRates[j], kNumChannels[k]));
+ else
+ EXPECT_EQ(-1, rs_.Reset(kRates[i], kRates[j], kNumChannels[k]));
+ }
+ }
+ }
+}
+
+// TODO(tlegrand): Replace code inside the two tests below with a function
+// with number of channels and ResamplerType as input.
+TEST_F(ResamplerTest, Mono) {
+ const int kChannels = 1;
+ for (size_t i = 0; i < kRatesSize; ++i) {
+ for (size_t j = 0; j < kRatesSize; ++j) {
+ std::ostringstream ss;
+ ss << "Input rate: " << kRates[i] << ", output rate: " << kRates[j];
+ SCOPED_TRACE(ss.str());
+
+ if (ValidRates(kRates[i], kRates[j])) {
+ size_t in_length = static_cast<size_t>(kRates[i] / 100);
+ size_t out_length = 0;
+ EXPECT_EQ(0, rs_.Reset(kRates[i], kRates[j], kChannels));
+ EXPECT_EQ(0, rs_.Push(data_in_, in_length, data_out_, kDataSize,
+ out_length));
+ EXPECT_EQ(static_cast<size_t>(kRates[j] / 100), out_length);
+ } else {
+ EXPECT_EQ(-1, rs_.Reset(kRates[i], kRates[j], kChannels));
+ }
+ }
+ }
+}
+
+TEST_F(ResamplerTest, Stereo) {
+ const int kChannels = 2;
+ for (size_t i = 0; i < kRatesSize; ++i) {
+ for (size_t j = 0; j < kRatesSize; ++j) {
+ std::ostringstream ss;
+ ss << "Input rate: " << kRates[i] << ", output rate: " << kRates[j];
+ SCOPED_TRACE(ss.str());
+
+ if (ValidRates(kRates[i], kRates[j])) {
+ size_t in_length = static_cast<size_t>(kChannels * kRates[i] / 100);
+ size_t out_length = 0;
+ EXPECT_EQ(0, rs_.Reset(kRates[i], kRates[j],
+ kChannels));
+ EXPECT_EQ(0, rs_.Push(data_in_, in_length, data_out_, kDataSize,
+ out_length));
+ EXPECT_EQ(static_cast<size_t>(kChannels * kRates[j] / 100), out_length);
+ } else {
+ EXPECT_EQ(-1, rs_.Reset(kRates[i], kRates[j],
+ kChannels));
+ }
+ }
+ }
+}
+
+} // namespace
+} // namespace webrtc
diff --git a/webrtc/common_audio/resampler/sinc_resampler.cc b/webrtc/common_audio/resampler/sinc_resampler.cc
new file mode 100644
index 0000000000..69ac2208cf
--- /dev/null
+++ b/webrtc/common_audio/resampler/sinc_resampler.cc
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// Modified from the Chromium original:
+// src/media/base/sinc_resampler.cc
+
+// Initial input buffer layout, dividing into regions r0_ to r4_ (note: r0_, r3_
+// and r4_ will move after the first load):
+//
+// |----------------|-----------------------------------------|----------------|
+//
+// request_frames_
+// <--------------------------------------------------------->
+// r0_ (during first load)
+//
+// kKernelSize / 2 kKernelSize / 2 kKernelSize / 2 kKernelSize / 2
+// <---------------> <---------------> <---------------> <--------------->
+// r1_ r2_ r3_ r4_
+//
+// block_size_ == r4_ - r2_
+// <--------------------------------------->
+//
+// request_frames_
+// <------------------ ... ----------------->
+// r0_ (during second load)
+//
+// On the second request r0_ slides to the right by kKernelSize / 2 and r3_, r4_
+// and block_size_ are reinitialized via step (3) in the algorithm below.
+//
+// These new regions remain constant until a Flush() occurs. While complicated,
+// this allows us to reduce jitter by always requesting the same amount from the
+// provided callback.
+//
+// The algorithm:
+//
+// 1) Allocate input_buffer of size: request_frames_ + kKernelSize; this ensures
+// there's enough room to read request_frames_ from the callback into region
+// r0_ (which will move between the first and subsequent passes).
+//
+// 2) Let r1_, r2_ each represent half the kernel centered around r0_:
+//
+// r0_ = input_buffer_ + kKernelSize / 2
+// r1_ = input_buffer_
+// r2_ = r0_
+//
+// r0_ is always request_frames_ in size. r1_, r2_ are kKernelSize / 2 in
+// size. r1_ must be zero initialized to avoid convolution with garbage (see
+// step (5) for why).
+//
+// 3) Let r3_, r4_ each represent half the kernel right aligned with the end of
+// r0_ and choose block_size_ as the distance in frames between r4_ and r2_:
+//
+// r3_ = r0_ + request_frames_ - kKernelSize
+// r4_ = r0_ + request_frames_ - kKernelSize / 2
+// block_size_ = r4_ - r2_ = request_frames_ - kKernelSize / 2
+//
+// 4) Consume request_frames_ frames into r0_.
+//
+// 5) Position kernel centered at start of r2_ and generate output frames until
+// the kernel is centered at the start of r4_ or we've finished generating
+// all the output frames.
+//
+// 6) Wrap left over data from the r3_ to r1_ and r4_ to r2_.
+//
+// 7) If we're on the second load, in order to avoid overwriting the frames we
+// just wrapped from r4_ we need to slide r0_ to the right by the size of
+// r4_, which is kKernelSize / 2:
+//
+// r0_ = r0_ + kKernelSize / 2 = input_buffer_ + kKernelSize
+//
+// r3_, r4_, and block_size_ then need to be reinitialized, so goto (3).
+//
+// 8) Else, if we're not on the second load, goto (4).
+//
+// Note: we're glossing over how the sub-sample handling works with
+// |virtual_source_idx_|, etc.
+
+// MSVC++ requires this to be set before any other includes to get M_PI.
+#define _USE_MATH_DEFINES
+
+#include "webrtc/common_audio/resampler/sinc_resampler.h"
+
+#include <assert.h>
+#include <math.h>
+#include <string.h>
+
+#include <limits>
+
+#include "webrtc/system_wrappers/include/cpu_features_wrapper.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+namespace {
+
+double SincScaleFactor(double io_ratio) {
+ // |sinc_scale_factor| is basically the normalized cutoff frequency of the
+ // low-pass filter.
+ double sinc_scale_factor = io_ratio > 1.0 ? 1.0 / io_ratio : 1.0;
+
+ // The sinc function is an idealized brick-wall filter, but since we're
+ // windowing it the transition from pass to stop does not happen right away.
+ // So we should adjust the low pass filter cutoff slightly downward to avoid
+ // some aliasing at the very high-end.
+ // TODO(crogers): this value is empirical and to be more exact should vary
+ // depending on kKernelSize.
+ sinc_scale_factor *= 0.9;
+
+ return sinc_scale_factor;
+}
+
+} // namespace
+
+// If we know the minimum architecture at compile time, avoid CPU detection.
+#if defined(WEBRTC_ARCH_X86_FAMILY)
+#if defined(__SSE2__)
+#define CONVOLVE_FUNC Convolve_SSE
+void SincResampler::InitializeCPUSpecificFeatures() {}
+#else
+// x86 CPU detection required. Function will be set by
+// InitializeCPUSpecificFeatures().
+// TODO(dalecurtis): Once Chrome moves to an SSE baseline this can be removed.
+#define CONVOLVE_FUNC convolve_proc_
+
+void SincResampler::InitializeCPUSpecificFeatures() {
+ convolve_proc_ = WebRtc_GetCPUInfo(kSSE2) ? Convolve_SSE : Convolve_C;
+}
+#endif
+#elif defined(WEBRTC_HAS_NEON)
+#define CONVOLVE_FUNC Convolve_NEON
+void SincResampler::InitializeCPUSpecificFeatures() {}
+#elif defined(WEBRTC_DETECT_NEON)
+#define CONVOLVE_FUNC convolve_proc_
+void SincResampler::InitializeCPUSpecificFeatures() {
+ convolve_proc_ = WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON ?
+ Convolve_NEON : Convolve_C;
+}
+#else
+// Unknown architecture.
+#define CONVOLVE_FUNC Convolve_C
+void SincResampler::InitializeCPUSpecificFeatures() {}
+#endif
+
+SincResampler::SincResampler(double io_sample_rate_ratio,
+ size_t request_frames,
+ SincResamplerCallback* read_cb)
+ : io_sample_rate_ratio_(io_sample_rate_ratio),
+ read_cb_(read_cb),
+ request_frames_(request_frames),
+ input_buffer_size_(request_frames_ + kKernelSize),
+ // Create input buffers with a 16-byte alignment for SSE optimizations.
+ kernel_storage_(static_cast<float*>(
+ AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))),
+ kernel_pre_sinc_storage_(static_cast<float*>(
+ AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))),
+ kernel_window_storage_(static_cast<float*>(
+ AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))),
+ input_buffer_(static_cast<float*>(
+ AlignedMalloc(sizeof(float) * input_buffer_size_, 16))),
+#if defined(WEBRTC_CPU_DETECTION)
+ convolve_proc_(NULL),
+#endif
+ r1_(input_buffer_.get()),
+ r2_(input_buffer_.get() + kKernelSize / 2) {
+#if defined(WEBRTC_CPU_DETECTION)
+ InitializeCPUSpecificFeatures();
+ assert(convolve_proc_);
+#endif
+ assert(request_frames_ > 0);
+ Flush();
+ assert(block_size_ > kKernelSize);
+
+ memset(kernel_storage_.get(), 0,
+ sizeof(*kernel_storage_.get()) * kKernelStorageSize);
+ memset(kernel_pre_sinc_storage_.get(), 0,
+ sizeof(*kernel_pre_sinc_storage_.get()) * kKernelStorageSize);
+ memset(kernel_window_storage_.get(), 0,
+ sizeof(*kernel_window_storage_.get()) * kKernelStorageSize);
+
+ InitializeKernel();
+}
+
+SincResampler::~SincResampler() {}
+
+void SincResampler::UpdateRegions(bool second_load) {
+ // Setup various region pointers in the buffer (see diagram above). If we're
+ // on the second load we need to slide r0_ to the right by kKernelSize / 2.
+ r0_ = input_buffer_.get() + (second_load ? kKernelSize : kKernelSize / 2);
+ r3_ = r0_ + request_frames_ - kKernelSize;
+ r4_ = r0_ + request_frames_ - kKernelSize / 2;
+ block_size_ = r4_ - r2_;
+
+ // r1_ at the beginning of the buffer.
+ assert(r1_ == input_buffer_.get());
+ // r1_ left of r2_, r4_ left of r3_ and size correct.
+ assert(r2_ - r1_ == r4_ - r3_);
+ // r2_ left of r3.
+ assert(r2_ < r3_);
+}
+
+void SincResampler::InitializeKernel() {
+ // Blackman window parameters.
+ static const double kAlpha = 0.16;
+ static const double kA0 = 0.5 * (1.0 - kAlpha);
+ static const double kA1 = 0.5;
+ static const double kA2 = 0.5 * kAlpha;
+
+ // Generates a set of windowed sinc() kernels.
+ // We generate a range of sub-sample offsets from 0.0 to 1.0.
+ const double sinc_scale_factor = SincScaleFactor(io_sample_rate_ratio_);
+ for (size_t offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) {
+ const float subsample_offset =
+ static_cast<float>(offset_idx) / kKernelOffsetCount;
+
+ for (size_t i = 0; i < kKernelSize; ++i) {
+ const size_t idx = i + offset_idx * kKernelSize;
+ const float pre_sinc = static_cast<float>(M_PI *
+ (static_cast<int>(i) - static_cast<int>(kKernelSize / 2) -
+ subsample_offset));
+ kernel_pre_sinc_storage_[idx] = pre_sinc;
+
+ // Compute Blackman window, matching the offset of the sinc().
+ const float x = (i - subsample_offset) / kKernelSize;
+ const float window = static_cast<float>(kA0 - kA1 * cos(2.0 * M_PI * x) +
+ kA2 * cos(4.0 * M_PI * x));
+ kernel_window_storage_[idx] = window;
+
+ // Compute the sinc with offset, then window the sinc() function and store
+ // at the correct offset.
+ kernel_storage_[idx] = static_cast<float>(window *
+ ((pre_sinc == 0) ?
+ sinc_scale_factor :
+ (sin(sinc_scale_factor * pre_sinc) / pre_sinc)));
+ }
+ }
+}
+
+void SincResampler::SetRatio(double io_sample_rate_ratio) {
+ if (fabs(io_sample_rate_ratio_ - io_sample_rate_ratio) <
+ std::numeric_limits<double>::epsilon()) {
+ return;
+ }
+
+ io_sample_rate_ratio_ = io_sample_rate_ratio;
+
+ // Optimize reinitialization by reusing values which are independent of
+ // |sinc_scale_factor|. Provides a 3x speedup.
+ const double sinc_scale_factor = SincScaleFactor(io_sample_rate_ratio_);
+ for (size_t offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) {
+ for (size_t i = 0; i < kKernelSize; ++i) {
+ const size_t idx = i + offset_idx * kKernelSize;
+ const float window = kernel_window_storage_[idx];
+ const float pre_sinc = kernel_pre_sinc_storage_[idx];
+
+ kernel_storage_[idx] = static_cast<float>(window *
+ ((pre_sinc == 0) ?
+ sinc_scale_factor :
+ (sin(sinc_scale_factor * pre_sinc) / pre_sinc)));
+ }
+ }
+}
+
+void SincResampler::Resample(size_t frames, float* destination) {
+ size_t remaining_frames = frames;
+
+ // Step (1) -- Prime the input buffer at the start of the input stream.
+ if (!buffer_primed_ && remaining_frames) {
+ read_cb_->Run(request_frames_, r0_);
+ buffer_primed_ = true;
+ }
+
+ // Step (2) -- Resample! const what we can outside of the loop for speed. It
+ // actually has an impact on ARM performance. See inner loop comment below.
+ const double current_io_ratio = io_sample_rate_ratio_;
+ const float* const kernel_ptr = kernel_storage_.get();
+ while (remaining_frames) {
+ // |i| may be negative if the last Resample() call ended on an iteration
+ // that put |virtual_source_idx_| over the limit.
+ //
+ // Note: The loop construct here can severely impact performance on ARM
+ // or when built with clang. See https://codereview.chromium.org/18566009/
+ for (int i = static_cast<int>(
+ ceil((block_size_ - virtual_source_idx_) / current_io_ratio));
+ i > 0; --i) {
+ assert(virtual_source_idx_ < block_size_);
+
+ // |virtual_source_idx_| lies in between two kernel offsets so figure out
+ // what they are.
+ const int source_idx = static_cast<int>(virtual_source_idx_);
+ const double subsample_remainder = virtual_source_idx_ - source_idx;
+
+ const double virtual_offset_idx =
+ subsample_remainder * kKernelOffsetCount;
+ const int offset_idx = static_cast<int>(virtual_offset_idx);
+
+ // We'll compute "convolutions" for the two kernels which straddle
+ // |virtual_source_idx_|.
+ const float* const k1 = kernel_ptr + offset_idx * kKernelSize;
+ const float* const k2 = k1 + kKernelSize;
+
+ // Ensure |k1|, |k2| are 16-byte aligned for SIMD usage. Should always be
+ // true so long as kKernelSize is a multiple of 16.
+ assert(0u == (reinterpret_cast<uintptr_t>(k1) & 0x0F));
+ assert(0u == (reinterpret_cast<uintptr_t>(k2) & 0x0F));
+
+ // Initialize input pointer based on quantized |virtual_source_idx_|.
+ const float* const input_ptr = r1_ + source_idx;
+
+ // Figure out how much to weight each kernel's "convolution".
+ const double kernel_interpolation_factor =
+ virtual_offset_idx - offset_idx;
+ *destination++ = CONVOLVE_FUNC(
+ input_ptr, k1, k2, kernel_interpolation_factor);
+
+ // Advance the virtual index.
+ virtual_source_idx_ += current_io_ratio;
+
+ if (!--remaining_frames)
+ return;
+ }
+
+ // Wrap back around to the start.
+ virtual_source_idx_ -= block_size_;
+
+ // Step (3) -- Copy r3_, r4_ to r1_, r2_.
+ // This wraps the last input frames back to the start of the buffer.
+ memcpy(r1_, r3_, sizeof(*input_buffer_.get()) * kKernelSize);
+
+ // Step (4) -- Reinitialize regions if necessary.
+ if (r0_ == r2_)
+ UpdateRegions(true);
+
+ // Step (5) -- Refresh the buffer with more input.
+ read_cb_->Run(request_frames_, r0_);
+ }
+}
+
+#undef CONVOLVE_FUNC
+
+size_t SincResampler::ChunkSize() const {
+ return static_cast<size_t>(block_size_ / io_sample_rate_ratio_);
+}
+
+void SincResampler::Flush() {
+ virtual_source_idx_ = 0;
+ buffer_primed_ = false;
+ memset(input_buffer_.get(), 0,
+ sizeof(*input_buffer_.get()) * input_buffer_size_);
+ UpdateRegions(false);
+}
+
+float SincResampler::Convolve_C(const float* input_ptr, const float* k1,
+ const float* k2,
+ double kernel_interpolation_factor) {
+ float sum1 = 0;
+ float sum2 = 0;
+
+ // Generate a single output sample. Unrolling this loop hurt performance in
+ // local testing.
+ size_t n = kKernelSize;
+ while (n--) {
+ sum1 += *input_ptr * *k1++;
+ sum2 += *input_ptr++ * *k2++;
+ }
+
+ // Linearly interpolate the two "convolutions".
+ return static_cast<float>((1.0 - kernel_interpolation_factor) * sum1 +
+ kernel_interpolation_factor * sum2);
+}
+
+} // namespace webrtc
diff --git a/webrtc/common_audio/resampler/sinc_resampler.h b/webrtc/common_audio/resampler/sinc_resampler.h
new file mode 100644
index 0000000000..45ade0cc69
--- /dev/null
+++ b/webrtc/common_audio/resampler/sinc_resampler.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// Modified from the Chromium original here:
+// src/media/base/sinc_resampler.h
+
+#ifndef WEBRTC_COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_
+#define WEBRTC_COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/system_wrappers/include/aligned_malloc.h"
+#include "webrtc/test/testsupport/gtest_prod_util.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+// Callback class for providing more data into the resampler. Expects |frames|
+// of data to be rendered into |destination|; zero padded if not enough frames
+// are available to satisfy the request.
+class SincResamplerCallback {
+ public:
+ virtual ~SincResamplerCallback() {}
+ virtual void Run(size_t frames, float* destination) = 0;
+};
+
+// SincResampler is a high-quality single-channel sample-rate converter.
+class SincResampler {
+ public:
+ // The kernel size can be adjusted for quality (higher is better) at the
+ // expense of performance. Must be a multiple of 32.
+ // TODO(dalecurtis): Test performance to see if we can jack this up to 64+.
+ static const size_t kKernelSize = 32;
+
+ // Default request size. Affects how often and for how much SincResampler
+ // calls back for input. Must be greater than kKernelSize.
+ static const size_t kDefaultRequestSize = 512;
+
+ // The kernel offset count is used for interpolation and is the number of
+ // sub-sample kernel shifts. Can be adjusted for quality (higher is better)
+ // at the expense of allocating more memory.
+ static const size_t kKernelOffsetCount = 32;
+ static const size_t kKernelStorageSize =
+ kKernelSize * (kKernelOffsetCount + 1);
+
+ // Constructs a SincResampler with the specified |read_cb|, which is used to
+ // acquire audio data for resampling. |io_sample_rate_ratio| is the ratio
+ // of input / output sample rates. |request_frames| controls the size in
+ // frames of the buffer requested by each |read_cb| call. The value must be
+ // greater than kKernelSize. Specify kDefaultRequestSize if there are no
+ // request size constraints.
+ SincResampler(double io_sample_rate_ratio,
+ size_t request_frames,
+ SincResamplerCallback* read_cb);
+ virtual ~SincResampler();
+
+ // Resample |frames| of data from |read_cb_| into |destination|.
+ void Resample(size_t frames, float* destination);
+
+ // The maximum size in frames that guarantees Resample() will only make a
+ // single call to |read_cb_| for more data.
+ size_t ChunkSize() const;
+
+ size_t request_frames() const { return request_frames_; }
+
+ // Flush all buffered data and reset internal indices. Not thread safe, do
+ // not call while Resample() is in progress.
+ void Flush();
+
+ // Update |io_sample_rate_ratio_|. SetRatio() will cause a reconstruction of
+ // the kernels used for resampling. Not thread safe, do not call while
+ // Resample() is in progress.
+ //
+ // TODO(ajm): Use this in PushSincResampler rather than reconstructing
+ // SincResampler. We would also need a way to update |request_frames_|.
+ void SetRatio(double io_sample_rate_ratio);
+
+ float* get_kernel_for_testing() { return kernel_storage_.get(); }
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(SincResamplerTest, Convolve);
+ FRIEND_TEST_ALL_PREFIXES(SincResamplerTest, ConvolveBenchmark);
+
+ void InitializeKernel();
+ void UpdateRegions(bool second_load);
+
+ // Selects runtime specific CPU features like SSE. Must be called before
+ // using SincResampler.
+ // TODO(ajm): Currently managed by the class internally. See the note with
+ // |convolve_proc_| below.
+ void InitializeCPUSpecificFeatures();
+
+ // Compute convolution of |k1| and |k2| over |input_ptr|, resultant sums are
+ // linearly interpolated using |kernel_interpolation_factor|. On x86 and ARM
+ // the underlying implementation is chosen at run time.
+ static float Convolve_C(const float* input_ptr, const float* k1,
+ const float* k2, double kernel_interpolation_factor);
+#if defined(WEBRTC_ARCH_X86_FAMILY)
+ static float Convolve_SSE(const float* input_ptr, const float* k1,
+ const float* k2,
+ double kernel_interpolation_factor);
+#elif defined(WEBRTC_DETECT_NEON) || defined(WEBRTC_HAS_NEON)
+ static float Convolve_NEON(const float* input_ptr, const float* k1,
+ const float* k2,
+ double kernel_interpolation_factor);
+#endif
+
+ // The ratio of input / output sample rates.
+ double io_sample_rate_ratio_;
+
+ // An index on the source input buffer with sub-sample precision. It must be
+ // double precision to avoid drift.
+ double virtual_source_idx_;
+
+ // The buffer is primed once at the very beginning of processing.
+ bool buffer_primed_;
+
+ // Source of data for resampling.
+ SincResamplerCallback* read_cb_;
+
+ // The size (in samples) to request from each |read_cb_| execution.
+ const size_t request_frames_;
+
+ // The number of source frames processed per pass.
+ size_t block_size_;
+
+ // The size (in samples) of the internal buffer used by the resampler.
+ const size_t input_buffer_size_;
+
+ // Contains kKernelOffsetCount kernels back-to-back, each of size kKernelSize.
+ // The kernel offsets are sub-sample shifts of a windowed sinc shifted from
+ // 0.0 to 1.0 sample.
+ rtc::scoped_ptr<float[], AlignedFreeDeleter> kernel_storage_;
+ rtc::scoped_ptr<float[], AlignedFreeDeleter> kernel_pre_sinc_storage_;
+ rtc::scoped_ptr<float[], AlignedFreeDeleter> kernel_window_storage_;
+
+ // Data from the source is copied into this buffer for each processing pass.
+ rtc::scoped_ptr<float[], AlignedFreeDeleter> input_buffer_;
+
+ // Stores the runtime selection of which Convolve function to use.
+ // TODO(ajm): Move to using a global static which must only be initialized
+ // once by the user. We're not doing this initially, because we don't have
+ // e.g. a LazyInstance helper in webrtc.
+#if defined(WEBRTC_CPU_DETECTION)
+ typedef float (*ConvolveProc)(const float*, const float*, const float*,
+ double);
+ ConvolveProc convolve_proc_;
+#endif
+
+ // Pointers to the various regions inside |input_buffer_|. See the diagram at
+ // the top of the .cc file for more information.
+ float* r0_;
+ float* const r1_;
+ float* const r2_;
+ float* r3_;
+ float* r4_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(SincResampler);
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_
diff --git a/webrtc/common_audio/resampler/sinc_resampler_neon.cc b/webrtc/common_audio/resampler/sinc_resampler_neon.cc
new file mode 100644
index 0000000000..e909a6c5de
--- /dev/null
+++ b/webrtc/common_audio/resampler/sinc_resampler_neon.cc
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// Modified from the Chromium original:
+// src/media/base/sinc_resampler.cc
+
+#include "webrtc/common_audio/resampler/sinc_resampler.h"
+
+#include <arm_neon.h>
+
+namespace webrtc {
+
+float SincResampler::Convolve_NEON(const float* input_ptr, const float* k1,
+ const float* k2,
+ double kernel_interpolation_factor) {
+ float32x4_t m_input;
+ float32x4_t m_sums1 = vmovq_n_f32(0);
+ float32x4_t m_sums2 = vmovq_n_f32(0);
+
+ const float* upper = input_ptr + kKernelSize;
+ for (; input_ptr < upper; ) {
+ m_input = vld1q_f32(input_ptr);
+ input_ptr += 4;
+ m_sums1 = vmlaq_f32(m_sums1, m_input, vld1q_f32(k1));
+ k1 += 4;
+ m_sums2 = vmlaq_f32(m_sums2, m_input, vld1q_f32(k2));
+ k2 += 4;
+ }
+
+ // Linearly interpolate the two "convolutions".
+ m_sums1 = vmlaq_f32(
+ vmulq_f32(m_sums1, vmovq_n_f32(1.0 - kernel_interpolation_factor)),
+ m_sums2, vmovq_n_f32(kernel_interpolation_factor));
+
+ // Sum components together.
+ float32x2_t m_half = vadd_f32(vget_high_f32(m_sums1), vget_low_f32(m_sums1));
+ return vget_lane_f32(vpadd_f32(m_half, m_half), 0);
+}
+
+} // namespace webrtc
diff --git a/webrtc/common_audio/resampler/sinc_resampler_sse.cc b/webrtc/common_audio/resampler/sinc_resampler_sse.cc
new file mode 100644
index 0000000000..9e3953fede
--- /dev/null
+++ b/webrtc/common_audio/resampler/sinc_resampler_sse.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// Modified from the Chromium original:
+// src/media/base/simd/sinc_resampler_sse.cc
+
+#include "webrtc/common_audio/resampler/sinc_resampler.h"
+
+#include <xmmintrin.h>
+
+namespace webrtc {
+
+float SincResampler::Convolve_SSE(const float* input_ptr, const float* k1,
+ const float* k2,
+ double kernel_interpolation_factor) {
+ __m128 m_input;
+ __m128 m_sums1 = _mm_setzero_ps();
+ __m128 m_sums2 = _mm_setzero_ps();
+
+ // Based on |input_ptr| alignment, we need to use loadu or load. Unrolling
+ // these loops hurt performance in local testing.
+ if (reinterpret_cast<uintptr_t>(input_ptr) & 0x0F) {
+ for (size_t i = 0; i < kKernelSize; i += 4) {
+ m_input = _mm_loadu_ps(input_ptr + i);
+ m_sums1 = _mm_add_ps(m_sums1, _mm_mul_ps(m_input, _mm_load_ps(k1 + i)));
+ m_sums2 = _mm_add_ps(m_sums2, _mm_mul_ps(m_input, _mm_load_ps(k2 + i)));
+ }
+ } else {
+ for (size_t i = 0; i < kKernelSize; i += 4) {
+ m_input = _mm_load_ps(input_ptr + i);
+ m_sums1 = _mm_add_ps(m_sums1, _mm_mul_ps(m_input, _mm_load_ps(k1 + i)));
+ m_sums2 = _mm_add_ps(m_sums2, _mm_mul_ps(m_input, _mm_load_ps(k2 + i)));
+ }
+ }
+
+ // Linearly interpolate the two "convolutions".
+ m_sums1 = _mm_mul_ps(m_sums1, _mm_set_ps1(
+ static_cast<float>(1.0 - kernel_interpolation_factor)));
+ m_sums2 = _mm_mul_ps(m_sums2, _mm_set_ps1(
+ static_cast<float>(kernel_interpolation_factor)));
+ m_sums1 = _mm_add_ps(m_sums1, m_sums2);
+
+ // Sum components together.
+ float result;
+ m_sums2 = _mm_add_ps(_mm_movehl_ps(m_sums1, m_sums1), m_sums1);
+ _mm_store_ss(&result, _mm_add_ss(m_sums2, _mm_shuffle_ps(
+ m_sums2, m_sums2, 1)));
+
+ return result;
+}
+
+} // namespace webrtc
diff --git a/webrtc/common_audio/resampler/sinc_resampler_unittest.cc b/webrtc/common_audio/resampler/sinc_resampler_unittest.cc
new file mode 100644
index 0000000000..b8d6c341a2
--- /dev/null
+++ b/webrtc/common_audio/resampler/sinc_resampler_unittest.cc
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// Modified from the Chromium original:
+// src/media/base/sinc_resampler_unittest.cc
+
+// MSVC++ requires this to be set before any other includes to get M_PI.
+#define _USE_MATH_DEFINES
+
+#include <math.h>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/common_audio/resampler/sinc_resampler.h"
+#include "webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h"
+#include "webrtc/system_wrappers/include/cpu_features_wrapper.h"
+#include "webrtc/system_wrappers/include/stringize_macros.h"
+#include "webrtc/system_wrappers/include/tick_util.h"
+#include "webrtc/test/test_suite.h"
+
+using testing::_;
+
+namespace webrtc {
+
+static const double kSampleRateRatio = 192000.0 / 44100.0;
+static const double kKernelInterpolationFactor = 0.5;
+
+// Helper class to ensure ChunkedResample() functions properly.
+class MockSource : public SincResamplerCallback {
+ public:
+ MOCK_METHOD2(Run, void(size_t frames, float* destination));
+};
+
+ACTION(ClearBuffer) {
+ memset(arg1, 0, arg0 * sizeof(float));
+}
+
+ACTION(FillBuffer) {
+ // Value chosen arbitrarily such that SincResampler resamples it to something
+ // easily representable on all platforms; e.g., using kSampleRateRatio this
+ // becomes 1.81219.
+ memset(arg1, 64, arg0 * sizeof(float));
+}
+
+// Test requesting multiples of ChunkSize() frames results in the proper number
+// of callbacks.
+TEST(SincResamplerTest, ChunkedResample) {
+ MockSource mock_source;
+
+ // Choose a high ratio of input to output samples which will result in quick
+ // exhaustion of SincResampler's internal buffers.
+ SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize,
+ &mock_source);
+
+ static const int kChunks = 2;
+ size_t max_chunk_size = resampler.ChunkSize() * kChunks;
+ rtc::scoped_ptr<float[]> resampled_destination(new float[max_chunk_size]);
+
+ // Verify requesting ChunkSize() frames causes a single callback.
+ EXPECT_CALL(mock_source, Run(_, _))
+ .Times(1).WillOnce(ClearBuffer());
+ resampler.Resample(resampler.ChunkSize(), resampled_destination.get());
+
+ // Verify requesting kChunks * ChunkSize() frames causes kChunks callbacks.
+ testing::Mock::VerifyAndClear(&mock_source);
+ EXPECT_CALL(mock_source, Run(_, _))
+ .Times(kChunks).WillRepeatedly(ClearBuffer());
+ resampler.Resample(max_chunk_size, resampled_destination.get());
+}
+
+// Test flush resets the internal state properly.
+TEST(SincResamplerTest, Flush) {
+ MockSource mock_source;
+ SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize,
+ &mock_source);
+ rtc::scoped_ptr<float[]> resampled_destination(
+ new float[resampler.ChunkSize()]);
+
+ // Fill the resampler with junk data.
+ EXPECT_CALL(mock_source, Run(_, _))
+ .Times(1).WillOnce(FillBuffer());
+ resampler.Resample(resampler.ChunkSize() / 2, resampled_destination.get());
+ ASSERT_NE(resampled_destination[0], 0);
+
+ // Flush and request more data, which should all be zeros now.
+ resampler.Flush();
+ testing::Mock::VerifyAndClear(&mock_source);
+ EXPECT_CALL(mock_source, Run(_, _))
+ .Times(1).WillOnce(ClearBuffer());
+ resampler.Resample(resampler.ChunkSize() / 2, resampled_destination.get());
+ for (size_t i = 0; i < resampler.ChunkSize() / 2; ++i)
+ ASSERT_FLOAT_EQ(resampled_destination[i], 0);
+}
+
+// Test flush resets the internal state properly.
+TEST(SincResamplerTest, DISABLED_SetRatioBench) {
+ MockSource mock_source;
+ SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize,
+ &mock_source);
+
+ TickTime start = TickTime::Now();
+ for (int i = 1; i < 10000; ++i)
+ resampler.SetRatio(1.0 / i);
+ double total_time_c_us = (TickTime::Now() - start).Microseconds();
+ printf("SetRatio() took %.2fms.\n", total_time_c_us / 1000);
+}
+
+
+// Define platform independent function name for Convolve* tests.
+#if defined(WEBRTC_ARCH_X86_FAMILY)
+#define CONVOLVE_FUNC Convolve_SSE
+#elif defined(WEBRTC_ARCH_ARM_V7)
+#define CONVOLVE_FUNC Convolve_NEON
+#endif
+
+// Ensure various optimized Convolve() methods return the same value. Only run
+// this test if other optimized methods exist, otherwise the default Convolve()
+// will be tested by the parameterized SincResampler tests below.
+#if defined(CONVOLVE_FUNC)
+TEST(SincResamplerTest, Convolve) {
+#if defined(WEBRTC_ARCH_X86_FAMILY)
+ ASSERT_TRUE(WebRtc_GetCPUInfo(kSSE2));
+#elif defined(WEBRTC_ARCH_ARM_V7)
+ ASSERT_TRUE(WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON);
+#endif
+
+ // Initialize a dummy resampler.
+ MockSource mock_source;
+ SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize,
+ &mock_source);
+
+ // The optimized Convolve methods are slightly more precise than Convolve_C(),
+ // so comparison must be done using an epsilon.
+ static const double kEpsilon = 0.00000005;
+
+ // Use a kernel from SincResampler as input and kernel data, this has the
+ // benefit of already being properly sized and aligned for Convolve_SSE().
+ double result = resampler.Convolve_C(
+ resampler.kernel_storage_.get(), resampler.kernel_storage_.get(),
+ resampler.kernel_storage_.get(), kKernelInterpolationFactor);
+ double result2 = resampler.CONVOLVE_FUNC(
+ resampler.kernel_storage_.get(), resampler.kernel_storage_.get(),
+ resampler.kernel_storage_.get(), kKernelInterpolationFactor);
+ EXPECT_NEAR(result2, result, kEpsilon);
+
+ // Test Convolve() w/ unaligned input pointer.
+ result = resampler.Convolve_C(
+ resampler.kernel_storage_.get() + 1, resampler.kernel_storage_.get(),
+ resampler.kernel_storage_.get(), kKernelInterpolationFactor);
+ result2 = resampler.CONVOLVE_FUNC(
+ resampler.kernel_storage_.get() + 1, resampler.kernel_storage_.get(),
+ resampler.kernel_storage_.get(), kKernelInterpolationFactor);
+ EXPECT_NEAR(result2, result, kEpsilon);
+}
+#endif
+
+// Benchmark for the various Convolve() methods. Make sure to build with
+// branding=Chrome so that RTC_DCHECKs are compiled out when benchmarking.
+// Original benchmarks were run with --convolve-iterations=50000000.
+TEST(SincResamplerTest, ConvolveBenchmark) {
+ // Initialize a dummy resampler.
+ MockSource mock_source;
+ SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize,
+ &mock_source);
+
+ // Retrieve benchmark iterations from command line.
+ // TODO(ajm): Reintroduce this as a command line option.
+ const int kConvolveIterations = 1000000;
+
+ printf("Benchmarking %d iterations:\n", kConvolveIterations);
+
+ // Benchmark Convolve_C().
+ TickTime start = TickTime::Now();
+ for (int i = 0; i < kConvolveIterations; ++i) {
+ resampler.Convolve_C(
+ resampler.kernel_storage_.get(), resampler.kernel_storage_.get(),
+ resampler.kernel_storage_.get(), kKernelInterpolationFactor);
+ }
+ double total_time_c_us = (TickTime::Now() - start).Microseconds();
+ printf("Convolve_C took %.2fms.\n", total_time_c_us / 1000);
+
+#if defined(CONVOLVE_FUNC)
+#if defined(WEBRTC_ARCH_X86_FAMILY)
+ ASSERT_TRUE(WebRtc_GetCPUInfo(kSSE2));
+#elif defined(WEBRTC_ARCH_ARM_V7)
+ ASSERT_TRUE(WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON);
+#endif
+
+ // Benchmark with unaligned input pointer.
+ start = TickTime::Now();
+ for (int j = 0; j < kConvolveIterations; ++j) {
+ resampler.CONVOLVE_FUNC(
+ resampler.kernel_storage_.get() + 1, resampler.kernel_storage_.get(),
+ resampler.kernel_storage_.get(), kKernelInterpolationFactor);
+ }
+ double total_time_optimized_unaligned_us =
+ (TickTime::Now() - start).Microseconds();
+ printf(STRINGIZE(CONVOLVE_FUNC) "(unaligned) took %.2fms; which is %.2fx "
+ "faster than Convolve_C.\n", total_time_optimized_unaligned_us / 1000,
+ total_time_c_us / total_time_optimized_unaligned_us);
+
+ // Benchmark with aligned input pointer.
+ start = TickTime::Now();
+ for (int j = 0; j < kConvolveIterations; ++j) {
+ resampler.CONVOLVE_FUNC(
+ resampler.kernel_storage_.get(), resampler.kernel_storage_.get(),
+ resampler.kernel_storage_.get(), kKernelInterpolationFactor);
+ }
+ double total_time_optimized_aligned_us =
+ (TickTime::Now() - start).Microseconds();
+ printf(STRINGIZE(CONVOLVE_FUNC) " (aligned) took %.2fms; which is %.2fx "
+ "faster than Convolve_C and %.2fx faster than "
+ STRINGIZE(CONVOLVE_FUNC) " (unaligned).\n",
+ total_time_optimized_aligned_us / 1000,
+ total_time_c_us / total_time_optimized_aligned_us,
+ total_time_optimized_unaligned_us / total_time_optimized_aligned_us);
+#endif
+}
+
+#undef CONVOLVE_FUNC
+
+typedef std::tr1::tuple<int, int, double, double> SincResamplerTestData;
+class SincResamplerTest
+ : public testing::TestWithParam<SincResamplerTestData> {
+ public:
+ SincResamplerTest()
+ : input_rate_(std::tr1::get<0>(GetParam())),
+ output_rate_(std::tr1::get<1>(GetParam())),
+ rms_error_(std::tr1::get<2>(GetParam())),
+ low_freq_error_(std::tr1::get<3>(GetParam())) {
+ }
+
+ virtual ~SincResamplerTest() {}
+
+ protected:
+ int input_rate_;
+ int output_rate_;
+ double rms_error_;
+ double low_freq_error_;
+};
+
+// Tests resampling using a given input and output sample rate.
+TEST_P(SincResamplerTest, Resample) {
+ // Make comparisons using one second of data.
+ static const double kTestDurationSecs = 1;
+ const size_t input_samples =
+ static_cast<size_t>(kTestDurationSecs * input_rate_);
+ const size_t output_samples =
+ static_cast<size_t>(kTestDurationSecs * output_rate_);
+
+ // Nyquist frequency for the input sampling rate.
+ const double input_nyquist_freq = 0.5 * input_rate_;
+
+ // Source for data to be resampled.
+ SinusoidalLinearChirpSource resampler_source(
+ input_rate_, input_samples, input_nyquist_freq, 0);
+
+ const double io_ratio = input_rate_ / static_cast<double>(output_rate_);
+ SincResampler resampler(io_ratio, SincResampler::kDefaultRequestSize,
+ &resampler_source);
+
+ // Force an update to the sample rate ratio to ensure dyanmic sample rate
+ // changes are working correctly.
+ rtc::scoped_ptr<float[]> kernel(new float[SincResampler::kKernelStorageSize]);
+ memcpy(kernel.get(), resampler.get_kernel_for_testing(),
+ SincResampler::kKernelStorageSize);
+ resampler.SetRatio(M_PI);
+ ASSERT_NE(0, memcmp(kernel.get(), resampler.get_kernel_for_testing(),
+ SincResampler::kKernelStorageSize));
+ resampler.SetRatio(io_ratio);
+ ASSERT_EQ(0, memcmp(kernel.get(), resampler.get_kernel_for_testing(),
+ SincResampler::kKernelStorageSize));
+
+ // 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.
+ rtc::scoped_ptr<float[]> resampled_destination(new float[output_samples]);
+ rtc::scoped_ptr<float[]> pure_destination(new float[output_samples]);
+
+ // Generate resampled signal.
+ resampler.Resample(output_samples, resampled_destination.get());
+
+ // Generate pure signal.
+ SinusoidalLinearChirpSource pure_source(
+ output_rate_, output_samples, input_nyquist_freq, 0);
+ pure_source.Run(output_samples, pure_destination.get());
+
+ // Range of the Nyquist frequency (0.5 * min(input rate, output_rate)) which
+ // we refer to as low and high.
+ static const double kLowFrequencyNyquistRange = 0.7;
+ static const double kHighFrequencyNyquistRange = 0.9;
+
+ // Calculate Root-Mean-Square-Error and maximum error for the resampling.
+ double sum_of_squares = 0;
+ double low_freq_max_error = 0;
+ double high_freq_max_error = 0;
+ int minimum_rate = std::min(input_rate_, output_rate_);
+ double low_frequency_range = kLowFrequencyNyquistRange * 0.5 * minimum_rate;
+ double high_frequency_range = kHighFrequencyNyquistRange * 0.5 * minimum_rate;
+ for (size_t i = 0; i < output_samples; ++i) {
+ double error = fabs(resampled_destination[i] - pure_destination[i]);
+
+ if (pure_source.Frequency(i) < low_frequency_range) {
+ if (error > low_freq_max_error)
+ low_freq_max_error = error;
+ } else if (pure_source.Frequency(i) < high_frequency_range) {
+ if (error > high_freq_max_error)
+ high_freq_max_error = error;
+ }
+ // TODO(dalecurtis): Sanity check frequencies > kHighFrequencyNyquistRange.
+
+ sum_of_squares += error * error;
+ }
+
+ double rms_error = sqrt(sum_of_squares / output_samples);
+
+ // Convert each error to dbFS.
+ #define DBFS(x) 20 * log10(x)
+ rms_error = DBFS(rms_error);
+ low_freq_max_error = DBFS(low_freq_max_error);
+ high_freq_max_error = DBFS(high_freq_max_error);
+
+ EXPECT_LE(rms_error, rms_error_);
+ EXPECT_LE(low_freq_max_error, low_freq_error_);
+
+ // All conversions currently have a high frequency error around -6 dbFS.
+ static const double kHighFrequencyMaxError = -6.02;
+ EXPECT_LE(high_freq_max_error, kHighFrequencyMaxError);
+}
+
+// Almost all conversions have an RMS error of around -14 dbFS.
+static const double kResamplingRMSError = -14.58;
+
+// 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(
+ SincResamplerTest, SincResamplerTest, testing::Values(
+ // To 44.1kHz
+ std::tr1::make_tuple(8000, 44100, kResamplingRMSError, -62.73),
+ std::tr1::make_tuple(11025, 44100, kResamplingRMSError, -72.19),
+ std::tr1::make_tuple(16000, 44100, kResamplingRMSError, -62.54),
+ std::tr1::make_tuple(22050, 44100, kResamplingRMSError, -73.53),
+ std::tr1::make_tuple(32000, 44100, kResamplingRMSError, -63.32),
+ std::tr1::make_tuple(44100, 44100, kResamplingRMSError, -73.53),
+ std::tr1::make_tuple(48000, 44100, -15.01, -64.04),
+ std::tr1::make_tuple(96000, 44100, -18.49, -25.51),
+ std::tr1::make_tuple(192000, 44100, -20.50, -13.31),
+
+ // To 48kHz
+ std::tr1::make_tuple(8000, 48000, kResamplingRMSError, -63.43),
+ std::tr1::make_tuple(11025, 48000, kResamplingRMSError, -62.61),
+ std::tr1::make_tuple(16000, 48000, kResamplingRMSError, -63.96),
+ std::tr1::make_tuple(22050, 48000, kResamplingRMSError, -62.42),
+ std::tr1::make_tuple(32000, 48000, kResamplingRMSError, -64.04),
+ std::tr1::make_tuple(44100, 48000, kResamplingRMSError, -62.63),
+ std::tr1::make_tuple(48000, 48000, kResamplingRMSError, -73.52),
+ std::tr1::make_tuple(96000, 48000, -18.40, -28.44),
+ std::tr1::make_tuple(192000, 48000, -20.43, -14.11),
+
+ // To 96kHz
+ std::tr1::make_tuple(8000, 96000, kResamplingRMSError, -63.19),
+ std::tr1::make_tuple(11025, 96000, kResamplingRMSError, -62.61),
+ std::tr1::make_tuple(16000, 96000, kResamplingRMSError, -63.39),
+ std::tr1::make_tuple(22050, 96000, kResamplingRMSError, -62.42),
+ std::tr1::make_tuple(32000, 96000, kResamplingRMSError, -63.95),
+ std::tr1::make_tuple(44100, 96000, kResamplingRMSError, -62.63),
+ std::tr1::make_tuple(48000, 96000, kResamplingRMSError, -73.52),
+ std::tr1::make_tuple(96000, 96000, kResamplingRMSError, -73.52),
+ std::tr1::make_tuple(192000, 96000, kResamplingRMSError, -28.41),
+
+ // To 192kHz
+ std::tr1::make_tuple(8000, 192000, kResamplingRMSError, -63.10),
+ std::tr1::make_tuple(11025, 192000, kResamplingRMSError, -62.61),
+ std::tr1::make_tuple(16000, 192000, kResamplingRMSError, -63.14),
+ std::tr1::make_tuple(22050, 192000, kResamplingRMSError, -62.42),
+ std::tr1::make_tuple(32000, 192000, kResamplingRMSError, -63.38),
+ std::tr1::make_tuple(44100, 192000, kResamplingRMSError, -62.63),
+ std::tr1::make_tuple(48000, 192000, kResamplingRMSError, -73.44),
+ std::tr1::make_tuple(96000, 192000, kResamplingRMSError, -73.52),
+ std::tr1::make_tuple(192000, 192000, kResamplingRMSError, -73.52)));
+
+} // namespace webrtc
diff --git a/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.cc b/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.cc
new file mode 100644
index 0000000000..5d215688ba
--- /dev/null
+++ b/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// MSVC++ requires this to be set before any other includes to get M_PI.
+#define _USE_MATH_DEFINES
+
+#include "webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h"
+
+#include <math.h>
+
+namespace webrtc {
+
+SinusoidalLinearChirpSource::SinusoidalLinearChirpSource(int sample_rate,
+ size_t samples,
+ double max_frequency,
+ double delay_samples)
+ : sample_rate_(sample_rate),
+ total_samples_(samples),
+ max_frequency_(max_frequency),
+ current_index_(0),
+ delay_samples_(delay_samples) {
+ // Chirp rate.
+ double duration = static_cast<double>(total_samples_) / sample_rate_;
+ k_ = (max_frequency_ - kMinFrequency) / duration;
+}
+
+void SinusoidalLinearChirpSource::Run(size_t frames, float* destination) {
+ for (size_t i = 0; i < frames; ++i, ++current_index_) {
+ // Filter out frequencies higher than Nyquist.
+ if (Frequency(current_index_) > 0.5 * sample_rate_) {
+ destination[i] = 0;
+ } else {
+ // Calculate time in seconds.
+ if (current_index_ < delay_samples_) {
+ destination[i] = 0;
+ } else {
+ // Sinusoidal linear chirp.
+ double t = (current_index_ - delay_samples_) / sample_rate_;
+ destination[i] =
+ sin(2 * M_PI * (kMinFrequency * t + (k_ / 2) * t * t));
+ }
+ }
+ }
+}
+
+double SinusoidalLinearChirpSource::Frequency(size_t position) {
+ return kMinFrequency + (position - delay_samples_) *
+ (max_frequency_ - kMinFrequency) / total_samples_;
+}
+
+} // namespace webrtc
diff --git a/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h b/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h
new file mode 100644
index 0000000000..1807f86a19
--- /dev/null
+++ b/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// Modified from the Chromium original here:
+// src/media/base/sinc_resampler_unittest.cc
+
+#ifndef WEBRTC_COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_
+#define WEBRTC_COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/common_audio/resampler/sinc_resampler.h"
+
+namespace webrtc {
+
+// Fake audio source for testing the resampler. Generates a sinusoidal linear
+// chirp (http://en.wikipedia.org/wiki/Chirp) which can be tuned to stress the
+// resampler for the specific sample rate conversion being used.
+class SinusoidalLinearChirpSource : public SincResamplerCallback {
+ public:
+ // |delay_samples| can be used to insert a fractional sample delay into the
+ // source. It will produce zeros until non-negative time is reached.
+ SinusoidalLinearChirpSource(int sample_rate, size_t samples,
+ double max_frequency, double delay_samples);
+
+ virtual ~SinusoidalLinearChirpSource() {}
+
+ void Run(size_t frames, float* destination) override;
+
+ double Frequency(size_t position);
+
+ private:
+ enum {
+ kMinFrequency = 5
+ };
+
+ int sample_rate_;
+ size_t total_samples_;
+ double max_frequency_;
+ double k_;
+ size_t current_index_;
+ double delay_samples_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(SinusoidalLinearChirpSource);
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_