render_signal_analyzer_unittest.cc (6316B)
1 /* 2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "modules/audio_processing/aec3/render_signal_analyzer.h" 12 13 #include <algorithm> 14 #include <array> 15 #include <cmath> 16 #include <cstddef> 17 #include <memory> 18 #include <numbers> 19 #include <optional> 20 #include <string> 21 22 #include "api/array_view.h" 23 #include "api/audio/echo_canceller3_config.h" 24 #include "modules/audio_processing/aec3/aec3_common.h" 25 #include "modules/audio_processing/aec3/aec3_fft.h" 26 #include "modules/audio_processing/aec3/block.h" 27 #include "modules/audio_processing/aec3/render_delay_buffer.h" 28 #include "modules/audio_processing/test/echo_canceller_test_tools.h" 29 #include "rtc_base/checks.h" 30 #include "rtc_base/random.h" 31 #include "rtc_base/strings/string_builder.h" 32 #include "test/gtest.h" 33 34 namespace webrtc { 35 namespace { 36 37 constexpr float kPi = std::numbers::pi_v<float>; 38 39 void ProduceSinusoidInNoise(int sample_rate_hz, 40 size_t sinusoid_channel, 41 float sinusoidal_frequency_hz, 42 Random* random_generator, 43 size_t* sample_counter, 44 Block* x) { 45 // Fill x with low-amplitude noise. 46 for (int band = 0; band < x->NumBands(); ++band) { 47 for (int channel = 0; channel < x->NumChannels(); ++channel) { 48 RandomizeSampleVector(random_generator, x->View(band, channel), 49 /*amplitude=*/500.f); 50 } 51 } 52 // Produce a sinusoid of the specified frequency in the specified channel. 53 for (size_t k = *sample_counter, j = 0; k < (*sample_counter + kBlockSize); 54 ++k, ++j) { 55 x->View(/*band=*/0, sinusoid_channel)[j] += 56 32000.f * 57 std::sin(2.f * kPi * sinusoidal_frequency_hz * k / sample_rate_hz); 58 } 59 *sample_counter = *sample_counter + kBlockSize; 60 } 61 62 void RunNarrowBandDetectionTest(size_t num_channels) { 63 RenderSignalAnalyzer analyzer(EchoCanceller3Config{}); 64 Random random_generator(42U); 65 constexpr int kSampleRateHz = 48000; 66 constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz); 67 Block x(kNumBands, num_channels); 68 std::array<float, kBlockSize> x_old; 69 Aec3Fft fft; 70 EchoCanceller3Config config; 71 std::unique_ptr<RenderDelayBuffer> render_delay_buffer( 72 RenderDelayBuffer::Create(config, kSampleRateHz, num_channels)); 73 74 std::array<float, kFftLengthBy2Plus1> mask; 75 x_old.fill(0.f); 76 constexpr int kSinusFrequencyBin = 32; 77 78 auto generate_sinusoid_test = [&](bool known_delay) { 79 size_t sample_counter = 0; 80 for (size_t k = 0; k < 100; ++k) { 81 ProduceSinusoidInNoise(16000, num_channels - 1, 82 16000 / 2 * kSinusFrequencyBin / kFftLengthBy2, 83 &random_generator, &sample_counter, &x); 84 85 render_delay_buffer->Insert(x); 86 if (k == 0) { 87 render_delay_buffer->Reset(); 88 } 89 render_delay_buffer->PrepareCaptureProcessing(); 90 91 analyzer.Update(*render_delay_buffer->GetRenderBuffer(), 92 known_delay ? std::optional<size_t>(0) : std::nullopt); 93 } 94 }; 95 96 generate_sinusoid_test(true); 97 mask.fill(1.f); 98 analyzer.MaskRegionsAroundNarrowBands(&mask); 99 for (int k = 0; k < static_cast<int>(mask.size()); ++k) { 100 EXPECT_EQ(abs(k - kSinusFrequencyBin) <= 2 ? 0.f : 1.f, mask[k]); 101 } 102 EXPECT_TRUE(analyzer.PoorSignalExcitation()); 103 EXPECT_TRUE(static_cast<bool>(analyzer.NarrowPeakBand())); 104 EXPECT_EQ(*analyzer.NarrowPeakBand(), 32); 105 106 // Verify that no bands are detected as narrow when the delay is unknown. 107 generate_sinusoid_test(false); 108 mask.fill(1.f); 109 analyzer.MaskRegionsAroundNarrowBands(&mask); 110 std::for_each(mask.begin(), mask.end(), [](float a) { EXPECT_EQ(1.f, a); }); 111 EXPECT_FALSE(analyzer.PoorSignalExcitation()); 112 } 113 114 std::string ProduceDebugText(size_t num_channels) { 115 StringBuilder ss; 116 ss << "number of channels: " << num_channels; 117 return ss.Release(); 118 } 119 } // namespace 120 121 #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) 122 // Verifies that the check for non-null output parameter works. 123 TEST(RenderSignalAnalyzerDeathTest, NullMaskOutput) { 124 RenderSignalAnalyzer analyzer(EchoCanceller3Config{}); 125 EXPECT_DEATH(analyzer.MaskRegionsAroundNarrowBands(nullptr), ""); 126 } 127 128 #endif 129 130 // Verify that no narrow bands are detected in a Gaussian noise signal. 131 TEST(RenderSignalAnalyzer, NoFalseDetectionOfNarrowBands) { 132 for (auto num_channels : {1, 2, 8}) { 133 SCOPED_TRACE(ProduceDebugText(num_channels)); 134 RenderSignalAnalyzer analyzer(EchoCanceller3Config{}); 135 Random random_generator(42U); 136 Block x(3, num_channels); 137 std::array<float, kBlockSize> x_old; 138 std::unique_ptr<RenderDelayBuffer> render_delay_buffer( 139 RenderDelayBuffer::Create(EchoCanceller3Config(), 48000, num_channels)); 140 std::array<float, kFftLengthBy2Plus1> mask; 141 x_old.fill(0.f); 142 143 for (int k = 0; k < 100; ++k) { 144 for (int band = 0; band < x.NumBands(); ++band) { 145 for (int channel = 0; channel < x.NumChannels(); ++channel) { 146 RandomizeSampleVector(&random_generator, x.View(band, channel)); 147 } 148 } 149 150 render_delay_buffer->Insert(x); 151 if (k == 0) { 152 render_delay_buffer->Reset(); 153 } 154 render_delay_buffer->PrepareCaptureProcessing(); 155 156 analyzer.Update(*render_delay_buffer->GetRenderBuffer(), 157 std::optional<size_t>(0)); 158 } 159 160 mask.fill(1.f); 161 analyzer.MaskRegionsAroundNarrowBands(&mask); 162 EXPECT_TRUE(std::all_of(mask.begin(), mask.end(), 163 [](float a) { return a == 1.f; })); 164 EXPECT_FALSE(analyzer.PoorSignalExcitation()); 165 EXPECT_FALSE(static_cast<bool>(analyzer.NarrowPeakBand())); 166 } 167 } 168 169 // Verify that a sinusoid signal is detected as narrow bands. 170 TEST(RenderSignalAnalyzer, NarrowBandDetection) { 171 for (auto num_channels : {1, 2, 8}) { 172 SCOPED_TRACE(ProduceDebugText(num_channels)); 173 RunNarrowBandDetectionTest(num_channels); 174 } 175 } 176 } // namespace webrtc