blob: 3e65ab29a5bdf3bbe7169297903edda7b146c21b [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:251/*
andrew@webrtc.org293d22b2012-01-30 22:04:262 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:253 *
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 */
Florent Castelli0afde762024-04-19 15:07:0810#include "api/audio/audio_processing.h"
Jonas Olssona4d87372019-07-05 17:08:3311
andrew@webrtc.orgddbb8a22014-04-22 21:00:0412#include <math.h>
ajm@google.com59e41402011-07-28 17:34:0413#include <stdio.h>
kwiberg62eaacf2016-02-17 14:39:0514
andrew@webrtc.org07bf9a02012-05-05 00:32:0015#include <algorithm>
Oleh Prypin708eccc2019-03-27 08:38:5216#include <cmath>
andrew@webrtc.orgddbb8a22014-04-22 21:00:0417#include <limits>
kwiberg62eaacf2016-02-17 14:39:0518#include <memory>
Sam Zackrissone277bde2019-10-25 08:07:5419#include <numeric>
bjornv@webrtc.org3e102492013-02-14 15:29:0920#include <queue>
Ali Tofighf3592cb2022-08-16 12:44:3821#include <string>
andrew@webrtc.org07bf9a02012-05-05 00:32:0022
Sam Zackrisson6558fa52019-08-26 08:12:4123#include "absl/flags/flag.h"
Ali Tofighf3592cb2022-08-16 12:44:3824#include "absl/strings/string_view.h"
Danil Chapovalovdc03d872024-10-24 14:16:4725#include "api/audio/builtin_audio_processing_builder.h"
Sam Zackrisson03cb7e52021-12-06 14:40:0426#include "api/audio/echo_detector_creator.h"
Danil Chapovalov9c21f632024-10-16 06:29:5927#include "api/environment/environment_factory.h"
Niels Möller105711e2022-06-14 13:48:2628#include "api/make_ref_counted.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3129#include "common_audio/include/audio_util.h"
30#include "common_audio/resampler/include/push_resampler.h"
31#include "common_audio/resampler/push_sinc_resampler.h"
32#include "common_audio/signal_processing/include/signal_processing_library.h"
33#include "modules/audio_processing/aec_dump/aec_dump_factory.h"
34#include "modules/audio_processing/audio_processing_impl.h"
Sam Zackrisson0beac582017-09-25 10:04:0235#include "modules/audio_processing/include/mock_audio_processing.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3136#include "modules/audio_processing/test/protobuf_utils.h"
37#include "modules/audio_processing/test/test_utils.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3138#include "rtc_base/arraysize.h"
39#include "rtc_base/checks.h"
Steve Anton10542f22019-01-11 17:11:0040#include "rtc_base/fake_clock.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3141#include "rtc_base/gtest_prod_util.h"
Mirko Bonadei5b86f0a2017-11-29 14:20:2642#include "rtc_base/numerics/safe_conversions.h"
Karl Wiberge40468b2017-11-22 09:42:2643#include "rtc_base/numerics/safe_minmax.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3144#include "rtc_base/protobuf_utils.h"
Jonas Olsson366a50c2018-09-06 11:41:3045#include "rtc_base/strings/string_builder.h"
Alessio Bazzicac054e782018-04-16 10:10:0946#include "rtc_base/swap_queue.h"
Niels Möllera12c42a2018-07-25 14:05:4847#include "rtc_base/system/arch.h"
Danil Chapovalov07122bc2019-03-26 13:37:0148#include "rtc_base/task_queue_for_test.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3149#include "rtc_base/thread.h"
Per Åhgrena43178c2020-09-25 10:02:3250#include "system_wrappers/include/cpu_features_wrapper.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3151#include "test/gtest.h"
Steve Anton10542f22019-01-11 17:11:0052#include "test/testsupport/file_utils.h"
kwiberg77eab702016-09-29 00:42:0153
leozwang@webrtc.orga3736342012-03-16 21:36:0054#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
Björn Tereliusefeeba02023-10-31 14:44:5055#include "external/webrtc/webrtc/modules/audio_processing/debug.pb.h"
leozwang@webrtc.org534e4952012-10-22 21:21:5256#include "external/webrtc/webrtc/modules/audio_processing/test/unittest.pb.h"
leozwang@webrtc.orga3736342012-03-16 21:36:0057#else
Björn Tereliusefeeba02023-10-31 14:44:5058#include "modules/audio_processing/debug.pb.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3159#include "modules/audio_processing/test/unittest.pb.h"
leozwang@webrtc.orga3736342012-03-16 21:36:0060#endif
niklase@google.com470e71d2011-07-07 08:21:2561
Sam Zackrisson6558fa52019-08-26 08:12:4162ABSL_FLAG(bool,
63 write_apm_ref_data,
64 false,
65 "Write ApmTest.Process results to file, instead of comparing results "
66 "to the existing reference data file.");
67
andrew@webrtc.org27c69802014-02-18 20:24:5668namespace webrtc {
niklase@google.com470e71d2011-07-07 08:21:2569namespace {
andrew@webrtc.org17e40642014-03-04 20:58:1370
Danil Chapovalova5d29062024-12-18 12:07:4271using ::testing::_;
72using ::testing::WithoutArgs;
73
Sam Zackrisson3bd444f2022-08-03 12:37:0074// All sample rates used by APM internally during processing. Other input /
75// output rates are resampled to / from one of these.
76const int kProcessSampleRates[] = {16000, 32000, 48000};
andrew@webrtc.org07bf9a02012-05-05 00:32:0077
ekmeyerson60d9b332015-08-14 17:35:5578enum StreamDirection { kForward = 0, kReverse };
79
andrew@webrtc.orgddbb8a22014-04-22 21:00:0480void ConvertToFloat(const int16_t* int_data, ChannelBuffer<float>* cb) {
Jonas Olssona4d87372019-07-05 17:08:3381 ChannelBuffer<int16_t> cb_int(cb->num_frames(), cb->num_channels());
82 Deinterleave(int_data, cb->num_frames(), cb->num_channels(),
andrew@webrtc.orga8b97372014-03-10 22:26:1283 cb_int.channels());
Peter Kasting69558702016-01-13 00:26:3584 for (size_t i = 0; i < cb->num_channels(); ++i) {
Jonas Olssona4d87372019-07-05 17:08:3385 S16ToFloat(cb_int.channels()[i], cb->num_frames(), cb->channels()[i]);
aluebs@webrtc.orgd35a5c32015-02-10 22:52:1586 }
andrew@webrtc.orga8b97372014-03-10 22:26:1287}
andrew@webrtc.org17e40642014-03-04 20:58:1388
Per Åhgren2507f8c2020-03-19 11:33:2989void ConvertToFloat(const Int16FrameData& frame, ChannelBuffer<float>* cb) {
90 ConvertToFloat(frame.data.data(), cb);
andrew@webrtc.orgddbb8a22014-04-22 21:00:0491}
92
Jonas Olssona4d87372019-07-05 17:08:3393void MixStereoToMono(const float* stereo,
94 float* mono,
pkasting25702cb2016-01-08 21:50:2795 size_t samples_per_channel) {
96 for (size_t i = 0; i < samples_per_channel; ++i)
andrew@webrtc.orgddbb8a22014-04-22 21:00:0497 mono[i] = (stereo[i * 2] + stereo[i * 2 + 1]) / 2;
andrew@webrtc.org81865342012-10-27 00:28:2798}
99
Jonas Olssona4d87372019-07-05 17:08:33100void MixStereoToMono(const int16_t* stereo,
101 int16_t* mono,
pkasting25702cb2016-01-08 21:50:27102 size_t samples_per_channel) {
103 for (size_t i = 0; i < samples_per_channel; ++i)
andrew@webrtc.orgddbb8a22014-04-22 21:00:04104 mono[i] = (stereo[i * 2] + stereo[i * 2 + 1]) >> 1;
105}
106
pkasting25702cb2016-01-08 21:50:27107void CopyLeftToRightChannel(int16_t* stereo, size_t samples_per_channel) {
108 for (size_t i = 0; i < samples_per_channel; i++) {
andrew@webrtc.org81865342012-10-27 00:28:27109 stereo[i * 2 + 1] = stereo[i * 2];
110 }
111}
112
yujo36b1a5f2017-06-12 19:45:32113void VerifyChannelsAreEqual(const int16_t* stereo, size_t samples_per_channel) {
pkasting25702cb2016-01-08 21:50:27114 for (size_t i = 0; i < samples_per_channel; i++) {
andrew@webrtc.org81865342012-10-27 00:28:27115 EXPECT_EQ(stereo[i * 2 + 1], stereo[i * 2]);
116 }
117}
118
andrew@webrtc.org17e40642014-03-04 20:58:13119void EnableAllAPComponents(AudioProcessing* ap) {
Sam Zackrissonb3b47ad2018-08-17 14:26:14120 AudioProcessing::Config apm_config = ap->GetConfig();
121 apm_config.echo_canceller.enabled = true;
andrew@webrtc.org17e40642014-03-04 20:58:13122#if defined(WEBRTC_AUDIOPROC_FIXED_PROFILE)
Sam Zackrissonb3b47ad2018-08-17 14:26:14123 apm_config.echo_canceller.mobile_mode = true;
Sam Zackrissonf0d1c032019-03-27 12:28:08124
125 apm_config.gain_controller1.enabled = true;
126 apm_config.gain_controller1.mode =
127 AudioProcessing::Config::GainController1::kAdaptiveDigital;
andrew@webrtc.org17e40642014-03-04 20:58:13128#elif defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE)
Sam Zackrissonb3b47ad2018-08-17 14:26:14129 apm_config.echo_canceller.mobile_mode = false;
andrew@webrtc.org17e40642014-03-04 20:58:13130
Sam Zackrissonf0d1c032019-03-27 12:28:08131 apm_config.gain_controller1.enabled = true;
132 apm_config.gain_controller1.mode =
133 AudioProcessing::Config::GainController1::kAdaptiveAnalog;
andrew@webrtc.org17e40642014-03-04 20:58:13134#endif
Sam Zackrisson2a959d92018-07-23 14:48:07135
saza0bad15f2019-10-16 09:46:11136 apm_config.noise_suppression.enabled = true;
137
peah8271d042016-11-22 15:24:52138 apm_config.high_pass_filter.enabled = true;
Per Åhgrenc0424252019-12-10 12:04:15139 apm_config.pipeline.maximum_internal_processing_rate = 48000;
peah8271d042016-11-22 15:24:52140 ap->ApplyConfig(apm_config);
andrew@webrtc.org17e40642014-03-04 20:58:13141}
142
bjornv@webrtc.org8dd60cc2014-09-11 08:36:35143// These functions are only used by ApmTest.Process.
andrew@webrtc.orgd7696c42013-12-03 23:39:16144template <class T>
145T AbsValue(T a) {
Jonas Olssona4d87372019-07-05 17:08:33146 return a > 0 ? a : -a;
andrew@webrtc.orgd7696c42013-12-03 23:39:16147}
148
Per Åhgren2507f8c2020-03-19 11:33:29149int16_t MaxAudioFrame(const Int16FrameData& frame) {
Per Åhgren2507f8c2020-03-19 11:33:29150 int16_t max_data = AbsValue(frame.data[0]);
Tommi5f163fc2024-11-19 10:53:51151 for (size_t i = 1; i < frame.size(); i++) {
Per Åhgren2507f8c2020-03-19 11:33:29152 max_data = std::max(max_data, AbsValue(frame.data[i]));
andrew@webrtc.orgd7696c42013-12-03 23:39:16153 }
154
155 return max_data;
156}
157
Ali Tofighf3592cb2022-08-16 12:44:38158void OpenFileAndWriteMessage(absl::string_view filename,
mbonadei7c2c8432017-04-07 07:59:12159 const MessageLite& msg) {
Ali Tofighf3592cb2022-08-16 12:44:38160 FILE* file = fopen(std::string(filename).c_str(), "wb");
andrew@webrtc.orga8b97372014-03-10 22:26:12161 ASSERT_TRUE(file != NULL);
162
Mirko Bonadei5b86f0a2017-11-29 14:20:26163 int32_t size = rtc::checked_cast<int32_t>(msg.ByteSizeLong());
andrew@webrtc.org81865342012-10-27 00:28:27164 ASSERT_GT(size, 0);
kwiberg62eaacf2016-02-17 14:39:05165 std::unique_ptr<uint8_t[]> array(new uint8_t[size]);
andrew@webrtc.orga8b97372014-03-10 22:26:12166 ASSERT_TRUE(msg.SerializeToArray(array.get(), size));
andrew@webrtc.org81865342012-10-27 00:28:27167
andrew@webrtc.orga8b97372014-03-10 22:26:12168 ASSERT_EQ(1u, fwrite(&size, sizeof(size), 1, file));
andrew@webrtc.org81865342012-10-27 00:28:27169 ASSERT_EQ(static_cast<size_t>(size),
Jonas Olssona4d87372019-07-05 17:08:33170 fwrite(array.get(), sizeof(array[0]), size, file));
andrew@webrtc.org81865342012-10-27 00:28:27171 fclose(file);
172}
173
Ali Tofighf3592cb2022-08-16 12:44:38174std::string ResourceFilePath(absl::string_view name, int sample_rate_hz) {
Jonas Olsson366a50c2018-09-06 11:41:30175 rtc::StringBuilder ss;
andrew@webrtc.orgddbb8a22014-04-22 21:00:04176 // Resource files are all stereo.
177 ss << name << sample_rate_hz / 1000 << "_stereo";
178 return test::ResourcePath(ss.str(), "pcm");
179}
180
pbos@webrtc.orga525c982015-01-12 17:31:18181// Temporary filenames unique to this process. Used to be able to run these
182// tests in parallel as each process needs to be running in isolation they can't
183// have competing filenames.
184std::map<std::string, std::string> temp_filenames;
185
Ali Tofighf3592cb2022-08-16 12:44:38186std::string OutputFilePath(absl::string_view name,
andrew@webrtc.orgf26c9e82014-04-24 03:46:46187 int input_rate,
188 int output_rate,
ekmeyerson60d9b332015-08-14 17:35:55189 int reverse_input_rate,
190 int reverse_output_rate,
Peter Kasting69558702016-01-13 00:26:35191 size_t num_input_channels,
192 size_t num_output_channels,
193 size_t num_reverse_input_channels,
194 size_t num_reverse_output_channels,
ekmeyerson60d9b332015-08-14 17:35:55195 StreamDirection file_direction) {
Jonas Olsson366a50c2018-09-06 11:41:30196 rtc::StringBuilder ss;
ekmeyerson60d9b332015-08-14 17:35:55197 ss << name << "_i" << num_input_channels << "_" << input_rate / 1000 << "_ir"
198 << num_reverse_input_channels << "_" << reverse_input_rate / 1000 << "_";
andrew@webrtc.orgddbb8a22014-04-22 21:00:04199 if (num_output_channels == 1) {
200 ss << "mono";
201 } else if (num_output_channels == 2) {
202 ss << "stereo";
203 } else {
Artem Titovd3251962021-11-15 15:57:07204 RTC_DCHECK_NOTREACHED();
andrew@webrtc.orgddbb8a22014-04-22 21:00:04205 }
ekmeyerson60d9b332015-08-14 17:35:55206 ss << output_rate / 1000;
207 if (num_reverse_output_channels == 1) {
208 ss << "_rmono";
209 } else if (num_reverse_output_channels == 2) {
210 ss << "_rstereo";
211 } else {
Artem Titovd3251962021-11-15 15:57:07212 RTC_DCHECK_NOTREACHED();
ekmeyerson60d9b332015-08-14 17:35:55213 }
214 ss << reverse_output_rate / 1000;
215 ss << "_d" << file_direction << "_pcm";
andrew@webrtc.orgddbb8a22014-04-22 21:00:04216
pbos@webrtc.orga525c982015-01-12 17:31:18217 std::string filename = ss.str();
pbosbb36fdf2015-07-09 14:48:14218 if (temp_filenames[filename].empty())
pbos@webrtc.orga525c982015-01-12 17:31:18219 temp_filenames[filename] = test::TempFilename(test::OutputPath(), filename);
220 return temp_filenames[filename];
andrew@webrtc.orgddbb8a22014-04-22 21:00:04221}
222
pbos@webrtc.org200ac002015-02-03 14:14:01223void ClearTempFiles() {
224 for (auto& kv : temp_filenames)
225 remove(kv.second.c_str());
226}
227
Gustaf Ullberg8ffeeb22017-10-11 09:42:38228// Only remove "out" files. Keep "ref" files.
229void ClearTempOutFiles() {
230 for (auto it = temp_filenames.begin(); it != temp_filenames.end();) {
231 const std::string& filename = it->first;
232 if (filename.substr(0, 3).compare("out") == 0) {
233 remove(it->second.c_str());
234 temp_filenames.erase(it++);
235 } else {
236 it++;
237 }
238 }
239}
240
Ali Tofighf3592cb2022-08-16 12:44:38241void OpenFileAndReadMessage(absl::string_view filename, MessageLite* msg) {
242 FILE* file = fopen(std::string(filename).c_str(), "rb");
andrew@webrtc.orga8b97372014-03-10 22:26:12243 ASSERT_TRUE(file != NULL);
244 ReadMessageFromFile(file, msg);
andrew@webrtc.org81865342012-10-27 00:28:27245 fclose(file);
246}
247
Sam Zackrisson3bd444f2022-08-03 12:37:00248// Reads a 10 ms chunk (actually AudioProcessing::GetFrameSize() samples per
249// channel) of int16 interleaved audio from the given (assumed stereo) file,
250// converts to deinterleaved float (optionally downmixing) and returns the
251// result in `cb`. Returns false if the file ended (or on error) and true
252// otherwise.
aluebs@webrtc.orgd82f55d2015-01-15 18:07:21253//
Artem Titov0b489302021-07-28 18:50:03254// `int_data` and `float_data` are just temporary space that must be
aluebs@webrtc.orgd82f55d2015-01-15 18:07:21255// sufficiently large to hold the 10 ms chunk.
Jonas Olssona4d87372019-07-05 17:08:33256bool ReadChunk(FILE* file,
257 int16_t* int_data,
258 float* float_data,
aluebs@webrtc.orgd82f55d2015-01-15 18:07:21259 ChannelBuffer<float>* cb) {
260 // The files always contain stereo audio.
aluebs@webrtc.orgd35a5c32015-02-10 22:52:15261 size_t frame_size = cb->num_frames() * 2;
aluebs@webrtc.orgd82f55d2015-01-15 18:07:21262 size_t read_count = fread(int_data, sizeof(int16_t), frame_size, file);
263 if (read_count != frame_size) {
264 // Check that the file really ended.
kwiberg9e2be5f2016-09-14 12:23:22265 RTC_DCHECK(feof(file));
aluebs@webrtc.orgd82f55d2015-01-15 18:07:21266 return false; // This is expected.
267 }
268
269 S16ToFloat(int_data, frame_size, float_data);
270 if (cb->num_channels() == 1) {
aluebs@webrtc.orgd35a5c32015-02-10 22:52:15271 MixStereoToMono(float_data, cb->channels()[0], cb->num_frames());
aluebs@webrtc.orgd82f55d2015-01-15 18:07:21272 } else {
Jonas Olssona4d87372019-07-05 17:08:33273 Deinterleave(float_data, cb->num_frames(), 2, cb->channels());
aluebs@webrtc.orgd82f55d2015-01-15 18:07:21274 }
275
276 return true;
277}
278
Per Åhgrena43178c2020-09-25 10:02:32279// Returns the reference file name that matches the current CPU
280// architecture/optimizations.
281std::string GetReferenceFilename() {
282#if defined(WEBRTC_AUDIOPROC_FIXED_PROFILE)
283 return test::ResourcePath("audio_processing/output_data_fixed", "pb");
284#elif defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE)
285 if (GetCPUInfo(kAVX2) != 0) {
286 return test::ResourcePath("audio_processing/output_data_float_avx2", "pb");
287 }
288 return test::ResourcePath("audio_processing/output_data_float", "pb");
289#endif
290}
291
Alessio Bazzica85a126e2022-08-11 13:48:54292// Flag that can temporarily be enabled for local debugging to inspect
293// `ApmTest.VerifyDebugDump(Int|Float)` failures. Do not upload code changes
294// with this flag set to true.
295constexpr bool kDumpWhenExpectMessageEqFails = false;
296
297// Checks the debug constants values used in this file so that no code change is
298// submitted with values temporarily used for local debugging.
299TEST(ApmUnitTests, CheckDebugConstants) {
300 ASSERT_FALSE(kDumpWhenExpectMessageEqFails);
301}
302
303// Expects the equality of `actual` and `expected` by inspecting a hard-coded
304// subset of `audioproc::Stream` fields.
305void ExpectStreamFieldsEq(const audioproc::Stream& actual,
306 const audioproc::Stream& expected) {
307 EXPECT_EQ(actual.input_data(), expected.input_data());
308 EXPECT_EQ(actual.output_data(), expected.output_data());
309 EXPECT_EQ(actual.delay(), expected.delay());
310 EXPECT_EQ(actual.drift(), expected.drift());
Alessio Bazzica3153b362022-09-05 14:05:24311 EXPECT_EQ(actual.applied_input_volume(), expected.applied_input_volume());
Alessio Bazzica85a126e2022-08-11 13:48:54312 EXPECT_EQ(actual.keypress(), expected.keypress());
313}
314
315// Expects the equality of `actual` and `expected` by inspecting a hard-coded
316// subset of `audioproc::Event` fields.
317void ExpectEventFieldsEq(const audioproc::Event& actual,
318 const audioproc::Event& expected) {
319 EXPECT_EQ(actual.type(), expected.type());
320 if (actual.type() != expected.type()) {
321 return;
322 }
323 switch (actual.type()) {
324 case audioproc::Event::STREAM:
325 ExpectStreamFieldsEq(actual.stream(), expected.stream());
326 break;
327 default:
328 // Not implemented.
329 break;
330 }
331}
332
333// Returns true if the `actual` and `expected` byte streams share the same size
334// and contain the same data. If they differ and `kDumpWhenExpectMessageEqFails`
335// is true, checks the equality of a subset of `audioproc::Event` (nested)
336// fields.
337bool ExpectMessageEq(rtc::ArrayView<const uint8_t> actual,
338 rtc::ArrayView<const uint8_t> expected) {
339 EXPECT_EQ(actual.size(), expected.size());
340 if (actual.size() != expected.size()) {
341 return false;
342 }
343 if (memcmp(actual.data(), expected.data(), actual.size()) == 0) {
344 // Same message. No need to parse.
345 return true;
346 }
347 if (kDumpWhenExpectMessageEqFails) {
348 // Parse differing messages and expect equality to produce detailed error
349 // messages.
350 audioproc::Event event_actual, event_expected;
351 RTC_DCHECK(event_actual.ParseFromArray(actual.data(), actual.size()));
352 RTC_DCHECK(event_expected.ParseFromArray(expected.data(), expected.size()));
353 ExpectEventFieldsEq(event_actual, event_expected);
354 }
355 return false;
356}
357
niklase@google.com470e71d2011-07-07 08:21:25358class ApmTest : public ::testing::Test {
359 protected:
360 ApmTest();
361 virtual void SetUp();
362 virtual void TearDown();
andrew@webrtc.org755b04a2011-11-15 16:57:56363
Mirko Bonadei71061bc2019-06-04 07:01:51364 static void SetUpTestSuite() {}
andrew@webrtc.org755b04a2011-11-15 16:57:56365
Mirko Bonadei71061bc2019-06-04 07:01:51366 static void TearDownTestSuite() { ClearTempFiles(); }
andrew@webrtc.orgdaacee82012-02-07 00:01:04367
andrew@webrtc.orga8b97372014-03-10 22:26:12368 // Used to select between int and float interface tests.
Jonas Olssona4d87372019-07-05 17:08:33369 enum Format { kIntFormat, kFloatFormat };
andrew@webrtc.orga8b97372014-03-10 22:26:12370
371 void Init(int sample_rate_hz,
andrew@webrtc.orgddbb8a22014-04-22 21:00:04372 int output_sample_rate_hz,
andrew@webrtc.orga8b97372014-03-10 22:26:12373 int reverse_sample_rate_hz,
Peter Kasting69558702016-01-13 00:26:35374 size_t num_input_channels,
375 size_t num_output_channels,
376 size_t num_reverse_channels,
andrew@webrtc.orgdaacee82012-02-07 00:01:04377 bool open_output_file);
andrew@webrtc.org17e40642014-03-04 20:58:13378 void Init(AudioProcessing* ap);
andrew@webrtc.org07bf9a02012-05-05 00:32:00379 void EnableAllComponents();
Per Åhgren2507f8c2020-03-19 11:33:29380 bool ReadFrame(FILE* file, Int16FrameData* frame);
381 bool ReadFrame(FILE* file, Int16FrameData* frame, ChannelBuffer<float>* cb);
382 void ReadFrameWithRewind(FILE* file, Int16FrameData* frame);
Jonas Olssona4d87372019-07-05 17:08:33383 void ReadFrameWithRewind(FILE* file,
Per Åhgren2507f8c2020-03-19 11:33:29384 Int16FrameData* frame,
andrew@webrtc.org17e40642014-03-04 20:58:13385 ChannelBuffer<float>* cb);
Jonas Olssona4d87372019-07-05 17:08:33386 void ProcessDelayVerificationTest(int delay_ms,
387 int system_delay_ms,
388 int delay_min,
389 int delay_max);
Michael Graczyk86c6d332015-07-23 18:41:39390 void TestChangingChannelsInt16Interface(
Peter Kasting69558702016-01-13 00:26:35391 size_t num_channels,
Michael Graczyk86c6d332015-07-23 18:41:39392 AudioProcessing::Error expected_return);
Peter Kasting69558702016-01-13 00:26:35393 void TestChangingForwardChannels(size_t num_in_channels,
394 size_t num_out_channels,
Michael Graczyk86c6d332015-07-23 18:41:39395 AudioProcessing::Error expected_return);
Peter Kasting69558702016-01-13 00:26:35396 void TestChangingReverseChannels(size_t num_rev_channels,
Michael Graczyk86c6d332015-07-23 18:41:39397 AudioProcessing::Error expected_return);
andrew@webrtc.org27c69802014-02-18 20:24:56398 void RunQuantizedVolumeDoesNotGetStuckTest(int sample_rate);
399 void RunManualVolumeChangeIsPossibleTest(int sample_rate);
andrew@webrtc.orga8b97372014-03-10 22:26:12400 void StreamParametersTest(Format format);
andrew@webrtc.orga8b97372014-03-10 22:26:12401 int ProcessStreamChooser(Format format);
402 int AnalyzeReverseStreamChooser(Format format);
Ali Tofighf3592cb2022-08-16 12:44:38403 void ProcessDebugDump(absl::string_view in_filename,
404 absl::string_view out_filename,
ivocd66b44d2016-01-15 11:06:36405 Format format,
406 int max_size_bytes);
andrew@webrtc.orga8b97372014-03-10 22:26:12407 void VerifyDebugDumpTest(Format format);
andrew@webrtc.orgdaacee82012-02-07 00:01:04408
409 const std::string output_path_;
andrew@webrtc.orgdaacee82012-02-07 00:01:04410 const std::string ref_filename_;
Niels Möller4f776ac2021-07-02 09:30:54411 rtc::scoped_refptr<AudioProcessing> apm_;
Per Åhgren2507f8c2020-03-19 11:33:29412 Int16FrameData frame_;
413 Int16FrameData revframe_;
Sam Zackrisson03cb7e52021-12-06 14:40:04414 std::unique_ptr<ChannelBuffer<float>> float_cb_;
415 std::unique_ptr<ChannelBuffer<float>> revfloat_cb_;
andrew@webrtc.orgddbb8a22014-04-22 21:00:04416 int output_sample_rate_hz_;
Peter Kasting69558702016-01-13 00:26:35417 size_t num_output_channels_;
niklase@google.com470e71d2011-07-07 08:21:25418 FILE* far_file_;
419 FILE* near_file_;
andrew@webrtc.orgdaacee82012-02-07 00:01:04420 FILE* out_file_;
niklase@google.com470e71d2011-07-07 08:21:25421};
422
423ApmTest::ApmTest()
andrew@webrtc.org27c69802014-02-18 20:24:56424 : output_path_(test::OutputPath()),
Per Åhgrena43178c2020-09-25 10:02:32425 ref_filename_(GetReferenceFilename()),
andrew@webrtc.orgddbb8a22014-04-22 21:00:04426 output_sample_rate_hz_(0),
andrew@webrtc.orga8b97372014-03-10 22:26:12427 num_output_channels_(0),
ajm@google.com22e65152011-07-18 18:03:01428 far_file_(NULL),
andrew@webrtc.orgdaacee82012-02-07 00:01:04429 near_file_(NULL),
aluebs@webrtc.orgc9ee4122014-02-03 14:41:57430 out_file_(NULL) {
Danil Chapovalovdc03d872024-10-24 14:16:47431 apm_ = BuiltinAudioProcessingBuilder().Build(CreateEnvironment());
Per Åhgrenc0424252019-12-10 12:04:15432 AudioProcessing::Config apm_config = apm_->GetConfig();
Per Åhgren0695df12020-01-13 13:43:13433 apm_config.gain_controller1.analog_gain_controller.enabled = false;
Per Åhgrenc0424252019-12-10 12:04:15434 apm_config.pipeline.maximum_internal_processing_rate = 48000;
435 apm_->ApplyConfig(apm_config);
aluebs@webrtc.orgc9ee4122014-02-03 14:41:57436}
niklase@google.com470e71d2011-07-07 08:21:25437
438void ApmTest::SetUp() {
andrew@webrtc.orgf3930e92013-09-18 22:37:32439 ASSERT_TRUE(apm_.get() != NULL);
niklase@google.com470e71d2011-07-07 08:21:25440
andrew@webrtc.orgddbb8a22014-04-22 21:00:04441 Init(32000, 32000, 32000, 2, 2, 2, false);
niklase@google.com470e71d2011-07-07 08:21:25442}
443
444void ApmTest::TearDown() {
niklase@google.com470e71d2011-07-07 08:21:25445 if (far_file_) {
446 ASSERT_EQ(0, fclose(far_file_));
447 }
448 far_file_ = NULL;
449
450 if (near_file_) {
451 ASSERT_EQ(0, fclose(near_file_));
452 }
453 near_file_ = NULL;
454
andrew@webrtc.orgdaacee82012-02-07 00:01:04455 if (out_file_) {
456 ASSERT_EQ(0, fclose(out_file_));
457 }
458 out_file_ = NULL;
niklase@google.com470e71d2011-07-07 08:21:25459}
460
andrew@webrtc.org17e40642014-03-04 20:58:13461void ApmTest::Init(AudioProcessing* ap) {
Sam Zackrisson70770ac2019-10-25 08:56:53462 ASSERT_EQ(
Tommi5f163fc2024-11-19 10:53:51463 AudioProcessing::kNoError,
464 ap->Initialize({{{frame_.sample_rate_hz, frame_.num_channels()},
Sam Zackrisson70770ac2019-10-25 08:56:53465 {output_sample_rate_hz_, num_output_channels_},
Tommi5f163fc2024-11-19 10:53:51466 {revframe_.sample_rate_hz, revframe_.num_channels()},
467 {revframe_.sample_rate_hz, revframe_.num_channels()}}}));
andrew@webrtc.org17e40642014-03-04 20:58:13468}
469
andrew@webrtc.orga8b97372014-03-10 22:26:12470void ApmTest::Init(int sample_rate_hz,
andrew@webrtc.orgddbb8a22014-04-22 21:00:04471 int output_sample_rate_hz,
andrew@webrtc.orga8b97372014-03-10 22:26:12472 int reverse_sample_rate_hz,
Peter Kasting69558702016-01-13 00:26:35473 size_t num_input_channels,
474 size_t num_output_channels,
475 size_t num_reverse_channels,
andrew@webrtc.orgdaacee82012-02-07 00:01:04476 bool open_output_file) {
Sam Zackrisson70770ac2019-10-25 08:56:53477 SetContainerFormat(sample_rate_hz, num_input_channels, &frame_, &float_cb_);
andrew@webrtc.orgddbb8a22014-04-22 21:00:04478 output_sample_rate_hz_ = output_sample_rate_hz;
andrew@webrtc.orga8b97372014-03-10 22:26:12479 num_output_channels_ = num_output_channels;
andrew@webrtc.orgdaacee82012-02-07 00:01:04480
Sam Zackrisson70770ac2019-10-25 08:56:53481 SetContainerFormat(reverse_sample_rate_hz, num_reverse_channels, &revframe_,
andrew@webrtc.orga8b97372014-03-10 22:26:12482 &revfloat_cb_);
andrew@webrtc.org17e40642014-03-04 20:58:13483 Init(apm_.get());
andrew@webrtc.org60730cf2014-01-07 17:45:09484
andrew@webrtc.orgdaacee82012-02-07 00:01:04485 if (far_file_) {
486 ASSERT_EQ(0, fclose(far_file_));
487 }
488 std::string filename = ResourceFilePath("far", sample_rate_hz);
489 far_file_ = fopen(filename.c_str(), "rb");
Jonas Olssona4d87372019-07-05 17:08:33490 ASSERT_TRUE(far_file_ != NULL) << "Could not open file " << filename << "\n";
andrew@webrtc.orgdaacee82012-02-07 00:01:04491
492 if (near_file_) {
493 ASSERT_EQ(0, fclose(near_file_));
494 }
495 filename = ResourceFilePath("near", sample_rate_hz);
496 near_file_ = fopen(filename.c_str(), "rb");
Jonas Olssona4d87372019-07-05 17:08:33497 ASSERT_TRUE(near_file_ != NULL) << "Could not open file " << filename << "\n";
andrew@webrtc.orgdaacee82012-02-07 00:01:04498
499 if (open_output_file) {
500 if (out_file_) {
501 ASSERT_EQ(0, fclose(out_file_));
502 }
ekmeyerson60d9b332015-08-14 17:35:55503 filename = OutputFilePath(
504 "out", sample_rate_hz, output_sample_rate_hz, reverse_sample_rate_hz,
505 reverse_sample_rate_hz, num_input_channels, num_output_channels,
506 num_reverse_channels, num_reverse_channels, kForward);
andrew@webrtc.orgdaacee82012-02-07 00:01:04507 out_file_ = fopen(filename.c_str(), "wb");
Jonas Olssona4d87372019-07-05 17:08:33508 ASSERT_TRUE(out_file_ != NULL)
509 << "Could not open file " << filename << "\n";
andrew@webrtc.orgdaacee82012-02-07 00:01:04510 }
511}
512
andrew@webrtc.org07bf9a02012-05-05 00:32:00513void ApmTest::EnableAllComponents() {
andrew@webrtc.org17e40642014-03-04 20:58:13514 EnableAllAPComponents(apm_.get());
andrew@webrtc.org07bf9a02012-05-05 00:32:00515}
516
Jonas Olssona4d87372019-07-05 17:08:33517bool ApmTest::ReadFrame(FILE* file,
Per Åhgren2507f8c2020-03-19 11:33:29518 Int16FrameData* frame,
andrew@webrtc.org17e40642014-03-04 20:58:13519 ChannelBuffer<float>* cb) {
andrew@webrtc.org07bf9a02012-05-05 00:32:00520 // The files always contain stereo audio.
Tommi5f163fc2024-11-19 10:53:51521 size_t frame_size = frame->samples_per_channel() * 2;
Jonas Olssona4d87372019-07-05 17:08:33522 size_t read_count =
Per Åhgren2507f8c2020-03-19 11:33:29523 fread(frame->data.data(), sizeof(int16_t), frame_size, file);
andrew@webrtc.org07bf9a02012-05-05 00:32:00524 if (read_count != frame_size) {
525 // Check that the file really ended.
526 EXPECT_NE(0, feof(file));
527 return false; // This is expected.
528 }
529
Tommi5f163fc2024-11-19 10:53:51530 if (frame->num_channels() == 1) {
Per Åhgren2507f8c2020-03-19 11:33:29531 MixStereoToMono(frame->data.data(), frame->data.data(),
Tommi5f163fc2024-11-19 10:53:51532 frame->samples_per_channel());
andrew@webrtc.org07bf9a02012-05-05 00:32:00533 }
534
andrew@webrtc.org17e40642014-03-04 20:58:13535 if (cb) {
andrew@webrtc.orga8b97372014-03-10 22:26:12536 ConvertToFloat(*frame, cb);
andrew@webrtc.org17e40642014-03-04 20:58:13537 }
andrew@webrtc.org07bf9a02012-05-05 00:32:00538 return true;
ajm@google.coma769fa52011-07-13 21:57:58539}
540
Per Åhgren2507f8c2020-03-19 11:33:29541bool ApmTest::ReadFrame(FILE* file, Int16FrameData* frame) {
andrew@webrtc.org17e40642014-03-04 20:58:13542 return ReadFrame(file, frame, NULL);
543}
544
andrew@webrtc.org27c69802014-02-18 20:24:56545// If the end of the file has been reached, rewind it and attempt to read the
546// frame again.
Dor Hen3fa21c82024-10-30 09:45:22547void ApmTest::ReadFrameWithRewind(FILE* /* file */,
548 Int16FrameData* /* frame */,
andrew@webrtc.org17e40642014-03-04 20:58:13549 ChannelBuffer<float>* cb) {
Sam Zackrisson70770ac2019-10-25 08:56:53550 if (!ReadFrame(near_file_, &frame_, cb)) {
andrew@webrtc.org27c69802014-02-18 20:24:56551 rewind(near_file_);
Sam Zackrisson70770ac2019-10-25 08:56:53552 ASSERT_TRUE(ReadFrame(near_file_, &frame_, cb));
andrew@webrtc.org27c69802014-02-18 20:24:56553 }
554}
555
Per Åhgren2507f8c2020-03-19 11:33:29556void ApmTest::ReadFrameWithRewind(FILE* file, Int16FrameData* frame) {
andrew@webrtc.org17e40642014-03-04 20:58:13557 ReadFrameWithRewind(file, frame, NULL);
558}
559
andrew@webrtc.orga8b97372014-03-10 22:26:12560int ApmTest::ProcessStreamChooser(Format format) {
561 if (format == kIntFormat) {
Per Åhgren2507f8c2020-03-19 11:33:29562 return apm_->ProcessStream(
563 frame_.data.data(),
Tommi5f163fc2024-11-19 10:53:51564 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
565 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:58566 frame_.data.data());
andrew@webrtc.org17e40642014-03-04 20:58:13567 }
Jonas Olssona4d87372019-07-05 17:08:33568 return apm_->ProcessStream(
Gustaf Ullbergcb307262019-10-29 08:30:44569 float_cb_->channels(),
Tommi5f163fc2024-11-19 10:53:51570 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Gustaf Ullbergcb307262019-10-29 08:30:44571 StreamConfig(output_sample_rate_hz_, num_output_channels_),
Jonas Olssona4d87372019-07-05 17:08:33572 float_cb_->channels());
andrew@webrtc.org17e40642014-03-04 20:58:13573}
574
andrew@webrtc.orga8b97372014-03-10 22:26:12575int ApmTest::AnalyzeReverseStreamChooser(Format format) {
576 if (format == kIntFormat) {
Per Åhgren2507f8c2020-03-19 11:33:29577 return apm_->ProcessReverseStream(
578 revframe_.data.data(),
Tommi5f163fc2024-11-19 10:53:51579 StreamConfig(revframe_.sample_rate_hz, revframe_.num_channels()),
580 StreamConfig(revframe_.sample_rate_hz, revframe_.num_channels()),
Per Åhgren2507f8c2020-03-19 11:33:29581 revframe_.data.data());
andrew@webrtc.org17e40642014-03-04 20:58:13582 }
andrew@webrtc.org17e40642014-03-04 20:58:13583 return apm_->AnalyzeReverseStream(
Gustaf Ullbergcb307262019-10-29 08:30:44584 revfloat_cb_->channels(),
Tommi5f163fc2024-11-19 10:53:51585 StreamConfig(revframe_.sample_rate_hz, revframe_.num_channels()));
andrew@webrtc.org17e40642014-03-04 20:58:13586}
587
Jonas Olssona4d87372019-07-05 17:08:33588void ApmTest::ProcessDelayVerificationTest(int delay_ms,
589 int system_delay_ms,
590 int delay_min,
591 int delay_max) {
Artem Titov0b489302021-07-28 18:50:03592 // The `revframe_` and `frame_` should include the proper frame information,
bjornv@webrtc.org3e102492013-02-14 15:29:09593 // hence can be used for extracting information.
Per Åhgren2507f8c2020-03-19 11:33:29594 Int16FrameData tmp_frame;
595 std::queue<Int16FrameData*> frame_queue;
bjornv@webrtc.org3e102492013-02-14 15:29:09596 bool causal = true;
597
Sam Zackrisson70770ac2019-10-25 08:56:53598 tmp_frame.CopyFrom(revframe_);
Tommi5f163fc2024-11-19 10:53:51599 tmp_frame.FillData(0);
bjornv@webrtc.org3e102492013-02-14 15:29:09600
Tommi5f163fc2024-11-19 10:53:51601 EXPECT_EQ(AudioProcessing::kNoError, apm_->Initialize());
Artem Titov0b489302021-07-28 18:50:03602 // Initialize the `frame_queue` with empty frames.
bjornv@webrtc.org3e102492013-02-14 15:29:09603 int frame_delay = delay_ms / 10;
604 while (frame_delay < 0) {
Per Åhgren2507f8c2020-03-19 11:33:29605 Int16FrameData* frame = new Int16FrameData();
bjornv@webrtc.org3e102492013-02-14 15:29:09606 frame->CopyFrom(tmp_frame);
607 frame_queue.push(frame);
608 frame_delay++;
609 causal = false;
610 }
611 while (frame_delay > 0) {
Per Åhgren2507f8c2020-03-19 11:33:29612 Int16FrameData* frame = new Int16FrameData();
bjornv@webrtc.org3e102492013-02-14 15:29:09613 frame->CopyFrom(tmp_frame);
614 frame_queue.push(frame);
615 frame_delay--;
616 }
bjornv@webrtc.orgbbd47fc2014-01-13 08:54:34617 // Run for 4.5 seconds, skipping statistics from the first 2.5 seconds. We
618 // need enough frames with audio to have reliable estimates, but as few as
619 // possible to keep processing time down. 4.5 seconds seemed to be a good
620 // compromise for this recording.
bjornv@webrtc.org3e102492013-02-14 15:29:09621 for (int frame_count = 0; frame_count < 450; ++frame_count) {
Per Åhgren2507f8c2020-03-19 11:33:29622 Int16FrameData* frame = new Int16FrameData();
bjornv@webrtc.org3e102492013-02-14 15:29:09623 frame->CopyFrom(tmp_frame);
624 // Use the near end recording, since that has more speech in it.
625 ASSERT_TRUE(ReadFrame(near_file_, frame));
626 frame_queue.push(frame);
Per Åhgren2507f8c2020-03-19 11:33:29627 Int16FrameData* reverse_frame = frame;
628 Int16FrameData* process_frame = frame_queue.front();
bjornv@webrtc.org3e102492013-02-14 15:29:09629 if (!causal) {
630 reverse_frame = frame_queue.front();
631 // When we call ProcessStream() the frame is modified, so we can't use the
632 // pointer directly when things are non-causal. Use an intermediate frame
633 // and copy the data.
634 process_frame = &tmp_frame;
635 process_frame->CopyFrom(*frame);
636 }
Tommi5f163fc2024-11-19 10:53:51637 EXPECT_EQ(
638 AudioProcessing::kNoError,
639 apm_->ProcessReverseStream(reverse_frame->data.data(),
640 StreamConfig(reverse_frame->sample_rate_hz,
641 reverse_frame->num_channels()),
642 StreamConfig(reverse_frame->sample_rate_hz,
643 reverse_frame->num_channels()),
644 reverse_frame->data.data()));
645 EXPECT_EQ(AudioProcessing::kNoError,
646 apm_->set_stream_delay_ms(system_delay_ms));
647 EXPECT_EQ(AudioProcessing::kNoError,
Per Åhgren2507f8c2020-03-19 11:33:29648 apm_->ProcessStream(process_frame->data.data(),
649 StreamConfig(process_frame->sample_rate_hz,
Tommi5f163fc2024-11-19 10:53:51650 process_frame->num_channels()),
Per Åhgren2507f8c2020-03-19 11:33:29651 StreamConfig(process_frame->sample_rate_hz,
Tommi5f163fc2024-11-19 10:53:51652 process_frame->num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:58653 process_frame->data.data()));
bjornv@webrtc.org3e102492013-02-14 15:29:09654 frame = frame_queue.front();
655 frame_queue.pop();
656 delete frame;
657
bjornv@webrtc.orgbbd47fc2014-01-13 08:54:34658 if (frame_count == 250) {
bjornv@webrtc.org3e102492013-02-14 15:29:09659 // Discard the first delay metrics to avoid convergence effects.
Per Åhgrencf4c8722019-12-30 13:32:14660 static_cast<void>(apm_->GetStatistics());
bjornv@webrtc.org3e102492013-02-14 15:29:09661 }
662 }
663
664 rewind(near_file_);
665 while (!frame_queue.empty()) {
Per Åhgren2507f8c2020-03-19 11:33:29666 Int16FrameData* frame = frame_queue.front();
bjornv@webrtc.org3e102492013-02-14 15:29:09667 frame_queue.pop();
668 delete frame;
669 }
670 // Calculate expected delay estimate and acceptable regions. Further,
671 // limit them w.r.t. AEC delay estimation support.
Peter Kastingdce40cf2015-08-24 21:52:23672 const size_t samples_per_ms =
Tommi5f163fc2024-11-19 10:53:51673 rtc::SafeMin<size_t>(16u, frame_.samples_per_channel() / 10);
kwiberg07038562017-06-12 18:40:47674 const int expected_median =
675 rtc::SafeClamp<int>(delay_ms - system_delay_ms, delay_min, delay_max);
676 const int expected_median_high = rtc::SafeClamp<int>(
677 expected_median + rtc::dchecked_cast<int>(96 / samples_per_ms), delay_min,
Peter Kastingdce40cf2015-08-24 21:52:23678 delay_max);
kwiberg07038562017-06-12 18:40:47679 const int expected_median_low = rtc::SafeClamp<int>(
680 expected_median - rtc::dchecked_cast<int>(96 / samples_per_ms), delay_min,
Peter Kastingdce40cf2015-08-24 21:52:23681 delay_max);
bjornv@webrtc.org3e102492013-02-14 15:29:09682 // Verify delay metrics.
Per Åhgrencf4c8722019-12-30 13:32:14683 AudioProcessingStats stats = apm_->GetStatistics();
Sam Zackrissoncdf0e6d2018-09-17 09:05:17684 ASSERT_TRUE(stats.delay_median_ms.has_value());
685 int32_t median = *stats.delay_median_ms;
bjornv@webrtc.org3e102492013-02-14 15:29:09686 EXPECT_GE(expected_median_high, median);
687 EXPECT_LE(expected_median_low, median);
688}
689
andrew@webrtc.orga8b97372014-03-10 22:26:12690void ApmTest::StreamParametersTest(Format format) {
niklase@google.com470e71d2011-07-07 08:21:25691 // No errors when the components are disabled.
Tommi5f163fc2024-11-19 10:53:51692 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(format));
niklase@google.com470e71d2011-07-07 08:21:25693
andrew@webrtc.org1e916932011-11-29 18:28:57694 // -- Missing AGC level --
Sam Zackrisson41478c72019-10-15 08:10:26695 AudioProcessing::Config apm_config = apm_->GetConfig();
696 apm_config.gain_controller1.enabled = true;
697 apm_->ApplyConfig(apm_config);
Jonas Olssona4d87372019-07-05 17:08:33698 EXPECT_EQ(apm_->kStreamParameterNotSetError, ProcessStreamChooser(format));
niklase@google.com470e71d2011-07-07 08:21:25699
andrew@webrtc.org1e916932011-11-29 18:28:57700 // Resets after successful ProcessStream().
Sam Zackrisson41478c72019-10-15 08:10:26701 apm_->set_stream_analog_level(127);
Tommi5f163fc2024-11-19 10:53:51702 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(format));
Jonas Olssona4d87372019-07-05 17:08:33703 EXPECT_EQ(apm_->kStreamParameterNotSetError, ProcessStreamChooser(format));
niklase@google.com470e71d2011-07-07 08:21:25704
andrew@webrtc.org1e916932011-11-29 18:28:57705 // Other stream parameters set correctly.
Sam Zackrissoncdf0e6d2018-09-17 09:05:17706 apm_config.echo_canceller.enabled = true;
707 apm_config.echo_canceller.mobile_mode = false;
708 apm_->ApplyConfig(apm_config);
Tommi5f163fc2024-11-19 10:53:51709 EXPECT_EQ(AudioProcessing::kNoError, apm_->set_stream_delay_ms(100));
Jonas Olssona4d87372019-07-05 17:08:33710 EXPECT_EQ(apm_->kStreamParameterNotSetError, ProcessStreamChooser(format));
Sam Zackrisson41478c72019-10-15 08:10:26711 apm_config.gain_controller1.enabled = false;
712 apm_->ApplyConfig(apm_config);
andrew@webrtc.org1e916932011-11-29 18:28:57713
714 // -- Missing delay --
Tommi5f163fc2024-11-19 10:53:51715 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(format));
716 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(format));
andrew@webrtc.org1e916932011-11-29 18:28:57717
718 // Resets after successful ProcessStream().
Tommi5f163fc2024-11-19 10:53:51719 EXPECT_EQ(AudioProcessing::kNoError, apm_->set_stream_delay_ms(100));
720 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(format));
721 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(format));
andrew@webrtc.org1e916932011-11-29 18:28:57722
723 // Other stream parameters set correctly.
Sam Zackrisson41478c72019-10-15 08:10:26724 apm_config.gain_controller1.enabled = true;
725 apm_->ApplyConfig(apm_config);
726 apm_->set_stream_analog_level(127);
Tommi5f163fc2024-11-19 10:53:51727 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(format));
Sam Zackrisson41478c72019-10-15 08:10:26728 apm_config.gain_controller1.enabled = false;
729 apm_->ApplyConfig(apm_config);
andrew@webrtc.org1e916932011-11-29 18:28:57730
andrew@webrtc.org1e916932011-11-29 18:28:57731 // -- No stream parameters --
Tommi5f163fc2024-11-19 10:53:51732 EXPECT_EQ(AudioProcessing::kNoError, AnalyzeReverseStreamChooser(format));
733 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(format));
niklase@google.com470e71d2011-07-07 08:21:25734
andrew@webrtc.org1e916932011-11-29 18:28:57735 // -- All there --
Tommi5f163fc2024-11-19 10:53:51736 EXPECT_EQ(AudioProcessing::kNoError, apm_->set_stream_delay_ms(100));
Sam Zackrisson41478c72019-10-15 08:10:26737 apm_->set_stream_analog_level(127);
Tommi5f163fc2024-11-19 10:53:51738 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(format));
andrew@webrtc.org17e40642014-03-04 20:58:13739}
740
741TEST_F(ApmTest, StreamParametersInt) {
andrew@webrtc.orga8b97372014-03-10 22:26:12742 StreamParametersTest(kIntFormat);
andrew@webrtc.org17e40642014-03-04 20:58:13743}
744
745TEST_F(ApmTest, StreamParametersFloat) {
andrew@webrtc.orga8b97372014-03-10 22:26:12746 StreamParametersTest(kFloatFormat);
niklase@google.com470e71d2011-07-07 08:21:25747}
748
Michael Graczyk86c6d332015-07-23 18:41:39749void ApmTest::TestChangingChannelsInt16Interface(
Peter Kasting69558702016-01-13 00:26:35750 size_t num_channels,
Michael Graczyk86c6d332015-07-23 18:41:39751 AudioProcessing::Error expected_return) {
Tommi5f163fc2024-11-19 10:53:51752 frame_.set_num_channels(num_channels);
Per Åhgren2507f8c2020-03-19 11:33:29753
754 EXPECT_EQ(expected_return,
755 apm_->ProcessStream(
756 frame_.data.data(),
Tommi5f163fc2024-11-19 10:53:51757 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
758 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:58759 frame_.data.data()));
Per Åhgren2507f8c2020-03-19 11:33:29760 EXPECT_EQ(expected_return,
761 apm_->ProcessReverseStream(
762 frame_.data.data(),
Tommi5f163fc2024-11-19 10:53:51763 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
764 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Per Åhgren2507f8c2020-03-19 11:33:29765 frame_.data.data()));
andrew@webrtc.org60730cf2014-01-07 17:45:09766}
767
Michael Graczyk86c6d332015-07-23 18:41:39768void ApmTest::TestChangingForwardChannels(
Peter Kasting69558702016-01-13 00:26:35769 size_t num_in_channels,
770 size_t num_out_channels,
Michael Graczyk86c6d332015-07-23 18:41:39771 AudioProcessing::Error expected_return) {
Per Åhgren2507f8c2020-03-19 11:33:29772 const StreamConfig input_stream = {frame_.sample_rate_hz, num_in_channels};
Michael Graczyk86c6d332015-07-23 18:41:39773 const StreamConfig output_stream = {output_sample_rate_hz_, num_out_channels};
774
775 EXPECT_EQ(expected_return,
776 apm_->ProcessStream(float_cb_->channels(), input_stream,
777 output_stream, float_cb_->channels()));
778}
779
780void ApmTest::TestChangingReverseChannels(
Peter Kasting69558702016-01-13 00:26:35781 size_t num_rev_channels,
Michael Graczyk86c6d332015-07-23 18:41:39782 AudioProcessing::Error expected_return) {
783 const ProcessingConfig processing_config = {
Per Åhgren2507f8c2020-03-19 11:33:29784 {{frame_.sample_rate_hz, apm_->num_input_channels()},
ekmeyerson60d9b332015-08-14 17:35:55785 {output_sample_rate_hz_, apm_->num_output_channels()},
Per Åhgren2507f8c2020-03-19 11:33:29786 {frame_.sample_rate_hz, num_rev_channels},
787 {frame_.sample_rate_hz, num_rev_channels}}};
Michael Graczyk86c6d332015-07-23 18:41:39788
ekmeyerson60d9b332015-08-14 17:35:55789 EXPECT_EQ(
790 expected_return,
791 apm_->ProcessReverseStream(
792 float_cb_->channels(), processing_config.reverse_input_stream(),
793 processing_config.reverse_output_stream(), float_cb_->channels()));
Michael Graczyk86c6d332015-07-23 18:41:39794}
795
796TEST_F(ApmTest, ChannelsInt16Interface) {
797 // Testing number of invalid and valid channels.
798 Init(16000, 16000, 16000, 4, 4, 4, false);
799
800 TestChangingChannelsInt16Interface(0, apm_->kBadNumberChannelsError);
801
Peter Kasting69558702016-01-13 00:26:35802 for (size_t i = 1; i < 4; i++) {
Tommi5f163fc2024-11-19 10:53:51803 TestChangingChannelsInt16Interface(i, AudioProcessing::kNoError);
niklase@google.com470e71d2011-07-07 08:21:25804 EXPECT_EQ(i, apm_->num_input_channels());
niklase@google.com470e71d2011-07-07 08:21:25805 }
806}
807
Michael Graczyk86c6d332015-07-23 18:41:39808TEST_F(ApmTest, Channels) {
809 // Testing number of invalid and valid channels.
810 Init(16000, 16000, 16000, 4, 4, 4, false);
811
812 TestChangingForwardChannels(0, 1, apm_->kBadNumberChannelsError);
813 TestChangingReverseChannels(0, apm_->kBadNumberChannelsError);
814
Peter Kasting69558702016-01-13 00:26:35815 for (size_t i = 1; i < 4; ++i) {
816 for (size_t j = 0; j < 1; ++j) {
Michael Graczyk86c6d332015-07-23 18:41:39817 // Output channels much be one or match input channels.
818 if (j == 1 || i == j) {
Tommi5f163fc2024-11-19 10:53:51819 TestChangingForwardChannels(i, j, AudioProcessing::kNoError);
820 TestChangingReverseChannels(i, AudioProcessing::kNoError);
Michael Graczyk86c6d332015-07-23 18:41:39821
822 EXPECT_EQ(i, apm_->num_input_channels());
823 EXPECT_EQ(j, apm_->num_output_channels());
824 // The number of reverse channels used for processing to is always 1.
Peter Kasting69558702016-01-13 00:26:35825 EXPECT_EQ(1u, apm_->num_reverse_channels());
Michael Graczyk86c6d332015-07-23 18:41:39826 } else {
827 TestChangingForwardChannels(i, j,
828 AudioProcessing::kBadNumberChannelsError);
829 }
830 }
831 }
832}
833
andrew@webrtc.orgddbb8a22014-04-22 21:00:04834TEST_F(ApmTest, SampleRatesInt) {
Sam Zackrisson12e319a2020-01-03 13:54:20835 // Testing some valid sample rates.
836 for (int sample_rate : {8000, 12000, 16000, 32000, 44100, 48000, 96000}) {
837 SetContainerFormat(sample_rate, 2, &frame_, &float_cb_);
andrew@webrtc.orgddbb8a22014-04-22 21:00:04838 EXPECT_NOERR(ProcessStreamChooser(kIntFormat));
niklase@google.com470e71d2011-07-07 08:21:25839 }
840}
841
Sam Zackrissone277bde2019-10-25 08:07:54842// This test repeatedly reconfigures the pre-amplifier in APM, processes a
843// number of frames, and checks that output signal has the right level.
844TEST_F(ApmTest, PreAmplifier) {
845 // Fill the audio frame with a sawtooth pattern.
Tommi5f163fc2024-11-19 10:53:51846 InterleavedView<int16_t> frame_data = frame_.view();
847 const size_t samples_per_channel = frame_.samples_per_channel();
Sam Zackrissone277bde2019-10-25 08:07:54848 for (size_t i = 0; i < samples_per_channel; i++) {
Tommi5f163fc2024-11-19 10:53:51849 for (size_t ch = 0; ch < frame_.num_channels(); ++ch) {
Sam Zackrissone277bde2019-10-25 08:07:54850 frame_data[i + ch * samples_per_channel] = 10000 * ((i % 3) - 1);
851 }
852 }
853 // Cache the frame in tmp_frame.
Per Åhgren2507f8c2020-03-19 11:33:29854 Int16FrameData tmp_frame;
Sam Zackrisson70770ac2019-10-25 08:56:53855 tmp_frame.CopyFrom(frame_);
Sam Zackrissone277bde2019-10-25 08:07:54856
Per Åhgren2507f8c2020-03-19 11:33:29857 auto compute_power = [](const Int16FrameData& frame) {
Tommi5f163fc2024-11-19 10:53:51858 rtc::ArrayView<const int16_t> data = frame.view().data();
Sam Zackrissone277bde2019-10-25 08:07:54859 return std::accumulate(data.begin(), data.end(), 0.0f,
860 [](float a, float b) { return a + b * b; }) /
861 data.size() / 32768 / 32768;
862 };
863
864 const float input_power = compute_power(tmp_frame);
865 // Double-check that the input data is large compared to the error kEpsilon.
866 constexpr float kEpsilon = 1e-4f;
867 RTC_DCHECK_GE(input_power, 10 * kEpsilon);
868
869 // 1. Enable pre-amp with 0 dB gain.
870 AudioProcessing::Config config = apm_->GetConfig();
871 config.pre_amplifier.enabled = true;
872 config.pre_amplifier.fixed_gain_factor = 1.0f;
873 apm_->ApplyConfig(config);
874
875 for (int i = 0; i < 20; ++i) {
Sam Zackrisson70770ac2019-10-25 08:56:53876 frame_.CopyFrom(tmp_frame);
Tommi5f163fc2024-11-19 10:53:51877 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(kIntFormat));
Sam Zackrissone277bde2019-10-25 08:07:54878 }
Sam Zackrisson70770ac2019-10-25 08:56:53879 float output_power = compute_power(frame_);
Sam Zackrissone277bde2019-10-25 08:07:54880 EXPECT_NEAR(output_power, input_power, kEpsilon);
881 config = apm_->GetConfig();
882 EXPECT_EQ(config.pre_amplifier.fixed_gain_factor, 1.0f);
883
884 // 2. Change pre-amp gain via ApplyConfig.
885 config.pre_amplifier.fixed_gain_factor = 2.0f;
886 apm_->ApplyConfig(config);
887
888 for (int i = 0; i < 20; ++i) {
Sam Zackrisson70770ac2019-10-25 08:56:53889 frame_.CopyFrom(tmp_frame);
Tommi5f163fc2024-11-19 10:53:51890 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(kIntFormat));
Sam Zackrissone277bde2019-10-25 08:07:54891 }
Sam Zackrisson70770ac2019-10-25 08:56:53892 output_power = compute_power(frame_);
Sam Zackrissone277bde2019-10-25 08:07:54893 EXPECT_NEAR(output_power, 4 * input_power, kEpsilon);
894 config = apm_->GetConfig();
895 EXPECT_EQ(config.pre_amplifier.fixed_gain_factor, 2.0f);
896
897 // 3. Change pre-amp gain via a RuntimeSetting.
898 apm_->SetRuntimeSetting(
899 AudioProcessing::RuntimeSetting::CreateCapturePreGain(1.5f));
900
901 for (int i = 0; i < 20; ++i) {
Sam Zackrisson70770ac2019-10-25 08:56:53902 frame_.CopyFrom(tmp_frame);
Tommi5f163fc2024-11-19 10:53:51903 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(kIntFormat));
Sam Zackrissone277bde2019-10-25 08:07:54904 }
Sam Zackrisson70770ac2019-10-25 08:56:53905 output_power = compute_power(frame_);
Sam Zackrissone277bde2019-10-25 08:07:54906 EXPECT_NEAR(output_power, 2.25 * input_power, kEpsilon);
907 config = apm_->GetConfig();
908 EXPECT_EQ(config.pre_amplifier.fixed_gain_factor, 1.5f);
909}
910
Alessio Bazzicafcf1af32022-09-07 15:14:26911// Ensures that the emulated analog mic gain functionality runs without
912// crashing.
Per Åhgrendb5d7282021-03-15 16:31:04913TEST_F(ApmTest, AnalogMicGainEmulation) {
914 // Fill the audio frame with a sawtooth pattern.
Tommi5f163fc2024-11-19 10:53:51915 InterleavedView<int16_t> frame_data = frame_.view();
916 const size_t samples_per_channel = frame_.samples_per_channel();
Per Åhgrendb5d7282021-03-15 16:31:04917 for (size_t i = 0; i < samples_per_channel; i++) {
Tommi5f163fc2024-11-19 10:53:51918 for (size_t ch = 0; ch < frame_.num_channels(); ++ch) {
Per Åhgrendb5d7282021-03-15 16:31:04919 frame_data[i + ch * samples_per_channel] = 100 * ((i % 3) - 1);
920 }
921 }
922 // Cache the frame in tmp_frame.
923 Int16FrameData tmp_frame;
924 tmp_frame.CopyFrom(frame_);
925
926 // Enable the analog gain emulation.
927 AudioProcessing::Config config = apm_->GetConfig();
928 config.capture_level_adjustment.enabled = true;
929 config.capture_level_adjustment.analog_mic_gain_emulation.enabled = true;
930 config.capture_level_adjustment.analog_mic_gain_emulation.initial_level = 21;
931 config.gain_controller1.enabled = true;
932 config.gain_controller1.mode =
933 AudioProcessing::Config::GainController1::Mode::kAdaptiveAnalog;
934 config.gain_controller1.analog_gain_controller.enabled = true;
935 apm_->ApplyConfig(config);
936
937 // Process a number of frames to ensure that the code runs without crashes.
938 for (int i = 0; i < 20; ++i) {
939 frame_.CopyFrom(tmp_frame);
Tommi5f163fc2024-11-19 10:53:51940 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(kIntFormat));
Per Åhgrendb5d7282021-03-15 16:31:04941 }
942}
943
944// This test repeatedly reconfigures the capture level adjustment functionality
945// in APM, processes a number of frames, and checks that output signal has the
946// right level.
947TEST_F(ApmTest, CaptureLevelAdjustment) {
948 // Fill the audio frame with a sawtooth pattern.
Tommi5f163fc2024-11-19 10:53:51949 InterleavedView<int16_t> frame_data = frame_.view();
950 const size_t samples_per_channel = frame_.samples_per_channel();
Per Åhgrendb5d7282021-03-15 16:31:04951 for (size_t i = 0; i < samples_per_channel; i++) {
Tommi5f163fc2024-11-19 10:53:51952 for (size_t ch = 0; ch < frame_.num_channels(); ++ch) {
Per Åhgrendb5d7282021-03-15 16:31:04953 frame_data[i + ch * samples_per_channel] = 100 * ((i % 3) - 1);
954 }
955 }
956 // Cache the frame in tmp_frame.
957 Int16FrameData tmp_frame;
958 tmp_frame.CopyFrom(frame_);
959
960 auto compute_power = [](const Int16FrameData& frame) {
Tommi5f163fc2024-11-19 10:53:51961 rtc::ArrayView<const int16_t> data = frame.view().data();
Per Åhgrendb5d7282021-03-15 16:31:04962 return std::accumulate(data.begin(), data.end(), 0.0f,
963 [](float a, float b) { return a + b * b; }) /
964 data.size() / 32768 / 32768;
965 };
966
967 const float input_power = compute_power(tmp_frame);
968 // Double-check that the input data is large compared to the error kEpsilon.
969 constexpr float kEpsilon = 1e-20f;
970 RTC_DCHECK_GE(input_power, 10 * kEpsilon);
971
972 // 1. Enable pre-amp with 0 dB gain.
973 AudioProcessing::Config config = apm_->GetConfig();
974 config.capture_level_adjustment.enabled = true;
975 config.capture_level_adjustment.pre_gain_factor = 0.5f;
976 config.capture_level_adjustment.post_gain_factor = 4.f;
977 const float expected_output_power1 =
978 config.capture_level_adjustment.pre_gain_factor *
979 config.capture_level_adjustment.pre_gain_factor *
980 config.capture_level_adjustment.post_gain_factor *
981 config.capture_level_adjustment.post_gain_factor * input_power;
982 apm_->ApplyConfig(config);
983
984 for (int i = 0; i < 20; ++i) {
985 frame_.CopyFrom(tmp_frame);
Tommi5f163fc2024-11-19 10:53:51986 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(kIntFormat));
Per Åhgrendb5d7282021-03-15 16:31:04987 }
988 float output_power = compute_power(frame_);
989 EXPECT_NEAR(output_power, expected_output_power1, kEpsilon);
990 config = apm_->GetConfig();
991 EXPECT_EQ(config.capture_level_adjustment.pre_gain_factor, 0.5f);
992 EXPECT_EQ(config.capture_level_adjustment.post_gain_factor, 4.f);
993
994 // 2. Change pre-amp gain via ApplyConfig.
995 config.capture_level_adjustment.pre_gain_factor = 1.0f;
996 config.capture_level_adjustment.post_gain_factor = 2.f;
997 const float expected_output_power2 =
998 config.capture_level_adjustment.pre_gain_factor *
999 config.capture_level_adjustment.pre_gain_factor *
1000 config.capture_level_adjustment.post_gain_factor *
1001 config.capture_level_adjustment.post_gain_factor * input_power;
1002 apm_->ApplyConfig(config);
1003
1004 for (int i = 0; i < 20; ++i) {
1005 frame_.CopyFrom(tmp_frame);
Tommi5f163fc2024-11-19 10:53:511006 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(kIntFormat));
Per Åhgrendb5d7282021-03-15 16:31:041007 }
1008 output_power = compute_power(frame_);
1009 EXPECT_NEAR(output_power, expected_output_power2, kEpsilon);
1010 config = apm_->GetConfig();
1011 EXPECT_EQ(config.capture_level_adjustment.pre_gain_factor, 1.0f);
1012 EXPECT_EQ(config.capture_level_adjustment.post_gain_factor, 2.f);
1013
1014 // 3. Change pre-amp gain via a RuntimeSetting.
1015 constexpr float kPreGain3 = 0.5f;
1016 constexpr float kPostGain3 = 3.f;
1017 const float expected_output_power3 =
1018 kPreGain3 * kPreGain3 * kPostGain3 * kPostGain3 * input_power;
1019
1020 apm_->SetRuntimeSetting(
1021 AudioProcessing::RuntimeSetting::CreateCapturePreGain(kPreGain3));
1022 apm_->SetRuntimeSetting(
1023 AudioProcessing::RuntimeSetting::CreateCapturePostGain(kPostGain3));
1024
1025 for (int i = 0; i < 20; ++i) {
1026 frame_.CopyFrom(tmp_frame);
Tommi5f163fc2024-11-19 10:53:511027 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(kIntFormat));
Per Åhgrendb5d7282021-03-15 16:31:041028 }
1029 output_power = compute_power(frame_);
1030 EXPECT_NEAR(output_power, expected_output_power3, kEpsilon);
1031 config = apm_->GetConfig();
1032 EXPECT_EQ(config.capture_level_adjustment.pre_gain_factor, 0.5f);
1033 EXPECT_EQ(config.capture_level_adjustment.post_gain_factor, 3.f);
1034}
1035
aluebs@webrtc.orgc9ee4122014-02-03 14:41:571036TEST_F(ApmTest, GainControl) {
Sam Zackrisson41478c72019-10-15 08:10:261037 AudioProcessing::Config config = apm_->GetConfig();
1038 config.gain_controller1.enabled = false;
1039 apm_->ApplyConfig(config);
1040 config.gain_controller1.enabled = true;
1041 apm_->ApplyConfig(config);
1042
niklase@google.com470e71d2011-07-07 08:21:251043 // Testing gain modes
Sam Zackrisson41478c72019-10-15 08:10:261044 for (auto mode :
1045 {AudioProcessing::Config::GainController1::kAdaptiveDigital,
1046 AudioProcessing::Config::GainController1::kFixedDigital,
1047 AudioProcessing::Config::GainController1::kAdaptiveAnalog}) {
1048 config.gain_controller1.mode = mode;
1049 apm_->ApplyConfig(config);
1050 apm_->set_stream_analog_level(100);
Tommi5f163fc2024-11-19 10:53:511051 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(kFloatFormat));
niklase@google.com470e71d2011-07-07 08:21:251052 }
niklase@google.com470e71d2011-07-07 08:21:251053
Sam Zackrisson41478c72019-10-15 08:10:261054 // Testing target levels
1055 for (int target_level_dbfs : {0, 15, 31}) {
1056 config.gain_controller1.target_level_dbfs = target_level_dbfs;
1057 apm_->ApplyConfig(config);
1058 apm_->set_stream_analog_level(100);
Tommi5f163fc2024-11-19 10:53:511059 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(kFloatFormat));
niklase@google.com470e71d2011-07-07 08:21:251060 }
1061
Sam Zackrissonf0d1c032019-03-27 12:28:081062 // Testing compression gains
Sam Zackrisson41478c72019-10-15 08:10:261063 for (int compression_gain_db : {0, 10, 90}) {
1064 config.gain_controller1.compression_gain_db = compression_gain_db;
1065 apm_->ApplyConfig(config);
1066 apm_->set_stream_analog_level(100);
Tommi5f163fc2024-11-19 10:53:511067 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(kFloatFormat));
niklase@google.com470e71d2011-07-07 08:21:251068 }
1069
1070 // Testing limiter off/on
Sam Zackrisson41478c72019-10-15 08:10:261071 for (bool enable : {false, true}) {
1072 config.gain_controller1.enable_limiter = enable;
1073 apm_->ApplyConfig(config);
1074 apm_->set_stream_analog_level(100);
Tommi5f163fc2024-11-19 10:53:511075 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(kFloatFormat));
Sam Zackrisson41478c72019-10-15 08:10:261076 }
niklase@google.com470e71d2011-07-07 08:21:251077
Hanna Silencd597042021-11-02 10:02:481078 // Testing level limits.
1079 constexpr int kMinLevel = 0;
1080 constexpr int kMaxLevel = 255;
1081 apm_->set_stream_analog_level(kMinLevel);
Tommi5f163fc2024-11-19 10:53:511082 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(kFloatFormat));
Hanna Silencd597042021-11-02 10:02:481083 apm_->set_stream_analog_level((kMinLevel + kMaxLevel) / 2);
Tommi5f163fc2024-11-19 10:53:511084 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(kFloatFormat));
Hanna Silencd597042021-11-02 10:02:481085 apm_->set_stream_analog_level(kMaxLevel);
Tommi5f163fc2024-11-19 10:53:511086 EXPECT_EQ(AudioProcessing::kNoError, ProcessStreamChooser(kFloatFormat));
niklase@google.com470e71d2011-07-07 08:21:251087}
1088
Sam Zackrissonf0d1c032019-03-27 12:28:081089#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
Tommia5e07cc2020-05-26 19:40:371090using ApmDeathTest = ApmTest;
1091
1092TEST_F(ApmDeathTest, GainControlDiesOnTooLowTargetLevelDbfs) {
Sam Zackrisson41478c72019-10-15 08:10:261093 auto config = apm_->GetConfig();
Per Åhgren0695df12020-01-13 13:43:131094 config.gain_controller1.enabled = true;
Sam Zackrisson41478c72019-10-15 08:10:261095 config.gain_controller1.target_level_dbfs = -1;
1096 EXPECT_DEATH(apm_->ApplyConfig(config), "");
Sam Zackrissonf0d1c032019-03-27 12:28:081097}
1098
Tommia5e07cc2020-05-26 19:40:371099TEST_F(ApmDeathTest, GainControlDiesOnTooHighTargetLevelDbfs) {
Sam Zackrisson41478c72019-10-15 08:10:261100 auto config = apm_->GetConfig();
Per Åhgren0695df12020-01-13 13:43:131101 config.gain_controller1.enabled = true;
Sam Zackrisson41478c72019-10-15 08:10:261102 config.gain_controller1.target_level_dbfs = 32;
1103 EXPECT_DEATH(apm_->ApplyConfig(config), "");
Sam Zackrissonf0d1c032019-03-27 12:28:081104}
1105
Tommia5e07cc2020-05-26 19:40:371106TEST_F(ApmDeathTest, GainControlDiesOnTooLowCompressionGainDb) {
Sam Zackrisson41478c72019-10-15 08:10:261107 auto config = apm_->GetConfig();
Per Åhgren0695df12020-01-13 13:43:131108 config.gain_controller1.enabled = true;
Sam Zackrisson41478c72019-10-15 08:10:261109 config.gain_controller1.compression_gain_db = -1;
1110 EXPECT_DEATH(apm_->ApplyConfig(config), "");
Sam Zackrissonf0d1c032019-03-27 12:28:081111}
1112
Tommia5e07cc2020-05-26 19:40:371113TEST_F(ApmDeathTest, GainControlDiesOnTooHighCompressionGainDb) {
Sam Zackrisson41478c72019-10-15 08:10:261114 auto config = apm_->GetConfig();
Per Åhgren0695df12020-01-13 13:43:131115 config.gain_controller1.enabled = true;
Sam Zackrisson41478c72019-10-15 08:10:261116 config.gain_controller1.compression_gain_db = 91;
1117 EXPECT_DEATH(apm_->ApplyConfig(config), "");
Sam Zackrissonf0d1c032019-03-27 12:28:081118}
1119
Tommia5e07cc2020-05-26 19:40:371120TEST_F(ApmDeathTest, ApmDiesOnTooLowAnalogLevel) {
Sam Zackrisson41478c72019-10-15 08:10:261121 auto config = apm_->GetConfig();
Per Åhgren0695df12020-01-13 13:43:131122 config.gain_controller1.enabled = true;
Sam Zackrisson41478c72019-10-15 08:10:261123 apm_->ApplyConfig(config);
Hanna Silencd597042021-11-02 10:02:481124 EXPECT_DEATH(apm_->set_stream_analog_level(-1), "");
Sam Zackrissonf0d1c032019-03-27 12:28:081125}
1126
Tommia5e07cc2020-05-26 19:40:371127TEST_F(ApmDeathTest, ApmDiesOnTooHighAnalogLevel) {
Sam Zackrisson41478c72019-10-15 08:10:261128 auto config = apm_->GetConfig();
Per Åhgren0695df12020-01-13 13:43:131129 config.gain_controller1.enabled = true;
Sam Zackrisson41478c72019-10-15 08:10:261130 apm_->ApplyConfig(config);
Hanna Silencd597042021-11-02 10:02:481131 EXPECT_DEATH(apm_->set_stream_analog_level(256), "");
Sam Zackrissonf0d1c032019-03-27 12:28:081132}
1133#endif
1134
andrew@webrtc.org27c69802014-02-18 20:24:561135void ApmTest::RunQuantizedVolumeDoesNotGetStuckTest(int sample_rate) {
andrew@webrtc.orgddbb8a22014-04-22 21:00:041136 Init(sample_rate, sample_rate, sample_rate, 2, 2, 2, false);
Sam Zackrisson41478c72019-10-15 08:10:261137 auto config = apm_->GetConfig();
1138 config.gain_controller1.enabled = true;
1139 config.gain_controller1.mode =
1140 AudioProcessing::Config::GainController1::kAdaptiveAnalog;
1141 apm_->ApplyConfig(config);
andrew@webrtc.org27c69802014-02-18 20:24:561142
1143 int out_analog_level = 0;
1144 for (int i = 0; i < 2000; ++i) {
Sam Zackrisson70770ac2019-10-25 08:56:531145 ReadFrameWithRewind(near_file_, &frame_);
andrew@webrtc.org27c69802014-02-18 20:24:561146 // Ensure the audio is at a low level, so the AGC will try to increase it.
Tommi5f163fc2024-11-19 10:53:511147 frame_.Scale(0.25f);
andrew@webrtc.org27c69802014-02-18 20:24:561148
1149 // Always pass in the same volume.
Sam Zackrisson41478c72019-10-15 08:10:261150 apm_->set_stream_analog_level(100);
Tommi5f163fc2024-11-19 10:53:511151 EXPECT_EQ(AudioProcessing::kNoError,
Per Åhgren2507f8c2020-03-19 11:33:291152 apm_->ProcessStream(
1153 frame_.data.data(),
Tommi5f163fc2024-11-19 10:53:511154 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
1155 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:581156 frame_.data.data()));
Sam Zackrisson41478c72019-10-15 08:10:261157 out_analog_level = apm_->recommended_stream_analog_level();
andrew@webrtc.org27c69802014-02-18 20:24:561158 }
1159
1160 // Ensure the AGC is still able to reach the maximum.
1161 EXPECT_EQ(255, out_analog_level);
1162}
1163
1164// Verifies that despite volume slider quantization, the AGC can continue to
1165// increase its volume.
1166TEST_F(ApmTest, QuantizedVolumeDoesNotGetStuck) {
Sam Zackrisson3bd444f2022-08-03 12:37:001167 for (size_t sample_rate_hz : kProcessSampleRates) {
1168 SCOPED_TRACE(::testing::Message() << "sample_rate_hz=" << sample_rate_hz);
1169 RunQuantizedVolumeDoesNotGetStuckTest(sample_rate_hz);
andrew@webrtc.org27c69802014-02-18 20:24:561170 }
1171}
1172
1173void ApmTest::RunManualVolumeChangeIsPossibleTest(int sample_rate) {
andrew@webrtc.orgddbb8a22014-04-22 21:00:041174 Init(sample_rate, sample_rate, sample_rate, 2, 2, 2, false);
Sam Zackrisson41478c72019-10-15 08:10:261175 auto config = apm_->GetConfig();
1176 config.gain_controller1.enabled = true;
1177 config.gain_controller1.mode =
1178 AudioProcessing::Config::GainController1::kAdaptiveAnalog;
1179 apm_->ApplyConfig(config);
andrew@webrtc.org27c69802014-02-18 20:24:561180
1181 int out_analog_level = 100;
1182 for (int i = 0; i < 1000; ++i) {
Sam Zackrisson70770ac2019-10-25 08:56:531183 ReadFrameWithRewind(near_file_, &frame_);
andrew@webrtc.org27c69802014-02-18 20:24:561184 // Ensure the audio is at a low level, so the AGC will try to increase it.
Tommi5f163fc2024-11-19 10:53:511185 frame_.Scale(0.25f);
andrew@webrtc.org27c69802014-02-18 20:24:561186
Sam Zackrisson41478c72019-10-15 08:10:261187 apm_->set_stream_analog_level(out_analog_level);
Tommi5f163fc2024-11-19 10:53:511188 EXPECT_EQ(AudioProcessing::kNoError,
Per Åhgren2507f8c2020-03-19 11:33:291189 apm_->ProcessStream(
1190 frame_.data.data(),
Tommi5f163fc2024-11-19 10:53:511191 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
1192 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:581193 frame_.data.data()));
Sam Zackrisson41478c72019-10-15 08:10:261194 out_analog_level = apm_->recommended_stream_analog_level();
andrew@webrtc.org27c69802014-02-18 20:24:561195 }
1196
1197 // Ensure the volume was raised.
1198 EXPECT_GT(out_analog_level, 100);
1199 int highest_level_reached = out_analog_level;
1200 // Simulate a user manual volume change.
1201 out_analog_level = 100;
1202
1203 for (int i = 0; i < 300; ++i) {
Sam Zackrisson70770ac2019-10-25 08:56:531204 ReadFrameWithRewind(near_file_, &frame_);
Tommi5f163fc2024-11-19 10:53:511205 frame_.Scale(0.25f);
andrew@webrtc.org27c69802014-02-18 20:24:561206
Sam Zackrisson41478c72019-10-15 08:10:261207 apm_->set_stream_analog_level(out_analog_level);
Tommi5f163fc2024-11-19 10:53:511208 EXPECT_EQ(AudioProcessing::kNoError,
Per Åhgren2507f8c2020-03-19 11:33:291209 apm_->ProcessStream(
1210 frame_.data.data(),
Tommi5f163fc2024-11-19 10:53:511211 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
1212 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:581213 frame_.data.data()));
Sam Zackrisson41478c72019-10-15 08:10:261214 out_analog_level = apm_->recommended_stream_analog_level();
andrew@webrtc.org27c69802014-02-18 20:24:561215 // Check that AGC respected the manually adjusted volume.
1216 EXPECT_LT(out_analog_level, highest_level_reached);
1217 }
1218 // Check that the volume was still raised.
1219 EXPECT_GT(out_analog_level, 100);
1220}
1221
1222TEST_F(ApmTest, ManualVolumeChangeIsPossible) {
Sam Zackrisson3bd444f2022-08-03 12:37:001223 for (size_t sample_rate_hz : kProcessSampleRates) {
1224 SCOPED_TRACE(::testing::Message() << "sample_rate_hz=" << sample_rate_hz);
1225 RunManualVolumeChangeIsPossibleTest(sample_rate_hz);
andrew@webrtc.org27c69802014-02-18 20:24:561226 }
1227}
1228
niklase@google.com470e71d2011-07-07 08:21:251229TEST_F(ApmTest, HighPassFilter) {
andrew@webrtc.org648af742012-02-08 01:57:291230 // Turn HP filter on/off
peah8271d042016-11-22 15:24:521231 AudioProcessing::Config apm_config;
1232 apm_config.high_pass_filter.enabled = true;
1233 apm_->ApplyConfig(apm_config);
1234 apm_config.high_pass_filter.enabled = false;
1235 apm_->ApplyConfig(apm_config);
niklase@google.com470e71d2011-07-07 08:21:251236}
1237
andrew@webrtc.orgecac9b72012-05-02 00:04:101238TEST_F(ApmTest, AllProcessingDisabledByDefault) {
Sam Zackrissoncdf0e6d2018-09-17 09:05:171239 AudioProcessing::Config config = apm_->GetConfig();
1240 EXPECT_FALSE(config.echo_canceller.enabled);
1241 EXPECT_FALSE(config.high_pass_filter.enabled);
Sam Zackrisson41478c72019-10-15 08:10:261242 EXPECT_FALSE(config.gain_controller1.enabled);
saza0bad15f2019-10-16 09:46:111243 EXPECT_FALSE(config.noise_suppression.enabled);
andrew@webrtc.orgecac9b72012-05-02 00:04:101244}
1245
Sam Zackrisson3bd444f2022-08-03 12:37:001246TEST_F(ApmTest, NoProcessingWhenAllComponentsDisabledInt) {
1247 // Test that ProcessStream simply copies input to output when all components
1248 // are disabled.
1249 // Runs over all processing rates, and some particularly common or special
1250 // rates.
1251 // - 8000 Hz: lowest sample rate seen in Chrome metrics,
1252 // - 22050 Hz: APM input/output frames are not exactly 10 ms,
1253 // - 44100 Hz: very common desktop sample rate.
1254 constexpr int kSampleRatesHz[] = {8000, 16000, 22050, 32000, 44100, 48000};
1255 for (size_t sample_rate_hz : kSampleRatesHz) {
1256 SCOPED_TRACE(::testing::Message() << "sample_rate_hz=" << sample_rate_hz);
1257 Init(sample_rate_hz, sample_rate_hz, sample_rate_hz, 2, 2, 2, false);
Tommi5f163fc2024-11-19 10:53:511258 frame_.FillStereoData(1000, 2000);
Per Åhgren2507f8c2020-03-19 11:33:291259 Int16FrameData frame_copy;
Sam Zackrisson70770ac2019-10-25 08:56:531260 frame_copy.CopyFrom(frame_);
andrew@webrtc.orgecac9b72012-05-02 00:04:101261 for (int j = 0; j < 1000; j++) {
Tommi5f163fc2024-11-19 10:53:511262 EXPECT_EQ(AudioProcessing::kNoError,
Per Åhgren2507f8c2020-03-19 11:33:291263 apm_->ProcessStream(
1264 frame_.data.data(),
Tommi5f163fc2024-11-19 10:53:511265 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
1266 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:581267 frame_.data.data()));
Tommi5f163fc2024-11-19 10:53:511268 EXPECT_TRUE(frame_.IsEqual(frame_copy));
1269 EXPECT_EQ(AudioProcessing::kNoError,
Per Åhgren2507f8c2020-03-19 11:33:291270 apm_->ProcessReverseStream(
1271 frame_.data.data(),
Tommi5f163fc2024-11-19 10:53:511272 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
1273 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Per Åhgren2507f8c2020-03-19 11:33:291274 frame_.data.data()));
Tommi5f163fc2024-11-19 10:53:511275 EXPECT_TRUE(frame_.IsEqual(frame_copy));
andrew@webrtc.orgecac9b72012-05-02 00:04:101276 }
1277 }
1278}
1279
mgraczyk@chromium.orgd6e84d92015-01-14 01:33:541280TEST_F(ApmTest, NoProcessingWhenAllComponentsDisabledFloat) {
Sam Zackrisson3bd444f2022-08-03 12:37:001281 // Test that ProcessStream simply copies input to output when all components
1282 // are disabled.
Per Åhgrenc8626b62019-08-23 13:49:511283 const size_t kSamples = 160;
1284 const int sample_rate = 16000;
Jonas Olssona4d87372019-07-05 17:08:331285 const float src[kSamples] = {-1.0f, 0.0f, 1.0f};
mgraczyk@chromium.orgd6e84d92015-01-14 01:33:541286 float dest[kSamples] = {};
1287
1288 auto src_channels = &src[0];
1289 auto dest_channels = &dest[0];
1290
Danil Chapovalovdc03d872024-10-24 14:16:471291 apm_ = BuiltinAudioProcessingBuilder().Build(CreateEnvironment());
Gustaf Ullbergcb307262019-10-29 08:30:441292 EXPECT_NOERR(apm_->ProcessStream(&src_channels, StreamConfig(sample_rate, 1),
1293 StreamConfig(sample_rate, 1),
1294 &dest_channels));
mgraczyk@chromium.orgd6e84d92015-01-14 01:33:541295
1296 for (size_t i = 0; i < kSamples; ++i) {
1297 EXPECT_EQ(src[i], dest[i]);
1298 }
ekmeyerson60d9b332015-08-14 17:35:551299
1300 // Same for ProcessReverseStream.
1301 float rev_dest[kSamples] = {};
1302 auto rev_dest_channels = &rev_dest[0];
1303
1304 StreamConfig input_stream = {sample_rate, 1};
1305 StreamConfig output_stream = {sample_rate, 1};
1306 EXPECT_NOERR(apm_->ProcessReverseStream(&src_channels, input_stream,
1307 output_stream, &rev_dest_channels));
1308
1309 for (size_t i = 0; i < kSamples; ++i) {
1310 EXPECT_EQ(src[i], rev_dest[i]);
1311 }
mgraczyk@chromium.orgd6e84d92015-01-14 01:33:541312}
1313
andrew@webrtc.org07bf9a02012-05-05 00:32:001314TEST_F(ApmTest, IdenticalInputChannelsResultInIdenticalOutputChannels) {
1315 EnableAllComponents();
1316
pkasting25702cb2016-01-08 21:50:271317 for (size_t i = 0; i < arraysize(kProcessSampleRates); i++) {
Jonas Olssona4d87372019-07-05 17:08:331318 Init(kProcessSampleRates[i], kProcessSampleRates[i], kProcessSampleRates[i],
1319 2, 2, 2, false);
andrew@webrtc.org07bf9a02012-05-05 00:32:001320 int analog_level = 127;
andrew@webrtc.orgddbb8a22014-04-22 21:00:041321 ASSERT_EQ(0, feof(far_file_));
1322 ASSERT_EQ(0, feof(near_file_));
Sam Zackrisson70770ac2019-10-25 08:56:531323 while (ReadFrame(far_file_, &revframe_) && ReadFrame(near_file_, &frame_)) {
Per Åhgren2507f8c2020-03-19 11:33:291324 CopyLeftToRightChannel(revframe_.data.data(),
Tommi5f163fc2024-11-19 10:53:511325 revframe_.samples_per_channel());
andrew@webrtc.org07bf9a02012-05-05 00:32:001326
Per Åhgren2507f8c2020-03-19 11:33:291327 ASSERT_EQ(
Tommi5f163fc2024-11-19 10:53:511328 AudioProcessing::kNoError,
Per Åhgren2507f8c2020-03-19 11:33:291329 apm_->ProcessReverseStream(
1330 revframe_.data.data(),
Tommi5f163fc2024-11-19 10:53:511331 StreamConfig(revframe_.sample_rate_hz, revframe_.num_channels()),
1332 StreamConfig(revframe_.sample_rate_hz, revframe_.num_channels()),
Per Åhgren2507f8c2020-03-19 11:33:291333 revframe_.data.data()));
andrew@webrtc.org07bf9a02012-05-05 00:32:001334
Tommi5f163fc2024-11-19 10:53:511335 CopyLeftToRightChannel(frame_.data.data(), frame_.samples_per_channel());
andrew@webrtc.org07bf9a02012-05-05 00:32:001336
Tommi5f163fc2024-11-19 10:53:511337 ASSERT_EQ(AudioProcessing::kNoError, apm_->set_stream_delay_ms(0));
Sam Zackrisson41478c72019-10-15 08:10:261338 apm_->set_stream_analog_level(analog_level);
Tommi5f163fc2024-11-19 10:53:511339 ASSERT_EQ(AudioProcessing::kNoError,
Per Åhgren2507f8c2020-03-19 11:33:291340 apm_->ProcessStream(
1341 frame_.data.data(),
Tommi5f163fc2024-11-19 10:53:511342 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
1343 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:581344 frame_.data.data()));
Sam Zackrisson41478c72019-10-15 08:10:261345 analog_level = apm_->recommended_stream_analog_level();
andrew@webrtc.org07bf9a02012-05-05 00:32:001346
Tommi5f163fc2024-11-19 10:53:511347 VerifyChannelsAreEqual(frame_.data.data(), frame_.samples_per_channel());
andrew@webrtc.org07bf9a02012-05-05 00:32:001348 }
bjornv@webrtc.org3e102492013-02-14 15:29:091349 rewind(far_file_);
1350 rewind(near_file_);
andrew@webrtc.org07bf9a02012-05-05 00:32:001351 }
1352}
1353
bjornv@webrtc.orgcb0ea432014-06-09 08:21:521354TEST_F(ApmTest, SplittingFilter) {
andrew@webrtc.org755b04a2011-11-15 16:57:561355 // Verify the filter is not active through undistorted audio when:
1356 // 1. No components are enabled...
Tommi5f163fc2024-11-19 10:53:511357 frame_.FillData(1000);
Per Åhgren2507f8c2020-03-19 11:33:291358 Int16FrameData frame_copy;
Sam Zackrisson70770ac2019-10-25 08:56:531359 frame_copy.CopyFrom(frame_);
Tommi5f163fc2024-11-19 10:53:511360 EXPECT_EQ(AudioProcessing::kNoError,
Per Åhgren2507f8c2020-03-19 11:33:291361 apm_->ProcessStream(
1362 frame_.data.data(),
Tommi5f163fc2024-11-19 10:53:511363 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
1364 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:581365 frame_.data.data()));
Tommi5f163fc2024-11-19 10:53:511366 EXPECT_EQ(AudioProcessing::kNoError,
Per Åhgren2507f8c2020-03-19 11:33:291367 apm_->ProcessStream(
1368 frame_.data.data(),
Tommi5f163fc2024-11-19 10:53:511369 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
1370 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:581371 frame_.data.data()));
Tommi5f163fc2024-11-19 10:53:511372 EXPECT_TRUE(frame_.IsEqual(frame_copy));
andrew@webrtc.org755b04a2011-11-15 16:57:561373
1374 // 2. Only the level estimator is enabled...
saza6787f232019-10-11 17:31:071375 auto apm_config = apm_->GetConfig();
Tommi5f163fc2024-11-19 10:53:511376 frame_.FillData(1000);
Sam Zackrisson70770ac2019-10-25 08:56:531377 frame_copy.CopyFrom(frame_);
saza6787f232019-10-11 17:31:071378 apm_->ApplyConfig(apm_config);
Tommi5f163fc2024-11-19 10:53:511379 EXPECT_EQ(AudioProcessing::kNoError,
Per Åhgren2507f8c2020-03-19 11:33:291380 apm_->ProcessStream(
1381 frame_.data.data(),
Tommi5f163fc2024-11-19 10:53:511382 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
1383 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:581384 frame_.data.data()));
Tommi5f163fc2024-11-19 10:53:511385 EXPECT_EQ(AudioProcessing::kNoError,
Per Åhgren2507f8c2020-03-19 11:33:291386 apm_->ProcessStream(
1387 frame_.data.data(),
Tommi5f163fc2024-11-19 10:53:511388 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
1389 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:581390 frame_.data.data()));
Tommi5f163fc2024-11-19 10:53:511391 EXPECT_TRUE(frame_.IsEqual(frame_copy));
saza6787f232019-10-11 17:31:071392 apm_->ApplyConfig(apm_config);
andrew@webrtc.org755b04a2011-11-15 16:57:561393
Sam Zackrissoncb1b5562018-09-28 12:15:091394 // Check the test is valid. We should have distortion from the filter
1395 // when AEC is enabled (which won't affect the audio).
Sam Zackrissoncdf0e6d2018-09-17 09:05:171396 apm_config.echo_canceller.enabled = true;
1397 apm_config.echo_canceller.mobile_mode = false;
1398 apm_->ApplyConfig(apm_config);
Tommi5f163fc2024-11-19 10:53:511399 frame_.SetProperties(/* samples_per_channel=*/320, /* num_channels=*/2);
1400 frame_.FillData(1000);
Sam Zackrisson70770ac2019-10-25 08:56:531401 frame_copy.CopyFrom(frame_);
Tommi5f163fc2024-11-19 10:53:511402 EXPECT_EQ(AudioProcessing::kNoError, apm_->set_stream_delay_ms(0));
1403 EXPECT_EQ(AudioProcessing::kNoError,
Per Åhgren2507f8c2020-03-19 11:33:291404 apm_->ProcessStream(
1405 frame_.data.data(),
Tommi5f163fc2024-11-19 10:53:511406 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
1407 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:581408 frame_.data.data()));
Tommi5f163fc2024-11-19 10:53:511409 EXPECT_FALSE(frame_.IsEqual(frame_copy));
andrew@webrtc.org755b04a2011-11-15 16:57:561410}
1411
andrew@webrtc.orga8b97372014-03-10 22:26:121412#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
Ali Tofighf3592cb2022-08-16 12:44:381413void ApmTest::ProcessDebugDump(absl::string_view in_filename,
1414 absl::string_view out_filename,
ivocd66b44d2016-01-15 11:06:361415 Format format,
1416 int max_size_bytes) {
Danil Chapovalov07122bc2019-03-26 13:37:011417 TaskQueueForTest worker_queue("ApmTest_worker_queue");
Ali Tofighf3592cb2022-08-16 12:44:381418 FILE* in_file = fopen(std::string(in_filename).c_str(), "rb");
andrew@webrtc.orga8b97372014-03-10 22:26:121419 ASSERT_TRUE(in_file != NULL);
1420 audioproc::Event event_msg;
1421 bool first_init = true;
1422
1423 while (ReadMessageFromFile(in_file, &event_msg)) {
1424 if (event_msg.type() == audioproc::Event::INIT) {
1425 const audioproc::Init msg = event_msg.init();
1426 int reverse_sample_rate = msg.sample_rate();
1427 if (msg.has_reverse_sample_rate()) {
1428 reverse_sample_rate = msg.reverse_sample_rate();
1429 }
andrew@webrtc.orgddbb8a22014-04-22 21:00:041430 int output_sample_rate = msg.sample_rate();
1431 if (msg.has_output_sample_rate()) {
1432 output_sample_rate = msg.output_sample_rate();
1433 }
1434
Jonas Olssona4d87372019-07-05 17:08:331435 Init(msg.sample_rate(), output_sample_rate, reverse_sample_rate,
1436 msg.num_input_channels(), msg.num_output_channels(),
1437 msg.num_reverse_channels(), false);
andrew@webrtc.orga8b97372014-03-10 22:26:121438 if (first_init) {
aleloif4dd1912017-06-15 08:55:381439 // AttachAecDump() writes an additional init message. Don't start
andrew@webrtc.orga8b97372014-03-10 22:26:121440 // recording until after the first init to avoid the extra message.
Danil Chapovalovb64eef12024-01-04 13:45:231441 auto aec_dump = AecDumpFactory::Create(out_filename, max_size_bytes,
1442 worker_queue.Get());
aleloif4dd1912017-06-15 08:55:381443 EXPECT_TRUE(aec_dump);
1444 apm_->AttachAecDump(std::move(aec_dump));
andrew@webrtc.orga8b97372014-03-10 22:26:121445 first_init = false;
1446 }
1447
1448 } else if (event_msg.type() == audioproc::Event::REVERSE_STREAM) {
1449 const audioproc::ReverseStream msg = event_msg.reverse_stream();
1450
1451 if (msg.channel_size() > 0) {
Tommi5f163fc2024-11-19 10:53:511452 ASSERT_EQ(revframe_.num_channels(),
Peter Kasting69558702016-01-13 00:26:351453 static_cast<size_t>(msg.channel_size()));
andrew@webrtc.orga8b97372014-03-10 22:26:121454 for (int i = 0; i < msg.channel_size(); ++i) {
Jonas Olssona4d87372019-07-05 17:08:331455 memcpy(revfloat_cb_->channels()[i], msg.channel(i).data(),
1456 msg.channel(i).size());
andrew@webrtc.orga8b97372014-03-10 22:26:121457 }
1458 } else {
Per Åhgren2507f8c2020-03-19 11:33:291459 memcpy(revframe_.data.data(), msg.data().data(), msg.data().size());
andrew@webrtc.orga8b97372014-03-10 22:26:121460 if (format == kFloatFormat) {
1461 // We're using an int16 input file; convert to float.
Sam Zackrisson70770ac2019-10-25 08:56:531462 ConvertToFloat(revframe_, revfloat_cb_.get());
andrew@webrtc.orga8b97372014-03-10 22:26:121463 }
1464 }
1465 AnalyzeReverseStreamChooser(format);
1466
1467 } else if (event_msg.type() == audioproc::Event::STREAM) {
1468 const audioproc::Stream msg = event_msg.stream();
1469 // ProcessStream could have changed this for the output frame.
Tommi5f163fc2024-11-19 10:53:511470 frame_.set_num_channels(apm_->num_input_channels());
andrew@webrtc.orga8b97372014-03-10 22:26:121471
Alessio Bazzica3153b362022-09-05 14:05:241472 apm_->set_stream_analog_level(msg.applied_input_volume());
andrew@webrtc.orga8b97372014-03-10 22:26:121473 EXPECT_NOERR(apm_->set_stream_delay_ms(msg.delay()));
andrew@webrtc.orga8b97372014-03-10 22:26:121474 if (msg.has_keypress()) {
1475 apm_->set_stream_key_pressed(msg.keypress());
1476 } else {
1477 apm_->set_stream_key_pressed(true);
1478 }
1479
1480 if (msg.input_channel_size() > 0) {
Tommi5f163fc2024-11-19 10:53:511481 ASSERT_EQ(frame_.num_channels(),
Peter Kasting69558702016-01-13 00:26:351482 static_cast<size_t>(msg.input_channel_size()));
andrew@webrtc.orga8b97372014-03-10 22:26:121483 for (int i = 0; i < msg.input_channel_size(); ++i) {
Jonas Olssona4d87372019-07-05 17:08:331484 memcpy(float_cb_->channels()[i], msg.input_channel(i).data(),
1485 msg.input_channel(i).size());
andrew@webrtc.orga8b97372014-03-10 22:26:121486 }
1487 } else {
Per Åhgren2507f8c2020-03-19 11:33:291488 memcpy(frame_.data.data(), msg.input_data().data(),
yujo36b1a5f2017-06-12 19:45:321489 msg.input_data().size());
andrew@webrtc.orga8b97372014-03-10 22:26:121490 if (format == kFloatFormat) {
1491 // We're using an int16 input file; convert to float.
Sam Zackrisson70770ac2019-10-25 08:56:531492 ConvertToFloat(frame_, float_cb_.get());
andrew@webrtc.orga8b97372014-03-10 22:26:121493 }
1494 }
1495 ProcessStreamChooser(format);
1496 }
1497 }
aleloif4dd1912017-06-15 08:55:381498 apm_->DetachAecDump();
andrew@webrtc.orga8b97372014-03-10 22:26:121499 fclose(in_file);
1500}
1501
1502void ApmTest::VerifyDebugDumpTest(Format format) {
Minyue Li656d6092018-08-10 13:38:521503 rtc::ScopedFakeClock fake_clock;
andrew@webrtc.orga8b97372014-03-10 22:26:121504 const std::string in_filename = test::ResourcePath("ref03", "aecdump");
henrik.lundin@webrtc.org1092ea02014-04-02 07:46:491505 std::string format_string;
1506 switch (format) {
1507 case kIntFormat:
1508 format_string = "_int";
1509 break;
1510 case kFloatFormat:
1511 format_string = "_float";
1512 break;
1513 }
pbos@webrtc.orga525c982015-01-12 17:31:181514 const std::string ref_filename = test::TempFilename(
1515 test::OutputPath(), std::string("ref") + format_string + "_aecdump");
1516 const std::string out_filename = test::TempFilename(
1517 test::OutputPath(), std::string("out") + format_string + "_aecdump");
ivocd66b44d2016-01-15 11:06:361518 const std::string limited_filename = test::TempFilename(
1519 test::OutputPath(), std::string("limited") + format_string + "_aecdump");
1520 const size_t logging_limit_bytes = 100000;
1521 // We expect at least this many bytes in the created logfile.
1522 const size_t logging_expected_bytes = 95000;
andrew@webrtc.orga8b97372014-03-10 22:26:121523 EnableAllComponents();
ivocd66b44d2016-01-15 11:06:361524 ProcessDebugDump(in_filename, ref_filename, format, -1);
1525 ProcessDebugDump(ref_filename, out_filename, format, -1);
1526 ProcessDebugDump(ref_filename, limited_filename, format, logging_limit_bytes);
andrew@webrtc.orga8b97372014-03-10 22:26:121527
1528 FILE* ref_file = fopen(ref_filename.c_str(), "rb");
1529 FILE* out_file = fopen(out_filename.c_str(), "rb");
ivocd66b44d2016-01-15 11:06:361530 FILE* limited_file = fopen(limited_filename.c_str(), "rb");
andrew@webrtc.orga8b97372014-03-10 22:26:121531 ASSERT_TRUE(ref_file != NULL);
1532 ASSERT_TRUE(out_file != NULL);
ivocd66b44d2016-01-15 11:06:361533 ASSERT_TRUE(limited_file != NULL);
kwiberg62eaacf2016-02-17 14:39:051534 std::unique_ptr<uint8_t[]> ref_bytes;
1535 std::unique_ptr<uint8_t[]> out_bytes;
1536 std::unique_ptr<uint8_t[]> limited_bytes;
andrew@webrtc.orga8b97372014-03-10 22:26:121537
1538 size_t ref_size = ReadMessageBytesFromFile(ref_file, &ref_bytes);
1539 size_t out_size = ReadMessageBytesFromFile(out_file, &out_bytes);
ivocd66b44d2016-01-15 11:06:361540 size_t limited_size = ReadMessageBytesFromFile(limited_file, &limited_bytes);
andrew@webrtc.orga8b97372014-03-10 22:26:121541 size_t bytes_read = 0;
ivocd66b44d2016-01-15 11:06:361542 size_t bytes_read_limited = 0;
andrew@webrtc.orga8b97372014-03-10 22:26:121543 while (ref_size > 0 && out_size > 0) {
1544 bytes_read += ref_size;
ivocd66b44d2016-01-15 11:06:361545 bytes_read_limited += limited_size;
andrew@webrtc.orga8b97372014-03-10 22:26:121546 EXPECT_EQ(ref_size, out_size);
ivocd66b44d2016-01-15 11:06:361547 EXPECT_GE(ref_size, limited_size);
Alessio Bazzica85a126e2022-08-11 13:48:541548 EXPECT_TRUE(ExpectMessageEq(/*actual=*/{out_bytes.get(), out_size},
1549 /*expected=*/{ref_bytes.get(), ref_size}));
1550 if (limited_size > 0) {
1551 EXPECT_TRUE(
1552 ExpectMessageEq(/*actual=*/{limited_bytes.get(), limited_size},
1553 /*expected=*/{ref_bytes.get(), ref_size}));
1554 }
andrew@webrtc.orga8b97372014-03-10 22:26:121555 ref_size = ReadMessageBytesFromFile(ref_file, &ref_bytes);
1556 out_size = ReadMessageBytesFromFile(out_file, &out_bytes);
ivocd66b44d2016-01-15 11:06:361557 limited_size = ReadMessageBytesFromFile(limited_file, &limited_bytes);
andrew@webrtc.orga8b97372014-03-10 22:26:121558 }
1559 EXPECT_GT(bytes_read, 0u);
ivocd66b44d2016-01-15 11:06:361560 EXPECT_GT(bytes_read_limited, logging_expected_bytes);
1561 EXPECT_LE(bytes_read_limited, logging_limit_bytes);
andrew@webrtc.orga8b97372014-03-10 22:26:121562 EXPECT_NE(0, feof(ref_file));
1563 EXPECT_NE(0, feof(out_file));
ivocd66b44d2016-01-15 11:06:361564 EXPECT_NE(0, feof(limited_file));
andrew@webrtc.orga8b97372014-03-10 22:26:121565 ASSERT_EQ(0, fclose(ref_file));
1566 ASSERT_EQ(0, fclose(out_file));
ivocd66b44d2016-01-15 11:06:361567 ASSERT_EQ(0, fclose(limited_file));
Peter Boströmfade1792015-05-12 08:44:111568 remove(ref_filename.c_str());
1569 remove(out_filename.c_str());
ivocd66b44d2016-01-15 11:06:361570 remove(limited_filename.c_str());
andrew@webrtc.orga8b97372014-03-10 22:26:121571}
1572
pbosc7a65692016-05-06 19:50:041573TEST_F(ApmTest, VerifyDebugDumpInt) {
andrew@webrtc.orga8b97372014-03-10 22:26:121574 VerifyDebugDumpTest(kIntFormat);
1575}
1576
pbosc7a65692016-05-06 19:50:041577TEST_F(ApmTest, VerifyDebugDumpFloat) {
andrew@webrtc.orga8b97372014-03-10 22:26:121578 VerifyDebugDumpTest(kFloatFormat);
1579}
1580#endif
1581
andrew@webrtc.org7bf26462011-12-03 00:03:311582// TODO(andrew): expand test to verify output.
pbosc7a65692016-05-06 19:50:041583TEST_F(ApmTest, DebugDump) {
Danil Chapovalov07122bc2019-03-26 13:37:011584 TaskQueueForTest worker_queue("ApmTest_worker_queue");
pbos@webrtc.orga525c982015-01-12 17:31:181585 const std::string filename =
1586 test::TempFilename(test::OutputPath(), "debug_aec");
aleloif4dd1912017-06-15 08:55:381587 {
Danil Chapovalovb64eef12024-01-04 13:45:231588 auto aec_dump = AecDumpFactory::Create("", -1, worker_queue.Get());
aleloif4dd1912017-06-15 08:55:381589 EXPECT_FALSE(aec_dump);
1590 }
andrew@webrtc.org7bf26462011-12-03 00:03:311591
1592#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
1593 // Stopping without having started should be OK.
aleloif4dd1912017-06-15 08:55:381594 apm_->DetachAecDump();
andrew@webrtc.org7bf26462011-12-03 00:03:311595
Danil Chapovalovb64eef12024-01-04 13:45:231596 auto aec_dump = AecDumpFactory::Create(filename, -1, worker_queue.Get());
aleloif4dd1912017-06-15 08:55:381597 EXPECT_TRUE(aec_dump);
1598 apm_->AttachAecDump(std::move(aec_dump));
Tommi5f163fc2024-11-19 10:53:511599 EXPECT_EQ(AudioProcessing::kNoError,
Per Åhgren2507f8c2020-03-19 11:33:291600 apm_->ProcessStream(
1601 frame_.data.data(),
Tommi5f163fc2024-11-19 10:53:511602 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
1603 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:581604 frame_.data.data()));
Tommi5f163fc2024-11-19 10:53:511605 EXPECT_EQ(
1606 AudioProcessing::kNoError,
1607 apm_->ProcessReverseStream(
1608 revframe_.data.data(),
1609 StreamConfig(revframe_.sample_rate_hz, revframe_.num_channels()),
1610 StreamConfig(revframe_.sample_rate_hz, revframe_.num_channels()),
1611 revframe_.data.data()));
aleloif4dd1912017-06-15 08:55:381612 apm_->DetachAecDump();
andrew@webrtc.org7bf26462011-12-03 00:03:311613
1614 // Verify the file has been written.
andrew@webrtc.orgf5d8c3b2012-01-24 21:35:391615 FILE* fid = fopen(filename.c_str(), "r");
1616 ASSERT_TRUE(fid != NULL);
1617
andrew@webrtc.org7bf26462011-12-03 00:03:311618 // Clean it up.
andrew@webrtc.orgf5d8c3b2012-01-24 21:35:391619 ASSERT_EQ(0, fclose(fid));
andrew@webrtc.org7bf26462011-12-03 00:03:311620 ASSERT_EQ(0, remove(filename.c_str()));
1621#else
andrew@webrtc.org7bf26462011-12-03 00:03:311622 // Verify the file has NOT been written.
1623 ASSERT_TRUE(fopen(filename.c_str(), "r") == NULL);
1624#endif // WEBRTC_AUDIOPROC_DEBUG_DUMP
1625}
1626
henrikg@webrtc.org863b5362013-12-06 16:05:171627// TODO(andrew): expand test to verify output.
pbosc7a65692016-05-06 19:50:041628TEST_F(ApmTest, DebugDumpFromFileHandle) {
Danil Chapovalov07122bc2019-03-26 13:37:011629 TaskQueueForTest worker_queue("ApmTest_worker_queue");
aleloif4dd1912017-06-15 08:55:381630
pbos@webrtc.orga525c982015-01-12 17:31:181631 const std::string filename =
1632 test::TempFilename(test::OutputPath(), "debug_aec");
Ali Tofigh2ab914c2022-04-13 10:55:151633 FileWrapper f = FileWrapper::OpenWriteOnly(filename);
Niels Möllere8e4dc42019-06-11 12:04:161634 ASSERT_TRUE(f.is_open());
henrikg@webrtc.org863b5362013-12-06 16:05:171635
1636#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
1637 // Stopping without having started should be OK.
aleloif4dd1912017-06-15 08:55:381638 apm_->DetachAecDump();
henrikg@webrtc.org863b5362013-12-06 16:05:171639
Danil Chapovalovb64eef12024-01-04 13:45:231640 auto aec_dump = AecDumpFactory::Create(std::move(f), -1, worker_queue.Get());
aleloif4dd1912017-06-15 08:55:381641 EXPECT_TRUE(aec_dump);
1642 apm_->AttachAecDump(std::move(aec_dump));
Tommi5f163fc2024-11-19 10:53:511643 EXPECT_EQ(
1644 AudioProcessing::kNoError,
1645 apm_->ProcessReverseStream(
1646 revframe_.data.data(),
1647 StreamConfig(revframe_.sample_rate_hz, revframe_.num_channels()),
1648 StreamConfig(revframe_.sample_rate_hz, revframe_.num_channels()),
1649 revframe_.data.data()));
1650 EXPECT_EQ(AudioProcessing::kNoError,
Per Åhgren2507f8c2020-03-19 11:33:291651 apm_->ProcessStream(
1652 frame_.data.data(),
Tommi5f163fc2024-11-19 10:53:511653 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
1654 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:581655 frame_.data.data()));
aleloif4dd1912017-06-15 08:55:381656 apm_->DetachAecDump();
henrikg@webrtc.org863b5362013-12-06 16:05:171657
1658 // Verify the file has been written.
Niels Möllere8e4dc42019-06-11 12:04:161659 FILE* fid = fopen(filename.c_str(), "r");
henrikg@webrtc.org863b5362013-12-06 16:05:171660 ASSERT_TRUE(fid != NULL);
1661
1662 // Clean it up.
1663 ASSERT_EQ(0, fclose(fid));
1664 ASSERT_EQ(0, remove(filename.c_str()));
henrikg@webrtc.org863b5362013-12-06 16:05:171665#endif // WEBRTC_AUDIOPROC_DEBUG_DUMP
1666}
1667
andrew@webrtc.org75f19482012-02-09 17:16:181668// TODO(andrew): Add a test to process a few frames with different combinations
1669// of enabled components.
1670
bjornv@webrtc.orgdc0b37d2014-09-23 05:03:441671TEST_F(ApmTest, Process) {
andrew@webrtc.org755b04a2011-11-15 16:57:561672 GOOGLE_PROTOBUF_VERIFY_VERSION;
andrew@webrtc.org27c69802014-02-18 20:24:561673 audioproc::OutputData ref_data;
andrew@webrtc.org755b04a2011-11-15 16:57:561674
Sam Zackrisson6558fa52019-08-26 08:12:411675 if (!absl::GetFlag(FLAGS_write_apm_ref_data)) {
andrew@webrtc.orga8b97372014-03-10 22:26:121676 OpenFileAndReadMessage(ref_filename_, &ref_data);
andrew@webrtc.org755b04a2011-11-15 16:57:561677 } else {
Sam Zackrisson3bd444f2022-08-03 12:37:001678 const int kChannels[] = {1, 2};
andrew@webrtc.orgdaacee82012-02-07 00:01:041679 // Write the desired tests to the protobuf reference file.
pkasting25702cb2016-01-08 21:50:271680 for (size_t i = 0; i < arraysize(kChannels); i++) {
1681 for (size_t j = 0; j < arraysize(kChannels); j++) {
Sam Zackrisson3bd444f2022-08-03 12:37:001682 for (int sample_rate_hz : AudioProcessing::kNativeSampleRatesHz) {
andrew@webrtc.org27c69802014-02-18 20:24:561683 audioproc::Test* test = ref_data.add_test();
andrew@webrtc.org60730cf2014-01-07 17:45:091684 test->set_num_reverse_channels(kChannels[i]);
1685 test->set_num_input_channels(kChannels[j]);
1686 test->set_num_output_channels(kChannels[j]);
Sam Zackrisson3bd444f2022-08-03 12:37:001687 test->set_sample_rate(sample_rate_hz);
aluebs@webrtc.orgf17ee9c2015-01-29 00:03:531688 test->set_use_aec_extended_filter(false);
andrew@webrtc.org755b04a2011-11-15 16:57:561689 }
1690 }
1691 }
aluebs@webrtc.orgf17ee9c2015-01-29 00:03:531692#if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE)
1693 // To test the extended filter mode.
1694 audioproc::Test* test = ref_data.add_test();
1695 test->set_num_reverse_channels(2);
1696 test->set_num_input_channels(2);
1697 test->set_num_output_channels(2);
1698 test->set_sample_rate(AudioProcessing::kSampleRate32kHz);
1699 test->set_use_aec_extended_filter(true);
1700#endif
andrew@webrtc.org755b04a2011-11-15 16:57:561701 }
1702
andrew@webrtc.orgdaacee82012-02-07 00:01:041703 for (int i = 0; i < ref_data.test_size(); i++) {
1704 printf("Running test %d of %d...\n", i + 1, ref_data.test_size());
andrew@webrtc.org755b04a2011-11-15 16:57:561705
andrew@webrtc.org27c69802014-02-18 20:24:561706 audioproc::Test* test = ref_data.mutable_test(i);
andrew@webrtc.org60730cf2014-01-07 17:45:091707 // TODO(ajm): We no longer allow different input and output channels. Skip
1708 // these tests for now, but they should be removed from the set.
1709 if (test->num_input_channels() != test->num_output_channels())
1710 continue;
1711
Danil Chapovalovdc03d872024-10-24 14:16:471712 apm_ = BuiltinAudioProcessingBuilder()
Sam Zackrisson03cb7e52021-12-06 14:40:041713 .SetEchoDetector(CreateEchoDetector())
Danil Chapovalovdc03d872024-10-24 14:16:471714 .Build(CreateEnvironment());
Per Åhgren0695df12020-01-13 13:43:131715 AudioProcessing::Config apm_config = apm_->GetConfig();
1716 apm_config.gain_controller1.analog_gain_controller.enabled = false;
1717 apm_->ApplyConfig(apm_config);
aluebs@webrtc.orgf17ee9c2015-01-29 00:03:531718
1719 EnableAllComponents();
1720
Jonas Olssona4d87372019-07-05 17:08:331721 Init(test->sample_rate(), test->sample_rate(), test->sample_rate(),
Peter Kasting69558702016-01-13 00:26:351722 static_cast<size_t>(test->num_input_channels()),
1723 static_cast<size_t>(test->num_output_channels()),
Jonas Olssona4d87372019-07-05 17:08:331724 static_cast<size_t>(test->num_reverse_channels()), true);
andrew@webrtc.orgdaacee82012-02-07 00:01:041725
andrew@webrtc.org755b04a2011-11-15 16:57:561726 int frame_count = 0;
andrew@webrtc.org755b04a2011-11-15 16:57:561727 int analog_level = 127;
1728 int analog_level_average = 0;
1729 int max_output_average = 0;
minyue58530ed2016-05-24 12:50:121730#if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE)
Jonas Olssona4d87372019-07-05 17:08:331731 int stats_index = 0;
minyue58530ed2016-05-24 12:50:121732#endif
andrew@webrtc.org755b04a2011-11-15 16:57:561733
Sam Zackrisson70770ac2019-10-25 08:56:531734 while (ReadFrame(far_file_, &revframe_) && ReadFrame(near_file_, &frame_)) {
Per Åhgren2507f8c2020-03-19 11:33:291735 EXPECT_EQ(
Tommi5f163fc2024-11-19 10:53:511736 AudioProcessing::kNoError,
Per Åhgren2507f8c2020-03-19 11:33:291737 apm_->ProcessReverseStream(
1738 revframe_.data.data(),
Tommi5f163fc2024-11-19 10:53:511739 StreamConfig(revframe_.sample_rate_hz, revframe_.num_channels()),
1740 StreamConfig(revframe_.sample_rate_hz, revframe_.num_channels()),
Per Åhgren2507f8c2020-03-19 11:33:291741 revframe_.data.data()));
andrew@webrtc.org755b04a2011-11-15 16:57:561742
Tommi5f163fc2024-11-19 10:53:511743 EXPECT_EQ(AudioProcessing::kNoError, apm_->set_stream_delay_ms(0));
Sam Zackrisson41478c72019-10-15 08:10:261744 apm_->set_stream_analog_level(analog_level);
andrew@webrtc.org755b04a2011-11-15 16:57:561745
Tommi5f163fc2024-11-19 10:53:511746 EXPECT_EQ(AudioProcessing::kNoError,
Per Åhgren2507f8c2020-03-19 11:33:291747 apm_->ProcessStream(
1748 frame_.data.data(),
Tommi5f163fc2024-11-19 10:53:511749 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
1750 StreamConfig(frame_.sample_rate_hz, frame_.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:581751 frame_.data.data()));
andrew@webrtc.org17e40642014-03-04 20:58:131752
andrew@webrtc.orgdaacee82012-02-07 00:01:041753 // Ensure the frame was downmixed properly.
Peter Kasting69558702016-01-13 00:26:351754 EXPECT_EQ(static_cast<size_t>(test->num_output_channels()),
Tommi5f163fc2024-11-19 10:53:511755 frame_.num_channels());
andrew@webrtc.org755b04a2011-11-15 16:57:561756
Sam Zackrisson70770ac2019-10-25 08:56:531757 max_output_average += MaxAudioFrame(frame_);
andrew@webrtc.org755b04a2011-11-15 16:57:561758
Sam Zackrisson41478c72019-10-15 08:10:261759 analog_level = apm_->recommended_stream_analog_level();
andrew@webrtc.org755b04a2011-11-15 16:57:561760 analog_level_average += analog_level;
Per Åhgrencf4c8722019-12-30 13:32:141761 AudioProcessingStats stats = apm_->GetStatistics();
bjornv@webrtc.org08329f42012-07-12 21:00:431762
Jonas Olssona4d87372019-07-05 17:08:331763 size_t write_count =
Tommi5f163fc2024-11-19 10:53:511764 fwrite(frame_.data.data(), sizeof(int16_t), frame_.size(), out_file_);
1765 ASSERT_EQ(frame_.size(), write_count);
andrew@webrtc.orgdaacee82012-02-07 00:01:041766
1767 // Reset in case of downmixing.
Tommi5f163fc2024-11-19 10:53:511768 frame_.set_num_channels(static_cast<size_t>(test->num_input_channels()));
andrew@webrtc.org755b04a2011-11-15 16:57:561769 frame_count++;
minyue58530ed2016-05-24 12:50:121770
1771#if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE)
1772 const int kStatsAggregationFrameNum = 100; // 1 second.
1773 if (frame_count % kStatsAggregationFrameNum == 0) {
Sam Zackrissonaf6c1392018-09-13 10:59:091774 // Get echo and delay metrics.
Mirko Bonadei54c90f22021-10-03 09:26:111775 AudioProcessingStats stats2 = apm_->GetStatistics();
minyue58530ed2016-05-24 12:50:121776
Sam Zackrissonaf6c1392018-09-13 10:59:091777 // Echo metrics.
Mirko Bonadei54c90f22021-10-03 09:26:111778 const float echo_return_loss = stats2.echo_return_loss.value_or(-1.0f);
Sam Zackrissonaf6c1392018-09-13 10:59:091779 const float echo_return_loss_enhancement =
Mirko Bonadei54c90f22021-10-03 09:26:111780 stats2.echo_return_loss_enhancement.value_or(-1.0f);
Sam Zackrissonaf6c1392018-09-13 10:59:091781 const float residual_echo_likelihood =
Mirko Bonadei54c90f22021-10-03 09:26:111782 stats2.residual_echo_likelihood.value_or(-1.0f);
Sam Zackrissonaf6c1392018-09-13 10:59:091783 const float residual_echo_likelihood_recent_max =
Mirko Bonadei54c90f22021-10-03 09:26:111784 stats2.residual_echo_likelihood_recent_max.value_or(-1.0f);
Sam Zackrissonaf6c1392018-09-13 10:59:091785
Sam Zackrisson6558fa52019-08-26 08:12:411786 if (!absl::GetFlag(FLAGS_write_apm_ref_data)) {
minyue58530ed2016-05-24 12:50:121787 const audioproc::Test::EchoMetrics& reference =
1788 test->echo_metrics(stats_index);
Sam Zackrissonaf6c1392018-09-13 10:59:091789 constexpr float kEpsilon = 0.01;
1790 EXPECT_NEAR(echo_return_loss, reference.echo_return_loss(), kEpsilon);
1791 EXPECT_NEAR(echo_return_loss_enhancement,
1792 reference.echo_return_loss_enhancement(), kEpsilon);
Sam Zackrissonaf6c1392018-09-13 10:59:091793 EXPECT_NEAR(residual_echo_likelihood,
1794 reference.residual_echo_likelihood(), kEpsilon);
1795 EXPECT_NEAR(residual_echo_likelihood_recent_max,
1796 reference.residual_echo_likelihood_recent_max(),
1797 kEpsilon);
minyue58530ed2016-05-24 12:50:121798 ++stats_index;
1799 } else {
Sam Zackrissonaf6c1392018-09-13 10:59:091800 audioproc::Test::EchoMetrics* message_echo = test->add_echo_metrics();
1801 message_echo->set_echo_return_loss(echo_return_loss);
1802 message_echo->set_echo_return_loss_enhancement(
1803 echo_return_loss_enhancement);
Sam Zackrissonaf6c1392018-09-13 10:59:091804 message_echo->set_residual_echo_likelihood(residual_echo_likelihood);
1805 message_echo->set_residual_echo_likelihood_recent_max(
1806 residual_echo_likelihood_recent_max);
minyue58530ed2016-05-24 12:50:121807 }
1808 }
1809#endif // defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE).
andrew@webrtc.org755b04a2011-11-15 16:57:561810 }
1811 max_output_average /= frame_count;
1812 analog_level_average /= frame_count;
1813
Sam Zackrisson6558fa52019-08-26 08:12:411814 if (!absl::GetFlag(FLAGS_write_apm_ref_data)) {
bjornv@webrtc.org8dd60cc2014-09-11 08:36:351815 const int kIntNear = 1;
Alessio Bazzica1db0a262022-02-15 14:18:091816 // All numbers being consistently higher on N7 compare to the reference
1817 // data.
bjornv@webrtc.orgdc0b37d2014-09-23 05:03:441818 // TODO(bjornv): If we start getting more of these offsets on Android we
1819 // should consider a different approach. Either using one slack for all,
1820 // or generate a separate android reference.
Kári Tristan Helgason640106e2018-09-06 13:29:451821#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
bjornv@webrtc.orgdc0b37d2014-09-23 05:03:441822 const int kMaxOutputAverageOffset = 9;
Sam Zackrissone507b0c2018-07-20 13:22:501823 const int kMaxOutputAverageNear = 26;
bjornv@webrtc.orgdc0b37d2014-09-23 05:03:441824#else
bjornv@webrtc.orgdc0b37d2014-09-23 05:03:441825 const int kMaxOutputAverageOffset = 0;
1826 const int kMaxOutputAverageNear = kIntNear;
1827#endif
bjornv@webrtc.org8dd60cc2014-09-11 08:36:351828 EXPECT_NEAR(test->analog_level_average(), analog_level_average, kIntNear);
bjornv@webrtc.orgdc0b37d2014-09-23 05:03:441829 EXPECT_NEAR(test->max_output_average(),
1830 max_output_average - kMaxOutputAverageOffset,
1831 kMaxOutputAverageNear);
andrew@webrtc.org755b04a2011-11-15 16:57:561832 } else {
andrew@webrtc.org755b04a2011-11-15 16:57:561833 test->set_analog_level_average(analog_level_average);
1834 test->set_max_output_average(max_output_average);
andrew@webrtc.org755b04a2011-11-15 16:57:561835 }
1836
1837 rewind(far_file_);
1838 rewind(near_file_);
1839 }
1840
Sam Zackrisson6558fa52019-08-26 08:12:411841 if (absl::GetFlag(FLAGS_write_apm_ref_data)) {
andrew@webrtc.orga8b97372014-03-10 22:26:121842 OpenFileAndWriteMessage(ref_filename_, ref_data);
andrew@webrtc.org755b04a2011-11-15 16:57:561843 }
1844}
andrew@webrtc.orgddbb8a22014-04-22 21:00:041845
andrew@webrtc.orgddbb8a22014-04-22 21:00:041846// Compares the reference and test arrays over a region around the expected
1847// delay. Finds the highest SNR in that region and adds the variance and squared
1848// error results to the supplied accumulators.
1849void UpdateBestSNR(const float* ref,
1850 const float* test,
pkasting25702cb2016-01-08 21:50:271851 size_t length,
andrew@webrtc.orgddbb8a22014-04-22 21:00:041852 int expected_delay,
1853 double* variance_acc,
1854 double* sq_error_acc) {
Sam Zackrisson3bd444f2022-08-03 12:37:001855 RTC_CHECK_LT(expected_delay, length)
1856 << "delay greater than signal length, cannot compute SNR";
andrew@webrtc.orgddbb8a22014-04-22 21:00:041857 double best_snr = std::numeric_limits<double>::min();
1858 double best_variance = 0;
1859 double best_sq_error = 0;
Sam Zackrisson3bd444f2022-08-03 12:37:001860 // Search over a region of nine samples around the expected delay.
andrew@webrtc.orgddbb8a22014-04-22 21:00:041861 for (int delay = std::max(expected_delay - 4, 0); delay <= expected_delay + 4;
1862 ++delay) {
1863 double sq_error = 0;
1864 double variance = 0;
pkasting25702cb2016-01-08 21:50:271865 for (size_t i = 0; i < length - delay; ++i) {
andrew@webrtc.orgddbb8a22014-04-22 21:00:041866 double error = test[i + delay] - ref[i];
1867 sq_error += error * error;
1868 variance += ref[i] * ref[i];
1869 }
1870
1871 if (sq_error == 0) {
1872 *variance_acc += variance;
1873 return;
1874 }
1875 double snr = variance / sq_error;
1876 if (snr > best_snr) {
1877 best_snr = snr;
1878 best_variance = variance;
1879 best_sq_error = sq_error;
1880 }
1881 }
1882
1883 *variance_acc += best_variance;
1884 *sq_error_acc += best_sq_error;
1885}
1886
1887// Used to test a multitude of sample rate and channel combinations. It works
1888// by first producing a set of reference files (in SetUpTestCase) that are
1889// assumed to be correct, as the used parameters are verified by other tests
1890// in this collection. Primarily the reference files are all produced at
1891// "native" rates which do not involve any resampling.
1892
1893// Each test pass produces an output file with a particular format. The output
1894// is matched against the reference file closest to its internal processing
1895// format. If necessary the output is resampled back to its process format.
1896// Due to the resampling distortion, we don't expect identical results, but
1897// enforce SNR thresholds which vary depending on the format. 0 is a special
1898// case SNR which corresponds to inf, or zero error.
Edward Lemurc5ee9872017-10-23 21:33:041899typedef std::tuple<int, int, int, int, double, double> AudioProcessingTestData;
andrew@webrtc.orgddbb8a22014-04-22 21:00:041900class AudioProcessingTest
Mirko Bonadei6a489f22019-04-09 13:11:121901 : public ::testing::TestWithParam<AudioProcessingTestData> {
andrew@webrtc.orgddbb8a22014-04-22 21:00:041902 public:
1903 AudioProcessingTest()
Edward Lemurc5ee9872017-10-23 21:33:041904 : input_rate_(std::get<0>(GetParam())),
1905 output_rate_(std::get<1>(GetParam())),
1906 reverse_input_rate_(std::get<2>(GetParam())),
1907 reverse_output_rate_(std::get<3>(GetParam())),
1908 expected_snr_(std::get<4>(GetParam())),
1909 expected_reverse_snr_(std::get<5>(GetParam())) {}
andrew@webrtc.orgddbb8a22014-04-22 21:00:041910
1911 virtual ~AudioProcessingTest() {}
1912
Mirko Bonadei71061bc2019-06-04 07:01:511913 static void SetUpTestSuite() {
andrew@webrtc.orgddbb8a22014-04-22 21:00:041914 // Create all needed output reference files.
Peter Kasting69558702016-01-13 00:26:351915 const size_t kNumChannels[] = {1, 2};
Sam Zackrisson3bd444f2022-08-03 12:37:001916 for (size_t i = 0; i < arraysize(kProcessSampleRates); ++i) {
pkasting25702cb2016-01-08 21:50:271917 for (size_t j = 0; j < arraysize(kNumChannels); ++j) {
1918 for (size_t k = 0; k < arraysize(kNumChannels); ++k) {
andrew@webrtc.orgddbb8a22014-04-22 21:00:041919 // The reference files always have matching input and output channels.
Sam Zackrisson3bd444f2022-08-03 12:37:001920 ProcessFormat(kProcessSampleRates[i], kProcessSampleRates[i],
1921 kProcessSampleRates[i], kProcessSampleRates[i],
1922 kNumChannels[j], kNumChannels[j], kNumChannels[k],
1923 kNumChannels[k], "ref");
andrew@webrtc.orgddbb8a22014-04-22 21:00:041924 }
1925 }
1926 }
1927 }
1928
Gustaf Ullberg8ffeeb22017-10-11 09:42:381929 void TearDown() {
1930 // Remove "out" files after each test.
1931 ClearTempOutFiles();
1932 }
1933
Mirko Bonadei71061bc2019-06-04 07:01:511934 static void TearDownTestSuite() { ClearTempFiles(); }
ekmeyerson60d9b332015-08-14 17:35:551935
andrew@webrtc.orgddbb8a22014-04-22 21:00:041936 // Runs a process pass on files with the given parameters and dumps the output
Artem Titov0b489302021-07-28 18:50:031937 // to a file specified with `output_file_prefix`. Both forward and reverse
ekmeyerson60d9b332015-08-14 17:35:551938 // output streams are dumped.
andrew@webrtc.orgddbb8a22014-04-22 21:00:041939 static void ProcessFormat(int input_rate,
1940 int output_rate,
ekmeyerson60d9b332015-08-14 17:35:551941 int reverse_input_rate,
1942 int reverse_output_rate,
Peter Kasting69558702016-01-13 00:26:351943 size_t num_input_channels,
1944 size_t num_output_channels,
1945 size_t num_reverse_input_channels,
1946 size_t num_reverse_output_channels,
Ali Tofighf3592cb2022-08-16 12:44:381947 absl::string_view output_file_prefix) {
Sam Zackrisson3bd444f2022-08-03 12:37:001948 AudioProcessing::Config apm_config;
Per Åhgren0695df12020-01-13 13:43:131949 apm_config.gain_controller1.analog_gain_controller.enabled = false;
Danil Chapovalovdc03d872024-10-24 14:16:471950 scoped_refptr<AudioProcessing> ap = BuiltinAudioProcessingBuilder()
Danil Chapovalov9c21f632024-10-16 06:29:591951 .SetConfig(apm_config)
Danil Chapovalovdc03d872024-10-24 14:16:471952 .Build(CreateEnvironment());
Per Åhgren0695df12020-01-13 13:43:131953
andrew@webrtc.orgddbb8a22014-04-22 21:00:041954 EnableAllAPComponents(ap.get());
andrew@webrtc.orgddbb8a22014-04-22 21:00:041955
ekmeyerson60d9b332015-08-14 17:35:551956 ProcessingConfig processing_config = {
1957 {{input_rate, num_input_channels},
1958 {output_rate, num_output_channels},
1959 {reverse_input_rate, num_reverse_input_channels},
1960 {reverse_output_rate, num_reverse_output_channels}}};
1961 ap->Initialize(processing_config);
1962
1963 FILE* far_file =
1964 fopen(ResourceFilePath("far", reverse_input_rate).c_str(), "rb");
andrew@webrtc.orgddbb8a22014-04-22 21:00:041965 FILE* near_file = fopen(ResourceFilePath("near", input_rate).c_str(), "rb");
Jonas Olssona4d87372019-07-05 17:08:331966 FILE* out_file = fopen(
1967 OutputFilePath(
1968 output_file_prefix, input_rate, output_rate, reverse_input_rate,
1969 reverse_output_rate, num_input_channels, num_output_channels,
1970 num_reverse_input_channels, num_reverse_output_channels, kForward)
1971 .c_str(),
1972 "wb");
1973 FILE* rev_out_file = fopen(
1974 OutputFilePath(
1975 output_file_prefix, input_rate, output_rate, reverse_input_rate,
1976 reverse_output_rate, num_input_channels, num_output_channels,
1977 num_reverse_input_channels, num_reverse_output_channels, kReverse)
1978 .c_str(),
1979 "wb");
andrew@webrtc.orgddbb8a22014-04-22 21:00:041980 ASSERT_TRUE(far_file != NULL);
1981 ASSERT_TRUE(near_file != NULL);
1982 ASSERT_TRUE(out_file != NULL);
ekmeyerson60d9b332015-08-14 17:35:551983 ASSERT_TRUE(rev_out_file != NULL);
andrew@webrtc.orgddbb8a22014-04-22 21:00:041984
Sam Zackrisson3bd444f2022-08-03 12:37:001985 ChannelBuffer<float> fwd_cb(AudioProcessing::GetFrameSize(input_rate),
andrew@webrtc.orgddbb8a22014-04-22 21:00:041986 num_input_channels);
Sam Zackrisson3bd444f2022-08-03 12:37:001987 ChannelBuffer<float> rev_cb(
1988 AudioProcessing::GetFrameSize(reverse_input_rate),
1989 num_reverse_input_channels);
1990 ChannelBuffer<float> out_cb(AudioProcessing::GetFrameSize(output_rate),
andrew@webrtc.orgddbb8a22014-04-22 21:00:041991 num_output_channels);
Sam Zackrisson3bd444f2022-08-03 12:37:001992 ChannelBuffer<float> rev_out_cb(
1993 AudioProcessing::GetFrameSize(reverse_output_rate),
1994 num_reverse_output_channels);
andrew@webrtc.orgddbb8a22014-04-22 21:00:041995
1996 // Temporary buffers.
1997 const int max_length =
ekmeyerson60d9b332015-08-14 17:35:551998 2 * std::max(std::max(out_cb.num_frames(), rev_out_cb.num_frames()),
1999 std::max(fwd_cb.num_frames(), rev_cb.num_frames()));
kwiberg62eaacf2016-02-17 14:39:052000 std::unique_ptr<float[]> float_data(new float[max_length]);
2001 std::unique_ptr<int16_t[]> int_data(new int16_t[max_length]);
andrew@webrtc.orgddbb8a22014-04-22 21:00:042002
2003 int analog_level = 127;
2004 while (ReadChunk(far_file, int_data.get(), float_data.get(), &rev_cb) &&
2005 ReadChunk(near_file, int_data.get(), float_data.get(), &fwd_cb)) {
ekmeyerson60d9b332015-08-14 17:35:552006 EXPECT_NOERR(ap->ProcessReverseStream(
2007 rev_cb.channels(), processing_config.reverse_input_stream(),
2008 processing_config.reverse_output_stream(), rev_out_cb.channels()));
andrew@webrtc.orgddbb8a22014-04-22 21:00:042009
2010 EXPECT_NOERR(ap->set_stream_delay_ms(0));
Sam Zackrisson41478c72019-10-15 08:10:262011 ap->set_stream_analog_level(analog_level);
andrew@webrtc.orgddbb8a22014-04-22 21:00:042012
2013 EXPECT_NOERR(ap->ProcessStream(
Gustaf Ullbergcb307262019-10-29 08:30:442014 fwd_cb.channels(), StreamConfig(input_rate, num_input_channels),
2015 StreamConfig(output_rate, num_output_channels), out_cb.channels()));
andrew@webrtc.orgddbb8a22014-04-22 21:00:042016
ekmeyerson60d9b332015-08-14 17:35:552017 // Dump forward output to file.
Tommif58ded72024-05-30 11:29:112018 RTC_DCHECK_EQ(out_cb.num_bands(), 1u); // Assumes full frequency band.
2019 DeinterleavedView<const float> deinterleaved_src(
2020 out_cb.channels()[0], out_cb.num_frames(), out_cb.num_channels());
2021 InterleavedView<float> interleaved_dst(
2022 float_data.get(), out_cb.num_frames(), out_cb.num_channels());
2023 Interleave(deinterleaved_src, interleaved_dst);
pkasting25702cb2016-01-08 21:50:272024 size_t out_length = out_cb.num_channels() * out_cb.num_frames();
ekmeyerson60d9b332015-08-14 17:35:552025
Jonas Olssona4d87372019-07-05 17:08:332026 ASSERT_EQ(out_length, fwrite(float_data.get(), sizeof(float_data[0]),
2027 out_length, out_file));
andrew@webrtc.orgddbb8a22014-04-22 21:00:042028
ekmeyerson60d9b332015-08-14 17:35:552029 // Dump reverse output to file.
Tommif58ded72024-05-30 11:29:112030 RTC_DCHECK_EQ(rev_out_cb.num_bands(), 1u);
2031 deinterleaved_src = DeinterleavedView<const float>(
2032 rev_out_cb.channels()[0], rev_out_cb.num_frames(),
2033 rev_out_cb.num_channels());
2034 interleaved_dst = InterleavedView<float>(
2035 float_data.get(), rev_out_cb.num_frames(), rev_out_cb.num_channels());
2036 Interleave(deinterleaved_src, interleaved_dst);
pkasting25702cb2016-01-08 21:50:272037 size_t rev_out_length =
2038 rev_out_cb.num_channels() * rev_out_cb.num_frames();
ekmeyerson60d9b332015-08-14 17:35:552039
Jonas Olssona4d87372019-07-05 17:08:332040 ASSERT_EQ(rev_out_length, fwrite(float_data.get(), sizeof(float_data[0]),
2041 rev_out_length, rev_out_file));
ekmeyerson60d9b332015-08-14 17:35:552042
Sam Zackrisson41478c72019-10-15 08:10:262043 analog_level = ap->recommended_stream_analog_level();
andrew@webrtc.orgddbb8a22014-04-22 21:00:042044 }
2045 fclose(far_file);
2046 fclose(near_file);
2047 fclose(out_file);
ekmeyerson60d9b332015-08-14 17:35:552048 fclose(rev_out_file);
andrew@webrtc.orgddbb8a22014-04-22 21:00:042049 }
2050
2051 protected:
2052 int input_rate_;
2053 int output_rate_;
ekmeyerson60d9b332015-08-14 17:35:552054 int reverse_input_rate_;
2055 int reverse_output_rate_;
andrew@webrtc.orgddbb8a22014-04-22 21:00:042056 double expected_snr_;
ekmeyerson60d9b332015-08-14 17:35:552057 double expected_reverse_snr_;
andrew@webrtc.orgddbb8a22014-04-22 21:00:042058};
2059
bjornv@webrtc.org2812b592014-06-02 11:27:292060TEST_P(AudioProcessingTest, Formats) {
andrew@webrtc.orgddbb8a22014-04-22 21:00:042061 struct ChannelFormat {
2062 int num_input;
2063 int num_output;
ekmeyerson60d9b332015-08-14 17:35:552064 int num_reverse_input;
2065 int num_reverse_output;
andrew@webrtc.orgddbb8a22014-04-22 21:00:042066 };
2067 ChannelFormat cf[] = {
Jonas Olssona4d87372019-07-05 17:08:332068 {1, 1, 1, 1}, {1, 1, 2, 1}, {2, 1, 1, 1},
2069 {2, 1, 2, 1}, {2, 2, 1, 1}, {2, 2, 2, 2},
andrew@webrtc.orgddbb8a22014-04-22 21:00:042070 };
andrew@webrtc.orgddbb8a22014-04-22 21:00:042071
pkasting25702cb2016-01-08 21:50:272072 for (size_t i = 0; i < arraysize(cf); ++i) {
ekmeyerson60d9b332015-08-14 17:35:552073 ProcessFormat(input_rate_, output_rate_, reverse_input_rate_,
2074 reverse_output_rate_, cf[i].num_input, cf[i].num_output,
2075 cf[i].num_reverse_input, cf[i].num_reverse_output, "out");
Alejandro Luebs47748742015-05-22 19:00:212076
ekmeyerson60d9b332015-08-14 17:35:552077 // Verify output for both directions.
2078 std::vector<StreamDirection> stream_directions;
2079 stream_directions.push_back(kForward);
2080 stream_directions.push_back(kReverse);
2081 for (StreamDirection file_direction : stream_directions) {
2082 const int in_rate = file_direction ? reverse_input_rate_ : input_rate_;
2083 const int out_rate = file_direction ? reverse_output_rate_ : output_rate_;
2084 const int out_num =
2085 file_direction ? cf[i].num_reverse_output : cf[i].num_output;
2086 const double expected_snr =
2087 file_direction ? expected_reverse_snr_ : expected_snr_;
2088
2089 const int min_ref_rate = std::min(in_rate, out_rate);
2090 int ref_rate;
ekmeyerson60d9b332015-08-14 17:35:552091 if (min_ref_rate > 32000) {
2092 ref_rate = 48000;
2093 } else if (min_ref_rate > 16000) {
2094 ref_rate = 32000;
ekmeyerson60d9b332015-08-14 17:35:552095 } else {
Sam Zackrisson3bd444f2022-08-03 12:37:002096 ref_rate = 16000;
ekmeyerson60d9b332015-08-14 17:35:552097 }
Per Åhgrenc0424252019-12-10 12:04:152098
ekmeyerson60d9b332015-08-14 17:35:552099 FILE* out_file = fopen(
2100 OutputFilePath("out", input_rate_, output_rate_, reverse_input_rate_,
2101 reverse_output_rate_, cf[i].num_input,
2102 cf[i].num_output, cf[i].num_reverse_input,
Jonas Olssona4d87372019-07-05 17:08:332103 cf[i].num_reverse_output, file_direction)
2104 .c_str(),
ekmeyerson60d9b332015-08-14 17:35:552105 "rb");
2106 // The reference files always have matching input and output channels.
Jonas Olssona4d87372019-07-05 17:08:332107 FILE* ref_file =
2108 fopen(OutputFilePath("ref", ref_rate, ref_rate, ref_rate, ref_rate,
2109 cf[i].num_output, cf[i].num_output,
2110 cf[i].num_reverse_output,
2111 cf[i].num_reverse_output, file_direction)
2112 .c_str(),
2113 "rb");
ekmeyerson60d9b332015-08-14 17:35:552114 ASSERT_TRUE(out_file != NULL);
2115 ASSERT_TRUE(ref_file != NULL);
andrew@webrtc.orgddbb8a22014-04-22 21:00:042116
Tommi5d3e6802024-05-24 14:43:552117 const size_t ref_samples_per_channel =
2118 AudioProcessing::GetFrameSize(ref_rate);
2119 const size_t ref_length = ref_samples_per_channel * out_num;
2120 const size_t out_samples_per_channel =
2121 AudioProcessing::GetFrameSize(out_rate);
2122 const size_t out_length = out_samples_per_channel * out_num;
ekmeyerson60d9b332015-08-14 17:35:552123 // Data from the reference file.
kwiberg62eaacf2016-02-17 14:39:052124 std::unique_ptr<float[]> ref_data(new float[ref_length]);
ekmeyerson60d9b332015-08-14 17:35:552125 // Data from the output file.
kwiberg62eaacf2016-02-17 14:39:052126 std::unique_ptr<float[]> out_data(new float[out_length]);
ekmeyerson60d9b332015-08-14 17:35:552127 // Data from the resampled output, in case the reference and output rates
2128 // don't match.
kwiberg62eaacf2016-02-17 14:39:052129 std::unique_ptr<float[]> cmp_data(new float[ref_length]);
andrew@webrtc.orgddbb8a22014-04-22 21:00:042130
Tommid6ef33e2024-07-03 12:23:482131 PushResampler<float> resampler(out_samples_per_channel,
2132 ref_samples_per_channel, out_num);
andrew@webrtc.orgddbb8a22014-04-22 21:00:042133
ekmeyerson60d9b332015-08-14 17:35:552134 // Compute the resampling delay of the output relative to the reference,
2135 // to find the region over which we should search for the best SNR.
2136 float expected_delay_sec = 0;
2137 if (in_rate != ref_rate) {
2138 // Input resampling delay.
2139 expected_delay_sec +=
2140 PushSincResampler::AlgorithmicDelaySeconds(in_rate);
2141 }
2142 if (out_rate != ref_rate) {
2143 // Output resampling delay.
2144 expected_delay_sec +=
2145 PushSincResampler::AlgorithmicDelaySeconds(ref_rate);
2146 // Delay of converting the output back to its processing rate for
2147 // testing.
2148 expected_delay_sec +=
2149 PushSincResampler::AlgorithmicDelaySeconds(out_rate);
2150 }
Sam Zackrisson3bd444f2022-08-03 12:37:002151 // The delay is multiplied by the number of channels because
2152 // UpdateBestSNR() computes the SNR over interleaved data without taking
2153 // channels into account.
ekmeyerson60d9b332015-08-14 17:35:552154 int expected_delay =
Oleh Prypin708eccc2019-03-27 08:38:522155 std::floor(expected_delay_sec * ref_rate + 0.5f) * out_num;
andrew@webrtc.orgddbb8a22014-04-22 21:00:042156
ekmeyerson60d9b332015-08-14 17:35:552157 double variance = 0;
2158 double sq_error = 0;
2159 while (fread(out_data.get(), sizeof(out_data[0]), out_length, out_file) &&
2160 fread(ref_data.get(), sizeof(ref_data[0]), ref_length, ref_file)) {
2161 float* out_ptr = out_data.get();
2162 if (out_rate != ref_rate) {
2163 // Resample the output back to its internal processing rate if
Sam Zackrisson3bd444f2022-08-03 12:37:002164 // necessary.
Tommi5d3e6802024-05-24 14:43:552165 InterleavedView<const float> src(out_ptr, out_samples_per_channel,
2166 out_num);
2167 InterleavedView<float> dst(cmp_data.get(), ref_samples_per_channel,
2168 out_num);
pkasting25702cb2016-01-08 21:50:272169 ASSERT_EQ(ref_length,
Tommi5d3e6802024-05-24 14:43:552170 static_cast<size_t>(resampler.Resample(src, dst)));
ekmeyerson60d9b332015-08-14 17:35:552171 out_ptr = cmp_data.get();
2172 }
andrew@webrtc.orgddbb8a22014-04-22 21:00:042173
Artem Titov0b489302021-07-28 18:50:032174 // Update the `sq_error` and `variance` accumulators with the highest
ekmeyerson60d9b332015-08-14 17:35:552175 // SNR of reference vs output.
2176 UpdateBestSNR(ref_data.get(), out_ptr, ref_length, expected_delay,
2177 &variance, &sq_error);
andrew@webrtc.orgddbb8a22014-04-22 21:00:042178 }
2179
ekmeyerson60d9b332015-08-14 17:35:552180 std::cout << "(" << input_rate_ << ", " << output_rate_ << ", "
2181 << reverse_input_rate_ << ", " << reverse_output_rate_ << ", "
2182 << cf[i].num_input << ", " << cf[i].num_output << ", "
2183 << cf[i].num_reverse_input << ", " << cf[i].num_reverse_output
2184 << ", " << file_direction << "): ";
2185 if (sq_error > 0) {
2186 double snr = 10 * log10(variance / sq_error);
2187 EXPECT_GE(snr, expected_snr);
2188 EXPECT_NE(0, expected_snr);
2189 std::cout << "SNR=" << snr << " dB" << std::endl;
2190 } else {
aluebs776593b2016-03-15 21:04:582191 std::cout << "SNR=inf dB" << std::endl;
ekmeyerson60d9b332015-08-14 17:35:552192 }
andrew@webrtc.orgddbb8a22014-04-22 21:00:042193
ekmeyerson60d9b332015-08-14 17:35:552194 fclose(out_file);
2195 fclose(ref_file);
andrew@webrtc.orgddbb8a22014-04-22 21:00:042196 }
andrew@webrtc.orgddbb8a22014-04-22 21:00:042197 }
2198}
2199
2200#if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE)
Mirko Bonadeic84f6612019-01-31 11:20:572201INSTANTIATE_TEST_SUITE_P(
ekmeyerson60d9b332015-08-14 17:35:552202 CommonFormats,
2203 AudioProcessingTest,
Sam Zackrisson3bd444f2022-08-03 12:37:002204 // Internal processing rates and the particularly common sample rate 44100
2205 // Hz are tested in a grid of combinations (capture in, render in, out).
Mirko Bonadei6a489f22019-04-09 13:11:122206 ::testing::Values(std::make_tuple(48000, 48000, 48000, 48000, 0, 0),
2207 std::make_tuple(48000, 48000, 32000, 48000, 40, 30),
2208 std::make_tuple(48000, 48000, 16000, 48000, 40, 20),
2209 std::make_tuple(48000, 44100, 48000, 44100, 20, 20),
2210 std::make_tuple(48000, 44100, 32000, 44100, 20, 15),
2211 std::make_tuple(48000, 44100, 16000, 44100, 20, 15),
2212 std::make_tuple(48000, 32000, 48000, 32000, 30, 35),
2213 std::make_tuple(48000, 32000, 32000, 32000, 30, 0),
2214 std::make_tuple(48000, 32000, 16000, 32000, 30, 20),
2215 std::make_tuple(48000, 16000, 48000, 16000, 25, 20),
2216 std::make_tuple(48000, 16000, 32000, 16000, 25, 20),
2217 std::make_tuple(48000, 16000, 16000, 16000, 25, 0),
Alejandro Luebs47748742015-05-22 19:00:212218
Mirko Bonadei6a489f22019-04-09 13:11:122219 std::make_tuple(44100, 48000, 48000, 48000, 30, 0),
2220 std::make_tuple(44100, 48000, 32000, 48000, 30, 30),
2221 std::make_tuple(44100, 48000, 16000, 48000, 30, 20),
2222 std::make_tuple(44100, 44100, 48000, 44100, 20, 20),
2223 std::make_tuple(44100, 44100, 32000, 44100, 20, 15),
2224 std::make_tuple(44100, 44100, 16000, 44100, 20, 15),
2225 std::make_tuple(44100, 32000, 48000, 32000, 30, 35),
2226 std::make_tuple(44100, 32000, 32000, 32000, 30, 0),
2227 std::make_tuple(44100, 32000, 16000, 32000, 30, 20),
2228 std::make_tuple(44100, 16000, 48000, 16000, 25, 20),
2229 std::make_tuple(44100, 16000, 32000, 16000, 25, 20),
2230 std::make_tuple(44100, 16000, 16000, 16000, 25, 0),
Alejandro Luebs47748742015-05-22 19:00:212231
Per Åhgrenc0424252019-12-10 12:04:152232 std::make_tuple(32000, 48000, 48000, 48000, 15, 0),
2233 std::make_tuple(32000, 48000, 32000, 48000, 15, 30),
2234 std::make_tuple(32000, 48000, 16000, 48000, 15, 20),
Mirko Bonadei6a489f22019-04-09 13:11:122235 std::make_tuple(32000, 44100, 48000, 44100, 19, 20),
2236 std::make_tuple(32000, 44100, 32000, 44100, 19, 15),
2237 std::make_tuple(32000, 44100, 16000, 44100, 19, 15),
2238 std::make_tuple(32000, 32000, 48000, 32000, 40, 35),
2239 std::make_tuple(32000, 32000, 32000, 32000, 0, 0),
Gustaf Ullberg09226fc2021-02-19 12:03:142240 std::make_tuple(32000, 32000, 16000, 32000, 39, 20),
Mirko Bonadei6a489f22019-04-09 13:11:122241 std::make_tuple(32000, 16000, 48000, 16000, 25, 20),
2242 std::make_tuple(32000, 16000, 32000, 16000, 25, 20),
2243 std::make_tuple(32000, 16000, 16000, 16000, 25, 0),
Alejandro Luebs47748742015-05-22 19:00:212244
Per Åhgrenc0424252019-12-10 12:04:152245 std::make_tuple(16000, 48000, 48000, 48000, 9, 0),
2246 std::make_tuple(16000, 48000, 32000, 48000, 9, 30),
2247 std::make_tuple(16000, 48000, 16000, 48000, 9, 20),
Mirko Bonadei6a489f22019-04-09 13:11:122248 std::make_tuple(16000, 44100, 48000, 44100, 15, 20),
2249 std::make_tuple(16000, 44100, 32000, 44100, 15, 15),
2250 std::make_tuple(16000, 44100, 16000, 44100, 15, 15),
2251 std::make_tuple(16000, 32000, 48000, 32000, 25, 35),
2252 std::make_tuple(16000, 32000, 32000, 32000, 25, 0),
2253 std::make_tuple(16000, 32000, 16000, 32000, 25, 20),
2254 std::make_tuple(16000, 16000, 48000, 16000, 39, 20),
Gustaf Ullberg09226fc2021-02-19 12:03:142255 std::make_tuple(16000, 16000, 32000, 16000, 39, 20),
Sam Zackrisson3bd444f2022-08-03 12:37:002256 std::make_tuple(16000, 16000, 16000, 16000, 0, 0),
2257
2258 // Other sample rates are not tested exhaustively, to keep
2259 // the test runtime manageable.
2260 //
2261 // Testing most other sample rates logged by Chrome UMA:
2262 // - WebRTC.AudioInputSampleRate
2263 // - WebRTC.AudioOutputSampleRate
2264 // ApmConfiguration.HandlingOfRateCombinations covers
2265 // remaining sample rates.
2266 std::make_tuple(192000, 192000, 48000, 192000, 20, 40),
2267 std::make_tuple(176400, 176400, 48000, 176400, 20, 35),
2268 std::make_tuple(96000, 96000, 48000, 96000, 20, 40),
2269 std::make_tuple(88200, 88200, 48000, 88200, 20, 20),
2270 std::make_tuple(44100, 44100, 48000, 44100, 20, 20)));
Alejandro Luebs47748742015-05-22 19:00:212271
2272#elif defined(WEBRTC_AUDIOPROC_FIXED_PROFILE)
Mirko Bonadeic84f6612019-01-31 11:20:572273INSTANTIATE_TEST_SUITE_P(
ekmeyerson60d9b332015-08-14 17:35:552274 CommonFormats,
2275 AudioProcessingTest,
Per Åhgren0aefbf02019-08-23 19:29:172276 ::testing::Values(std::make_tuple(48000, 48000, 48000, 48000, 19, 0),
2277 std::make_tuple(48000, 48000, 32000, 48000, 19, 30),
2278 std::make_tuple(48000, 48000, 16000, 48000, 19, 20),
Mirko Bonadei6a489f22019-04-09 13:11:122279 std::make_tuple(48000, 44100, 48000, 44100, 15, 20),
2280 std::make_tuple(48000, 44100, 32000, 44100, 15, 15),
2281 std::make_tuple(48000, 44100, 16000, 44100, 15, 15),
Per Åhgren0aefbf02019-08-23 19:29:172282 std::make_tuple(48000, 32000, 48000, 32000, 19, 35),
2283 std::make_tuple(48000, 32000, 32000, 32000, 19, 0),
2284 std::make_tuple(48000, 32000, 16000, 32000, 19, 20),
Mirko Bonadei6a489f22019-04-09 13:11:122285 std::make_tuple(48000, 16000, 48000, 16000, 20, 20),
2286 std::make_tuple(48000, 16000, 32000, 16000, 20, 20),
2287 std::make_tuple(48000, 16000, 16000, 16000, 20, 0),
andrew@webrtc.orgddbb8a22014-04-22 21:00:042288
Mirko Bonadei6a489f22019-04-09 13:11:122289 std::make_tuple(44100, 48000, 48000, 48000, 15, 0),
2290 std::make_tuple(44100, 48000, 32000, 48000, 15, 30),
2291 std::make_tuple(44100, 48000, 16000, 48000, 15, 20),
2292 std::make_tuple(44100, 44100, 48000, 44100, 15, 20),
2293 std::make_tuple(44100, 44100, 32000, 44100, 15, 15),
2294 std::make_tuple(44100, 44100, 16000, 44100, 15, 15),
Per Åhgren0aefbf02019-08-23 19:29:172295 std::make_tuple(44100, 32000, 48000, 32000, 18, 35),
2296 std::make_tuple(44100, 32000, 32000, 32000, 18, 0),
2297 std::make_tuple(44100, 32000, 16000, 32000, 18, 20),
2298 std::make_tuple(44100, 16000, 48000, 16000, 19, 20),
2299 std::make_tuple(44100, 16000, 32000, 16000, 19, 20),
2300 std::make_tuple(44100, 16000, 16000, 16000, 19, 0),
andrew@webrtc.orgddbb8a22014-04-22 21:00:042301
Per Åhgrenc0424252019-12-10 12:04:152302 std::make_tuple(32000, 48000, 48000, 48000, 17, 0),
2303 std::make_tuple(32000, 48000, 32000, 48000, 17, 30),
2304 std::make_tuple(32000, 48000, 16000, 48000, 17, 20),
Mirko Bonadei6a489f22019-04-09 13:11:122305 std::make_tuple(32000, 44100, 48000, 44100, 20, 20),
2306 std::make_tuple(32000, 44100, 32000, 44100, 20, 15),
2307 std::make_tuple(32000, 44100, 16000, 44100, 20, 15),
Per Åhgrene35b32c2019-11-22 17:22:042308 std::make_tuple(32000, 32000, 48000, 32000, 27, 35),
Mirko Bonadei6a489f22019-04-09 13:11:122309 std::make_tuple(32000, 32000, 32000, 32000, 0, 0),
Per Åhgrene35b32c2019-11-22 17:22:042310 std::make_tuple(32000, 32000, 16000, 32000, 30, 20),
Mirko Bonadei6a489f22019-04-09 13:11:122311 std::make_tuple(32000, 16000, 48000, 16000, 20, 20),
2312 std::make_tuple(32000, 16000, 32000, 16000, 20, 20),
2313 std::make_tuple(32000, 16000, 16000, 16000, 20, 0),
andrew@webrtc.orgddbb8a22014-04-22 21:00:042314
Per Åhgrenc0424252019-12-10 12:04:152315 std::make_tuple(16000, 48000, 48000, 48000, 11, 0),
2316 std::make_tuple(16000, 48000, 32000, 48000, 11, 30),
2317 std::make_tuple(16000, 48000, 16000, 48000, 11, 20),
Mirko Bonadei6a489f22019-04-09 13:11:122318 std::make_tuple(16000, 44100, 48000, 44100, 15, 20),
2319 std::make_tuple(16000, 44100, 32000, 44100, 15, 15),
2320 std::make_tuple(16000, 44100, 16000, 44100, 15, 15),
Per Åhgren0cbb58e2019-10-29 21:59:442321 std::make_tuple(16000, 32000, 48000, 32000, 24, 35),
Per Åhgrene35b32c2019-11-22 17:22:042322 std::make_tuple(16000, 32000, 32000, 32000, 24, 0),
Mirko Bonadei6a489f22019-04-09 13:11:122323 std::make_tuple(16000, 32000, 16000, 32000, 25, 20),
Per Åhgrene35b32c2019-11-22 17:22:042324 std::make_tuple(16000, 16000, 48000, 16000, 28, 20),
2325 std::make_tuple(16000, 16000, 32000, 16000, 28, 20),
Sam Zackrisson3bd444f2022-08-03 12:37:002326 std::make_tuple(16000, 16000, 16000, 16000, 0, 0),
2327
2328 std::make_tuple(192000, 192000, 48000, 192000, 20, 40),
2329 std::make_tuple(176400, 176400, 48000, 176400, 20, 35),
2330 std::make_tuple(96000, 96000, 48000, 96000, 20, 40),
2331 std::make_tuple(88200, 88200, 48000, 88200, 20, 20),
2332 std::make_tuple(44100, 44100, 48000, 44100, 20, 20)));
andrew@webrtc.orgddbb8a22014-04-22 21:00:042333#endif
2334
Per Åhgren3e8bf282019-08-29 21:38:402335// Produces a scoped trace debug output.
2336std::string ProduceDebugText(int render_input_sample_rate_hz,
2337 int render_output_sample_rate_hz,
2338 int capture_input_sample_rate_hz,
2339 int capture_output_sample_rate_hz,
2340 size_t render_input_num_channels,
2341 size_t render_output_num_channels,
2342 size_t capture_input_num_channels,
2343 size_t capture_output_num_channels) {
2344 rtc::StringBuilder ss;
2345 ss << "Sample rates:"
Jonas Olsson6c9bc392020-01-14 14:54:352346 "\n Render input: "
Jonas Olssonb2b20312020-01-14 11:11:312347 << render_input_sample_rate_hz
2348 << " Hz"
Jonas Olsson6c9bc392020-01-14 14:54:352349 "\n Render output: "
Jonas Olssonb2b20312020-01-14 11:11:312350 << render_output_sample_rate_hz
2351 << " Hz"
Jonas Olsson6c9bc392020-01-14 14:54:352352 "\n Capture input: "
Jonas Olssonb2b20312020-01-14 11:11:312353 << capture_input_sample_rate_hz
2354 << " Hz"
Jonas Olsson6c9bc392020-01-14 14:54:352355 "\n Capture output: "
Jonas Olssonb2b20312020-01-14 11:11:312356 << capture_output_sample_rate_hz
2357 << " Hz"
Jonas Olsson6c9bc392020-01-14 14:54:352358 "\nNumber of channels:"
2359 "\n Render input: "
Jonas Olssonb2b20312020-01-14 11:11:312360 << render_input_num_channels
Jonas Olsson6c9bc392020-01-14 14:54:352361 << "\n Render output: " << render_output_num_channels
2362 << "\n Capture input: " << capture_input_num_channels
2363 << "\n Capture output: " << capture_output_num_channels;
Per Åhgren3e8bf282019-08-29 21:38:402364 return ss.Release();
2365}
2366
2367// Validates that running the audio processing module using various combinations
2368// of sample rates and number of channels works as intended.
2369void RunApmRateAndChannelTest(
2370 rtc::ArrayView<const int> sample_rates_hz,
2371 rtc::ArrayView<const int> render_channel_counts,
2372 rtc::ArrayView<const int> capture_channel_counts) {
Per Åhgren3e8bf282019-08-29 21:38:402373 webrtc::AudioProcessing::Config apm_config;
Sam Zackrisson3bd444f2022-08-03 12:37:002374 apm_config.pipeline.multi_channel_render = true;
2375 apm_config.pipeline.multi_channel_capture = true;
Per Åhgren3e8bf282019-08-29 21:38:402376 apm_config.echo_canceller.enabled = true;
Danil Chapovalov9c21f632024-10-16 06:29:592377 scoped_refptr<AudioProcessing> apm =
Danil Chapovalovdc03d872024-10-24 14:16:472378 BuiltinAudioProcessingBuilder(apm_config).Build(CreateEnvironment());
Per Åhgren3e8bf282019-08-29 21:38:402379
2380 StreamConfig render_input_stream_config;
2381 StreamConfig render_output_stream_config;
2382 StreamConfig capture_input_stream_config;
2383 StreamConfig capture_output_stream_config;
2384
2385 std::vector<float> render_input_frame_channels;
2386 std::vector<float*> render_input_frame;
2387 std::vector<float> render_output_frame_channels;
2388 std::vector<float*> render_output_frame;
2389 std::vector<float> capture_input_frame_channels;
2390 std::vector<float*> capture_input_frame;
2391 std::vector<float> capture_output_frame_channels;
2392 std::vector<float*> capture_output_frame;
2393
2394 for (auto render_input_sample_rate_hz : sample_rates_hz) {
2395 for (auto render_output_sample_rate_hz : sample_rates_hz) {
2396 for (auto capture_input_sample_rate_hz : sample_rates_hz) {
2397 for (auto capture_output_sample_rate_hz : sample_rates_hz) {
2398 for (size_t render_input_num_channels : render_channel_counts) {
2399 for (size_t capture_input_num_channels : capture_channel_counts) {
2400 size_t render_output_num_channels = render_input_num_channels;
2401 size_t capture_output_num_channels = capture_input_num_channels;
2402 auto populate_audio_frame = [](int sample_rate_hz,
2403 size_t num_channels,
2404 StreamConfig* cfg,
2405 std::vector<float>* channels_data,
2406 std::vector<float*>* frame_data) {
2407 cfg->set_sample_rate_hz(sample_rate_hz);
2408 cfg->set_num_channels(num_channels);
Per Åhgren3e8bf282019-08-29 21:38:402409
Sam Zackrisson3bd444f2022-08-03 12:37:002410 size_t max_frame_size =
2411 AudioProcessing::GetFrameSize(sample_rate_hz);
Per Åhgren3e8bf282019-08-29 21:38:402412 channels_data->resize(num_channels * max_frame_size);
2413 std::fill(channels_data->begin(), channels_data->end(), 0.5f);
2414 frame_data->resize(num_channels);
2415 for (size_t channel = 0; channel < num_channels; ++channel) {
2416 (*frame_data)[channel] =
2417 &(*channels_data)[channel * max_frame_size];
2418 }
2419 };
2420
2421 populate_audio_frame(
2422 render_input_sample_rate_hz, render_input_num_channels,
2423 &render_input_stream_config, &render_input_frame_channels,
2424 &render_input_frame);
2425 populate_audio_frame(
2426 render_output_sample_rate_hz, render_output_num_channels,
2427 &render_output_stream_config, &render_output_frame_channels,
2428 &render_output_frame);
2429 populate_audio_frame(
2430 capture_input_sample_rate_hz, capture_input_num_channels,
2431 &capture_input_stream_config, &capture_input_frame_channels,
2432 &capture_input_frame);
2433 populate_audio_frame(
2434 capture_output_sample_rate_hz, capture_output_num_channels,
2435 &capture_output_stream_config, &capture_output_frame_channels,
2436 &capture_output_frame);
2437
2438 for (size_t frame = 0; frame < 2; ++frame) {
2439 SCOPED_TRACE(ProduceDebugText(
2440 render_input_sample_rate_hz, render_output_sample_rate_hz,
2441 capture_input_sample_rate_hz, capture_output_sample_rate_hz,
2442 render_input_num_channels, render_output_num_channels,
2443 render_input_num_channels, capture_output_num_channels));
2444
2445 int result = apm->ProcessReverseStream(
2446 &render_input_frame[0], render_input_stream_config,
2447 render_output_stream_config, &render_output_frame[0]);
2448 EXPECT_EQ(result, AudioProcessing::kNoError);
2449 result = apm->ProcessStream(
2450 &capture_input_frame[0], capture_input_stream_config,
2451 capture_output_stream_config, &capture_output_frame[0]);
2452 EXPECT_EQ(result, AudioProcessing::kNoError);
2453 }
2454 }
2455 }
2456 }
2457 }
2458 }
2459 }
2460}
2461
Alessio Bazzica3438a932020-10-14 10:47:502462constexpr void Toggle(bool& b) {
2463 b ^= true;
2464}
2465
Alessio Bazzicac054e782018-04-16 10:10:092466TEST(RuntimeSettingTest, TestDefaultCtor) {
2467 auto s = AudioProcessing::RuntimeSetting();
2468 EXPECT_EQ(AudioProcessing::RuntimeSetting::Type::kNotSpecified, s.type());
2469}
2470
Alessio Bazzicac054e782018-04-16 10:10:092471TEST(RuntimeSettingTest, TestUsageWithSwapQueue) {
2472 SwapQueue<AudioProcessing::RuntimeSetting> q(1);
2473 auto s = AudioProcessing::RuntimeSetting();
2474 ASSERT_TRUE(q.Insert(&s));
2475 ASSERT_TRUE(q.Remove(&s));
2476 EXPECT_EQ(AudioProcessing::RuntimeSetting::Type::kNotSpecified, s.type());
2477}
2478
Sam Zackrisson0beac582017-09-25 10:04:022479TEST(ApmConfiguration, EnablePostProcessing) {
2480 // Verify that apm uses a capture post processing module if one is provided.
Sam Zackrisson0beac582017-09-25 10:04:022481 auto mock_post_processor_ptr =
Mirko Bonadei6a489f22019-04-09 13:11:122482 new ::testing::NiceMock<test::MockCustomProcessing>();
Sam Zackrisson0beac582017-09-25 10:04:022483 auto mock_post_processor =
Alex Loiko5825aa62017-12-18 15:02:402484 std::unique_ptr<CustomProcessing>(mock_post_processor_ptr);
Danil Chapovalov9c21f632024-10-16 06:29:592485 scoped_refptr<AudioProcessing> apm =
Danil Chapovalovdc03d872024-10-24 14:16:472486 BuiltinAudioProcessingBuilder()
Ivo Creusen5ec7e122017-12-22 10:35:592487 .SetCapturePostProcessing(std::move(mock_post_processor))
Danil Chapovalovdc03d872024-10-24 14:16:472488 .Build(CreateEnvironment());
Sam Zackrisson0beac582017-09-25 10:04:022489
Per Åhgren2507f8c2020-03-19 11:33:292490 Int16FrameData audio;
Tommi5f163fc2024-11-19 10:53:512491 audio.SetProperties(AudioProcessing::GetFrameSize(
2492 AudioProcessing::NativeRate::kSampleRate16kHz),
2493 /* num_channels=*/1);
Sam Zackrisson0beac582017-09-25 10:04:022494
Mirko Bonadei6a489f22019-04-09 13:11:122495 EXPECT_CALL(*mock_post_processor_ptr, Process(::testing::_)).Times(1);
Per Åhgren2507f8c2020-03-19 11:33:292496 apm->ProcessStream(audio.data.data(),
Tommi5f163fc2024-11-19 10:53:512497 StreamConfig(audio.sample_rate_hz, audio.num_channels()),
2498 StreamConfig(audio.sample_rate_hz, audio.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:582499 audio.data.data());
Sam Zackrisson0beac582017-09-25 10:04:022500}
2501
Alex Loiko5825aa62017-12-18 15:02:402502TEST(ApmConfiguration, EnablePreProcessing) {
2503 // Verify that apm uses a capture post processing module if one is provided.
Alex Loiko5825aa62017-12-18 15:02:402504 auto mock_pre_processor_ptr =
Mirko Bonadei6a489f22019-04-09 13:11:122505 new ::testing::NiceMock<test::MockCustomProcessing>();
Alex Loiko5825aa62017-12-18 15:02:402506 auto mock_pre_processor =
2507 std::unique_ptr<CustomProcessing>(mock_pre_processor_ptr);
Danil Chapovalov9c21f632024-10-16 06:29:592508 scoped_refptr<AudioProcessing> apm =
Danil Chapovalovdc03d872024-10-24 14:16:472509 BuiltinAudioProcessingBuilder()
Ivo Creusen62337e52018-01-09 13:17:332510 .SetRenderPreProcessing(std::move(mock_pre_processor))
Danil Chapovalovdc03d872024-10-24 14:16:472511 .Build(CreateEnvironment());
Alex Loiko5825aa62017-12-18 15:02:402512
Per Åhgren2507f8c2020-03-19 11:33:292513 Int16FrameData audio;
Tommi5f163fc2024-11-19 10:53:512514 audio.SetProperties(AudioProcessing::GetFrameSize(
2515 AudioProcessing::NativeRate::kSampleRate16kHz),
2516 /* num_channels=*/1);
Alex Loiko5825aa62017-12-18 15:02:402517
Mirko Bonadei6a489f22019-04-09 13:11:122518 EXPECT_CALL(*mock_pre_processor_ptr, Process(::testing::_)).Times(1);
Per Åhgren2507f8c2020-03-19 11:33:292519 apm->ProcessReverseStream(
Tommi5f163fc2024-11-19 10:53:512520 audio.data.data(),
2521 StreamConfig(audio.sample_rate_hz, audio.num_channels()),
2522 StreamConfig(audio.sample_rate_hz, audio.num_channels()),
Per Åhgren2507f8c2020-03-19 11:33:292523 audio.data.data());
Alex Loiko5825aa62017-12-18 15:02:402524}
2525
Valeriia Nemychnikovaf06eb572018-08-29 08:37:092526TEST(ApmConfiguration, EnableCaptureAnalyzer) {
2527 // Verify that apm uses a capture analyzer if one is provided.
2528 auto mock_capture_analyzer_ptr =
Mirko Bonadei6a489f22019-04-09 13:11:122529 new ::testing::NiceMock<test::MockCustomAudioAnalyzer>();
Valeriia Nemychnikovaf06eb572018-08-29 08:37:092530 auto mock_capture_analyzer =
2531 std::unique_ptr<CustomAudioAnalyzer>(mock_capture_analyzer_ptr);
Danil Chapovalov9c21f632024-10-16 06:29:592532 scoped_refptr<AudioProcessing> apm =
Danil Chapovalovdc03d872024-10-24 14:16:472533 BuiltinAudioProcessingBuilder()
Valeriia Nemychnikovaf06eb572018-08-29 08:37:092534 .SetCaptureAnalyzer(std::move(mock_capture_analyzer))
Danil Chapovalovdc03d872024-10-24 14:16:472535 .Build(CreateEnvironment());
Valeriia Nemychnikovaf06eb572018-08-29 08:37:092536
Per Åhgren2507f8c2020-03-19 11:33:292537 Int16FrameData audio;
Tommi5f163fc2024-11-19 10:53:512538 audio.SetProperties(AudioProcessing::GetFrameSize(
2539 AudioProcessing::NativeRate::kSampleRate16kHz),
2540 /* num_channels=*/1);
Valeriia Nemychnikovaf06eb572018-08-29 08:37:092541
Mirko Bonadei6a489f22019-04-09 13:11:122542 EXPECT_CALL(*mock_capture_analyzer_ptr, Analyze(::testing::_)).Times(1);
Per Åhgren2507f8c2020-03-19 11:33:292543 apm->ProcessStream(audio.data.data(),
Tommi5f163fc2024-11-19 10:53:512544 StreamConfig(audio.sample_rate_hz, audio.num_channels()),
2545 StreamConfig(audio.sample_rate_hz, audio.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:582546 audio.data.data());
Valeriia Nemychnikovaf06eb572018-08-29 08:37:092547}
2548
Alex Loiko73ec0192018-05-15 08:52:282549TEST(ApmConfiguration, PreProcessingReceivesRuntimeSettings) {
2550 auto mock_pre_processor_ptr =
Mirko Bonadei6a489f22019-04-09 13:11:122551 new ::testing::NiceMock<test::MockCustomProcessing>();
Alex Loiko73ec0192018-05-15 08:52:282552 auto mock_pre_processor =
2553 std::unique_ptr<CustomProcessing>(mock_pre_processor_ptr);
Danil Chapovalov9c21f632024-10-16 06:29:592554 scoped_refptr<AudioProcessing> apm =
Danil Chapovalovdc03d872024-10-24 14:16:472555 BuiltinAudioProcessingBuilder()
Alex Loiko73ec0192018-05-15 08:52:282556 .SetRenderPreProcessing(std::move(mock_pre_processor))
Danil Chapovalovdc03d872024-10-24 14:16:472557 .Build(CreateEnvironment());
Alex Loiko73ec0192018-05-15 08:52:282558 apm->SetRuntimeSetting(
2559 AudioProcessing::RuntimeSetting::CreateCustomRenderSetting(0));
2560
2561 // RuntimeSettings forwarded during 'Process*Stream' calls.
2562 // Therefore we have to make one such call.
Per Åhgren2507f8c2020-03-19 11:33:292563 Int16FrameData audio;
Tommi5f163fc2024-11-19 10:53:512564 audio.SetProperties(AudioProcessing::GetFrameSize(
2565 AudioProcessing::NativeRate::kSampleRate16kHz),
2566 /* num_channels=*/1);
Alex Loiko73ec0192018-05-15 08:52:282567
Mirko Bonadei6a489f22019-04-09 13:11:122568 EXPECT_CALL(*mock_pre_processor_ptr, SetRuntimeSetting(::testing::_))
2569 .Times(1);
Per Åhgren2507f8c2020-03-19 11:33:292570 apm->ProcessReverseStream(
Tommi5f163fc2024-11-19 10:53:512571 audio.data.data(),
2572 StreamConfig(audio.sample_rate_hz, audio.num_channels()),
2573 StreamConfig(audio.sample_rate_hz, audio.num_channels()),
Per Åhgren2507f8c2020-03-19 11:33:292574 audio.data.data());
Alex Loiko73ec0192018-05-15 08:52:282575}
2576
Danil Chapovalova5d29062024-12-18 12:07:422577class MockEchoControlFactory : public EchoControlFactory {
Gustaf Ullberg002ef282017-10-12 13:13:172578 public:
Danil Chapovalova5d29062024-12-18 12:07:422579 MOCK_METHOD(std::unique_ptr<EchoControl>,
2580 Create,
2581 (const Environment&, int, int, int),
2582 (override));
Gustaf Ullberg002ef282017-10-12 13:13:172583};
2584
2585TEST(ApmConfiguration, EchoControlInjection) {
2586 // Verify that apm uses an injected echo controller if one is provided.
Danil Chapovalova5d29062024-12-18 12:07:422587 auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
2588 EXPECT_CALL(*echo_control_factory, Create(_, _, _, _))
2589 .WillOnce(WithoutArgs([] {
2590 auto ec = std::make_unique<test::MockEchoControl>();
2591 EXPECT_CALL(*ec, AnalyzeRender).Times(1);
2592 EXPECT_CALL(*ec, AnalyzeCapture).Times(2);
2593 EXPECT_CALL(*ec, ProcessCapture(_, _, _)).Times(2);
2594 return ec;
2595 }));
Gustaf Ullberg002ef282017-10-12 13:13:172596
Danil Chapovalov9c21f632024-10-16 06:29:592597 scoped_refptr<AudioProcessing> apm =
Danil Chapovalovdc03d872024-10-24 14:16:472598 BuiltinAudioProcessingBuilder()
Ivo Creusen5ec7e122017-12-22 10:35:592599 .SetEchoControlFactory(std::move(echo_control_factory))
Danil Chapovalovdc03d872024-10-24 14:16:472600 .Build(CreateEnvironment());
Gustaf Ullberg002ef282017-10-12 13:13:172601
Per Åhgren2507f8c2020-03-19 11:33:292602 Int16FrameData audio;
Tommi5f163fc2024-11-19 10:53:512603 audio.SetProperties(AudioProcessing::GetFrameSize(
2604 AudioProcessing::NativeRate::kSampleRate16kHz),
2605 /* num_channels=*/1);
Per Åhgren2507f8c2020-03-19 11:33:292606 apm->ProcessStream(audio.data.data(),
Tommi5f163fc2024-11-19 10:53:512607 StreamConfig(audio.sample_rate_hz, audio.num_channels()),
2608 StreamConfig(audio.sample_rate_hz, audio.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:582609 audio.data.data());
Per Åhgren2507f8c2020-03-19 11:33:292610 apm->ProcessReverseStream(
Tommi5f163fc2024-11-19 10:53:512611 audio.data.data(),
2612 StreamConfig(audio.sample_rate_hz, audio.num_channels()),
2613 StreamConfig(audio.sample_rate_hz, audio.num_channels()),
Per Åhgren2507f8c2020-03-19 11:33:292614 audio.data.data());
2615 apm->ProcessStream(audio.data.data(),
Tommi5f163fc2024-11-19 10:53:512616 StreamConfig(audio.sample_rate_hz, audio.num_channels()),
2617 StreamConfig(audio.sample_rate_hz, audio.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:582618 audio.data.data());
Gustaf Ullberg002ef282017-10-12 13:13:172619}
Ivo Creusenae0260962017-11-20 12:07:162620
Sam Zackrisson03cb7e52021-12-06 14:40:042621TEST(ApmConfiguration, EchoDetectorInjection) {
2622 using ::testing::_;
2623 rtc::scoped_refptr<test::MockEchoDetector> mock_echo_detector =
2624 rtc::make_ref_counted<::testing::StrictMock<test::MockEchoDetector>>();
2625 EXPECT_CALL(*mock_echo_detector,
2626 Initialize(/*capture_sample_rate_hz=*/16000, _,
2627 /*render_sample_rate_hz=*/16000, _))
2628 .Times(1);
Danil Chapovalovdc03d872024-10-24 14:16:472629 scoped_refptr<AudioProcessing> apm = BuiltinAudioProcessingBuilder()
Danil Chapovalov9c21f632024-10-16 06:29:592630 .SetEchoDetector(mock_echo_detector)
Danil Chapovalovdc03d872024-10-24 14:16:472631 .Build(CreateEnvironment());
Sam Zackrisson03cb7e52021-12-06 14:40:042632
2633 // The echo detector is included in processing when enabled.
2634 EXPECT_CALL(*mock_echo_detector, AnalyzeRenderAudio(_))
2635 .WillOnce([](rtc::ArrayView<const float> render_audio) {
2636 EXPECT_EQ(render_audio.size(), 160u);
2637 });
2638 EXPECT_CALL(*mock_echo_detector, AnalyzeCaptureAudio(_))
2639 .WillOnce([](rtc::ArrayView<const float> capture_audio) {
2640 EXPECT_EQ(capture_audio.size(), 160u);
2641 });
2642 EXPECT_CALL(*mock_echo_detector, GetMetrics()).Times(1);
2643
2644 Int16FrameData frame;
Tommi5f163fc2024-11-19 10:53:512645 frame.SetProperties(AudioProcessing::GetFrameSize(
2646 AudioProcessing::NativeRate::kSampleRate16kHz),
2647 /* num_channels=*/1);
Sam Zackrisson03cb7e52021-12-06 14:40:042648
2649 apm->ProcessReverseStream(frame.data.data(), StreamConfig(16000, 1),
2650 StreamConfig(16000, 1), frame.data.data());
2651 apm->ProcessStream(frame.data.data(), StreamConfig(16000, 1),
2652 StreamConfig(16000, 1), frame.data.data());
2653
2654 // When processing rates change, the echo detector is also reinitialized to
2655 // match those.
2656 EXPECT_CALL(*mock_echo_detector,
2657 Initialize(/*capture_sample_rate_hz=*/48000, _,
2658 /*render_sample_rate_hz=*/16000, _))
2659 .Times(1);
2660 EXPECT_CALL(*mock_echo_detector,
2661 Initialize(/*capture_sample_rate_hz=*/48000, _,
2662 /*render_sample_rate_hz=*/48000, _))
2663 .Times(1);
2664 EXPECT_CALL(*mock_echo_detector, AnalyzeRenderAudio(_))
2665 .WillOnce([](rtc::ArrayView<const float> render_audio) {
2666 EXPECT_EQ(render_audio.size(), 480u);
2667 });
2668 EXPECT_CALL(*mock_echo_detector, AnalyzeCaptureAudio(_))
2669 .Times(2)
2670 .WillRepeatedly([](rtc::ArrayView<const float> capture_audio) {
2671 EXPECT_EQ(capture_audio.size(), 480u);
2672 });
2673 EXPECT_CALL(*mock_echo_detector, GetMetrics()).Times(2);
2674
Tommi5f163fc2024-11-19 10:53:512675 frame.SetProperties(AudioProcessing::GetFrameSize(
2676 AudioProcessing::NativeRate::kSampleRate48kHz),
2677 frame.num_channels());
Sam Zackrisson03cb7e52021-12-06 14:40:042678 apm->ProcessStream(frame.data.data(), StreamConfig(48000, 1),
2679 StreamConfig(48000, 1), frame.data.data());
2680 apm->ProcessReverseStream(frame.data.data(), StreamConfig(48000, 1),
2681 StreamConfig(48000, 1), frame.data.data());
2682 apm->ProcessStream(frame.data.data(), StreamConfig(48000, 1),
2683 StreamConfig(48000, 1), frame.data.data());
2684}
2685
2686rtc::scoped_refptr<AudioProcessing> CreateApm(bool mobile_aec) {
2687 // Enable residual echo detection, for stats.
Danil Chapovalov9c21f632024-10-16 06:29:592688 scoped_refptr<AudioProcessing> apm =
Danil Chapovalovdc03d872024-10-24 14:16:472689 BuiltinAudioProcessingBuilder()
Sam Zackrisson03cb7e52021-12-06 14:40:042690 .SetEchoDetector(CreateEchoDetector())
Danil Chapovalovdc03d872024-10-24 14:16:472691 .Build(CreateEnvironment());
Ivo Creusenae0260962017-11-20 12:07:162692 if (!apm) {
2693 return apm;
2694 }
2695
2696 ProcessingConfig processing_config = {
2697 {{32000, 1}, {32000, 1}, {32000, 1}, {32000, 1}}};
2698
2699 if (apm->Initialize(processing_config) != 0) {
2700 return nullptr;
2701 }
2702
Sam Zackrisson03cb7e52021-12-06 14:40:042703 // Disable all components except for an AEC.
Sam Zackrissoncdf0e6d2018-09-17 09:05:172704 AudioProcessing::Config apm_config;
Sam Zackrissoncdf0e6d2018-09-17 09:05:172705 apm_config.high_pass_filter.enabled = false;
Sam Zackrisson41478c72019-10-15 08:10:262706 apm_config.gain_controller1.enabled = false;
Sam Zackrissoncdf0e6d2018-09-17 09:05:172707 apm_config.gain_controller2.enabled = false;
2708 apm_config.echo_canceller.enabled = true;
Per Åhgren8607f842019-04-12 20:02:262709 apm_config.echo_canceller.mobile_mode = mobile_aec;
saza0bad15f2019-10-16 09:46:112710 apm_config.noise_suppression.enabled = false;
Sam Zackrissoncdf0e6d2018-09-17 09:05:172711 apm->ApplyConfig(apm_config);
Ivo Creusenae0260962017-11-20 12:07:162712 return apm;
2713}
2714
2715#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) || defined(WEBRTC_MAC)
2716#define MAYBE_ApmStatistics DISABLED_ApmStatistics
2717#else
2718#define MAYBE_ApmStatistics ApmStatistics
2719#endif
2720
Per Åhgren8607f842019-04-12 20:02:262721TEST(MAYBE_ApmStatistics, AECEnabledTest) {
2722 // Set up APM with AEC3 and process some audio.
Niels Möller4f776ac2021-07-02 09:30:542723 rtc::scoped_refptr<AudioProcessing> apm = CreateApm(false);
Ivo Creusenae0260962017-11-20 12:07:162724 ASSERT_TRUE(apm);
Per Åhgren200feba1c2019-03-06 03:16:462725 AudioProcessing::Config apm_config;
2726 apm_config.echo_canceller.enabled = true;
Per Åhgren200feba1c2019-03-06 03:16:462727 apm->ApplyConfig(apm_config);
Ivo Creusenae0260962017-11-20 12:07:162728
2729 // Set up an audioframe.
Per Åhgren2507f8c2020-03-19 11:33:292730 Int16FrameData frame;
Tommi5f163fc2024-11-19 10:53:512731 frame.SetProperties(AudioProcessing::GetFrameSize(
2732 AudioProcessing::NativeRate::kSampleRate32kHz),
2733 /* num_channels=*/1);
Ivo Creusenae0260962017-11-20 12:07:162734
2735 // Fill the audio frame with a sawtooth pattern.
Per Åhgren2507f8c2020-03-19 11:33:292736 int16_t* ptr = frame.data.data();
Ivo Creusenae0260962017-11-20 12:07:162737 for (size_t i = 0; i < frame.kMaxDataSizeSamples; i++) {
2738 ptr[i] = 10000 * ((i % 3) - 1);
2739 }
2740
2741 // Do some processing.
2742 for (int i = 0; i < 200; i++) {
Per Åhgren2507f8c2020-03-19 11:33:292743 EXPECT_EQ(apm->ProcessReverseStream(
2744 frame.data.data(),
Tommi5f163fc2024-11-19 10:53:512745 StreamConfig(frame.sample_rate_hz, frame.num_channels()),
2746 StreamConfig(frame.sample_rate_hz, frame.num_channels()),
Per Åhgren2507f8c2020-03-19 11:33:292747 frame.data.data()),
2748 0);
Ivo Creusenae0260962017-11-20 12:07:162749 EXPECT_EQ(apm->set_stream_delay_ms(0), 0);
Per Åhgren2507f8c2020-03-19 11:33:292750 EXPECT_EQ(apm->ProcessStream(
2751 frame.data.data(),
Tommi5f163fc2024-11-19 10:53:512752 StreamConfig(frame.sample_rate_hz, frame.num_channels()),
2753 StreamConfig(frame.sample_rate_hz, frame.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:582754 frame.data.data()),
Per Åhgren2507f8c2020-03-19 11:33:292755 0);
Ivo Creusenae0260962017-11-20 12:07:162756 }
2757
2758 // Test statistics interface.
Per Åhgrencf4c8722019-12-30 13:32:142759 AudioProcessingStats stats = apm->GetStatistics();
Ivo Creusenae0260962017-11-20 12:07:162760 // We expect all statistics to be set and have a sensible value.
Sam Zackrisson03cb7e52021-12-06 14:40:042761 ASSERT_TRUE(stats.residual_echo_likelihood.has_value());
Ivo Creusenae0260962017-11-20 12:07:162762 EXPECT_GE(*stats.residual_echo_likelihood, 0.0);
2763 EXPECT_LE(*stats.residual_echo_likelihood, 1.0);
Sam Zackrisson03cb7e52021-12-06 14:40:042764 ASSERT_TRUE(stats.residual_echo_likelihood_recent_max.has_value());
Ivo Creusenae0260962017-11-20 12:07:162765 EXPECT_GE(*stats.residual_echo_likelihood_recent_max, 0.0);
2766 EXPECT_LE(*stats.residual_echo_likelihood_recent_max, 1.0);
Sam Zackrisson03cb7e52021-12-06 14:40:042767 ASSERT_TRUE(stats.echo_return_loss.has_value());
Ivo Creusenae0260962017-11-20 12:07:162768 EXPECT_NE(*stats.echo_return_loss, -100.0);
Sam Zackrisson03cb7e52021-12-06 14:40:042769 ASSERT_TRUE(stats.echo_return_loss_enhancement.has_value());
Ivo Creusenae0260962017-11-20 12:07:162770 EXPECT_NE(*stats.echo_return_loss_enhancement, -100.0);
Ivo Creusenae0260962017-11-20 12:07:162771}
2772
2773TEST(MAYBE_ApmStatistics, AECMEnabledTest) {
2774 // Set up APM with AECM and process some audio.
Niels Möller4f776ac2021-07-02 09:30:542775 rtc::scoped_refptr<AudioProcessing> apm = CreateApm(true);
Ivo Creusenae0260962017-11-20 12:07:162776 ASSERT_TRUE(apm);
2777
2778 // Set up an audioframe.
Per Åhgren2507f8c2020-03-19 11:33:292779 Int16FrameData frame;
Tommi5f163fc2024-11-19 10:53:512780 frame.SetProperties(AudioProcessing::GetFrameSize(
2781 AudioProcessing::NativeRate::kSampleRate32kHz),
2782 /* num_channels=*/1);
Ivo Creusenae0260962017-11-20 12:07:162783
2784 // Fill the audio frame with a sawtooth pattern.
Per Åhgren2507f8c2020-03-19 11:33:292785 int16_t* ptr = frame.data.data();
Ivo Creusenae0260962017-11-20 12:07:162786 for (size_t i = 0; i < frame.kMaxDataSizeSamples; i++) {
2787 ptr[i] = 10000 * ((i % 3) - 1);
2788 }
2789
2790 // Do some processing.
2791 for (int i = 0; i < 200; i++) {
Per Åhgren2507f8c2020-03-19 11:33:292792 EXPECT_EQ(apm->ProcessReverseStream(
2793 frame.data.data(),
Tommi5f163fc2024-11-19 10:53:512794 StreamConfig(frame.sample_rate_hz, frame.num_channels()),
2795 StreamConfig(frame.sample_rate_hz, frame.num_channels()),
Per Åhgren2507f8c2020-03-19 11:33:292796 frame.data.data()),
2797 0);
Ivo Creusenae0260962017-11-20 12:07:162798 EXPECT_EQ(apm->set_stream_delay_ms(0), 0);
Per Åhgren2507f8c2020-03-19 11:33:292799 EXPECT_EQ(apm->ProcessStream(
2800 frame.data.data(),
Tommi5f163fc2024-11-19 10:53:512801 StreamConfig(frame.sample_rate_hz, frame.num_channels()),
2802 StreamConfig(frame.sample_rate_hz, frame.num_channels()),
Per Åhgrendc5522b2020-03-19 13:55:582803 frame.data.data()),
Per Åhgren2507f8c2020-03-19 11:33:292804 0);
Ivo Creusenae0260962017-11-20 12:07:162805 }
2806
2807 // Test statistics interface.
Per Åhgrencf4c8722019-12-30 13:32:142808 AudioProcessingStats stats = apm->GetStatistics();
Ivo Creusenae0260962017-11-20 12:07:162809 // We expect only the residual echo detector statistics to be set and have a
2810 // sensible value.
Sam Zackrisson03cb7e52021-12-06 14:40:042811 ASSERT_TRUE(stats.residual_echo_likelihood.has_value());
2812 EXPECT_GE(*stats.residual_echo_likelihood, 0.0);
2813 EXPECT_LE(*stats.residual_echo_likelihood, 1.0);
2814 ASSERT_TRUE(stats.residual_echo_likelihood_recent_max.has_value());
2815 EXPECT_GE(*stats.residual_echo_likelihood_recent_max, 0.0);
2816 EXPECT_LE(*stats.residual_echo_likelihood_recent_max, 1.0);
2817 EXPECT_FALSE(stats.echo_return_loss.has_value());
2818 EXPECT_FALSE(stats.echo_return_loss_enhancement.has_value());
Ivo Creusenae0260962017-11-20 12:07:162819}
Sam Zackrissonb24c00f2018-11-26 15:18:252820
Alessio Bazzica1db0a262022-02-15 14:18:092821TEST(ApmStatistics, DoNotReportVoiceDetectedStat) {
Sam Zackrisson4db667b2018-12-21 15:29:272822 ProcessingConfig processing_config = {
2823 {{32000, 1}, {32000, 1}, {32000, 1}, {32000, 1}}};
Sam Zackrisson4db667b2018-12-21 15:29:272824
2825 // Set up an audioframe.
Per Åhgren2507f8c2020-03-19 11:33:292826 Int16FrameData frame;
Tommi5f163fc2024-11-19 10:53:512827 frame.SetProperties(AudioProcessing::GetFrameSize(
2828 AudioProcessing::NativeRate::kSampleRate32kHz),
2829 /* num_channels=*/1);
Sam Zackrisson4db667b2018-12-21 15:29:272830
2831 // Fill the audio frame with a sawtooth pattern.
Per Åhgren2507f8c2020-03-19 11:33:292832 int16_t* ptr = frame.data.data();
Sam Zackrisson4db667b2018-12-21 15:29:272833 for (size_t i = 0; i < frame.kMaxDataSizeSamples; i++) {
2834 ptr[i] = 10000 * ((i % 3) - 1);
2835 }
2836
Danil Chapovalov9c21f632024-10-16 06:29:592837 scoped_refptr<AudioProcessing> apm =
Danil Chapovalovdc03d872024-10-24 14:16:472838 BuiltinAudioProcessingBuilder().Build(CreateEnvironment());
Sam Zackrisson4db667b2018-12-21 15:29:272839 apm->Initialize(processing_config);
2840
Alessio Bazzica1db0a262022-02-15 14:18:092841 // No metric should be reported.
Tommi5f163fc2024-11-19 10:53:512842 EXPECT_EQ(apm->ProcessStream(
2843 frame.data.data(),
2844 StreamConfig(frame.sample_rate_hz, frame.num_channels()),
2845 StreamConfig(frame.sample_rate_hz, frame.num_channels()),
2846 frame.data.data()),
2847 0);
Alessio Bazzica1db0a262022-02-15 14:18:092848 EXPECT_FALSE(apm->GetStatistics().voice_detected.has_value());
Sam Zackrisson4db667b2018-12-21 15:29:272849}
Per Åhgren3e8bf282019-08-29 21:38:402850
Sam Zackrisson03cb7e52021-12-06 14:40:042851TEST(ApmStatistics, GetStatisticsReportsNoEchoDetectorStatsWhenDisabled) {
Danil Chapovalov9c21f632024-10-16 06:29:592852 scoped_refptr<AudioProcessing> apm =
Danil Chapovalovdc03d872024-10-24 14:16:472853 BuiltinAudioProcessingBuilder().Build(CreateEnvironment());
Sam Zackrisson03cb7e52021-12-06 14:40:042854 Int16FrameData frame;
Tommi5f163fc2024-11-19 10:53:512855 frame.SetProperties(AudioProcessing::GetFrameSize(
2856 AudioProcessing::NativeRate::kSampleRate32kHz),
2857 /* num_channels=*/1);
2858 ASSERT_EQ(apm->ProcessStream(
2859 frame.data.data(),
2860 StreamConfig(frame.sample_rate_hz, frame.num_channels()),
2861 StreamConfig(frame.sample_rate_hz, frame.num_channels()),
2862 frame.data.data()),
2863 0);
Sam Zackrisson03cb7e52021-12-06 14:40:042864 // Echo detector is disabled by default, no stats reported.
2865 AudioProcessingStats stats = apm->GetStatistics();
2866 EXPECT_FALSE(stats.residual_echo_likelihood.has_value());
2867 EXPECT_FALSE(stats.residual_echo_likelihood_recent_max.has_value());
2868}
2869
2870TEST(ApmStatistics, GetStatisticsReportsEchoDetectorStatsWhenEnabled) {
2871 // Create APM with an echo detector injected.
Danil Chapovalov9c21f632024-10-16 06:29:592872 scoped_refptr<AudioProcessing> apm =
Danil Chapovalovdc03d872024-10-24 14:16:472873 BuiltinAudioProcessingBuilder()
Sam Zackrisson03cb7e52021-12-06 14:40:042874 .SetEchoDetector(CreateEchoDetector())
Danil Chapovalovdc03d872024-10-24 14:16:472875 .Build(CreateEnvironment());
Sam Zackrisson03cb7e52021-12-06 14:40:042876 Int16FrameData frame;
Tommi5f163fc2024-11-19 10:53:512877 frame.SetProperties(AudioProcessing::GetFrameSize(
2878 AudioProcessing::NativeRate::kSampleRate32kHz),
2879 /* num_channels=*/1);
Sam Zackrisson03cb7e52021-12-06 14:40:042880 // Echo detector enabled: Report stats.
Tommi5f163fc2024-11-19 10:53:512881 ASSERT_EQ(apm->ProcessStream(
2882 frame.data.data(),
2883 StreamConfig(frame.sample_rate_hz, frame.num_channels()),
2884 StreamConfig(frame.sample_rate_hz, frame.num_channels()),
2885 frame.data.data()),
2886 0);
Sam Zackrisson03cb7e52021-12-06 14:40:042887 AudioProcessingStats stats = apm->GetStatistics();
2888 EXPECT_TRUE(stats.residual_echo_likelihood.has_value());
2889 EXPECT_TRUE(stats.residual_echo_likelihood_recent_max.has_value());
2890}
2891
Per Åhgren3e8bf282019-08-29 21:38:402892TEST(ApmConfiguration, HandlingOfRateAndChannelCombinations) {
2893 std::array<int, 3> sample_rates_hz = {16000, 32000, 48000};
2894 std::array<int, 2> render_channel_counts = {1, 7};
2895 std::array<int, 2> capture_channel_counts = {1, 7};
2896 RunApmRateAndChannelTest(sample_rates_hz, render_channel_counts,
2897 capture_channel_counts);
2898}
2899
2900TEST(ApmConfiguration, HandlingOfChannelCombinations) {
2901 std::array<int, 1> sample_rates_hz = {48000};
2902 std::array<int, 8> render_channel_counts = {1, 2, 3, 4, 5, 6, 7, 8};
2903 std::array<int, 8> capture_channel_counts = {1, 2, 3, 4, 5, 6, 7, 8};
2904 RunApmRateAndChannelTest(sample_rates_hz, render_channel_counts,
2905 capture_channel_counts);
2906}
2907
2908TEST(ApmConfiguration, HandlingOfRateCombinations) {
Sam Zackrisson3bd444f2022-08-03 12:37:002909 // Test rates <= 96000 logged by Chrome UMA:
2910 // - WebRTC.AudioInputSampleRate
2911 // - WebRTC.AudioOutputSampleRate
2912 // Higher rates are tested in AudioProcessingTest.Format, to keep the number
2913 // of combinations in this test manageable.
2914 std::array<int, 9> sample_rates_hz = {8000, 11025, 16000, 22050, 32000,
2915 44100, 48000, 88200, 96000};
Per Åhgren3e8bf282019-08-29 21:38:402916 std::array<int, 1> render_channel_counts = {2};
2917 std::array<int, 1> capture_channel_counts = {2};
2918 RunApmRateAndChannelTest(sample_rates_hz, render_channel_counts,
2919 capture_channel_counts);
2920}
2921
Yves Gerey1fce3f82019-12-05 16:45:312922TEST(ApmConfiguration, SelfAssignment) {
2923 // At some point memory sanitizer was complaining about self-assigment.
2924 // Make sure we don't regress.
2925 AudioProcessing::Config config;
2926 AudioProcessing::Config* config2 = &config;
2927 *config2 = *config2; // Workaround -Wself-assign-overloaded
2928 SUCCEED(); // Real success is absence of defects from asan/msan/ubsan.
2929}
2930
Alessio Bazzica3438a932020-10-14 10:47:502931TEST(AudioProcessing, GainController1ConfigEqual) {
2932 AudioProcessing::Config::GainController1 a;
2933 AudioProcessing::Config::GainController1 b;
2934 EXPECT_EQ(a, b);
2935
2936 Toggle(a.enabled);
2937 b.enabled = a.enabled;
2938 EXPECT_EQ(a, b);
2939
2940 a.mode = AudioProcessing::Config::GainController1::Mode::kAdaptiveDigital;
2941 b.mode = a.mode;
2942 EXPECT_EQ(a, b);
2943
2944 a.target_level_dbfs++;
2945 b.target_level_dbfs = a.target_level_dbfs;
2946 EXPECT_EQ(a, b);
2947
2948 a.compression_gain_db++;
2949 b.compression_gain_db = a.compression_gain_db;
2950 EXPECT_EQ(a, b);
2951
2952 Toggle(a.enable_limiter);
2953 b.enable_limiter = a.enable_limiter;
2954 EXPECT_EQ(a, b);
2955
Alessio Bazzica3438a932020-10-14 10:47:502956 auto& a_analog = a.analog_gain_controller;
2957 auto& b_analog = b.analog_gain_controller;
2958
2959 Toggle(a_analog.enabled);
2960 b_analog.enabled = a_analog.enabled;
2961 EXPECT_EQ(a, b);
2962
2963 a_analog.startup_min_volume++;
2964 b_analog.startup_min_volume = a_analog.startup_min_volume;
2965 EXPECT_EQ(a, b);
2966
2967 a_analog.clipped_level_min++;
2968 b_analog.clipped_level_min = a_analog.clipped_level_min;
2969 EXPECT_EQ(a, b);
2970
Alessio Bazzica3438a932020-10-14 10:47:502971 Toggle(a_analog.enable_digital_adaptive);
2972 b_analog.enable_digital_adaptive = a_analog.enable_digital_adaptive;
2973 EXPECT_EQ(a, b);
2974}
2975
2976// Checks that one differing parameter is sufficient to make two configs
2977// different.
2978TEST(AudioProcessing, GainController1ConfigNotEqual) {
2979 AudioProcessing::Config::GainController1 a;
2980 const AudioProcessing::Config::GainController1 b;
2981
2982 Toggle(a.enabled);
2983 EXPECT_NE(a, b);
Alessio Bazzicaa2efd152021-04-29 14:17:492984 a = b;
Alessio Bazzica3438a932020-10-14 10:47:502985
2986 a.mode = AudioProcessing::Config::GainController1::Mode::kAdaptiveDigital;
2987 EXPECT_NE(a, b);
Alessio Bazzicaa2efd152021-04-29 14:17:492988 a = b;
Alessio Bazzica3438a932020-10-14 10:47:502989
2990 a.target_level_dbfs++;
2991 EXPECT_NE(a, b);
Alessio Bazzicaa2efd152021-04-29 14:17:492992 a = b;
Alessio Bazzica3438a932020-10-14 10:47:502993
2994 a.compression_gain_db++;
2995 EXPECT_NE(a, b);
Alessio Bazzicaa2efd152021-04-29 14:17:492996 a = b;
Alessio Bazzica3438a932020-10-14 10:47:502997
2998 Toggle(a.enable_limiter);
2999 EXPECT_NE(a, b);
Alessio Bazzicaa2efd152021-04-29 14:17:493000 a = b;
Alessio Bazzica3438a932020-10-14 10:47:503001
Alessio Bazzica3438a932020-10-14 10:47:503002 auto& a_analog = a.analog_gain_controller;
3003 const auto& b_analog = b.analog_gain_controller;
3004
3005 Toggle(a_analog.enabled);
3006 EXPECT_NE(a, b);
Alessio Bazzicaa2efd152021-04-29 14:17:493007 a_analog = b_analog;
Alessio Bazzica3438a932020-10-14 10:47:503008
3009 a_analog.startup_min_volume++;
3010 EXPECT_NE(a, b);
Alessio Bazzicaa2efd152021-04-29 14:17:493011 a_analog = b_analog;
Alessio Bazzica3438a932020-10-14 10:47:503012
3013 a_analog.clipped_level_min++;
3014 EXPECT_NE(a, b);
Alessio Bazzicaa2efd152021-04-29 14:17:493015 a_analog = b_analog;
Alessio Bazzica3438a932020-10-14 10:47:503016
Alessio Bazzica3438a932020-10-14 10:47:503017 Toggle(a_analog.enable_digital_adaptive);
3018 EXPECT_NE(a, b);
Alessio Bazzicaa2efd152021-04-29 14:17:493019 a_analog = b_analog;
Alessio Bazzica3438a932020-10-14 10:47:503020}
3021
3022TEST(AudioProcessing, GainController2ConfigEqual) {
3023 AudioProcessing::Config::GainController2 a;
3024 AudioProcessing::Config::GainController2 b;
3025 EXPECT_EQ(a, b);
3026
3027 Toggle(a.enabled);
3028 b.enabled = a.enabled;
3029 EXPECT_EQ(a, b);
3030
Alessio Bazzicaa2efd152021-04-29 14:17:493031 a.fixed_digital.gain_db += 1.0f;
Alessio Bazzica3438a932020-10-14 10:47:503032 b.fixed_digital.gain_db = a.fixed_digital.gain_db;
3033 EXPECT_EQ(a, b);
3034
3035 auto& a_adaptive = a.adaptive_digital;
3036 auto& b_adaptive = b.adaptive_digital;
3037
3038 Toggle(a_adaptive.enabled);
3039 b_adaptive.enabled = a_adaptive.enabled;
3040 EXPECT_EQ(a, b);
3041
Alessio Bazzicaa850e6c2021-10-04 11:35:553042 a_adaptive.headroom_db += 1.0f;
3043 b_adaptive.headroom_db = a_adaptive.headroom_db;
3044 EXPECT_EQ(a, b);
3045
3046 a_adaptive.max_gain_db += 1.0f;
3047 b_adaptive.max_gain_db = a_adaptive.max_gain_db;
3048 EXPECT_EQ(a, b);
3049
3050 a_adaptive.initial_gain_db += 1.0f;
3051 b_adaptive.initial_gain_db = a_adaptive.initial_gain_db;
3052 EXPECT_EQ(a, b);
3053
Alessio Bazzicaa2efd152021-04-29 14:17:493054 a_adaptive.max_gain_change_db_per_second += 1.0f;
Alessio Bazzica3438a932020-10-14 10:47:503055 b_adaptive.max_gain_change_db_per_second =
3056 a_adaptive.max_gain_change_db_per_second;
3057 EXPECT_EQ(a, b);
3058
Alessio Bazzicaa2efd152021-04-29 14:17:493059 a_adaptive.max_output_noise_level_dbfs += 1.0f;
Alessio Bazzica3438a932020-10-14 10:47:503060 b_adaptive.max_output_noise_level_dbfs =
3061 a_adaptive.max_output_noise_level_dbfs;
3062 EXPECT_EQ(a, b);
3063}
3064
3065// Checks that one differing parameter is sufficient to make two configs
3066// different.
3067TEST(AudioProcessing, GainController2ConfigNotEqual) {
3068 AudioProcessing::Config::GainController2 a;
3069 const AudioProcessing::Config::GainController2 b;
3070
3071 Toggle(a.enabled);
3072 EXPECT_NE(a, b);
Alessio Bazzicaa2efd152021-04-29 14:17:493073 a = b;
Alessio Bazzica3438a932020-10-14 10:47:503074
Alessio Bazzicaa2efd152021-04-29 14:17:493075 a.fixed_digital.gain_db += 1.0f;
Alessio Bazzica3438a932020-10-14 10:47:503076 EXPECT_NE(a, b);
Alessio Bazzicaa2efd152021-04-29 14:17:493077 a.fixed_digital = b.fixed_digital;
Alessio Bazzica3438a932020-10-14 10:47:503078
3079 auto& a_adaptive = a.adaptive_digital;
3080 const auto& b_adaptive = b.adaptive_digital;
3081
3082 Toggle(a_adaptive.enabled);
3083 EXPECT_NE(a, b);
Alessio Bazzicaa2efd152021-04-29 14:17:493084 a_adaptive = b_adaptive;
Alessio Bazzica3438a932020-10-14 10:47:503085
Alessio Bazzicaa850e6c2021-10-04 11:35:553086 a_adaptive.headroom_db += 1.0f;
3087 EXPECT_NE(a, b);
3088 a_adaptive = b_adaptive;
3089
3090 a_adaptive.max_gain_db += 1.0f;
3091 EXPECT_NE(a, b);
3092 a_adaptive = b_adaptive;
3093
3094 a_adaptive.initial_gain_db += 1.0f;
3095 EXPECT_NE(a, b);
3096 a_adaptive = b_adaptive;
3097
Alessio Bazzicaa2efd152021-04-29 14:17:493098 a_adaptive.max_gain_change_db_per_second += 1.0f;
Alessio Bazzica3438a932020-10-14 10:47:503099 EXPECT_NE(a, b);
Alessio Bazzicaa2efd152021-04-29 14:17:493100 a_adaptive = b_adaptive;
Alessio Bazzica3438a932020-10-14 10:47:503101
Alessio Bazzicaa2efd152021-04-29 14:17:493102 a_adaptive.max_output_noise_level_dbfs += 1.0f;
Alessio Bazzica3438a932020-10-14 10:47:503103 EXPECT_NE(a, b);
Alessio Bazzicaa2efd152021-04-29 14:17:493104 a_adaptive = b_adaptive;
Alessio Bazzica3438a932020-10-14 10:47:503105}
3106
Sam Zackrisson5dd54822022-11-17 10:26:583107struct ApmFormatHandlingTestParams {
3108 enum class ExpectedOutput {
3109 kErrorAndUnmodified,
3110 kErrorAndSilence,
3111 kErrorAndCopyOfFirstChannel,
3112 kErrorAndExactCopy,
3113 kNoError
3114 };
3115
3116 StreamConfig input_config;
3117 StreamConfig output_config;
3118 ExpectedOutput expected_output;
3119};
3120
3121class ApmFormatHandlingTest
3122 : public ::testing::TestWithParam<
3123 std::tuple<StreamDirection, ApmFormatHandlingTestParams>> {
3124 public:
3125 ApmFormatHandlingTest()
3126 : stream_direction_(std::get<0>(GetParam())),
3127 test_params_(std::get<1>(GetParam())) {}
3128
3129 protected:
3130 ::testing::Message ProduceDebugMessage() {
3131 return ::testing::Message()
3132 << "input sample_rate_hz="
3133 << test_params_.input_config.sample_rate_hz()
3134 << " num_channels=" << test_params_.input_config.num_channels()
3135 << ", output sample_rate_hz="
3136 << test_params_.output_config.sample_rate_hz()
3137 << " num_channels=" << test_params_.output_config.num_channels()
3138 << ", stream_direction=" << stream_direction_ << ", expected_output="
3139 << static_cast<int>(test_params_.expected_output);
3140 }
3141
3142 StreamDirection stream_direction_;
3143 ApmFormatHandlingTestParams test_params_;
3144};
3145
3146INSTANTIATE_TEST_SUITE_P(
3147 FormatValidation,
3148 ApmFormatHandlingTest,
3149 testing::Combine(
3150 ::testing::Values(kForward, kReverse),
3151 ::testing::Values(
3152 // Test cases with values on the boundary of legal ranges.
3153 ApmFormatHandlingTestParams{
3154 StreamConfig(16000, 1), StreamConfig(8000, 1),
3155 ApmFormatHandlingTestParams::ExpectedOutput::kNoError},
3156 ApmFormatHandlingTestParams{
3157 StreamConfig(8000, 1), StreamConfig(16000, 1),
3158 ApmFormatHandlingTestParams::ExpectedOutput::kNoError},
3159 ApmFormatHandlingTestParams{
3160 StreamConfig(384000, 1), StreamConfig(16000, 1),
3161 ApmFormatHandlingTestParams::ExpectedOutput::kNoError},
3162 ApmFormatHandlingTestParams{
3163 StreamConfig(16000, 1), StreamConfig(384000, 1),
3164 ApmFormatHandlingTestParams::ExpectedOutput::kNoError},
3165 ApmFormatHandlingTestParams{
3166 StreamConfig(16000, 2), StreamConfig(16000, 1),
3167 ApmFormatHandlingTestParams::ExpectedOutput::kNoError},
3168 ApmFormatHandlingTestParams{
3169 StreamConfig(16000, 3), StreamConfig(16000, 3),
3170 ApmFormatHandlingTestParams::ExpectedOutput::kNoError},
3171
Sam Zackrisson06cba442022-11-21 15:32:423172 // Supported but incompatible formats.
3173 ApmFormatHandlingTestParams{
3174 StreamConfig(16000, 3), StreamConfig(16000, 2),
3175 ApmFormatHandlingTestParams::ExpectedOutput::
3176 kErrorAndCopyOfFirstChannel},
3177 ApmFormatHandlingTestParams{
3178 StreamConfig(16000, 3), StreamConfig(16000, 4),
3179 ApmFormatHandlingTestParams::ExpectedOutput::
3180 kErrorAndCopyOfFirstChannel},
3181
Sam Zackrisson5dd54822022-11-17 10:26:583182 // Unsupported format and input / output mismatch.
3183 ApmFormatHandlingTestParams{
3184 StreamConfig(7900, 1), StreamConfig(16000, 1),
3185 ApmFormatHandlingTestParams::ExpectedOutput::kErrorAndSilence},
3186 ApmFormatHandlingTestParams{
3187 StreamConfig(16000, 1), StreamConfig(7900, 1),
3188 ApmFormatHandlingTestParams::ExpectedOutput::kErrorAndSilence},
3189 ApmFormatHandlingTestParams{
3190 StreamConfig(390000, 1), StreamConfig(16000, 1),
3191 ApmFormatHandlingTestParams::ExpectedOutput::kErrorAndSilence},
3192 ApmFormatHandlingTestParams{
3193 StreamConfig(16000, 1), StreamConfig(390000, 1),
3194 ApmFormatHandlingTestParams::ExpectedOutput::kErrorAndSilence},
3195 ApmFormatHandlingTestParams{
3196 StreamConfig(-16000, 1), StreamConfig(16000, 1),
3197 ApmFormatHandlingTestParams::ExpectedOutput::kErrorAndSilence},
3198
3199 // Unsupported format but input / output formats match.
3200 ApmFormatHandlingTestParams{StreamConfig(7900, 1),
3201 StreamConfig(7900, 1),
3202 ApmFormatHandlingTestParams::
3203 ExpectedOutput::kErrorAndExactCopy},
3204 ApmFormatHandlingTestParams{StreamConfig(390000, 1),
3205 StreamConfig(390000, 1),
3206 ApmFormatHandlingTestParams::
3207 ExpectedOutput::kErrorAndExactCopy},
3208
3209 // Unsupported but identical sample rate, channel mismatch.
3210 ApmFormatHandlingTestParams{
3211 StreamConfig(7900, 1), StreamConfig(7900, 2),
3212 ApmFormatHandlingTestParams::ExpectedOutput::
3213 kErrorAndCopyOfFirstChannel},
3214 ApmFormatHandlingTestParams{
3215 StreamConfig(7900, 2), StreamConfig(7900, 1),
3216 ApmFormatHandlingTestParams::ExpectedOutput::
3217 kErrorAndCopyOfFirstChannel},
3218
3219 // Test cases with meaningless output format.
3220 ApmFormatHandlingTestParams{
3221 StreamConfig(16000, 1), StreamConfig(-16000, 1),
3222 ApmFormatHandlingTestParams::ExpectedOutput::
3223 kErrorAndUnmodified},
3224 ApmFormatHandlingTestParams{
3225 StreamConfig(-16000, 1), StreamConfig(-16000, 1),
3226 ApmFormatHandlingTestParams::ExpectedOutput::
3227 kErrorAndUnmodified})));
3228
3229TEST_P(ApmFormatHandlingTest, IntApi) {
3230 SCOPED_TRACE(ProduceDebugMessage());
3231
3232 // Set up input and output data.
3233 const size_t num_input_samples =
3234 test_params_.input_config.num_channels() *
3235 std::abs(test_params_.input_config.sample_rate_hz() / 100);
3236 const size_t num_output_samples =
3237 test_params_.output_config.num_channels() *
3238 std::abs(test_params_.output_config.sample_rate_hz() / 100);
3239 std::vector<int16_t> input_block(num_input_samples);
3240 for (int i = 0; i < static_cast<int>(input_block.size()); ++i) {
3241 input_block[i] = i;
3242 }
3243 std::vector<int16_t> output_block(num_output_samples);
3244 constexpr int kUnlikelyOffset = 37;
3245 for (int i = 0; i < static_cast<int>(output_block.size()); ++i) {
3246 output_block[i] = i - kUnlikelyOffset;
3247 }
3248
3249 // Call APM.
Danil Chapovalov9c21f632024-10-16 06:29:593250 scoped_refptr<AudioProcessing> ap =
Danil Chapovalovdc03d872024-10-24 14:16:473251 BuiltinAudioProcessingBuilder().Build(CreateEnvironment());
Sam Zackrisson5dd54822022-11-17 10:26:583252 int error;
3253 if (stream_direction_ == kForward) {
3254 error = ap->ProcessStream(input_block.data(), test_params_.input_config,
3255 test_params_.output_config, output_block.data());
3256 } else {
3257 error = ap->ProcessReverseStream(
3258 input_block.data(), test_params_.input_config,
3259 test_params_.output_config, output_block.data());
3260 }
3261
3262 // Check output.
3263 switch (test_params_.expected_output) {
3264 case ApmFormatHandlingTestParams::ExpectedOutput::kNoError:
3265 EXPECT_EQ(error, AudioProcessing::kNoError);
3266 break;
3267 case ApmFormatHandlingTestParams::ExpectedOutput::kErrorAndUnmodified:
3268 EXPECT_NE(error, AudioProcessing::kNoError);
3269 for (int i = 0; i < static_cast<int>(output_block.size()); ++i) {
3270 EXPECT_EQ(output_block[i], i - kUnlikelyOffset);
3271 }
3272 break;
3273 case ApmFormatHandlingTestParams::ExpectedOutput::kErrorAndSilence:
3274 EXPECT_NE(error, AudioProcessing::kNoError);
3275 for (int i = 0; i < static_cast<int>(output_block.size()); ++i) {
3276 EXPECT_EQ(output_block[i], 0);
3277 }
3278 break;
3279 case ApmFormatHandlingTestParams::ExpectedOutput::
3280 kErrorAndCopyOfFirstChannel:
3281 EXPECT_NE(error, AudioProcessing::kNoError);
3282 for (size_t ch = 0; ch < test_params_.output_config.num_channels();
3283 ++ch) {
3284 for (size_t i = 0; i < test_params_.output_config.num_frames(); ++i) {
3285 EXPECT_EQ(
3286 output_block[ch + i * test_params_.output_config.num_channels()],
3287 static_cast<int16_t>(i *
3288 test_params_.input_config.num_channels()));
3289 }
3290 }
3291 break;
3292 case ApmFormatHandlingTestParams::ExpectedOutput::kErrorAndExactCopy:
3293 EXPECT_NE(error, AudioProcessing::kNoError);
3294 for (int i = 0; i < static_cast<int>(output_block.size()); ++i) {
3295 EXPECT_EQ(output_block[i], i);
3296 }
3297 break;
3298 }
3299}
3300
3301TEST_P(ApmFormatHandlingTest, FloatApi) {
3302 SCOPED_TRACE(ProduceDebugMessage());
3303
3304 // Set up input and output data.
3305 const size_t input_samples_per_channel =
3306 std::abs(test_params_.input_config.sample_rate_hz()) / 100;
3307 const size_t output_samples_per_channel =
3308 std::abs(test_params_.output_config.sample_rate_hz()) / 100;
3309 const size_t input_num_channels = test_params_.input_config.num_channels();
3310 const size_t output_num_channels = test_params_.output_config.num_channels();
3311 ChannelBuffer<float> input_block(input_samples_per_channel,
3312 input_num_channels);
3313 ChannelBuffer<float> output_block(output_samples_per_channel,
3314 output_num_channels);
3315 for (size_t ch = 0; ch < input_num_channels; ++ch) {
3316 for (size_t i = 0; i < input_samples_per_channel; ++i) {
3317 input_block.channels()[ch][i] = ch + i * input_num_channels;
3318 }
3319 }
3320 constexpr int kUnlikelyOffset = 37;
3321 for (size_t ch = 0; ch < output_num_channels; ++ch) {
3322 for (size_t i = 0; i < output_samples_per_channel; ++i) {
3323 output_block.channels()[ch][i] =
3324 ch + i * output_num_channels - kUnlikelyOffset;
3325 }
3326 }
3327
3328 // Call APM.
Danil Chapovalov9c21f632024-10-16 06:29:593329 scoped_refptr<AudioProcessing> ap =
Danil Chapovalovdc03d872024-10-24 14:16:473330 BuiltinAudioProcessingBuilder().Build(CreateEnvironment());
Sam Zackrisson5dd54822022-11-17 10:26:583331 int error;
3332 if (stream_direction_ == kForward) {
3333 error =
3334 ap->ProcessStream(input_block.channels(), test_params_.input_config,
3335 test_params_.output_config, output_block.channels());
3336 } else {
3337 error = ap->ProcessReverseStream(
3338 input_block.channels(), test_params_.input_config,
3339 test_params_.output_config, output_block.channels());
3340 }
3341
3342 // Check output.
3343 switch (test_params_.expected_output) {
3344 case ApmFormatHandlingTestParams::ExpectedOutput::kNoError:
3345 EXPECT_EQ(error, AudioProcessing::kNoError);
3346 break;
3347 case ApmFormatHandlingTestParams::ExpectedOutput::kErrorAndUnmodified:
3348 EXPECT_NE(error, AudioProcessing::kNoError);
3349 for (size_t ch = 0; ch < output_num_channels; ++ch) {
3350 for (size_t i = 0; i < output_samples_per_channel; ++i) {
3351 EXPECT_EQ(output_block.channels()[ch][i],
3352 ch + i * output_num_channels - kUnlikelyOffset);
3353 }
3354 }
3355 break;
3356 case ApmFormatHandlingTestParams::ExpectedOutput::kErrorAndSilence:
3357 EXPECT_NE(error, AudioProcessing::kNoError);
3358 for (size_t ch = 0; ch < output_num_channels; ++ch) {
3359 for (size_t i = 0; i < output_samples_per_channel; ++i) {
3360 EXPECT_EQ(output_block.channels()[ch][i], 0);
3361 }
3362 }
3363 break;
3364 case ApmFormatHandlingTestParams::ExpectedOutput::
3365 kErrorAndCopyOfFirstChannel:
3366 EXPECT_NE(error, AudioProcessing::kNoError);
3367 for (size_t ch = 0; ch < output_num_channels; ++ch) {
3368 for (size_t i = 0; i < output_samples_per_channel; ++i) {
3369 EXPECT_EQ(output_block.channels()[ch][i],
3370 input_block.channels()[0][i]);
3371 }
3372 }
3373 break;
3374 case ApmFormatHandlingTestParams::ExpectedOutput::kErrorAndExactCopy:
3375 EXPECT_NE(error, AudioProcessing::kNoError);
3376 for (size_t ch = 0; ch < output_num_channels; ++ch) {
3377 for (size_t i = 0; i < output_samples_per_channel; ++i) {
3378 EXPECT_EQ(output_block.channels()[ch][i],
3379 input_block.channels()[ch][i]);
3380 }
3381 }
3382 break;
3383 }
3384}
3385
3386TEST(ApmAnalyzeReverseStreamFormatTest, AnalyzeReverseStream) {
3387 for (auto&& [input_config, expect_error] :
3388 {std::tuple(StreamConfig(16000, 2), /*expect_error=*/false),
3389 std::tuple(StreamConfig(8000, 1), /*expect_error=*/false),
3390 std::tuple(StreamConfig(384000, 1), /*expect_error=*/false),
3391 std::tuple(StreamConfig(7900, 1), /*expect_error=*/true),
3392 std::tuple(StreamConfig(390000, 1), /*expect_error=*/true),
3393 std::tuple(StreamConfig(16000, 0), /*expect_error=*/true),
3394 std::tuple(StreamConfig(-16000, 0), /*expect_error=*/true)}) {
3395 SCOPED_TRACE(::testing::Message()
3396 << "sample_rate_hz=" << input_config.sample_rate_hz()
3397 << " num_channels=" << input_config.num_channels());
3398
3399 // Set up input data.
3400 ChannelBuffer<float> input_block(
3401 std::abs(input_config.sample_rate_hz()) / 100,
3402 input_config.num_channels());
3403
3404 // Call APM.
Danil Chapovalov9c21f632024-10-16 06:29:593405 scoped_refptr<AudioProcessing> ap =
Danil Chapovalovdc03d872024-10-24 14:16:473406 BuiltinAudioProcessingBuilder().Build(CreateEnvironment());
Sam Zackrisson5dd54822022-11-17 10:26:583407 int error = ap->AnalyzeReverseStream(input_block.channels(), input_config);
3408
3409 // Check output.
3410 if (expect_error) {
3411 EXPECT_NE(error, AudioProcessing::kNoError);
3412 } else {
3413 EXPECT_EQ(error, AudioProcessing::kNoError);
3414 }
3415 }
3416}
3417
Danil Chapovalova5d29062024-12-18 12:07:423418} // namespace
andrew@webrtc.org27c69802014-02-18 20:24:563419} // namespace webrtc