Run fullband processing at output rate on ARM

The audio processing in the band-split domain on ARM platforms
operate at a sampling frequency of 32 kHz. This CL upsamples
the signal to fullband before the "fullband processing"
if an output rate of 48 kHz is chosen.

Change-Id: I268acd33aff1fcfa4f75ba8c0fb3e16abb9f74e8
Bug: b/130016532
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/155640
Commit-Queue: Gustaf Ullberg <gustaf@webrtc.org>
Reviewed-by: Per Ã…hgren <peah@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29415}
diff --git a/modules/audio_processing/audio_buffer.cc b/modules/audio_processing/audio_buffer.cc
index 4b0ca20..81ded91 100644
--- a/modules/audio_processing/audio_buffer.cc
+++ b/modules/audio_processing/audio_buffer.cc
@@ -65,9 +65,8 @@
       num_channels_(buffer_num_channels),
       num_bands_(NumBandsFromFramesPerChannel(buffer_num_frames_)),
       num_split_frames_(rtc::CheckedDivExact(buffer_num_frames_, num_bands_)),
-      data_(new ChannelBuffer<float>(buffer_num_frames_, buffer_num_channels_)),
-      output_buffer_(
-          new ChannelBuffer<float>(output_num_frames_, num_channels_)) {
+      data_(
+          new ChannelBuffer<float>(buffer_num_frames_, buffer_num_channels_)) {
   RTC_DCHECK_GT(input_num_frames_, 0);
   RTC_DCHECK_GT(buffer_num_frames_, 0);
   RTC_DCHECK_GT(output_num_frames_, 0);
@@ -185,6 +184,29 @@
   }
 }
 
+void AudioBuffer::CopyTo(AudioBuffer* buffer) const {
+  RTC_DCHECK_EQ(buffer->num_frames(), output_num_frames_);
+
+  const bool resampling_needed = output_num_frames_ != buffer_num_frames_;
+  if (resampling_needed) {
+    for (size_t i = 0; i < num_channels_; ++i) {
+      output_resamplers_[i]->Resample(data_->channels()[i], buffer_num_frames_,
+                                      buffer->channels()[i],
+                                      buffer->num_frames());
+    }
+  } else {
+    for (size_t i = 0; i < num_channels_; ++i) {
+      memcpy(buffer->channels()[i], data_->channels()[i],
+             buffer_num_frames_ * sizeof(**buffer->channels()));
+    }
+  }
+
+  for (size_t i = num_channels_; i < buffer->num_channels(); ++i) {
+    memcpy(buffer->channels()[i], buffer->channels()[0],
+           output_num_frames_ * sizeof(**buffer->channels()));
+  }
+}
+
 void AudioBuffer::RestoreNumChannels() {
   num_channels_ = buffer_num_channels_;
   data_->set_num_channels(buffer_num_channels_);
diff --git a/modules/audio_processing/audio_buffer.h b/modules/audio_processing/audio_buffer.h
index 7bab26d..d27ccca 100644
--- a/modules/audio_processing/audio_buffer.h
+++ b/modules/audio_processing/audio_buffer.h
@@ -115,6 +115,7 @@
   // Copies data from the buffer.
   void CopyTo(AudioFrame* frame) const;
   void CopyTo(const StreamConfig& stream_config, float* const* data);
+  void CopyTo(AudioBuffer* buffer) const;
 
   // Splits the buffer data into frequency bands.
   void SplitIntoFrequencyBands();
@@ -165,7 +166,6 @@
   std::unique_ptr<ChannelBuffer<float>> data_;
   std::unique_ptr<ChannelBuffer<float>> split_data_;
   std::unique_ptr<SplittingFilter> splitting_filter_;
-  std::unique_ptr<ChannelBuffer<float>> output_buffer_;
   std::vector<std::unique_ptr<PushSincResampler>> input_resamplers_;
   std::vector<std::unique_ptr<PushSincResampler>> output_resamplers_;
   bool downmix_by_averaging_ = true;
diff --git a/modules/audio_processing/audio_buffer_unittest.cc b/modules/audio_processing/audio_buffer_unittest.cc
index 9641b1f..402e5c4 100644
--- a/modules/audio_processing/audio_buffer_unittest.cc
+++ b/modules/audio_processing/audio_buffer_unittest.cc
@@ -10,6 +10,7 @@
 
 #include "modules/audio_processing/audio_buffer.h"
 
+#include <cmath>
 #include "test/gtest.h"
 
 namespace webrtc {
@@ -44,4 +45,47 @@
 }
 #endif
 
+TEST(AudioBufferTest, CopyWithoutResampling) {
+  AudioBuffer ab1(32000, 2, 32000, 2, 32000, 2);
+  AudioBuffer ab2(32000, 2, 32000, 2, 32000, 2);
+  // Fill first buffer.
+  for (size_t ch = 0; ch < ab1.num_channels(); ++ch) {
+    for (size_t i = 0; i < ab1.num_frames(); ++i) {
+      ab1.channels()[ch][i] = i + ch;
+    }
+  }
+  // Copy to second buffer.
+  ab1.CopyTo(&ab2);
+  // Verify content of second buffer.
+  for (size_t ch = 0; ch < ab2.num_channels(); ++ch) {
+    for (size_t i = 0; i < ab2.num_frames(); ++i) {
+      EXPECT_EQ(ab2.channels()[ch][i], i + ch);
+    }
+  }
+}
+
+TEST(AudioBufferTest, CopyWithResampling) {
+  AudioBuffer ab1(32000, 2, 32000, 2, 48000, 2);
+  AudioBuffer ab2(48000, 2, 48000, 2, 48000, 2);
+  float energy_ab1 = 0.f;
+  float energy_ab2 = 0.f;
+  const float pi = std::acos(-1.f);
+  // Put a sine and compute energy of first buffer.
+  for (size_t ch = 0; ch < ab1.num_channels(); ++ch) {
+    for (size_t i = 0; i < ab1.num_frames(); ++i) {
+      ab1.channels()[ch][i] = std::sin(2 * pi * 100.f / 32000.f * i);
+      energy_ab1 += ab1.channels()[ch][i] * ab1.channels()[ch][i];
+    }
+  }
+  // Copy to second buffer.
+  ab1.CopyTo(&ab2);
+  // Compute energy of second buffer.
+  for (size_t ch = 0; ch < ab2.num_channels(); ++ch) {
+    for (size_t i = 0; i < ab2.num_frames(); ++i) {
+      energy_ab2 += ab2.channels()[ch][i] * ab2.channels()[ch][i];
+    }
+  }
+  // Verify that energies match.
+  EXPECT_NEAR(energy_ab1, energy_ab2 * 32000.f / 48000.f, .01f * energy_ab1);
+}
 }  // namespace webrtc
diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc
index c661848..ceb1006 100644
--- a/modules/audio_processing/audio_processing_impl.cc
+++ b/modules/audio_processing/audio_processing_impl.cc
@@ -525,6 +525,20 @@
       formats_.api_format.output_stream().sample_rate_hz(),
       formats_.api_format.output_stream().num_channels()));
 
+  if (capture_nonlocked_.capture_processing_format.sample_rate_hz() <
+          formats_.api_format.output_stream().sample_rate_hz() &&
+      formats_.api_format.output_stream().sample_rate_hz() == 48000) {
+    capture_.capture_fullband_audio.reset(
+        new AudioBuffer(formats_.api_format.input_stream().sample_rate_hz(),
+                        formats_.api_format.input_stream().num_channels(),
+                        formats_.api_format.output_stream().sample_rate_hz(),
+                        formats_.api_format.output_stream().num_channels(),
+                        formats_.api_format.output_stream().sample_rate_hz(),
+                        formats_.api_format.output_stream().num_channels()));
+  } else {
+    capture_.capture_fullband_audio.reset();
+  }
+
   AllocateRenderQueue();
 
   public_submodules_->gain_control->Initialize(num_proc_channels(),
@@ -803,6 +817,12 @@
   return capture_nonlocked_.capture_processing_format.sample_rate_hz();
 }
 
+int AudioProcessingImpl::proc_fullband_sample_rate_hz() const {
+  return capture_.capture_fullband_audio
+             ? capture_.capture_fullband_audio->num_frames() * 100
+             : capture_nonlocked_.capture_processing_format.sample_rate_hz();
+}
+
 int AudioProcessingImpl::proc_split_sample_rate_hz() const {
   // Used as callback from submodules, hence locking is not allowed.
   return capture_nonlocked_.split_rate;
@@ -968,7 +988,12 @@
   capture_.keyboard_info.Extract(src, formats_.api_format.input_stream());
   capture_.capture_audio->CopyFrom(src, formats_.api_format.input_stream());
   RETURN_ON_ERR(ProcessCaptureStreamLocked());
-  capture_.capture_audio->CopyTo(formats_.api_format.output_stream(), dest);
+  if (capture_.capture_fullband_audio) {
+    capture_.capture_fullband_audio->CopyTo(formats_.api_format.output_stream(),
+                                            dest);
+  } else {
+    capture_.capture_audio->CopyTo(formats_.api_format.output_stream(), dest);
+  }
 
   if (aec_dump_) {
     RecordProcessedCaptureStream(dest);
@@ -1264,7 +1289,11 @@
   RETURN_ON_ERR(ProcessCaptureStreamLocked());
   if (submodule_states_.CaptureMultiBandProcessingActive() ||
       submodule_states_.CaptureFullBandProcessingActive()) {
-    capture_.capture_audio->CopyTo(frame);
+    if (capture_.capture_fullband_audio) {
+      capture_.capture_fullband_audio->CopyTo(frame);
+    } else {
+      capture_.capture_audio->CopyTo(frame);
+    }
   }
   if (capture_.stats.voice_detected) {
     frame->vad_activity_ = *capture_.stats.voice_detected
@@ -1446,6 +1475,11 @@
     capture_buffer->MergeFrequencyBands();
   }
 
+  if (capture_.capture_fullband_audio) {
+    capture_buffer->CopyTo(capture_.capture_fullband_audio.get());
+    capture_buffer = capture_.capture_fullband_audio.get();
+  }
+
   if (config_.residual_echo_detector.enabled) {
     RTC_DCHECK(private_submodules_->echo_detector);
     private_submodules_->echo_detector->AnalyzeCaptureAudio(
@@ -1830,8 +1864,8 @@
       public_submodules_->transient_suppressor.reset(new TransientSuppressor());
     }
     public_submodules_->transient_suppressor->Initialize(
-        capture_nonlocked_.capture_processing_format.sample_rate_hz(),
-        capture_nonlocked_.split_rate, num_proc_channels());
+        proc_fullband_sample_rate_hz(), capture_nonlocked_.split_rate,
+        num_proc_channels());
   }
 }
 
@@ -1956,7 +1990,8 @@
 
 void AudioProcessingImpl::InitializeGainController2() {
   if (config_.gain_controller2.enabled) {
-    private_submodules_->gain_controller2->Initialize(proc_sample_rate_hz());
+    private_submodules_->gain_controller2->Initialize(
+        proc_fullband_sample_rate_hz());
   }
 }
 
@@ -1972,21 +2007,21 @@
 void AudioProcessingImpl::InitializeResidualEchoDetector() {
   RTC_DCHECK(private_submodules_->echo_detector);
   private_submodules_->echo_detector->Initialize(
-      proc_sample_rate_hz(), 1,
+      proc_fullband_sample_rate_hz(), 1,
       formats_.render_processing_format.sample_rate_hz(), 1);
 }
 
 void AudioProcessingImpl::InitializeAnalyzer() {
   if (private_submodules_->capture_analyzer) {
-    private_submodules_->capture_analyzer->Initialize(proc_sample_rate_hz(),
-                                                      num_proc_channels());
+    private_submodules_->capture_analyzer->Initialize(
+        proc_fullband_sample_rate_hz(), num_proc_channels());
   }
 }
 
 void AudioProcessingImpl::InitializePostProcessor() {
   if (private_submodules_->capture_post_processor) {
     private_submodules_->capture_post_processor->Initialize(
-        proc_sample_rate_hz(), num_proc_channels());
+        proc_fullband_sample_rate_hz(), num_proc_channels());
   }
 }
 
diff --git a/modules/audio_processing/audio_processing_impl.h b/modules/audio_processing/audio_processing_impl.h
index eb75362..c8e8c01 100644
--- a/modules/audio_processing/audio_processing_impl.h
+++ b/modules/audio_processing/audio_processing_impl.h
@@ -245,6 +245,10 @@
   void InitializeAnalyzer() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
   void InitializePreProcessor() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_render_);
 
+  // Sample rate used for the fullband processing.
+  int proc_fullband_sample_rate_hz() const
+      RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
+
   // Empties and handles the respective RuntimeSetting queues.
   void HandleCaptureRuntimeSettings()
       RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
@@ -387,6 +391,7 @@
     bool key_pressed;
     bool transient_suppressor_enabled;
     std::unique_ptr<AudioBuffer> capture_audio;
+    std::unique_ptr<AudioBuffer> capture_fullband_audio;
     // Only the rate and samples fields of capture_processing_format_ are used
     // because the capture processing number of channels is mutable and is
     // tracked by the capture_audio_.