APM: refactor emulated input volume for capture level adjustment

Switching to an AGC implementation agnostic solution for the input
volume emulation functionality offered by the
`capture_levels_adjuster` sub-module.

This CL also fixes a (silent) bug due to which, when the input
volume is emulated via the capture adjuster sub-module, AGC2
reads an incorrect value for the applied input volume.

Tested: audioproc_f with `--analog_mic_gain_emulation 1` used
to verify bit-exactness for one Wav file and one AEC dump for
which the input volume varies.

Bug: webrtc:7494, b/241923537
Change-Id: Ide3085f9a5dfd85888ad812ebd56faa175fb2ba7
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/273902
Reviewed-by: Per Ã…hgren <peah@webrtc.org>
Commit-Queue: Alessio Bazzica <alessiob@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38053}
diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc
index 453b8d9..ebb69f5 100644
--- a/modules/audio_processing/audio_processing_impl.cc
+++ b/modules/audio_processing/audio_processing_impl.cc
@@ -1099,18 +1099,13 @@
   }
 
   if (submodules_.capture_levels_adjuster) {
-    // If the analog mic gain emulation is active, get the emulated analog mic
-    // gain and pass it to the analog gain control functionality.
     if (config_.capture_level_adjustment.analog_mic_gain_emulation.enabled) {
-      int level = submodules_.capture_levels_adjuster->GetAnalogMicGainLevel();
-      if (submodules_.agc_manager) {
-        submodules_.agc_manager->set_stream_analog_level(level);
-      } else if (submodules_.gain_control) {
-        int error = submodules_.gain_control->set_stream_analog_level(level);
-        RTC_DCHECK_EQ(kNoError, error);
-      }
+      // When the input volume is emulated, retrieve the volume applied to the
+      // input audio and notify that to APM so that the volume is passed to the
+      // active AGC.
+      set_stream_analog_level_locked(
+          submodules_.capture_levels_adjuster->GetAnalogMicGainLevel());
     }
-
     submodules_.capture_levels_adjuster->ApplyPreLevelAdjustment(
         *capture_buffer);
   }
@@ -1369,16 +1364,12 @@
     submodules_.capture_levels_adjuster->ApplyPostLevelAdjustment(
         *capture_buffer);
 
-    // If the analog mic gain emulation is active, retrieve the level from the
-    // analog gain control and set it to mic gain emulator.
     if (config_.capture_level_adjustment.analog_mic_gain_emulation.enabled) {
-      if (submodules_.agc_manager) {
-        submodules_.capture_levels_adjuster->SetAnalogMicGainLevel(
-            submodules_.agc_manager->recommended_analog_level());
-      } else if (submodules_.gain_control) {
-        submodules_.capture_levels_adjuster->SetAnalogMicGainLevel(
-            submodules_.gain_control->stream_analog_level());
-      }
+      // If the input volume emulation is used, retrieve the recommended input
+      // volume and set that to emulate the input volume on the next processed
+      // audio frame.
+      submodules_.capture_levels_adjuster->SetAnalogMicGainLevel(
+          recommended_stream_analog_level_locked());
     }
   }
 
@@ -1603,14 +1594,20 @@
 }
 
 void AudioProcessingImpl::set_stream_analog_level(int level) {
-  MutexLock lock_capture(&mutex_capture_);
+  // Check that input volume emulation is disabled since, when enabled, there is
+  // no externally applied input volume to notify to APM.
+  RTC_DCHECK(
+      !submodules_.capture_levels_adjuster ||
+      !config_.capture_level_adjustment.analog_mic_gain_emulation.enabled);
 
-  if (config_.capture_level_adjustment.analog_mic_gain_emulation.enabled) {
-    // If the analog mic gain is emulated internally, simply cache the level for
-    // later reporting back as the recommended stream analog level to use.
-    capture_.cached_stream_analog_level_ = level;
-    return;
-  }
+  MutexLock lock_capture(&mutex_capture_);
+  set_stream_analog_level_locked(level);
+}
+
+void AudioProcessingImpl::set_stream_analog_level_locked(int level) {
+  // Cache the level for later reporting back as the recommended input volume to
+  // use.
+  capture_.cached_stream_analog_level_ = level;
 
   if (submodules_.agc_manager) {
     submodules_.agc_manager->set_stream_analog_level(level);
@@ -1624,10 +1621,6 @@
     RTC_DCHECK_EQ(kNoError, error);
     return;
   }
-
-  // If no analog mic gain control functionality is in place, cache the level
-  // for later reporting back as the recommended stream analog level to use.
-  capture_.cached_stream_analog_level_ = level;
 }
 
 int AudioProcessingImpl::recommended_stream_analog_level() const {
@@ -1636,10 +1629,6 @@
 }
 
 int AudioProcessingImpl::recommended_stream_analog_level_locked() const {
-  if (config_.capture_level_adjustment.analog_mic_gain_emulation.enabled) {
-    return capture_.cached_stream_analog_level_;
-  }
-
   if (submodules_.agc_manager) {
     return submodules_.agc_manager->recommended_analog_level();
   }
diff --git a/modules/audio_processing/audio_processing_impl.h b/modules/audio_processing/audio_processing_impl.h
index 20135de..a535310 100644
--- a/modules/audio_processing/audio_processing_impl.h
+++ b/modules/audio_processing/audio_processing_impl.h
@@ -160,6 +160,8 @@
   FRIEND_TEST_ALL_PREFIXES(ApmWithSubmodulesExcludedTest,
                            BitexactWithDisabledModules);
 
+  void set_stream_analog_level_locked(int level)
+      RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
   int recommended_stream_analog_level_locked() const
       RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
 
diff --git a/modules/audio_processing/test/audio_processing_simulator.cc b/modules/audio_processing/test/audio_processing_simulator.cc
index b29027c..4a95b44 100644
--- a/modules/audio_processing/test/audio_processing_simulator.cc
+++ b/modules/audio_processing/test/audio_processing_simulator.cc
@@ -210,6 +210,7 @@
 void AudioProcessingSimulator::ProcessStream(bool fixed_interface) {
   // Optionally use the fake recording device to simulate analog gain.
   if (settings_.simulate_mic_gain) {
+    RTC_DCHECK(!settings_.use_analog_mic_gain_emulation);
     if (settings_.aec_dump_input_filename) {
       // When the analog gain is simulated and an AEC dump is used as input, set
       // the undo level to `aec_dump_mic_level_` to virtually restore the
@@ -225,7 +226,7 @@
 
     // Notify the current mic level to AGC.
     ap_->set_stream_analog_level(fake_recording_device_.MicLevel());
-  } else {
+  } else if (!settings_.use_analog_mic_gain_emulation) {
     // Notify the current mic level to AGC.
     ap_->set_stream_analog_level(settings_.aec_dump_input_filename
                                      ? aec_dump_mic_level_
diff --git a/modules/audio_processing/test/audioproc_float_impl.cc b/modules/audio_processing/test/audioproc_float_impl.cc
index dd9fc70..c23ec74 100644
--- a/modules/audio_processing/test/audioproc_float_impl.cc
+++ b/modules/audio_processing/test/audioproc_float_impl.cc
@@ -640,6 +640,12 @@
       "Error: --simulated_mic_kind must be specified when mic simulation is "
       "enabled\n");
 
+  // TODO(bugs.webrtc.org/7494): Document how the two settings below differ.
+  ReportConditionalErrorAndExit(
+      settings.simulate_mic_gain && settings.use_analog_mic_gain_emulation,
+      "Error: --simulate_mic_gain and --use_analog_mic_gain_emulation cannot "
+      "be enabled at the same time\n");
+
   auto valid_wav_name = [](absl::string_view wav_file_name) {
     if (wav_file_name.size() < 5) {
       return false;