diff --git a/modules/audio_processing/aec_dump/aec_dump_impl.cc b/modules/audio_processing/aec_dump/aec_dump_impl.cc index e4ee8726b7..00573436c8 100644 --- a/modules/audio_processing/aec_dump/aec_dump_impl.cc +++ b/modules/audio_processing/aec_dump/aec_dump_impl.cc @@ -192,11 +192,18 @@ void AecDumpImpl::WriteRuntimeSetting( // Runtime AGC1 compression gain is ignored. // TODO(http://bugs.webrtc.org/10432): Store compression gain in aecdumps. break; - case AudioProcessing::RuntimeSetting::Type::kCaptureFixedPostGain: + case AudioProcessing::RuntimeSetting::Type::kCaptureFixedPostGain: { float x; runtime_setting.GetFloat(&x); setting->set_capture_fixed_post_gain(x); break; + } + case AudioProcessing::RuntimeSetting::Type::kPlayoutVolumeChange: { + int x; + runtime_setting.GetInt(&x); + setting->set_playout_volume_change(x); + break; + } case AudioProcessing::RuntimeSetting::Type::kNotSpecified: RTC_NOTREACHED(); break; diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc index 13db45f743..c2ff7f0424 100644 --- a/modules/audio_processing/audio_processing_impl.cc +++ b/modules/audio_processing/audio_processing_impl.cc @@ -853,6 +853,7 @@ void AudioProcessingImpl::SetRuntimeSetting(RuntimeSetting setting) { case RuntimeSetting::Type::kCapturePreGain: case RuntimeSetting::Type::kCaptureCompressionGain: case RuntimeSetting::Type::kCaptureFixedPostGain: + case RuntimeSetting::Type::kPlayoutVolumeChange: capture_runtime_settings_enqueuer_.Enqueue(setting); return; } @@ -998,6 +999,12 @@ void AudioProcessingImpl::HandleCaptureRuntimeSettings() { } break; } + case RuntimeSetting::Type::kPlayoutVolumeChange: { + int value; + setting.GetInt(&value); + capture_.playout_volume = value; + break; + } case RuntimeSetting::Type::kCustomRenderProcessingRuntimeSetting: RTC_NOTREACHED(); break; @@ -1023,6 +1030,7 @@ void AudioProcessingImpl::HandleRenderRuntimeSettings() { case RuntimeSetting::Type::kCapturePreGain: // fall-through case RuntimeSetting::Type::kCaptureCompressionGain: // fall-through case RuntimeSetting::Type::kCaptureFixedPostGain: // fall-through + case RuntimeSetting::Type::kPlayoutVolumeChange: // fall-through case RuntimeSetting::Type::kNotSpecified: RTC_NOTREACHED(); break; @@ -1291,6 +1299,14 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { capture_.prev_pre_amp_gain >= 0.f); capture_.prev_pre_amp_gain = pre_amp_gain; } + + // Detect volume change. + capture_.echo_path_gain_change = + capture_.echo_path_gain_change || + (capture_.prev_playout_volume != capture_.playout_volume && + capture_.prev_playout_volume >= 0); + capture_.prev_playout_volume = capture_.playout_volume; + private_submodules_->echo_controller->AnalyzeCapture(capture_buffer); } @@ -2087,7 +2103,9 @@ AudioProcessingImpl::ApmCaptureState::ApmCaptureState( split_rate(kSampleRate16kHz), echo_path_gain_change(false), prev_analog_mic_level(-1), - prev_pre_amp_gain(-1.f) {} + prev_pre_amp_gain(-1.f), + playout_volume(-1), + prev_playout_volume(-1) {} AudioProcessingImpl::ApmCaptureState::~ApmCaptureState() = default; diff --git a/modules/audio_processing/audio_processing_impl.h b/modules/audio_processing/audio_processing_impl.h index 47eed0a1b7..a2b023a68e 100644 --- a/modules/audio_processing/audio_processing_impl.h +++ b/modules/audio_processing/audio_processing_impl.h @@ -398,6 +398,8 @@ class AudioProcessingImpl : public AudioProcessing { bool echo_path_gain_change; int prev_analog_mic_level; float prev_pre_amp_gain; + int playout_volume; + int prev_playout_volume; AudioProcessingStats stats; } capture_ RTC_GUARDED_BY(crit_capture_); diff --git a/modules/audio_processing/audio_processing_impl_unittest.cc b/modules/audio_processing/audio_processing_impl_unittest.cc index 7359f6ba6b..d688db0274 100644 --- a/modules/audio_processing/audio_processing_impl_unittest.cc +++ b/modules/audio_processing/audio_processing_impl_unittest.cc @@ -296,6 +296,59 @@ TEST(AudioProcessingImplTest, apm->ProcessStream(&frame); } +TEST(AudioProcessingImplTest, EchoControllerObservesPlayoutVolumeChange) { + // Tests that the echo controller observes an echo path gain change when a + // playout volume change is reported. + auto echo_control_factory = absl::make_unique(); + const auto* echo_control_factory_ptr = echo_control_factory.get(); + + std::unique_ptr apm( + AudioProcessingBuilder() + .SetEchoControlFactory(std::move(echo_control_factory)) + .Create()); + apm->gain_control()->Enable(false); // Disable AGC. + apm->gain_control()->set_mode(GainControl::Mode::kFixedDigital); + + AudioFrame frame; + constexpr int16_t kAudioLevel = 10000; + constexpr size_t kSampleRateHz = 48000; + constexpr size_t kNumChannels = 2; + InitializeAudioFrame(kSampleRateHz, kNumChannels, &frame); + FillFixedFrame(kAudioLevel, &frame); + + MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext(); + + EXPECT_CALL(*echo_control_mock, AnalyzeCapture(NotNull())).Times(1); + EXPECT_CALL(*echo_control_mock, + ProcessCapture(NotNull(), /*echo_path_change=*/false)) + .Times(1); + apm->ProcessStream(&frame); + + EXPECT_CALL(*echo_control_mock, AnalyzeCapture(NotNull())).Times(1); + EXPECT_CALL(*echo_control_mock, + ProcessCapture(NotNull(), /*echo_path_change=*/false)) + .Times(1); + apm->SetRuntimeSetting( + AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50)); + apm->ProcessStream(&frame); + + EXPECT_CALL(*echo_control_mock, AnalyzeCapture(NotNull())).Times(1); + EXPECT_CALL(*echo_control_mock, + ProcessCapture(NotNull(), /*echo_path_change=*/false)) + .Times(1); + apm->SetRuntimeSetting( + AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50)); + apm->ProcessStream(&frame); + + EXPECT_CALL(*echo_control_mock, AnalyzeCapture(NotNull())).Times(1); + EXPECT_CALL(*echo_control_mock, + ProcessCapture(NotNull(), /*echo_path_change=*/true)) + .Times(1); + apm->SetRuntimeSetting( + AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(100)); + apm->ProcessStream(&frame); +} + TEST(AudioProcessingImplTest, RenderPreProcessorBeforeEchoDetector) { // Make sure that signal changes caused by a render pre-processing sub-module // take place before any echo detector analysis. diff --git a/modules/audio_processing/debug.proto b/modules/audio_processing/debug.proto index 2af7c81398..0c50a65aa8 100644 --- a/modules/audio_processing/debug.proto +++ b/modules/audio_processing/debug.proto @@ -84,6 +84,7 @@ message RuntimeSetting { optional float capture_pre_gain = 1; optional float custom_render_processing_setting = 2; optional float capture_fixed_post_gain = 3; + optional int32 playout_volume_change = 4; } message Event { diff --git a/modules/audio_processing/include/audio_processing.h b/modules/audio_processing/include/audio_processing.h index 68054d18ea..f6e4331acd 100644 --- a/modules/audio_processing/include/audio_processing.h +++ b/modules/audio_processing/include/audio_processing.h @@ -392,6 +392,7 @@ class AudioProcessing : public rtc::RefCountInterface { kCapturePreGain, kCaptureCompressionGain, kCaptureFixedPostGain, + kPlayoutVolumeChange, kCustomRenderProcessingRuntimeSetting }; @@ -419,6 +420,10 @@ class AudioProcessing : public rtc::RefCountInterface { return {Type::kCaptureFixedPostGain, gain_db}; } + static RuntimeSetting CreatePlayoutVolumeChange(int volume) { + return {Type::kPlayoutVolumeChange, volume}; + } + static RuntimeSetting CreateCustomRenderSetting(float payload) { return {Type::kCustomRenderProcessingRuntimeSetting, payload}; } @@ -426,13 +431,24 @@ class AudioProcessing : public rtc::RefCountInterface { Type type() const { return type_; } void GetFloat(float* value) const { RTC_DCHECK(value); - *value = value_; + *value = value_.float_value; + } + void GetInt(int* value) const { + RTC_DCHECK(value); + *value = value_.int_value; } private: RuntimeSetting(Type id, float value) : type_(id), value_(value) {} + RuntimeSetting(Type id, int value) : type_(id), value_(value) {} Type type_; - float value_; + union U { + U() {} + U(int value) : int_value(value) {} + U(float value) : float_value(value) {} + float float_value; + int int_value; + } value_; }; ~AudioProcessing() override {} diff --git a/modules/audio_processing/test/aec_dump_based_simulator.cc b/modules/audio_processing/test/aec_dump_based_simulator.cc index e52dfcfb2d..00fd25e759 100644 --- a/modules/audio_processing/test/aec_dump_based_simulator.cc +++ b/modules/audio_processing/test/aec_dump_based_simulator.cc @@ -584,6 +584,10 @@ void AecDumpBasedSimulator::HandleMessage( AudioProcessing::RuntimeSetting::CreateCaptureFixedPostGain( msg.capture_fixed_post_gain())); } + } else if (msg.has_playout_volume_change()) { + ap_->SetRuntimeSetting( + AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange( + msg.playout_volume_change())); } } diff --git a/modules/audio_processing/test/runtime_setting_util.cc b/modules/audio_processing/test/runtime_setting_util.cc index bc5f700bf2..a78ca187a8 100644 --- a/modules/audio_processing/test/runtime_setting_util.cc +++ b/modules/audio_processing/test/runtime_setting_util.cc @@ -18,10 +18,11 @@ void ReplayRuntimeSetting(AudioProcessing* apm, const webrtc::audioproc::RuntimeSetting& setting) { RTC_CHECK(apm); // TODO(bugs.webrtc.org/9138): Add ability to handle different types - // of settings. Currently only CapturePreGain and CaptureFixedPostGain are - // supported. + // of settings. Currently CapturePreGain, CaptureFixedPostGain and + // PlayoutVolumeChange are supported. RTC_CHECK(setting.has_capture_pre_gain() || - setting.has_capture_fixed_post_gain()); + setting.has_capture_fixed_post_gain() || + setting.has_playout_volume_change()); if (setting.has_capture_pre_gain()) { apm->SetRuntimeSetting( @@ -31,6 +32,10 @@ void ReplayRuntimeSetting(AudioProcessing* apm, apm->SetRuntimeSetting( AudioProcessing::RuntimeSetting::CreateCaptureFixedPostGain( setting.capture_fixed_post_gain())); + } else if (setting.has_playout_volume_change()) { + apm->SetRuntimeSetting( + AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange( + setting.playout_volume_change())); } } } // namespace webrtc