summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/common_audio/resampler
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /third_party/libwebrtc/common_audio/resampler
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/common_audio/resampler')
-rw-r--r--third_party/libwebrtc/common_audio/resampler/include/push_resampler.h59
-rw-r--r--third_party/libwebrtc/common_audio/resampler/include/resampler.h99
-rw-r--r--third_party/libwebrtc/common_audio/resampler/push_resampler.cc123
-rw-r--r--third_party/libwebrtc/common_audio/resampler/push_resampler_unittest.cc48
-rw-r--r--third_party/libwebrtc/common_audio/resampler/push_sinc_resampler.cc102
-rw-r--r--third_party/libwebrtc/common_audio/resampler/push_sinc_resampler.h81
-rw-r--r--third_party/libwebrtc/common_audio/resampler/push_sinc_resampler_unittest.cc367
-rw-r--r--third_party/libwebrtc/common_audio/resampler/resampler.cc923
-rw-r--r--third_party/libwebrtc/common_audio/resampler/resampler_unittest.cc168
-rw-r--r--third_party/libwebrtc/common_audio/resampler/sinc_resampler.cc366
-rw-r--r--third_party/libwebrtc/common_audio/resampler/sinc_resampler.h181
-rw-r--r--third_party/libwebrtc/common_audio/resampler/sinc_resampler_avx2.cc66
-rw-r--r--third_party/libwebrtc/common_audio/resampler/sinc_resampler_neon.cc48
-rw-r--r--third_party/libwebrtc/common_audio/resampler/sinc_resampler_sse.cc63
-rw-r--r--third_party/libwebrtc/common_audio/resampler/sinc_resampler_unittest.cc393
-rw-r--r--third_party/libwebrtc/common_audio/resampler/sinusoidal_linear_chirp_source.cc57
-rw-r--r--third_party/libwebrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h56
17 files changed, 3200 insertions, 0 deletions
diff --git a/third_party/libwebrtc/common_audio/resampler/include/push_resampler.h b/third_party/libwebrtc/common_audio/resampler/include/push_resampler.h
new file mode 100644
index 0000000000..3da67120f0
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/resampler/include/push_resampler.h
@@ -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.
+ */
+
+#ifndef COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_
+#define COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_
+
+#include <memory>
+#include <vector>
+
+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,
+ size_t 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:
+ int src_sample_rate_hz_;
+ int dst_sample_rate_hz_;
+ size_t num_channels_;
+ // Vector that is needed to provide the proper inputs and outputs to the
+ // interleave/de-interleave methods used in Resample. This needs to be
+ // heap-allocated on the state to support an arbitrary number of channels
+ // without doing run-time heap-allocations in the Resample method.
+ std::vector<T*> channel_data_array_;
+
+ struct ChannelResampler {
+ std::unique_ptr<PushSincResampler> resampler;
+ std::vector<T> source;
+ std::vector<T> destination;
+ };
+
+ std::vector<ChannelResampler> channel_resamplers_;
+};
+} // namespace webrtc
+
+#endif // COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_
diff --git a/third_party/libwebrtc/common_audio/resampler/include/resampler.h b/third_party/libwebrtc/common_audio/resampler/include/resampler.h
new file mode 100644
index 0000000000..41940f9a12
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/resampler/include/resampler.h
@@ -0,0 +1,99 @@
+/*
+ * 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 COMMON_AUDIO_RESAMPLER_INCLUDE_RESAMPLER_H_
+#define COMMON_AUDIO_RESAMPLER_INCLUDE_RESAMPLER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace webrtc {
+
+// All methods return 0 on success and -1 on failure.
+class Resampler {
+ public:
+ Resampler();
+ Resampler(int inFreq, int outFreq, size_t num_channels);
+ ~Resampler();
+
+ // Reset all states
+ int Reset(int inFreq, int outFreq, size_t num_channels);
+
+ // Reset all states if any parameter has changed
+ int ResetIfNeeded(int inFreq, int outFreq, size_t num_channels);
+
+ // Resample samplesIn to samplesOut.
+ int Push(const int16_t* samplesIn,
+ size_t lengthIn,
+ int16_t* samplesOut,
+ size_t maxLen,
+ size_t& outLen); // NOLINT: to avoid changing APIs
+
+ private:
+ enum ResamplerMode {
+ kResamplerMode1To1,
+ kResamplerMode1To2,
+ kResamplerMode1To3,
+ kResamplerMode1To4,
+ kResamplerMode1To6,
+ kResamplerMode1To12,
+ kResamplerMode2To3,
+ kResamplerMode2To11,
+ kResamplerMode4To11,
+ kResamplerMode8To11,
+ kResamplerMode11To16,
+ kResamplerMode11To32,
+ kResamplerMode2To1,
+ kResamplerMode3To1,
+ kResamplerMode4To1,
+ kResamplerMode6To1,
+ kResamplerMode12To1,
+ kResamplerMode3To2,
+ kResamplerMode11To2,
+ kResamplerMode11To4,
+ kResamplerMode11To8
+ };
+
+ // Computes the resampler mode for a given sampling frequency pair.
+ // Returns -1 for unsupported frequency pairs.
+ static int ComputeResamplerMode(int in_freq_hz,
+ int out_freq_hz,
+ ResamplerMode* mode);
+
+ // 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_;
+ size_t num_channels_;
+
+ // Extra instance for stereo
+ Resampler* helper_left_;
+ Resampler* helper_right_;
+};
+
+} // namespace webrtc
+
+#endif // COMMON_AUDIO_RESAMPLER_INCLUDE_RESAMPLER_H_
diff --git a/third_party/libwebrtc/common_audio/resampler/push_resampler.cc b/third_party/libwebrtc/common_audio/resampler/push_resampler.cc
new file mode 100644
index 0000000000..810d778993
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/resampler/push_resampler.cc
@@ -0,0 +1,123 @@
+/*
+ * 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 "common_audio/resampler/include/push_resampler.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include <memory>
+
+#include "common_audio/include/audio_util.h"
+#include "common_audio/resampler/push_sinc_resampler.h"
+#include "rtc_base/checks.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,
+ size_t num_channels) {
+ // These checks used to be factored out of this template function due to
+ // Windows debug build issues with clang. http://crbug.com/615050
+ RTC_DCHECK_GT(src_sample_rate_hz, 0);
+ RTC_DCHECK_GT(dst_sample_rate_hz, 0);
+ RTC_DCHECK_GT(num_channels, 0);
+
+ 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) {
+ 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);
+ channel_resamplers_.clear();
+ for (size_t i = 0; i < num_channels; ++i) {
+ channel_resamplers_.push_back(ChannelResampler());
+ auto channel_resampler = channel_resamplers_.rbegin();
+ channel_resampler->resampler = std::make_unique<PushSincResampler>(
+ src_size_10ms_mono, dst_size_10ms_mono);
+ channel_resampler->source.resize(src_size_10ms_mono);
+ channel_resampler->destination.resize(dst_size_10ms_mono);
+ }
+
+ channel_data_array_.resize(num_channels_);
+
+ return 0;
+}
+
+template <typename T>
+int PushResampler<T>::Resample(const T* src,
+ size_t src_length,
+ T* dst,
+ size_t dst_capacity) {
+ // These checks used to be factored out of this template function due to
+ // Windows debug build issues with clang. http://crbug.com/615050
+ const size_t src_size_10ms = (src_sample_rate_hz_ / 100) * num_channels_;
+ const size_t dst_size_10ms = (dst_sample_rate_hz_ / 100) * num_channels_;
+ RTC_DCHECK_EQ(src_length, src_size_10ms);
+ RTC_DCHECK_GE(dst_capacity, dst_size_10ms);
+
+ 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);
+ }
+
+ const size_t src_length_mono = src_length / num_channels_;
+ const size_t dst_capacity_mono = dst_capacity / num_channels_;
+
+ for (size_t ch = 0; ch < num_channels_; ++ch) {
+ channel_data_array_[ch] = channel_resamplers_[ch].source.data();
+ }
+
+ Deinterleave(src, src_length_mono, num_channels_, channel_data_array_.data());
+
+ size_t dst_length_mono = 0;
+
+ for (auto& resampler : channel_resamplers_) {
+ dst_length_mono = resampler.resampler->Resample(
+ resampler.source.data(), src_length_mono, resampler.destination.data(),
+ dst_capacity_mono);
+ }
+
+ for (size_t ch = 0; ch < num_channels_; ++ch) {
+ channel_data_array_[ch] = channel_resamplers_[ch].destination.data();
+ }
+
+ Interleave(channel_data_array_.data(), dst_length_mono, num_channels_, dst);
+ return static_cast<int>(dst_length_mono * num_channels_);
+}
+
+// Explictly generate required instantiations.
+template class PushResampler<int16_t>;
+template class PushResampler<float>;
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/common_audio/resampler/push_resampler_unittest.cc b/third_party/libwebrtc/common_audio/resampler/push_resampler_unittest.cc
new file mode 100644
index 0000000000..91f2233aad
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/resampler/push_resampler_unittest.cc
@@ -0,0 +1,48 @@
+/*
+ * 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 "common_audio/resampler/include/push_resampler.h"
+
+#include "rtc_base/checks.h" // RTC_DCHECK_IS_ON
+#include "test/gtest.h"
+#include "test/testsupport/rtc_expect_death.h"
+
+// Quality testing of PushResampler is done in audio/remix_resample_unittest.cc.
+
+namespace webrtc {
+
+TEST(PushResamplerTest, VerifiesInputParameters) {
+ PushResampler<int16_t> resampler;
+ EXPECT_EQ(0, resampler.InitializeIfNeeded(16000, 16000, 1));
+ EXPECT_EQ(0, resampler.InitializeIfNeeded(16000, 16000, 2));
+ EXPECT_EQ(0, resampler.InitializeIfNeeded(16000, 16000, 8));
+}
+
+#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+TEST(PushResamplerDeathTest, VerifiesBadInputParameters1) {
+ PushResampler<int16_t> resampler;
+ RTC_EXPECT_DEATH(resampler.InitializeIfNeeded(-1, 16000, 1),
+ "src_sample_rate_hz");
+}
+
+TEST(PushResamplerDeathTest, VerifiesBadInputParameters2) {
+ PushResampler<int16_t> resampler;
+ RTC_EXPECT_DEATH(resampler.InitializeIfNeeded(16000, -1, 1),
+ "dst_sample_rate_hz");
+}
+
+TEST(PushResamplerDeathTest, VerifiesBadInputParameters3) {
+ PushResampler<int16_t> resampler;
+ RTC_EXPECT_DEATH(resampler.InitializeIfNeeded(16000, 16000, 0),
+ "num_channels");
+}
+#endif
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/common_audio/resampler/push_sinc_resampler.cc b/third_party/libwebrtc/common_audio/resampler/push_sinc_resampler.cc
new file mode 100644
index 0000000000..d4b7eed026
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/resampler/push_sinc_resampler.cc
@@ -0,0 +1,102 @@
+/*
+ * 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 "common_audio/resampler/push_sinc_resampler.h"
+
+#include <cstring>
+
+#include "common_audio/include/audio_util.h"
+#include "rtc_base/checks.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/third_party/libwebrtc/common_audio/resampler/push_sinc_resampler.h b/third_party/libwebrtc/common_audio/resampler/push_sinc_resampler.h
new file mode 100644
index 0000000000..7946ef8f82
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/resampler/push_sinc_resampler.h
@@ -0,0 +1,81 @@
+/*
+ * 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 COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_
+#define COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "common_audio/resampler/sinc_resampler.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;
+
+ PushSincResampler(const PushSincResampler&) = delete;
+ PushSincResampler& operator=(const PushSincResampler&) = delete;
+
+ // 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(); }
+
+ std::unique_ptr<SincResampler> resampler_;
+ std::unique_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_;
+};
+
+} // namespace webrtc
+
+#endif // COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_
diff --git a/third_party/libwebrtc/common_audio/resampler/push_sinc_resampler_unittest.cc b/third_party/libwebrtc/common_audio/resampler/push_sinc_resampler_unittest.cc
new file mode 100644
index 0000000000..8f82199d1d
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/resampler/push_sinc_resampler_unittest.cc
@@ -0,0 +1,367 @@
+/*
+ * 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 "common_audio/resampler/push_sinc_resampler.h"
+
+#include <algorithm>
+#include <cmath>
+#include <cstring>
+#include <memory>
+
+#include "common_audio/include/audio_util.h"
+#include "common_audio/resampler/sinusoidal_linear_chirp_source.h"
+#include "rtc_base/time_utils.h"
+#include "test/gmock.h"
+#include "test/gtest.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) override {
+ 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;
+
+ std::unique_ptr<float[]> resampled_destination(new float[output_samples]);
+ std::unique_ptr<float[]> source(new float[input_samples]);
+ std::unique_ptr<int16_t[]> source_int(new int16_t[input_samples]);
+ std::unique_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);
+ int64_t start = rtc::TimeNanos();
+ for (int i = 0; i < kResampleIterations; ++i) {
+ sinc_resampler.Resample(output_samples, resampled_destination.get());
+ }
+ double total_time_sinc_us =
+ (rtc::TimeNanos() - start) / rtc::kNumNanosecsPerMicrosec;
+ printf("SincResampler took %.2f us per frame.\n",
+ total_time_sinc_us / kResampleIterations);
+
+ PushSincResampler resampler(input_samples, output_samples);
+ start = rtc::TimeNanos();
+ 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 =
+ (rtc::TimeNanos() - start) / rtc::kNumNanosecsPerMicrosec;
+ 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.
+ std::unique_ptr<float[]> resampled_destination(new float[output_samples]);
+ std::unique_ptr<float[]> pure_destination(new float[output_samples]);
+ std::unique_ptr<float[]> source(new float[input_samples]);
+ std::unique_ptr<int16_t[]> source_int(new int16_t[input_block_size]);
+ std::unique_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.01;
+ 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_SUITE_P(
+ PushSincResamplerTest,
+ PushSincResamplerTest,
+ ::testing::Values(
+ // First run through the rates tested in SincResamplerTest. The
+ // thresholds are identical.
+ //
+ // We don't directly test rates which fail to provide an integer number
+ // of samples in a 10 ms block (22050 and 11025 Hz), they are replaced
+ // by nearby rates in order to simplify testing.
+ //
+ // The PushSincResampler is in practice sample rate agnostic and derives
+ // resampling ratios from the block size, which for WebRTC purposes are
+ // blocks of floor(sample_rate/100) samples. So the 22050 Hz case is
+ // treated identically to the 22000 Hz case. Direct tests of 22050 Hz
+ // have to account for the simulated clock drift induced by the
+ // resampler inferring an incorrect sample rate ratio, without testing
+ // anything new within the resampler itself.
+
+ // To 22kHz
+ std::make_tuple(8000, 22000, kResamplingRMSError, -62.73),
+ std::make_tuple(11000, 22000, kResamplingRMSError, -74.17),
+ std::make_tuple(16000, 22000, kResamplingRMSError, -62.54),
+ std::make_tuple(22000, 22000, kResamplingRMSError, -73.53),
+ std::make_tuple(32000, 22000, kResamplingRMSError, -46.45),
+ std::make_tuple(44100, 22000, kResamplingRMSError, -28.34),
+ std::make_tuple(48000, 22000, -15.01, -25.56),
+ std::make_tuple(96000, 22000, -18.49, -13.30),
+ std::make_tuple(192000, 22000, -20.50, -9.20),
+
+ // To 44.1kHz
+ ::testing::make_tuple(8000, 44100, kResamplingRMSError, -62.73),
+ ::testing::make_tuple(11000, 44100, kResamplingRMSError, -63.57),
+ ::testing::make_tuple(16000, 44100, kResamplingRMSError, -62.54),
+ ::testing::make_tuple(22000, 44100, kResamplingRMSError, -62.73),
+ ::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(11000, 48000, kResamplingRMSError, -63.96),
+ ::testing::make_tuple(16000, 48000, kResamplingRMSError, -63.96),
+ ::testing::make_tuple(22000, 48000, kResamplingRMSError, -63.80),
+ ::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(11000, 96000, kResamplingRMSError, -63.89),
+ ::testing::make_tuple(16000, 96000, kResamplingRMSError, -63.39),
+ ::testing::make_tuple(22000, 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(11000, 192000, kResamplingRMSError, -63.17),
+ ::testing::make_tuple(16000, 192000, kResamplingRMSError, -63.14),
+ ::testing::make_tuple(22000, 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(11000, 16000, kResamplingRMSError, -72.31),
+ ::testing::make_tuple(16000, 16000, kResamplingRMSError, -75.51),
+ ::testing::make_tuple(22000, 16000, kResamplingRMSError, -52.08),
+ ::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.9596),
+
+ // To 32 kHz
+ ::testing::make_tuple(8000, 32000, kResamplingRMSError, -70.30),
+ ::testing::make_tuple(11000, 32000, kResamplingRMSError, -71.34),
+ ::testing::make_tuple(16000, 32000, kResamplingRMSError, -75.51),
+ ::testing::make_tuple(22000, 32000, kResamplingRMSError, -72.05),
+ ::testing::make_tuple(32000, 32000, kResamplingRMSError, -75.51),
+ ::testing::make_tuple(44100, 32000, -16.44, -51.0349),
+ ::testing::make_tuple(48000, 32000, -16.90, -43.9967),
+ ::testing::make_tuple(96000, 32000, -19.61, -18.04),
+ ::testing::make_tuple(192000, 32000, -21.02, -10.94)));
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/common_audio/resampler/resampler.cc b/third_party/libwebrtc/common_audio/resampler/resampler.cc
new file mode 100644
index 0000000000..0fdb249052
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/resampler/resampler.cc
@@ -0,0 +1,923 @@
+/*
+ * 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 "common_audio/resampler/include/resampler.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common_audio/signal_processing/include/signal_processing_library.h"
+#include "rtc_base/logging.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),
+ helper_left_(nullptr),
+ helper_right_(nullptr) {}
+
+Resampler::Resampler(int inFreq, int outFreq, size_t 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 (helper_left_) {
+ delete helper_left_;
+ }
+ if (helper_right_) {
+ delete helper_right_;
+ }
+}
+
+int Resampler::ResetIfNeeded(int inFreq, int outFreq, size_t 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, size_t num_channels) {
+ if (num_channels != 1 && num_channels != 2) {
+ RTC_LOG(LS_WARNING)
+ << "Reset() called with unsupported channel count, num_channels = "
+ << num_channels;
+ return -1;
+ }
+ ResamplerMode mode;
+ if (ComputeResamplerMode(inFreq, outFreq, &mode) != 0) {
+ RTC_LOG(LS_WARNING)
+ << "Reset() called with unsupported sample rates, inFreq = " << inFreq
+ << ", outFreq = " << outFreq;
+ return -1;
+ }
+ // Reinitialize internal state for the frequencies and sample rates.
+ num_channels_ = num_channels;
+ my_mode_ = mode;
+
+ if (state1_) {
+ free(state1_);
+ state1_ = nullptr;
+ }
+ if (state2_) {
+ free(state2_);
+ state2_ = nullptr;
+ }
+ if (state3_) {
+ free(state3_);
+ state3_ = nullptr;
+ }
+ if (in_buffer_) {
+ free(in_buffer_);
+ in_buffer_ = nullptr;
+ }
+ if (out_buffer_) {
+ free(out_buffer_);
+ out_buffer_ = nullptr;
+ }
+ if (helper_left_) {
+ delete helper_left_;
+ helper_left_ = nullptr;
+ }
+ if (helper_right_) {
+ delete helper_right_;
+ helper_right_ = nullptr;
+ }
+
+ in_buffer_size_ = 0;
+ out_buffer_size_ = 0;
+ in_buffer_size_max_ = 0;
+ out_buffer_size_max_ = 0;
+
+ // We need to track what domain we're in.
+ my_in_frequency_khz_ = inFreq / 1000;
+ my_out_frequency_khz_ = outFreq / 1000;
+
+ if (num_channels_ == 2) {
+ // Create two mono resamplers.
+ helper_left_ = new Resampler(inFreq, outFreq, 1);
+ helper_right_ = new Resampler(inFreq, outFreq, 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(
+ static_cast<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(
+ static_cast<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(
+ static_cast<WebRtcSpl_State16khzTo48khz*>(state3_));
+ break;
+ case kResamplerMode2To3:
+ // 2:6
+ state1_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz));
+ WebRtcSpl_ResetResample16khzTo48khz(
+ static_cast<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(
+ static_cast<WebRtcSpl_State8khzTo22khz*>(state2_));
+ break;
+ case kResamplerMode4To11:
+ state1_ = malloc(sizeof(WebRtcSpl_State8khzTo22khz));
+ WebRtcSpl_ResetResample8khzTo22khz(
+ static_cast<WebRtcSpl_State8khzTo22khz*>(state1_));
+ break;
+ case kResamplerMode8To11:
+ state1_ = malloc(sizeof(WebRtcSpl_State16khzTo22khz));
+ WebRtcSpl_ResetResample16khzTo22khz(
+ static_cast<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(
+ static_cast<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(
+ static_cast<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(
+ static_cast<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(
+ static_cast<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(
+ static_cast<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(
+ static_cast<WebRtcSpl_State48khzTo16khz*>(state2_));
+ break;
+ case kResamplerMode11To2:
+ state1_ = malloc(sizeof(WebRtcSpl_State22khzTo8khz));
+ WebRtcSpl_ResetResample22khzTo8khz(
+ static_cast<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(
+ static_cast<WebRtcSpl_State22khzTo8khz*>(state1_));
+ break;
+ case kResamplerMode11To8:
+ state1_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz));
+ WebRtcSpl_ResetResample22khzTo16khz(
+ static_cast<WebRtcSpl_State22khzTo16khz*>(state1_));
+ break;
+ }
+
+ return 0;
+}
+
+int Resampler::ComputeResamplerMode(int in_freq_hz,
+ int out_freq_hz,
+ ResamplerMode* mode) {
+ // Start with a math exercise, Euclid's algorithm to find the gcd:
+ int a = in_freq_hz;
+ int b = out_freq_hz;
+ int c = a % b;
+ while (c != 0) {
+ a = b;
+ b = c;
+ c = a % b;
+ }
+ // b is now the gcd;
+
+ // Scale with GCD
+ const int reduced_in_freq = in_freq_hz / b;
+ const int reduced_out_freq = out_freq_hz / b;
+
+ if (reduced_in_freq == reduced_out_freq) {
+ *mode = kResamplerMode1To1;
+ } else if (reduced_in_freq == 1) {
+ switch (reduced_out_freq) {
+ case 2:
+ *mode = kResamplerMode1To2;
+ break;
+ case 3:
+ *mode = kResamplerMode1To3;
+ break;
+ case 4:
+ *mode = kResamplerMode1To4;
+ break;
+ case 6:
+ *mode = kResamplerMode1To6;
+ break;
+ case 12:
+ *mode = kResamplerMode1To12;
+ break;
+ default:
+ return -1;
+ }
+ } else if (reduced_out_freq == 1) {
+ switch (reduced_in_freq) {
+ case 2:
+ *mode = kResamplerMode2To1;
+ break;
+ case 3:
+ *mode = kResamplerMode3To1;
+ break;
+ case 4:
+ *mode = kResamplerMode4To1;
+ break;
+ case 6:
+ *mode = kResamplerMode6To1;
+ break;
+ case 12:
+ *mode = kResamplerMode12To1;
+ break;
+ default:
+ return -1;
+ }
+ } else if ((reduced_in_freq == 2) && (reduced_out_freq == 3)) {
+ *mode = kResamplerMode2To3;
+ } else if ((reduced_in_freq == 2) && (reduced_out_freq == 11)) {
+ *mode = kResamplerMode2To11;
+ } else if ((reduced_in_freq == 4) && (reduced_out_freq == 11)) {
+ *mode = kResamplerMode4To11;
+ } else if ((reduced_in_freq == 8) && (reduced_out_freq == 11)) {
+ *mode = kResamplerMode8To11;
+ } else if ((reduced_in_freq == 3) && (reduced_out_freq == 2)) {
+ *mode = kResamplerMode3To2;
+ } else if ((reduced_in_freq == 11) && (reduced_out_freq == 2)) {
+ *mode = kResamplerMode11To2;
+ } else if ((reduced_in_freq == 11) && (reduced_out_freq == 4)) {
+ *mode = kResamplerMode11To4;
+ } else if ((reduced_in_freq == 11) && (reduced_out_freq == 16)) {
+ *mode = kResamplerMode11To16;
+ } else if ((reduced_in_freq == 11) && (reduced_out_freq == 32)) {
+ *mode = kResamplerMode11To32;
+ } else if ((reduced_in_freq == 11) && (reduced_out_freq == 8)) {
+ *mode = kResamplerMode11To8;
+ } else {
+ return -1;
+ }
+ 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 helper object for each channel
+ int16_t* left =
+ static_cast<int16_t*>(malloc(lengthIn * sizeof(int16_t) / 2));
+ int16_t* right =
+ static_cast<int16_t*>(malloc(lengthIn * sizeof(int16_t) / 2));
+ int16_t* out_left =
+ static_cast<int16_t*>(malloc(maxLen / 2 * sizeof(int16_t)));
+ int16_t* out_right =
+ static_cast<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 |= helper_left_->Push(left, lengthIn, out_left, maxLen / 2,
+ actualOutLen_left);
+ res |= helper_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,
+ static_cast<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 = static_cast<int32_t*>(malloc(336 * sizeof(int32_t)));
+
+ for (size_t i = 0; i < lengthIn; i += 160) {
+ WebRtcSpl_Resample16khzTo48khz(
+ samplesIn + i, samplesOut + i * 3,
+ static_cast<WebRtcSpl_State16khzTo48khz*>(state1_), tmp_mem);
+ }
+ outLen = lengthIn * 3;
+ free(tmp_mem);
+ return 0;
+ case kResamplerMode1To4:
+ if (maxLen < (lengthIn * 4)) {
+ return -1;
+ }
+
+ tmp = static_cast<int16_t*>(malloc(sizeof(int16_t) * 2 * lengthIn));
+ // 1:2
+ WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp,
+ static_cast<int32_t*>(state1_));
+ // 2:4
+ WebRtcSpl_UpsampleBy2(tmp, lengthIn * 2, samplesOut,
+ static_cast<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 = static_cast<int32_t*>(malloc(336 * sizeof(int32_t)));
+ tmp = static_cast<int16_t*>(malloc(sizeof(int16_t) * 2 * lengthIn));
+
+ WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp,
+ static_cast<int32_t*>(state1_));
+ outLen = lengthIn * 2;
+
+ for (size_t i = 0; i < outLen; i += 160) {
+ WebRtcSpl_Resample16khzTo48khz(
+ tmp + i, samplesOut + i * 3,
+ static_cast<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 = static_cast<int32_t*>(malloc(336 * sizeof(int32_t)));
+ tmp = static_cast<int16_t*>(malloc(sizeof(int16_t) * 4 * lengthIn));
+ // 1:2
+ WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut,
+ static_cast<int32_t*>(state1_));
+ outLen = lengthIn * 2;
+ // 2:4
+ WebRtcSpl_UpsampleBy2(samplesOut, outLen, tmp,
+ static_cast<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,
+ static_cast<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 = static_cast<int32_t*>(malloc(336 * sizeof(int32_t)));
+ for (size_t i = 0; i < lengthIn; i += 160) {
+ WebRtcSpl_Resample16khzTo48khz(
+ samplesIn + i, tmp + i * 3,
+ static_cast<WebRtcSpl_State16khzTo48khz*>(state1_), tmp_mem);
+ }
+ lengthIn = lengthIn * 3;
+ // 6:3
+ WebRtcSpl_DownsampleBy2(tmp, lengthIn, samplesOut,
+ static_cast<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 = static_cast<int16_t*>(malloc(sizeof(int16_t) * 2 * lengthIn));
+ // 1:2
+ WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp,
+ static_cast<int32_t*>(state1_));
+ lengthIn *= 2;
+
+ tmp_mem = static_cast<int32_t*>(malloc(98 * sizeof(int32_t)));
+
+ for (size_t i = 0; i < lengthIn; i += 80) {
+ WebRtcSpl_Resample8khzTo22khz(
+ tmp + i, samplesOut + (i * 11) / 4,
+ static_cast<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 = static_cast<int32_t*>(malloc(98 * sizeof(int32_t)));
+
+ for (size_t i = 0; i < lengthIn; i += 80) {
+ WebRtcSpl_Resample8khzTo22khz(
+ samplesIn + i, samplesOut + (i * 11) / 4,
+ static_cast<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 = static_cast<int32_t*>(malloc(88 * sizeof(int32_t)));
+
+ for (size_t i = 0; i < lengthIn; i += 160) {
+ WebRtcSpl_Resample16khzTo22khz(
+ samplesIn + i, samplesOut + (i * 11) / 8,
+ static_cast<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 = static_cast<int32_t*>(malloc(104 * sizeof(int32_t)));
+ tmp = static_cast<int16_t*>(malloc((sizeof(int16_t) * lengthIn * 2)));
+
+ WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp,
+ static_cast<int32_t*>(state1_));
+
+ for (size_t i = 0; i < (lengthIn * 2); i += 220) {
+ WebRtcSpl_Resample22khzTo16khz(
+ tmp + i, samplesOut + (i / 220) * 160,
+ static_cast<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 = static_cast<int32_t*>(malloc(104 * sizeof(int32_t)));
+ tmp = static_cast<int16_t*>(malloc((sizeof(int16_t) * lengthIn * 2)));
+
+ // 11 -> 22 kHz in samplesOut
+ WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut,
+ static_cast<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,
+ static_cast<WebRtcSpl_State22khzTo16khz*>(state2_), tmp_mem);
+ }
+
+ // 16 -> 32 in samplesOut
+ WebRtcSpl_UpsampleBy2(tmp, (lengthIn * 16) / 11, samplesOut,
+ static_cast<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,
+ static_cast<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 = static_cast<int32_t*>(malloc(496 * sizeof(int32_t)));
+
+ for (size_t i = 0; i < lengthIn; i += 480) {
+ WebRtcSpl_Resample48khzTo16khz(
+ samplesIn + i, samplesOut + i / 3,
+ static_cast<WebRtcSpl_State48khzTo16khz*>(state1_), tmp_mem);
+ }
+ outLen = lengthIn / 3;
+ free(tmp_mem);
+ return 0;
+ case kResamplerMode4To1:
+ if (maxLen < (lengthIn / 4)) {
+ return -1;
+ }
+ tmp = static_cast<int16_t*>(malloc(sizeof(int16_t) * lengthIn / 2));
+ // 4:2
+ WebRtcSpl_DownsampleBy2(samplesIn, lengthIn, tmp,
+ static_cast<int32_t*>(state1_));
+ // 2:1
+ WebRtcSpl_DownsampleBy2(tmp, lengthIn / 2, samplesOut,
+ static_cast<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 = static_cast<int32_t*>(malloc(496 * sizeof(int32_t)));
+ tmp = static_cast<int16_t*>(malloc((sizeof(int16_t) * lengthIn) / 3));
+
+ for (size_t i = 0; i < lengthIn; i += 480) {
+ WebRtcSpl_Resample48khzTo16khz(
+ samplesIn + i, tmp + i / 3,
+ static_cast<WebRtcSpl_State48khzTo16khz*>(state1_), tmp_mem);
+ }
+ outLen = lengthIn / 3;
+ free(tmp_mem);
+ WebRtcSpl_DownsampleBy2(tmp, outLen, samplesOut,
+ static_cast<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 = static_cast<int32_t*>(malloc(496 * sizeof(int32_t)));
+ tmp = static_cast<int16_t*>(malloc((sizeof(int16_t) * lengthIn) / 3));
+ tmp_2 = static_cast<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,
+ static_cast<WebRtcSpl_State48khzTo16khz*>(state1_), tmp_mem);
+ }
+ outLen = lengthIn / 3;
+ free(tmp_mem);
+ // 4:2
+ WebRtcSpl_DownsampleBy2(tmp, outLen, tmp_2,
+ static_cast<int32_t*>(state2_));
+ outLen = outLen / 2;
+ free(tmp);
+ // 2:1
+ WebRtcSpl_DownsampleBy2(tmp_2, outLen, samplesOut,
+ static_cast<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,
+ static_cast<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 = static_cast<int32_t*>(malloc(496 * sizeof(int32_t)));
+ for (size_t i = 0; i < lengthIn; i += 480) {
+ WebRtcSpl_Resample48khzTo16khz(
+ tmp + i, samplesOut + i / 3,
+ static_cast<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 = static_cast<int32_t*>(malloc(126 * sizeof(int32_t)));
+ tmp =
+ static_cast<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,
+ static_cast<WebRtcSpl_State22khzTo8khz*>(state1_), tmp_mem);
+ }
+ lengthIn = (lengthIn * 4) / 11;
+
+ WebRtcSpl_DownsampleBy2(tmp, lengthIn, samplesOut,
+ static_cast<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 = static_cast<int32_t*>(malloc(126 * sizeof(int32_t)));
+
+ for (size_t i = 0; i < lengthIn; i += 220) {
+ WebRtcSpl_Resample22khzTo8khz(
+ samplesIn + i, samplesOut + (i * 4) / 11,
+ static_cast<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 = static_cast<int32_t*>(malloc(104 * sizeof(int32_t)));
+
+ for (size_t i = 0; i < lengthIn; i += 220) {
+ WebRtcSpl_Resample22khzTo16khz(
+ samplesIn + i, samplesOut + (i * 8) / 11,
+ static_cast<WebRtcSpl_State22khzTo16khz*>(state1_), tmp_mem);
+ }
+ outLen = (lengthIn * 8) / 11;
+ free(tmp_mem);
+ return 0;
+ }
+ return 0;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/common_audio/resampler/resampler_unittest.cc b/third_party/libwebrtc/common_audio/resampler/resampler_unittest.cc
new file mode 100644
index 0000000000..1b90d3e30b
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/resampler/resampler_unittest.cc
@@ -0,0 +1,168 @@
+/*
+ * 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 "common_audio/resampler/include/resampler.h"
+
+#include <array>
+
+#include "rtc_base/strings/string_builder.h"
+#include "test/gtest.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();
+ void SetUp() override;
+ void TearDown() override;
+
+ void ResetIfNeededAndPush(int in_rate, int out_rate, int num_channels);
+
+ 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() {}
+
+void ResamplerTest::ResetIfNeededAndPush(int in_rate,
+ int out_rate,
+ int num_channels) {
+ rtc::StringBuilder ss;
+ ss << "Input rate: " << in_rate << ", output rate: " << out_rate
+ << ", channel count: " << num_channels;
+ SCOPED_TRACE(ss.str());
+
+ if (ValidRates(in_rate, out_rate)) {
+ size_t in_length = static_cast<size_t>(in_rate / 100);
+ size_t out_length = 0;
+ EXPECT_EQ(0, rs_.ResetIfNeeded(in_rate, out_rate, num_channels));
+ EXPECT_EQ(0,
+ rs_.Push(data_in_, in_length, data_out_, kDataSize, out_length));
+ EXPECT_EQ(static_cast<size_t>(out_rate / 100), out_length);
+ } else {
+ EXPECT_EQ(-1, rs_.ResetIfNeeded(in_rate, out_rate, num_channels));
+ }
+}
+
+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) {
+ rtc::StringBuilder 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) {
+ rtc::StringBuilder 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) {
+ rtc::StringBuilder 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));
+ }
+ }
+ }
+}
+
+// Try multiple resets between a few supported and unsupported rates.
+TEST_F(ResamplerTest, MultipleResets) {
+ constexpr size_t kNumChanges = 5;
+ constexpr std::array<int, kNumChanges> kInRates = {
+ {8000, 44000, 44000, 32000, 32000}};
+ constexpr std::array<int, kNumChanges> kOutRates = {
+ {16000, 48000, 48000, 16000, 16000}};
+ constexpr std::array<int, kNumChanges> kNumChannels = {{2, 2, 2, 2, 1}};
+ for (size_t i = 0; i < kNumChanges; ++i) {
+ ResetIfNeededAndPush(kInRates[i], kOutRates[i], kNumChannels[i]);
+ }
+}
+
+} // namespace
+} // namespace webrtc
diff --git a/third_party/libwebrtc/common_audio/resampler/sinc_resampler.cc b/third_party/libwebrtc/common_audio/resampler/sinc_resampler.cc
new file mode 100644
index 0000000000..fac11aa362
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/resampler/sinc_resampler.cc
@@ -0,0 +1,366 @@
+/*
+ * 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 "common_audio/resampler/sinc_resampler.h"
+
+#include <math.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <limits>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/system/arch.h"
+#include "system_wrappers/include/cpu_features_wrapper.h" // kSSE2, WebRtc_G...
+
+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
+
+const size_t SincResampler::kKernelSize;
+
+// If we know the minimum architecture at compile time, avoid CPU detection.
+void SincResampler::InitializeCPUSpecificFeatures() {
+#if defined(WEBRTC_HAS_NEON)
+ convolve_proc_ = Convolve_NEON;
+#elif defined(WEBRTC_ARCH_X86_FAMILY)
+ // Using AVX2 instead of SSE2 when AVX2 supported.
+ if (GetCPUInfo(kAVX2))
+ convolve_proc_ = Convolve_AVX2;
+ else if (GetCPUInfo(kSSE2))
+ convolve_proc_ = Convolve_SSE;
+ else
+ convolve_proc_ = Convolve_C;
+#else
+ // Unknown architecture.
+ convolve_proc_ = Convolve_C;
+#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 32-byte alignment for SIMD optimizations.
+ kernel_storage_(static_cast<float*>(
+ AlignedMalloc(sizeof(float) * kKernelStorageSize, 32))),
+ kernel_pre_sinc_storage_(static_cast<float*>(
+ AlignedMalloc(sizeof(float) * kKernelStorageSize, 32))),
+ kernel_window_storage_(static_cast<float*>(
+ AlignedMalloc(sizeof(float) * kKernelStorageSize, 32))),
+ input_buffer_(static_cast<float*>(
+ AlignedMalloc(sizeof(float) * input_buffer_size_, 32))),
+ convolve_proc_(nullptr),
+ r1_(input_buffer_.get()),
+ r2_(input_buffer_.get() + kKernelSize / 2) {
+ InitializeCPUSpecificFeatures();
+ RTC_DCHECK(convolve_proc_);
+ RTC_DCHECK_GT(request_frames_, 0);
+ Flush();
+ RTC_DCHECK_GT(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.
+ RTC_DCHECK_EQ(r1_, input_buffer_.get());
+ // r1_ left of r2_, r4_ left of r3_ and size correct.
+ RTC_DCHECK_EQ(r2_ - r1_, r4_ - r3_);
+ // r2_ left of r3.
+ RTC_DCHECK_LT(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) {
+ RTC_DCHECK_LT(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 32-byte aligned for SIMD usage. Should always be
+ // true so long as kKernelSize is a multiple of 32.
+ RTC_DCHECK_EQ(0, reinterpret_cast<uintptr_t>(k1) % 32);
+ RTC_DCHECK_EQ(0, reinterpret_cast<uintptr_t>(k2) % 32);
+
+ // 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_proc_(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/third_party/libwebrtc/common_audio/resampler/sinc_resampler.h b/third_party/libwebrtc/common_audio/resampler/sinc_resampler.h
new file mode 100644
index 0000000000..b89bba7ab4
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/resampler/sinc_resampler.h
@@ -0,0 +1,181 @@
+/*
+ * 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 COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_
+#define COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "rtc_base/gtest_prod_util.h"
+#include "rtc_base/memory/aligned_malloc.h"
+#include "rtc_base/system/arch.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();
+
+ SincResampler(const SincResampler&) = delete;
+ SincResampler& operator=(const SincResampler&) = delete;
+
+ // 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);
+ static float Convolve_AVX2(const float* input_ptr,
+ const float* k1,
+ const float* k2,
+ double kernel_interpolation_factor);
+#elif 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.
+ std::unique_ptr<float[], AlignedFreeDeleter> kernel_storage_;
+ std::unique_ptr<float[], AlignedFreeDeleter> kernel_pre_sinc_storage_;
+ std::unique_ptr<float[], AlignedFreeDeleter> kernel_window_storage_;
+
+ // Data from the source is copied into this buffer for each processing pass.
+ std::unique_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.
+ typedef float (*ConvolveProc)(const float*,
+ const float*,
+ const float*,
+ double);
+ ConvolveProc convolve_proc_;
+
+ // 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_;
+};
+
+} // namespace webrtc
+
+#endif // COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_
diff --git a/third_party/libwebrtc/common_audio/resampler/sinc_resampler_avx2.cc b/third_party/libwebrtc/common_audio/resampler/sinc_resampler_avx2.cc
new file mode 100644
index 0000000000..d945a10be2
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/resampler/sinc_resampler_avx2.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020 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 <immintrin.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <xmmintrin.h>
+
+#include "common_audio/resampler/sinc_resampler.h"
+
+namespace webrtc {
+
+float SincResampler::Convolve_AVX2(const float* input_ptr,
+ const float* k1,
+ const float* k2,
+ double kernel_interpolation_factor) {
+ __m256 m_input;
+ __m256 m_sums1 = _mm256_setzero_ps();
+ __m256 m_sums2 = _mm256_setzero_ps();
+
+ // Based on `input_ptr` alignment, we need to use loadu or load. Unrolling
+ // these loops has not been tested or benchmarked.
+ bool aligned_input = (reinterpret_cast<uintptr_t>(input_ptr) & 0x1F) == 0;
+ if (!aligned_input) {
+ for (size_t i = 0; i < kKernelSize; i += 8) {
+ m_input = _mm256_loadu_ps(input_ptr + i);
+ m_sums1 = _mm256_fmadd_ps(m_input, _mm256_load_ps(k1 + i), m_sums1);
+ m_sums2 = _mm256_fmadd_ps(m_input, _mm256_load_ps(k2 + i), m_sums2);
+ }
+ } else {
+ for (size_t i = 0; i < kKernelSize; i += 8) {
+ m_input = _mm256_load_ps(input_ptr + i);
+ m_sums1 = _mm256_fmadd_ps(m_input, _mm256_load_ps(k1 + i), m_sums1);
+ m_sums2 = _mm256_fmadd_ps(m_input, _mm256_load_ps(k2 + i), m_sums2);
+ }
+ }
+
+ // Linearly interpolate the two "convolutions".
+ __m128 m128_sums1 = _mm_add_ps(_mm256_extractf128_ps(m_sums1, 0),
+ _mm256_extractf128_ps(m_sums1, 1));
+ __m128 m128_sums2 = _mm_add_ps(_mm256_extractf128_ps(m_sums2, 0),
+ _mm256_extractf128_ps(m_sums2, 1));
+ m128_sums1 = _mm_mul_ps(
+ m128_sums1,
+ _mm_set_ps1(static_cast<float>(1.0 - kernel_interpolation_factor)));
+ m128_sums2 = _mm_mul_ps(
+ m128_sums2, _mm_set_ps1(static_cast<float>(kernel_interpolation_factor)));
+ m128_sums1 = _mm_add_ps(m128_sums1, m128_sums2);
+
+ // Sum components together.
+ float result;
+ m128_sums2 = _mm_add_ps(_mm_movehl_ps(m128_sums1, m128_sums1), m128_sums1);
+ _mm_store_ss(&result, _mm_add_ss(m128_sums2,
+ _mm_shuffle_ps(m128_sums2, m128_sums2, 1)));
+
+ return result;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/common_audio/resampler/sinc_resampler_neon.cc b/third_party/libwebrtc/common_audio/resampler/sinc_resampler_neon.cc
new file mode 100644
index 0000000000..9ee918bca3
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/resampler/sinc_resampler_neon.cc
@@ -0,0 +1,48 @@
+/*
+ * 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 <arm_neon.h>
+
+#include "common_audio/resampler/sinc_resampler.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/third_party/libwebrtc/common_audio/resampler/sinc_resampler_sse.cc b/third_party/libwebrtc/common_audio/resampler/sinc_resampler_sse.cc
new file mode 100644
index 0000000000..30a8d1b2d9
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/resampler/sinc_resampler_sse.cc
@@ -0,0 +1,63 @@
+/*
+ * 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 <stddef.h>
+#include <stdint.h>
+#include <xmmintrin.h>
+
+#include "common_audio/resampler/sinc_resampler.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/third_party/libwebrtc/common_audio/resampler/sinc_resampler_unittest.cc b/third_party/libwebrtc/common_audio/resampler/sinc_resampler_unittest.cc
new file mode 100644
index 0000000000..b267c89c8b
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/resampler/sinc_resampler_unittest.cc
@@ -0,0 +1,393 @@
+/*
+ * 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 "common_audio/resampler/sinc_resampler.h"
+
+#include <math.h>
+
+#include <algorithm>
+#include <memory>
+#include <tuple>
+
+#include "common_audio/resampler/sinusoidal_linear_chirp_source.h"
+#include "rtc_base/system/arch.h"
+#include "rtc_base/time_utils.h"
+#include "system_wrappers/include/cpu_features_wrapper.h"
+#include "test/gmock.h"
+#include "test/gtest.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_METHOD(void, Run, (size_t frames, float* destination), (override));
+};
+
+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;
+ std::unique_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);
+ std::unique_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);
+
+ int64_t start = rtc::TimeNanos();
+ for (int i = 1; i < 10000; ++i)
+ resampler.SetRatio(1.0 / i);
+ double total_time_c_us =
+ (rtc::TimeNanos() - start) / rtc::kNumNanosecsPerMicrosec;
+ printf("SetRatio() took %.2fms.\n", total_time_c_us / 1000);
+}
+
+// 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.
+TEST(SincResamplerTest, Convolve) {
+#if defined(WEBRTC_ARCH_X86_FAMILY)
+ ASSERT_TRUE(GetCPUInfo(kSSE2));
+#elif defined(WEBRTC_ARCH_ARM_V7)
+ ASSERT_TRUE(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_proc_(
+ 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_proc_(
+ resampler.kernel_storage_.get() + 1, resampler.kernel_storage_.get(),
+ resampler.kernel_storage_.get(), kKernelInterpolationFactor);
+ EXPECT_NEAR(result2, result, kEpsilon);
+}
+
+// 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().
+ int64_t start = rtc::TimeNanos();
+ 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 =
+ (rtc::TimeNanos() - start) / rtc::kNumNanosecsPerMicrosec;
+ printf("Convolve_C took %.2fms.\n", total_time_c_us / 1000);
+
+#if defined(WEBRTC_ARCH_X86_FAMILY)
+ ASSERT_TRUE(GetCPUInfo(kSSE2));
+#elif defined(WEBRTC_ARCH_ARM_V7)
+ ASSERT_TRUE(GetCPUFeaturesARM() & kCPUFeatureNEON);
+#endif
+
+ // Benchmark with unaligned input pointer.
+ start = rtc::TimeNanos();
+ for (int j = 0; j < kConvolveIterations; ++j) {
+ resampler.convolve_proc_(
+ resampler.kernel_storage_.get() + 1, resampler.kernel_storage_.get(),
+ resampler.kernel_storage_.get(), kKernelInterpolationFactor);
+ }
+ double total_time_optimized_unaligned_us =
+ (rtc::TimeNanos() - start) / rtc::kNumNanosecsPerMicrosec;
+ printf(
+ "convolve_proc_(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 = rtc::TimeNanos();
+ for (int j = 0; j < kConvolveIterations; ++j) {
+ resampler.convolve_proc_(
+ resampler.kernel_storage_.get(), resampler.kernel_storage_.get(),
+ resampler.kernel_storage_.get(), kKernelInterpolationFactor);
+ }
+ double total_time_optimized_aligned_us =
+ (rtc::TimeNanos() - start) / rtc::kNumNanosecsPerMicrosec;
+ printf(
+ "convolve_proc_ (aligned) took %.2fms; which is %.2fx "
+ "faster than Convolve_C and %.2fx faster than "
+ "convolve_proc_ (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);
+}
+
+typedef std::tuple<int, int, double, double> SincResamplerTestData;
+class SincResamplerTest
+ : public ::testing::TestWithParam<SincResamplerTestData> {
+ public:
+ SincResamplerTest()
+ : input_rate_(std::get<0>(GetParam())),
+ output_rate_(std::get<1>(GetParam())),
+ rms_error_(std::get<2>(GetParam())),
+ low_freq_error_(std::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 dynamic sample rate
+ // changes are working correctly.
+ std::unique_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.
+ std::unique_ptr<float[]> resampled_destination(new float[output_samples]);
+ std::unique_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_SUITE_P(
+ SincResamplerTest,
+ SincResamplerTest,
+ ::testing::Values(
+ // To 22.05kHz
+ std::make_tuple(8000, 22050, kResamplingRMSError, -62.73),
+ std::make_tuple(11025, 22050, kResamplingRMSError, -72.19),
+ std::make_tuple(16000, 22050, kResamplingRMSError, -62.54),
+ std::make_tuple(22050, 22050, kResamplingRMSError, -73.53),
+ std::make_tuple(32000, 22050, kResamplingRMSError, -46.45),
+ std::make_tuple(44100, 22050, kResamplingRMSError, -28.49),
+ std::make_tuple(48000, 22050, -15.01, -25.56),
+ std::make_tuple(96000, 22050, -18.49, -13.42),
+ std::make_tuple(192000, 22050, -20.50, -9.23),
+
+ // To 44.1kHz
+ std::make_tuple(8000, 44100, kResamplingRMSError, -62.73),
+ std::make_tuple(11025, 44100, kResamplingRMSError, -72.19),
+ std::make_tuple(16000, 44100, kResamplingRMSError, -62.54),
+ std::make_tuple(22050, 44100, kResamplingRMSError, -73.53),
+ std::make_tuple(32000, 44100, kResamplingRMSError, -63.32),
+ std::make_tuple(44100, 44100, kResamplingRMSError, -73.52),
+ std::make_tuple(48000, 44100, -15.01, -64.04),
+ std::make_tuple(96000, 44100, -18.49, -25.51),
+ std::make_tuple(192000, 44100, -20.50, -13.31),
+
+ // To 48kHz
+ std::make_tuple(8000, 48000, kResamplingRMSError, -63.43),
+ std::make_tuple(11025, 48000, kResamplingRMSError, -62.61),
+ std::make_tuple(16000, 48000, kResamplingRMSError, -63.95),
+ std::make_tuple(22050, 48000, kResamplingRMSError, -62.42),
+ std::make_tuple(32000, 48000, kResamplingRMSError, -64.04),
+ std::make_tuple(44100, 48000, kResamplingRMSError, -62.63),
+ std::make_tuple(48000, 48000, kResamplingRMSError, -73.52),
+ std::make_tuple(96000, 48000, -18.40, -28.44),
+ std::make_tuple(192000, 48000, -20.43, -14.11),
+
+ // To 96kHz
+ std::make_tuple(8000, 96000, kResamplingRMSError, -63.19),
+ std::make_tuple(11025, 96000, kResamplingRMSError, -62.61),
+ std::make_tuple(16000, 96000, kResamplingRMSError, -63.39),
+ std::make_tuple(22050, 96000, kResamplingRMSError, -62.42),
+ std::make_tuple(32000, 96000, kResamplingRMSError, -63.95),
+ std::make_tuple(44100, 96000, kResamplingRMSError, -62.63),
+ std::make_tuple(48000, 96000, kResamplingRMSError, -73.52),
+ std::make_tuple(96000, 96000, kResamplingRMSError, -73.52),
+ std::make_tuple(192000, 96000, kResamplingRMSError, -28.41),
+
+ // To 192kHz
+ std::make_tuple(8000, 192000, kResamplingRMSError, -63.10),
+ std::make_tuple(11025, 192000, kResamplingRMSError, -62.61),
+ std::make_tuple(16000, 192000, kResamplingRMSError, -63.14),
+ std::make_tuple(22050, 192000, kResamplingRMSError, -62.42),
+ std::make_tuple(32000, 192000, kResamplingRMSError, -63.38),
+ std::make_tuple(44100, 192000, kResamplingRMSError, -62.63),
+ std::make_tuple(48000, 192000, kResamplingRMSError, -73.44),
+ std::make_tuple(96000, 192000, kResamplingRMSError, -73.52),
+ std::make_tuple(192000, 192000, kResamplingRMSError, -73.52)));
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/common_audio/resampler/sinusoidal_linear_chirp_source.cc b/third_party/libwebrtc/common_audio/resampler/sinusoidal_linear_chirp_source.cc
new file mode 100644
index 0000000000..2afdd1be47
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/resampler/sinusoidal_linear_chirp_source.cc
@@ -0,0 +1,57 @@
+/*
+ * 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 "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/third_party/libwebrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h b/third_party/libwebrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h
new file mode 100644
index 0000000000..ccd11bbd61
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h
@@ -0,0 +1,56 @@
+/*
+ * 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 COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_
+#define COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_
+
+#include "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);
+
+ ~SinusoidalLinearChirpSource() override {}
+
+ SinusoidalLinearChirpSource(const SinusoidalLinearChirpSource&) = delete;
+ SinusoidalLinearChirpSource& operator=(const SinusoidalLinearChirpSource&) =
+ delete;
+
+ void Run(size_t frames, float* destination) override;
+
+ double Frequency(size_t position);
+
+ private:
+ static constexpr int kMinFrequency = 5;
+
+ int sample_rate_;
+ size_t total_samples_;
+ double max_frequency_;
+ double k_;
+ size_t current_index_;
+ double delay_samples_;
+};
+
+} // namespace webrtc
+
+#endif // COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_