blob: 8926f0223f285568a6daf993f9463bf73e7ed5ef [file] [log] [blame]
andrew@webrtc.org60730cf2014-01-07 17:45:09 +00001/*
2 * Copyright (c) 2014 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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/audio_processing/audio_processing_impl.h"
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000012
Alessio Bazzicad2b97402018-08-09 14:23:11 +020013#include "modules/audio_processing/include/audio_processing.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020014#include "modules/audio_processing/test/test_utils.h"
Alessio Bazzicad2b97402018-08-09 14:23:11 +020015#include "rtc_base/refcountedobject.h"
16#include "rtc_base/scoped_ref_ptr.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "test/gmock.h"
18#include "test/gtest.h"
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000019
20using ::testing::Invoke;
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000021
22namespace webrtc {
peaha9cc40b2017-06-29 08:32:09 -070023namespace {
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000024
25class MockInitialize : public AudioProcessingImpl {
26 public:
peah88ac8532016-09-12 16:47:25 -070027 explicit MockInitialize(const webrtc::Config& config)
28 : AudioProcessingImpl(config) {}
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000029
andrew@webrtc.orge84978f2014-01-25 02:09:06 +000030 MOCK_METHOD0(InitializeLocked, int());
danilchap56359be2017-09-07 07:53:45 -070031 int RealInitializeLocked() RTC_NO_THREAD_SAFETY_ANALYSIS {
pbos@webrtc.org788acd12014-12-15 09:41:24 +000032 return AudioProcessingImpl::InitializeLocked();
33 }
peaha9cc40b2017-06-29 08:32:09 -070034
Niels Möller6f72f562017-10-19 13:15:17 +020035 MOCK_CONST_METHOD0(AddRef, void());
36 MOCK_CONST_METHOD0(Release, rtc::RefCountReleaseStatus());
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000037};
38
Alessio Bazzicad2b97402018-08-09 14:23:11 +020039void InitializeAudioFrame(size_t input_rate,
40 size_t num_channels,
41 AudioFrame* frame) {
Alex Loikob5c9a792018-04-16 16:31:22 +020042 const size_t samples_per_input_channel = rtc::CheckedDivExact(
43 input_rate, static_cast<size_t>(rtc::CheckedDivExact(
44 1000, AudioProcessing::kChunkSizeMs)));
Alex Loikob5c9a792018-04-16 16:31:22 +020045 RTC_DCHECK_LE(samples_per_input_channel * num_channels,
46 AudioFrame::kMaxDataSizeSamples);
Alessio Bazzicad2b97402018-08-09 14:23:11 +020047 frame->samples_per_channel_ = samples_per_input_channel;
48 frame->sample_rate_hz_ = input_rate;
49 frame->num_channels_ = num_channels;
50}
51
52void FillFixedFrame(int16_t audio_level, AudioFrame* frame) {
53 const size_t num_samples = frame->samples_per_channel_ * frame->num_channels_;
54 for (size_t i = 0; i < num_samples; ++i) {
55 frame->mutable_data()[i] = audio_level;
Alex Loikob5c9a792018-04-16 16:31:22 +020056 }
57}
58
Alessio Bazzicad2b97402018-08-09 14:23:11 +020059// Mocks EchoDetector and records the first samples of the last analyzed render
60// stream frame. Used to check what data is read by an EchoDetector
61// implementation injected into an APM.
62class TestEchoDetector : public EchoDetector {
63 public:
64 TestEchoDetector()
65 : analyze_render_audio_called_(false),
66 last_render_audio_first_sample_(0.f) {}
67 ~TestEchoDetector() override = default;
68 void AnalyzeRenderAudio(rtc::ArrayView<const float> render_audio) override {
69 last_render_audio_first_sample_ = render_audio[0];
70 analyze_render_audio_called_ = true;
71 }
72 void AnalyzeCaptureAudio(rtc::ArrayView<const float> capture_audio) override {
73 }
74 void Initialize(int capture_sample_rate_hz,
75 int num_capture_channels,
76 int render_sample_rate_hz,
77 int num_render_channels) override {}
78 EchoDetector::Metrics GetMetrics() const override { return {}; }
79 // Returns true if AnalyzeRenderAudio() has been called at least once.
80 bool analyze_render_audio_called() const {
81 return analyze_render_audio_called_;
82 }
83 // Returns the first sample of the last analyzed render frame.
84 float last_render_audio_first_sample() const {
85 return last_render_audio_first_sample_;
86 }
87
88 private:
89 bool analyze_render_audio_called_;
90 float last_render_audio_first_sample_;
91};
92
93// Mocks CustomProcessing and applies ProcessSample() to all the samples.
94// Meant to be injected into an APM to modify samples in a known and detectable
95// way.
96class TestRenderPreProcessor : public CustomProcessing {
97 public:
98 TestRenderPreProcessor() = default;
99 ~TestRenderPreProcessor() = default;
100 void Initialize(int sample_rate_hz, int num_channels) override {}
101 void Process(AudioBuffer* audio) override {
102 for (size_t k = 0; k < audio->num_channels(); ++k) {
103 rtc::ArrayView<float> channel_view(audio->channels_f()[k],
104 audio->num_frames());
105 std::transform(channel_view.begin(), channel_view.end(),
106 channel_view.begin(), ProcessSample);
107 }
108 };
109 std::string ToString() const override { return "TestRenderPreProcessor"; }
110 void SetRuntimeSetting(AudioProcessing::RuntimeSetting setting) override {}
111 // Modifies a sample. This member is used in Process() to modify a frame and
112 // it is publicly visible to enable tests.
113 static constexpr float ProcessSample(float x) { return 2.f * x; }
114};
115
peaha9cc40b2017-06-29 08:32:09 -0700116} // namespace
117
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000118TEST(AudioProcessingImplTest, AudioParameterChangeTriggersInit) {
peah88ac8532016-09-12 16:47:25 -0700119 webrtc::Config config;
andrew@webrtc.orge84978f2014-01-25 02:09:06 +0000120 MockInitialize mock(config);
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000121 ON_CALL(mock, InitializeLocked())
122 .WillByDefault(Invoke(&mock, &MockInitialize::RealInitializeLocked));
123
124 EXPECT_CALL(mock, InitializeLocked()).Times(1);
125 mock.Initialize();
126
127 AudioFrame frame;
peah2ace3f92016-09-10 04:42:27 -0700128 // Call with the default parameters; there should be an init.
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000129 frame.num_channels_ = 1;
130 SetFrameSampleRate(&frame, 16000);
Per Ã…hgren4bdced52017-06-27 16:00:38 +0200131 EXPECT_CALL(mock, InitializeLocked()).Times(0);
andrew@webrtc.orga8b97372014-03-10 22:26:12 +0000132 EXPECT_NOERR(mock.ProcessStream(&frame));
aluebsb0319552016-03-17 20:39:53 -0700133 EXPECT_NOERR(mock.ProcessReverseStream(&frame));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000134
135 // New sample rate. (Only impacts ProcessStream).
136 SetFrameSampleRate(&frame, 32000);
Yves Gerey665174f2018-06-19 15:03:05 +0200137 EXPECT_CALL(mock, InitializeLocked()).Times(1);
andrew@webrtc.orga8b97372014-03-10 22:26:12 +0000138 EXPECT_NOERR(mock.ProcessStream(&frame));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000139
140 // New number of channels.
peah2ace3f92016-09-10 04:42:27 -0700141 // TODO(peah): Investigate why this causes 2 inits.
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000142 frame.num_channels_ = 2;
Yves Gerey665174f2018-06-19 15:03:05 +0200143 EXPECT_CALL(mock, InitializeLocked()).Times(2);
andrew@webrtc.orga8b97372014-03-10 22:26:12 +0000144 EXPECT_NOERR(mock.ProcessStream(&frame));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000145 // ProcessStream sets num_channels_ == num_output_channels.
146 frame.num_channels_ = 2;
aluebsb0319552016-03-17 20:39:53 -0700147 EXPECT_NOERR(mock.ProcessReverseStream(&frame));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000148
aluebsb0319552016-03-17 20:39:53 -0700149 // A new sample rate passed to ProcessReverseStream should cause an init.
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000150 SetFrameSampleRate(&frame, 16000);
Alex Luebs5b830fe2016-03-08 17:52:52 +0100151 EXPECT_CALL(mock, InitializeLocked()).Times(1);
aluebsb0319552016-03-17 20:39:53 -0700152 EXPECT_NOERR(mock.ProcessReverseStream(&frame));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000153}
154
Alessio Bazzicac054e782018-04-16 12:10:09 +0200155TEST(AudioProcessingImplTest, UpdateCapturePreGainRuntimeSetting) {
Alex Loikob5c9a792018-04-16 16:31:22 +0200156 std::unique_ptr<AudioProcessing> apm(AudioProcessingBuilder().Create());
157 webrtc::AudioProcessing::Config apm_config;
158 apm_config.pre_amplifier.enabled = true;
159 apm_config.pre_amplifier.fixed_gain_factor = 1.f;
160 apm->ApplyConfig(apm_config);
161
162 AudioFrame frame;
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200163 constexpr int16_t kAudioLevel = 10000;
164 constexpr size_t kSampleRateHz = 48000;
165 constexpr size_t kNumChannels = 2;
166 InitializeAudioFrame(kSampleRateHz, kNumChannels, &frame);
Alex Loikob5c9a792018-04-16 16:31:22 +0200167
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200168 FillFixedFrame(kAudioLevel, &frame);
Alex Loikob5c9a792018-04-16 16:31:22 +0200169 apm->ProcessStream(&frame);
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200170 EXPECT_EQ(frame.data()[100], kAudioLevel)
Alex Loikob5c9a792018-04-16 16:31:22 +0200171 << "With factor 1, frame shouldn't be modified.";
172
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200173 constexpr float kGainFactor = 2.f;
Alex Loikob5c9a792018-04-16 16:31:22 +0200174 apm->SetRuntimeSetting(
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200175 AudioProcessing::RuntimeSetting::CreateCapturePreGain(kGainFactor));
Alex Loikob5c9a792018-04-16 16:31:22 +0200176
177 // Process for two frames to have time to ramp up gain.
178 for (int i = 0; i < 2; ++i) {
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200179 FillFixedFrame(kAudioLevel, &frame);
Alex Loikob5c9a792018-04-16 16:31:22 +0200180 apm->ProcessStream(&frame);
181 }
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200182 EXPECT_EQ(frame.data()[100], kGainFactor * kAudioLevel)
Alex Loikob5c9a792018-04-16 16:31:22 +0200183 << "Frame should be amplified.";
Alessio Bazzicac054e782018-04-16 12:10:09 +0200184}
185
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200186TEST(AudioProcessingImplTest, RenderPreProcessorBeforeEchoDetector) {
187 // Make sure that signal changes caused by a render pre-processing sub-module
188 // take place before any echo detector analysis.
189 rtc::scoped_refptr<TestEchoDetector> test_echo_detector(
190 new rtc::RefCountedObject<TestEchoDetector>());
191 std::unique_ptr<CustomProcessing> test_render_pre_processor(
192 new TestRenderPreProcessor());
193 // Create APM injecting the test echo detector and render pre-processor.
194 std::unique_ptr<AudioProcessing> apm(
195 AudioProcessingBuilder()
196 .SetEchoDetector(test_echo_detector)
197 .SetRenderPreProcessing(std::move(test_render_pre_processor))
198 .Create());
199 webrtc::AudioProcessing::Config apm_config;
200 apm_config.pre_amplifier.enabled = true;
201 apm_config.residual_echo_detector.enabled = true;
202 apm->ApplyConfig(apm_config);
203
204 constexpr int16_t kAudioLevel = 1000;
205 constexpr int kSampleRateHz = 16000;
206 constexpr size_t kNumChannels = 1;
207 AudioFrame frame;
208 InitializeAudioFrame(kSampleRateHz, kNumChannels, &frame);
209
210 constexpr float kAudioLevelFloat = static_cast<float>(kAudioLevel);
211 constexpr float kExpectedPreprocessedAudioLevel =
212 TestRenderPreProcessor::ProcessSample(kAudioLevelFloat);
213 ASSERT_NE(kAudioLevelFloat, kExpectedPreprocessedAudioLevel);
214
215 // Analyze a render stream frame.
216 FillFixedFrame(kAudioLevel, &frame);
217 ASSERT_EQ(AudioProcessing::Error::kNoError,
218 apm->ProcessReverseStream(&frame));
219 // Trigger a call to in EchoDetector::AnalyzeRenderAudio() via
220 // ProcessStream().
221 FillFixedFrame(kAudioLevel, &frame);
222 ASSERT_EQ(AudioProcessing::Error::kNoError, apm->ProcessStream(&frame));
223 // Regardless of how the call to in EchoDetector::AnalyzeRenderAudio() is
224 // triggered, the line below checks that the call has occurred. If not, the
225 // APM implementation may have changed and this test might need to be adapted.
226 ASSERT_TRUE(test_echo_detector->analyze_render_audio_called());
227 // Check that the data read in EchoDetector::AnalyzeRenderAudio() is that
228 // produced by the render pre-processor.
229 EXPECT_EQ(kExpectedPreprocessedAudioLevel,
230 test_echo_detector->last_render_audio_first_sample());
231}
232
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000233} // namespace webrtc