audio_processing_impl_unittest.cc (41180B)
1 /* 2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "modules/audio_processing/audio_processing_impl.h" 12 13 #include <algorithm> 14 #include <array> 15 #include <cstddef> 16 #include <cstdint> 17 #include <memory> 18 #include <string> 19 #include <tuple> 20 #include <utility> 21 #include <vector> 22 23 #include "api/array_view.h" 24 #include "api/audio/audio_processing.h" 25 #include "api/audio/builtin_audio_processing_builder.h" 26 #include "api/audio/echo_control.h" 27 #include "api/environment/environment.h" 28 #include "api/environment/environment_factory.h" 29 #include "api/make_ref_counted.h" 30 #include "api/ref_count.h" 31 #include "api/scoped_refptr.h" 32 #include "modules/audio_processing/test/echo_canceller_test_tools.h" 33 #include "modules/audio_processing/test/echo_control_mock.h" 34 #include "modules/audio_processing/test/test_utils.h" 35 #include "rtc_base/random.h" 36 #include "test/gmock.h" 37 #include "test/gtest.h" 38 39 namespace webrtc { 40 namespace { 41 42 using ::testing::Invoke; 43 using ::testing::NotNull; 44 45 class MockInitialize : public AudioProcessingImpl { 46 public: 47 MockInitialize() : AudioProcessingImpl(CreateEnvironment()) {} 48 49 MOCK_METHOD(void, InitializeLocked, (), (override)); 50 void RealInitializeLocked() { 51 AssertLockedForTest(); 52 AudioProcessingImpl::InitializeLocked(); 53 } 54 55 MOCK_METHOD(void, AddRef, (), (const, override)); 56 MOCK_METHOD(RefCountReleaseStatus, Release, (), (const, override)); 57 }; 58 59 // Creates MockEchoControl instances and provides a raw pointer access to 60 // the next created one. The raw pointer is meant to be used with gmock. 61 // Returning a pointer of the next created MockEchoControl instance is necessary 62 // for the following reasons: (i) gmock expectations must be set before any call 63 // occurs, (ii) APM is initialized the first time that 64 // AudioProcessingImpl::ProcessStream() is called and the initialization leads 65 // to the creation of a new EchoControl object. 66 class MockEchoControlFactory : public EchoControlFactory { 67 public: 68 MockEchoControlFactory() : next_mock_(std::make_unique<MockEchoControl>()) {} 69 // Returns a pointer to the next MockEchoControl that this factory creates. 70 MockEchoControl* GetNext() const { return next_mock_.get(); } 71 std::unique_ptr<EchoControl> Create(const Environment& /* env */, 72 int /* sample_rate_hz */, 73 int /* num_render_channels */, 74 int /* num_capture_channels */) override { 75 std::unique_ptr<EchoControl> mock = std::move(next_mock_); 76 next_mock_ = std::make_unique<MockEchoControl>(); 77 return mock; 78 } 79 80 private: 81 std::unique_ptr<MockEchoControl> next_mock_; 82 }; 83 84 // Mocks EchoDetector and records the first samples of the last analyzed render 85 // stream frame. Used to check what data is read by an EchoDetector 86 // implementation injected into an APM. 87 class TestEchoDetector : public EchoDetector { 88 public: 89 TestEchoDetector() 90 : analyze_render_audio_called_(false), 91 last_render_audio_first_sample_(0.f) {} 92 ~TestEchoDetector() override = default; 93 void AnalyzeRenderAudio(ArrayView<const float> render_audio) override { 94 last_render_audio_first_sample_ = render_audio[0]; 95 analyze_render_audio_called_ = true; 96 } 97 void AnalyzeCaptureAudio( 98 ArrayView<const float> /* capture_audio */) override {} 99 void Initialize(int /* capture_sample_rate_hz */, 100 int /* num_capture_channels */, 101 int /* render_sample_rate_hz */, 102 int /* num_render_channels */) override {} 103 EchoDetector::Metrics GetMetrics() const override { return {}; } 104 // Returns true if AnalyzeRenderAudio() has been called at least once. 105 bool analyze_render_audio_called() const { 106 return analyze_render_audio_called_; 107 } 108 // Returns the first sample of the last analyzed render frame. 109 float last_render_audio_first_sample() const { 110 return last_render_audio_first_sample_; 111 } 112 113 private: 114 bool analyze_render_audio_called_; 115 float last_render_audio_first_sample_; 116 }; 117 118 // Mocks CustomProcessing and applies ProcessSample() to all the samples. 119 // Meant to be injected into an APM to modify samples in a known and detectable 120 // way. 121 class TestRenderPreProcessor : public CustomProcessing { 122 public: 123 TestRenderPreProcessor() = default; 124 ~TestRenderPreProcessor() override = default; 125 void Initialize(int /* sample_rate_hz */, int /* num_channels */) override {} 126 void Process(AudioBuffer* audio) override { 127 for (size_t k = 0; k < audio->num_channels(); ++k) { 128 ArrayView<float> channel_view(audio->channels()[k], audio->num_frames()); 129 std::transform(channel_view.begin(), channel_view.end(), 130 channel_view.begin(), ProcessSample); 131 } 132 } 133 std::string ToString() const override { return "TestRenderPreProcessor"; } 134 void SetRuntimeSetting( 135 AudioProcessing::RuntimeSetting /* setting */) override {} 136 // Modifies a sample. This member is used in Process() to modify a frame and 137 // it is publicly visible to enable tests. 138 static constexpr float ProcessSample(float x) { return 2.f * x; } 139 }; 140 141 // Runs `apm` input processing for volume adjustments for `num_frames` random 142 // frames starting from the volume `initial_volume`. This includes three steps: 143 // 1) Set the input volume 2) Process the stream 3) Set the new recommended 144 // input volume. Returns the new recommended input volume. 145 int ProcessInputVolume(AudioProcessing& apm, 146 int num_frames, 147 int initial_volume) { 148 constexpr int kSampleRateHz = 48000; 149 constexpr int kNumChannels = 1; 150 std::array<float, kSampleRateHz / 100> buffer; 151 float* channel_pointers[] = {buffer.data()}; 152 StreamConfig stream_config(/*sample_rate_hz=*/kSampleRateHz, 153 /*num_channels=*/kNumChannels); 154 int recommended_input_volume = initial_volume; 155 for (int i = 0; i < num_frames; ++i) { 156 Random random_generator(2341U); 157 RandomizeSampleVector(&random_generator, buffer); 158 159 apm.set_stream_analog_level(recommended_input_volume); 160 apm.ProcessStream(channel_pointers, stream_config, stream_config, 161 channel_pointers); 162 recommended_input_volume = apm.recommended_stream_analog_level(); 163 } 164 return recommended_input_volume; 165 } 166 167 } // namespace 168 169 TEST(AudioProcessingImplTest, AudioParameterChangeTriggersInit) { 170 MockInitialize mock; 171 ON_CALL(mock, InitializeLocked) 172 .WillByDefault(Invoke(&mock, &MockInitialize::RealInitializeLocked)); 173 174 EXPECT_CALL(mock, InitializeLocked).Times(1); 175 mock.Initialize(); 176 177 constexpr size_t kMaxTestedSampleRateHz = 32000; 178 constexpr size_t kMaxTestedNumChannels = 2; 179 std::array<int16_t, kMaxTestedNumChannels * kMaxTestedSampleRateHz / 100> 180 frame; 181 frame.fill(0); 182 StreamConfig config(16000, 1); 183 // Call with the default parameters; there should be an init. 184 EXPECT_CALL(mock, InitializeLocked).Times(0); 185 EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data())); 186 EXPECT_NOERR( 187 mock.ProcessReverseStream(frame.data(), config, config, frame.data())); 188 189 // New sample rate. (Only impacts ProcessStream). 190 config = StreamConfig(32000, 1); 191 EXPECT_CALL(mock, InitializeLocked).Times(1); 192 EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data())); 193 194 // New number of channels. 195 config = StreamConfig(32000, 2); 196 EXPECT_CALL(mock, InitializeLocked).Times(2); 197 EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data())); 198 EXPECT_NOERR( 199 mock.ProcessReverseStream(frame.data(), config, config, frame.data())); 200 201 // A new sample rate passed to ProcessReverseStream should cause an init. 202 config = StreamConfig(16000, 2); 203 EXPECT_CALL(mock, InitializeLocked).Times(1); 204 EXPECT_NOERR( 205 mock.ProcessReverseStream(frame.data(), config, config, frame.data())); 206 } 207 208 TEST(AudioProcessingImplTest, UpdateCapturePreGainRuntimeSetting) { 209 scoped_refptr<AudioProcessing> apm = 210 BuiltinAudioProcessingBuilder().Build(CreateEnvironment()); 211 AudioProcessing::Config apm_config; 212 apm_config.pre_amplifier.enabled = true; 213 apm_config.pre_amplifier.fixed_gain_factor = 1.f; 214 apm->ApplyConfig(apm_config); 215 216 constexpr int kSampleRateHz = 48000; 217 constexpr int16_t kAudioLevel = 10000; 218 constexpr size_t kNumChannels = 2; 219 220 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame; 221 StreamConfig config(kSampleRateHz, kNumChannels); 222 frame.fill(kAudioLevel); 223 apm->ProcessStream(frame.data(), config, config, frame.data()); 224 EXPECT_EQ(frame[100], kAudioLevel) 225 << "With factor 1, frame shouldn't be modified."; 226 227 constexpr float kGainFactor = 2.f; 228 apm->SetRuntimeSetting( 229 AudioProcessing::RuntimeSetting::CreateCapturePreGain(kGainFactor)); 230 231 // Process for two frames to have time to ramp up gain. 232 for (int i = 0; i < 2; ++i) { 233 frame.fill(kAudioLevel); 234 apm->ProcessStream(frame.data(), config, config, frame.data()); 235 } 236 EXPECT_EQ(frame[100], kGainFactor * kAudioLevel) 237 << "Frame should be amplified."; 238 } 239 240 TEST(AudioProcessingImplTest, 241 LevelAdjustmentUpdateCapturePreGainRuntimeSetting) { 242 scoped_refptr<AudioProcessing> apm = 243 BuiltinAudioProcessingBuilder().Build(CreateEnvironment()); 244 AudioProcessing::Config apm_config; 245 apm_config.capture_level_adjustment.enabled = true; 246 apm_config.capture_level_adjustment.pre_gain_factor = 1.f; 247 apm->ApplyConfig(apm_config); 248 249 constexpr int kSampleRateHz = 48000; 250 constexpr int16_t kAudioLevel = 10000; 251 constexpr size_t kNumChannels = 2; 252 253 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame; 254 StreamConfig config(kSampleRateHz, kNumChannels); 255 frame.fill(kAudioLevel); 256 apm->ProcessStream(frame.data(), config, config, frame.data()); 257 EXPECT_EQ(frame[100], kAudioLevel) 258 << "With factor 1, frame shouldn't be modified."; 259 260 constexpr float kGainFactor = 2.f; 261 apm->SetRuntimeSetting( 262 AudioProcessing::RuntimeSetting::CreateCapturePreGain(kGainFactor)); 263 264 // Process for two frames to have time to ramp up gain. 265 for (int i = 0; i < 2; ++i) { 266 frame.fill(kAudioLevel); 267 apm->ProcessStream(frame.data(), config, config, frame.data()); 268 } 269 EXPECT_EQ(frame[100], kGainFactor * kAudioLevel) 270 << "Frame should be amplified."; 271 } 272 273 TEST(AudioProcessingImplTest, 274 LevelAdjustmentUpdateCapturePostGainRuntimeSetting) { 275 scoped_refptr<AudioProcessing> apm = 276 BuiltinAudioProcessingBuilder().Build(CreateEnvironment()); 277 AudioProcessing::Config apm_config; 278 apm_config.capture_level_adjustment.enabled = true; 279 apm_config.capture_level_adjustment.post_gain_factor = 1.f; 280 apm->ApplyConfig(apm_config); 281 282 constexpr int kSampleRateHz = 48000; 283 constexpr int16_t kAudioLevel = 10000; 284 constexpr size_t kNumChannels = 2; 285 286 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame; 287 StreamConfig config(kSampleRateHz, kNumChannels); 288 frame.fill(kAudioLevel); 289 apm->ProcessStream(frame.data(), config, config, frame.data()); 290 EXPECT_EQ(frame[100], kAudioLevel) 291 << "With factor 1, frame shouldn't be modified."; 292 293 constexpr float kGainFactor = 2.f; 294 apm->SetRuntimeSetting( 295 AudioProcessing::RuntimeSetting::CreateCapturePostGain(kGainFactor)); 296 297 // Process for two frames to have time to ramp up gain. 298 for (int i = 0; i < 2; ++i) { 299 frame.fill(kAudioLevel); 300 apm->ProcessStream(frame.data(), config, config, frame.data()); 301 } 302 EXPECT_EQ(frame[100], kGainFactor * kAudioLevel) 303 << "Frame should be amplified."; 304 } 305 306 TEST(AudioProcessingImplTest, EchoControllerObservesSetCaptureUsageChange) { 307 // Tests that the echo controller observes that the capture usage has been 308 // updated. 309 auto echo_control_factory = std::make_unique<MockEchoControlFactory>(); 310 const MockEchoControlFactory* echo_control_factory_ptr = 311 echo_control_factory.get(); 312 313 scoped_refptr<AudioProcessing> apm = 314 BuiltinAudioProcessingBuilder() 315 .SetEchoControlFactory(std::move(echo_control_factory)) 316 .Build(CreateEnvironment()); 317 318 constexpr int16_t kAudioLevel = 10000; 319 constexpr int kSampleRateHz = 48000; 320 constexpr int kNumChannels = 2; 321 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame; 322 StreamConfig config(kSampleRateHz, kNumChannels); 323 frame.fill(kAudioLevel); 324 325 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext(); 326 327 // Ensure that SetCaptureOutputUsage is not called when no runtime settings 328 // are passed. 329 EXPECT_CALL(*echo_control_mock, SetCaptureOutputUsage(testing::_)).Times(0); 330 apm->ProcessStream(frame.data(), config, config, frame.data()); 331 332 // Ensure that SetCaptureOutputUsage is called with the right information when 333 // a runtime setting is passed. 334 EXPECT_CALL(*echo_control_mock, 335 SetCaptureOutputUsage(/*capture_output_used=*/false)) 336 .Times(1); 337 EXPECT_TRUE(apm->PostRuntimeSetting( 338 AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting( 339 /*capture_output_used=*/false))); 340 apm->ProcessStream(frame.data(), config, config, frame.data()); 341 342 EXPECT_CALL(*echo_control_mock, 343 SetCaptureOutputUsage(/*capture_output_used=*/true)) 344 .Times(1); 345 EXPECT_TRUE(apm->PostRuntimeSetting( 346 AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting( 347 /*capture_output_used=*/true))); 348 apm->ProcessStream(frame.data(), config, config, frame.data()); 349 350 // The number of positions to place items in the queue is equal to the queue 351 // size minus 1. 352 constexpr int kNumSlotsInQueue = RuntimeSettingQueueSize(); 353 354 // Ensure that SetCaptureOutputUsage is called with the right information when 355 // many runtime settings are passed. 356 for (int k = 0; k < kNumSlotsInQueue - 1; ++k) { 357 EXPECT_TRUE(apm->PostRuntimeSetting( 358 AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting( 359 /*capture_output_used=*/false))); 360 } 361 EXPECT_CALL(*echo_control_mock, 362 SetCaptureOutputUsage(/*capture_output_used=*/false)) 363 .Times(kNumSlotsInQueue - 1); 364 apm->ProcessStream(frame.data(), config, config, frame.data()); 365 366 // Ensure that SetCaptureOutputUsage is properly called with the fallback 367 // value when the runtime settings queue becomes full. 368 for (int k = 0; k < kNumSlotsInQueue; ++k) { 369 EXPECT_TRUE(apm->PostRuntimeSetting( 370 AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting( 371 /*capture_output_used=*/false))); 372 } 373 EXPECT_FALSE(apm->PostRuntimeSetting( 374 AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting( 375 /*capture_output_used=*/false))); 376 EXPECT_FALSE(apm->PostRuntimeSetting( 377 AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting( 378 /*capture_output_used=*/false))); 379 EXPECT_CALL(*echo_control_mock, 380 SetCaptureOutputUsage(/*capture_output_used=*/false)) 381 .Times(kNumSlotsInQueue); 382 EXPECT_CALL(*echo_control_mock, 383 SetCaptureOutputUsage(/*capture_output_used=*/true)) 384 .Times(1); 385 apm->ProcessStream(frame.data(), config, config, frame.data()); 386 } 387 388 TEST(AudioProcessingImplTest, 389 EchoControllerObservesPreAmplifierEchoPathGainChange) { 390 // Tests that the echo controller observes an echo path gain change when the 391 // pre-amplifier submodule changes the gain. 392 auto echo_control_factory = std::make_unique<MockEchoControlFactory>(); 393 const auto* echo_control_factory_ptr = echo_control_factory.get(); 394 395 scoped_refptr<AudioProcessing> apm = 396 BuiltinAudioProcessingBuilder() 397 .SetEchoControlFactory(std::move(echo_control_factory)) 398 .Build(CreateEnvironment()); 399 // Disable AGC. 400 AudioProcessing::Config apm_config; 401 apm_config.gain_controller1.enabled = false; 402 apm_config.gain_controller2.enabled = false; 403 apm_config.pre_amplifier.enabled = true; 404 apm_config.pre_amplifier.fixed_gain_factor = 1.f; 405 apm->ApplyConfig(apm_config); 406 407 constexpr int16_t kAudioLevel = 10000; 408 constexpr size_t kSampleRateHz = 48000; 409 constexpr size_t kNumChannels = 2; 410 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame; 411 StreamConfig config(kSampleRateHz, kNumChannels); 412 frame.fill(kAudioLevel); 413 414 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext(); 415 416 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1); 417 EXPECT_CALL(*echo_control_mock, 418 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false)) 419 .Times(1); 420 apm->ProcessStream(frame.data(), config, config, frame.data()); 421 422 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1); 423 EXPECT_CALL(*echo_control_mock, 424 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true)) 425 .Times(1); 426 apm->SetRuntimeSetting( 427 AudioProcessing::RuntimeSetting::CreateCapturePreGain(2.f)); 428 apm->ProcessStream(frame.data(), config, config, frame.data()); 429 } 430 431 TEST(AudioProcessingImplTest, 432 EchoControllerObservesLevelAdjustmentPreGainEchoPathGainChange) { 433 // Tests that the echo controller observes an echo path gain change when the 434 // pre-amplifier submodule changes the gain. 435 auto echo_control_factory = std::make_unique<MockEchoControlFactory>(); 436 const auto* echo_control_factory_ptr = echo_control_factory.get(); 437 438 scoped_refptr<AudioProcessing> apm = 439 BuiltinAudioProcessingBuilder() 440 .SetEchoControlFactory(std::move(echo_control_factory)) 441 .Build(CreateEnvironment()); 442 // Disable AGC. 443 AudioProcessing::Config apm_config; 444 apm_config.gain_controller1.enabled = false; 445 apm_config.gain_controller2.enabled = false; 446 apm_config.capture_level_adjustment.enabled = true; 447 apm_config.capture_level_adjustment.pre_gain_factor = 1.f; 448 apm->ApplyConfig(apm_config); 449 450 constexpr int16_t kAudioLevel = 10000; 451 constexpr size_t kSampleRateHz = 48000; 452 constexpr size_t kNumChannels = 2; 453 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame; 454 StreamConfig config(kSampleRateHz, kNumChannels); 455 frame.fill(kAudioLevel); 456 457 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext(); 458 459 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1); 460 EXPECT_CALL(*echo_control_mock, 461 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false)) 462 .Times(1); 463 apm->ProcessStream(frame.data(), config, config, frame.data()); 464 465 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1); 466 EXPECT_CALL(*echo_control_mock, 467 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true)) 468 .Times(1); 469 apm->SetRuntimeSetting( 470 AudioProcessing::RuntimeSetting::CreateCapturePreGain(2.f)); 471 apm->ProcessStream(frame.data(), config, config, frame.data()); 472 } 473 474 TEST(AudioProcessingImplTest, 475 EchoControllerObservesAnalogAgc1EchoPathGainChange) { 476 // Tests that the echo controller observes an echo path gain change when the 477 // AGC1 analog adaptive submodule changes the analog gain. 478 auto echo_control_factory = std::make_unique<MockEchoControlFactory>(); 479 const auto* echo_control_factory_ptr = echo_control_factory.get(); 480 481 scoped_refptr<AudioProcessing> apm = 482 BuiltinAudioProcessingBuilder() 483 .SetEchoControlFactory(std::move(echo_control_factory)) 484 .Build(CreateEnvironment()); 485 AudioProcessing::Config apm_config; 486 // Enable AGC1. 487 apm_config.gain_controller1.enabled = true; 488 apm_config.gain_controller1.analog_gain_controller.enabled = true; 489 apm_config.gain_controller2.enabled = false; 490 apm_config.pre_amplifier.enabled = false; 491 apm->ApplyConfig(apm_config); 492 493 constexpr int16_t kAudioLevel = 1000; 494 constexpr size_t kSampleRateHz = 48000; 495 constexpr size_t kNumChannels = 2; 496 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame; 497 StreamConfig stream_config(kSampleRateHz, kNumChannels); 498 frame.fill(kAudioLevel); 499 500 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext(); 501 502 constexpr int kInitialStreamAnalogLevel = 123; 503 apm->set_stream_analog_level(kInitialStreamAnalogLevel); 504 505 // When the first fame is processed, no echo path gain change must be 506 // detected. 507 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1); 508 EXPECT_CALL(*echo_control_mock, 509 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false)) 510 .Times(1); 511 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data()); 512 513 // Simulate the application of the recommended analog level. 514 int recommended_analog_level = apm->recommended_stream_analog_level(); 515 if (recommended_analog_level == kInitialStreamAnalogLevel) { 516 // Force an analog gain change if it did not happen. 517 recommended_analog_level++; 518 } 519 apm->set_stream_analog_level(recommended_analog_level); 520 521 // After the first fame and with a stream analog level change, the echo path 522 // gain change must be detected. 523 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1); 524 EXPECT_CALL(*echo_control_mock, 525 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true)) 526 .Times(1); 527 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data()); 528 } 529 530 TEST(AudioProcessingImplTest, EchoControllerObservesPlayoutVolumeChange) { 531 // Tests that the echo controller observes an echo path gain change when a 532 // playout volume change is reported. 533 auto echo_control_factory = std::make_unique<MockEchoControlFactory>(); 534 const auto* echo_control_factory_ptr = echo_control_factory.get(); 535 536 scoped_refptr<AudioProcessing> apm = 537 BuiltinAudioProcessingBuilder() 538 .SetEchoControlFactory(std::move(echo_control_factory)) 539 .Build(CreateEnvironment()); 540 // Disable AGC. 541 AudioProcessing::Config apm_config; 542 apm_config.gain_controller1.enabled = false; 543 apm_config.gain_controller2.enabled = false; 544 apm->ApplyConfig(apm_config); 545 546 constexpr int16_t kAudioLevel = 10000; 547 constexpr size_t kSampleRateHz = 48000; 548 constexpr size_t kNumChannels = 2; 549 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame; 550 StreamConfig stream_config(kSampleRateHz, kNumChannels); 551 frame.fill(kAudioLevel); 552 553 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext(); 554 555 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1); 556 EXPECT_CALL(*echo_control_mock, 557 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false)) 558 .Times(1); 559 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data()); 560 561 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1); 562 EXPECT_CALL(*echo_control_mock, 563 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false)) 564 .Times(1); 565 apm->SetRuntimeSetting( 566 AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50)); 567 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data()); 568 569 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1); 570 EXPECT_CALL(*echo_control_mock, 571 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false)) 572 .Times(1); 573 apm->SetRuntimeSetting( 574 AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50)); 575 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data()); 576 577 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1); 578 EXPECT_CALL(*echo_control_mock, 579 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true)) 580 .Times(1); 581 apm->SetRuntimeSetting( 582 AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(100)); 583 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data()); 584 } 585 586 TEST(AudioProcessingImplTest, RenderPreProcessorBeforeEchoDetector) { 587 // Make sure that signal changes caused by a render pre-processing sub-module 588 // take place before any echo detector analysis. 589 auto test_echo_detector = make_ref_counted<TestEchoDetector>(); 590 std::unique_ptr<CustomProcessing> test_render_pre_processor( 591 new TestRenderPreProcessor()); 592 // Create APM injecting the test echo detector and render pre-processor. 593 scoped_refptr<AudioProcessing> apm = 594 BuiltinAudioProcessingBuilder() 595 .SetEchoDetector(test_echo_detector) 596 .SetRenderPreProcessing(std::move(test_render_pre_processor)) 597 .Build(CreateEnvironment()); 598 AudioProcessing::Config apm_config; 599 apm_config.pre_amplifier.enabled = true; 600 apm->ApplyConfig(apm_config); 601 602 constexpr int16_t kAudioLevel = 1000; 603 constexpr int kSampleRateHz = 16000; 604 constexpr size_t kNumChannels = 1; 605 // Explicitly initialize APM to ensure no render frames are discarded. 606 const ProcessingConfig processing_config = {{ 607 {kSampleRateHz, kNumChannels}, 608 {kSampleRateHz, kNumChannels}, 609 {kSampleRateHz, kNumChannels}, 610 {kSampleRateHz, kNumChannels}, 611 }}; 612 apm->Initialize(processing_config); 613 614 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame; 615 StreamConfig stream_config(kSampleRateHz, kNumChannels); 616 617 constexpr float kAudioLevelFloat = static_cast<float>(kAudioLevel); 618 constexpr float kExpectedPreprocessedAudioLevel = 619 TestRenderPreProcessor::ProcessSample(kAudioLevelFloat); 620 ASSERT_NE(kAudioLevelFloat, kExpectedPreprocessedAudioLevel); 621 622 // Analyze a render stream frame. 623 frame.fill(kAudioLevel); 624 ASSERT_EQ(AudioProcessing::Error::kNoError, 625 apm->ProcessReverseStream(frame.data(), stream_config, 626 stream_config, frame.data())); 627 // Trigger a call to in EchoDetector::AnalyzeRenderAudio() via 628 // ProcessStream(). 629 frame.fill(kAudioLevel); 630 ASSERT_EQ(AudioProcessing::Error::kNoError, 631 apm->ProcessStream(frame.data(), stream_config, stream_config, 632 frame.data())); 633 // Regardless of how the call to in EchoDetector::AnalyzeRenderAudio() is 634 // triggered, the line below checks that the call has occurred. If not, the 635 // APM implementation may have changed and this test might need to be adapted. 636 ASSERT_TRUE(test_echo_detector->analyze_render_audio_called()); 637 // Check that the data read in EchoDetector::AnalyzeRenderAudio() is that 638 // produced by the render pre-processor. 639 EXPECT_EQ(kExpectedPreprocessedAudioLevel, 640 test_echo_detector->last_render_audio_first_sample()); 641 } 642 643 class StartupInputVolumeParameterizedTest 644 : public ::testing::TestWithParam<int> {}; 645 646 // Tests that, when no input volume controller is used, the startup input volume 647 // is never modified. 648 TEST_P(StartupInputVolumeParameterizedTest, 649 WithNoInputVolumeControllerStartupVolumeNotModified) { 650 AudioProcessing::Config config; 651 config.gain_controller1.enabled = false; 652 config.gain_controller2.enabled = false; 653 auto apm = BuiltinAudioProcessingBuilder(config).Build(CreateEnvironment()); 654 655 int startup_volume = GetParam(); 656 int recommended_volume = ProcessInputVolume( 657 *apm, /*num_frames=*/1, /*initial_volume=*/startup_volume); 658 EXPECT_EQ(recommended_volume, startup_volume); 659 } 660 661 INSTANTIATE_TEST_SUITE_P(AudioProcessingImplTest, 662 StartupInputVolumeParameterizedTest, 663 ::testing::Values(0, 5, 15, 50, 100)); 664 665 // Tests that, when no input volume controller is used, the recommended input 666 // volume always matches the applied one. 667 TEST(AudioProcessingImplTest, 668 WithNoInputVolumeControllerAppliedAndRecommendedVolumesMatch) { 669 AudioProcessing::Config config; 670 config.gain_controller1.enabled = false; 671 config.gain_controller2.enabled = false; 672 auto apm = BuiltinAudioProcessingBuilder(config).Build(CreateEnvironment()); 673 674 Random rand_gen(42); 675 for (int i = 0; i < 32; ++i) { 676 SCOPED_TRACE(i); 677 int32_t applied_volume = rand_gen.Rand(/*low=*/0, /*high=*/255); 678 int recommended_volume = 679 ProcessInputVolume(*apm, /*num_frames=*/1, applied_volume); 680 EXPECT_EQ(recommended_volume, applied_volume); 681 } 682 } 683 684 class ApmInputVolumeControllerParametrizedTest 685 : public ::testing::TestWithParam< 686 std::tuple<int, int, AudioProcessing::Config>> { 687 protected: 688 ApmInputVolumeControllerParametrizedTest() 689 : sample_rate_hz_(std::get<0>(GetParam())), 690 num_channels_(std::get<1>(GetParam())), 691 channels_(num_channels_), 692 channel_pointers_(num_channels_) { 693 const int frame_size = sample_rate_hz_ / 100; 694 for (int c = 0; c < num_channels_; ++c) { 695 channels_[c].resize(frame_size); 696 channel_pointers_[c] = channels_[c].data(); 697 std::fill(channels_[c].begin(), channels_[c].end(), 0.0f); 698 } 699 } 700 701 int sample_rate_hz() const { return sample_rate_hz_; } 702 int num_channels() const { return num_channels_; } 703 AudioProcessing::Config GetConfig() const { return std::get<2>(GetParam()); } 704 705 float* const* channel_pointers() { return channel_pointers_.data(); } 706 707 private: 708 const int sample_rate_hz_; 709 const int num_channels_; 710 std::vector<std::vector<float>> channels_; 711 std::vector<float*> channel_pointers_; 712 }; 713 714 TEST_P(ApmInputVolumeControllerParametrizedTest, 715 EnforceMinInputVolumeAtStartupWithZeroVolume) { 716 const StreamConfig stream_config(sample_rate_hz(), num_channels()); 717 auto apm = 718 BuiltinAudioProcessingBuilder(GetConfig()).Build(CreateEnvironment()); 719 720 apm->set_stream_analog_level(0); 721 apm->ProcessStream(channel_pointers(), stream_config, stream_config, 722 channel_pointers()); 723 EXPECT_GT(apm->recommended_stream_analog_level(), 0); 724 } 725 726 TEST_P(ApmInputVolumeControllerParametrizedTest, 727 EnforceMinInputVolumeAtStartupWithNonZeroVolume) { 728 const StreamConfig stream_config(sample_rate_hz(), num_channels()); 729 auto apm = 730 BuiltinAudioProcessingBuilder(GetConfig()).Build(CreateEnvironment()); 731 732 constexpr int kStartupVolume = 3; 733 apm->set_stream_analog_level(kStartupVolume); 734 apm->ProcessStream(channel_pointers(), stream_config, stream_config, 735 channel_pointers()); 736 EXPECT_GT(apm->recommended_stream_analog_level(), kStartupVolume); 737 } 738 739 TEST_P(ApmInputVolumeControllerParametrizedTest, 740 EnforceMinInputVolumeAfterManualVolumeAdjustment) { 741 const auto config = GetConfig(); 742 if (config.gain_controller1.enabled) { 743 // After a downward manual adjustment, AGC1 slowly converges to the minimum 744 // input volume. 745 GTEST_SKIP() << "Does not apply to AGC1"; 746 } 747 const StreamConfig stream_config(sample_rate_hz(), num_channels()); 748 auto apm = 749 BuiltinAudioProcessingBuilder(GetConfig()).Build(CreateEnvironment()); 750 751 apm->set_stream_analog_level(20); 752 apm->ProcessStream(channel_pointers(), stream_config, stream_config, 753 channel_pointers()); 754 constexpr int kManuallyAdjustedVolume = 3; 755 apm->set_stream_analog_level(kManuallyAdjustedVolume); 756 apm->ProcessStream(channel_pointers(), stream_config, stream_config, 757 channel_pointers()); 758 EXPECT_GT(apm->recommended_stream_analog_level(), kManuallyAdjustedVolume); 759 } 760 761 TEST_P(ApmInputVolumeControllerParametrizedTest, 762 DoNotEnforceMinInputVolumeAtStartupWithHighVolume) { 763 const StreamConfig stream_config(sample_rate_hz(), num_channels()); 764 auto apm = 765 BuiltinAudioProcessingBuilder(GetConfig()).Build(CreateEnvironment()); 766 767 constexpr int kStartupVolume = 200; 768 apm->set_stream_analog_level(kStartupVolume); 769 apm->ProcessStream(channel_pointers(), stream_config, stream_config, 770 channel_pointers()); 771 EXPECT_EQ(apm->recommended_stream_analog_level(), kStartupVolume); 772 } 773 774 TEST_P(ApmInputVolumeControllerParametrizedTest, 775 DoNotEnforceMinInputVolumeAfterManualVolumeAdjustmentToZero) { 776 const StreamConfig stream_config(sample_rate_hz(), num_channels()); 777 auto apm = 778 BuiltinAudioProcessingBuilder(GetConfig()).Build(CreateEnvironment()); 779 780 apm->set_stream_analog_level(100); 781 apm->ProcessStream(channel_pointers(), stream_config, stream_config, 782 channel_pointers()); 783 apm->set_stream_analog_level(0); 784 apm->ProcessStream(channel_pointers(), stream_config, stream_config, 785 channel_pointers()); 786 EXPECT_EQ(apm->recommended_stream_analog_level(), 0); 787 } 788 789 INSTANTIATE_TEST_SUITE_P( 790 AudioProcessingImplTest, 791 ApmInputVolumeControllerParametrizedTest, 792 ::testing::Combine( 793 ::testing::Values(8000, 16000, 32000, 48000), // Sample rates. 794 ::testing::Values(1, 2), // Number of channels. 795 ::testing::Values( 796 // Full AGC1. 797 AudioProcessing::Config{ 798 .gain_controller1 = {.enabled = true, 799 .analog_gain_controller = 800 {.enabled = true, 801 .enable_digital_adaptive = true}}, 802 .gain_controller2 = {.enabled = false}}, 803 // Hybrid AGC. 804 AudioProcessing::Config{ 805 .gain_controller1 = {.enabled = true, 806 .analog_gain_controller = 807 {.enabled = true, 808 .enable_digital_adaptive = false}}, 809 .gain_controller2 = {.enabled = true, 810 .adaptive_digital = {.enabled = true}}}))); 811 812 // When the input volume is not emulated and no input volume controller is 813 // active, the recommended volume must always be the applied volume. 814 TEST(AudioProcessingImplTest, 815 RecommendAppliedInputVolumeWithNoAgcWithNoEmulation) { 816 auto apm = BuiltinAudioProcessingBuilder( 817 {.capture_level_adjustment = {.enabled = false}, 818 .gain_controller1 = {.enabled = false}}) 819 .Build(CreateEnvironment()); 820 821 constexpr int kOneFrame = 1; 822 EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/123), 123); 823 EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/59), 59); 824 EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/135), 135); 825 } 826 827 // When the input volume is emulated, the recommended volume must always be the 828 // applied volume and at any time it must not be that set in the input volume 829 // emulator. 830 // TODO(bugs.webrtc.org/14581): Enable when APM fixed to let this test pass. 831 TEST(AudioProcessingImplTest, 832 DISABLED_RecommendAppliedInputVolumeWithNoAgcWithEmulation) { 833 auto apm = BuiltinAudioProcessingBuilder( 834 {.capture_level_adjustment = {.enabled = true, 835 .analog_mic_gain_emulation{ 836 .enabled = true, 837 .initial_level = 255}}, 838 .gain_controller1 = {.enabled = false}}) 839 .Build(CreateEnvironment()); 840 841 constexpr int kOneFrame = 1; 842 EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/123), 123); 843 EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/59), 59); 844 EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/135), 135); 845 } 846 847 // Even if there is an enabled input volume controller, when the input volume is 848 // emulated, the recommended volume is always the applied volume because the 849 // active controller must only adjust the internally emulated volume and leave 850 // the externally applied volume unchanged. 851 // TODO(bugs.webrtc.org/14581): Enable when APM fixed to let this test pass. 852 TEST(AudioProcessingImplTest, 853 DISABLED_RecommendAppliedInputVolumeWithAgcWithEmulation) { 854 auto apm = BuiltinAudioProcessingBuilder( 855 {.capture_level_adjustment = {.enabled = true, 856 .analog_mic_gain_emulation{ 857 .enabled = true}}, 858 .gain_controller1 = {.enabled = true, 859 .analog_gain_controller{ 860 .enabled = true, 861 }}}) 862 .Build(CreateEnvironment()); 863 864 constexpr int kOneFrame = 1; 865 EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/123), 123); 866 EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/59), 59); 867 EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/135), 135); 868 } 869 870 class Agc2ParametrizedTest 871 : public ::testing::TestWithParam<AudioProcessing::Config> {}; 872 873 TEST_P(Agc2ParametrizedTest, ProcessSucceedsWhenOneAgcEnabled) { 874 auto apm = 875 BuiltinAudioProcessingBuilder(GetParam()).Build(CreateEnvironment()); 876 constexpr int kSampleRateHz = 48000; 877 constexpr int kNumChannels = 1; 878 std::array<float, kSampleRateHz / 100> buffer; 879 float* channel_pointers[] = {buffer.data()}; 880 StreamConfig stream_config(kSampleRateHz, kNumChannels); 881 Random random_generator(2341U); 882 constexpr int kFramesToProcess = 10; 883 int volume = 100; 884 for (int i = 0; i < kFramesToProcess; ++i) { 885 SCOPED_TRACE(i); 886 RandomizeSampleVector(&random_generator, buffer); 887 apm->set_stream_analog_level(volume); 888 ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config, 889 channel_pointers), 890 kNoErr); 891 volume = apm->recommended_stream_analog_level(); 892 } 893 } 894 895 TEST_P(Agc2ParametrizedTest, 896 BitExactWithAndWithoutTransientSuppressionEnabledInConfig) { 897 const Environment env = CreateEnvironment(); 898 // Enable transient suppression in the config (expect no effect). 899 auto config = GetParam(); 900 config.transient_suppression.enabled = true; 901 auto apm = BuiltinAudioProcessingBuilder(config).Build(env); 902 ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError); 903 // Disable transient suppression in the config. 904 auto config_reference = GetParam(); 905 config_reference.transient_suppression.enabled = false; 906 auto apm_reference = 907 BuiltinAudioProcessingBuilder(config_reference).Build(env); 908 ASSERT_EQ(apm_reference->Initialize(), AudioProcessing::kNoError); 909 910 constexpr int kSampleRateHz = 16000; 911 constexpr int kNumChannels = 1; 912 std::array<float, kSampleRateHz / 100> buffer; 913 std::array<float, kSampleRateHz / 100> buffer_reference; 914 float* channel_pointers[] = {buffer.data()}; 915 float* channel_pointers_reference[] = {buffer_reference.data()}; 916 StreamConfig stream_config(/*sample_rate_hz=*/kSampleRateHz, 917 /*num_channels=*/kNumChannels); 918 Random random_generator(2341U); 919 constexpr int kFramesToProcessPerConfiguration = 100; 920 int volume = 100; 921 int volume_reference = 100; 922 for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) { 923 RandomizeSampleVector(&random_generator, buffer); 924 std::copy(buffer.begin(), buffer.end(), buffer_reference.begin()); 925 apm->set_stream_analog_level(volume); 926 apm_reference->set_stream_analog_level(volume_reference); 927 ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config, 928 channel_pointers), 929 kNoErr); 930 ASSERT_EQ( 931 apm_reference->ProcessStream(channel_pointers_reference, stream_config, 932 stream_config, channel_pointers_reference), 933 kNoErr); 934 volume = apm->recommended_stream_analog_level(); 935 volume_reference = apm_reference->recommended_stream_analog_level(); 936 for (int j = 0; j < kSampleRateHz / 100; ++j) { 937 // Expect no effect from transient suppression. 938 EXPECT_EQ(buffer[j], buffer_reference[j]); 939 } 940 } 941 } 942 943 INSTANTIATE_TEST_SUITE_P( 944 AudioProcessingImplTest, 945 Agc2ParametrizedTest, 946 ::testing::Values( 947 // Full AGC1, TS disabled. 948 AudioProcessing::Config{ 949 .transient_suppression = {.enabled = false}, 950 .gain_controller1 = 951 {.enabled = true, 952 .analog_gain_controller = {.enabled = true, 953 .enable_digital_adaptive = true}}, 954 .gain_controller2 = {.enabled = false}}, 955 // Hybrid AGC, TS disabled. 956 AudioProcessing::Config{ 957 .transient_suppression = {.enabled = false}, 958 .gain_controller1 = 959 {.enabled = true, 960 .analog_gain_controller = {.enabled = true, 961 .enable_digital_adaptive = false}}, 962 .gain_controller2 = {.enabled = true, 963 .adaptive_digital = {.enabled = true}}}, 964 // Full AGC2, TS disabled. 965 AudioProcessing::Config{ 966 .transient_suppression = {.enabled = false}, 967 .gain_controller1 = 968 {.enabled = false, 969 .analog_gain_controller = {.enabled = false, 970 .enable_digital_adaptive = false}}, 971 .gain_controller2 = {.enabled = true, 972 .input_volume_controller = {.enabled = true}, 973 .adaptive_digital = {.enabled = true}}})); 974 975 } // namespace webrtc