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/echo_remover.h"
12 
13 #include <algorithm>
14 #include <memory>
15 #include <numeric>
16 #include <sstream>
17 #include <string>
18 
19 #include "modules/audio_processing/aec3/aec3_common.h"
20 #include "modules/audio_processing/aec3/render_buffer.h"
21 #include "modules/audio_processing/aec3/render_delay_buffer.h"
22 #include "modules/audio_processing/logging/apm_data_dumper.h"
23 #include "modules/audio_processing/test/echo_canceller_test_tools.h"
24 #include "rtc_base/random.h"
25 #include "test/gtest.h"
26 
27 namespace webrtc {
28 namespace {
29 
ProduceDebugText(int sample_rate_hz)30 std::string ProduceDebugText(int sample_rate_hz) {
31   std::ostringstream ss;
32   ss << "Sample rate: " << sample_rate_hz;
33   return ss.str();
34 }
35 
ProduceDebugText(int sample_rate_hz,int delay)36 std::string ProduceDebugText(int sample_rate_hz, int delay) {
37   std::ostringstream ss(ProduceDebugText(sample_rate_hz));
38   ss << ", Delay: " << delay;
39   return ss.str();
40 }
41 
42 constexpr size_t kDownSamplingFactor = 4;
43 constexpr size_t kNumMatchedFilters = 4;
44 
45 }  // namespace
46 
47 // Verifies the basic API call sequence
TEST(EchoRemover,BasicApiCalls)48 TEST(EchoRemover, BasicApiCalls) {
49   for (auto rate : {8000, 16000, 32000, 48000}) {
50     SCOPED_TRACE(ProduceDebugText(rate));
51     std::unique_ptr<EchoRemover> remover(
52         EchoRemover::Create(EchoCanceller3Config(), rate));
53     std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create(
54         NumBandsForRate(rate), kDownSamplingFactor,
55         GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
56         GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
57 
58     std::vector<std::vector<float>> render(NumBandsForRate(rate),
59                                            std::vector<float>(kBlockSize, 0.f));
60     std::vector<std::vector<float>> capture(
61         NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
62     for (size_t k = 0; k < 100; ++k) {
63       EchoPathVariability echo_path_variability(k % 3 == 0 ? true : false,
64                                                 k % 5 == 0 ? true : false);
65       rtc::Optional<size_t> echo_path_delay_samples =
66           (k % 6 == 0 ? rtc::Optional<size_t>(k * 10)
67                       : rtc::nullopt);
68       render_buffer->Insert(render);
69       render_buffer->UpdateBuffers();
70       remover->ProcessCapture(echo_path_delay_samples, echo_path_variability,
71                               k % 2 == 0 ? true : false,
72                               render_buffer->GetRenderBuffer(), &capture);
73     }
74   }
75 }
76 
77 #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
78 
79 // Verifies the check for the samplerate.
80 // TODO(peah): Re-enable the test once the issue with memory leaks during DEATH
81 // tests on test bots has been fixed.
TEST(EchoRemover,DISABLED_WrongSampleRate)82 TEST(EchoRemover, DISABLED_WrongSampleRate) {
83   EXPECT_DEATH(std::unique_ptr<EchoRemover>(
84                    EchoRemover::Create(EchoCanceller3Config(), 8001)),
85                "");
86 }
87 
88 // Verifies the check for the capture block size.
TEST(EchoRemover,WrongCaptureBlockSize)89 TEST(EchoRemover, WrongCaptureBlockSize) {
90   for (auto rate : {8000, 16000, 32000, 48000}) {
91     SCOPED_TRACE(ProduceDebugText(rate));
92     std::unique_ptr<EchoRemover> remover(
93         EchoRemover::Create(EchoCanceller3Config(), rate));
94     std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create(
95         NumBandsForRate(rate), kDownSamplingFactor,
96         GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
97         GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
98     std::vector<std::vector<float>> capture(
99         NumBandsForRate(rate), std::vector<float>(kBlockSize - 1, 0.f));
100     EchoPathVariability echo_path_variability(false, false);
101     rtc::Optional<size_t> echo_path_delay_samples;
102     EXPECT_DEATH(remover->ProcessCapture(
103                      echo_path_delay_samples, echo_path_variability, false,
104                      render_buffer->GetRenderBuffer(), &capture),
105                  "");
106   }
107 }
108 
109 // Verifies the check for the number of capture bands.
110 // TODO(peah): Re-enable the test once the issue with memory leaks during DEATH
111 // tests on test bots has been fixed.c
TEST(EchoRemover,DISABLED_WrongCaptureNumBands)112 TEST(EchoRemover, DISABLED_WrongCaptureNumBands) {
113   for (auto rate : {16000, 32000, 48000}) {
114     SCOPED_TRACE(ProduceDebugText(rate));
115     std::unique_ptr<EchoRemover> remover(
116         EchoRemover::Create(EchoCanceller3Config(), rate));
117     std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create(
118         NumBandsForRate(rate), kDownSamplingFactor,
119         GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
120         GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
121     std::vector<std::vector<float>> capture(
122         NumBandsForRate(rate == 48000 ? 16000 : rate + 16000),
123         std::vector<float>(kBlockSize, 0.f));
124     EchoPathVariability echo_path_variability(false, false);
125     rtc::Optional<size_t> echo_path_delay_samples;
126     EXPECT_DEATH(remover->ProcessCapture(
127                      echo_path_delay_samples, echo_path_variability, false,
128                      render_buffer->GetRenderBuffer(), &capture),
129                  "");
130   }
131 }
132 
133 // Verifies the check for non-null capture block.
TEST(EchoRemover,NullCapture)134 TEST(EchoRemover, NullCapture) {
135   std::unique_ptr<EchoRemover> remover(
136       EchoRemover::Create(EchoCanceller3Config(), 8000));
137   std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create(
138       3, kDownSamplingFactor,
139       GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
140       GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
141   EchoPathVariability echo_path_variability(false, false);
142   rtc::Optional<size_t> echo_path_delay_samples;
143   EXPECT_DEATH(
144       remover->ProcessCapture(echo_path_delay_samples, echo_path_variability,
145                               false, render_buffer->GetRenderBuffer(), nullptr),
146       "");
147 }
148 
149 #endif
150 
151 // Performs a sanity check that the echo_remover is able to properly
152 // remove echoes.
TEST(EchoRemover,BasicEchoRemoval)153 TEST(EchoRemover, BasicEchoRemoval) {
154   constexpr int kNumBlocksToProcess = 500;
155   Random random_generator(42U);
156   for (auto rate : {8000, 16000, 32000, 48000}) {
157     std::vector<std::vector<float>> x(NumBandsForRate(rate),
158                                       std::vector<float>(kBlockSize, 0.f));
159     std::vector<std::vector<float>> y(NumBandsForRate(rate),
160                                       std::vector<float>(kBlockSize, 0.f));
161     EchoPathVariability echo_path_variability(false, false);
162     for (size_t delay_samples : {0, 64, 150, 200, 301}) {
163       SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
164       std::unique_ptr<EchoRemover> remover(
165           EchoRemover::Create(EchoCanceller3Config(), rate));
166       std::unique_ptr<RenderDelayBuffer> render_buffer(
167           RenderDelayBuffer::Create(
168               NumBandsForRate(rate), kDownSamplingFactor,
169               GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
170               GetRenderDelayBufferSize(kDownSamplingFactor,
171                                        kNumMatchedFilters)));
172       std::vector<std::unique_ptr<DelayBuffer<float>>> delay_buffers(x.size());
173       for (size_t j = 0; j < x.size(); ++j) {
174         delay_buffers[j].reset(new DelayBuffer<float>(delay_samples));
175       }
176 
177       float input_energy = 0.f;
178       float output_energy = 0.f;
179       for (int k = 0; k < kNumBlocksToProcess; ++k) {
180         const bool silence = k < 100 || (k % 100 >= 10);
181 
182         for (size_t j = 0; j < x.size(); ++j) {
183           if (silence) {
184             std::fill(x[j].begin(), x[j].end(), 0.f);
185           } else {
186             RandomizeSampleVector(&random_generator, x[j]);
187           }
188           delay_buffers[j]->Delay(x[j], y[j]);
189         }
190 
191         if (k > kNumBlocksToProcess / 2) {
192           for (size_t j = 0; j < x.size(); ++j) {
193             input_energy = std::inner_product(y[j].begin(), y[j].end(),
194                                               y[j].begin(), input_energy);
195           }
196         }
197 
198         render_buffer->Insert(x);
199         render_buffer->UpdateBuffers();
200 
201         remover->ProcessCapture(delay_samples, echo_path_variability, false,
202                                 render_buffer->GetRenderBuffer(), &y);
203 
204         if (k > kNumBlocksToProcess / 2) {
205           for (size_t j = 0; j < x.size(); ++j) {
206             output_energy = std::inner_product(y[j].begin(), y[j].end(),
207                                                y[j].begin(), output_energy);
208           }
209         }
210       }
211       EXPECT_GT(input_energy, 10.f * output_energy);
212     }
213   }
214 }
215 
216 }  // namespace webrtc
217