AGC2 Bi-Quad filter: separate target and unit test.
Adding a build target for the bi-qaud filter to make it available for the RNN VAD of AGC2. Also adding a unit test to test the computation both in-place and not in-place while comparing the produced output to that of scipy.signal. Bug: webrtc:9076 Change-Id: I16176a477ee4b81bb1e090c4906c3a9948ad2772 Reviewed-on: https://webrtc-review.googlesource.com/74220 Commit-Queue: Alessio Bazzica <alessiob@webrtc.org> Reviewed-by: Alex Loiko <aleloi@webrtc.org> Cr-Commit-Position: refs/heads/master@{#23141}
This commit is contained in:
parent
78ad6ce381
commit
d8d02147d9
@ -543,6 +543,7 @@ if (rtc_include_tests) {
|
||||
"../audio_coding:neteq_input_audio_tools",
|
||||
"aec_dump:mock_aec_dump_unittests",
|
||||
"agc2:adaptive_digital_unittests",
|
||||
"agc2:biquad_filter_unittests",
|
||||
"agc2:fixed_digital_unittests",
|
||||
"agc2:noise_estimator_unittests",
|
||||
"agc2/rnn_vad:unittests",
|
||||
|
||||
@ -47,6 +47,27 @@ rtc_source_set("adaptive_digital") {
|
||||
]
|
||||
}
|
||||
|
||||
rtc_source_set("biquad_filter") {
|
||||
visibility = [ ":*" ]
|
||||
sources = [
|
||||
"biquad_filter.cc",
|
||||
"biquad_filter.h",
|
||||
]
|
||||
deps = [
|
||||
"../../../api:array_view",
|
||||
"../../../rtc_base:rtc_base_approved",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_source_set("common") {
|
||||
sources = [
|
||||
"agc2_common.h",
|
||||
]
|
||||
deps = [
|
||||
"../../../rtc_base:rtc_base_approved",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_source_set("fixed_digital") {
|
||||
sources = [
|
||||
"fixed_digital_level_estimator.cc",
|
||||
@ -75,41 +96,6 @@ rtc_source_set("fixed_digital") {
|
||||
]
|
||||
}
|
||||
|
||||
rtc_source_set("common") {
|
||||
sources = [
|
||||
"agc2_common.h",
|
||||
]
|
||||
deps = [
|
||||
"../../../rtc_base:rtc_base_approved",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_source_set("noise_level_estimator") {
|
||||
sources = [
|
||||
"biquad_filter.cc",
|
||||
"biquad_filter.h",
|
||||
"down_sampler.cc",
|
||||
"down_sampler.h",
|
||||
"noise_level_estimator.cc",
|
||||
"noise_level_estimator.h",
|
||||
"noise_spectrum_estimator.cc",
|
||||
"noise_spectrum_estimator.h",
|
||||
"signal_classifier.cc",
|
||||
"signal_classifier.h",
|
||||
]
|
||||
deps = [
|
||||
"..:aec_core",
|
||||
"..:apm_logging",
|
||||
"..:audio_frame_view",
|
||||
"../../../api:array_view",
|
||||
"../../../common_audio",
|
||||
"../../../rtc_base:checks",
|
||||
"../../../rtc_base:macromagic",
|
||||
]
|
||||
|
||||
configs += [ "..:apm_debug_dump" ]
|
||||
}
|
||||
|
||||
rtc_source_set("gain_applier") {
|
||||
sources = [
|
||||
"gain_applier.cc",
|
||||
@ -122,19 +108,65 @@ rtc_source_set("gain_applier") {
|
||||
]
|
||||
}
|
||||
|
||||
rtc_source_set("test_utils") {
|
||||
testonly = true
|
||||
visibility = [ ":*" ]
|
||||
rtc_source_set("noise_level_estimator") {
|
||||
sources = [
|
||||
"agc2_testing_common.cc",
|
||||
"agc2_testing_common.h",
|
||||
"vector_float_frame.cc",
|
||||
"vector_float_frame.h",
|
||||
"down_sampler.cc",
|
||||
"down_sampler.h",
|
||||
"noise_level_estimator.cc",
|
||||
"noise_level_estimator.h",
|
||||
"noise_spectrum_estimator.cc",
|
||||
"noise_spectrum_estimator.h",
|
||||
"signal_classifier.cc",
|
||||
"signal_classifier.h",
|
||||
]
|
||||
deps = [
|
||||
":biquad_filter",
|
||||
"..:aec_core",
|
||||
"..:apm_logging",
|
||||
"..:audio_frame_view",
|
||||
"../../../api:array_view",
|
||||
"../../../common_audio",
|
||||
"../../../rtc_base:checks",
|
||||
"../../../rtc_base:macromagic",
|
||||
]
|
||||
|
||||
configs += [ "..:apm_debug_dump" ]
|
||||
}
|
||||
|
||||
rtc_source_set("adaptive_digital_unittests") {
|
||||
testonly = true
|
||||
configs += [ "..:apm_debug_dump" ]
|
||||
|
||||
sources = [
|
||||
"adaptive_digital_gain_applier_unittest.cc",
|
||||
"adaptive_mode_level_estimator_unittest.cc",
|
||||
"gain_applier_unittest.cc",
|
||||
"saturation_protector_unittest.cc",
|
||||
]
|
||||
deps = [
|
||||
":adaptive_digital",
|
||||
":common",
|
||||
":gain_applier",
|
||||
":test_utils",
|
||||
"..:apm_logging",
|
||||
"..:audio_frame_view",
|
||||
"../../../api:array_view",
|
||||
"../../../common_audio",
|
||||
"../../../rtc_base:checks",
|
||||
"../../../rtc_base:rtc_base_approved",
|
||||
"../../../rtc_base:rtc_base_tests_utils",
|
||||
"../vad:vad_with_level",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_source_set("biquad_filter_unittests") {
|
||||
testonly = true
|
||||
sources = [
|
||||
"biquad_filter_unittest.cc",
|
||||
]
|
||||
deps = [
|
||||
":biquad_filter",
|
||||
"../../../rtc_base:rtc_base_tests_utils",
|
||||
]
|
||||
}
|
||||
|
||||
@ -168,32 +200,6 @@ rtc_source_set("fixed_digital_unittests") {
|
||||
]
|
||||
}
|
||||
|
||||
rtc_source_set("adaptive_digital_unittests") {
|
||||
testonly = true
|
||||
configs += [ "..:apm_debug_dump" ]
|
||||
|
||||
sources = [
|
||||
"adaptive_digital_gain_applier_unittest.cc",
|
||||
"adaptive_mode_level_estimator_unittest.cc",
|
||||
"gain_applier_unittest.cc",
|
||||
"saturation_protector_unittest.cc",
|
||||
]
|
||||
deps = [
|
||||
":adaptive_digital",
|
||||
":common",
|
||||
":gain_applier",
|
||||
":test_utils",
|
||||
"..:apm_logging",
|
||||
"..:audio_frame_view",
|
||||
"../../../api:array_view",
|
||||
"../../../common_audio",
|
||||
"../../../rtc_base:checks",
|
||||
"../../../rtc_base:rtc_base_approved",
|
||||
"../../../rtc_base:rtc_base_tests_utils",
|
||||
"../vad:vad_with_level",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_source_set("noise_estimator_unittests") {
|
||||
testonly = true
|
||||
configs += [ "..:apm_debug_dump" ]
|
||||
@ -213,3 +219,19 @@ rtc_source_set("noise_estimator_unittests") {
|
||||
"../../../rtc_base:rtc_base_tests_utils",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_source_set("test_utils") {
|
||||
testonly = true
|
||||
visibility = [ ":*" ]
|
||||
sources = [
|
||||
"agc2_testing_common.cc",
|
||||
"agc2_testing_common.h",
|
||||
"vector_float_frame.cc",
|
||||
"vector_float_frame.h",
|
||||
]
|
||||
deps = [
|
||||
"..:audio_frame_view",
|
||||
"../../../rtc_base:checks",
|
||||
"../../../rtc_base:rtc_base_approved",
|
||||
]
|
||||
}
|
||||
|
||||
@ -12,9 +12,8 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// This method applies a biquad filter to an input signal x to produce an
|
||||
// output signal y. The biquad coefficients are specified at the construction
|
||||
// of the object.
|
||||
// Transposed direct form I implementation of a bi-quad filter applied to an
|
||||
// input signal |x| to produce an output signal |y|.
|
||||
void BiQuadFilter::Process(rtc::ArrayView<const float> x,
|
||||
rtc::ArrayView<float> y) {
|
||||
for (size_t k = 0; k < x.size(); ++k) {
|
||||
|
||||
@ -19,6 +19,10 @@ namespace webrtc {
|
||||
|
||||
class BiQuadFilter {
|
||||
public:
|
||||
// Normalized filter coefficients.
|
||||
// b_0 + b_1 • z^(-1) + b_2 • z^(-2)
|
||||
// H(z) = ---------------------------------
|
||||
// 1 + a_1 • z^(-1) + a_2 • z^(-2)
|
||||
struct BiQuadCoefficients {
|
||||
float b[3];
|
||||
float a[2];
|
||||
@ -31,7 +35,7 @@ class BiQuadFilter {
|
||||
}
|
||||
|
||||
// Produces a filtered output y of the input x. Both x and y need to
|
||||
// have the same length.
|
||||
// have the same length. In-place modification is allowed.
|
||||
void Process(rtc::ArrayView<const float> x, rtc::ArrayView<float> y);
|
||||
|
||||
private:
|
||||
|
||||
117
modules/audio_processing/agc2/biquad_filter_unittest.cc
Normal file
117
modules/audio_processing/agc2/biquad_filter_unittest.cc
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2018 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/agc2/biquad_filter.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
||||
// TODO(bugs.webrtc.org/8948): Add when the issue is fixed.
|
||||
// #include "test/fpe_observer.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
constexpr size_t kFrameSize = 8;
|
||||
constexpr size_t kNumFrames = 4;
|
||||
using FloatArraySequence =
|
||||
std::array<std::array<float, kFrameSize>, kNumFrames>;
|
||||
|
||||
constexpr FloatArraySequence kBiQuadInputSeq = {
|
||||
{{-87.166290f, -8.029022f, 101.619583f, -0.294296f, -5.825764f, -8.890625f,
|
||||
10.310432f, 54.845333f},
|
||||
{-64.647644f, -6.883945f, 11.059189f, -95.242538f, -108.870834f,
|
||||
11.024944f, 63.044102f, -52.709583f},
|
||||
{-32.350529f, -18.108028f, -74.022339f, -8.986874f, -1.525581f,
|
||||
103.705513f, 6.346226f, -14.319557f},
|
||||
{22.645832f, -64.597153f, 55.462521f, -109.393188f, 10.117825f,
|
||||
-40.019642f, -98.612228f, -8.330326f}}};
|
||||
|
||||
// Generated via "B, A = scipy.signal.butter(2, 30/12000, btype='highpass')"
|
||||
const BiQuadFilter::BiQuadCoefficients kBiQuadConfig = {
|
||||
{0.99446179f, -1.98892358f, 0.99446179f},
|
||||
{-1.98889291f, 0.98895425f}};
|
||||
|
||||
// Comparing to scipy. The expected output is generated as follows:
|
||||
// zi = np.float32([0, 0])
|
||||
// for i in range(4):
|
||||
// yn, zi = scipy.signal.lfilter(B, A, x[i], zi=zi)
|
||||
// print(yn)
|
||||
constexpr FloatArraySequence kBiQuadOutputSeq = {
|
||||
{{-86.68354497f, -7.02175351f, 102.10290352f, -0.37487333f, -5.87205847f,
|
||||
-8.85521608f, 10.33772563f, 54.51157181f},
|
||||
{-64.92531604f, -6.76395978f, 11.15534507f, -94.68073341f, -107.18177856f,
|
||||
13.24642474f, 64.84288941f, -50.97822629f},
|
||||
{-30.1579652f, -15.64850899f, -71.06662821f, -5.5883229f, 1.91175353f,
|
||||
106.5572003f, 8.57183046f, -12.06298473f},
|
||||
{24.84286614f, -62.18094158f, 57.91488056f, -106.65685933f, 13.38760103f,
|
||||
-36.60367134f, -94.44880104f, -3.59920354f}}};
|
||||
|
||||
// Fail for every pair from two equally sized rtc::ArrayView<float> views such
|
||||
// that their relative error is above a given threshold. If the expected value
|
||||
// of a pair is 0, the tolerance is used to check the absolute error.
|
||||
void ExpectNearRelative(rtc::ArrayView<const float> expected,
|
||||
rtc::ArrayView<const float> computed,
|
||||
const float tolerance) {
|
||||
// The relative error is undefined when the expected value is 0.
|
||||
// When that happens, check the absolute error instead. |safe_den| is used
|
||||
// below to implement such logic.
|
||||
auto safe_den = [](float x) { return (x == 0.f) ? 1.f : std::fabs(x); };
|
||||
ASSERT_EQ(expected.size(), computed.size());
|
||||
for (size_t i = 0; i < expected.size(); ++i) {
|
||||
const float abs_diff = std::fabs(expected[i] - computed[i]);
|
||||
// No failure when the values are equal.
|
||||
if (abs_diff == 0.f)
|
||||
continue;
|
||||
SCOPED_TRACE(i);
|
||||
SCOPED_TRACE(expected[i]);
|
||||
SCOPED_TRACE(computed[i]);
|
||||
EXPECT_LE(abs_diff / safe_den(expected[i]), tolerance);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(BiQuadFilterTest, FilterNotInPlace) {
|
||||
BiQuadFilter filter;
|
||||
filter.Initialize(kBiQuadConfig);
|
||||
std::array<float, kFrameSize> samples;
|
||||
|
||||
// TODO(https://bugs.webrtc.org/8948): Add when the issue is fixed.
|
||||
// FloatingPointExceptionObserver fpe_observer;
|
||||
|
||||
for (size_t i = 0; i < kNumFrames; ++i) {
|
||||
SCOPED_TRACE(i);
|
||||
filter.Process(kBiQuadInputSeq[i], samples);
|
||||
ExpectNearRelative(kBiQuadOutputSeq[i], samples, 2e-4f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BiQuadFilterTest, FilterInPlace) {
|
||||
BiQuadFilter filter;
|
||||
filter.Initialize(kBiQuadConfig);
|
||||
std::array<float, kFrameSize> samples;
|
||||
|
||||
// TODO(https://bugs.webrtc.org/8948): Add when the issue is fixed.
|
||||
// FloatingPointExceptionObserver fpe_observer;
|
||||
|
||||
for (size_t i = 0; i < kNumFrames; ++i) {
|
||||
SCOPED_TRACE(i);
|
||||
std::copy(kBiQuadInputSeq[i].begin(), kBiQuadInputSeq[i].end(),
|
||||
samples.begin());
|
||||
filter.Process({samples}, {samples});
|
||||
ExpectNearRelative(kBiQuadOutputSeq[i], samples, 2e-4f);
|
||||
}
|
||||
}
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
Loading…
x
Reference in New Issue
Block a user