summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/audio_processing/utility
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/libwebrtc/modules/audio_processing/utility
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/modules/audio_processing/utility')
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/BUILD.gn79
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/DEPS3
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter.cc126
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter.h80
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter_gn/moz.build221
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter_unittest.cc157
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/delay_estimator.cc708
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/delay_estimator.h257
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/delay_estimator_internal.h51
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/delay_estimator_unittest.cc621
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/delay_estimator_wrapper.cc489
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/delay_estimator_wrapper.h248
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/legacy_delay_estimator_gn/moz.build222
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper.cc135
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper.h94
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper_gn/moz.build221
-rw-r--r--third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper_unittest.cc182
17 files changed, 3894 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/BUILD.gn b/third_party/libwebrtc/modules/audio_processing/utility/BUILD.gn
new file mode 100644
index 0000000000..4851e77b03
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/utility/BUILD.gn
@@ -0,0 +1,79 @@
+# Copyright (c) 2014 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.
+
+import("../../../webrtc.gni")
+
+rtc_library("cascaded_biquad_filter") {
+ sources = [
+ "cascaded_biquad_filter.cc",
+ "cascaded_biquad_filter.h",
+ ]
+ deps = [
+ "../../../api:array_view",
+ "../../../rtc_base:checks",
+ ]
+}
+
+rtc_library("legacy_delay_estimator") {
+ sources = [
+ "delay_estimator.cc",
+ "delay_estimator.h",
+ "delay_estimator_internal.h",
+ "delay_estimator_wrapper.cc",
+ "delay_estimator_wrapper.h",
+ ]
+ deps = [ "../../../rtc_base:checks" ]
+}
+
+rtc_library("pffft_wrapper") {
+ visibility = [ "../*" ]
+ sources = [
+ "pffft_wrapper.cc",
+ "pffft_wrapper.h",
+ ]
+ deps = [
+ "../../../api:array_view",
+ "../../../rtc_base:checks",
+ "//third_party/pffft",
+ ]
+}
+
+if (rtc_include_tests) {
+ rtc_library("cascaded_biquad_filter_unittest") {
+ testonly = true
+
+ sources = [ "cascaded_biquad_filter_unittest.cc" ]
+ deps = [
+ ":cascaded_biquad_filter",
+ "../../../test:test_support",
+ "//testing/gtest",
+ ]
+ }
+
+ rtc_library("legacy_delay_estimator_unittest") {
+ testonly = true
+
+ sources = [ "delay_estimator_unittest.cc" ]
+ deps = [
+ ":legacy_delay_estimator",
+ "../../../test:test_support",
+ "//testing/gtest",
+ ]
+ }
+
+ rtc_library("pffft_wrapper_unittest") {
+ testonly = true
+ sources = [ "pffft_wrapper_unittest.cc" ]
+ deps = [
+ ":pffft_wrapper",
+ "../../../test:test_support",
+ "//testing/gtest",
+ "//third_party/pffft",
+ ]
+ }
+}
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/DEPS b/third_party/libwebrtc/modules/audio_processing/utility/DEPS
new file mode 100644
index 0000000000..c72d810b24
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/utility/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+third_party/pffft",
+]
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter.cc b/third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter.cc
new file mode 100644
index 0000000000..0d236ce0be
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter.cc
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2016 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 "modules/audio_processing/utility/cascaded_biquad_filter.h"
+
+#include <algorithm>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+CascadedBiQuadFilter::BiQuadParam::BiQuadParam(std::complex<float> zero,
+ std::complex<float> pole,
+ float gain,
+ bool mirror_zero_along_i_axis)
+ : zero(zero),
+ pole(pole),
+ gain(gain),
+ mirror_zero_along_i_axis(mirror_zero_along_i_axis) {}
+
+CascadedBiQuadFilter::BiQuadParam::BiQuadParam(const BiQuadParam&) = default;
+
+CascadedBiQuadFilter::BiQuad::BiQuad(
+ const CascadedBiQuadFilter::BiQuadParam& param)
+ : x(), y() {
+ float z_r = std::real(param.zero);
+ float z_i = std::imag(param.zero);
+ float p_r = std::real(param.pole);
+ float p_i = std::imag(param.pole);
+ float gain = param.gain;
+
+ if (param.mirror_zero_along_i_axis) {
+ // Assuming zeroes at z_r and -z_r.
+ RTC_DCHECK(z_i == 0.f);
+ coefficients.b[0] = gain * 1.f;
+ coefficients.b[1] = 0.f;
+ coefficients.b[2] = gain * -(z_r * z_r);
+ } else {
+ // Assuming zeros at (z_r + z_i*i) and (z_r - z_i*i).
+ coefficients.b[0] = gain * 1.f;
+ coefficients.b[1] = gain * -2.f * z_r;
+ coefficients.b[2] = gain * (z_r * z_r + z_i * z_i);
+ }
+
+ // Assuming poles at (p_r + p_i*i) and (p_r - p_i*i).
+ coefficients.a[0] = -2.f * p_r;
+ coefficients.a[1] = p_r * p_r + p_i * p_i;
+}
+
+void CascadedBiQuadFilter::BiQuad::BiQuad::Reset() {
+ x[0] = x[1] = y[0] = y[1] = 0.f;
+}
+
+CascadedBiQuadFilter::CascadedBiQuadFilter(
+ const CascadedBiQuadFilter::BiQuadCoefficients& coefficients,
+ size_t num_biquads)
+ : biquads_(num_biquads, BiQuad(coefficients)) {}
+
+CascadedBiQuadFilter::CascadedBiQuadFilter(
+ const std::vector<CascadedBiQuadFilter::BiQuadParam>& biquad_params) {
+ for (const auto& param : biquad_params) {
+ biquads_.push_back(BiQuad(param));
+ }
+}
+
+CascadedBiQuadFilter::~CascadedBiQuadFilter() = default;
+
+void CascadedBiQuadFilter::Process(rtc::ArrayView<const float> x,
+ rtc::ArrayView<float> y) {
+ if (biquads_.size() > 0) {
+ ApplyBiQuad(x, y, &biquads_[0]);
+ for (size_t k = 1; k < biquads_.size(); ++k) {
+ ApplyBiQuad(y, y, &biquads_[k]);
+ }
+ } else {
+ std::copy(x.begin(), x.end(), y.begin());
+ }
+}
+
+void CascadedBiQuadFilter::Process(rtc::ArrayView<float> y) {
+ for (auto& biquad : biquads_) {
+ ApplyBiQuad(y, y, &biquad);
+ }
+}
+
+void CascadedBiQuadFilter::Reset() {
+ for (auto& biquad : biquads_) {
+ biquad.Reset();
+ }
+}
+
+void CascadedBiQuadFilter::ApplyBiQuad(rtc::ArrayView<const float> x,
+ rtc::ArrayView<float> y,
+ CascadedBiQuadFilter::BiQuad* biquad) {
+ RTC_DCHECK_EQ(x.size(), y.size());
+ const float c_a_0 = biquad->coefficients.a[0];
+ const float c_a_1 = biquad->coefficients.a[1];
+ const float c_b_0 = biquad->coefficients.b[0];
+ const float c_b_1 = biquad->coefficients.b[1];
+ const float c_b_2 = biquad->coefficients.b[2];
+ float m_x_0 = biquad->x[0];
+ float m_x_1 = biquad->x[1];
+ float m_y_0 = biquad->y[0];
+ float m_y_1 = biquad->y[1];
+ for (size_t k = 0; k < x.size(); ++k) {
+ const float tmp = x[k];
+ y[k] = c_b_0 * tmp + c_b_1 * m_x_0 + c_b_2 * m_x_1 - c_a_0 * m_y_0 -
+ c_a_1 * m_y_1;
+ m_x_1 = m_x_0;
+ m_x_0 = tmp;
+ m_y_1 = m_y_0;
+ m_y_0 = y[k];
+ }
+ biquad->x[0] = m_x_0;
+ biquad->x[1] = m_x_1;
+ biquad->y[0] = m_y_0;
+ biquad->y[1] = m_y_1;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter.h b/third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter.h
new file mode 100644
index 0000000000..120b52aa57
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2016 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 MODULES_AUDIO_PROCESSING_UTILITY_CASCADED_BIQUAD_FILTER_H_
+#define MODULES_AUDIO_PROCESSING_UTILITY_CASCADED_BIQUAD_FILTER_H_
+
+#include <stddef.h>
+
+#include <complex>
+#include <vector>
+
+#include "api/array_view.h"
+
+namespace webrtc {
+
+// Applies a number of biquads in a cascaded manner. The filter implementation
+// is direct form 1.
+class CascadedBiQuadFilter {
+ public:
+ struct BiQuadParam {
+ BiQuadParam(std::complex<float> zero,
+ std::complex<float> pole,
+ float gain,
+ bool mirror_zero_along_i_axis = false);
+ explicit BiQuadParam(const BiQuadParam&);
+ std::complex<float> zero;
+ std::complex<float> pole;
+ float gain;
+ bool mirror_zero_along_i_axis;
+ };
+
+ struct BiQuadCoefficients {
+ float b[3];
+ float a[2];
+ };
+
+ struct BiQuad {
+ explicit BiQuad(const BiQuadCoefficients& coefficients)
+ : coefficients(coefficients), x(), y() {}
+ explicit BiQuad(const CascadedBiQuadFilter::BiQuadParam& param);
+ void Reset();
+ BiQuadCoefficients coefficients;
+ float x[2];
+ float y[2];
+ };
+
+ CascadedBiQuadFilter(
+ const CascadedBiQuadFilter::BiQuadCoefficients& coefficients,
+ size_t num_biquads);
+ explicit CascadedBiQuadFilter(
+ const std::vector<CascadedBiQuadFilter::BiQuadParam>& biquad_params);
+ ~CascadedBiQuadFilter();
+ CascadedBiQuadFilter(const CascadedBiQuadFilter&) = delete;
+ CascadedBiQuadFilter& operator=(const CascadedBiQuadFilter&) = delete;
+
+ // Applies the biquads on the values in x in order to form the output in y.
+ void Process(rtc::ArrayView<const float> x, rtc::ArrayView<float> y);
+ // Applies the biquads on the values in y in an in-place manner.
+ void Process(rtc::ArrayView<float> y);
+ // Resets the filter to its initial state.
+ void Reset();
+
+ private:
+ void ApplyBiQuad(rtc::ArrayView<const float> x,
+ rtc::ArrayView<float> y,
+ CascadedBiQuadFilter::BiQuad* biquad);
+
+ std::vector<BiQuad> biquads_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_AUDIO_PROCESSING_UTILITY_CASCADED_BIQUAD_FILTER_H_
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter_gn/moz.build
new file mode 100644
index 0000000000..8cf3aaefeb
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter_gn/moz.build
@@ -0,0 +1,221 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "!/third_party/libwebrtc/gen",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["ANDROID"] = True
+ DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1"
+ DEFINES["HAVE_SYS_UIO_H"] = True
+ DEFINES["WEBRTC_ANDROID"] = True
+ DEFINES["WEBRTC_ANDROID_OPENSLES"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_GNU_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "log"
+ ]
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+if CONFIG["CPU_ARCH"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "arm":
+
+ CXXFLAGS += [
+ "-mfpu=neon"
+ ]
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "mips32":
+
+ DEFINES["MIPS32_LE"] = True
+ DEFINES["MIPS_FPU_LE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "mips64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Android":
+
+ OS_LIBS += [
+ "android_support",
+ "unwind"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ OS_LIBS += [
+ "android_support"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+Library("cascaded_biquad_filter_gn")
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter_unittest.cc b/third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter_unittest.cc
new file mode 100644
index 0000000000..ff7022dba4
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/utility/cascaded_biquad_filter_unittest.cc
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2016 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 "modules/audio_processing/utility/cascaded_biquad_filter.h"
+
+#include <vector>
+
+#include "test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+
+// Coefficients for a second order Butterworth high-pass filter with cutoff
+// frequency 100 Hz.
+const CascadedBiQuadFilter::BiQuadCoefficients kHighPassFilterCoefficients = {
+ {0.97261f, -1.94523f, 0.97261f},
+ {-1.94448f, 0.94598f}};
+
+const CascadedBiQuadFilter::BiQuadCoefficients kTransparentCoefficients = {
+ {1.f, 0.f, 0.f},
+ {0.f, 0.f}};
+
+const CascadedBiQuadFilter::BiQuadCoefficients kBlockingCoefficients = {
+ {0.f, 0.f, 0.f},
+ {0.f, 0.f}};
+
+std::vector<float> CreateInputWithIncreasingValues(size_t vector_length) {
+ std::vector<float> v(vector_length);
+ for (size_t k = 0; k < v.size(); ++k) {
+ v[k] = k;
+ }
+ return v;
+}
+
+} // namespace
+
+// Verifies that the filter applies an effect which removes the input signal.
+// The test also verifies that the in-place Process API call works as intended.
+TEST(CascadedBiquadFilter, BlockingConfiguration) {
+ std::vector<float> values = CreateInputWithIncreasingValues(1000);
+
+ CascadedBiQuadFilter filter(kBlockingCoefficients, 1);
+ filter.Process(values);
+
+ EXPECT_EQ(std::vector<float>(1000, 0.f), values);
+}
+
+// Verifies that the filter is able to form a zero-mean output from a
+// non-zeromean input signal when coefficients for a high-pass filter are
+// applied. The test also verifies that the filter works with multiple biquads.
+TEST(CascadedBiquadFilter, HighPassConfiguration) {
+ std::vector<float> values(1000);
+ for (size_t k = 0; k < values.size(); ++k) {
+ values[k] = 1.f;
+ }
+
+ CascadedBiQuadFilter filter(kHighPassFilterCoefficients, 2);
+ filter.Process(values);
+
+ for (size_t k = values.size() / 2; k < values.size(); ++k) {
+ EXPECT_NEAR(0.f, values[k], 1e-4);
+ }
+}
+
+// Verifies that the reset functionality works as intended.
+TEST(CascadedBiquadFilter, HighPassConfigurationResetFunctionality) {
+ CascadedBiQuadFilter filter(kHighPassFilterCoefficients, 2);
+
+ std::vector<float> values1(100, 1.f);
+ filter.Process(values1);
+
+ filter.Reset();
+
+ std::vector<float> values2(100, 1.f);
+ filter.Process(values2);
+
+ for (size_t k = 0; k < values1.size(); ++k) {
+ EXPECT_EQ(values1[k], values2[k]);
+ }
+}
+
+// Verifies that the filter is able to produce a transparent effect with no
+// impact on the data when the proper coefficients are applied. The test also
+// verifies that the non-in-place Process API call works as intended.
+TEST(CascadedBiquadFilter, TransparentConfiguration) {
+ const std::vector<float> input = CreateInputWithIncreasingValues(1000);
+ std::vector<float> output(input.size());
+
+ CascadedBiQuadFilter filter(kTransparentCoefficients, 1);
+ filter.Process(input, output);
+
+ EXPECT_EQ(input, output);
+}
+
+#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+// Verifies that the check of the lengths for the input and output works for the
+// non-in-place call.
+TEST(CascadedBiquadFilterDeathTest, InputSizeCheckVerification) {
+ const std::vector<float> input = CreateInputWithIncreasingValues(10);
+ std::vector<float> output(input.size() - 1);
+
+ CascadedBiQuadFilter filter(kTransparentCoefficients, 1);
+ EXPECT_DEATH(filter.Process(input, output), "");
+}
+#endif
+
+// Verifies the conversion from zero, pole, gain to filter coefficients for
+// lowpass filter.
+TEST(CascadedBiquadFilter, BiQuadParamLowPass) {
+ CascadedBiQuadFilter::BiQuadParam param(
+ {-1.0f, 0.0f}, {0.23146901f, 0.39514232f}, 0.1866943331163784f);
+ CascadedBiQuadFilter::BiQuad filter(param);
+ const float epsilon = 1e-6f;
+ EXPECT_NEAR(filter.coefficients.b[0], 0.18669433f, epsilon);
+ EXPECT_NEAR(filter.coefficients.b[1], 0.37338867f, epsilon);
+ EXPECT_NEAR(filter.coefficients.b[2], 0.18669433f, epsilon);
+ EXPECT_NEAR(filter.coefficients.a[0], -0.46293803f, epsilon);
+ EXPECT_NEAR(filter.coefficients.a[1], 0.20971536f, epsilon);
+}
+
+// Verifies the conversion from zero, pole, gain to filter coefficients for
+// highpass filter.
+TEST(CascadedBiquadFilter, BiQuadParamHighPass) {
+ CascadedBiQuadFilter::BiQuadParam param(
+ {1.0f, 0.0f}, {0.72712179f, 0.21296904f}, 0.75707637533388494f);
+ CascadedBiQuadFilter::BiQuad filter(param);
+ const float epsilon = 1e-6f;
+ EXPECT_NEAR(filter.coefficients.b[0], 0.75707638f, epsilon);
+ EXPECT_NEAR(filter.coefficients.b[1], -1.51415275f, epsilon);
+ EXPECT_NEAR(filter.coefficients.b[2], 0.75707638f, epsilon);
+ EXPECT_NEAR(filter.coefficients.a[0], -1.45424359f, epsilon);
+ EXPECT_NEAR(filter.coefficients.a[1], 0.57406192f, epsilon);
+}
+
+// Verifies the conversion from zero, pole, gain to filter coefficients for
+// bandpass filter.
+TEST(CascadedBiquadFilter, BiQuadParamBandPass) {
+ CascadedBiQuadFilter::BiQuadParam param(
+ {1.0f, 0.0f}, {1.11022302e-16f, 0.71381051f}, 0.2452372752527856f, true);
+ CascadedBiQuadFilter::BiQuad filter(param);
+ const float epsilon = 1e-6f;
+ EXPECT_NEAR(filter.coefficients.b[0], 0.24523728f, epsilon);
+ EXPECT_NEAR(filter.coefficients.b[1], 0.f, epsilon);
+ EXPECT_NEAR(filter.coefficients.b[2], -0.24523728f, epsilon);
+ EXPECT_NEAR(filter.coefficients.a[0], -2.22044605e-16f, epsilon);
+ EXPECT_NEAR(filter.coefficients.a[1], 5.09525449e-01f, epsilon);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator.cc b/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator.cc
new file mode 100644
index 0000000000..6868392f6f
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator.cc
@@ -0,0 +1,708 @@
+/*
+ * Copyright (c) 2012 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 "modules/audio_processing/utility/delay_estimator.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+namespace {
+
+// Number of right shifts for scaling is linearly depending on number of bits in
+// the far-end binary spectrum.
+static const int kShiftsAtZero = 13; // Right shifts at zero binary spectrum.
+static const int kShiftsLinearSlope = 3;
+
+static const int32_t kProbabilityOffset = 1024; // 2 in Q9.
+static const int32_t kProbabilityLowerLimit = 8704; // 17 in Q9.
+static const int32_t kProbabilityMinSpread = 2816; // 5.5 in Q9.
+
+// Robust validation settings
+static const float kHistogramMax = 3000.f;
+static const float kLastHistogramMax = 250.f;
+static const float kMinHistogramThreshold = 1.5f;
+static const int kMinRequiredHits = 10;
+static const int kMaxHitsWhenPossiblyNonCausal = 10;
+static const int kMaxHitsWhenPossiblyCausal = 1000;
+static const float kQ14Scaling = 1.f / (1 << 14); // Scaling by 2^14 to get Q0.
+static const float kFractionSlope = 0.05f;
+static const float kMinFractionWhenPossiblyCausal = 0.5f;
+static const float kMinFractionWhenPossiblyNonCausal = 0.25f;
+
+} // namespace
+
+// Counts and returns number of bits of a 32-bit word.
+static int BitCount(uint32_t u32) {
+ uint32_t tmp =
+ u32 - ((u32 >> 1) & 033333333333) - ((u32 >> 2) & 011111111111);
+ tmp = ((tmp + (tmp >> 3)) & 030707070707);
+ tmp = (tmp + (tmp >> 6));
+ tmp = (tmp + (tmp >> 12) + (tmp >> 24)) & 077;
+
+ return ((int)tmp);
+}
+
+// Compares the `binary_vector` with all rows of the `binary_matrix` and counts
+// per row the number of times they have the same value.
+//
+// Inputs:
+// - binary_vector : binary "vector" stored in a long
+// - binary_matrix : binary "matrix" stored as a vector of long
+// - matrix_size : size of binary "matrix"
+//
+// Output:
+// - bit_counts : "Vector" stored as a long, containing for each
+// row the number of times the matrix row and the
+// input vector have the same value
+//
+static void BitCountComparison(uint32_t binary_vector,
+ const uint32_t* binary_matrix,
+ int matrix_size,
+ int32_t* bit_counts) {
+ int n = 0;
+
+ // Compare `binary_vector` with all rows of the `binary_matrix`
+ for (; n < matrix_size; n++) {
+ bit_counts[n] = (int32_t)BitCount(binary_vector ^ binary_matrix[n]);
+ }
+}
+
+// Collects necessary statistics for the HistogramBasedValidation(). This
+// function has to be called prior to calling HistogramBasedValidation(). The
+// statistics updated and used by the HistogramBasedValidation() are:
+// 1. the number of `candidate_hits`, which states for how long we have had the
+// same `candidate_delay`
+// 2. the `histogram` of candidate delays over time. This histogram is
+// weighted with respect to a reliability measure and time-varying to cope
+// with possible delay shifts.
+// For further description see commented code.
+//
+// Inputs:
+// - candidate_delay : The delay to validate.
+// - valley_depth_q14 : The cost function has a valley/minimum at the
+// `candidate_delay` location. `valley_depth_q14` is the
+// cost function difference between the minimum and
+// maximum locations. The value is in the Q14 domain.
+// - valley_level_q14 : Is the cost function value at the minimum, in Q14.
+static void UpdateRobustValidationStatistics(BinaryDelayEstimator* self,
+ int candidate_delay,
+ int32_t valley_depth_q14,
+ int32_t valley_level_q14) {
+ const float valley_depth = valley_depth_q14 * kQ14Scaling;
+ float decrease_in_last_set = valley_depth;
+ const int max_hits_for_slow_change = (candidate_delay < self->last_delay)
+ ? kMaxHitsWhenPossiblyNonCausal
+ : kMaxHitsWhenPossiblyCausal;
+ int i = 0;
+
+ RTC_DCHECK_EQ(self->history_size, self->farend->history_size);
+ // Reset `candidate_hits` if we have a new candidate.
+ if (candidate_delay != self->last_candidate_delay) {
+ self->candidate_hits = 0;
+ self->last_candidate_delay = candidate_delay;
+ }
+ self->candidate_hits++;
+
+ // The `histogram` is updated differently across the bins.
+ // 1. The `candidate_delay` histogram bin is increased with the
+ // `valley_depth`, which is a simple measure of how reliable the
+ // `candidate_delay` is. The histogram is not increased above
+ // `kHistogramMax`.
+ self->histogram[candidate_delay] += valley_depth;
+ if (self->histogram[candidate_delay] > kHistogramMax) {
+ self->histogram[candidate_delay] = kHistogramMax;
+ }
+ // 2. The histogram bins in the neighborhood of `candidate_delay` are
+ // unaffected. The neighborhood is defined as x + {-2, -1, 0, 1}.
+ // 3. The histogram bins in the neighborhood of `last_delay` are decreased
+ // with `decrease_in_last_set`. This value equals the difference between
+ // the cost function values at the locations `candidate_delay` and
+ // `last_delay` until we reach `max_hits_for_slow_change` consecutive hits
+ // at the `candidate_delay`. If we exceed this amount of hits the
+ // `candidate_delay` is a "potential" candidate and we start decreasing
+ // these histogram bins more rapidly with `valley_depth`.
+ if (self->candidate_hits < max_hits_for_slow_change) {
+ decrease_in_last_set =
+ (self->mean_bit_counts[self->compare_delay] - valley_level_q14) *
+ kQ14Scaling;
+ }
+ // 4. All other bins are decreased with `valley_depth`.
+ // TODO(bjornv): Investigate how to make this loop more efficient. Split up
+ // the loop? Remove parts that doesn't add too much.
+ for (i = 0; i < self->history_size; ++i) {
+ int is_in_last_set = (i >= self->last_delay - 2) &&
+ (i <= self->last_delay + 1) && (i != candidate_delay);
+ int is_in_candidate_set =
+ (i >= candidate_delay - 2) && (i <= candidate_delay + 1);
+ self->histogram[i] -=
+ decrease_in_last_set * is_in_last_set +
+ valley_depth * (!is_in_last_set && !is_in_candidate_set);
+ // 5. No histogram bin can go below 0.
+ if (self->histogram[i] < 0) {
+ self->histogram[i] = 0;
+ }
+ }
+}
+
+// Validates the `candidate_delay`, estimated in WebRtc_ProcessBinarySpectrum(),
+// based on a mix of counting concurring hits with a modified histogram
+// of recent delay estimates. In brief a candidate is valid (returns 1) if it
+// is the most likely according to the histogram. There are a couple of
+// exceptions that are worth mentioning:
+// 1. If the `candidate_delay` < `last_delay` it can be that we are in a
+// non-causal state, breaking a possible echo control algorithm. Hence, we
+// open up for a quicker change by allowing the change even if the
+// `candidate_delay` is not the most likely one according to the histogram.
+// 2. There's a minimum number of hits (kMinRequiredHits) and the histogram
+// value has to reached a minimum (kMinHistogramThreshold) to be valid.
+// 3. The action is also depending on the filter length used for echo control.
+// If the delay difference is larger than what the filter can capture, we
+// also move quicker towards a change.
+// For further description see commented code.
+//
+// Input:
+// - candidate_delay : The delay to validate.
+//
+// Return value:
+// - is_histogram_valid : 1 - The `candidate_delay` is valid.
+// 0 - Otherwise.
+static int HistogramBasedValidation(const BinaryDelayEstimator* self,
+ int candidate_delay) {
+ float fraction = 1.f;
+ float histogram_threshold = self->histogram[self->compare_delay];
+ const int delay_difference = candidate_delay - self->last_delay;
+ int is_histogram_valid = 0;
+
+ // The histogram based validation of `candidate_delay` is done by comparing
+ // the `histogram` at bin `candidate_delay` with a `histogram_threshold`.
+ // This `histogram_threshold` equals a `fraction` of the `histogram` at bin
+ // `last_delay`. The `fraction` is a piecewise linear function of the
+ // `delay_difference` between the `candidate_delay` and the `last_delay`
+ // allowing for a quicker move if
+ // i) a potential echo control filter can not handle these large differences.
+ // ii) keeping `last_delay` instead of updating to `candidate_delay` could
+ // force an echo control into a non-causal state.
+ // We further require the histogram to have reached a minimum value of
+ // `kMinHistogramThreshold`. In addition, we also require the number of
+ // `candidate_hits` to be more than `kMinRequiredHits` to remove spurious
+ // values.
+
+ // Calculate a comparison histogram value (`histogram_threshold`) that is
+ // depending on the distance between the `candidate_delay` and `last_delay`.
+ // TODO(bjornv): How much can we gain by turning the fraction calculation
+ // into tables?
+ if (delay_difference > self->allowed_offset) {
+ fraction = 1.f - kFractionSlope * (delay_difference - self->allowed_offset);
+ fraction = (fraction > kMinFractionWhenPossiblyCausal
+ ? fraction
+ : kMinFractionWhenPossiblyCausal);
+ } else if (delay_difference < 0) {
+ fraction =
+ kMinFractionWhenPossiblyNonCausal - kFractionSlope * delay_difference;
+ fraction = (fraction > 1.f ? 1.f : fraction);
+ }
+ histogram_threshold *= fraction;
+ histogram_threshold =
+ (histogram_threshold > kMinHistogramThreshold ? histogram_threshold
+ : kMinHistogramThreshold);
+
+ is_histogram_valid =
+ (self->histogram[candidate_delay] >= histogram_threshold) &&
+ (self->candidate_hits > kMinRequiredHits);
+
+ return is_histogram_valid;
+}
+
+// Performs a robust validation of the `candidate_delay` estimated in
+// WebRtc_ProcessBinarySpectrum(). The algorithm takes the
+// `is_instantaneous_valid` and the `is_histogram_valid` and combines them
+// into a robust validation. The HistogramBasedValidation() has to be called
+// prior to this call.
+// For further description on how the combination is done, see commented code.
+//
+// Inputs:
+// - candidate_delay : The delay to validate.
+// - is_instantaneous_valid : The instantaneous validation performed in
+// WebRtc_ProcessBinarySpectrum().
+// - is_histogram_valid : The histogram based validation.
+//
+// Return value:
+// - is_robust : 1 - The candidate_delay is valid according to a
+// combination of the two inputs.
+// : 0 - Otherwise.
+static int RobustValidation(const BinaryDelayEstimator* self,
+ int candidate_delay,
+ int is_instantaneous_valid,
+ int is_histogram_valid) {
+ int is_robust = 0;
+
+ // The final robust validation is based on the two algorithms; 1) the
+ // `is_instantaneous_valid` and 2) the histogram based with result stored in
+ // `is_histogram_valid`.
+ // i) Before we actually have a valid estimate (`last_delay` == -2), we say
+ // a candidate is valid if either algorithm states so
+ // (`is_instantaneous_valid` OR `is_histogram_valid`).
+ is_robust =
+ (self->last_delay < 0) && (is_instantaneous_valid || is_histogram_valid);
+ // ii) Otherwise, we need both algorithms to be certain
+ // (`is_instantaneous_valid` AND `is_histogram_valid`)
+ is_robust |= is_instantaneous_valid && is_histogram_valid;
+ // iii) With one exception, i.e., the histogram based algorithm can overrule
+ // the instantaneous one if `is_histogram_valid` = 1 and the histogram
+ // is significantly strong.
+ is_robust |= is_histogram_valid &&
+ (self->histogram[candidate_delay] > self->last_delay_histogram);
+
+ return is_robust;
+}
+
+void WebRtc_FreeBinaryDelayEstimatorFarend(BinaryDelayEstimatorFarend* self) {
+ if (self == NULL) {
+ return;
+ }
+
+ free(self->binary_far_history);
+ self->binary_far_history = NULL;
+
+ free(self->far_bit_counts);
+ self->far_bit_counts = NULL;
+
+ free(self);
+}
+
+BinaryDelayEstimatorFarend* WebRtc_CreateBinaryDelayEstimatorFarend(
+ int history_size) {
+ BinaryDelayEstimatorFarend* self = NULL;
+
+ if (history_size > 1) {
+ // Sanity conditions fulfilled.
+ self = static_cast<BinaryDelayEstimatorFarend*>(
+ malloc(sizeof(BinaryDelayEstimatorFarend)));
+ }
+ if (self == NULL) {
+ return NULL;
+ }
+
+ self->history_size = 0;
+ self->binary_far_history = NULL;
+ self->far_bit_counts = NULL;
+ if (WebRtc_AllocateFarendBufferMemory(self, history_size) == 0) {
+ WebRtc_FreeBinaryDelayEstimatorFarend(self);
+ self = NULL;
+ }
+ return self;
+}
+
+int WebRtc_AllocateFarendBufferMemory(BinaryDelayEstimatorFarend* self,
+ int history_size) {
+ RTC_DCHECK(self);
+ // (Re-)Allocate memory for history buffers.
+ self->binary_far_history = static_cast<uint32_t*>(
+ realloc(self->binary_far_history,
+ history_size * sizeof(*self->binary_far_history)));
+ self->far_bit_counts = static_cast<int*>(realloc(
+ self->far_bit_counts, history_size * sizeof(*self->far_bit_counts)));
+ if ((self->binary_far_history == NULL) || (self->far_bit_counts == NULL)) {
+ history_size = 0;
+ }
+ // Fill with zeros if we have expanded the buffers.
+ if (history_size > self->history_size) {
+ int size_diff = history_size - self->history_size;
+ memset(&self->binary_far_history[self->history_size], 0,
+ sizeof(*self->binary_far_history) * size_diff);
+ memset(&self->far_bit_counts[self->history_size], 0,
+ sizeof(*self->far_bit_counts) * size_diff);
+ }
+ self->history_size = history_size;
+
+ return self->history_size;
+}
+
+void WebRtc_InitBinaryDelayEstimatorFarend(BinaryDelayEstimatorFarend* self) {
+ RTC_DCHECK(self);
+ memset(self->binary_far_history, 0, sizeof(uint32_t) * self->history_size);
+ memset(self->far_bit_counts, 0, sizeof(int) * self->history_size);
+}
+
+void WebRtc_SoftResetBinaryDelayEstimatorFarend(
+ BinaryDelayEstimatorFarend* self,
+ int delay_shift) {
+ int abs_shift = abs(delay_shift);
+ int shift_size = 0;
+ int dest_index = 0;
+ int src_index = 0;
+ int padding_index = 0;
+
+ RTC_DCHECK(self);
+ shift_size = self->history_size - abs_shift;
+ RTC_DCHECK_GT(shift_size, 0);
+ if (delay_shift == 0) {
+ return;
+ } else if (delay_shift > 0) {
+ dest_index = abs_shift;
+ } else if (delay_shift < 0) {
+ src_index = abs_shift;
+ padding_index = shift_size;
+ }
+
+ // Shift and zero pad buffers.
+ memmove(&self->binary_far_history[dest_index],
+ &self->binary_far_history[src_index],
+ sizeof(*self->binary_far_history) * shift_size);
+ memset(&self->binary_far_history[padding_index], 0,
+ sizeof(*self->binary_far_history) * abs_shift);
+ memmove(&self->far_bit_counts[dest_index], &self->far_bit_counts[src_index],
+ sizeof(*self->far_bit_counts) * shift_size);
+ memset(&self->far_bit_counts[padding_index], 0,
+ sizeof(*self->far_bit_counts) * abs_shift);
+}
+
+void WebRtc_AddBinaryFarSpectrum(BinaryDelayEstimatorFarend* handle,
+ uint32_t binary_far_spectrum) {
+ RTC_DCHECK(handle);
+ // Shift binary spectrum history and insert current `binary_far_spectrum`.
+ memmove(&(handle->binary_far_history[1]), &(handle->binary_far_history[0]),
+ (handle->history_size - 1) * sizeof(uint32_t));
+ handle->binary_far_history[0] = binary_far_spectrum;
+
+ // Shift history of far-end binary spectrum bit counts and insert bit count
+ // of current `binary_far_spectrum`.
+ memmove(&(handle->far_bit_counts[1]), &(handle->far_bit_counts[0]),
+ (handle->history_size - 1) * sizeof(int));
+ handle->far_bit_counts[0] = BitCount(binary_far_spectrum);
+}
+
+void WebRtc_FreeBinaryDelayEstimator(BinaryDelayEstimator* self) {
+ if (self == NULL) {
+ return;
+ }
+
+ free(self->mean_bit_counts);
+ self->mean_bit_counts = NULL;
+
+ free(self->bit_counts);
+ self->bit_counts = NULL;
+
+ free(self->binary_near_history);
+ self->binary_near_history = NULL;
+
+ free(self->histogram);
+ self->histogram = NULL;
+
+ // BinaryDelayEstimator does not have ownership of `farend`, hence we do not
+ // free the memory here. That should be handled separately by the user.
+ self->farend = NULL;
+
+ free(self);
+}
+
+BinaryDelayEstimator* WebRtc_CreateBinaryDelayEstimator(
+ BinaryDelayEstimatorFarend* farend,
+ int max_lookahead) {
+ BinaryDelayEstimator* self = NULL;
+
+ if ((farend != NULL) && (max_lookahead >= 0)) {
+ // Sanity conditions fulfilled.
+ self = static_cast<BinaryDelayEstimator*>(
+ malloc(sizeof(BinaryDelayEstimator)));
+ }
+ if (self == NULL) {
+ return NULL;
+ }
+
+ self->farend = farend;
+ self->near_history_size = max_lookahead + 1;
+ self->history_size = 0;
+ self->robust_validation_enabled = 0; // Disabled by default.
+ self->allowed_offset = 0;
+
+ self->lookahead = max_lookahead;
+
+ // Allocate memory for spectrum and history buffers.
+ self->mean_bit_counts = NULL;
+ self->bit_counts = NULL;
+ self->histogram = NULL;
+ self->binary_near_history = static_cast<uint32_t*>(
+ malloc((max_lookahead + 1) * sizeof(*self->binary_near_history)));
+ if (self->binary_near_history == NULL ||
+ WebRtc_AllocateHistoryBufferMemory(self, farend->history_size) == 0) {
+ WebRtc_FreeBinaryDelayEstimator(self);
+ self = NULL;
+ }
+
+ return self;
+}
+
+int WebRtc_AllocateHistoryBufferMemory(BinaryDelayEstimator* self,
+ int history_size) {
+ BinaryDelayEstimatorFarend* far = self->farend;
+ // (Re-)Allocate memory for spectrum and history buffers.
+ if (history_size != far->history_size) {
+ // Only update far-end buffers if we need.
+ history_size = WebRtc_AllocateFarendBufferMemory(far, history_size);
+ }
+ // The extra array element in `mean_bit_counts` and `histogram` is a dummy
+ // element only used while `last_delay` == -2, i.e., before we have a valid
+ // estimate.
+ self->mean_bit_counts = static_cast<int32_t*>(
+ realloc(self->mean_bit_counts,
+ (history_size + 1) * sizeof(*self->mean_bit_counts)));
+ self->bit_counts = static_cast<int32_t*>(
+ realloc(self->bit_counts, history_size * sizeof(*self->bit_counts)));
+ self->histogram = static_cast<float*>(
+ realloc(self->histogram, (history_size + 1) * sizeof(*self->histogram)));
+
+ if ((self->mean_bit_counts == NULL) || (self->bit_counts == NULL) ||
+ (self->histogram == NULL)) {
+ history_size = 0;
+ }
+ // Fill with zeros if we have expanded the buffers.
+ if (history_size > self->history_size) {
+ int size_diff = history_size - self->history_size;
+ memset(&self->mean_bit_counts[self->history_size], 0,
+ sizeof(*self->mean_bit_counts) * size_diff);
+ memset(&self->bit_counts[self->history_size], 0,
+ sizeof(*self->bit_counts) * size_diff);
+ memset(&self->histogram[self->history_size], 0,
+ sizeof(*self->histogram) * size_diff);
+ }
+ self->history_size = history_size;
+
+ return self->history_size;
+}
+
+void WebRtc_InitBinaryDelayEstimator(BinaryDelayEstimator* self) {
+ int i = 0;
+ RTC_DCHECK(self);
+
+ memset(self->bit_counts, 0, sizeof(int32_t) * self->history_size);
+ memset(self->binary_near_history, 0,
+ sizeof(uint32_t) * self->near_history_size);
+ for (i = 0; i <= self->history_size; ++i) {
+ self->mean_bit_counts[i] = (20 << 9); // 20 in Q9.
+ self->histogram[i] = 0.f;
+ }
+ self->minimum_probability = kMaxBitCountsQ9; // 32 in Q9.
+ self->last_delay_probability = (int)kMaxBitCountsQ9; // 32 in Q9.
+
+ // Default return value if we're unable to estimate. -1 is used for errors.
+ self->last_delay = -2;
+
+ self->last_candidate_delay = -2;
+ self->compare_delay = self->history_size;
+ self->candidate_hits = 0;
+ self->last_delay_histogram = 0.f;
+}
+
+int WebRtc_SoftResetBinaryDelayEstimator(BinaryDelayEstimator* self,
+ int delay_shift) {
+ int lookahead = 0;
+ RTC_DCHECK(self);
+ lookahead = self->lookahead;
+ self->lookahead -= delay_shift;
+ if (self->lookahead < 0) {
+ self->lookahead = 0;
+ }
+ if (self->lookahead > self->near_history_size - 1) {
+ self->lookahead = self->near_history_size - 1;
+ }
+ return lookahead - self->lookahead;
+}
+
+int WebRtc_ProcessBinarySpectrum(BinaryDelayEstimator* self,
+ uint32_t binary_near_spectrum) {
+ int i = 0;
+ int candidate_delay = -1;
+ int valid_candidate = 0;
+
+ int32_t value_best_candidate = kMaxBitCountsQ9;
+ int32_t value_worst_candidate = 0;
+ int32_t valley_depth = 0;
+
+ RTC_DCHECK(self);
+ if (self->farend->history_size != self->history_size) {
+ // Non matching history sizes.
+ return -1;
+ }
+ if (self->near_history_size > 1) {
+ // If we apply lookahead, shift near-end binary spectrum history. Insert
+ // current `binary_near_spectrum` and pull out the delayed one.
+ memmove(&(self->binary_near_history[1]), &(self->binary_near_history[0]),
+ (self->near_history_size - 1) * sizeof(uint32_t));
+ self->binary_near_history[0] = binary_near_spectrum;
+ binary_near_spectrum = self->binary_near_history[self->lookahead];
+ }
+
+ // Compare with delayed spectra and store the `bit_counts` for each delay.
+ BitCountComparison(binary_near_spectrum, self->farend->binary_far_history,
+ self->history_size, self->bit_counts);
+
+ // Update `mean_bit_counts`, which is the smoothed version of `bit_counts`.
+ for (i = 0; i < self->history_size; i++) {
+ // `bit_counts` is constrained to [0, 32], meaning we can smooth with a
+ // factor up to 2^26. We use Q9.
+ int32_t bit_count = (self->bit_counts[i] << 9); // Q9.
+
+ // Update `mean_bit_counts` only when far-end signal has something to
+ // contribute. If `far_bit_counts` is zero the far-end signal is weak and
+ // we likely have a poor echo condition, hence don't update.
+ if (self->farend->far_bit_counts[i] > 0) {
+ // Make number of right shifts piecewise linear w.r.t. `far_bit_counts`.
+ int shifts = kShiftsAtZero;
+ shifts -= (kShiftsLinearSlope * self->farend->far_bit_counts[i]) >> 4;
+ WebRtc_MeanEstimatorFix(bit_count, shifts, &(self->mean_bit_counts[i]));
+ }
+ }
+
+ // Find `candidate_delay`, `value_best_candidate` and `value_worst_candidate`
+ // of `mean_bit_counts`.
+ for (i = 0; i < self->history_size; i++) {
+ if (self->mean_bit_counts[i] < value_best_candidate) {
+ value_best_candidate = self->mean_bit_counts[i];
+ candidate_delay = i;
+ }
+ if (self->mean_bit_counts[i] > value_worst_candidate) {
+ value_worst_candidate = self->mean_bit_counts[i];
+ }
+ }
+ valley_depth = value_worst_candidate - value_best_candidate;
+
+ // The `value_best_candidate` is a good indicator on the probability of
+ // `candidate_delay` being an accurate delay (a small `value_best_candidate`
+ // means a good binary match). In the following sections we make a decision
+ // whether to update `last_delay` or not.
+ // 1) If the difference bit counts between the best and the worst delay
+ // candidates is too small we consider the situation to be unreliable and
+ // don't update `last_delay`.
+ // 2) If the situation is reliable we update `last_delay` if the value of the
+ // best candidate delay has a value less than
+ // i) an adaptive threshold `minimum_probability`, or
+ // ii) this corresponding value `last_delay_probability`, but updated at
+ // this time instant.
+
+ // Update `minimum_probability`.
+ if ((self->minimum_probability > kProbabilityLowerLimit) &&
+ (valley_depth > kProbabilityMinSpread)) {
+ // The "hard" threshold can't be lower than 17 (in Q9).
+ // The valley in the curve also has to be distinct, i.e., the
+ // difference between `value_worst_candidate` and `value_best_candidate` has
+ // to be large enough.
+ int32_t threshold = value_best_candidate + kProbabilityOffset;
+ if (threshold < kProbabilityLowerLimit) {
+ threshold = kProbabilityLowerLimit;
+ }
+ if (self->minimum_probability > threshold) {
+ self->minimum_probability = threshold;
+ }
+ }
+ // Update `last_delay_probability`.
+ // We use a Markov type model, i.e., a slowly increasing level over time.
+ self->last_delay_probability++;
+ // Validate `candidate_delay`. We have a reliable instantaneous delay
+ // estimate if
+ // 1) The valley is distinct enough (`valley_depth` > `kProbabilityOffset`)
+ // and
+ // 2) The depth of the valley is deep enough
+ // (`value_best_candidate` < `minimum_probability`)
+ // and deeper than the best estimate so far
+ // (`value_best_candidate` < `last_delay_probability`)
+ valid_candidate = ((valley_depth > kProbabilityOffset) &&
+ ((value_best_candidate < self->minimum_probability) ||
+ (value_best_candidate < self->last_delay_probability)));
+
+ // Check for nonstationary farend signal.
+ const bool non_stationary_farend =
+ std::any_of(self->farend->far_bit_counts,
+ self->farend->far_bit_counts + self->history_size,
+ [](int a) { return a > 0; });
+
+ if (non_stationary_farend) {
+ // Only update the validation statistics when the farend is nonstationary
+ // as the underlying estimates are otherwise frozen.
+ UpdateRobustValidationStatistics(self, candidate_delay, valley_depth,
+ value_best_candidate);
+ }
+
+ if (self->robust_validation_enabled) {
+ int is_histogram_valid = HistogramBasedValidation(self, candidate_delay);
+ valid_candidate = RobustValidation(self, candidate_delay, valid_candidate,
+ is_histogram_valid);
+ }
+
+ // Only update the delay estimate when the farend is nonstationary and when
+ // a valid delay candidate is available.
+ if (non_stationary_farend && valid_candidate) {
+ if (candidate_delay != self->last_delay) {
+ self->last_delay_histogram =
+ (self->histogram[candidate_delay] > kLastHistogramMax
+ ? kLastHistogramMax
+ : self->histogram[candidate_delay]);
+ // Adjust the histogram if we made a change to `last_delay`, though it was
+ // not the most likely one according to the histogram.
+ if (self->histogram[candidate_delay] <
+ self->histogram[self->compare_delay]) {
+ self->histogram[self->compare_delay] = self->histogram[candidate_delay];
+ }
+ }
+ self->last_delay = candidate_delay;
+ if (value_best_candidate < self->last_delay_probability) {
+ self->last_delay_probability = value_best_candidate;
+ }
+ self->compare_delay = self->last_delay;
+ }
+
+ return self->last_delay;
+}
+
+int WebRtc_binary_last_delay(BinaryDelayEstimator* self) {
+ RTC_DCHECK(self);
+ return self->last_delay;
+}
+
+float WebRtc_binary_last_delay_quality(BinaryDelayEstimator* self) {
+ float quality = 0;
+ RTC_DCHECK(self);
+
+ if (self->robust_validation_enabled) {
+ // Simply a linear function of the histogram height at delay estimate.
+ quality = self->histogram[self->compare_delay] / kHistogramMax;
+ } else {
+ // Note that `last_delay_probability` states how deep the minimum of the
+ // cost function is, so it is rather an error probability.
+ quality = (float)(kMaxBitCountsQ9 - self->last_delay_probability) /
+ kMaxBitCountsQ9;
+ if (quality < 0) {
+ quality = 0;
+ }
+ }
+ return quality;
+}
+
+void WebRtc_MeanEstimatorFix(int32_t new_value,
+ int factor,
+ int32_t* mean_value) {
+ int32_t diff = new_value - *mean_value;
+
+ // mean_new = mean_value + ((new_value - mean_value) >> factor);
+ if (diff < 0) {
+ diff = -((-diff) >> factor);
+ } else {
+ diff = (diff >> factor);
+ }
+ *mean_value += diff;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator.h b/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator.h
new file mode 100644
index 0000000000..b6fc36a759
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator.h
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2012 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.
+ */
+
+// Performs delay estimation on binary converted spectra.
+// The return value is 0 - OK and -1 - Error, unless otherwise stated.
+
+#ifndef MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_H_
+#define MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_H_
+
+#include <stdint.h>
+
+namespace webrtc {
+
+static const int32_t kMaxBitCountsQ9 = (32 << 9); // 32 matching bits in Q9.
+
+typedef struct {
+ // Pointer to bit counts.
+ int* far_bit_counts;
+ // Binary history variables.
+ uint32_t* binary_far_history;
+ int history_size;
+} BinaryDelayEstimatorFarend;
+
+typedef struct {
+ // Pointer to bit counts.
+ int32_t* mean_bit_counts;
+ // Array only used locally in ProcessBinarySpectrum() but whose size is
+ // determined at run-time.
+ int32_t* bit_counts;
+
+ // Binary history variables.
+ uint32_t* binary_near_history;
+ int near_history_size;
+ int history_size;
+
+ // Delay estimation variables.
+ int32_t minimum_probability;
+ int last_delay_probability;
+
+ // Delay memory.
+ int last_delay;
+
+ // Robust validation
+ int robust_validation_enabled;
+ int allowed_offset;
+ int last_candidate_delay;
+ int compare_delay;
+ int candidate_hits;
+ float* histogram;
+ float last_delay_histogram;
+
+ // For dynamically changing the lookahead when using SoftReset...().
+ int lookahead;
+
+ // Far-end binary spectrum history buffer etc.
+ BinaryDelayEstimatorFarend* farend;
+} BinaryDelayEstimator;
+
+// Releases the memory allocated by
+// WebRtc_CreateBinaryDelayEstimatorFarend(...).
+// Input:
+// - self : Pointer to the binary delay estimation far-end
+// instance which is the return value of
+// WebRtc_CreateBinaryDelayEstimatorFarend().
+//
+void WebRtc_FreeBinaryDelayEstimatorFarend(BinaryDelayEstimatorFarend* self);
+
+// Allocates the memory needed by the far-end part of the binary delay
+// estimation. The memory needs to be initialized separately through
+// WebRtc_InitBinaryDelayEstimatorFarend(...).
+//
+// Inputs:
+// - history_size : Size of the far-end binary spectrum history.
+//
+// Return value:
+// - BinaryDelayEstimatorFarend*
+// : Created `handle`. If the memory can't be allocated
+// or if any of the input parameters are invalid NULL
+// is returned.
+//
+BinaryDelayEstimatorFarend* WebRtc_CreateBinaryDelayEstimatorFarend(
+ int history_size);
+
+// Re-allocates the buffers.
+//
+// Inputs:
+// - self : Pointer to the binary estimation far-end instance
+// which is the return value of
+// WebRtc_CreateBinaryDelayEstimatorFarend().
+// - history_size : Size of the far-end binary spectrum history.
+//
+// Return value:
+// - history_size : The history size allocated.
+int WebRtc_AllocateFarendBufferMemory(BinaryDelayEstimatorFarend* self,
+ int history_size);
+
+// Initializes the delay estimation far-end instance created with
+// WebRtc_CreateBinaryDelayEstimatorFarend(...).
+//
+// Input:
+// - self : Pointer to the delay estimation far-end instance.
+//
+// Output:
+// - self : Initialized far-end instance.
+//
+void WebRtc_InitBinaryDelayEstimatorFarend(BinaryDelayEstimatorFarend* self);
+
+// Soft resets the delay estimation far-end instance created with
+// WebRtc_CreateBinaryDelayEstimatorFarend(...).
+//
+// Input:
+// - delay_shift : The amount of blocks to shift history buffers.
+//
+void WebRtc_SoftResetBinaryDelayEstimatorFarend(
+ BinaryDelayEstimatorFarend* self,
+ int delay_shift);
+
+// Adds the binary far-end spectrum to the internal far-end history buffer. This
+// spectrum is used as reference when calculating the delay using
+// WebRtc_ProcessBinarySpectrum().
+//
+// Inputs:
+// - self : Pointer to the delay estimation far-end
+// instance.
+// - binary_far_spectrum : Far-end binary spectrum.
+//
+// Output:
+// - self : Updated far-end instance.
+//
+void WebRtc_AddBinaryFarSpectrum(BinaryDelayEstimatorFarend* self,
+ uint32_t binary_far_spectrum);
+
+// Releases the memory allocated by WebRtc_CreateBinaryDelayEstimator(...).
+//
+// Note that BinaryDelayEstimator utilizes BinaryDelayEstimatorFarend, but does
+// not take ownership of it, hence the BinaryDelayEstimator has to be torn down
+// before the far-end.
+//
+// Input:
+// - self : Pointer to the binary delay estimation instance
+// which is the return value of
+// WebRtc_CreateBinaryDelayEstimator().
+//
+void WebRtc_FreeBinaryDelayEstimator(BinaryDelayEstimator* self);
+
+// Allocates the memory needed by the binary delay estimation. The memory needs
+// to be initialized separately through WebRtc_InitBinaryDelayEstimator(...).
+//
+// See WebRtc_CreateDelayEstimator(..) in delay_estimator_wrapper.c for detailed
+// description.
+BinaryDelayEstimator* WebRtc_CreateBinaryDelayEstimator(
+ BinaryDelayEstimatorFarend* farend,
+ int max_lookahead);
+
+// Re-allocates `history_size` dependent buffers. The far-end buffers will be
+// updated at the same time if needed.
+//
+// Input:
+// - self : Pointer to the binary estimation instance which is
+// the return value of
+// WebRtc_CreateBinaryDelayEstimator().
+// - history_size : Size of the history buffers.
+//
+// Return value:
+// - history_size : The history size allocated.
+int WebRtc_AllocateHistoryBufferMemory(BinaryDelayEstimator* self,
+ int history_size);
+
+// Initializes the delay estimation instance created with
+// WebRtc_CreateBinaryDelayEstimator(...).
+//
+// Input:
+// - self : Pointer to the delay estimation instance.
+//
+// Output:
+// - self : Initialized instance.
+//
+void WebRtc_InitBinaryDelayEstimator(BinaryDelayEstimator* self);
+
+// Soft resets the delay estimation instance created with
+// WebRtc_CreateBinaryDelayEstimator(...).
+//
+// Input:
+// - delay_shift : The amount of blocks to shift history buffers.
+//
+// Return value:
+// - actual_shifts : The actual number of shifts performed.
+//
+int WebRtc_SoftResetBinaryDelayEstimator(BinaryDelayEstimator* self,
+ int delay_shift);
+
+// Estimates and returns the delay between the binary far-end and binary near-
+// end spectra. It is assumed the binary far-end spectrum has been added using
+// WebRtc_AddBinaryFarSpectrum() prior to this call. The value will be offset by
+// the lookahead (i.e. the lookahead should be subtracted from the returned
+// value).
+//
+// Inputs:
+// - self : Pointer to the delay estimation instance.
+// - binary_near_spectrum : Near-end binary spectrum of the current block.
+//
+// Output:
+// - self : Updated instance.
+//
+// Return value:
+// - delay : >= 0 - Calculated delay value.
+// -2 - Insufficient data for estimation.
+//
+int WebRtc_ProcessBinarySpectrum(BinaryDelayEstimator* self,
+ uint32_t binary_near_spectrum);
+
+// Returns the last calculated delay updated by the function
+// WebRtc_ProcessBinarySpectrum(...).
+//
+// Input:
+// - self : Pointer to the delay estimation instance.
+//
+// Return value:
+// - delay : >= 0 - Last calculated delay value
+// -2 - Insufficient data for estimation.
+//
+int WebRtc_binary_last_delay(BinaryDelayEstimator* self);
+
+// Returns the estimation quality of the last calculated delay updated by the
+// function WebRtc_ProcessBinarySpectrum(...). The estimation quality is a value
+// in the interval [0, 1]. The higher the value, the better the quality.
+//
+// Return value:
+// - delay_quality : >= 0 - Estimation quality of last calculated
+// delay value.
+float WebRtc_binary_last_delay_quality(BinaryDelayEstimator* self);
+
+// Updates the `mean_value` recursively with a step size of 2^-`factor`. This
+// function is used internally in the Binary Delay Estimator as well as the
+// Fixed point wrapper.
+//
+// Inputs:
+// - new_value : The new value the mean should be updated with.
+// - factor : The step size, in number of right shifts.
+//
+// Input/Output:
+// - mean_value : Pointer to the mean value.
+//
+void WebRtc_MeanEstimatorFix(int32_t new_value,
+ int factor,
+ int32_t* mean_value);
+
+} // namespace webrtc
+
+#endif // MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_H_
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator_internal.h b/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator_internal.h
new file mode 100644
index 0000000000..891e20027d
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator_internal.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012 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.
+ */
+
+// Header file including the delay estimator handle used for testing.
+
+#ifndef MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_INTERNAL_H_
+#define MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_INTERNAL_H_
+
+#include "modules/audio_processing/utility/delay_estimator.h"
+
+namespace webrtc {
+
+typedef union {
+ float float_;
+ int32_t int32_;
+} SpectrumType;
+
+typedef struct {
+ // Pointers to mean values of spectrum.
+ SpectrumType* mean_far_spectrum;
+ // `mean_far_spectrum` initialization indicator.
+ int far_spectrum_initialized;
+
+ int spectrum_size;
+
+ // Far-end part of binary spectrum based delay estimation.
+ BinaryDelayEstimatorFarend* binary_farend;
+} DelayEstimatorFarend;
+
+typedef struct {
+ // Pointers to mean values of spectrum.
+ SpectrumType* mean_near_spectrum;
+ // `mean_near_spectrum` initialization indicator.
+ int near_spectrum_initialized;
+
+ int spectrum_size;
+
+ // Binary spectrum based delay estimator
+ BinaryDelayEstimator* binary_handle;
+} DelayEstimator;
+
+} // namespace webrtc
+
+#endif // MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_INTERNAL_H_
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator_unittest.cc b/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator_unittest.cc
new file mode 100644
index 0000000000..6052612ef3
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator_unittest.cc
@@ -0,0 +1,621 @@
+/*
+ * Copyright (c) 2012 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 "modules/audio_processing/utility/delay_estimator.h"
+
+#include "modules/audio_processing/utility/delay_estimator_internal.h"
+#include "modules/audio_processing/utility/delay_estimator_wrapper.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+
+constexpr int kSpectrumSize = 65;
+// Delay history sizes.
+constexpr int kMaxDelay = 100;
+constexpr int kLookahead = 10;
+constexpr int kHistorySize = kMaxDelay + kLookahead;
+// Length of binary spectrum sequence.
+constexpr int kSequenceLength = 400;
+
+const int kDifferentHistorySize = 3;
+const int kDifferentLookahead = 1;
+
+const int kEnable[] = {0, 1};
+const size_t kSizeEnable = sizeof(kEnable) / sizeof(*kEnable);
+
+class DelayEstimatorTest : public ::testing::Test {
+ protected:
+ DelayEstimatorTest();
+ void SetUp() override;
+ void TearDown() override;
+
+ void Init();
+ void InitBinary();
+ void VerifyDelay(BinaryDelayEstimator* binary_handle, int offset, int delay);
+ void RunBinarySpectra(BinaryDelayEstimator* binary1,
+ BinaryDelayEstimator* binary2,
+ int near_offset,
+ int lookahead_offset,
+ int far_offset);
+ void RunBinarySpectraTest(int near_offset,
+ int lookahead_offset,
+ int ref_robust_validation,
+ int robust_validation);
+
+ void* handle_;
+ DelayEstimator* self_;
+ void* farend_handle_;
+ DelayEstimatorFarend* farend_self_;
+ BinaryDelayEstimator* binary_;
+ BinaryDelayEstimatorFarend* binary_farend_;
+ int spectrum_size_;
+ // Dummy input spectra.
+ float far_f_[kSpectrumSize];
+ float near_f_[kSpectrumSize];
+ uint16_t far_u16_[kSpectrumSize];
+ uint16_t near_u16_[kSpectrumSize];
+ uint32_t binary_spectrum_[kSequenceLength + kHistorySize];
+};
+
+DelayEstimatorTest::DelayEstimatorTest()
+ : handle_(NULL),
+ self_(NULL),
+ farend_handle_(NULL),
+ farend_self_(NULL),
+ binary_(NULL),
+ binary_farend_(NULL),
+ spectrum_size_(kSpectrumSize) {
+ // Dummy input data are set with more or less arbitrary non-zero values.
+ memset(far_f_, 1, sizeof(far_f_));
+ memset(near_f_, 2, sizeof(near_f_));
+ memset(far_u16_, 1, sizeof(far_u16_));
+ memset(near_u16_, 2, sizeof(near_u16_));
+ // Construct a sequence of binary spectra used to verify delay estimate. The
+ // `kSequenceLength` has to be long enough for the delay estimation to leave
+ // the initialized state.
+ binary_spectrum_[0] = 1;
+ for (int i = 1; i < (kSequenceLength + kHistorySize); i++) {
+ binary_spectrum_[i] = 3 * binary_spectrum_[i - 1];
+ }
+}
+
+void DelayEstimatorTest::SetUp() {
+ farend_handle_ =
+ WebRtc_CreateDelayEstimatorFarend(kSpectrumSize, kHistorySize);
+ ASSERT_TRUE(farend_handle_ != NULL);
+ farend_self_ = reinterpret_cast<DelayEstimatorFarend*>(farend_handle_);
+ handle_ = WebRtc_CreateDelayEstimator(farend_handle_, kLookahead);
+ ASSERT_TRUE(handle_ != NULL);
+ self_ = reinterpret_cast<DelayEstimator*>(handle_);
+ binary_farend_ = WebRtc_CreateBinaryDelayEstimatorFarend(kHistorySize);
+ ASSERT_TRUE(binary_farend_ != NULL);
+ binary_ = WebRtc_CreateBinaryDelayEstimator(binary_farend_, kLookahead);
+ ASSERT_TRUE(binary_ != NULL);
+}
+
+void DelayEstimatorTest::TearDown() {
+ WebRtc_FreeDelayEstimator(handle_);
+ handle_ = NULL;
+ self_ = NULL;
+ WebRtc_FreeDelayEstimatorFarend(farend_handle_);
+ farend_handle_ = NULL;
+ farend_self_ = NULL;
+ WebRtc_FreeBinaryDelayEstimator(binary_);
+ binary_ = NULL;
+ WebRtc_FreeBinaryDelayEstimatorFarend(binary_farend_);
+ binary_farend_ = NULL;
+}
+
+void DelayEstimatorTest::Init() {
+ // Initialize Delay Estimator
+ EXPECT_EQ(0, WebRtc_InitDelayEstimatorFarend(farend_handle_));
+ EXPECT_EQ(0, WebRtc_InitDelayEstimator(handle_));
+ // Verify initialization.
+ EXPECT_EQ(0, farend_self_->far_spectrum_initialized);
+ EXPECT_EQ(0, self_->near_spectrum_initialized);
+ EXPECT_EQ(-2, WebRtc_last_delay(handle_)); // Delay in initial state.
+ EXPECT_FLOAT_EQ(0, WebRtc_last_delay_quality(handle_)); // Zero quality.
+}
+
+void DelayEstimatorTest::InitBinary() {
+ // Initialize Binary Delay Estimator (far-end part).
+ WebRtc_InitBinaryDelayEstimatorFarend(binary_farend_);
+ // Initialize Binary Delay Estimator
+ WebRtc_InitBinaryDelayEstimator(binary_);
+ // Verify initialization. This does not guarantee a complete check, since
+ // `last_delay` may be equal to -2 before initialization if done on the fly.
+ EXPECT_EQ(-2, binary_->last_delay);
+}
+
+void DelayEstimatorTest::VerifyDelay(BinaryDelayEstimator* binary_handle,
+ int offset,
+ int delay) {
+ // Verify that we WebRtc_binary_last_delay() returns correct delay.
+ EXPECT_EQ(delay, WebRtc_binary_last_delay(binary_handle));
+
+ if (delay != -2) {
+ // Verify correct delay estimate. In the non-causal case the true delay
+ // is equivalent with the `offset`.
+ EXPECT_EQ(offset, delay);
+ }
+}
+
+void DelayEstimatorTest::RunBinarySpectra(BinaryDelayEstimator* binary1,
+ BinaryDelayEstimator* binary2,
+ int near_offset,
+ int lookahead_offset,
+ int far_offset) {
+ int different_validations =
+ binary1->robust_validation_enabled ^ binary2->robust_validation_enabled;
+ WebRtc_InitBinaryDelayEstimatorFarend(binary_farend_);
+ WebRtc_InitBinaryDelayEstimator(binary1);
+ WebRtc_InitBinaryDelayEstimator(binary2);
+ // Verify initialization. This does not guarantee a complete check, since
+ // `last_delay` may be equal to -2 before initialization if done on the fly.
+ EXPECT_EQ(-2, binary1->last_delay);
+ EXPECT_EQ(-2, binary2->last_delay);
+ for (int i = kLookahead; i < (kSequenceLength + kLookahead); i++) {
+ WebRtc_AddBinaryFarSpectrum(binary_farend_,
+ binary_spectrum_[i + far_offset]);
+ int delay_1 = WebRtc_ProcessBinarySpectrum(binary1, binary_spectrum_[i]);
+ int delay_2 = WebRtc_ProcessBinarySpectrum(
+ binary2, binary_spectrum_[i - near_offset]);
+
+ VerifyDelay(binary1, far_offset + kLookahead, delay_1);
+ VerifyDelay(binary2,
+ far_offset + kLookahead + lookahead_offset + near_offset,
+ delay_2);
+ // Expect the two delay estimates to be offset by `lookahead_offset` +
+ // `near_offset` when we have left the initial state.
+ if ((delay_1 != -2) && (delay_2 != -2)) {
+ EXPECT_EQ(delay_1, delay_2 - lookahead_offset - near_offset);
+ }
+ // For the case of identical signals `delay_1` and `delay_2` should match
+ // all the time, unless one of them has robust validation turned on. In
+ // that case the robust validation leaves the initial state faster.
+ if ((near_offset == 0) && (lookahead_offset == 0)) {
+ if (!different_validations) {
+ EXPECT_EQ(delay_1, delay_2);
+ } else {
+ if (binary1->robust_validation_enabled) {
+ EXPECT_GE(delay_1, delay_2);
+ } else {
+ EXPECT_GE(delay_2, delay_1);
+ }
+ }
+ }
+ }
+ // Verify that we have left the initialized state.
+ EXPECT_NE(-2, WebRtc_binary_last_delay(binary1));
+ EXPECT_LT(0, WebRtc_binary_last_delay_quality(binary1));
+ EXPECT_NE(-2, WebRtc_binary_last_delay(binary2));
+ EXPECT_LT(0, WebRtc_binary_last_delay_quality(binary2));
+}
+
+void DelayEstimatorTest::RunBinarySpectraTest(int near_offset,
+ int lookahead_offset,
+ int ref_robust_validation,
+ int robust_validation) {
+ BinaryDelayEstimator* binary2 = WebRtc_CreateBinaryDelayEstimator(
+ binary_farend_, kLookahead + lookahead_offset);
+ // Verify the delay for both causal and non-causal systems. For causal systems
+ // the delay is equivalent with a positive `offset` of the far-end sequence.
+ // For non-causal systems the delay is equivalent with a negative `offset` of
+ // the far-end sequence.
+ binary_->robust_validation_enabled = ref_robust_validation;
+ binary2->robust_validation_enabled = robust_validation;
+ for (int offset = -kLookahead;
+ offset < kMaxDelay - lookahead_offset - near_offset; offset++) {
+ RunBinarySpectra(binary_, binary2, near_offset, lookahead_offset, offset);
+ }
+ WebRtc_FreeBinaryDelayEstimator(binary2);
+ binary2 = NULL;
+ binary_->robust_validation_enabled = 0; // Reset reference.
+}
+
+TEST_F(DelayEstimatorTest, CorrectErrorReturnsOfWrapper) {
+ // In this test we verify correct error returns on invalid API calls.
+
+ // WebRtc_CreateDelayEstimatorFarend() and WebRtc_CreateDelayEstimator()
+ // should return a NULL pointer on invalid input values.
+ // Make sure we have a non-NULL value at start, so we can detect NULL after
+ // create failure.
+ void* handle = farend_handle_;
+ handle = WebRtc_CreateDelayEstimatorFarend(33, kHistorySize);
+ EXPECT_TRUE(handle == NULL);
+ handle = WebRtc_CreateDelayEstimatorFarend(kSpectrumSize, 1);
+ EXPECT_TRUE(handle == NULL);
+
+ handle = handle_;
+ handle = WebRtc_CreateDelayEstimator(NULL, kLookahead);
+ EXPECT_TRUE(handle == NULL);
+ handle = WebRtc_CreateDelayEstimator(farend_handle_, -1);
+ EXPECT_TRUE(handle == NULL);
+
+ // WebRtc_InitDelayEstimatorFarend() and WebRtc_InitDelayEstimator() should
+ // return -1 if we have a NULL pointer as `handle`.
+ EXPECT_EQ(-1, WebRtc_InitDelayEstimatorFarend(NULL));
+ EXPECT_EQ(-1, WebRtc_InitDelayEstimator(NULL));
+
+ // WebRtc_AddFarSpectrumFloat() should return -1 if we have:
+ // 1) NULL pointer as `handle`.
+ // 2) NULL pointer as far-end spectrum.
+ // 3) Incorrect spectrum size.
+ EXPECT_EQ(-1, WebRtc_AddFarSpectrumFloat(NULL, far_f_, spectrum_size_));
+ // Use `farend_handle_` which is properly created at SetUp().
+ EXPECT_EQ(-1,
+ WebRtc_AddFarSpectrumFloat(farend_handle_, NULL, spectrum_size_));
+ EXPECT_EQ(-1, WebRtc_AddFarSpectrumFloat(farend_handle_, far_f_,
+ spectrum_size_ + 1));
+
+ // WebRtc_AddFarSpectrumFix() should return -1 if we have:
+ // 1) NULL pointer as `handle`.
+ // 2) NULL pointer as far-end spectrum.
+ // 3) Incorrect spectrum size.
+ // 4) Too high precision in far-end spectrum (Q-domain > 15).
+ EXPECT_EQ(-1, WebRtc_AddFarSpectrumFix(NULL, far_u16_, spectrum_size_, 0));
+ EXPECT_EQ(-1,
+ WebRtc_AddFarSpectrumFix(farend_handle_, NULL, spectrum_size_, 0));
+ EXPECT_EQ(-1, WebRtc_AddFarSpectrumFix(farend_handle_, far_u16_,
+ spectrum_size_ + 1, 0));
+ EXPECT_EQ(-1, WebRtc_AddFarSpectrumFix(farend_handle_, far_u16_,
+ spectrum_size_, 16));
+
+ // WebRtc_set_history_size() should return -1 if:
+ // 1) `handle` is a NULL.
+ // 2) `history_size` <= 1.
+ EXPECT_EQ(-1, WebRtc_set_history_size(NULL, 1));
+ EXPECT_EQ(-1, WebRtc_set_history_size(handle_, 1));
+ // WebRtc_history_size() should return -1 if:
+ // 1) NULL pointer input.
+ EXPECT_EQ(-1, WebRtc_history_size(NULL));
+ // 2) there is a mismatch between history size.
+ void* tmp_handle = WebRtc_CreateDelayEstimator(farend_handle_, kHistorySize);
+ EXPECT_EQ(0, WebRtc_InitDelayEstimator(tmp_handle));
+ EXPECT_EQ(kDifferentHistorySize,
+ WebRtc_set_history_size(tmp_handle, kDifferentHistorySize));
+ EXPECT_EQ(kDifferentHistorySize, WebRtc_history_size(tmp_handle));
+ EXPECT_EQ(kHistorySize, WebRtc_set_history_size(handle_, kHistorySize));
+ EXPECT_EQ(-1, WebRtc_history_size(tmp_handle));
+
+ // WebRtc_set_lookahead() should return -1 if we try a value outside the
+ /// buffer.
+ EXPECT_EQ(-1, WebRtc_set_lookahead(handle_, kLookahead + 1));
+ EXPECT_EQ(-1, WebRtc_set_lookahead(handle_, -1));
+
+ // WebRtc_set_allowed_offset() should return -1 if we have:
+ // 1) NULL pointer as `handle`.
+ // 2) `allowed_offset` < 0.
+ EXPECT_EQ(-1, WebRtc_set_allowed_offset(NULL, 0));
+ EXPECT_EQ(-1, WebRtc_set_allowed_offset(handle_, -1));
+
+ EXPECT_EQ(-1, WebRtc_get_allowed_offset(NULL));
+
+ // WebRtc_enable_robust_validation() should return -1 if we have:
+ // 1) NULL pointer as `handle`.
+ // 2) Incorrect `enable` value (not 0 or 1).
+ EXPECT_EQ(-1, WebRtc_enable_robust_validation(NULL, kEnable[0]));
+ EXPECT_EQ(-1, WebRtc_enable_robust_validation(handle_, -1));
+ EXPECT_EQ(-1, WebRtc_enable_robust_validation(handle_, 2));
+
+ // WebRtc_is_robust_validation_enabled() should return -1 if we have NULL
+ // pointer as `handle`.
+ EXPECT_EQ(-1, WebRtc_is_robust_validation_enabled(NULL));
+
+ // WebRtc_DelayEstimatorProcessFloat() should return -1 if we have:
+ // 1) NULL pointer as `handle`.
+ // 2) NULL pointer as near-end spectrum.
+ // 3) Incorrect spectrum size.
+ // 4) Non matching history sizes if multiple delay estimators using the same
+ // far-end reference.
+ EXPECT_EQ(-1,
+ WebRtc_DelayEstimatorProcessFloat(NULL, near_f_, spectrum_size_));
+ // Use `handle_` which is properly created at SetUp().
+ EXPECT_EQ(-1,
+ WebRtc_DelayEstimatorProcessFloat(handle_, NULL, spectrum_size_));
+ EXPECT_EQ(-1, WebRtc_DelayEstimatorProcessFloat(handle_, near_f_,
+ spectrum_size_ + 1));
+ // `tmp_handle` is already in a non-matching state.
+ EXPECT_EQ(-1, WebRtc_DelayEstimatorProcessFloat(tmp_handle, near_f_,
+ spectrum_size_));
+
+ // WebRtc_DelayEstimatorProcessFix() should return -1 if we have:
+ // 1) NULL pointer as `handle`.
+ // 2) NULL pointer as near-end spectrum.
+ // 3) Incorrect spectrum size.
+ // 4) Too high precision in near-end spectrum (Q-domain > 15).
+ // 5) Non matching history sizes if multiple delay estimators using the same
+ // far-end reference.
+ EXPECT_EQ(
+ -1, WebRtc_DelayEstimatorProcessFix(NULL, near_u16_, spectrum_size_, 0));
+ EXPECT_EQ(-1,
+ WebRtc_DelayEstimatorProcessFix(handle_, NULL, spectrum_size_, 0));
+ EXPECT_EQ(-1, WebRtc_DelayEstimatorProcessFix(handle_, near_u16_,
+ spectrum_size_ + 1, 0));
+ EXPECT_EQ(-1, WebRtc_DelayEstimatorProcessFix(handle_, near_u16_,
+ spectrum_size_, 16));
+ // `tmp_handle` is already in a non-matching state.
+ EXPECT_EQ(-1, WebRtc_DelayEstimatorProcessFix(tmp_handle, near_u16_,
+ spectrum_size_, 0));
+ WebRtc_FreeDelayEstimator(tmp_handle);
+
+ // WebRtc_last_delay() should return -1 if we have a NULL pointer as `handle`.
+ EXPECT_EQ(-1, WebRtc_last_delay(NULL));
+
+ // Free any local memory if needed.
+ WebRtc_FreeDelayEstimator(handle);
+}
+
+TEST_F(DelayEstimatorTest, VerifyAllowedOffset) {
+ // Is set to zero by default.
+ EXPECT_EQ(0, WebRtc_get_allowed_offset(handle_));
+ for (int i = 1; i >= 0; i--) {
+ EXPECT_EQ(0, WebRtc_set_allowed_offset(handle_, i));
+ EXPECT_EQ(i, WebRtc_get_allowed_offset(handle_));
+ Init();
+ // Unaffected over a reset.
+ EXPECT_EQ(i, WebRtc_get_allowed_offset(handle_));
+ }
+}
+
+TEST_F(DelayEstimatorTest, VerifyEnableRobustValidation) {
+ // Disabled by default.
+ EXPECT_EQ(0, WebRtc_is_robust_validation_enabled(handle_));
+ for (size_t i = 0; i < kSizeEnable; ++i) {
+ EXPECT_EQ(0, WebRtc_enable_robust_validation(handle_, kEnable[i]));
+ EXPECT_EQ(kEnable[i], WebRtc_is_robust_validation_enabled(handle_));
+ Init();
+ // Unaffected over a reset.
+ EXPECT_EQ(kEnable[i], WebRtc_is_robust_validation_enabled(handle_));
+ }
+}
+
+TEST_F(DelayEstimatorTest, InitializedSpectrumAfterProcess) {
+ // In this test we verify that the mean spectra are initialized after first
+ // time we call WebRtc_AddFarSpectrum() and Process() respectively. The test
+ // also verifies the state is not left for zero spectra.
+ const float kZerosFloat[kSpectrumSize] = {0.0};
+ const uint16_t kZerosU16[kSpectrumSize] = {0};
+
+ // For floating point operations, process one frame and verify initialization
+ // flag.
+ Init();
+ EXPECT_EQ(0, WebRtc_AddFarSpectrumFloat(farend_handle_, kZerosFloat,
+ spectrum_size_));
+ EXPECT_EQ(0, farend_self_->far_spectrum_initialized);
+ EXPECT_EQ(0,
+ WebRtc_AddFarSpectrumFloat(farend_handle_, far_f_, spectrum_size_));
+ EXPECT_EQ(1, farend_self_->far_spectrum_initialized);
+ EXPECT_EQ(-2, WebRtc_DelayEstimatorProcessFloat(handle_, kZerosFloat,
+ spectrum_size_));
+ EXPECT_EQ(0, self_->near_spectrum_initialized);
+ EXPECT_EQ(
+ -2, WebRtc_DelayEstimatorProcessFloat(handle_, near_f_, spectrum_size_));
+ EXPECT_EQ(1, self_->near_spectrum_initialized);
+
+ // For fixed point operations, process one frame and verify initialization
+ // flag.
+ Init();
+ EXPECT_EQ(0, WebRtc_AddFarSpectrumFix(farend_handle_, kZerosU16,
+ spectrum_size_, 0));
+ EXPECT_EQ(0, farend_self_->far_spectrum_initialized);
+ EXPECT_EQ(
+ 0, WebRtc_AddFarSpectrumFix(farend_handle_, far_u16_, spectrum_size_, 0));
+ EXPECT_EQ(1, farend_self_->far_spectrum_initialized);
+ EXPECT_EQ(-2, WebRtc_DelayEstimatorProcessFix(handle_, kZerosU16,
+ spectrum_size_, 0));
+ EXPECT_EQ(0, self_->near_spectrum_initialized);
+ EXPECT_EQ(-2, WebRtc_DelayEstimatorProcessFix(handle_, near_u16_,
+ spectrum_size_, 0));
+ EXPECT_EQ(1, self_->near_spectrum_initialized);
+}
+
+TEST_F(DelayEstimatorTest, CorrectLastDelay) {
+ // In this test we verify that we get the correct last delay upon valid call.
+ // We simply process the same data until we leave the initialized state
+ // (`last_delay` = -2). Then we compare the Process() output with the
+ // last_delay() call.
+
+ // TODO(bjornv): Update quality values for robust validation.
+ int last_delay = 0;
+ // Floating point operations.
+ Init();
+ for (int i = 0; i < 200; i++) {
+ EXPECT_EQ(
+ 0, WebRtc_AddFarSpectrumFloat(farend_handle_, far_f_, spectrum_size_));
+ last_delay =
+ WebRtc_DelayEstimatorProcessFloat(handle_, near_f_, spectrum_size_);
+ if (last_delay != -2) {
+ EXPECT_EQ(last_delay, WebRtc_last_delay(handle_));
+ if (!WebRtc_is_robust_validation_enabled(handle_)) {
+ EXPECT_FLOAT_EQ(7203.f / kMaxBitCountsQ9,
+ WebRtc_last_delay_quality(handle_));
+ }
+ break;
+ }
+ }
+ // Verify that we have left the initialized state.
+ EXPECT_NE(-2, WebRtc_last_delay(handle_));
+ EXPECT_LT(0, WebRtc_last_delay_quality(handle_));
+
+ // Fixed point operations.
+ Init();
+ for (int i = 0; i < 200; i++) {
+ EXPECT_EQ(0, WebRtc_AddFarSpectrumFix(farend_handle_, far_u16_,
+ spectrum_size_, 0));
+ last_delay =
+ WebRtc_DelayEstimatorProcessFix(handle_, near_u16_, spectrum_size_, 0);
+ if (last_delay != -2) {
+ EXPECT_EQ(last_delay, WebRtc_last_delay(handle_));
+ if (!WebRtc_is_robust_validation_enabled(handle_)) {
+ EXPECT_FLOAT_EQ(7203.f / kMaxBitCountsQ9,
+ WebRtc_last_delay_quality(handle_));
+ }
+ break;
+ }
+ }
+ // Verify that we have left the initialized state.
+ EXPECT_NE(-2, WebRtc_last_delay(handle_));
+ EXPECT_LT(0, WebRtc_last_delay_quality(handle_));
+}
+
+TEST_F(DelayEstimatorTest, CorrectErrorReturnsOfBinaryEstimatorFarend) {
+ // In this test we verify correct output on invalid API calls to the Binary
+ // Delay Estimator (far-end part).
+
+ BinaryDelayEstimatorFarend* binary = binary_farend_;
+ // WebRtc_CreateBinaryDelayEstimatorFarend() should return -1 if the input
+ // history size is less than 2. This is to make sure the buffer shifting
+ // applies properly.
+ // Make sure we have a non-NULL value at start, so we can detect NULL after
+ // create failure.
+ binary = WebRtc_CreateBinaryDelayEstimatorFarend(1);
+ EXPECT_TRUE(binary == NULL);
+}
+
+TEST_F(DelayEstimatorTest, CorrectErrorReturnsOfBinaryEstimator) {
+ // In this test we verify correct output on invalid API calls to the Binary
+ // Delay Estimator.
+
+ BinaryDelayEstimator* binary_handle = binary_;
+ // WebRtc_CreateBinaryDelayEstimator() should return -1 if we have a NULL
+ // pointer as `binary_farend` or invalid input values. Upon failure, the
+ // `binary_handle` should be NULL.
+ // Make sure we have a non-NULL value at start, so we can detect NULL after
+ // create failure.
+ binary_handle = WebRtc_CreateBinaryDelayEstimator(NULL, kLookahead);
+ EXPECT_TRUE(binary_handle == NULL);
+ binary_handle = WebRtc_CreateBinaryDelayEstimator(binary_farend_, -1);
+ EXPECT_TRUE(binary_handle == NULL);
+}
+
+TEST_F(DelayEstimatorTest, MeanEstimatorFix) {
+ // In this test we verify that we update the mean value in correct direction
+ // only. With "direction" we mean increase or decrease.
+
+ int32_t mean_value = 4000;
+ int32_t mean_value_before = mean_value;
+ int32_t new_mean_value = mean_value * 2;
+
+ // Increasing `mean_value`.
+ WebRtc_MeanEstimatorFix(new_mean_value, 10, &mean_value);
+ EXPECT_LT(mean_value_before, mean_value);
+ EXPECT_GT(new_mean_value, mean_value);
+
+ // Decreasing `mean_value`.
+ new_mean_value = mean_value / 2;
+ mean_value_before = mean_value;
+ WebRtc_MeanEstimatorFix(new_mean_value, 10, &mean_value);
+ EXPECT_GT(mean_value_before, mean_value);
+ EXPECT_LT(new_mean_value, mean_value);
+}
+
+TEST_F(DelayEstimatorTest, ExactDelayEstimateMultipleNearSameSpectrum) {
+ // In this test we verify that we get the correct delay estimates if we shift
+ // the signal accordingly. We create two Binary Delay Estimators and feed them
+ // with the same signals, so they should output the same results.
+ // We verify both causal and non-causal delays.
+ // For these noise free signals, the robust validation should not have an
+ // impact, hence we turn robust validation on/off for both reference and
+ // delayed near end.
+
+ for (size_t i = 0; i < kSizeEnable; ++i) {
+ for (size_t j = 0; j < kSizeEnable; ++j) {
+ RunBinarySpectraTest(0, 0, kEnable[i], kEnable[j]);
+ }
+ }
+}
+
+TEST_F(DelayEstimatorTest, ExactDelayEstimateMultipleNearDifferentSpectrum) {
+ // In this test we use the same setup as above, but we now feed the two Binary
+ // Delay Estimators with different signals, so they should output different
+ // results.
+ // For these noise free signals, the robust validation should not have an
+ // impact, hence we turn robust validation on/off for both reference and
+ // delayed near end.
+
+ const int kNearOffset = 1;
+ for (size_t i = 0; i < kSizeEnable; ++i) {
+ for (size_t j = 0; j < kSizeEnable; ++j) {
+ RunBinarySpectraTest(kNearOffset, 0, kEnable[i], kEnable[j]);
+ }
+ }
+}
+
+TEST_F(DelayEstimatorTest, ExactDelayEstimateMultipleNearDifferentLookahead) {
+ // In this test we use the same setup as above, feeding the two Binary
+ // Delay Estimators with the same signals. The difference is that we create
+ // them with different lookahead.
+ // For these noise free signals, the robust validation should not have an
+ // impact, hence we turn robust validation on/off for both reference and
+ // delayed near end.
+
+ const int kLookaheadOffset = 1;
+ for (size_t i = 0; i < kSizeEnable; ++i) {
+ for (size_t j = 0; j < kSizeEnable; ++j) {
+ RunBinarySpectraTest(0, kLookaheadOffset, kEnable[i], kEnable[j]);
+ }
+ }
+}
+
+TEST_F(DelayEstimatorTest, AllowedOffsetNoImpactWhenRobustValidationDisabled) {
+ // The same setup as in ExactDelayEstimateMultipleNearSameSpectrum with the
+ // difference that `allowed_offset` is set for the reference binary delay
+ // estimator.
+
+ binary_->allowed_offset = 10;
+ RunBinarySpectraTest(0, 0, 0, 0);
+ binary_->allowed_offset = 0; // Reset reference.
+}
+
+TEST_F(DelayEstimatorTest, VerifyLookaheadAtCreate) {
+ void* farend_handle =
+ WebRtc_CreateDelayEstimatorFarend(kSpectrumSize, kMaxDelay);
+ ASSERT_TRUE(farend_handle != NULL);
+ void* handle = WebRtc_CreateDelayEstimator(farend_handle, kLookahead);
+ ASSERT_TRUE(handle != NULL);
+ EXPECT_EQ(kLookahead, WebRtc_lookahead(handle));
+ WebRtc_FreeDelayEstimator(handle);
+ WebRtc_FreeDelayEstimatorFarend(farend_handle);
+}
+
+TEST_F(DelayEstimatorTest, VerifyLookaheadIsSetAndKeptAfterInit) {
+ EXPECT_EQ(kLookahead, WebRtc_lookahead(handle_));
+ EXPECT_EQ(kDifferentLookahead,
+ WebRtc_set_lookahead(handle_, kDifferentLookahead));
+ EXPECT_EQ(kDifferentLookahead, WebRtc_lookahead(handle_));
+ EXPECT_EQ(0, WebRtc_InitDelayEstimatorFarend(farend_handle_));
+ EXPECT_EQ(kDifferentLookahead, WebRtc_lookahead(handle_));
+ EXPECT_EQ(0, WebRtc_InitDelayEstimator(handle_));
+ EXPECT_EQ(kDifferentLookahead, WebRtc_lookahead(handle_));
+}
+
+TEST_F(DelayEstimatorTest, VerifyHistorySizeAtCreate) {
+ EXPECT_EQ(kHistorySize, WebRtc_history_size(handle_));
+}
+
+TEST_F(DelayEstimatorTest, VerifyHistorySizeIsSetAndKeptAfterInit) {
+ EXPECT_EQ(kHistorySize, WebRtc_history_size(handle_));
+ EXPECT_EQ(kDifferentHistorySize,
+ WebRtc_set_history_size(handle_, kDifferentHistorySize));
+ EXPECT_EQ(kDifferentHistorySize, WebRtc_history_size(handle_));
+ EXPECT_EQ(0, WebRtc_InitDelayEstimator(handle_));
+ EXPECT_EQ(kDifferentHistorySize, WebRtc_history_size(handle_));
+ EXPECT_EQ(0, WebRtc_InitDelayEstimatorFarend(farend_handle_));
+ EXPECT_EQ(kDifferentHistorySize, WebRtc_history_size(handle_));
+}
+
+// TODO(bjornv): Add tests for SoftReset...(...).
+
+} // namespace
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator_wrapper.cc b/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator_wrapper.cc
new file mode 100644
index 0000000000..3b1409cc0b
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator_wrapper.cc
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 2012 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 "modules/audio_processing/utility/delay_estimator_wrapper.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "modules/audio_processing/utility/delay_estimator.h"
+#include "modules/audio_processing/utility/delay_estimator_internal.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+// Only bit `kBandFirst` through bit `kBandLast` are processed and
+// `kBandFirst` - `kBandLast` must be < 32.
+constexpr int kBandFirst = 12;
+constexpr int kBandLast = 43;
+
+static __inline uint32_t SetBit(uint32_t in, int pos) {
+ uint32_t mask = (1 << pos);
+ uint32_t out = (in | mask);
+
+ return out;
+}
+
+// Calculates the mean recursively. Same version as WebRtc_MeanEstimatorFix(),
+// but for float.
+//
+// Inputs:
+// - new_value : New additional value.
+// - scale : Scale for smoothing (should be less than 1.0).
+//
+// Input/Output:
+// - mean_value : Pointer to the mean value for updating.
+//
+static void MeanEstimatorFloat(float new_value,
+ float scale,
+ float* mean_value) {
+ RTC_DCHECK_LT(scale, 1.0f);
+ *mean_value += (new_value - *mean_value) * scale;
+}
+
+// Computes the binary spectrum by comparing the input `spectrum` with a
+// `threshold_spectrum`. Float and fixed point versions.
+//
+// Inputs:
+// - spectrum : Spectrum of which the binary spectrum should be
+// calculated.
+// - threshold_spectrum : Threshold spectrum with which the input
+// spectrum is compared.
+// Return:
+// - out : Binary spectrum.
+//
+static uint32_t BinarySpectrumFix(const uint16_t* spectrum,
+ SpectrumType* threshold_spectrum,
+ int q_domain,
+ int* threshold_initialized) {
+ int i = kBandFirst;
+ uint32_t out = 0;
+
+ RTC_DCHECK_LT(q_domain, 16);
+
+ if (!(*threshold_initialized)) {
+ // Set the `threshold_spectrum` to half the input `spectrum` as starting
+ // value. This speeds up the convergence.
+ for (i = kBandFirst; i <= kBandLast; i++) {
+ if (spectrum[i] > 0) {
+ // Convert input spectrum from Q(`q_domain`) to Q15.
+ int32_t spectrum_q15 = ((int32_t)spectrum[i]) << (15 - q_domain);
+ threshold_spectrum[i].int32_ = (spectrum_q15 >> 1);
+ *threshold_initialized = 1;
+ }
+ }
+ }
+ for (i = kBandFirst; i <= kBandLast; i++) {
+ // Convert input spectrum from Q(`q_domain`) to Q15.
+ int32_t spectrum_q15 = ((int32_t)spectrum[i]) << (15 - q_domain);
+ // Update the `threshold_spectrum`.
+ WebRtc_MeanEstimatorFix(spectrum_q15, 6, &(threshold_spectrum[i].int32_));
+ // Convert `spectrum` at current frequency bin to a binary value.
+ if (spectrum_q15 > threshold_spectrum[i].int32_) {
+ out = SetBit(out, i - kBandFirst);
+ }
+ }
+
+ return out;
+}
+
+static uint32_t BinarySpectrumFloat(const float* spectrum,
+ SpectrumType* threshold_spectrum,
+ int* threshold_initialized) {
+ int i = kBandFirst;
+ uint32_t out = 0;
+ const float kScale = 1 / 64.0;
+
+ if (!(*threshold_initialized)) {
+ // Set the `threshold_spectrum` to half the input `spectrum` as starting
+ // value. This speeds up the convergence.
+ for (i = kBandFirst; i <= kBandLast; i++) {
+ if (spectrum[i] > 0.0f) {
+ threshold_spectrum[i].float_ = (spectrum[i] / 2);
+ *threshold_initialized = 1;
+ }
+ }
+ }
+
+ for (i = kBandFirst; i <= kBandLast; i++) {
+ // Update the `threshold_spectrum`.
+ MeanEstimatorFloat(spectrum[i], kScale, &(threshold_spectrum[i].float_));
+ // Convert `spectrum` at current frequency bin to a binary value.
+ if (spectrum[i] > threshold_spectrum[i].float_) {
+ out = SetBit(out, i - kBandFirst);
+ }
+ }
+
+ return out;
+}
+
+void WebRtc_FreeDelayEstimatorFarend(void* handle) {
+ DelayEstimatorFarend* self = (DelayEstimatorFarend*)handle;
+
+ if (handle == NULL) {
+ return;
+ }
+
+ free(self->mean_far_spectrum);
+ self->mean_far_spectrum = NULL;
+
+ WebRtc_FreeBinaryDelayEstimatorFarend(self->binary_farend);
+ self->binary_farend = NULL;
+
+ free(self);
+}
+
+void* WebRtc_CreateDelayEstimatorFarend(int spectrum_size, int history_size) {
+ DelayEstimatorFarend* self = NULL;
+
+ // Check if the sub band used in the delay estimation is small enough to fit
+ // the binary spectra in a uint32_t.
+ static_assert(kBandLast - kBandFirst < 32, "");
+
+ if (spectrum_size >= kBandLast) {
+ self = static_cast<DelayEstimatorFarend*>(
+ malloc(sizeof(DelayEstimatorFarend)));
+ }
+
+ if (self != NULL) {
+ int memory_fail = 0;
+
+ // Allocate memory for the binary far-end spectrum handling.
+ self->binary_farend = WebRtc_CreateBinaryDelayEstimatorFarend(history_size);
+ memory_fail |= (self->binary_farend == NULL);
+
+ // Allocate memory for spectrum buffers.
+ self->mean_far_spectrum = static_cast<SpectrumType*>(
+ malloc(spectrum_size * sizeof(SpectrumType)));
+ memory_fail |= (self->mean_far_spectrum == NULL);
+
+ self->spectrum_size = spectrum_size;
+
+ if (memory_fail) {
+ WebRtc_FreeDelayEstimatorFarend(self);
+ self = NULL;
+ }
+ }
+
+ return self;
+}
+
+int WebRtc_InitDelayEstimatorFarend(void* handle) {
+ DelayEstimatorFarend* self = (DelayEstimatorFarend*)handle;
+
+ if (self == NULL) {
+ return -1;
+ }
+
+ // Initialize far-end part of binary delay estimator.
+ WebRtc_InitBinaryDelayEstimatorFarend(self->binary_farend);
+
+ // Set averaged far and near end spectra to zero.
+ memset(self->mean_far_spectrum, 0,
+ sizeof(SpectrumType) * self->spectrum_size);
+ // Reset initialization indicators.
+ self->far_spectrum_initialized = 0;
+
+ return 0;
+}
+
+void WebRtc_SoftResetDelayEstimatorFarend(void* handle, int delay_shift) {
+ DelayEstimatorFarend* self = (DelayEstimatorFarend*)handle;
+ RTC_DCHECK(self);
+ WebRtc_SoftResetBinaryDelayEstimatorFarend(self->binary_farend, delay_shift);
+}
+
+int WebRtc_AddFarSpectrumFix(void* handle,
+ const uint16_t* far_spectrum,
+ int spectrum_size,
+ int far_q) {
+ DelayEstimatorFarend* self = (DelayEstimatorFarend*)handle;
+ uint32_t binary_spectrum = 0;
+
+ if (self == NULL) {
+ return -1;
+ }
+ if (far_spectrum == NULL) {
+ // Empty far end spectrum.
+ return -1;
+ }
+ if (spectrum_size != self->spectrum_size) {
+ // Data sizes don't match.
+ return -1;
+ }
+ if (far_q > 15) {
+ // If `far_q` is larger than 15 we cannot guarantee no wrap around.
+ return -1;
+ }
+
+ // Get binary spectrum.
+ binary_spectrum = BinarySpectrumFix(far_spectrum, self->mean_far_spectrum,
+ far_q, &(self->far_spectrum_initialized));
+ WebRtc_AddBinaryFarSpectrum(self->binary_farend, binary_spectrum);
+
+ return 0;
+}
+
+int WebRtc_AddFarSpectrumFloat(void* handle,
+ const float* far_spectrum,
+ int spectrum_size) {
+ DelayEstimatorFarend* self = (DelayEstimatorFarend*)handle;
+ uint32_t binary_spectrum = 0;
+
+ if (self == NULL) {
+ return -1;
+ }
+ if (far_spectrum == NULL) {
+ // Empty far end spectrum.
+ return -1;
+ }
+ if (spectrum_size != self->spectrum_size) {
+ // Data sizes don't match.
+ return -1;
+ }
+
+ // Get binary spectrum.
+ binary_spectrum = BinarySpectrumFloat(far_spectrum, self->mean_far_spectrum,
+ &(self->far_spectrum_initialized));
+ WebRtc_AddBinaryFarSpectrum(self->binary_farend, binary_spectrum);
+
+ return 0;
+}
+
+void WebRtc_FreeDelayEstimator(void* handle) {
+ DelayEstimator* self = (DelayEstimator*)handle;
+
+ if (handle == NULL) {
+ return;
+ }
+
+ free(self->mean_near_spectrum);
+ self->mean_near_spectrum = NULL;
+
+ WebRtc_FreeBinaryDelayEstimator(self->binary_handle);
+ self->binary_handle = NULL;
+
+ free(self);
+}
+
+void* WebRtc_CreateDelayEstimator(void* farend_handle, int max_lookahead) {
+ DelayEstimator* self = NULL;
+ DelayEstimatorFarend* farend = (DelayEstimatorFarend*)farend_handle;
+
+ if (farend_handle != NULL) {
+ self = static_cast<DelayEstimator*>(malloc(sizeof(DelayEstimator)));
+ }
+
+ if (self != NULL) {
+ int memory_fail = 0;
+
+ // Allocate memory for the farend spectrum handling.
+ self->binary_handle =
+ WebRtc_CreateBinaryDelayEstimator(farend->binary_farend, max_lookahead);
+ memory_fail |= (self->binary_handle == NULL);
+
+ // Allocate memory for spectrum buffers.
+ self->mean_near_spectrum = static_cast<SpectrumType*>(
+ malloc(farend->spectrum_size * sizeof(SpectrumType)));
+ memory_fail |= (self->mean_near_spectrum == NULL);
+
+ self->spectrum_size = farend->spectrum_size;
+
+ if (memory_fail) {
+ WebRtc_FreeDelayEstimator(self);
+ self = NULL;
+ }
+ }
+
+ return self;
+}
+
+int WebRtc_InitDelayEstimator(void* handle) {
+ DelayEstimator* self = (DelayEstimator*)handle;
+
+ if (self == NULL) {
+ return -1;
+ }
+
+ // Initialize binary delay estimator.
+ WebRtc_InitBinaryDelayEstimator(self->binary_handle);
+
+ // Set averaged far and near end spectra to zero.
+ memset(self->mean_near_spectrum, 0,
+ sizeof(SpectrumType) * self->spectrum_size);
+ // Reset initialization indicators.
+ self->near_spectrum_initialized = 0;
+
+ return 0;
+}
+
+int WebRtc_SoftResetDelayEstimator(void* handle, int delay_shift) {
+ DelayEstimator* self = (DelayEstimator*)handle;
+ RTC_DCHECK(self);
+ return WebRtc_SoftResetBinaryDelayEstimator(self->binary_handle, delay_shift);
+}
+
+int WebRtc_set_history_size(void* handle, int history_size) {
+ DelayEstimator* self = static_cast<DelayEstimator*>(handle);
+
+ if ((self == NULL) || (history_size <= 1)) {
+ return -1;
+ }
+ return WebRtc_AllocateHistoryBufferMemory(self->binary_handle, history_size);
+}
+
+int WebRtc_history_size(const void* handle) {
+ const DelayEstimator* self = static_cast<const DelayEstimator*>(handle);
+
+ if (self == NULL) {
+ return -1;
+ }
+ if (self->binary_handle->farend->history_size !=
+ self->binary_handle->history_size) {
+ // Non matching history sizes.
+ return -1;
+ }
+ return self->binary_handle->history_size;
+}
+
+int WebRtc_set_lookahead(void* handle, int lookahead) {
+ DelayEstimator* self = (DelayEstimator*)handle;
+ RTC_DCHECK(self);
+ RTC_DCHECK(self->binary_handle);
+ if ((lookahead > self->binary_handle->near_history_size - 1) ||
+ (lookahead < 0)) {
+ return -1;
+ }
+ self->binary_handle->lookahead = lookahead;
+ return self->binary_handle->lookahead;
+}
+
+int WebRtc_lookahead(void* handle) {
+ DelayEstimator* self = (DelayEstimator*)handle;
+ RTC_DCHECK(self);
+ RTC_DCHECK(self->binary_handle);
+ return self->binary_handle->lookahead;
+}
+
+int WebRtc_set_allowed_offset(void* handle, int allowed_offset) {
+ DelayEstimator* self = (DelayEstimator*)handle;
+
+ if ((self == NULL) || (allowed_offset < 0)) {
+ return -1;
+ }
+ self->binary_handle->allowed_offset = allowed_offset;
+ return 0;
+}
+
+int WebRtc_get_allowed_offset(const void* handle) {
+ const DelayEstimator* self = (const DelayEstimator*)handle;
+
+ if (self == NULL) {
+ return -1;
+ }
+ return self->binary_handle->allowed_offset;
+}
+
+int WebRtc_enable_robust_validation(void* handle, int enable) {
+ DelayEstimator* self = (DelayEstimator*)handle;
+
+ if (self == NULL) {
+ return -1;
+ }
+ if ((enable < 0) || (enable > 1)) {
+ return -1;
+ }
+ RTC_DCHECK(self->binary_handle);
+ self->binary_handle->robust_validation_enabled = enable;
+ return 0;
+}
+
+int WebRtc_is_robust_validation_enabled(const void* handle) {
+ const DelayEstimator* self = (const DelayEstimator*)handle;
+
+ if (self == NULL) {
+ return -1;
+ }
+ return self->binary_handle->robust_validation_enabled;
+}
+
+int WebRtc_DelayEstimatorProcessFix(void* handle,
+ const uint16_t* near_spectrum,
+ int spectrum_size,
+ int near_q) {
+ DelayEstimator* self = (DelayEstimator*)handle;
+ uint32_t binary_spectrum = 0;
+
+ if (self == NULL) {
+ return -1;
+ }
+ if (near_spectrum == NULL) {
+ // Empty near end spectrum.
+ return -1;
+ }
+ if (spectrum_size != self->spectrum_size) {
+ // Data sizes don't match.
+ return -1;
+ }
+ if (near_q > 15) {
+ // If `near_q` is larger than 15 we cannot guarantee no wrap around.
+ return -1;
+ }
+
+ // Get binary spectra.
+ binary_spectrum =
+ BinarySpectrumFix(near_spectrum, self->mean_near_spectrum, near_q,
+ &(self->near_spectrum_initialized));
+
+ return WebRtc_ProcessBinarySpectrum(self->binary_handle, binary_spectrum);
+}
+
+int WebRtc_DelayEstimatorProcessFloat(void* handle,
+ const float* near_spectrum,
+ int spectrum_size) {
+ DelayEstimator* self = (DelayEstimator*)handle;
+ uint32_t binary_spectrum = 0;
+
+ if (self == NULL) {
+ return -1;
+ }
+ if (near_spectrum == NULL) {
+ // Empty near end spectrum.
+ return -1;
+ }
+ if (spectrum_size != self->spectrum_size) {
+ // Data sizes don't match.
+ return -1;
+ }
+
+ // Get binary spectrum.
+ binary_spectrum = BinarySpectrumFloat(near_spectrum, self->mean_near_spectrum,
+ &(self->near_spectrum_initialized));
+
+ return WebRtc_ProcessBinarySpectrum(self->binary_handle, binary_spectrum);
+}
+
+int WebRtc_last_delay(void* handle) {
+ DelayEstimator* self = (DelayEstimator*)handle;
+
+ if (self == NULL) {
+ return -1;
+ }
+
+ return WebRtc_binary_last_delay(self->binary_handle);
+}
+
+float WebRtc_last_delay_quality(void* handle) {
+ DelayEstimator* self = (DelayEstimator*)handle;
+ RTC_DCHECK(self);
+ return WebRtc_binary_last_delay_quality(self->binary_handle);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator_wrapper.h b/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator_wrapper.h
new file mode 100644
index 0000000000..a90cbe31cb
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator_wrapper.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2012 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.
+ */
+
+// Performs delay estimation on block by block basis.
+// The return value is 0 - OK and -1 - Error, unless otherwise stated.
+
+#ifndef MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_WRAPPER_H_
+#define MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_WRAPPER_H_
+
+#include <stdint.h>
+
+namespace webrtc {
+
+// Releases the memory allocated by WebRtc_CreateDelayEstimatorFarend(...)
+void WebRtc_FreeDelayEstimatorFarend(void* handle);
+
+// Allocates the memory needed by the far-end part of the delay estimation. The
+// memory needs to be initialized separately through
+// WebRtc_InitDelayEstimatorFarend(...).
+//
+// Inputs:
+// - spectrum_size : Size of the spectrum used both in far-end and
+// near-end. Used to allocate memory for spectrum
+// specific buffers.
+// - history_size : The far-end history buffer size. A change in buffer
+// size can be forced with WebRtc_set_history_size().
+// Note that the maximum delay which can be estimated is
+// determined together with WebRtc_set_lookahead().
+//
+// Return value:
+// - void* : Created `handle`. If the memory can't be allocated or
+// if any of the input parameters are invalid NULL is
+// returned.
+void* WebRtc_CreateDelayEstimatorFarend(int spectrum_size, int history_size);
+
+// Initializes the far-end part of the delay estimation instance returned by
+// WebRtc_CreateDelayEstimatorFarend(...)
+int WebRtc_InitDelayEstimatorFarend(void* handle);
+
+// Soft resets the far-end part of the delay estimation instance returned by
+// WebRtc_CreateDelayEstimatorFarend(...).
+// Input:
+// - delay_shift : The amount of blocks to shift history buffers.
+void WebRtc_SoftResetDelayEstimatorFarend(void* handle, int delay_shift);
+
+// Adds the far-end spectrum to the far-end history buffer. This spectrum is
+// used as reference when calculating the delay using
+// WebRtc_ProcessSpectrum().
+//
+// Inputs:
+// - far_spectrum : Far-end spectrum.
+// - spectrum_size : The size of the data arrays (same for both far- and
+// near-end).
+// - far_q : The Q-domain of the far-end data.
+//
+// Output:
+// - handle : Updated far-end instance.
+//
+int WebRtc_AddFarSpectrumFix(void* handle,
+ const uint16_t* far_spectrum,
+ int spectrum_size,
+ int far_q);
+
+// See WebRtc_AddFarSpectrumFix() for description.
+int WebRtc_AddFarSpectrumFloat(void* handle,
+ const float* far_spectrum,
+ int spectrum_size);
+
+// Releases the memory allocated by WebRtc_CreateDelayEstimator(...)
+void WebRtc_FreeDelayEstimator(void* handle);
+
+// Allocates the memory needed by the delay estimation. The memory needs to be
+// initialized separately through WebRtc_InitDelayEstimator(...).
+//
+// Inputs:
+// - farend_handle : Pointer to the far-end part of the delay estimation
+// instance created prior to this call using
+// WebRtc_CreateDelayEstimatorFarend().
+//
+// Note that WebRtc_CreateDelayEstimator does not take
+// ownership of `farend_handle`, which has to be torn
+// down properly after this instance.
+//
+// - max_lookahead : Maximum amount of non-causal lookahead allowed. The
+// actual amount of lookahead used can be controlled by
+// WebRtc_set_lookahead(...). The default `lookahead` is
+// set to `max_lookahead` at create time. Use
+// WebRtc_set_lookahead(...) before start if a different
+// value is desired.
+//
+// Using lookahead can detect cases in which a near-end
+// signal occurs before the corresponding far-end signal.
+// It will delay the estimate for the current block by an
+// equal amount, and the returned values will be offset
+// by it.
+//
+// A value of zero is the typical no-lookahead case.
+// This also represents the minimum delay which can be
+// estimated.
+//
+// Note that the effective range of delay estimates is
+// [-`lookahead`,... ,`history_size`-`lookahead`)
+// where `history_size` is set through
+// WebRtc_set_history_size().
+//
+// Return value:
+// - void* : Created `handle`. If the memory can't be allocated or
+// if any of the input parameters are invalid NULL is
+// returned.
+void* WebRtc_CreateDelayEstimator(void* farend_handle, int max_lookahead);
+
+// Initializes the delay estimation instance returned by
+// WebRtc_CreateDelayEstimator(...)
+int WebRtc_InitDelayEstimator(void* handle);
+
+// Soft resets the delay estimation instance returned by
+// WebRtc_CreateDelayEstimator(...)
+// Input:
+// - delay_shift : The amount of blocks to shift history buffers.
+//
+// Return value:
+// - actual_shifts : The actual number of shifts performed.
+int WebRtc_SoftResetDelayEstimator(void* handle, int delay_shift);
+
+// Sets the effective `history_size` used. Valid values from 2. We simply need
+// at least two delays to compare to perform an estimate. If `history_size` is
+// changed, buffers are reallocated filling in with zeros if necessary.
+// Note that changing the `history_size` affects both buffers in far-end and
+// near-end. Hence it is important to change all DelayEstimators that use the
+// same reference far-end, to the same `history_size` value.
+// Inputs:
+// - handle : Pointer to the delay estimation instance.
+// - history_size : Effective history size to be used.
+// Return value:
+// - new_history_size : The new history size used. If the memory was not able
+// to be allocated 0 is returned.
+int WebRtc_set_history_size(void* handle, int history_size);
+
+// Returns the history_size currently used.
+// Input:
+// - handle : Pointer to the delay estimation instance.
+int WebRtc_history_size(const void* handle);
+
+// Sets the amount of `lookahead` to use. Valid values are [0, max_lookahead]
+// where `max_lookahead` was set at create time through
+// WebRtc_CreateDelayEstimator(...).
+//
+// Input:
+// - handle : Pointer to the delay estimation instance.
+// - lookahead : The amount of lookahead to be used.
+//
+// Return value:
+// - new_lookahead : The actual amount of lookahead set, unless `handle` is
+// a NULL pointer or `lookahead` is invalid, for which an
+// error is returned.
+int WebRtc_set_lookahead(void* handle, int lookahead);
+
+// Returns the amount of lookahead we currently use.
+// Input:
+// - handle : Pointer to the delay estimation instance.
+int WebRtc_lookahead(void* handle);
+
+// Sets the `allowed_offset` used in the robust validation scheme. If the
+// delay estimator is used in an echo control component, this parameter is
+// related to the filter length. In principle `allowed_offset` should be set to
+// the echo control filter length minus the expected echo duration, i.e., the
+// delay offset the echo control can handle without quality regression. The
+// default value, used if not set manually, is zero. Note that `allowed_offset`
+// has to be non-negative.
+// Inputs:
+// - handle : Pointer to the delay estimation instance.
+// - allowed_offset : The amount of delay offset, measured in partitions,
+// the echo control filter can handle.
+int WebRtc_set_allowed_offset(void* handle, int allowed_offset);
+
+// Returns the `allowed_offset` in number of partitions.
+int WebRtc_get_allowed_offset(const void* handle);
+
+// Enables/Disables a robust validation functionality in the delay estimation.
+// This is by default set to disabled at create time. The state is preserved
+// over a reset.
+// Inputs:
+// - handle : Pointer to the delay estimation instance.
+// - enable : Enable (1) or disable (0) this feature.
+int WebRtc_enable_robust_validation(void* handle, int enable);
+
+// Returns 1 if robust validation is enabled and 0 if disabled.
+int WebRtc_is_robust_validation_enabled(const void* handle);
+
+// Estimates and returns the delay between the far-end and near-end blocks. The
+// value will be offset by the lookahead (i.e. the lookahead should be
+// subtracted from the returned value).
+// Inputs:
+// - handle : Pointer to the delay estimation instance.
+// - near_spectrum : Pointer to the near-end spectrum data of the current
+// block.
+// - spectrum_size : The size of the data arrays (same for both far- and
+// near-end).
+// - near_q : The Q-domain of the near-end data.
+//
+// Output:
+// - handle : Updated instance.
+//
+// Return value:
+// - delay : >= 0 - Calculated delay value.
+// -1 - Error.
+// -2 - Insufficient data for estimation.
+int WebRtc_DelayEstimatorProcessFix(void* handle,
+ const uint16_t* near_spectrum,
+ int spectrum_size,
+ int near_q);
+
+// See WebRtc_DelayEstimatorProcessFix() for description.
+int WebRtc_DelayEstimatorProcessFloat(void* handle,
+ const float* near_spectrum,
+ int spectrum_size);
+
+// Returns the last calculated delay updated by the function
+// WebRtc_DelayEstimatorProcess(...).
+//
+// Input:
+// - handle : Pointer to the delay estimation instance.
+//
+// Return value:
+// - delay : >= 0 - Last calculated delay value.
+// -1 - Error.
+// -2 - Insufficient data for estimation.
+int WebRtc_last_delay(void* handle);
+
+// Returns the estimation quality/probability of the last calculated delay
+// updated by the function WebRtc_DelayEstimatorProcess(...). The estimation
+// quality is a value in the interval [0, 1]. The higher the value, the better
+// the quality.
+//
+// Return value:
+// - delay_quality : >= 0 - Estimation quality of last calculated delay.
+float WebRtc_last_delay_quality(void* handle);
+
+} // namespace webrtc
+
+#endif // MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_WRAPPER_H_
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/legacy_delay_estimator_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/utility/legacy_delay_estimator_gn/moz.build
new file mode 100644
index 0000000000..11294e2aef
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/utility/legacy_delay_estimator_gn/moz.build
@@ -0,0 +1,222 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "!/third_party/libwebrtc/gen",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator.cc",
+ "/third_party/libwebrtc/modules/audio_processing/utility/delay_estimator_wrapper.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["ANDROID"] = True
+ DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1"
+ DEFINES["HAVE_SYS_UIO_H"] = True
+ DEFINES["WEBRTC_ANDROID"] = True
+ DEFINES["WEBRTC_ANDROID_OPENSLES"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_GNU_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "log"
+ ]
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+if CONFIG["CPU_ARCH"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "arm":
+
+ CXXFLAGS += [
+ "-mfpu=neon"
+ ]
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "mips32":
+
+ DEFINES["MIPS32_LE"] = True
+ DEFINES["MIPS_FPU_LE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "mips64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Android":
+
+ OS_LIBS += [
+ "android_support",
+ "unwind"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ OS_LIBS += [
+ "android_support"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+Library("legacy_delay_estimator_gn")
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper.cc b/third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper.cc
new file mode 100644
index 0000000000..88642fb12b
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper.cc
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2019 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 "modules/audio_processing/utility/pffft_wrapper.h"
+
+#include "rtc_base/checks.h"
+#include "third_party/pffft/src/pffft.h"
+
+namespace webrtc {
+namespace {
+
+size_t GetBufferSize(size_t fft_size, Pffft::FftType fft_type) {
+ return fft_size * (fft_type == Pffft::FftType::kReal ? 1 : 2);
+}
+
+float* AllocatePffftBuffer(size_t size) {
+ return static_cast<float*>(pffft_aligned_malloc(size * sizeof(float)));
+}
+
+} // namespace
+
+Pffft::FloatBuffer::FloatBuffer(size_t fft_size, FftType fft_type)
+ : size_(GetBufferSize(fft_size, fft_type)),
+ data_(AllocatePffftBuffer(size_)) {}
+
+Pffft::FloatBuffer::~FloatBuffer() {
+ pffft_aligned_free(data_);
+}
+
+rtc::ArrayView<const float> Pffft::FloatBuffer::GetConstView() const {
+ return {data_, size_};
+}
+
+rtc::ArrayView<float> Pffft::FloatBuffer::GetView() {
+ return {data_, size_};
+}
+
+Pffft::Pffft(size_t fft_size, FftType fft_type)
+ : fft_size_(fft_size),
+ fft_type_(fft_type),
+ pffft_status_(pffft_new_setup(
+ fft_size_,
+ fft_type == Pffft::FftType::kReal ? PFFFT_REAL : PFFFT_COMPLEX)),
+ scratch_buffer_(
+ AllocatePffftBuffer(GetBufferSize(fft_size_, fft_type_))) {
+ RTC_DCHECK(pffft_status_);
+ RTC_DCHECK(scratch_buffer_);
+}
+
+Pffft::~Pffft() {
+ pffft_destroy_setup(pffft_status_);
+ pffft_aligned_free(scratch_buffer_);
+}
+
+bool Pffft::IsValidFftSize(size_t fft_size, FftType fft_type) {
+ if (fft_size == 0) {
+ return false;
+ }
+ // PFFFT only supports transforms for inputs of length N of the form
+ // N = (2^a)*(3^b)*(5^c) where b >=0 and c >= 0 and a >= 5 for the real FFT
+ // and a >= 4 for the complex FFT.
+ constexpr int kFactors[] = {2, 3, 5};
+ int factorization[] = {0, 0, 0};
+ int n = static_cast<int>(fft_size);
+ for (int i = 0; i < 3; ++i) {
+ while (n % kFactors[i] == 0) {
+ n = n / kFactors[i];
+ factorization[i]++;
+ }
+ }
+ int a_min = (fft_type == Pffft::FftType::kReal) ? 5 : 4;
+ return factorization[0] >= a_min && n == 1;
+}
+
+bool Pffft::IsSimdEnabled() {
+ return pffft_simd_size() > 1;
+}
+
+std::unique_ptr<Pffft::FloatBuffer> Pffft::CreateBuffer() const {
+ // Cannot use make_unique from absl because Pffft is the only friend of
+ // Pffft::FloatBuffer.
+ std::unique_ptr<Pffft::FloatBuffer> buffer(
+ new Pffft::FloatBuffer(fft_size_, fft_type_));
+ return buffer;
+}
+
+void Pffft::ForwardTransform(const FloatBuffer& in,
+ FloatBuffer* out,
+ bool ordered) {
+ RTC_DCHECK_EQ(in.size(), GetBufferSize(fft_size_, fft_type_));
+ RTC_DCHECK_EQ(in.size(), out->size());
+ RTC_DCHECK(scratch_buffer_);
+ if (ordered) {
+ pffft_transform_ordered(pffft_status_, in.const_data(), out->data(),
+ scratch_buffer_, PFFFT_FORWARD);
+ } else {
+ pffft_transform(pffft_status_, in.const_data(), out->data(),
+ scratch_buffer_, PFFFT_FORWARD);
+ }
+}
+
+void Pffft::BackwardTransform(const FloatBuffer& in,
+ FloatBuffer* out,
+ bool ordered) {
+ RTC_DCHECK_EQ(in.size(), GetBufferSize(fft_size_, fft_type_));
+ RTC_DCHECK_EQ(in.size(), out->size());
+ RTC_DCHECK(scratch_buffer_);
+ if (ordered) {
+ pffft_transform_ordered(pffft_status_, in.const_data(), out->data(),
+ scratch_buffer_, PFFFT_BACKWARD);
+ } else {
+ pffft_transform(pffft_status_, in.const_data(), out->data(),
+ scratch_buffer_, PFFFT_BACKWARD);
+ }
+}
+
+void Pffft::FrequencyDomainConvolve(const FloatBuffer& fft_x,
+ const FloatBuffer& fft_y,
+ FloatBuffer* out,
+ float scaling) {
+ RTC_DCHECK_EQ(fft_x.size(), GetBufferSize(fft_size_, fft_type_));
+ RTC_DCHECK_EQ(fft_x.size(), fft_y.size());
+ RTC_DCHECK_EQ(fft_x.size(), out->size());
+ pffft_zconvolve_accumulate(pffft_status_, fft_x.const_data(),
+ fft_y.const_data(), out->data(), scaling);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper.h b/third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper.h
new file mode 100644
index 0000000000..983c2fd1bc
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2019 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 MODULES_AUDIO_PROCESSING_UTILITY_PFFFT_WRAPPER_H_
+#define MODULES_AUDIO_PROCESSING_UTILITY_PFFFT_WRAPPER_H_
+
+#include <memory>
+
+#include "api/array_view.h"
+
+// Forward declaration.
+struct PFFFT_Setup;
+
+namespace webrtc {
+
+// Pretty-Fast Fast Fourier Transform (PFFFT) wrapper class.
+// Not thread safe.
+class Pffft {
+ public:
+ enum class FftType { kReal, kComplex };
+
+ // 1D floating point buffer used as input/output data type for the FFT ops.
+ // It must be constructed using Pffft::CreateBuffer().
+ class FloatBuffer {
+ public:
+ FloatBuffer(const FloatBuffer&) = delete;
+ FloatBuffer& operator=(const FloatBuffer&) = delete;
+ ~FloatBuffer();
+
+ rtc::ArrayView<const float> GetConstView() const;
+ rtc::ArrayView<float> GetView();
+
+ private:
+ friend class Pffft;
+ FloatBuffer(size_t fft_size, FftType fft_type);
+ const float* const_data() const { return data_; }
+ float* data() { return data_; }
+ size_t size() const { return size_; }
+
+ const size_t size_;
+ float* const data_;
+ };
+
+ // TODO(https://crbug.com/webrtc/9577): Consider adding a factory and making
+ // the ctor private.
+ // static std::unique_ptr<Pffft> Create(size_t fft_size,
+ // FftType fft_type); Ctor. `fft_size` must be a supported size (see
+ // Pffft::IsValidFftSize()). If not supported, the code will crash.
+ Pffft(size_t fft_size, FftType fft_type);
+ Pffft(const Pffft&) = delete;
+ Pffft& operator=(const Pffft&) = delete;
+ ~Pffft();
+
+ // Returns true if the FFT size is supported.
+ static bool IsValidFftSize(size_t fft_size, FftType fft_type);
+
+ // Returns true if SIMD code optimizations are being used.
+ static bool IsSimdEnabled();
+
+ // Creates a buffer of the right size.
+ std::unique_ptr<FloatBuffer> CreateBuffer() const;
+
+ // TODO(https://crbug.com/webrtc/9577): Overload with rtc::ArrayView args.
+ // Computes the forward fast Fourier transform.
+ void ForwardTransform(const FloatBuffer& in, FloatBuffer* out, bool ordered);
+ // Computes the backward fast Fourier transform.
+ void BackwardTransform(const FloatBuffer& in, FloatBuffer* out, bool ordered);
+
+ // Multiplies the frequency components of `fft_x` and `fft_y` and accumulates
+ // them into `out`. The arrays must have been obtained with
+ // ForwardTransform(..., /*ordered=*/false) - i.e., `fft_x` and `fft_y` must
+ // not be ordered.
+ void FrequencyDomainConvolve(const FloatBuffer& fft_x,
+ const FloatBuffer& fft_y,
+ FloatBuffer* out,
+ float scaling = 1.f);
+
+ private:
+ const size_t fft_size_;
+ const FftType fft_type_;
+ PFFFT_Setup* pffft_status_;
+ float* const scratch_buffer_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_AUDIO_PROCESSING_UTILITY_PFFFT_WRAPPER_H_
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper_gn/moz.build
new file mode 100644
index 0000000000..02898359bf
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper_gn/moz.build
@@ -0,0 +1,221 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "!/third_party/libwebrtc/gen",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["ANDROID"] = True
+ DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1"
+ DEFINES["HAVE_SYS_UIO_H"] = True
+ DEFINES["WEBRTC_ANDROID"] = True
+ DEFINES["WEBRTC_ANDROID_OPENSLES"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_GNU_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "log"
+ ]
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+if CONFIG["CPU_ARCH"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "arm":
+
+ CXXFLAGS += [
+ "-mfpu=neon"
+ ]
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "mips32":
+
+ DEFINES["MIPS32_LE"] = True
+ DEFINES["MIPS_FPU_LE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "mips64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Android":
+
+ OS_LIBS += [
+ "android_support",
+ "unwind"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ OS_LIBS += [
+ "android_support"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+Library("pffft_wrapper_gn")
diff --git a/third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper_unittest.cc b/third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper_unittest.cc
new file mode 100644
index 0000000000..2ad6849cd4
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/utility/pffft_wrapper_unittest.cc
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2019 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 "modules/audio_processing/utility/pffft_wrapper.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <memory>
+
+#include "test/gtest.h"
+#include "third_party/pffft/src/pffft.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+
+constexpr size_t kMaxValidSizeCheck = 1024;
+
+static constexpr int kFftSizes[] = {
+ 16, 32, 64, 96, 128, 160, 192, 256, 288, 384, 5 * 96, 512,
+ 576, 5 * 128, 800, 864, 1024, 2048, 2592, 4000, 4096, 12000, 36864};
+
+void CreatePffftWrapper(size_t fft_size, Pffft::FftType fft_type) {
+ Pffft pffft_wrapper(fft_size, fft_type);
+}
+
+float* AllocateScratchBuffer(size_t fft_size, bool complex_fft) {
+ return static_cast<float*>(
+ pffft_aligned_malloc(fft_size * (complex_fft ? 2 : 1) * sizeof(float)));
+}
+
+double frand() {
+ return std::rand() / static_cast<double>(RAND_MAX);
+}
+
+void ExpectArrayViewsEquality(rtc::ArrayView<const float> a,
+ rtc::ArrayView<const float> b) {
+ ASSERT_EQ(a.size(), b.size());
+ for (size_t i = 0; i < a.size(); ++i) {
+ SCOPED_TRACE(i);
+ EXPECT_EQ(a[i], b[i]);
+ }
+}
+
+// Compares the output of the PFFFT C++ wrapper to that of the C PFFFT.
+// Bit-exactness is expected.
+void PffftValidateWrapper(size_t fft_size, bool complex_fft) {
+ // Always use the same seed to avoid flakiness.
+ std::srand(0);
+
+ // Init PFFFT.
+ PFFFT_Setup* pffft_status =
+ pffft_new_setup(fft_size, complex_fft ? PFFFT_COMPLEX : PFFFT_REAL);
+ ASSERT_TRUE(pffft_status) << "FFT size (" << fft_size << ") not supported.";
+ size_t num_floats = fft_size * (complex_fft ? 2 : 1);
+ int num_bytes = static_cast<int>(num_floats) * sizeof(float);
+ float* in = static_cast<float*>(pffft_aligned_malloc(num_bytes));
+ float* out = static_cast<float*>(pffft_aligned_malloc(num_bytes));
+ float* scratch = AllocateScratchBuffer(fft_size, complex_fft);
+
+ // Init PFFFT C++ wrapper.
+ Pffft::FftType fft_type =
+ complex_fft ? Pffft::FftType::kComplex : Pffft::FftType::kReal;
+ ASSERT_TRUE(Pffft::IsValidFftSize(fft_size, fft_type));
+ Pffft pffft_wrapper(fft_size, fft_type);
+ auto in_wrapper = pffft_wrapper.CreateBuffer();
+ auto out_wrapper = pffft_wrapper.CreateBuffer();
+
+ // Input and output buffers views.
+ rtc::ArrayView<float> in_view(in, num_floats);
+ rtc::ArrayView<float> out_view(out, num_floats);
+ auto in_wrapper_view = in_wrapper->GetView();
+ EXPECT_EQ(in_wrapper_view.size(), num_floats);
+ auto out_wrapper_view = out_wrapper->GetConstView();
+ EXPECT_EQ(out_wrapper_view.size(), num_floats);
+
+ // Random input data.
+ for (size_t i = 0; i < num_floats; ++i) {
+ in_wrapper_view[i] = in[i] = static_cast<float>(frand() * 2.0 - 1.0);
+ }
+
+ // Forward transform.
+ pffft_transform(pffft_status, in, out, scratch, PFFFT_FORWARD);
+ pffft_wrapper.ForwardTransform(*in_wrapper, out_wrapper.get(),
+ /*ordered=*/false);
+ ExpectArrayViewsEquality(out_view, out_wrapper_view);
+
+ // Copy the FFT results into the input buffers to compute the backward FFT.
+ std::copy(out_view.begin(), out_view.end(), in_view.begin());
+ std::copy(out_wrapper_view.begin(), out_wrapper_view.end(),
+ in_wrapper_view.begin());
+
+ // Backward transform.
+ pffft_transform(pffft_status, in, out, scratch, PFFFT_BACKWARD);
+ pffft_wrapper.BackwardTransform(*in_wrapper, out_wrapper.get(),
+ /*ordered=*/false);
+ ExpectArrayViewsEquality(out_view, out_wrapper_view);
+
+ pffft_destroy_setup(pffft_status);
+ pffft_aligned_free(in);
+ pffft_aligned_free(out);
+ pffft_aligned_free(scratch);
+}
+
+} // namespace
+
+TEST(PffftTest, CreateWrapperWithValidSize) {
+ for (size_t fft_size = 0; fft_size < kMaxValidSizeCheck; ++fft_size) {
+ SCOPED_TRACE(fft_size);
+ if (Pffft::IsValidFftSize(fft_size, Pffft::FftType::kReal)) {
+ CreatePffftWrapper(fft_size, Pffft::FftType::kReal);
+ }
+ if (Pffft::IsValidFftSize(fft_size, Pffft::FftType::kComplex)) {
+ CreatePffftWrapper(fft_size, Pffft::FftType::kComplex);
+ }
+ }
+}
+
+#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+
+class PffftInvalidSizeDeathTest : public ::testing::Test,
+ public ::testing::WithParamInterface<size_t> {
+};
+
+TEST_P(PffftInvalidSizeDeathTest, DoNotCreateRealWrapper) {
+ size_t fft_size = GetParam();
+ ASSERT_FALSE(Pffft::IsValidFftSize(fft_size, Pffft::FftType::kReal));
+ EXPECT_DEATH(CreatePffftWrapper(fft_size, Pffft::FftType::kReal), "");
+}
+
+TEST_P(PffftInvalidSizeDeathTest, DoNotCreateComplexWrapper) {
+ size_t fft_size = GetParam();
+ ASSERT_FALSE(Pffft::IsValidFftSize(fft_size, Pffft::FftType::kComplex));
+ EXPECT_DEATH(CreatePffftWrapper(fft_size, Pffft::FftType::kComplex), "");
+}
+
+INSTANTIATE_TEST_SUITE_P(PffftTest,
+ PffftInvalidSizeDeathTest,
+ ::testing::Values(17,
+ 33,
+ 65,
+ 97,
+ 129,
+ 161,
+ 193,
+ 257,
+ 289,
+ 385,
+ 481,
+ 513,
+ 577,
+ 641,
+ 801,
+ 865,
+ 1025));
+
+#endif
+
+// TODO(https://crbug.com/webrtc/9577): Enable once SIMD is always enabled.
+TEST(PffftTest, DISABLED_CheckSimd) {
+ EXPECT_TRUE(Pffft::IsSimdEnabled());
+}
+
+TEST(PffftTest, FftBitExactness) {
+ for (int fft_size : kFftSizes) {
+ SCOPED_TRACE(fft_size);
+ if (fft_size != 16) {
+ PffftValidateWrapper(fft_size, /*complex_fft=*/false);
+ }
+ PffftValidateWrapper(fft_size, /*complex_fft=*/true);
+ }
+}
+
+} // namespace test
+} // namespace webrtc