Revert "Add core multi-channel pipeline in AEC3"

This reverts commit f3a197e55323aee974a932c52dd19fa88e5d4e38.

Reason for revert: Speculative revert, as this may'be broken some build bots

Original change's description:
> Add core multi-channel pipeline in AEC3
> This CL adds basic the basic pipeline to support multi-channel
> processing in AEC3.
> 
> Apart from that, it removes the 8 kHz processing support in several
> places of the AEC3 code.
> 
> Bug: webrtc:10913
> Change-Id: If5b75fa325ed0071deea94a7546cb4a7adf22137
> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/150332
> Commit-Queue: Per Ã…hgren <peah@webrtc.org>
> Reviewed-by: Sam Zackrisson <saza@webrtc.org>
> Cr-Commit-Position: refs/heads/master@{#29017}

TBR=saza@webrtc.org,peah@webrtc.org

Change-Id: I877d2993b9ccf024bd1d57bca1513c3e24d0bed3
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: webrtc:10913
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/150940
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29022}
diff --git a/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc b/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc
index 64d84cd..8215736 100644
--- a/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc
+++ b/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc
@@ -53,17 +53,10 @@
 // Verifies that the optimized methods for filter adaptation are similar to
 // their reference counterparts.
 TEST(AdaptiveFirFilter, FilterAdaptationNeonOptimizations) {
-  constexpr size_t kNumRenderChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
-
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(EchoCanceller3Config(), kSampleRateHz,
-                                kNumRenderChannels));
+      RenderDelayBuffer::Create(EchoCanceller3Config(), 48000));
   Random random_generator(42U);
-  std::vector<std::vector<std::vector<float>>> x(
-      kNumBands, std::vector<std::vector<float>>(
-                     kNumRenderChannels, std::vector<float>(kBlockSize, 0.f)));
+  std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
   FftData S_C;
   FftData S_NEON;
   FftData G;
@@ -78,11 +71,7 @@
   }
 
   for (size_t k = 0; k < 30; ++k) {
-    for (size_t band = 0; band < x.size(); ++band) {
-      for (size_t channel = 0; channel < x[band].size(); ++channel) {
-        RandomizeSampleVector(&random_generator, x[band][channel]);
-      }
-    }
+    RandomizeSampleVector(&random_generator, x[0]);
     render_delay_buffer->Insert(x);
     if (k == 0) {
       render_delay_buffer->Reset();
@@ -173,20 +162,12 @@
 // Verifies that the optimized methods for filter adaptation are bitexact to
 // their reference counterparts.
 TEST(AdaptiveFirFilter, FilterAdaptationSse2Optimizations) {
-  constexpr size_t kNumRenderChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
-
   bool use_sse2 = (WebRtc_GetCPUInfo(kSSE2) != 0);
   if (use_sse2) {
     std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-        RenderDelayBuffer::Create(EchoCanceller3Config(), kSampleRateHz,
-                                  kNumRenderChannels));
+        RenderDelayBuffer::Create(EchoCanceller3Config(), 48000));
     Random random_generator(42U);
-    std::vector<std::vector<std::vector<float>>> x(
-        kNumBands,
-        std::vector<std::vector<float>>(kNumRenderChannels,
-                                        std::vector<float>(kBlockSize, 0.f)));
+    std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
     FftData S_C;
     FftData S_SSE2;
     FftData G;
@@ -201,11 +182,7 @@
     }
 
     for (size_t k = 0; k < 500; ++k) {
-      for (size_t band = 0; band < x.size(); ++band) {
-        for (size_t channel = 0; channel < x[band].size(); ++channel) {
-          RandomizeSampleVector(&random_generator, x[band][channel]);
-        }
-      }
+      RandomizeSampleVector(&random_generator, x[0]);
       render_delay_buffer->Insert(x);
       if (k == 0) {
         render_delay_buffer->Reset();
@@ -304,7 +281,7 @@
   ApmDataDumper data_dumper(42);
   AdaptiveFirFilter filter(9, 9, 250, DetectOptimization(), &data_dumper);
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(EchoCanceller3Config(), 48000, 1));
+      RenderDelayBuffer::Create(EchoCanceller3Config(), 48000));
   EXPECT_DEATH(filter.Filter(*render_delay_buffer->GetRenderBuffer(), nullptr),
                "");
 }
@@ -333,10 +310,6 @@
 // Verifies that the filter is being able to properly filter a signal and to
 // adapt its coefficients.
 TEST(AdaptiveFirFilter, FilterAndAdapt) {
-  constexpr size_t kNumRenderChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
-
   constexpr size_t kNumBlocksToProcess = 1000;
   ApmDataDumper data_dumper(42);
   EchoCanceller3Config config;
@@ -347,13 +320,11 @@
   Aec3Fft fft;
   config.delay.default_delay = 1;
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(config, kSampleRateHz, kNumRenderChannels));
+      RenderDelayBuffer::Create(config, 48000));
   ShadowFilterUpdateGain gain(config.filter.shadow,
                               config.filter.config_change_duration_blocks);
   Random random_generator(42U);
-  std::vector<std::vector<std::vector<float>>> x(
-      kNumBands, std::vector<std::vector<float>>(
-                     kNumRenderChannels, std::vector<float>(kBlockSize, 0.f)));
+  std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
   std::vector<float> n(kBlockSize, 0.f);
   std::vector<float> y(kBlockSize, 0.f);
   AecState aec_state(EchoCanceller3Config{});
@@ -386,15 +357,15 @@
 
     SCOPED_TRACE(ProduceDebugText(delay_samples));
     for (size_t j = 0; j < kNumBlocksToProcess; ++j) {
-      RandomizeSampleVector(&random_generator, x[0][0]);
-      delay_buffer.Delay(x[0][0], y);
+      RandomizeSampleVector(&random_generator, x[0]);
+      delay_buffer.Delay(x[0], y);
 
       RandomizeSampleVector(&random_generator, n);
       static constexpr float kNoiseScaling = 1.f / 100.f;
       std::transform(y.begin(), y.end(), n.begin(), y.begin(),
                      [](float a, float b) { return a + b * kNoiseScaling; });
 
-      x_hp_filter.Process(x[0][0]);
+      x_hp_filter.Process(x[0]);
       y_hp_filter.Process(y);
 
       render_delay_buffer->Insert(x);
diff --git a/modules/audio_processing/aec3/aec3_common.h b/modules/audio_processing/aec3/aec3_common.h
index bf554e3..56c7a90 100644
--- a/modules/audio_processing/aec3/aec3_common.h
+++ b/modules/audio_processing/aec3/aec3_common.h
@@ -54,12 +54,16 @@
 
 // TODO(peah): Integrate this with how it is done inside audio_processing_impl.
 constexpr size_t NumBandsForRate(int sample_rate_hz) {
-  return static_cast<size_t>(sample_rate_hz / 16000);
+  return static_cast<size_t>(sample_rate_hz == 8000 ? 1
+                                                    : sample_rate_hz / 16000);
+}
+constexpr int LowestBandRate(int sample_rate_hz) {
+  return sample_rate_hz == 8000 ? sample_rate_hz : 16000;
 }
 
 constexpr bool ValidFullBandRate(int sample_rate_hz) {
-  return sample_rate_hz == 16000 || sample_rate_hz == 32000 ||
-         sample_rate_hz == 48000;
+  return sample_rate_hz == 8000 || sample_rate_hz == 16000 ||
+         sample_rate_hz == 32000 || sample_rate_hz == 48000;
 }
 
 constexpr int GetTimeDomainLength(int filter_length_blocks) {
@@ -96,10 +100,21 @@
 static_assert(1 << kFftLengthBy2Log2 == kFftLengthBy2,
               "Proper number of shifts for the fft length");
 
+static_assert(1 == NumBandsForRate(8000), "Number of bands for 8 kHz");
 static_assert(1 == NumBandsForRate(16000), "Number of bands for 16 kHz");
 static_assert(2 == NumBandsForRate(32000), "Number of bands for 32 kHz");
 static_assert(3 == NumBandsForRate(48000), "Number of bands for 48 kHz");
 
+static_assert(8000 == LowestBandRate(8000), "Sample rate of band 0 for 8 kHz");
+static_assert(16000 == LowestBandRate(16000),
+              "Sample rate of band 0 for 16 kHz");
+static_assert(16000 == LowestBandRate(32000),
+              "Sample rate of band 0 for 32 kHz");
+static_assert(16000 == LowestBandRate(48000),
+              "Sample rate of band 0 for 48 kHz");
+
+static_assert(ValidFullBandRate(8000),
+              "Test that 8 kHz is a valid sample rate");
 static_assert(ValidFullBandRate(16000),
               "Test that 16 kHz is a valid sample rate");
 static_assert(ValidFullBandRate(32000),
diff --git a/modules/audio_processing/aec3/aec_state.cc b/modules/audio_processing/aec3/aec_state.cc
index 566c62f..eab0094 100644
--- a/modules/audio_processing/aec3/aec_state.cc
+++ b/modules/audio_processing/aec3/aec_state.cc
@@ -121,7 +121,7 @@
   }
 
   const std::vector<float>& aligned_render_block =
-      render_buffer.Block(-delay_state_.DirectPathFilterDelay())[0][0];
+      render_buffer.Block(-delay_state_.DirectPathFilterDelay())[0];
 
   // Update render counters.
   const float render_energy = std::inner_product(
diff --git a/modules/audio_processing/aec3/aec_state_unittest.cc b/modules/audio_processing/aec3/aec_state_unittest.cc
index 4631eac..bf47a05 100644
--- a/modules/audio_processing/aec3/aec_state_unittest.cc
+++ b/modules/audio_processing/aec3/aec_state_unittest.cc
@@ -19,21 +19,16 @@
 
 // Verify the general functionality of AecState
 TEST(AecState, NormalUsage) {
-  constexpr size_t kNumChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
   ApmDataDumper data_dumper(42);
   EchoCanceller3Config config;
   AecState state(config);
   absl::optional<DelayEstimate> delay_estimate =
       DelayEstimate(DelayEstimate::Quality::kRefined, 10);
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(config, kSampleRateHz, kNumChannels));
+      RenderDelayBuffer::Create(config, 48000));
   std::array<float, kFftLengthBy2Plus1> E2_main = {};
   std::array<float, kFftLengthBy2Plus1> Y2 = {};
-  std::vector<std::vector<std::vector<float>>> x(
-      kNumBands, std::vector<std::vector<float>>(
-                     kNumChannels, std::vector<float>(kBlockSize, 0.f)));
+  std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
   EchoPathVariability echo_path_variability(
       false, EchoPathVariability::DelayAdjustment::kNone, false);
   SubtractorOutput output;
@@ -58,11 +53,7 @@
       GetTimeDomainLength(config.filter.main.length_blocks), 0.f);
 
   // Verify that linear AEC usability is true when the filter is converged
-  for (size_t band = 0; band < kNumBands; ++band) {
-    for (size_t channel = 0; channel < kNumChannels; ++channel) {
-      std::fill(x[band][channel].begin(), x[band][channel].end(), 101.f);
-    }
-  }
+  std::fill(x[0].begin(), x[0].end(), 101.f);
   for (int k = 0; k < 3000; ++k) {
     render_delay_buffer->Insert(x);
     output.ComputeMetrics(y);
@@ -83,7 +74,7 @@
   EXPECT_FALSE(state.UsableLinearEstimate());
 
   // Verify that the active render detection works as intended.
-  std::fill(x[0][0].begin(), x[0][0].end(), 101.f);
+  std::fill(x[0].begin(), x[0].end(), 101.f);
   render_delay_buffer->Insert(x);
   output.ComputeMetrics(y);
   state.HandleEchoPathChange(EchoPathVariability(
@@ -103,13 +94,11 @@
   EXPECT_TRUE(state.ActiveRender());
 
   // Verify that the ERL is properly estimated
-  for (auto& band : x) {
-    for (auto& channel : band) {
-      channel = std::vector<float>(kBlockSize, 0.f);
-    }
+  for (auto& x_k : x) {
+    x_k = std::vector<float>(kBlockSize, 0.f);
   }
 
-  x[0][0][0] = 5000.f;
+  x[0][0] = 5000.f;
   for (size_t k = 0;
        k < render_delay_buffer->GetRenderBuffer()->GetFftBuffer().size(); ++k) {
     render_delay_buffer->Insert(x);
@@ -190,7 +179,7 @@
   EchoCanceller3Config config;
   AecState state(config);
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(config, 48000, 1));
+      RenderDelayBuffer::Create(config, 48000));
   absl::optional<DelayEstimate> delay_estimate;
   std::array<float, kFftLengthBy2Plus1> E2_main;
   std::array<float, kFftLengthBy2Plus1> Y2;
diff --git a/modules/audio_processing/aec3/block_delay_buffer_unittest.cc b/modules/audio_processing/aec3/block_delay_buffer_unittest.cc
index bda1821..ec825ba 100644
--- a/modules/audio_processing/aec3/block_delay_buffer_unittest.cc
+++ b/modules/audio_processing/aec3/block_delay_buffer_unittest.cc
@@ -50,10 +50,10 @@
 // Verifies that the correct signal delay is achived.
 TEST(BlockDelayBuffer, CorrectDelayApplied) {
   for (size_t delay : {0, 1, 27, 160, 4321, 7021}) {
-    for (auto rate : {16000, 32000, 48000}) {
+    for (auto rate : {8000, 16000, 32000, 48000}) {
       SCOPED_TRACE(ProduceDebugText(rate, delay));
       size_t num_bands = NumBandsForRate(rate);
-      size_t subband_frame_length = 160;
+      size_t subband_frame_length = rate == 8000 ? 80 : 160;
 
       BlockDelayBuffer delay_buffer(num_bands, subband_frame_length, delay);
 
diff --git a/modules/audio_processing/aec3/block_framer.cc b/modules/audio_processing/aec3/block_framer.cc
index 8241ce6..ca7667c 100644
--- a/modules/audio_processing/aec3/block_framer.cc
+++ b/modules/audio_processing/aec3/block_framer.cc
@@ -17,16 +17,9 @@
 
 namespace webrtc {
 
-BlockFramer::BlockFramer(size_t num_bands, size_t num_channels)
+BlockFramer::BlockFramer(size_t num_bands)
     : num_bands_(num_bands),
-      num_channels_(num_channels),
-      buffer_(num_bands_,
-              std::vector<std::vector<float>>(
-                  num_channels,
-                  std::vector<float>(kBlockSize, 0.f))) {
-  RTC_DCHECK_LT(0, num_bands);
-  RTC_DCHECK_LT(0, num_channels);
-}
+      buffer_(num_bands_, std::vector<float>(kBlockSize, 0.f)) {}
 
 BlockFramer::~BlockFramer() = default;
 
@@ -34,52 +27,33 @@
 // samples for InsertBlockAndExtractSubFrame to produce a frame. In order to
 // achieve this, the InsertBlockAndExtractSubFrame and InsertBlock methods need
 // to be called in the correct order.
-void BlockFramer::InsertBlock(
-    const std::vector<std::vector<std::vector<float>>>& block) {
+void BlockFramer::InsertBlock(const std::vector<std::vector<float>>& block) {
   RTC_DCHECK_EQ(num_bands_, block.size());
-  for (size_t band = 0; band < num_bands_; ++band) {
-    RTC_DCHECK_EQ(num_channels_, block[band].size());
-    for (size_t channel = 0; channel < num_channels_; ++channel) {
-      RTC_DCHECK_EQ(kBlockSize, block[band][channel].size());
-      RTC_DCHECK_EQ(0, buffer_[band][channel].size());
-
-      buffer_[band][channel].insert(buffer_[band][channel].begin(),
-                                    block[band][channel].begin(),
-                                    block[band][channel].end());
-    }
+  for (size_t i = 0; i < num_bands_; ++i) {
+    RTC_DCHECK_EQ(kBlockSize, block[i].size());
+    RTC_DCHECK_EQ(0, buffer_[i].size());
+    buffer_[i].insert(buffer_[i].begin(), block[i].begin(), block[i].end());
   }
 }
 
 void BlockFramer::InsertBlockAndExtractSubFrame(
-    const std::vector<std::vector<std::vector<float>>>& block,
-    std::vector<std::vector<rtc::ArrayView<float>>>* sub_frame) {
+    const std::vector<std::vector<float>>& block,
+    std::vector<rtc::ArrayView<float>>* sub_frame) {
   RTC_DCHECK(sub_frame);
   RTC_DCHECK_EQ(num_bands_, block.size());
   RTC_DCHECK_EQ(num_bands_, sub_frame->size());
-  for (size_t band = 0; band < num_bands_; ++band) {
-    RTC_DCHECK_EQ(num_channels_, block[band].size());
-    RTC_DCHECK_EQ(num_channels_, (*sub_frame)[0].size());
-    for (size_t channel = 0; channel < num_channels_; ++channel) {
-      RTC_DCHECK_LE(kSubFrameLength,
-                    buffer_[band][channel].size() + kBlockSize);
-      RTC_DCHECK_EQ(kBlockSize, block[band][channel].size());
-      RTC_DCHECK_GE(kBlockSize, buffer_[band][channel].size());
-      RTC_DCHECK_EQ(kSubFrameLength, (*sub_frame)[band][channel].size());
-
-      const int samples_to_frame =
-          kSubFrameLength - buffer_[band][channel].size();
-      std::copy(buffer_[band][channel].begin(), buffer_[band][channel].end(),
-                (*sub_frame)[band][channel].begin());
-      std::copy(
-          block[band][channel].begin(),
-          block[band][channel].begin() + samples_to_frame,
-          (*sub_frame)[band][channel].begin() + buffer_[band][channel].size());
-      buffer_[band][channel].clear();
-      buffer_[band][channel].insert(
-          buffer_[band][channel].begin(),
-          block[band][channel].begin() + samples_to_frame,
-          block[band][channel].end());
-    }
+  for (size_t i = 0; i < num_bands_; ++i) {
+    RTC_DCHECK_LE(kSubFrameLength, buffer_[i].size() + kBlockSize);
+    RTC_DCHECK_EQ(kBlockSize, block[i].size());
+    RTC_DCHECK_GE(kBlockSize, buffer_[i].size());
+    RTC_DCHECK_EQ(kSubFrameLength, (*sub_frame)[i].size());
+    const int samples_to_frame = kSubFrameLength - buffer_[i].size();
+    std::copy(buffer_[i].begin(), buffer_[i].end(), (*sub_frame)[i].begin());
+    std::copy(block[i].begin(), block[i].begin() + samples_to_frame,
+              (*sub_frame)[i].begin() + buffer_[i].size());
+    buffer_[i].clear();
+    buffer_[i].insert(buffer_[i].begin(), block[i].begin() + samples_to_frame,
+                      block[i].end());
   }
 }
 
diff --git a/modules/audio_processing/aec3/block_framer.h b/modules/audio_processing/aec3/block_framer.h
index 1d37866..fae4b29 100644
--- a/modules/audio_processing/aec3/block_framer.h
+++ b/modules/audio_processing/aec3/block_framer.h
@@ -15,10 +15,11 @@
 
 #include "api/array_view.h"
 #include "modules/audio_processing/aec3/aec3_common.h"
+#include "rtc_base/constructor_magic.h"
 
 namespace webrtc {
 
-// Class for producing frames consisting of 2 subframes of 80 samples each
+// Class for producing frames consisting of 1 or 2 subframes of 80 samples each
 // from 64 sample blocks. The class is designed to work together with the
 // FrameBlocker class which performs the reverse conversion. Used together with
 // that, this class produces output frames are the same rate as frames are
@@ -26,22 +27,20 @@
 // overrun if any other rate of packets insertion is used.
 class BlockFramer {
  public:
-  BlockFramer(size_t num_bands, size_t num_channels);
+  explicit BlockFramer(size_t num_bands);
   ~BlockFramer();
-  BlockFramer(const BlockFramer&) = delete;
-  BlockFramer& operator=(const BlockFramer&) = delete;
-
   // Adds a 64 sample block into the data that will form the next output frame.
-  void InsertBlock(const std::vector<std::vector<std::vector<float>>>& block);
+  void InsertBlock(const std::vector<std::vector<float>>& block);
   // Adds a 64 sample block and extracts an 80 sample subframe.
   void InsertBlockAndExtractSubFrame(
-      const std::vector<std::vector<std::vector<float>>>& block,
-      std::vector<std::vector<rtc::ArrayView<float>>>* sub_frame);
+      const std::vector<std::vector<float>>& block,
+      std::vector<rtc::ArrayView<float>>* sub_frame);
 
  private:
   const size_t num_bands_;
-  const size_t num_channels_;
-  std::vector<std::vector<std::vector<float>>> buffer_;
+  std::vector<std::vector<float>> buffer_;
+
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockFramer);
 };
 }  // namespace webrtc
 
diff --git a/modules/audio_processing/aec3/block_framer_unittest.cc b/modules/audio_processing/aec3/block_framer_unittest.cc
index e9a16d0..9baade9 100644
--- a/modules/audio_processing/aec3/block_framer_unittest.cc
+++ b/modules/audio_processing/aec3/block_framer_unittest.cc
@@ -20,87 +20,66 @@
 namespace webrtc {
 namespace {
 
-void SetupSubFrameView(
-    std::vector<std::vector<std::vector<float>>>* sub_frame,
-    std::vector<std::vector<rtc::ArrayView<float>>>* sub_frame_view) {
-  for (size_t band = 0; band < sub_frame_view->size(); ++band) {
-    for (size_t channel = 0; channel < (*sub_frame_view)[band].size();
-         ++channel) {
-      (*sub_frame_view)[band][channel] =
-          rtc::ArrayView<float>((*sub_frame)[band][channel].data(),
-                                (*sub_frame)[band][channel].size());
-    }
+void SetupSubFrameView(std::vector<std::vector<float>>* sub_frame,
+                       std::vector<rtc::ArrayView<float>>* sub_frame_view) {
+  for (size_t k = 0; k < sub_frame_view->size(); ++k) {
+    (*sub_frame_view)[k] =
+        rtc::ArrayView<float>((*sub_frame)[k].data(), (*sub_frame)[k].size());
   }
 }
 
 float ComputeSampleValue(size_t chunk_counter,
                          size_t chunk_size,
                          size_t band,
-                         size_t channel,
                          size_t sample_index,
                          int offset) {
-  float value = static_cast<int>(100 + chunk_counter * chunk_size +
-                                 sample_index + channel) +
-                offset;
-  return 5000 * band + value;
+  float value =
+      static_cast<int>(chunk_counter * chunk_size + sample_index) + offset;
+  return value > 0 ? 5000 * band + value : 0;
 }
 
-bool VerifySubFrame(
-    size_t sub_frame_counter,
-    int offset,
-    const std::vector<std::vector<rtc::ArrayView<float>>>& sub_frame_view) {
-  for (size_t band = 0; band < sub_frame_view.size(); ++band) {
-    for (size_t channel = 0; channel < sub_frame_view[band].size(); ++channel) {
-      for (size_t sample = 0; sample < sub_frame_view[band][channel].size();
-           ++sample) {
-        const float reference_value = ComputeSampleValue(
-            sub_frame_counter, kSubFrameLength, band, channel, sample, offset);
-        if (reference_value != sub_frame_view[band][channel][sample]) {
-          return false;
-        }
+bool VerifySubFrame(size_t sub_frame_counter,
+                    int offset,
+                    const std::vector<rtc::ArrayView<float>>& sub_frame_view) {
+  for (size_t k = 0; k < sub_frame_view.size(); ++k) {
+    for (size_t i = 0; i < sub_frame_view[k].size(); ++i) {
+      const float reference_value =
+          ComputeSampleValue(sub_frame_counter, kSubFrameLength, k, i, offset);
+      if (reference_value != sub_frame_view[k][i]) {
+        return false;
       }
     }
   }
   return true;
 }
 
-void FillBlock(size_t block_counter,
-               std::vector<std::vector<std::vector<float>>>* block) {
-  for (size_t band = 0; band < block->size(); ++band) {
-    for (size_t channel = 0; channel < (*block)[band].size(); ++channel) {
-      for (size_t sample = 0; sample < (*block)[band][channel].size();
-           ++sample) {
-        (*block)[band][channel][sample] = ComputeSampleValue(
-            block_counter, kBlockSize, band, channel, sample, 0);
-      }
+void FillBlock(size_t block_counter, std::vector<std::vector<float>>* block) {
+  for (size_t k = 0; k < block->size(); ++k) {
+    for (size_t i = 0; i < (*block)[0].size(); ++i) {
+      (*block)[k][i] = ComputeSampleValue(block_counter, kBlockSize, k, i, 0);
     }
   }
 }
 
 // Verifies that the BlockFramer is able to produce the expected frame content.
-void RunFramerTest(int sample_rate_hz, size_t num_channels) {
-  constexpr size_t kNumSubFramesToProcess = 10;
+void RunFramerTest(int sample_rate_hz) {
+  constexpr size_t kNumSubFramesToProcess = 2;
   const size_t num_bands = NumBandsForRate(sample_rate_hz);
 
-  std::vector<std::vector<std::vector<float>>> block(
-      num_bands, std::vector<std::vector<float>>(
-                     num_channels, std::vector<float>(kBlockSize, 0.f)));
-  std::vector<std::vector<std::vector<float>>> output_sub_frame(
-      num_bands, std::vector<std::vector<float>>(
-                     num_channels, std::vector<float>(kSubFrameLength, 0.f)));
-  std::vector<std::vector<rtc::ArrayView<float>>> output_sub_frame_view(
-      num_bands, std::vector<rtc::ArrayView<float>>(num_channels));
+  std::vector<std::vector<float>> block(num_bands,
+                                        std::vector<float>(kBlockSize, 0.f));
+  std::vector<std::vector<float>> output_sub_frame(
+      num_bands, std::vector<float>(kSubFrameLength, 0.f));
+  std::vector<rtc::ArrayView<float>> output_sub_frame_view(num_bands);
   SetupSubFrameView(&output_sub_frame, &output_sub_frame_view);
-  BlockFramer framer(num_bands, num_channels);
+  BlockFramer framer(num_bands);
 
   size_t block_index = 0;
   for (size_t sub_frame_index = 0; sub_frame_index < kNumSubFramesToProcess;
        ++sub_frame_index) {
     FillBlock(block_index++, &block);
     framer.InsertBlockAndExtractSubFrame(block, &output_sub_frame_view);
-    if (sub_frame_index > 1) {
-      EXPECT_TRUE(VerifySubFrame(sub_frame_index, -64, output_sub_frame_view));
-    }
+    EXPECT_TRUE(VerifySubFrame(sub_frame_index, -64, output_sub_frame_view));
 
     if ((sub_frame_index + 1) % 4 == 0) {
       FillBlock(block_index++, &block);
@@ -112,30 +91,21 @@
 #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
 // Verifies that the BlockFramer crashes if the InsertBlockAndExtractSubFrame
 // method is called for inputs with the wrong number of bands or band lengths.
-void RunWronglySizedInsertAndExtractParametersTest(
-    int sample_rate_hz,
-    size_t correct_num_channels,
-    size_t num_block_bands,
-    size_t num_block_channels,
-    size_t block_length,
-    size_t num_sub_frame_bands,
-    size_t num_sub_frame_channels,
-    size_t sub_frame_length) {
+void RunWronglySizedInsertAndExtractParametersTest(int sample_rate_hz,
+                                                   size_t num_block_bands,
+                                                   size_t block_length,
+                                                   size_t num_sub_frame_bands,
+                                                   size_t sub_frame_length) {
   const size_t correct_num_bands = NumBandsForRate(sample_rate_hz);
 
-  std::vector<std::vector<std::vector<float>>> block(
-      num_block_bands,
-      std::vector<std::vector<float>>(num_block_channels,
-                                      std::vector<float>(block_length, 0.f)));
-  std::vector<std::vector<std::vector<float>>> output_sub_frame(
-      num_sub_frame_bands,
-      std::vector<std::vector<float>>(
-          num_sub_frame_channels, std::vector<float>(sub_frame_length, 0.f)));
-  std::vector<std::vector<rtc::ArrayView<float>>> output_sub_frame_view(
-      output_sub_frame.size(),
-      std::vector<rtc::ArrayView<float>>(num_sub_frame_channels));
+  std::vector<std::vector<float>> block(num_block_bands,
+                                        std::vector<float>(block_length, 0.f));
+  std::vector<std::vector<float>> output_sub_frame(
+      num_sub_frame_bands, std::vector<float>(sub_frame_length, 0.f));
+  std::vector<rtc::ArrayView<float>> output_sub_frame_view(
+      output_sub_frame.size());
   SetupSubFrameView(&output_sub_frame, &output_sub_frame_view);
-  BlockFramer framer(correct_num_bands, correct_num_channels);
+  BlockFramer framer(correct_num_bands);
   EXPECT_DEATH(
       framer.InsertBlockAndExtractSubFrame(block, &output_sub_frame_view), "");
 }
@@ -143,29 +113,20 @@
 // Verifies that the BlockFramer crashes if the InsertBlock method is called for
 // inputs with the wrong number of bands or band lengths.
 void RunWronglySizedInsertParameterTest(int sample_rate_hz,
-                                        size_t correct_num_channels,
                                         size_t num_block_bands,
-                                        size_t num_block_channels,
                                         size_t block_length) {
   const size_t correct_num_bands = NumBandsForRate(sample_rate_hz);
 
-  std::vector<std::vector<std::vector<float>>> correct_block(
-      correct_num_bands,
-      std::vector<std::vector<float>>(correct_num_channels,
-                                      std::vector<float>(kBlockSize, 0.f)));
-  std::vector<std::vector<std::vector<float>>> wrong_block(
-      num_block_bands,
-      std::vector<std::vector<float>>(num_block_channels,
-                                      std::vector<float>(block_length, 0.f)));
-  std::vector<std::vector<std::vector<float>>> output_sub_frame(
-      correct_num_bands,
-      std::vector<std::vector<float>>(
-          correct_num_channels, std::vector<float>(kSubFrameLength, 0.f)));
-  std::vector<std::vector<rtc::ArrayView<float>>> output_sub_frame_view(
-      output_sub_frame.size(),
-      std::vector<rtc::ArrayView<float>>(correct_num_channels));
+  std::vector<std::vector<float>> correct_block(
+      correct_num_bands, std::vector<float>(kBlockSize, 0.f));
+  std::vector<std::vector<float>> wrong_block(
+      num_block_bands, std::vector<float>(block_length, 0.f));
+  std::vector<std::vector<float>> output_sub_frame(
+      correct_num_bands, std::vector<float>(kSubFrameLength, 0.f));
+  std::vector<rtc::ArrayView<float>> output_sub_frame_view(
+      output_sub_frame.size());
   SetupSubFrameView(&output_sub_frame, &output_sub_frame_view);
-  BlockFramer framer(correct_num_bands, correct_num_channels);
+  BlockFramer framer(correct_num_bands);
   framer.InsertBlockAndExtractSubFrame(correct_block, &output_sub_frame_view);
   framer.InsertBlockAndExtractSubFrame(correct_block, &output_sub_frame_view);
   framer.InsertBlockAndExtractSubFrame(correct_block, &output_sub_frame_view);
@@ -177,25 +138,18 @@
 // Verifies that the BlockFramer crashes if the InsertBlock method is called
 // after a wrong number of previous InsertBlockAndExtractSubFrame method calls
 // have been made.
-
 void RunWronglyInsertOrderTest(int sample_rate_hz,
-                               size_t num_channels,
                                size_t num_preceeding_api_calls) {
   const size_t correct_num_bands = NumBandsForRate(sample_rate_hz);
 
-  std::vector<std::vector<std::vector<float>>> block(
-      correct_num_bands,
-      std::vector<std::vector<float>>(num_channels,
-                                      std::vector<float>(kBlockSize, 0.f)));
-  std::vector<std::vector<std::vector<float>>> output_sub_frame(
-      correct_num_bands,
-      std::vector<std::vector<float>>(
-          num_channels, std::vector<float>(kSubFrameLength, 0.f)));
-  std::vector<std::vector<rtc::ArrayView<float>>> output_sub_frame_view(
-      output_sub_frame.size(),
-      std::vector<rtc::ArrayView<float>>(num_channels));
+  std::vector<std::vector<float>> block(correct_num_bands,
+                                        std::vector<float>(kBlockSize, 0.f));
+  std::vector<std::vector<float>> output_sub_frame(
+      correct_num_bands, std::vector<float>(kSubFrameLength, 0.f));
+  std::vector<rtc::ArrayView<float>> output_sub_frame_view(
+      output_sub_frame.size());
   SetupSubFrameView(&output_sub_frame, &output_sub_frame_view);
-  BlockFramer framer(correct_num_bands, num_channels);
+  BlockFramer framer(correct_num_bands);
   for (size_t k = 0; k < num_preceeding_api_calls; ++k) {
     framer.InsertBlockAndExtractSubFrame(block, &output_sub_frame_view);
   }
@@ -204,10 +158,9 @@
 }
 #endif
 
-std::string ProduceDebugText(int sample_rate_hz, size_t num_channels) {
+std::string ProduceDebugText(int sample_rate_hz) {
   rtc::StringBuilder ss;
   ss << "Sample rate: " << sample_rate_hz;
-  ss << ", number of channels: " << num_channels;
   return ss.Release();
 }
 
@@ -215,157 +168,83 @@
 
 #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
 TEST(BlockFramer, WrongNumberOfBandsInBlockForInsertBlockAndExtractSubFrame) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (auto correct_num_channels : {1, 2, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
-      const size_t correct_num_bands = NumBandsForRate(rate);
-      const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
-      RunWronglySizedInsertAndExtractParametersTest(
-          rate, correct_num_channels, wrong_num_bands, correct_num_channels,
-          kBlockSize, correct_num_bands, correct_num_channels, kSubFrameLength);
-    }
-  }
-}
-
-TEST(BlockFramer,
-     WrongNumberOfChannelsInBlockForInsertBlockAndExtractSubFrame) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (auto correct_num_channels : {1, 2, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
-      const size_t correct_num_bands = NumBandsForRate(rate);
-      const size_t wrong_num_channels = correct_num_channels + 1;
-      RunWronglySizedInsertAndExtractParametersTest(
-          rate, correct_num_channels, correct_num_bands, wrong_num_channels,
-          kBlockSize, correct_num_bands, correct_num_channels, kSubFrameLength);
-    }
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    const size_t correct_num_bands = NumBandsForRate(rate);
+    const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
+    RunWronglySizedInsertAndExtractParametersTest(
+        rate, wrong_num_bands, kBlockSize, correct_num_bands, kSubFrameLength);
   }
 }
 
 TEST(BlockFramer,
      WrongNumberOfBandsInSubFrameForInsertBlockAndExtractSubFrame) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (auto correct_num_channels : {1, 2, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
-      const size_t correct_num_bands = NumBandsForRate(rate);
-      const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
-      RunWronglySizedInsertAndExtractParametersTest(
-          rate, correct_num_channels, correct_num_bands, correct_num_channels,
-          kBlockSize, wrong_num_bands, correct_num_channels, kSubFrameLength);
-    }
-  }
-}
-
-TEST(BlockFramer,
-     WrongNumberOfChannelsInSubFrameForInsertBlockAndExtractSubFrame) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (auto correct_num_channels : {1, 2, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
-      const size_t correct_num_bands = NumBandsForRate(rate);
-      const size_t wrong_num_channels = correct_num_channels + 1;
-      RunWronglySizedInsertAndExtractParametersTest(
-          rate, correct_num_channels, correct_num_bands, correct_num_channels,
-          kBlockSize, correct_num_bands, wrong_num_channels, kSubFrameLength);
-    }
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    const size_t correct_num_bands = NumBandsForRate(rate);
+    const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
+    RunWronglySizedInsertAndExtractParametersTest(
+        rate, correct_num_bands, kBlockSize, wrong_num_bands, kSubFrameLength);
   }
 }
 
 TEST(BlockFramer, WrongNumberOfSamplesInBlockForInsertBlockAndExtractSubFrame) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (auto correct_num_channels : {1, 2, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
-      const size_t correct_num_bands = NumBandsForRate(rate);
-      RunWronglySizedInsertAndExtractParametersTest(
-          rate, correct_num_channels, correct_num_bands, correct_num_channels,
-          kBlockSize - 1, correct_num_bands, correct_num_channels,
-          kSubFrameLength);
-    }
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    const size_t correct_num_bands = NumBandsForRate(rate);
+    RunWronglySizedInsertAndExtractParametersTest(
+        rate, correct_num_bands, kBlockSize - 1, correct_num_bands,
+        kSubFrameLength);
   }
 }
 
 TEST(BlockFramer,
      WrongNumberOfSamplesInSubFrameForInsertBlockAndExtractSubFrame) {
-  const size_t correct_num_channels = 1;
-  for (auto rate : {16000, 32000, 48000}) {
-    SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
     const size_t correct_num_bands = NumBandsForRate(rate);
-    RunWronglySizedInsertAndExtractParametersTest(
-        rate, correct_num_channels, correct_num_bands, correct_num_channels,
-        kBlockSize, correct_num_bands, correct_num_channels,
-        kSubFrameLength - 1);
+    RunWronglySizedInsertAndExtractParametersTest(rate, correct_num_bands,
+                                                  kBlockSize, correct_num_bands,
+                                                  kSubFrameLength - 1);
   }
 }
 
 TEST(BlockFramer, WrongNumberOfBandsInBlockForInsertBlock) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (auto correct_num_channels : {1, 2, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
-      const size_t correct_num_bands = NumBandsForRate(rate);
-      const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
-      RunWronglySizedInsertParameterTest(rate, correct_num_channels,
-                                         wrong_num_bands, correct_num_channels,
-                                         kBlockSize);
-    }
-  }
-}
-
-TEST(BlockFramer, WrongNumberOfChannelsInBlockForInsertBlock) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (auto correct_num_channels : {1, 2, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
-      const size_t correct_num_bands = NumBandsForRate(rate);
-      const size_t wrong_num_channels = correct_num_channels + 1;
-      RunWronglySizedInsertParameterTest(rate, correct_num_channels,
-                                         correct_num_bands, wrong_num_channels,
-                                         kBlockSize);
-    }
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    const size_t correct_num_bands = NumBandsForRate(rate);
+    const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
+    RunWronglySizedInsertParameterTest(rate, wrong_num_bands, kBlockSize);
   }
 }
 
 TEST(BlockFramer, WrongNumberOfSamplesInBlockForInsertBlock) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (auto correct_num_channels : {1, 2, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
-      const size_t correct_num_bands = NumBandsForRate(rate);
-      RunWronglySizedInsertParameterTest(rate, correct_num_channels,
-                                         correct_num_bands,
-                                         correct_num_channels, kBlockSize - 1);
-    }
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    const size_t correct_num_bands = NumBandsForRate(rate);
+    RunWronglySizedInsertParameterTest(rate, correct_num_bands, kBlockSize - 1);
   }
 }
 
 TEST(BlockFramer, WrongNumberOfPreceedingApiCallsForInsertBlock) {
-  for (size_t num_channels : {1, 2, 8}) {
-    for (auto rate : {16000, 32000, 48000}) {
-      for (size_t num_calls = 0; num_calls < 4; ++num_calls) {
-        rtc::StringBuilder ss;
-        ss << "Sample rate: " << rate;
-        ss << ", Num channels: " << num_channels;
-        ss << ", Num preceeding InsertBlockAndExtractSubFrame calls: "
-           << num_calls;
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    for (size_t num_calls = 0; num_calls < 4; ++num_calls) {
+      rtc::StringBuilder ss;
+      ss << "Sample rate: " << rate;
+      ss << ", Num preceeding InsertBlockAndExtractSubFrame calls: "
+         << num_calls;
 
-        SCOPED_TRACE(ss.str());
-        RunWronglyInsertOrderTest(rate, num_channels, num_calls);
-      }
+      SCOPED_TRACE(ss.str());
+      RunWronglyInsertOrderTest(rate, num_calls);
     }
   }
 }
 
-// Verifies that the verification for 0 number of channels works.
-TEST(BlockFramer, ZeroNumberOfChannelsParameter) {
-  EXPECT_DEATH(BlockFramer(16000, 0), "");
-}
-
-// Verifies that the verification for 0 number of bands works.
-TEST(BlockFramer, ZeroNumberOfBandsParameter) {
-  EXPECT_DEATH(BlockFramer(0, 1), "");
-}
-
-// Verifies that the verification for null sub_frame pointer works.
+// Verifiers that the verification for null sub_frame pointer works.
 TEST(BlockFramer, NullSubFrameParameter) {
-  EXPECT_DEATH(BlockFramer(1, 1).InsertBlockAndExtractSubFrame(
-                   std::vector<std::vector<std::vector<float>>>(
-                       1, std::vector<std::vector<float>>(
-                              1, std::vector<float>(kBlockSize, 0.f))),
+  EXPECT_DEATH(BlockFramer(1).InsertBlockAndExtractSubFrame(
+                   std::vector<std::vector<float>>(
+                       1, std::vector<float>(kBlockSize, 0.f)),
                    nullptr),
                "");
 }
@@ -373,11 +252,9 @@
 #endif
 
 TEST(BlockFramer, FrameBitexactness) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (auto num_channels : {1, 2, 4, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, num_channels));
-      RunFramerTest(rate, num_channels);
-    }
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    RunFramerTest(rate);
   }
 }
 
diff --git a/modules/audio_processing/aec3/block_processor.cc b/modules/audio_processing/aec3/block_processor.cc
index 33b6b9b..184248f 100644
--- a/modules/audio_processing/aec3/block_processor.cc
+++ b/modules/audio_processing/aec3/block_processor.cc
@@ -39,8 +39,6 @@
  public:
   BlockProcessorImpl(const EchoCanceller3Config& config,
                      int sample_rate_hz,
-                     size_t num_render_channels,
-                     size_t num_capture_channels,
                      std::unique_ptr<RenderDelayBuffer> render_buffer,
                      std::unique_ptr<RenderDelayController> delay_controller,
                      std::unique_ptr<EchoRemover> echo_remover);
@@ -49,13 +47,11 @@
 
   ~BlockProcessorImpl() override;
 
-  void ProcessCapture(
-      bool echo_path_gain_change,
-      bool capture_signal_saturation,
-      std::vector<std::vector<std::vector<float>>>* capture_block) override;
+  void ProcessCapture(bool echo_path_gain_change,
+                      bool capture_signal_saturation,
+                      std::vector<std::vector<float>>* capture_block) override;
 
-  void BufferRender(
-      const std::vector<std::vector<std::vector<float>>>& block) override;
+  void BufferRender(const std::vector<std::vector<float>>& block) override;
 
   void UpdateEchoLeakageStatus(bool leakage_detected) override;
 
@@ -84,8 +80,6 @@
 BlockProcessorImpl::BlockProcessorImpl(
     const EchoCanceller3Config& config,
     int sample_rate_hz,
-    size_t num_render_channels,
-    size_t num_capture_channels,
     std::unique_ptr<RenderDelayBuffer> render_buffer,
     std::unique_ptr<RenderDelayController> delay_controller,
     std::unique_ptr<EchoRemover> echo_remover)
@@ -105,17 +99,18 @@
 void BlockProcessorImpl::ProcessCapture(
     bool echo_path_gain_change,
     bool capture_signal_saturation,
-    std::vector<std::vector<std::vector<float>>>* capture_block) {
+    std::vector<std::vector<float>>* capture_block) {
   RTC_DCHECK(capture_block);
   RTC_DCHECK_EQ(NumBandsForRate(sample_rate_hz_), capture_block->size());
-  RTC_DCHECK_EQ(kBlockSize, (*capture_block)[0][0].size());
+  RTC_DCHECK_EQ(kBlockSize, (*capture_block)[0].size());
 
   capture_call_counter_++;
 
   data_dumper_->DumpRaw("aec3_processblock_call_order",
                         static_cast<int>(BlockProcessorApiCall::kCapture));
   data_dumper_->DumpWav("aec3_processblock_capture_input", kBlockSize,
-                        &(*capture_block)[0][0][0], 16000, 1);
+                        &(*capture_block)[0][0],
+                        LowestBandRate(sample_rate_hz_), 1);
 
   if (render_properly_started_) {
     if (!capture_properly_started_) {
@@ -156,7 +151,8 @@
   }
 
   data_dumper_->DumpWav("aec3_processblock_capture_input2", kBlockSize,
-                        &(*capture_block)[0][0][0], 16000, 1);
+                        &(*capture_block)[0][0],
+                        LowestBandRate(sample_rate_hz_), 1);
 
   bool has_delay_estimator = !config_.delay.use_external_delay_estimator;
   if (has_delay_estimator) {
@@ -165,7 +161,7 @@
     // alignment.
     estimated_delay_ = delay_controller_->GetDelay(
         render_buffer_->GetDownsampledRenderBuffer(), render_buffer_->Delay(),
-        (*capture_block)[0][0]);
+        (*capture_block)[0]);
 
     if (estimated_delay_) {
       bool delay_change =
@@ -196,15 +192,15 @@
 }
 
 void BlockProcessorImpl::BufferRender(
-    const std::vector<std::vector<std::vector<float>>>& block) {
+    const std::vector<std::vector<float>>& block) {
   RTC_DCHECK_EQ(NumBandsForRate(sample_rate_hz_), block.size());
-  RTC_DCHECK_EQ(kBlockSize, block[0][0].size());
+  RTC_DCHECK_EQ(kBlockSize, block[0].size());
   data_dumper_->DumpRaw("aec3_processblock_call_order",
                         static_cast<int>(BlockProcessorApiCall::kRender));
   data_dumper_->DumpWav("aec3_processblock_render_input", kBlockSize,
-                        &block[0][0][0], 16000, 1);
+                        &block[0][0], LowestBandRate(sample_rate_hz_), 1);
   data_dumper_->DumpWav("aec3_processblock_render_input2", kBlockSize,
-                        &block[0][0][0], 16000, 1);
+                        &block[0][0], LowestBandRate(sample_rate_hz_), 1);
 
   render_event_ = render_buffer_->Insert(block);
 
@@ -222,7 +218,7 @@
 
 void BlockProcessorImpl::GetMetrics(EchoControl::Metrics* metrics) const {
   echo_remover_->GetMetrics(metrics);
-  constexpr int block_size_ms = 4;
+  const int block_size_ms = sample_rate_hz_ == 8000 ? 8 : 4;
   absl::optional<size_t> delay = render_buffer_->Delay();
   metrics->delay_ms = delay ? static_cast<int>(*delay) * block_size_ms : 0;
 }
@@ -234,53 +230,44 @@
 }  // namespace
 
 BlockProcessor* BlockProcessor::Create(const EchoCanceller3Config& config,
-                                       int sample_rate_hz,
-                                       size_t num_render_channels,
-                                       size_t num_capture_channels) {
+                                       int sample_rate_hz) {
   std::unique_ptr<RenderDelayBuffer> render_buffer(
-      RenderDelayBuffer::Create(config, sample_rate_hz, num_render_channels));
+      RenderDelayBuffer::Create(config, sample_rate_hz));
   std::unique_ptr<RenderDelayController> delay_controller;
   if (!config.delay.use_external_delay_estimator) {
     delay_controller.reset(
         RenderDelayController::Create(config, sample_rate_hz));
   }
-  std::unique_ptr<EchoRemover> echo_remover(EchoRemover::Create(
-      config, sample_rate_hz, num_render_channels, num_capture_channels));
-  return Create(config, sample_rate_hz, num_render_channels,
-                num_capture_channels, std::move(render_buffer),
+  std::unique_ptr<EchoRemover> echo_remover(
+      EchoRemover::Create(config, sample_rate_hz));
+  return Create(config, sample_rate_hz, std::move(render_buffer),
                 std::move(delay_controller), std::move(echo_remover));
 }
 
 BlockProcessor* BlockProcessor::Create(
     const EchoCanceller3Config& config,
     int sample_rate_hz,
-    size_t num_render_channels,
-    size_t num_capture_channels,
     std::unique_ptr<RenderDelayBuffer> render_buffer) {
   std::unique_ptr<RenderDelayController> delay_controller;
   if (!config.delay.use_external_delay_estimator) {
     delay_controller.reset(
         RenderDelayController::Create(config, sample_rate_hz));
   }
-  std::unique_ptr<EchoRemover> echo_remover(EchoRemover::Create(
-      config, sample_rate_hz, num_render_channels, num_capture_channels));
-  return Create(config, sample_rate_hz, num_render_channels,
-                num_capture_channels, std::move(render_buffer),
+  std::unique_ptr<EchoRemover> echo_remover(
+      EchoRemover::Create(config, sample_rate_hz));
+  return Create(config, sample_rate_hz, std::move(render_buffer),
                 std::move(delay_controller), std::move(echo_remover));
 }
 
 BlockProcessor* BlockProcessor::Create(
     const EchoCanceller3Config& config,
     int sample_rate_hz,
-    size_t num_render_channels,
-    size_t num_capture_channels,
     std::unique_ptr<RenderDelayBuffer> render_buffer,
     std::unique_ptr<RenderDelayController> delay_controller,
     std::unique_ptr<EchoRemover> echo_remover) {
-  return new BlockProcessorImpl(config, sample_rate_hz, num_render_channels,
-                                num_capture_channels, std::move(render_buffer),
-                                std::move(delay_controller),
-                                std::move(echo_remover));
+  return new BlockProcessorImpl(
+      config, sample_rate_hz, std::move(render_buffer),
+      std::move(delay_controller), std::move(echo_remover));
 }
 
 }  // namespace webrtc
diff --git a/modules/audio_processing/aec3/block_processor.h b/modules/audio_processing/aec3/block_processor.h
index 3ae5a75..8b1bb90 100644
--- a/modules/audio_processing/aec3/block_processor.h
+++ b/modules/audio_processing/aec3/block_processor.h
@@ -28,21 +28,15 @@
 class BlockProcessor {
  public:
   static BlockProcessor* Create(const EchoCanceller3Config& config,
-                                int sample_rate_hz,
-                                size_t num_render_channels,
-                                size_t num_capture_channels);
+                                int sample_rate_hz);
   // Only used for testing purposes.
   static BlockProcessor* Create(
       const EchoCanceller3Config& config,
       int sample_rate_hz,
-      size_t num_render_channels,
-      size_t num_capture_channels,
       std::unique_ptr<RenderDelayBuffer> render_buffer);
   static BlockProcessor* Create(
       const EchoCanceller3Config& config,
       int sample_rate_hz,
-      size_t num_render_channels,
-      size_t num_capture_channels,
       std::unique_ptr<RenderDelayBuffer> render_buffer,
       std::unique_ptr<RenderDelayController> delay_controller,
       std::unique_ptr<EchoRemover> echo_remover);
@@ -59,11 +53,11 @@
   virtual void ProcessCapture(
       bool echo_path_gain_change,
       bool capture_signal_saturation,
-      std::vector<std::vector<std::vector<float>>>* capture_block) = 0;
+      std::vector<std::vector<float>>* capture_block) = 0;
 
   // Buffers a block of render data supplied by a FrameBlocker object.
   virtual void BufferRender(
-      const std::vector<std::vector<std::vector<float>>>& render_block) = 0;
+      const std::vector<std::vector<float>>& render_block) = 0;
 
   // Reports whether echo leakage has been detected in the echo canceller
   // output.
diff --git a/modules/audio_processing/aec3/block_processor_unittest.cc b/modules/audio_processing/aec3/block_processor_unittest.cc
index 9c315e1..bd085da 100644
--- a/modules/audio_processing/aec3/block_processor_unittest.cc
+++ b/modules/audio_processing/aec3/block_processor_unittest.cc
@@ -36,16 +36,11 @@
 // Verifies that the basic BlockProcessor functionality works and that the API
 // methods are callable.
 void RunBasicSetupAndApiCallTest(int sample_rate_hz, int num_iterations) {
-  constexpr size_t kNumRenderChannels = 1;
-  constexpr size_t kNumCaptureChannels = 1;
-
   std::unique_ptr<BlockProcessor> block_processor(
-      BlockProcessor::Create(EchoCanceller3Config(), sample_rate_hz,
-                             kNumRenderChannels, kNumCaptureChannels));
-  std::vector<std::vector<std::vector<float>>> block(
-      NumBandsForRate(sample_rate_hz),
-      std::vector<std::vector<float>>(kNumRenderChannels,
-                                      std::vector<float>(kBlockSize, 1000.f)));
+      BlockProcessor::Create(EchoCanceller3Config(), sample_rate_hz));
+  std::vector<std::vector<float>> block(NumBandsForRate(sample_rate_hz),
+                                        std::vector<float>(kBlockSize, 1000.f));
+
   for (int k = 0; k < num_iterations; ++k) {
     block_processor->BufferRender(block);
     block_processor->ProcessCapture(false, false, &block);
@@ -55,67 +50,43 @@
 
 #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
 void RunRenderBlockSizeVerificationTest(int sample_rate_hz) {
-  constexpr size_t kNumRenderChannels = 1;
-  constexpr size_t kNumCaptureChannels = 1;
-
   std::unique_ptr<BlockProcessor> block_processor(
-      BlockProcessor::Create(EchoCanceller3Config(), sample_rate_hz,
-                             kNumRenderChannels, kNumCaptureChannels));
-  std::vector<std::vector<std::vector<float>>> block(
-      NumBandsForRate(sample_rate_hz),
-      std::vector<std::vector<float>>(kNumRenderChannels,
-                                      std::vector<float>(kBlockSize - 1, 0.f)));
+      BlockProcessor::Create(EchoCanceller3Config(), sample_rate_hz));
+  std::vector<std::vector<float>> block(
+      NumBandsForRate(sample_rate_hz), std::vector<float>(kBlockSize - 1, 0.f));
 
   EXPECT_DEATH(block_processor->BufferRender(block), "");
 }
 
 void RunCaptureBlockSizeVerificationTest(int sample_rate_hz) {
-  constexpr size_t kNumRenderChannels = 1;
-  constexpr size_t kNumCaptureChannels = 1;
-
   std::unique_ptr<BlockProcessor> block_processor(
-      BlockProcessor::Create(EchoCanceller3Config(), sample_rate_hz,
-                             kNumRenderChannels, kNumCaptureChannels));
-  std::vector<std::vector<std::vector<float>>> block(
-      NumBandsForRate(sample_rate_hz),
-      std::vector<std::vector<float>>(kNumRenderChannels,
-                                      std::vector<float>(kBlockSize - 1, 0.f)));
+      BlockProcessor::Create(EchoCanceller3Config(), sample_rate_hz));
+  std::vector<std::vector<float>> block(
+      NumBandsForRate(sample_rate_hz), std::vector<float>(kBlockSize - 1, 0.f));
 
   EXPECT_DEATH(block_processor->ProcessCapture(false, false, &block), "");
 }
 
 void RunRenderNumBandsVerificationTest(int sample_rate_hz) {
-  constexpr size_t kNumRenderChannels = 1;
-  constexpr size_t kNumCaptureChannels = 1;
-
   const size_t wrong_num_bands = NumBandsForRate(sample_rate_hz) < 3
                                      ? NumBandsForRate(sample_rate_hz) + 1
                                      : 1;
   std::unique_ptr<BlockProcessor> block_processor(
-      BlockProcessor::Create(EchoCanceller3Config(), sample_rate_hz,
-                             kNumRenderChannels, kNumCaptureChannels));
-  std::vector<std::vector<std::vector<float>>> block(
-      wrong_num_bands,
-      std::vector<std::vector<float>>(kNumRenderChannels,
-                                      std::vector<float>(kBlockSize, 0.f)));
+      BlockProcessor::Create(EchoCanceller3Config(), sample_rate_hz));
+  std::vector<std::vector<float>> block(wrong_num_bands,
+                                        std::vector<float>(kBlockSize, 0.f));
 
   EXPECT_DEATH(block_processor->BufferRender(block), "");
 }
 
 void RunCaptureNumBandsVerificationTest(int sample_rate_hz) {
-  constexpr size_t kNumRenderChannels = 1;
-  constexpr size_t kNumCaptureChannels = 1;
-
   const size_t wrong_num_bands = NumBandsForRate(sample_rate_hz) < 3
                                      ? NumBandsForRate(sample_rate_hz) + 1
                                      : 1;
   std::unique_ptr<BlockProcessor> block_processor(
-      BlockProcessor::Create(EchoCanceller3Config(), sample_rate_hz,
-                             kNumRenderChannels, kNumCaptureChannels));
-  std::vector<std::vector<std::vector<float>>> block(
-      wrong_num_bands,
-      std::vector<std::vector<float>>(kNumRenderChannels,
-                                      std::vector<float>(kBlockSize, 0.f)));
+      BlockProcessor::Create(EchoCanceller3Config(), sample_rate_hz));
+  std::vector<std::vector<float>> block(wrong_num_bands,
+                                        std::vector<float>(kBlockSize, 0.f));
 
   EXPECT_DEATH(block_processor->ProcessCapture(false, false, &block), "");
 }
@@ -133,19 +104,17 @@
 // the render delay buffer inside block processor.
 // TODO(peah): Activate the unittest once the required code has been landed.
 TEST(BlockProcessor, DISABLED_DelayControllerIntegration) {
-  constexpr size_t kNumRenderChannels = 1;
-  constexpr size_t kNumCaptureChannels = 1;
   constexpr size_t kNumBlocks = 310;
   constexpr size_t kDelayInSamples = 640;
   constexpr size_t kDelayHeadroom = 1;
   constexpr size_t kDelayInBlocks =
       kDelayInSamples / kBlockSize - kDelayHeadroom;
   Random random_generator(42U);
-  for (auto rate : {16000, 32000, 48000}) {
+  for (auto rate : {8000, 16000, 32000, 48000}) {
     SCOPED_TRACE(ProduceDebugText(rate));
     std::unique_ptr<testing::StrictMock<webrtc::test::MockRenderDelayBuffer>>
         render_delay_buffer_mock(
-            new StrictMock<webrtc::test::MockRenderDelayBuffer>(rate, 1));
+            new StrictMock<webrtc::test::MockRenderDelayBuffer>(rate));
     EXPECT_CALL(*render_delay_buffer_mock, Insert(_))
         .Times(kNumBlocks)
         .WillRepeatedly(Return(RenderDelayBuffer::BufferingEvent::kNone));
@@ -156,21 +125,16 @@
         .Times(kNumBlocks + 1)
         .WillRepeatedly(Return(0));
     std::unique_ptr<BlockProcessor> block_processor(BlockProcessor::Create(
-        EchoCanceller3Config(), rate, kNumRenderChannels, kNumCaptureChannels,
-        std::move(render_delay_buffer_mock)));
+        EchoCanceller3Config(), rate, std::move(render_delay_buffer_mock)));
 
-    std::vector<std::vector<std::vector<float>>> render_block(
-        NumBandsForRate(rate),
-        std::vector<std::vector<float>>(kNumRenderChannels,
-                                        std::vector<float>(kBlockSize, 0.f)));
-    std::vector<std::vector<std::vector<float>>> capture_block(
-        NumBandsForRate(rate),
-        std::vector<std::vector<float>>(kNumCaptureChannels,
-                                        std::vector<float>(kBlockSize, 0.f)));
+    std::vector<std::vector<float>> render_block(
+        NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
+    std::vector<std::vector<float>> capture_block(
+        NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
     DelayBuffer<float> signal_delay_buffer(kDelayInSamples);
     for (size_t k = 0; k < kNumBlocks; ++k) {
-      RandomizeSampleVector(&random_generator, render_block[0][0]);
-      signal_delay_buffer.Delay(render_block[0][0], capture_block[0][0]);
+      RandomizeSampleVector(&random_generator, render_block[0]);
+      signal_delay_buffer.Delay(render_block[0], capture_block[0]);
       block_processor->BufferRender(render_block);
       block_processor->ProcessCapture(false, false, &capture_block);
     }
@@ -180,15 +144,12 @@
 // Verifies that BlockProcessor submodules are called in a proper manner.
 TEST(BlockProcessor, DISABLED_SubmoduleIntegration) {
   constexpr size_t kNumBlocks = 310;
-  constexpr size_t kNumRenderChannels = 1;
-  constexpr size_t kNumCaptureChannels = 1;
-
   Random random_generator(42U);
-  for (auto rate : {16000, 32000, 48000}) {
+  for (auto rate : {8000, 16000, 32000, 48000}) {
     SCOPED_TRACE(ProduceDebugText(rate));
     std::unique_ptr<testing::StrictMock<webrtc::test::MockRenderDelayBuffer>>
         render_delay_buffer_mock(
-            new StrictMock<webrtc::test::MockRenderDelayBuffer>(rate, 1));
+            new StrictMock<webrtc::test::MockRenderDelayBuffer>(rate));
     std::unique_ptr<
         ::testing::StrictMock<webrtc::test::MockRenderDelayController>>
         render_delay_controller_mock(
@@ -213,22 +174,17 @@
         .Times(kNumBlocks);
 
     std::unique_ptr<BlockProcessor> block_processor(BlockProcessor::Create(
-        EchoCanceller3Config(), rate, kNumRenderChannels, kNumCaptureChannels,
-        std::move(render_delay_buffer_mock),
+        EchoCanceller3Config(), rate, std::move(render_delay_buffer_mock),
         std::move(render_delay_controller_mock), std::move(echo_remover_mock)));
 
-    std::vector<std::vector<std::vector<float>>> render_block(
-        NumBandsForRate(rate),
-        std::vector<std::vector<float>>(kNumRenderChannels,
-                                        std::vector<float>(kBlockSize, 0.f)));
-    std::vector<std::vector<std::vector<float>>> capture_block(
-        NumBandsForRate(rate),
-        std::vector<std::vector<float>>(kNumCaptureChannels,
-                                        std::vector<float>(kBlockSize, 0.f)));
+    std::vector<std::vector<float>> render_block(
+        NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
+    std::vector<std::vector<float>> capture_block(
+        NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
     DelayBuffer<float> signal_delay_buffer(640);
     for (size_t k = 0; k < kNumBlocks; ++k) {
-      RandomizeSampleVector(&random_generator, render_block[0][0]);
-      signal_delay_buffer.Delay(render_block[0][0], capture_block[0][0]);
+      RandomizeSampleVector(&random_generator, render_block[0]);
+      signal_delay_buffer.Delay(render_block[0], capture_block[0]);
       block_processor->BufferRender(render_block);
       block_processor->ProcessCapture(false, false, &capture_block);
       block_processor->UpdateEchoLeakageStatus(false);
@@ -237,7 +193,7 @@
 }
 
 TEST(BlockProcessor, BasicSetupAndApiCalls) {
-  for (auto rate : {16000, 32000, 48000}) {
+  for (auto rate : {8000, 16000, 32000, 48000}) {
     SCOPED_TRACE(ProduceDebugText(rate));
     RunBasicSetupAndApiCallTest(rate, 1);
   }
@@ -251,21 +207,21 @@
 // TODO(gustaf): Re-enable the test once the issue with memory leaks during
 // DEATH tests on test bots has been fixed.
 TEST(BlockProcessor, DISABLED_VerifyRenderBlockSizeCheck) {
-  for (auto rate : {16000, 32000, 48000}) {
+  for (auto rate : {8000, 16000, 32000, 48000}) {
     SCOPED_TRACE(ProduceDebugText(rate));
     RunRenderBlockSizeVerificationTest(rate);
   }
 }
 
 TEST(BlockProcessor, VerifyCaptureBlockSizeCheck) {
-  for (auto rate : {16000, 32000, 48000}) {
+  for (auto rate : {8000, 16000, 32000, 48000}) {
     SCOPED_TRACE(ProduceDebugText(rate));
     RunCaptureBlockSizeVerificationTest(rate);
   }
 }
 
 TEST(BlockProcessor, VerifyRenderNumBandsCheck) {
-  for (auto rate : {16000, 32000, 48000}) {
+  for (auto rate : {8000, 16000, 32000, 48000}) {
     SCOPED_TRACE(ProduceDebugText(rate));
     RunRenderNumBandsVerificationTest(rate);
   }
@@ -274,7 +230,7 @@
 // TODO(peah): Verify the check for correct number of bands in the capture
 // signal.
 TEST(BlockProcessor, VerifyCaptureNumBandsCheck) {
-  for (auto rate : {16000, 32000, 48000}) {
+  for (auto rate : {8000, 16000, 32000, 48000}) {
     SCOPED_TRACE(ProduceDebugText(rate));
     RunCaptureNumBandsVerificationTest(rate);
   }
@@ -283,7 +239,7 @@
 // Verifiers that the verification for null ProcessCapture input works.
 TEST(BlockProcessor, NullProcessCaptureParameter) {
   EXPECT_DEATH(std::unique_ptr<BlockProcessor>(
-                   BlockProcessor::Create(EchoCanceller3Config(), 16000, 1, 1))
+                   BlockProcessor::Create(EchoCanceller3Config(), 8000))
                    ->ProcessCapture(false, false, nullptr),
                "");
 }
@@ -293,7 +249,7 @@
 // tests on test bots has been fixed.
 TEST(BlockProcessor, DISABLED_WrongSampleRate) {
   EXPECT_DEATH(std::unique_ptr<BlockProcessor>(
-                   BlockProcessor::Create(EchoCanceller3Config(), 8001, 1, 1)),
+                   BlockProcessor::Create(EchoCanceller3Config(), 8001)),
                "");
 }
 
diff --git a/modules/audio_processing/aec3/decimator_unittest.cc b/modules/audio_processing/aec3/decimator_unittest.cc
index 946089a..cf8de84 100644
--- a/modules/audio_processing/aec3/decimator_unittest.cc
+++ b/modules/audio_processing/aec3/decimator_unittest.cc
@@ -90,7 +90,7 @@
 TEST(Decimator, NoLeakageFromUpperFrequencies) {
   float input_power;
   float output_power;
-  for (auto rate : {16000, 32000, 48000}) {
+  for (auto rate : {8000, 16000, 32000, 48000}) {
     for (auto down_sampling_factor : kDownSamplingFactors) {
       ProduceDebugText(rate);
       ProduceDecimatedSinusoidalOutputPower(rate, down_sampling_factor,
diff --git a/modules/audio_processing/aec3/echo_audibility.cc b/modules/audio_processing/aec3/echo_audibility.cc
index 4154e53..e857a7e 100644
--- a/modules/audio_processing/aec3/echo_audibility.cc
+++ b/modules/audio_processing/aec3/echo_audibility.cc
@@ -97,7 +97,7 @@
   } else {
     for (int idx = render_block_write_prev_; idx != render_block_write_current;
          idx = block_buffer.IncIndex(idx)) {
-      auto block = block_buffer.buffer[idx][0][0];
+      auto block = block_buffer.buffer[idx][0];
       auto r = std::minmax_element(block.cbegin(), block.cend());
       float max_abs = std::max(std::fabs(*r.first), std::fabs(*r.second));
       if (max_abs < 10) {
diff --git a/modules/audio_processing/aec3/echo_canceller3.cc b/modules/audio_processing/aec3/echo_canceller3.cc
index cf953ae..c2ad56b 100644
--- a/modules/audio_processing/aec3/echo_canceller3.cc
+++ b/modules/audio_processing/aec3/echo_canceller3.cc
@@ -45,36 +45,27 @@
   return adjusted_cfg;
 }
 
-void FillSubFrameView(
-    AudioBuffer* frame,
-    size_t sub_frame_index,
-    std::vector<std::vector<rtc::ArrayView<float>>>* sub_frame_view) {
+void FillSubFrameView(AudioBuffer* frame,
+                      size_t sub_frame_index,
+                      std::vector<rtc::ArrayView<float>>* sub_frame_view) {
   RTC_DCHECK_GE(1, sub_frame_index);
   RTC_DCHECK_LE(0, sub_frame_index);
   RTC_DCHECK_EQ(frame->num_bands(), sub_frame_view->size());
-  RTC_DCHECK_EQ(frame->num_channels(), (*sub_frame_view)[0].size());
-  for (size_t band = 0; band < sub_frame_view->size(); ++band) {
-    for (size_t channel = 0; channel < (*sub_frame_view)[0].size(); ++channel) {
-      (*sub_frame_view)[band][channel] = rtc::ArrayView<float>(
-          &frame->split_bands(channel)[band][sub_frame_index * kSubFrameLength],
-          kSubFrameLength);
-    }
+  for (size_t k = 0; k < sub_frame_view->size(); ++k) {
+    (*sub_frame_view)[k] = rtc::ArrayView<float>(
+        &frame->split_bands(0)[k][sub_frame_index * kSubFrameLength],
+        kSubFrameLength);
   }
 }
 
-void FillSubFrameView(
-    std::vector<std::vector<std::vector<float>>>* frame,
-    size_t sub_frame_index,
-    std::vector<std::vector<rtc::ArrayView<float>>>* sub_frame_view) {
+void FillSubFrameView(std::vector<std::vector<float>>* frame,
+                      size_t sub_frame_index,
+                      std::vector<rtc::ArrayView<float>>* sub_frame_view) {
   RTC_DCHECK_GE(1, sub_frame_index);
   RTC_DCHECK_EQ(frame->size(), sub_frame_view->size());
-  RTC_DCHECK_EQ((*frame)[0].size(), (*sub_frame_view)[0].size());
-  for (size_t band = 0; band < frame->size(); ++band) {
-    for (size_t channel = 0; channel < (*frame)[band].size(); ++channel) {
-      (*sub_frame_view)[band][channel] = rtc::ArrayView<float>(
-          &(*frame)[band][channel][sub_frame_index * kSubFrameLength],
-          kSubFrameLength);
-    }
+  for (size_t k = 0; k < frame->size(); ++k) {
+    (*sub_frame_view)[k] = rtc::ArrayView<float>(
+        &(*frame)[k][sub_frame_index * kSubFrameLength], kSubFrameLength);
   }
 }
 
@@ -86,8 +77,8 @@
     FrameBlocker* capture_blocker,
     BlockFramer* output_framer,
     BlockProcessor* block_processor,
-    std::vector<std::vector<std::vector<float>>>* block,
-    std::vector<std::vector<rtc::ArrayView<float>>>* sub_frame_view) {
+    std::vector<std::vector<float>>* block,
+    std::vector<rtc::ArrayView<float>>* sub_frame_view) {
   FillSubFrameView(capture, sub_frame_index, sub_frame_view);
   capture_blocker->InsertSubFrameAndExtractBlock(*sub_frame_view, block);
   block_processor->ProcessCapture(level_change, saturated_microphone_signal,
@@ -101,7 +92,7 @@
     FrameBlocker* capture_blocker,
     BlockFramer* output_framer,
     BlockProcessor* block_processor,
-    std::vector<std::vector<std::vector<float>>>* block) {
+    std::vector<std::vector<float>>* block) {
   if (!capture_blocker->IsBlockAvailable()) {
     return;
   }
@@ -113,21 +104,20 @@
 }
 
 void BufferRenderFrameContent(
-    std::vector<std::vector<std::vector<float>>>* render_frame,
+    std::vector<std::vector<float>>* render_frame,
     size_t sub_frame_index,
     FrameBlocker* render_blocker,
     BlockProcessor* block_processor,
-    std::vector<std::vector<std::vector<float>>>* block,
-    std::vector<std::vector<rtc::ArrayView<float>>>* sub_frame_view) {
+    std::vector<std::vector<float>>* block,
+    std::vector<rtc::ArrayView<float>>* sub_frame_view) {
   FillSubFrameView(render_frame, sub_frame_index, sub_frame_view);
   render_blocker->InsertSubFrameAndExtractBlock(*sub_frame_view, block);
   block_processor->BufferRender(*block);
 }
 
-void BufferRemainingRenderFrameContent(
-    FrameBlocker* render_blocker,
-    BlockProcessor* block_processor,
-    std::vector<std::vector<std::vector<float>>>* block) {
+void BufferRemainingRenderFrameContent(FrameBlocker* render_blocker,
+                                       BlockProcessor* block_processor,
+                                       std::vector<std::vector<float>>* block) {
   if (!render_blocker->IsBlockAvailable()) {
     return;
   }
@@ -137,19 +127,14 @@
 
 void CopyBufferIntoFrame(const AudioBuffer& buffer,
                          size_t num_bands,
-                         size_t num_channels,
-                         std::vector<std::vector<std::vector<float>>>* frame) {
+                         size_t frame_length,
+                         std::vector<std::vector<float>>* frame) {
   RTC_DCHECK_EQ(num_bands, frame->size());
-  RTC_DCHECK_EQ(num_channels, (*frame)[0].size());
-  RTC_DCHECK_EQ(AudioBuffer::kSplitBandSize, (*frame)[0][0].size());
-  for (size_t band = 0; band < num_bands; ++band) {
-    for (size_t channel = 0; channel < num_channels; ++channel) {
-      rtc::ArrayView<const float> buffer_view(
-          &buffer.split_bands_const(channel)[band][0],
-          AudioBuffer::kSplitBandSize);
-      std::copy(buffer_view.begin(), buffer_view.end(),
-                (*frame)[band][channel].begin());
-    }
+  RTC_DCHECK_EQ(frame_length, (*frame)[0].size());
+  for (size_t k = 0; k < num_bands; ++k) {
+    rtc::ArrayView<const float> buffer_view(&buffer.split_bands_const(0)[k][0],
+                                            frame_length);
+    std::copy(buffer_view.begin(), buffer_view.end(), (*frame)[k].begin());
   }
 }
 
@@ -158,39 +143,40 @@
 class EchoCanceller3::RenderWriter {
  public:
   RenderWriter(ApmDataDumper* data_dumper,
-               SwapQueue<std::vector<std::vector<std::vector<float>>>,
+               SwapQueue<std::vector<std::vector<float>>,
                          Aec3RenderQueueItemVerifier>* render_transfer_queue,
-               size_t num_bands,
-               size_t num_channels);
+               int sample_rate_hz,
+               int frame_length,
+               int num_bands);
   ~RenderWriter();
   void Insert(const AudioBuffer& input);
 
  private:
   ApmDataDumper* data_dumper_;
-  const size_t num_bands_;
-  const size_t num_channels_;
+  const int sample_rate_hz_;
+  const size_t frame_length_;
+  const int num_bands_;
   HighPassFilter high_pass_filter_;
-  std::vector<std::vector<std::vector<float>>> render_queue_input_frame_;
-  SwapQueue<std::vector<std::vector<std::vector<float>>>,
-            Aec3RenderQueueItemVerifier>* render_transfer_queue_;
+  std::vector<std::vector<float>> render_queue_input_frame_;
+  SwapQueue<std::vector<std::vector<float>>, Aec3RenderQueueItemVerifier>*
+      render_transfer_queue_;
   RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderWriter);
 };
 
 EchoCanceller3::RenderWriter::RenderWriter(
     ApmDataDumper* data_dumper,
-    SwapQueue<std::vector<std::vector<std::vector<float>>>,
-              Aec3RenderQueueItemVerifier>* render_transfer_queue,
-    size_t num_bands,
-    size_t num_channels)
+    SwapQueue<std::vector<std::vector<float>>, Aec3RenderQueueItemVerifier>*
+        render_transfer_queue,
+    int sample_rate_hz,
+    int frame_length,
+    int num_bands)
     : data_dumper_(data_dumper),
+      sample_rate_hz_(sample_rate_hz),
+      frame_length_(frame_length),
       num_bands_(num_bands),
-      num_channels_(num_channels),
-      high_pass_filter_(num_channels),
-      render_queue_input_frame_(
-          num_bands_,
-          std::vector<std::vector<float>>(
-              num_channels_,
-              std::vector<float>(AudioBuffer::kSplitBandSize, 0.f))),
+      high_pass_filter_(1),
+      render_queue_input_frame_(num_bands_,
+                                std::vector<float>(frame_length_, 0.f)),
       render_transfer_queue_(render_transfer_queue) {
   RTC_DCHECK(data_dumper);
 }
@@ -199,21 +185,21 @@
 
 void EchoCanceller3::RenderWriter::Insert(const AudioBuffer& input) {
   RTC_DCHECK_EQ(1, input.num_channels());
-  RTC_DCHECK_EQ(AudioBuffer::kSplitBandSize, input.num_frames_per_band());
+  RTC_DCHECK_EQ(frame_length_, input.num_frames_per_band());
   RTC_DCHECK_EQ(num_bands_, input.num_bands());
 
   // TODO(bugs.webrtc.org/8759) Temporary work-around.
-  if (num_bands_ != input.num_bands())
+  if (num_bands_ != static_cast<int>(input.num_bands()))
     return;
 
-  data_dumper_->DumpWav("aec3_render_input", AudioBuffer::kSplitBandSize,
-                        &input.split_bands_const(0)[0][0], 16000, 1);
+  data_dumper_->DumpWav("aec3_render_input", frame_length_,
+                        &input.split_bands_const(0)[0][0],
+                        LowestBandRate(sample_rate_hz_), 1);
 
-  CopyBufferIntoFrame(input, num_bands_, num_channels_,
+  CopyBufferIntoFrame(input, num_bands_, frame_length_,
                       &render_queue_input_frame_);
-  for (size_t channel = 0; channel < num_channels_; ++channel) {
-    high_pass_filter_.Process(render_queue_input_frame_[0][channel]);
-  }
+
+  high_pass_filter_.Process(render_queue_input_frame_[0]);
 
   static_cast<void>(render_transfer_queue_->Insert(&render_queue_input_frame_));
 }
@@ -221,71 +207,43 @@
 int EchoCanceller3::instance_count_ = 0;
 
 EchoCanceller3::EchoCanceller3(const EchoCanceller3Config& config,
-                               int sample_rate_hz,
-                               size_t num_render_channels,
-                               size_t num_capture_channels)
-    : EchoCanceller3(AdjustConfig(config),
-                     sample_rate_hz,
-                     num_render_channels,
-                     num_capture_channels,
-                     std::unique_ptr<BlockProcessor>(
-                         BlockProcessor::Create(AdjustConfig(config),
-                                                sample_rate_hz,
-                                                num_render_channels,
-                                                num_capture_channels))) {}
+                               int sample_rate_hz)
+    : EchoCanceller3(
+          AdjustConfig(config),
+          sample_rate_hz,
+          std::unique_ptr<BlockProcessor>(
+              BlockProcessor::Create(AdjustConfig(config), sample_rate_hz))) {}
 EchoCanceller3::EchoCanceller3(const EchoCanceller3Config& config,
                                int sample_rate_hz,
-                               size_t num_render_channels,
-                               size_t num_capture_channels,
                                std::unique_ptr<BlockProcessor> block_processor)
     : data_dumper_(
           new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
       config_(config),
       sample_rate_hz_(sample_rate_hz),
       num_bands_(NumBandsForRate(sample_rate_hz_)),
-      num_render_channels_(num_render_channels),
-      num_capture_channels_(num_capture_channels),
-      output_framer_(num_bands_, num_capture_channels_),
-      capture_blocker_(num_bands_, num_capture_channels_),
-      render_blocker_(num_bands_, num_render_channels_),
+      frame_length_(rtc::CheckedDivExact(LowestBandRate(sample_rate_hz_), 100)),
+      output_framer_(num_bands_),
+      capture_blocker_(num_bands_),
+      render_blocker_(num_bands_),
       render_transfer_queue_(
           kRenderTransferQueueSizeFrames,
-          std::vector<std::vector<std::vector<float>>>(
-              num_bands_,
-              std::vector<std::vector<float>>(
-                  num_render_channels_,
-                  std::vector<float>(AudioBuffer::kSplitBandSize, 0.f))),
-          Aec3RenderQueueItemVerifier(num_bands_,
-                                      num_render_channels_,
-                                      AudioBuffer::kSplitBandSize)),
-      block_processor_(std::move(block_processor)),
-      render_queue_output_frame_(
-          num_bands_,
           std::vector<std::vector<float>>(
-              num_render_channels_,
-              std::vector<float>(AudioBuffer::kSplitBandSize, 0.f))),
-      render_block_(
-          num_bands_,
-          std::vector<std::vector<float>>(num_render_channels_,
-                                          std::vector<float>(kBlockSize, 0.f))),
-      capture_block_(
-          num_bands_,
-          std::vector<std::vector<float>>(num_capture_channels_,
-                                          std::vector<float>(kBlockSize, 0.f))),
-      render_sub_frame_view_(
-          num_bands_,
-          std::vector<rtc::ArrayView<float>>(num_render_channels_)),
-      capture_sub_frame_view_(
-          num_bands_,
-          std::vector<rtc::ArrayView<float>>(num_capture_channels_)),
+              num_bands_,
+              std::vector<float>(frame_length_, 0.f)),
+          Aec3RenderQueueItemVerifier(num_bands_, frame_length_)),
+      block_processor_(std::move(block_processor)),
+      render_queue_output_frame_(num_bands_,
+                                 std::vector<float>(frame_length_, 0.f)),
+      block_(num_bands_, std::vector<float>(kBlockSize, 0.f)),
+      sub_frame_view_(num_bands_),
       block_delay_buffer_(num_bands_,
-                          AudioBuffer::kSplitBandSize,
+                          frame_length_,
                           config_.delay.fixed_capture_delay_samples) {
   RTC_DCHECK(ValidFullBandRate(sample_rate_hz_));
 
-  render_writer_.reset(new RenderWriter(data_dumper_.get(),
-                                        &render_transfer_queue_, num_bands_,
-                                        num_render_channels_));
+  render_writer_.reset(
+      new RenderWriter(data_dumper_.get(), &render_transfer_queue_,
+                       sample_rate_hz_, frame_length_, num_bands_));
 
   RTC_DCHECK_EQ(num_bands_, std::max(sample_rate_hz_, 16000) / 16000);
   RTC_DCHECK_GE(kMaxNumBands, num_bands_);
@@ -295,7 +253,6 @@
 
 void EchoCanceller3::AnalyzeRender(const AudioBuffer& render) {
   RTC_DCHECK_RUNS_SERIALIZED(&render_race_checker_);
-  RTC_DCHECK_EQ(render.num_channels(), num_render_channels_);
   data_dumper_->DumpRaw("aec3_call_order",
                         static_cast<int>(EchoCanceller3ApiCall::kRender));
 
@@ -308,10 +265,10 @@
                         capture.channels_const()[0], sample_rate_hz_, 1);
 
   saturated_microphone_signal_ = false;
-  for (size_t channel = 0; channel < capture.num_channels(); ++channel) {
+  for (size_t k = 0; k < capture.num_channels(); ++k) {
     saturated_microphone_signal_ |=
         DetectSaturation(rtc::ArrayView<const float>(
-            capture.channels_const()[channel], capture.num_frames()));
+            capture.channels_const()[k], capture.num_frames()));
     if (saturated_microphone_signal_) {
       break;
     }
@@ -323,8 +280,7 @@
   RTC_DCHECK(capture);
   RTC_DCHECK_EQ(1u, capture->num_channels());
   RTC_DCHECK_EQ(num_bands_, capture->num_bands());
-  RTC_DCHECK_EQ(AudioBuffer::kSplitBandSize, capture->num_frames_per_band());
-  RTC_DCHECK_EQ(capture->num_channels(), num_capture_channels_);
+  RTC_DCHECK_EQ(frame_length_, capture->num_frames_per_band());
   data_dumper_->DumpRaw("aec3_call_order",
                         static_cast<int>(EchoCanceller3ApiCall::kCapture));
 
@@ -337,29 +293,32 @@
     block_delay_buffer_.DelaySignal(capture);
   }
 
-  rtc::ArrayView<float> capture_lower_band = rtc::ArrayView<float>(
-      &capture->split_bands(0)[0][0], AudioBuffer::kSplitBandSize);
+  rtc::ArrayView<float> capture_lower_band =
+      rtc::ArrayView<float>(&capture->split_bands(0)[0][0], frame_length_);
 
-  data_dumper_->DumpWav("aec3_capture_input", capture_lower_band, 16000, 1);
+  data_dumper_->DumpWav("aec3_capture_input", capture_lower_band,
+                        LowestBandRate(sample_rate_hz_), 1);
 
   EmptyRenderQueue();
 
-  ProcessCaptureFrameContent(capture, level_change,
-                             saturated_microphone_signal_, 0, &capture_blocker_,
-                             &output_framer_, block_processor_.get(),
-                             &capture_block_, &capture_sub_frame_view_);
+  ProcessCaptureFrameContent(
+      capture, level_change, saturated_microphone_signal_, 0, &capture_blocker_,
+      &output_framer_, block_processor_.get(), &block_, &sub_frame_view_);
 
-  ProcessCaptureFrameContent(capture, level_change,
-                             saturated_microphone_signal_, 1, &capture_blocker_,
-                             &output_framer_, block_processor_.get(),
-                             &capture_block_, &capture_sub_frame_view_);
+  if (sample_rate_hz_ != 8000) {
+    ProcessCaptureFrameContent(
+        capture, level_change, saturated_microphone_signal_, 1,
+        &capture_blocker_, &output_framer_, block_processor_.get(), &block_,
+        &sub_frame_view_);
+  }
 
   ProcessRemainingCaptureFrameContent(
       level_change, saturated_microphone_signal_, &capture_blocker_,
-      &output_framer_, block_processor_.get(), &capture_block_);
+      &output_framer_, block_processor_.get(), &block_);
 
-  data_dumper_->DumpWav("aec3_capture_output", AudioBuffer::kSplitBandSize,
-                        &capture->split_bands(0)[0][0], 16000, 1);
+  data_dumper_->DumpWav("aec3_capture_output", frame_length_,
+                        &capture->split_bands(0)[0][0],
+                        LowestBandRate(sample_rate_hz_), 1);
 }
 
 EchoControl::Metrics EchoCanceller3::GetMetrics() const {
@@ -383,15 +342,16 @@
     api_call_metrics_.ReportRenderCall();
 
     BufferRenderFrameContent(&render_queue_output_frame_, 0, &render_blocker_,
-                             block_processor_.get(), &render_block_,
-                             &render_sub_frame_view_);
+                             block_processor_.get(), &block_, &sub_frame_view_);
 
-    BufferRenderFrameContent(&render_queue_output_frame_, 1, &render_blocker_,
-                             block_processor_.get(), &render_block_,
-                             &render_sub_frame_view_);
+    if (sample_rate_hz_ != 8000) {
+      BufferRenderFrameContent(&render_queue_output_frame_, 1, &render_blocker_,
+                               block_processor_.get(), &block_,
+                               &sub_frame_view_);
+    }
 
     BufferRemainingRenderFrameContent(&render_blocker_, block_processor_.get(),
-                                      &render_block_);
+                                      &block_);
 
     frame_to_buffer =
         render_transfer_queue_.Remove(&render_queue_output_frame_);
diff --git a/modules/audio_processing/aec3/echo_canceller3.h b/modules/audio_processing/aec3/echo_canceller3.h
index 5b59674..d7dea80 100644
--- a/modules/audio_processing/aec3/echo_canceller3.h
+++ b/modules/audio_processing/aec3/echo_canceller3.h
@@ -27,6 +27,7 @@
 #include "modules/audio_processing/audio_buffer.h"
 #include "modules/audio_processing/logging/apm_data_dumper.h"
 #include "rtc_base/checks.h"
+#include "rtc_base/constructor_magic.h"
 #include "rtc_base/race_checker.h"
 #include "rtc_base/swap_queue.h"
 #include "rtc_base/thread_annotations.h"
@@ -37,33 +38,23 @@
 // queue.
 class Aec3RenderQueueItemVerifier {
  public:
-  Aec3RenderQueueItemVerifier(size_t num_bands,
-                              size_t num_channels,
-                              size_t frame_length)
-      : num_bands_(num_bands),
-        num_channels_(num_channels),
-        frame_length_(frame_length) {}
+  explicit Aec3RenderQueueItemVerifier(size_t num_bands, size_t frame_length)
+      : num_bands_(num_bands), frame_length_(frame_length) {}
 
-  bool operator()(const std::vector<std::vector<std::vector<float>>>& v) const {
+  bool operator()(const std::vector<std::vector<float>>& v) const {
     if (v.size() != num_bands_) {
       return false;
     }
-    for (const auto& band : v) {
-      if (band.size() != num_channels_) {
+    for (const auto& v_k : v) {
+      if (v_k.size() != frame_length_) {
         return false;
       }
-      for (const auto& channel : band) {
-        if (channel.size() != frame_length_) {
-          return false;
-        }
-      }
     }
     return true;
   }
 
  private:
   const size_t num_bands_;
-  const size_t num_channels_;
   const size_t frame_length_;
 };
 
@@ -82,20 +73,12 @@
 class EchoCanceller3 : public EchoControl {
  public:
   // Normal c-tor to use.
-  EchoCanceller3(const EchoCanceller3Config& config,
-                 int sample_rate_hz,
-                 size_t num_render_channels,
-                 size_t num_capture_channels);
+  EchoCanceller3(const EchoCanceller3Config& config, int sample_rate_hz);
   // Testing c-tor that is used only for testing purposes.
   EchoCanceller3(const EchoCanceller3Config& config,
                  int sample_rate_hz,
-                 size_t num_render_channels,
-                 size_t num_capture_channels,
                  std::unique_ptr<BlockProcessor> block_processor);
   ~EchoCanceller3() override;
-  EchoCanceller3(const EchoCanceller3&) = delete;
-  EchoCanceller3& operator=(const EchoCanceller3&) = delete;
-
   // Analyzes and stores an internal copy of the split-band domain render
   // signal.
   void AnalyzeRender(AudioBuffer* render) override { AnalyzeRender(*render); }
@@ -145,30 +128,25 @@
   const EchoCanceller3Config config_;
   const int sample_rate_hz_;
   const int num_bands_;
-  const size_t num_render_channels_;
-  const size_t num_capture_channels_;
+  const size_t frame_length_;
   BlockFramer output_framer_ RTC_GUARDED_BY(capture_race_checker_);
   FrameBlocker capture_blocker_ RTC_GUARDED_BY(capture_race_checker_);
   FrameBlocker render_blocker_ RTC_GUARDED_BY(capture_race_checker_);
-  SwapQueue<std::vector<std::vector<std::vector<float>>>,
-            Aec3RenderQueueItemVerifier>
+  SwapQueue<std::vector<std::vector<float>>, Aec3RenderQueueItemVerifier>
       render_transfer_queue_;
   std::unique_ptr<BlockProcessor> block_processor_
       RTC_GUARDED_BY(capture_race_checker_);
-  std::vector<std::vector<std::vector<float>>> render_queue_output_frame_
+  std::vector<std::vector<float>> render_queue_output_frame_
       RTC_GUARDED_BY(capture_race_checker_);
   bool saturated_microphone_signal_ RTC_GUARDED_BY(capture_race_checker_) =
       false;
-  std::vector<std::vector<std::vector<float>>> render_block_
-      RTC_GUARDED_BY(capture_race_checker_);
-  std::vector<std::vector<std::vector<float>>> capture_block_
-      RTC_GUARDED_BY(capture_race_checker_);
-  std::vector<std::vector<rtc::ArrayView<float>>> render_sub_frame_view_
-      RTC_GUARDED_BY(capture_race_checker_);
-  std::vector<std::vector<rtc::ArrayView<float>>> capture_sub_frame_view_
+  std::vector<std::vector<float>> block_ RTC_GUARDED_BY(capture_race_checker_);
+  std::vector<rtc::ArrayView<float>> sub_frame_view_
       RTC_GUARDED_BY(capture_race_checker_);
   BlockDelayBuffer block_delay_buffer_ RTC_GUARDED_BY(capture_race_checker_);
   ApiCallJitterMetrics api_call_metrics_ RTC_GUARDED_BY(capture_race_checker_);
+
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(EchoCanceller3);
 };
 }  // namespace webrtc
 
diff --git a/modules/audio_processing/aec3/echo_canceller3_unittest.cc b/modules/audio_processing/aec3/echo_canceller3_unittest.cc
index a2f3367..a29b779 100644
--- a/modules/audio_processing/aec3/echo_canceller3_unittest.cc
+++ b/modules/audio_processing/aec3/echo_canceller3_unittest.cc
@@ -109,13 +109,12 @@
   explicit CaptureTransportVerificationProcessor(size_t num_bands) {}
   ~CaptureTransportVerificationProcessor() override = default;
 
-  void ProcessCapture(
-      bool level_change,
-      bool saturated_microphone_signal,
-      std::vector<std::vector<std::vector<float>>>* capture_block) override {}
+  void ProcessCapture(bool level_change,
+                      bool saturated_microphone_signal,
+                      std::vector<std::vector<float>>* capture_block) override {
+  }
 
-  void BufferRender(
-      const std::vector<std::vector<std::vector<float>>>& block) override {}
+  void BufferRender(const std::vector<std::vector<float>>& block) override {}
 
   void UpdateEchoLeakageStatus(bool leakage_detected) override {}
 
@@ -134,18 +133,16 @@
   explicit RenderTransportVerificationProcessor(size_t num_bands) {}
   ~RenderTransportVerificationProcessor() override = default;
 
-  void ProcessCapture(
-      bool level_change,
-      bool saturated_microphone_signal,
-      std::vector<std::vector<std::vector<float>>>* capture_block) override {
-    std::vector<std::vector<std::vector<float>>> render_block =
+  void ProcessCapture(bool level_change,
+                      bool saturated_microphone_signal,
+                      std::vector<std::vector<float>>* capture_block) override {
+    std::vector<std::vector<float>> render_block =
         received_render_blocks_.front();
     received_render_blocks_.pop_front();
     capture_block->swap(render_block);
   }
 
-  void BufferRender(
-      const std::vector<std::vector<std::vector<float>>>& block) override {
+  void BufferRender(const std::vector<std::vector<float>>& block) override {
     received_render_blocks_.push_back(block);
   }
 
@@ -156,8 +153,7 @@
   void SetAudioBufferDelay(size_t delay_ms) override {}
 
  private:
-  std::deque<std::vector<std::vector<std::vector<float>>>>
-      received_render_blocks_;
+  std::deque<std::vector<std::vector<float>>> received_render_blocks_;
   RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderTransportVerificationProcessor);
 };
 
@@ -166,7 +162,7 @@
   explicit EchoCanceller3Tester(int sample_rate_hz)
       : sample_rate_hz_(sample_rate_hz),
         num_bands_(NumBandsForRate(sample_rate_hz_)),
-        frame_length_(160),
+        frame_length_(sample_rate_hz_ == 8000 ? 80 : 160),
         fullband_frame_length_(rtc::CheckedDivExact(sample_rate_hz_, 100)),
         capture_buffer_(fullband_frame_length_ * 100,
                         1,
@@ -186,7 +182,7 @@
   // output.
   void RunCaptureTransportVerificationTest() {
     EchoCanceller3 aec3(
-        EchoCanceller3Config(), sample_rate_hz_, 1, 1,
+        EchoCanceller3Config(), sample_rate_hz_,
         std::unique_ptr<BlockProcessor>(
             new CaptureTransportVerificationProcessor(num_bands_)));
 
@@ -211,7 +207,7 @@
   // block processor.
   void RunRenderTransportVerificationTest() {
     EchoCanceller3 aec3(
-        EchoCanceller3Config(), sample_rate_hz_, 1, 1,
+        EchoCanceller3Config(), sample_rate_hz_,
         std::unique_ptr<BlockProcessor>(
             new RenderTransportVerificationProcessor(num_bands_)));
 
@@ -255,34 +251,37 @@
 
   void RunEchoPathChangeVerificationTest(
       EchoPathChangeTestVariant echo_path_change_test_variant) {
-    constexpr size_t kNumFullBlocksPerFrame = 160 / kBlockSize;
-    constexpr size_t kExpectedNumBlocksToProcess =
-        (kNumFramesToProcess * 160) / kBlockSize;
+    const size_t num_full_blocks_per_frame =
+        rtc::CheckedDivExact(LowestBandRate(sample_rate_hz_), 100) / kBlockSize;
+    const size_t expected_num_block_to_process =
+        (kNumFramesToProcess *
+         rtc::CheckedDivExact(LowestBandRate(sample_rate_hz_), 100)) /
+        kBlockSize;
     std::unique_ptr<testing::StrictMock<webrtc::test::MockBlockProcessor>>
         block_processor_mock(
             new StrictMock<webrtc::test::MockBlockProcessor>());
     EXPECT_CALL(*block_processor_mock, BufferRender(_))
-        .Times(kExpectedNumBlocksToProcess);
+        .Times(expected_num_block_to_process);
     EXPECT_CALL(*block_processor_mock, UpdateEchoLeakageStatus(_)).Times(0);
 
     switch (echo_path_change_test_variant) {
       case EchoPathChangeTestVariant::kNone:
         EXPECT_CALL(*block_processor_mock, ProcessCapture(false, _, _))
-            .Times(kExpectedNumBlocksToProcess);
+            .Times(expected_num_block_to_process);
         break;
       case EchoPathChangeTestVariant::kOneSticky:
         EXPECT_CALL(*block_processor_mock, ProcessCapture(true, _, _))
-            .Times(kExpectedNumBlocksToProcess);
+            .Times(expected_num_block_to_process);
         break;
       case EchoPathChangeTestVariant::kOneNonSticky:
         EXPECT_CALL(*block_processor_mock, ProcessCapture(true, _, _))
-            .Times(kNumFullBlocksPerFrame);
+            .Times(num_full_blocks_per_frame);
         EXPECT_CALL(*block_processor_mock, ProcessCapture(false, _, _))
-            .Times(kExpectedNumBlocksToProcess - kNumFullBlocksPerFrame);
+            .Times(expected_num_block_to_process - num_full_blocks_per_frame);
         break;
     }
 
-    EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_, 1, 1,
+    EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_,
                         std::move(block_processor_mock));
 
     for (size_t frame_index = 0; frame_index < kNumFramesToProcess;
@@ -331,15 +330,17 @@
 
   void RunEchoLeakageVerificationTest(
       EchoLeakageTestVariant leakage_report_variant) {
-    constexpr size_t kExpectedNumBlocksToProcess =
-        (kNumFramesToProcess * 160) / kBlockSize;
+    const size_t expected_num_block_to_process =
+        (kNumFramesToProcess *
+         rtc::CheckedDivExact(LowestBandRate(sample_rate_hz_), 100)) /
+        kBlockSize;
     std::unique_ptr<testing::StrictMock<webrtc::test::MockBlockProcessor>>
         block_processor_mock(
             new StrictMock<webrtc::test::MockBlockProcessor>());
     EXPECT_CALL(*block_processor_mock, BufferRender(_))
-        .Times(kExpectedNumBlocksToProcess);
+        .Times(expected_num_block_to_process);
     EXPECT_CALL(*block_processor_mock, ProcessCapture(_, _, _))
-        .Times(kExpectedNumBlocksToProcess);
+        .Times(expected_num_block_to_process);
 
     switch (leakage_report_variant) {
       case EchoLeakageTestVariant::kNone:
@@ -362,7 +363,7 @@
       } break;
     }
 
-    EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_, 1, 1,
+    EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_,
                         std::move(block_processor_mock));
 
     for (size_t frame_index = 0; frame_index < kNumFramesToProcess;
@@ -417,38 +418,41 @@
 
   void RunCaptureSaturationVerificationTest(
       SaturationTestVariant saturation_variant) {
-    const size_t kNumFullBlocksPerFrame = 160 / kBlockSize;
-    const size_t kExpectedNumBlocksToProcess =
-        (kNumFramesToProcess * 160) / kBlockSize;
+    const size_t num_full_blocks_per_frame =
+        rtc::CheckedDivExact(LowestBandRate(sample_rate_hz_), 100) / kBlockSize;
+    const size_t expected_num_block_to_process =
+        (kNumFramesToProcess *
+         rtc::CheckedDivExact(LowestBandRate(sample_rate_hz_), 100)) /
+        kBlockSize;
     std::unique_ptr<testing::StrictMock<webrtc::test::MockBlockProcessor>>
         block_processor_mock(
             new StrictMock<webrtc::test::MockBlockProcessor>());
     EXPECT_CALL(*block_processor_mock, BufferRender(_))
-        .Times(kExpectedNumBlocksToProcess);
+        .Times(expected_num_block_to_process);
     EXPECT_CALL(*block_processor_mock, UpdateEchoLeakageStatus(_)).Times(0);
 
     switch (saturation_variant) {
       case SaturationTestVariant::kNone:
         EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _))
-            .Times(kExpectedNumBlocksToProcess);
+            .Times(expected_num_block_to_process);
         break;
       case SaturationTestVariant::kOneNegative: {
         ::testing::InSequence s;
         EXPECT_CALL(*block_processor_mock, ProcessCapture(_, true, _))
-            .Times(kNumFullBlocksPerFrame);
+            .Times(num_full_blocks_per_frame);
         EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _))
-            .Times(kExpectedNumBlocksToProcess - kNumFullBlocksPerFrame);
+            .Times(expected_num_block_to_process - num_full_blocks_per_frame);
       } break;
       case SaturationTestVariant::kOnePositive: {
         ::testing::InSequence s;
         EXPECT_CALL(*block_processor_mock, ProcessCapture(_, true, _))
-            .Times(kNumFullBlocksPerFrame);
+            .Times(num_full_blocks_per_frame);
         EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _))
-            .Times(kExpectedNumBlocksToProcess - kNumFullBlocksPerFrame);
+            .Times(expected_num_block_to_process - num_full_blocks_per_frame);
       } break;
     }
 
-    EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_, 1, 1,
+    EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_,
                         std::move(block_processor_mock));
     for (size_t frame_index = 0; frame_index < kNumFramesToProcess;
          ++frame_index) {
@@ -488,7 +492,7 @@
   void RunRenderSwapQueueVerificationTest() {
     const EchoCanceller3Config config;
     EchoCanceller3 aec3(
-        config, sample_rate_hz_, 1, 1,
+        config, sample_rate_hz_,
         std::unique_ptr<BlockProcessor>(
             new RenderTransportVerificationProcessor(num_bands_)));
 
@@ -538,7 +542,7 @@
   // This test verifies that a buffer overrun in the render swapqueue is
   // properly reported.
   void RunRenderPipelineSwapQueueOverrunReturnValueTest() {
-    EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_, 1, 1);
+    EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_);
 
     constexpr size_t kRenderTransferQueueSize = 30;
     for (size_t k = 0; k < 2; ++k) {
@@ -563,7 +567,7 @@
     // Set aec3_sample_rate_hz to be different from sample_rate_hz_ in such a
     // way that the number of bands for the rates are different.
     const int aec3_sample_rate_hz = sample_rate_hz_ == 48000 ? 32000 : 48000;
-    EchoCanceller3 aec3(EchoCanceller3Config(), aec3_sample_rate_hz, 1, 1);
+    EchoCanceller3 aec3(EchoCanceller3Config(), aec3_sample_rate_hz);
     PopulateInputFrame(frame_length_, 0, &render_buffer_.channels_f()[0][0], 0);
 
     EXPECT_DEATH(aec3.AnalyzeRender(&render_buffer_), "");
@@ -576,12 +580,43 @@
     // Set aec3_sample_rate_hz to be different from sample_rate_hz_ in such a
     // way that the number of bands for the rates are different.
     const int aec3_sample_rate_hz = sample_rate_hz_ == 48000 ? 32000 : 48000;
-    EchoCanceller3 aec3(EchoCanceller3Config(), aec3_sample_rate_hz, 1, 1);
+    EchoCanceller3 aec3(EchoCanceller3Config(), aec3_sample_rate_hz);
     PopulateInputFrame(frame_length_, num_bands_, 0,
                        &capture_buffer_.split_bands_f(0)[0], 100);
     EXPECT_DEATH(aec3.ProcessCapture(&capture_buffer_, false), "");
   }
 
+  // Verifies the that the check for the frame length in the AnalyzeRender input
+  // is correct by adjusting the sample rates of EchoCanceller3 and the input
+  // AudioBuffer to have a different frame lengths.
+  void RunAnalyzeRenderFrameLengthCheckVerification() {
+    // Set aec3_sample_rate_hz to be different from sample_rate_hz_ in such a
+    // way that the band frame lengths are different.
+    const int aec3_sample_rate_hz = sample_rate_hz_ == 8000 ? 16000 : 8000;
+    EchoCanceller3 aec3(EchoCanceller3Config(), aec3_sample_rate_hz);
+
+    OptionalBandSplit();
+    PopulateInputFrame(frame_length_, 0, &render_buffer_.channels_f()[0][0], 0);
+
+    EXPECT_DEATH(aec3.AnalyzeRender(&render_buffer_), "");
+  }
+
+  // Verifies the that the check for the frame length in the AnalyzeRender input
+  // is correct by adjusting the sample rates of EchoCanceller3 and the input
+  // AudioBuffer to have a different frame lengths.
+  void RunProcessCaptureFrameLengthCheckVerification() {
+    // Set aec3_sample_rate_hz to be different from sample_rate_hz_ in such a
+    // way that the band frame lengths are different.
+    const int aec3_sample_rate_hz = sample_rate_hz_ == 8000 ? 16000 : 8000;
+    EchoCanceller3 aec3(EchoCanceller3Config(), aec3_sample_rate_hz);
+
+    OptionalBandSplit();
+    PopulateInputFrame(frame_length_, num_bands_, 0,
+                       &capture_buffer_.split_bands_f(0)[0], 100);
+
+    EXPECT_DEATH(aec3.ProcessCapture(&capture_buffer_, false), "");
+  }
+
 #endif
 
  private:
@@ -618,25 +653,28 @@
 }  // namespace
 
 TEST(EchoCanceller3Buffering, CaptureBitexactness) {
-  for (auto rate : {16000, 32000, 48000}) {
+  for (auto rate : {8000, 16000, 32000, 48000}) {
     SCOPED_TRACE(ProduceDebugText(rate));
     EchoCanceller3Tester(rate).RunCaptureTransportVerificationTest();
   }
 }
 
 TEST(EchoCanceller3Buffering, RenderBitexactness) {
-  for (auto rate : {16000, 32000, 48000}) {
+  for (auto rate : {8000, 16000, 32000, 48000}) {
     SCOPED_TRACE(ProduceDebugText(rate));
     EchoCanceller3Tester(rate).RunRenderTransportVerificationTest();
   }
 }
 
 TEST(EchoCanceller3Buffering, RenderSwapQueue) {
-  EchoCanceller3Tester(16000).RunRenderSwapQueueVerificationTest();
+  for (auto rate : {8000, 16000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    EchoCanceller3Tester(rate).RunRenderSwapQueueVerificationTest();
+  }
 }
 
 TEST(EchoCanceller3Buffering, RenderSwapQueueOverrunReturnValue) {
-  for (auto rate : {16000, 32000, 48000}) {
+  for (auto rate : {8000, 16000, 32000, 48000}) {
     SCOPED_TRACE(ProduceDebugText(rate));
     EchoCanceller3Tester(rate)
         .RunRenderPipelineSwapQueueOverrunReturnValueTest();
@@ -647,7 +685,7 @@
   auto variants = {EchoCanceller3Tester::SaturationTestVariant::kNone,
                    EchoCanceller3Tester::SaturationTestVariant::kOneNegative,
                    EchoCanceller3Tester::SaturationTestVariant::kOnePositive};
-  for (auto rate : {16000, 32000, 48000}) {
+  for (auto rate : {8000, 16000, 32000, 48000}) {
     for (auto variant : variants) {
       SCOPED_TRACE(ProduceDebugText(rate, static_cast<int>(variant)));
       EchoCanceller3Tester(rate).RunCaptureSaturationVerificationTest(variant);
@@ -660,7 +698,7 @@
       EchoCanceller3Tester::EchoPathChangeTestVariant::kNone,
       EchoCanceller3Tester::EchoPathChangeTestVariant::kOneSticky,
       EchoCanceller3Tester::EchoPathChangeTestVariant::kOneNonSticky};
-  for (auto rate : {16000, 32000, 48000}) {
+  for (auto rate : {8000, 16000, 32000, 48000}) {
     for (auto variant : variants) {
       SCOPED_TRACE(ProduceDebugText(rate, static_cast<int>(variant)));
       EchoCanceller3Tester(rate).RunEchoPathChangeVerificationTest(variant);
@@ -674,7 +712,7 @@
       EchoCanceller3Tester::EchoLeakageTestVariant::kFalseSticky,
       EchoCanceller3Tester::EchoLeakageTestVariant::kTrueSticky,
       EchoCanceller3Tester::EchoLeakageTestVariant::kTrueNonSticky};
-  for (auto rate : {16000, 32000, 48000}) {
+  for (auto rate : {8000, 16000, 32000, 48000}) {
     for (auto variant : variants) {
       SCOPED_TRACE(ProduceDebugText(rate, static_cast<int>(variant)));
       EchoCanceller3Tester(rate).RunEchoLeakageVerificationTest(variant);
@@ -685,16 +723,33 @@
 #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
 
 TEST(EchoCanceller3InputCheck, WrongCaptureNumBandsCheckVerification) {
-  for (auto rate : {16000, 32000, 48000}) {
+  for (auto rate : {8000, 16000, 32000, 48000}) {
     SCOPED_TRACE(ProduceDebugText(rate));
     EchoCanceller3Tester(rate).RunProcessCaptureNumBandsCheckVerification();
   }
 }
 
+// TODO(peah): Re-enable the test once the issue with memory leaks during DEATH
+// tests on test bots has been fixed.
+TEST(EchoCanceller3InputCheck,
+     DISABLED_WrongRenderFrameLengthCheckVerification) {
+  for (auto rate : {8000, 16000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    EchoCanceller3Tester(rate).RunAnalyzeRenderFrameLengthCheckVerification();
+  }
+}
+
+TEST(EchoCanceller3InputCheck, WrongCaptureFrameLengthCheckVerification) {
+  for (auto rate : {8000, 16000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    EchoCanceller3Tester(rate).RunProcessCaptureFrameLengthCheckVerification();
+  }
+}
+
 // Verifiers that the verification for null input to the capture processing api
 // call works.
 TEST(EchoCanceller3InputCheck, NullCaptureProcessingParameter) {
-  EXPECT_DEATH(EchoCanceller3(EchoCanceller3Config(), 16000, 1, 1)
+  EXPECT_DEATH(EchoCanceller3(EchoCanceller3Config(), 16000)
                    .ProcessCapture(nullptr, false),
                "");
 }
@@ -704,7 +759,7 @@
 // tests on test bots has been fixed.
 TEST(EchoCanceller3InputCheck, DISABLED_WrongSampleRate) {
   ApmDataDumper data_dumper(0);
-  EXPECT_DEATH(EchoCanceller3(EchoCanceller3Config(), 8001, 1, 1), "");
+  EXPECT_DEATH(EchoCanceller3(EchoCanceller3Config(), 8001), "");
 }
 
 #endif
diff --git a/modules/audio_processing/aec3/echo_path_delay_estimator_unittest.cc b/modules/audio_processing/aec3/echo_path_delay_estimator_unittest.cc
index 9a1bf44..ddf6bc5 100644
--- a/modules/audio_processing/aec3/echo_path_delay_estimator_unittest.cc
+++ b/modules/audio_processing/aec3/echo_path_delay_estimator_unittest.cc
@@ -36,17 +36,12 @@
 
 // Verifies that the basic API calls work.
 TEST(EchoPathDelayEstimator, BasicApiCalls) {
-  constexpr size_t kNumChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
   ApmDataDumper data_dumper(0);
   EchoCanceller3Config config;
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(config, kSampleRateHz, kNumChannels));
+      RenderDelayBuffer::Create(config, 48000));
   EchoPathDelayEstimator estimator(&data_dumper, config);
-  std::vector<std::vector<std::vector<float>>> render(
-      kNumBands, std::vector<std::vector<float>>(
-                     kNumChannels, std::vector<float>(kBlockSize)));
+  std::vector<std::vector<float>> render(3, std::vector<float>(kBlockSize));
   std::vector<float> capture(kBlockSize);
   for (size_t k = 0; k < 100; ++k) {
     render_delay_buffer->Insert(render);
@@ -58,14 +53,8 @@
 // Verifies that the delay estimator produces correct delay for artificially
 // delayed signals.
 TEST(EchoPathDelayEstimator, DelayEstimation) {
-  constexpr size_t kNumChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
-
   Random random_generator(42U);
-  std::vector<std::vector<std::vector<float>>> render(
-      kNumBands, std::vector<std::vector<float>>(
-                     kNumChannels, std::vector<float>(kBlockSize)));
+  std::vector<std::vector<float>> render(3, std::vector<float>(kBlockSize));
   std::vector<float> capture(kBlockSize);
   ApmDataDumper data_dumper(0);
   constexpr size_t kDownSamplingFactors[] = {2, 4, 8};
@@ -76,14 +65,14 @@
     for (size_t delay_samples : {30, 64, 150, 200, 800, 4000}) {
       SCOPED_TRACE(ProduceDebugText(delay_samples, down_sampling_factor));
       std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-          RenderDelayBuffer::Create(config, kSampleRateHz, kNumChannels));
+          RenderDelayBuffer::Create(config, 48000));
       DelayBuffer<float> signal_delay_buffer(delay_samples);
       EchoPathDelayEstimator estimator(&data_dumper, config);
 
       absl::optional<DelayEstimate> estimated_delay_samples;
       for (size_t k = 0; k < (500 + (delay_samples) / kBlockSize); ++k) {
-        RandomizeSampleVector(&random_generator, render[0][0]);
-        signal_delay_buffer.Delay(render[0][0], capture);
+        RandomizeSampleVector(&random_generator, render[0]);
+        signal_delay_buffer.Delay(render[0], capture);
         render_delay_buffer->Insert(render);
 
         if (k == 0) {
@@ -117,26 +106,20 @@
 // Verifies that the delay estimator does not produce delay estimates for render
 // signals of low level.
 TEST(EchoPathDelayEstimator, NoDelayEstimatesForLowLevelRenderSignals) {
-  constexpr size_t kNumChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
   Random random_generator(42U);
   EchoCanceller3Config config;
-  std::vector<std::vector<std::vector<float>>> render(
-      kNumBands, std::vector<std::vector<float>>(
-                     kNumChannels, std::vector<float>(kBlockSize)));
+  std::vector<std::vector<float>> render(3, std::vector<float>(kBlockSize));
   std::vector<float> capture(kBlockSize);
   ApmDataDumper data_dumper(0);
   EchoPathDelayEstimator estimator(&data_dumper, config);
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(EchoCanceller3Config(), kSampleRateHz,
-                                kNumChannels));
+      RenderDelayBuffer::Create(EchoCanceller3Config(), 48000));
   for (size_t k = 0; k < 100; ++k) {
-    RandomizeSampleVector(&random_generator, render[0][0]);
-    for (auto& render_k : render[0][0]) {
+    RandomizeSampleVector(&random_generator, render[0]);
+    for (auto& render_k : render[0]) {
       render_k *= 100.f / 32767.f;
     }
-    std::copy(render[0][0].begin(), render[0][0].end(), capture.begin());
+    std::copy(render[0].begin(), render[0].end(), capture.begin());
     render_delay_buffer->Insert(render);
     render_delay_buffer->PrepareCaptureProcessing();
     EXPECT_FALSE(estimator.EstimateDelay(
@@ -154,7 +137,7 @@
   EchoCanceller3Config config;
   EchoPathDelayEstimator estimator(&data_dumper, config);
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(config, 48000, 1));
+      RenderDelayBuffer::Create(config, 48000));
   std::vector<float> capture(kBlockSize);
   EXPECT_DEATH(estimator.EstimateDelay(
                    render_delay_buffer->GetDownsampledRenderBuffer(), capture),
@@ -169,7 +152,7 @@
   EchoCanceller3Config config;
   EchoPathDelayEstimator estimator(&data_dumper, config);
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(config, 48000, 1));
+      RenderDelayBuffer::Create(config, 48000));
   std::vector<float> capture(std::vector<float>(kBlockSize - 1));
   EXPECT_DEATH(estimator.EstimateDelay(
                    render_delay_buffer->GetDownsampledRenderBuffer(), capture),
diff --git a/modules/audio_processing/aec3/echo_remover.cc b/modules/audio_processing/aec3/echo_remover.cc
index 60538d6..c7e7f7c 100644
--- a/modules/audio_processing/aec3/echo_remover.cc
+++ b/modules/audio_processing/aec3/echo_remover.cc
@@ -84,10 +84,7 @@
 // Class for removing the echo from the capture signal.
 class EchoRemoverImpl final : public EchoRemover {
  public:
-  EchoRemoverImpl(const EchoCanceller3Config& config,
-                  int sample_rate_hz,
-                  size_t num_render_channels,
-                  size_t num_capture_channels);
+  EchoRemoverImpl(const EchoCanceller3Config& config, int sample_rate_hz);
   ~EchoRemoverImpl() override;
 
   void GetMetrics(EchoControl::Metrics* metrics) const override;
@@ -95,12 +92,11 @@
   // Removes the echo from a block of samples from the capture signal. The
   // supplied render signal is assumed to be pre-aligned with the capture
   // signal.
-  void ProcessCapture(
-      EchoPathVariability echo_path_variability,
-      bool capture_signal_saturation,
-      const absl::optional<DelayEstimate>& external_delay,
-      RenderBuffer* render_buffer,
-      std::vector<std::vector<std::vector<float>>>* capture) override;
+  void ProcessCapture(EchoPathVariability echo_path_variability,
+                      bool capture_signal_saturation,
+                      const absl::optional<DelayEstimate>& external_delay,
+                      RenderBuffer* render_buffer,
+                      std::vector<std::vector<float>>* capture) override;
 
   // Updates the status on whether echo leakage is detected in the output of the
   // echo remover.
@@ -121,8 +117,6 @@
   std::unique_ptr<ApmDataDumper> data_dumper_;
   const Aec3Optimization optimization_;
   const int sample_rate_hz_;
-  const size_t num_render_channels_;
-  const size_t num_capture_channels_;
   const bool use_shadow_filter_output_;
   Subtractor subtractor_;
   SuppressionGain suppression_gain_;
@@ -147,17 +141,13 @@
 int EchoRemoverImpl::instance_count_ = 0;
 
 EchoRemoverImpl::EchoRemoverImpl(const EchoCanceller3Config& config,
-                                 int sample_rate_hz,
-                                 size_t num_render_channels,
-                                 size_t num_capture_channels)
+                                 int sample_rate_hz)
     : config_(config),
       fft_(),
       data_dumper_(
           new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
       optimization_(DetectOptimization()),
       sample_rate_hz_(sample_rate_hz),
-      num_render_channels_(num_render_channels),
-      num_capture_channels_(num_capture_channels),
       use_shadow_filter_output_(
           config_.filter.enable_shadow_filter_output_usage),
       subtractor_(config, data_dumper_.get(), optimization_),
@@ -171,8 +161,6 @@
   x_old_.fill(0.f);
   y_old_.fill(0.f);
   e_old_.fill(0.f);
-  (void)num_render_channels_;
-  (void)num_capture_channels_;
 }
 
 EchoRemoverImpl::~EchoRemoverImpl() = default;
@@ -189,26 +177,23 @@
     bool capture_signal_saturation,
     const absl::optional<DelayEstimate>& external_delay,
     RenderBuffer* render_buffer,
-    std::vector<std::vector<std::vector<float>>>* capture) {
+    std::vector<std::vector<float>>* capture) {
   ++block_counter_;
-  const std::vector<std::vector<std::vector<float>>>& x =
-      render_buffer->Block(0);
-  std::vector<std::vector<std::vector<float>>>* y = capture;
+  const std::vector<std::vector<float>>& x = render_buffer->Block(0);
+  std::vector<std::vector<float>>* y = capture;
   RTC_DCHECK(render_buffer);
   RTC_DCHECK(y);
   RTC_DCHECK_EQ(x.size(), NumBandsForRate(sample_rate_hz_));
   RTC_DCHECK_EQ(y->size(), NumBandsForRate(sample_rate_hz_));
-  RTC_DCHECK_EQ(x[0].size(), num_render_channels_);
-  RTC_DCHECK_EQ((*y)[0].size(), num_capture_channels_);
-  RTC_DCHECK_EQ(x[0][0].size(), kBlockSize);
-  RTC_DCHECK_EQ((*y)[0][0].size(), kBlockSize);
-  const std::vector<float>& x0 = x[0][0];
-  std::vector<float>& y0 = (*y)[0][0];
+  RTC_DCHECK_EQ(x[0].size(), kBlockSize);
+  RTC_DCHECK_EQ((*y)[0].size(), kBlockSize);
+  const std::vector<float>& x0 = x[0];
+  std::vector<float>& y0 = (*y)[0];
 
   data_dumper_->DumpWav("aec3_echo_remover_capture_input", kBlockSize, &y0[0],
-                        16000, 1);
+                        LowestBandRate(sample_rate_hz_), 1);
   data_dumper_->DumpWav("aec3_echo_remover_render_input", kBlockSize, &x0[0],
-                        16000, 1);
+                        LowestBandRate(sample_rate_hz_), 1);
   data_dumper_->DumpRaw("aec3_echo_remover_capture_input", y0);
   data_dumper_->DumpRaw("aec3_echo_remover_render_input", x0);
 
@@ -279,7 +264,8 @@
                     subtractor_output, y0);
 
   // Choose the linear output.
-  data_dumper_->DumpWav("aec3_output_linear2", kBlockSize, &e[0], 16000, 1);
+  data_dumper_->DumpWav("aec3_output_linear2", kBlockSize, &e[0],
+                        LowestBandRate(sample_rate_hz_), 1);
   if (aec_state_.UseLinearFilterOutput()) {
     if (!linear_filter_output_last_selected_) {
       SignalTransition(y0, e, y0);
@@ -294,7 +280,8 @@
   linear_filter_output_last_selected_ = aec_state_.UseLinearFilterOutput();
   const auto& Y_fft = aec_state_.UseLinearFilterOutput() ? E : Y;
 
-  data_dumper_->DumpWav("aec3_output_linear", kBlockSize, &y0[0], 16000, 1);
+  data_dumper_->DumpWav("aec3_output_linear", kBlockSize, &y0[0],
+                        LowestBandRate(sample_rate_hz_), 1);
 
   // Estimate the residual echo power.
   residual_echo_estimator_.Estimate(aec_state_, *render_buffer, S2_linear, Y2,
@@ -330,14 +317,16 @@
 
   // Debug outputs for the purpose of development and analysis.
   data_dumper_->DumpWav("aec3_echo_estimate", kBlockSize,
-                        &subtractor_output.s_main[0], 16000, 1);
+                        &subtractor_output.s_main[0],
+                        LowestBandRate(sample_rate_hz_), 1);
   data_dumper_->DumpRaw("aec3_output", y0);
   data_dumper_->DumpRaw("aec3_narrow_render",
                         render_signal_analyzer_.NarrowPeakBand() ? 1 : 0);
   data_dumper_->DumpRaw("aec3_N2", cng_.NoiseSpectrum());
   data_dumper_->DumpRaw("aec3_suppressor_gain", G);
-  data_dumper_->DumpWav(
-      "aec3_output", rtc::ArrayView<const float>(&y0[0], kBlockSize), 16000, 1);
+  data_dumper_->DumpWav("aec3_output",
+                        rtc::ArrayView<const float>(&y0[0], kBlockSize),
+                        LowestBandRate(sample_rate_hz_), 1);
   data_dumper_->DumpRaw("aec3_using_subtractor_output",
                         aec_state_.UseLinearFilterOutput() ? 1 : 0);
   data_dumper_->DumpRaw("aec3_E2", E2);
@@ -401,11 +390,8 @@
 }  // namespace
 
 EchoRemover* EchoRemover::Create(const EchoCanceller3Config& config,
-                                 int sample_rate_hz,
-                                 size_t num_render_channels,
-                                 size_t num_capture_channels) {
-  return new EchoRemoverImpl(config, sample_rate_hz, num_render_channels,
-                             num_capture_channels);
+                                 int sample_rate_hz) {
+  return new EchoRemoverImpl(config, sample_rate_hz);
 }
 
 }  // namespace webrtc
diff --git a/modules/audio_processing/aec3/echo_remover.h b/modules/audio_processing/aec3/echo_remover.h
index 6098a68..357f67d 100644
--- a/modules/audio_processing/aec3/echo_remover.h
+++ b/modules/audio_processing/aec3/echo_remover.h
@@ -26,9 +26,7 @@
 class EchoRemover {
  public:
   static EchoRemover* Create(const EchoCanceller3Config& config,
-                             int sample_rate_hz,
-                             size_t num_render_channels,
-                             size_t num_capture_channels);
+                             int sample_rate_hz);
   virtual ~EchoRemover() = default;
 
   // Get current metrics.
@@ -42,7 +40,7 @@
       bool capture_signal_saturation,
       const absl::optional<DelayEstimate>& external_delay,
       RenderBuffer* render_buffer,
-      std::vector<std::vector<std::vector<float>>>* capture) = 0;
+      std::vector<std::vector<float>>* capture) = 0;
 
   // Updates the status on whether echo leakage is detected in the output of the
   // echo remover.
diff --git a/modules/audio_processing/aec3/echo_remover_unittest.cc b/modules/audio_processing/aec3/echo_remover_unittest.cc
index 15d0913..abe43ae 100644
--- a/modules/audio_processing/aec3/echo_remover_unittest.cc
+++ b/modules/audio_processing/aec3/echo_remover_unittest.cc
@@ -44,40 +44,29 @@
 // Verifies the basic API call sequence
 TEST(EchoRemover, BasicApiCalls) {
   absl::optional<DelayEstimate> delay_estimate;
-  for (auto rate : {16000, 32000, 48000}) {
-    for (size_t num_render_channels : {1, 2, 8}) {
-      for (size_t num_capture_channels : {1, 2, 8}) {
-        SCOPED_TRACE(ProduceDebugText(rate));
-        std::unique_ptr<EchoRemover> remover(
-            EchoRemover::Create(EchoCanceller3Config(), rate,
-                                num_render_channels, num_capture_channels));
-        std::unique_ptr<RenderDelayBuffer> render_buffer(
-            RenderDelayBuffer::Create(EchoCanceller3Config(), rate,
-                                      num_render_channels));
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    std::unique_ptr<EchoRemover> remover(
+        EchoRemover::Create(EchoCanceller3Config(), rate));
+    std::unique_ptr<RenderDelayBuffer> render_buffer(
+        RenderDelayBuffer::Create(EchoCanceller3Config(), rate));
 
-        std::vector<std::vector<std::vector<float>>> render(
-            NumBandsForRate(rate),
-            std::vector<std::vector<float>>(
-                num_render_channels, std::vector<float>(kBlockSize, 0.f)));
-        std::vector<std::vector<std::vector<float>>> capture(
-            NumBandsForRate(rate),
-            std::vector<std::vector<float>>(
-                num_capture_channels, std::vector<float>(kBlockSize, 0.f)));
-        for (size_t k = 0; k < 100; ++k) {
-          EchoPathVariability echo_path_variability(
-              k % 3 == 0 ? true : false,
-              k % 5 == 0
-                  ? EchoPathVariability::DelayAdjustment::kNewDetectedDelay
-                  : EchoPathVariability::DelayAdjustment::kNone,
-              false);
-          render_buffer->Insert(render);
-          render_buffer->PrepareCaptureProcessing();
+    std::vector<std::vector<float>> render(NumBandsForRate(rate),
+                                           std::vector<float>(kBlockSize, 0.f));
+    std::vector<std::vector<float>> capture(
+        NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
+    for (size_t k = 0; k < 100; ++k) {
+      EchoPathVariability echo_path_variability(
+          k % 3 == 0 ? true : false,
+          k % 5 == 0 ? EchoPathVariability::DelayAdjustment::kNewDetectedDelay
+                     : EchoPathVariability::DelayAdjustment::kNone,
+          false);
+      render_buffer->Insert(render);
+      render_buffer->PrepareCaptureProcessing();
 
-          remover->ProcessCapture(echo_path_variability,
-                                  k % 2 == 0 ? true : false, delay_estimate,
-                                  render_buffer->GetRenderBuffer(), &capture);
-        }
-      }
+      remover->ProcessCapture(echo_path_variability, k % 2 == 0 ? true : false,
+                              delay_estimate, render_buffer->GetRenderBuffer(),
+                              &capture);
     }
   }
 }
@@ -89,22 +78,21 @@
 // tests on test bots has been fixed.
 TEST(EchoRemover, DISABLED_WrongSampleRate) {
   EXPECT_DEATH(std::unique_ptr<EchoRemover>(
-                   EchoRemover::Create(EchoCanceller3Config(), 8001, 1, 1)),
+                   EchoRemover::Create(EchoCanceller3Config(), 8001)),
                "");
 }
 
 // Verifies the check for the capture block size.
 TEST(EchoRemover, WrongCaptureBlockSize) {
   absl::optional<DelayEstimate> delay_estimate;
-  for (auto rate : {16000, 32000, 48000}) {
+  for (auto rate : {8000, 16000, 32000, 48000}) {
     SCOPED_TRACE(ProduceDebugText(rate));
     std::unique_ptr<EchoRemover> remover(
-        EchoRemover::Create(EchoCanceller3Config(), rate, 1, 1));
+        EchoRemover::Create(EchoCanceller3Config(), rate));
     std::unique_ptr<RenderDelayBuffer> render_buffer(
-        RenderDelayBuffer::Create(EchoCanceller3Config(), rate, 1));
-    std::vector<std::vector<std::vector<float>>> capture(
-        NumBandsForRate(rate), std::vector<std::vector<float>>(
-                                   1, std::vector<float>(kBlockSize - 1, 0.f)));
+        RenderDelayBuffer::Create(EchoCanceller3Config(), rate));
+    std::vector<std::vector<float>> capture(
+        NumBandsForRate(rate), std::vector<float>(kBlockSize - 1, 0.f));
     EchoPathVariability echo_path_variability(
         false, EchoPathVariability::DelayAdjustment::kNone, false);
     EXPECT_DEATH(
@@ -122,13 +110,12 @@
   for (auto rate : {16000, 32000, 48000}) {
     SCOPED_TRACE(ProduceDebugText(rate));
     std::unique_ptr<EchoRemover> remover(
-        EchoRemover::Create(EchoCanceller3Config(), rate, 1, 1));
+        EchoRemover::Create(EchoCanceller3Config(), rate));
     std::unique_ptr<RenderDelayBuffer> render_buffer(
-        RenderDelayBuffer::Create(EchoCanceller3Config(), rate, 1));
-    std::vector<std::vector<std::vector<float>>> capture(
+        RenderDelayBuffer::Create(EchoCanceller3Config(), rate));
+    std::vector<std::vector<float>> capture(
         NumBandsForRate(rate == 48000 ? 16000 : rate + 16000),
-        std::vector<std::vector<float>>(1,
-                                        std::vector<float>(kBlockSize, 0.f)));
+        std::vector<float>(kBlockSize, 0.f));
     EchoPathVariability echo_path_variability(
         false, EchoPathVariability::DelayAdjustment::kNone, false);
     EXPECT_DEATH(
@@ -142,9 +129,9 @@
 TEST(EchoRemover, NullCapture) {
   absl::optional<DelayEstimate> delay_estimate;
   std::unique_ptr<EchoRemover> remover(
-      EchoRemover::Create(EchoCanceller3Config(), 16000, 1, 1));
+      EchoRemover::Create(EchoCanceller3Config(), 8000));
   std::unique_ptr<RenderDelayBuffer> render_buffer(
-      RenderDelayBuffer::Create(EchoCanceller3Config(), 16000, 1));
+      RenderDelayBuffer::Create(EchoCanceller3Config(), 8000));
   EchoPathVariability echo_path_variability(
       false, EchoPathVariability::DelayAdjustment::kNone, false);
   EXPECT_DEATH(
@@ -161,76 +148,61 @@
   constexpr int kNumBlocksToProcess = 500;
   Random random_generator(42U);
   absl::optional<DelayEstimate> delay_estimate;
-  for (size_t num_channels : {1, 2, 4}) {
-    for (auto rate : {16000, 32000, 48000}) {
-      std::vector<std::vector<std::vector<float>>> x(
-          NumBandsForRate(rate),
-          std::vector<std::vector<float>>(num_channels,
-                                          std::vector<float>(kBlockSize, 0.f)));
-      std::vector<std::vector<std::vector<float>>> y(
-          NumBandsForRate(rate),
-          std::vector<std::vector<float>>(num_channels,
-                                          std::vector<float>(kBlockSize, 0.f)));
-      EchoPathVariability echo_path_variability(
-          false, EchoPathVariability::DelayAdjustment::kNone, false);
-      for (size_t delay_samples : {0, 64, 150, 200, 301}) {
-        SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
-        EchoCanceller3Config config;
-        std::unique_ptr<EchoRemover> remover(
-            EchoRemover::Create(config, rate, num_channels, num_channels));
-        std::unique_ptr<RenderDelayBuffer> render_buffer(
-            RenderDelayBuffer::Create(config, rate, num_channels));
-        render_buffer->AlignFromDelay(delay_samples / kBlockSize);
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    std::vector<std::vector<float>> x(NumBandsForRate(rate),
+                                      std::vector<float>(kBlockSize, 0.f));
+    std::vector<std::vector<float>> y(NumBandsForRate(rate),
+                                      std::vector<float>(kBlockSize, 0.f));
+    EchoPathVariability echo_path_variability(
+        false, EchoPathVariability::DelayAdjustment::kNone, false);
+    for (size_t delay_samples : {0, 64, 150, 200, 301}) {
+      SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
+      EchoCanceller3Config config;
+      std::unique_ptr<EchoRemover> remover(EchoRemover::Create(config, rate));
+      std::unique_ptr<RenderDelayBuffer> render_buffer(
+          RenderDelayBuffer::Create(config, rate));
+      render_buffer->AlignFromDelay(delay_samples / kBlockSize);
 
-        std::vector<std::vector<std::unique_ptr<DelayBuffer<float>>>>
-            delay_buffers(x.size());
-        for (size_t band = 0; band < delay_buffers.size(); ++band) {
-          delay_buffers[band].resize(x[0].size());
-        }
-
-        for (size_t band = 0; band < x.size(); ++band) {
-          for (size_t channel = 0; channel < x[0].size(); ++channel) {
-            delay_buffers[band][channel].reset(
-                new DelayBuffer<float>(delay_samples));
-          }
-        }
-
-        float input_energy = 0.f;
-        float output_energy = 0.f;
-        for (int k = 0; k < kNumBlocksToProcess; ++k) {
-          const bool silence = k < 100 || (k % 100 >= 10);
-
-          for (size_t band = 0; band < x.size(); ++band) {
-            for (size_t channel = 0; channel < x[0].size(); ++channel) {
-              if (silence) {
-                std::fill(x[band][channel].begin(), x[band][channel].end(),
-                          0.f);
-              } else {
-                RandomizeSampleVector(&random_generator, x[band][channel]);
-              }
-              delay_buffers[band][channel]->Delay(x[band][channel],
-                                                  y[band][channel]);
-            }
-          }
-
-          if (k > kNumBlocksToProcess / 2) {
-            input_energy = std::inner_product(y[0][0].begin(), y[0][0].end(),
-                                              y[0][0].begin(), input_energy);
-          }
-
-          render_buffer->Insert(x);
-          render_buffer->PrepareCaptureProcessing();
-
-          remover->ProcessCapture(echo_path_variability, false, delay_estimate,
-                                  render_buffer->GetRenderBuffer(), &y);
-
-          if (k > kNumBlocksToProcess / 2) {
-            output_energy = std::inner_product(y[0][0].begin(), y[0][0].end(),
-                                               y[0][0].begin(), output_energy);
-          }
-        }
-        EXPECT_GT(input_energy, 10.f * output_energy);
+      std::vector<std::unique_ptr<DelayBuffer<float>>> delay_buffers(x.size());
+      for (size_t j = 0; j < x.size(); ++j) {
+        delay_buffers[j].reset(new DelayBuffer<float>(delay_samples));
       }
+
+      float input_energy = 0.f;
+      float output_energy = 0.f;
+      for (int k = 0; k < kNumBlocksToProcess; ++k) {
+        const bool silence = k < 100 || (k % 100 >= 10);
+
+        for (size_t j = 0; j < x.size(); ++j) {
+          if (silence) {
+            std::fill(x[j].begin(), x[j].end(), 0.f);
+          } else {
+            RandomizeSampleVector(&random_generator, x[j]);
+          }
+          delay_buffers[j]->Delay(x[j], y[j]);
+        }
+
+        if (k > kNumBlocksToProcess / 2) {
+          for (size_t j = 0; j < x.size(); ++j) {
+            input_energy = std::inner_product(y[j].begin(), y[j].end(),
+                                              y[j].begin(), input_energy);
+          }
+        }
+
+        render_buffer->Insert(x);
+        render_buffer->PrepareCaptureProcessing();
+
+        remover->ProcessCapture(echo_path_variability, false, delay_estimate,
+                                render_buffer->GetRenderBuffer(), &y);
+
+        if (k > kNumBlocksToProcess / 2) {
+          for (size_t j = 0; j < x.size(); ++j) {
+            output_energy = std::inner_product(y[j].begin(), y[j].end(),
+                                               y[j].begin(), output_energy);
+          }
+        }
+      }
+      EXPECT_GT(input_energy, 10.f * output_energy);
     }
   }
 }
diff --git a/modules/audio_processing/aec3/erle_estimator_unittest.cc b/modules/audio_processing/aec3/erle_estimator_unittest.cc
index 18ba25a..31c550c 100644
--- a/modules/audio_processing/aec3/erle_estimator_unittest.cc
+++ b/modules/audio_processing/aec3/erle_estimator_unittest.cc
@@ -46,7 +46,7 @@
   EXPECT_NEAR(reference_lf, erle_time_domain, 0.5);
 }
 
-void FormFarendTimeFrame(std::vector<std::vector<std::vector<float>>>* x) {
+void FormFarendTimeFrame(rtc::ArrayView<float> x) {
   const std::array<float, kBlockSize> frame = {
       7459.88, 17209.6, 17383,   20768.9, 16816.7, 18386.3, 4492.83, 9675.85,
       6665.52, 14808.6, 9342.3,  7483.28, 19261.7, 4145.98, 1622.18, 13475.2,
@@ -56,12 +56,8 @@
       11405,   15031.4, 14541.6, 19765.5, 18346.3, 19350.2, 3157.47, 18095.8,
       1743.68, 21328.2, 19727.5, 7295.16, 10332.4, 11055.5, 20107.4, 14708.4,
       12416.2, 16434,   2454.69, 9840.8,  6867.23, 1615.75, 6059.9,  8394.19};
-  for (size_t band = 0; band < x->size(); ++band) {
-    for (size_t channel = 0; channel < (*x)[band].size(); ++channel) {
-      RTC_DCHECK_GE((*x)[band][channel].size(), frame.size());
-      std::copy(frame.begin(), frame.end(), (*x)[band][channel].begin());
-    }
-  }
+  RTC_DCHECK_GE(x.size(), frame.size());
+  std::copy(frame.begin(), frame.end(), x.begin());
 }
 
 void FormFarendFrame(const RenderBuffer& render_buffer,
@@ -79,18 +75,14 @@
 
 }  // namespace
 
-void FormNearendFrame(std::vector<std::vector<std::vector<float>>>* x,
+void FormNearendFrame(rtc::ArrayView<float> x,
                       std::array<float, kFftLengthBy2Plus1>* X2,
                       std::array<float, kFftLengthBy2Plus1>* E2,
                       std::array<float, kFftLengthBy2Plus1>* Y2) {
-  for (size_t band = 0; band < x->size(); ++band) {
-    for (size_t channel = 0; channel < (*x)[band].size(); ++channel) {
-      std::fill((*x)[band][channel].begin(), (*x)[band][channel].end(), 0.f);
-      X2->fill(0.f);
-      Y2->fill(500.f * 1000.f * 1000.f);
-      E2->fill((*Y2)[0]);
-    }
-  }
+  x[0] = 0.f;
+  X2->fill(0.f);
+  Y2->fill(500.f * 1000.f * 1000.f);
+  E2->fill((*Y2)[0]);
 }
 
 void GetFilterFreq(std::vector<std::array<float, kFftLengthBy2Plus1>>&
@@ -112,24 +104,18 @@
   std::array<float, kFftLengthBy2Plus1> X2;
   std::array<float, kFftLengthBy2Plus1> E2;
   std::array<float, kFftLengthBy2Plus1> Y2;
-  constexpr size_t kNumChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
-
   EchoCanceller3Config config;
-  std::vector<std::vector<std::vector<float>>> x(
-      kNumBands, std::vector<std::vector<float>>(
-                     kNumChannels, std::vector<float>(kBlockSize, 0.f)));
+  std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
   std::vector<std::array<float, kFftLengthBy2Plus1>> filter_frequency_response(
       config.filter.main.length_blocks);
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(config, kSampleRateHz, kNumChannels));
+      RenderDelayBuffer::Create(config, 48000));
 
   GetFilterFreq(filter_frequency_response, config.delay.delay_headroom_samples);
 
   ErleEstimator estimator(0, config);
 
-  FormFarendTimeFrame(&x);
+  FormFarendTimeFrame(x[0]);
   render_delay_buffer->Insert(x);
   render_delay_buffer->PrepareCaptureProcessing();
   // Verifies that the ERLE estimate is properly increased to higher values.
@@ -144,7 +130,7 @@
   VerifyErle(estimator.Erle(), std::pow(2.f, estimator.FullbandErleLog2()),
              config.erle.max_l, config.erle.max_h);
 
-  FormNearendFrame(&x, &X2, &E2, &Y2);
+  FormNearendFrame(x[0], &X2, &E2, &Y2);
   // Verifies that the ERLE is not immediately decreased during nearend
   // activity.
   for (size_t k = 0; k < 50; ++k) {
@@ -158,27 +144,22 @@
 }
 
 TEST(ErleEstimator, VerifyErleTrackingOnOnsets) {
-  constexpr size_t kNumChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
   std::array<float, kFftLengthBy2Plus1> X2;
   std::array<float, kFftLengthBy2Plus1> E2;
   std::array<float, kFftLengthBy2Plus1> Y2;
   EchoCanceller3Config config;
-  std::vector<std::vector<std::vector<float>>> x(
-      kNumBands, std::vector<std::vector<float>>(
-                     kNumChannels, std::vector<float>(kBlockSize, 0.f)));
+  std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
   std::vector<std::array<float, kFftLengthBy2Plus1>> filter_frequency_response(
       config.filter.main.length_blocks);
 
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(config, kSampleRateHz, kNumChannels));
+      RenderDelayBuffer::Create(config, 48000));
 
   GetFilterFreq(filter_frequency_response, config.delay.delay_headroom_samples);
 
   ErleEstimator estimator(0, config);
 
-  FormFarendTimeFrame(&x);
+  FormFarendTimeFrame(x[0]);
   render_delay_buffer->Insert(x);
   render_delay_buffer->PrepareCaptureProcessing();
 
@@ -199,7 +180,7 @@
       estimator.Update(*render_delay_buffer->GetRenderBuffer(),
                        filter_frequency_response, X2, Y2, E2, true, true);
     }
-    FormNearendFrame(&x, &X2, &E2, &Y2);
+    FormNearendFrame(x[0], &X2, &E2, &Y2);
     for (size_t k = 0; k < 300; ++k) {
       render_delay_buffer->Insert(x);
       render_delay_buffer->PrepareCaptureProcessing();
@@ -208,7 +189,7 @@
     }
   }
   VerifyErleBands(estimator.ErleOnsets(), config.erle.min, config.erle.min);
-  FormNearendFrame(&x, &X2, &E2, &Y2);
+  FormNearendFrame(x[0], &X2, &E2, &Y2);
   for (size_t k = 0; k < 1000; k++) {
     estimator.Update(*render_delay_buffer->GetRenderBuffer(),
                      filter_frequency_response, X2, Y2, E2, true, true);
diff --git a/modules/audio_processing/aec3/filter_analyzer.cc b/modules/audio_processing/aec3/filter_analyzer.cc
index 138c188..06bd4b7 100644
--- a/modules/audio_processing/aec3/filter_analyzer.cc
+++ b/modules/audio_processing/aec3/filter_analyzer.cc
@@ -96,8 +96,8 @@
   filter_length_blocks_ = filter_time_domain.size() * (1.f / kBlockSize);
 
   consistent_estimate_ = consistent_filter_detector_.Detect(
-      h_highpass_, region_, render_buffer.Block(-delay_blocks_)[0][0],
-      peak_index_, delay_blocks_);
+      h_highpass_, region_, render_buffer.Block(-delay_blocks_)[0], peak_index_,
+      delay_blocks_);
 }
 
 void FilterAnalyzer::UpdateFilterGain(
diff --git a/modules/audio_processing/aec3/frame_blocker.cc b/modules/audio_processing/aec3/frame_blocker.cc
index 63aaf09..ca122e5 100644
--- a/modules/audio_processing/aec3/frame_blocker.cc
+++ b/modules/audio_processing/aec3/frame_blocker.cc
@@ -15,73 +15,55 @@
 
 namespace webrtc {
 
-FrameBlocker::FrameBlocker(size_t num_bands, size_t num_channels)
-    : num_bands_(num_bands),
-      num_channels_(num_channels),
-      buffer_(num_bands_, std::vector<std::vector<float>>(num_channels)) {
-  RTC_DCHECK_LT(0, num_bands);
-  RTC_DCHECK_LT(0, num_channels);
-  for (auto& band : buffer_) {
-    for (auto& channel : band) {
-      channel.reserve(kBlockSize);
-      RTC_DCHECK(channel.empty());
-    }
+FrameBlocker::FrameBlocker(size_t num_bands)
+    : num_bands_(num_bands), buffer_(num_bands_) {
+  for (auto& b : buffer_) {
+    b.reserve(kBlockSize);
+    RTC_DCHECK(b.empty());
   }
 }
 
 FrameBlocker::~FrameBlocker() = default;
 
 void FrameBlocker::InsertSubFrameAndExtractBlock(
-    const std::vector<std::vector<rtc::ArrayView<float>>>& sub_frame,
-    std::vector<std::vector<std::vector<float>>>* block) {
+    const std::vector<rtc::ArrayView<float>>& sub_frame,
+    std::vector<std::vector<float>>* block) {
   RTC_DCHECK(block);
   RTC_DCHECK_EQ(num_bands_, block->size());
   RTC_DCHECK_EQ(num_bands_, sub_frame.size());
-  for (size_t band = 0; band < num_bands_; ++band) {
-    RTC_DCHECK_EQ(num_channels_, (*block)[band].size());
-    RTC_DCHECK_EQ(num_channels_, sub_frame[band].size());
-    for (size_t channel = 0; channel < num_channels_; ++channel) {
-      RTC_DCHECK_GE(kBlockSize - 16, buffer_[band][channel].size());
-      RTC_DCHECK_EQ(kBlockSize, (*block)[band][channel].size());
-      RTC_DCHECK_EQ(kSubFrameLength, sub_frame[band][channel].size());
-      const int samples_to_block = kBlockSize - buffer_[band][channel].size();
-      (*block)[band][channel].clear();
-      (*block)[band][channel].insert((*block)[band][channel].begin(),
-                                     buffer_[band][channel].begin(),
-                                     buffer_[band][channel].end());
-      (*block)[band][channel].insert(
-          (*block)[band][channel].begin() + buffer_[band][channel].size(),
-          sub_frame[band][channel].begin(),
-          sub_frame[band][channel].begin() + samples_to_block);
-      buffer_[band][channel].clear();
-      buffer_[band][channel].insert(
-          buffer_[band][channel].begin(),
-          sub_frame[band][channel].begin() + samples_to_block,
-          sub_frame[band][channel].end());
-    }
+  for (size_t i = 0; i < num_bands_; ++i) {
+    RTC_DCHECK_GE(kBlockSize - 16, buffer_[i].size());
+    RTC_DCHECK_EQ(kBlockSize, (*block)[i].size());
+    RTC_DCHECK_EQ(kSubFrameLength, sub_frame[i].size());
+    const int samples_to_block = kBlockSize - buffer_[i].size();
+    (*block)[i].clear();
+    (*block)[i].insert((*block)[i].begin(), buffer_[i].begin(),
+                       buffer_[i].end());
+    (*block)[i].insert((*block)[i].begin() + buffer_[i].size(),
+                       sub_frame[i].begin(),
+                       sub_frame[i].begin() + samples_to_block);
+    buffer_[i].clear();
+    buffer_[i].insert(buffer_[i].begin(),
+                      sub_frame[i].begin() + samples_to_block,
+                      sub_frame[i].end());
   }
 }
 
 bool FrameBlocker::IsBlockAvailable() const {
-  return kBlockSize == buffer_[0][0].size();
+  return kBlockSize == buffer_[0].size();
 }
 
-void FrameBlocker::ExtractBlock(
-    std::vector<std::vector<std::vector<float>>>* block) {
+void FrameBlocker::ExtractBlock(std::vector<std::vector<float>>* block) {
   RTC_DCHECK(block);
   RTC_DCHECK_EQ(num_bands_, block->size());
   RTC_DCHECK(IsBlockAvailable());
-  for (size_t band = 0; band < num_bands_; ++band) {
-    RTC_DCHECK_EQ(num_channels_, (*block)[band].size());
-    for (size_t channel = 0; channel < num_channels_; ++channel) {
-      RTC_DCHECK_EQ(kBlockSize, buffer_[band][channel].size());
-      RTC_DCHECK_EQ(kBlockSize, (*block)[band][channel].size());
-      (*block)[band][channel].clear();
-      (*block)[band][channel].insert((*block)[band][channel].begin(),
-                                     buffer_[band][channel].begin(),
-                                     buffer_[band][channel].end());
-      buffer_[band][channel].clear();
-    }
+  for (size_t i = 0; i < num_bands_; ++i) {
+    RTC_DCHECK_EQ(kBlockSize, buffer_[i].size());
+    RTC_DCHECK_EQ(kBlockSize, (*block)[i].size());
+    (*block)[i].clear();
+    (*block)[i].insert((*block)[i].begin(), buffer_[i].begin(),
+                       buffer_[i].end());
+    buffer_[i].clear();
   }
 }
 
diff --git a/modules/audio_processing/aec3/frame_blocker.h b/modules/audio_processing/aec3/frame_blocker.h
index ebd6f77..759f431 100644
--- a/modules/audio_processing/aec3/frame_blocker.h
+++ b/modules/audio_processing/aec3/frame_blocker.h
@@ -17,33 +17,32 @@
 
 #include "api/array_view.h"
 #include "modules/audio_processing/aec3/aec3_common.h"
+#include "rtc_base/constructor_magic.h"
 
 namespace webrtc {
 
-// Class for producing 64 sample multiband blocks from frames consisting of 2
-// subframes of 80 samples.
+// Class for producing 64 sample multiband blocks from frames consisting of 1 or
+// 2 subframes of 80 samples.
 class FrameBlocker {
  public:
-  FrameBlocker(size_t num_bands, size_t num_channels);
+  explicit FrameBlocker(size_t num_bands);
   ~FrameBlocker();
-  FrameBlocker(const FrameBlocker&) = delete;
-  FrameBlocker& operator=(const FrameBlocker&) = delete;
-
   // Inserts one 80 sample multiband subframe from the multiband frame and
   // extracts one 64 sample multiband block.
   void InsertSubFrameAndExtractBlock(
-      const std::vector<std::vector<rtc::ArrayView<float>>>& sub_frame,
-      std::vector<std::vector<std::vector<float>>>* block);
+      const std::vector<rtc::ArrayView<float>>& sub_frame,
+      std::vector<std::vector<float>>* block);
   // Reports whether a multiband block of 64 samples is available for
   // extraction.
   bool IsBlockAvailable() const;
   // Extracts a multiband block of 64 samples.
-  void ExtractBlock(std::vector<std::vector<std::vector<float>>>* block);
+  void ExtractBlock(std::vector<std::vector<float>>* block);
 
  private:
   const size_t num_bands_;
-  const size_t num_channels_;
-  std::vector<std::vector<std::vector<float>>> buffer_;
+  std::vector<std::vector<float>> buffer_;
+
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(FrameBlocker);
 };
 }  // namespace webrtc
 
diff --git a/modules/audio_processing/aec3/frame_blocker_unittest.cc b/modules/audio_processing/aec3/frame_blocker_unittest.cc
index e907608..3ec74cc 100644
--- a/modules/audio_processing/aec3/frame_blocker_unittest.cc
+++ b/modules/audio_processing/aec3/frame_blocker_unittest.cc
@@ -24,62 +24,45 @@
 float ComputeSampleValue(size_t chunk_counter,
                          size_t chunk_size,
                          size_t band,
-                         size_t channel,
                          size_t sample_index,
                          int offset) {
   float value =
-      static_cast<int>(chunk_counter * chunk_size + sample_index + channel) +
-      offset;
+      static_cast<int>(chunk_counter * chunk_size + sample_index) + offset;
   return value > 0 ? 5000 * band + value : 0;
 }
 
 void FillSubFrame(size_t sub_frame_counter,
                   int offset,
-                  std::vector<std::vector<std::vector<float>>>* sub_frame) {
-  for (size_t band = 0; band < sub_frame->size(); ++band) {
-    for (size_t channel = 0; channel < (*sub_frame)[band].size(); ++channel) {
-      for (size_t sample = 0; sample < (*sub_frame)[band][channel].size();
-           ++sample) {
-        (*sub_frame)[band][channel][sample] = ComputeSampleValue(
-            sub_frame_counter, kSubFrameLength, band, channel, sample, offset);
-      }
+                  std::vector<std::vector<float>>* sub_frame) {
+  for (size_t k = 0; k < sub_frame->size(); ++k) {
+    for (size_t i = 0; i < (*sub_frame)[0].size(); ++i) {
+      (*sub_frame)[k][i] =
+          ComputeSampleValue(sub_frame_counter, kSubFrameLength, k, i, offset);
     }
   }
 }
 
-void FillSubFrameView(
-    size_t sub_frame_counter,
-    int offset,
-    std::vector<std::vector<std::vector<float>>>* sub_frame,
-    std::vector<std::vector<rtc::ArrayView<float>>>* sub_frame_view) {
+void FillSubFrameView(size_t sub_frame_counter,
+                      int offset,
+                      std::vector<std::vector<float>>* sub_frame,
+                      std::vector<rtc::ArrayView<float>>* sub_frame_view) {
   FillSubFrame(sub_frame_counter, offset, sub_frame);
-  for (size_t band = 0; band < sub_frame_view->size(); ++band) {
-    for (size_t channel = 0; channel < (*sub_frame_view)[band].size();
-         ++channel) {
-      (*sub_frame_view)[band][channel] = rtc::ArrayView<float>(
-          &(*sub_frame)[band][channel][0], (*sub_frame)[band][channel].size());
-    }
+  for (size_t k = 0; k < sub_frame_view->size(); ++k) {
+    (*sub_frame_view)[k] =
+        rtc::ArrayView<float>(&(*sub_frame)[k][0], (*sub_frame)[k].size());
   }
 }
 
-bool VerifySubFrame(
-    size_t sub_frame_counter,
-    int offset,
-    const std::vector<std::vector<rtc::ArrayView<float>>>& sub_frame_view) {
-  std::vector<std::vector<std::vector<float>>> reference_sub_frame(
-      sub_frame_view.size(),
-      std::vector<std::vector<float>>(
-          sub_frame_view[0].size(),
-          std::vector<float>(sub_frame_view[0][0].size(), 0.f)));
+bool VerifySubFrame(size_t sub_frame_counter,
+                    int offset,
+                    const std::vector<rtc::ArrayView<float>>& sub_frame_view) {
+  std::vector<std::vector<float>> reference_sub_frame(
+      sub_frame_view.size(), std::vector<float>(sub_frame_view[0].size(), 0.f));
   FillSubFrame(sub_frame_counter, offset, &reference_sub_frame);
-  for (size_t band = 0; band < sub_frame_view.size(); ++band) {
-    for (size_t channel = 0; channel < sub_frame_view[band].size(); ++channel) {
-      for (size_t sample = 0; sample < sub_frame_view[band][channel].size();
-           ++sample) {
-        if (reference_sub_frame[band][channel][sample] !=
-            sub_frame_view[band][channel][sample]) {
-          return false;
-        }
+  for (size_t k = 0; k < sub_frame_view.size(); ++k) {
+    for (size_t i = 0; i < sub_frame_view[k].size(); ++i) {
+      if (reference_sub_frame[k][i] != sub_frame_view[k][i]) {
+        return false;
       }
     }
   }
@@ -88,15 +71,13 @@
 
 bool VerifyBlock(size_t block_counter,
                  int offset,
-                 const std::vector<std::vector<std::vector<float>>>& block) {
-  for (size_t band = 0; band < block.size(); ++band) {
-    for (size_t channel = 0; channel < block[band].size(); ++channel) {
-      for (size_t sample = 0; sample < block[band][channel].size(); ++sample) {
-        const float reference_value = ComputeSampleValue(
-            block_counter, kBlockSize, band, channel, sample, offset);
-        if (reference_value != block[band][channel][sample]) {
-          return false;
-        }
+                 const std::vector<std::vector<float>>& block) {
+  for (size_t k = 0; k < block.size(); ++k) {
+    for (size_t i = 0; i < block[k].size(); ++i) {
+      const float reference_value =
+          ComputeSampleValue(block_counter, kBlockSize, k, i, offset);
+      if (reference_value != block[k][i]) {
+        return false;
       }
     }
   }
@@ -104,19 +85,16 @@
 }
 
 // Verifies that the FrameBlocker properly forms blocks out of the frames.
-void RunBlockerTest(int sample_rate_hz, size_t num_channels) {
+void RunBlockerTest(int sample_rate_hz) {
   constexpr size_t kNumSubFramesToProcess = 20;
   const size_t num_bands = NumBandsForRate(sample_rate_hz);
 
-  std::vector<std::vector<std::vector<float>>> block(
-      num_bands, std::vector<std::vector<float>>(
-                     num_channels, std::vector<float>(kBlockSize, 0.f)));
-  std::vector<std::vector<std::vector<float>>> input_sub_frame(
-      num_bands, std::vector<std::vector<float>>(
-                     num_channels, std::vector<float>(kSubFrameLength, 0.f)));
-  std::vector<std::vector<rtc::ArrayView<float>>> input_sub_frame_view(
-      num_bands, std::vector<rtc::ArrayView<float>>(num_channels));
-  FrameBlocker blocker(num_bands, num_channels);
+  std::vector<std::vector<float>> block(num_bands,
+                                        std::vector<float>(kBlockSize, 0.f));
+  std::vector<std::vector<float>> input_sub_frame(
+      num_bands, std::vector<float>(kSubFrameLength, 0.f));
+  std::vector<rtc::ArrayView<float>> input_sub_frame_view(num_bands);
+  FrameBlocker blocker(num_bands);
 
   size_t block_counter = 0;
   for (size_t sub_frame_index = 0; sub_frame_index < kNumSubFramesToProcess;
@@ -141,25 +119,20 @@
 
 // Verifies that the FrameBlocker and BlockFramer work well together and produce
 // the expected output.
-void RunBlockerAndFramerTest(int sample_rate_hz, size_t num_channels) {
+void RunBlockerAndFramerTest(int sample_rate_hz) {
   const size_t kNumSubFramesToProcess = 20;
   const size_t num_bands = NumBandsForRate(sample_rate_hz);
 
-  std::vector<std::vector<std::vector<float>>> block(
-      num_bands, std::vector<std::vector<float>>(
-                     num_channels, std::vector<float>(kBlockSize, 0.f)));
-  std::vector<std::vector<std::vector<float>>> input_sub_frame(
-      num_bands, std::vector<std::vector<float>>(
-                     num_channels, std::vector<float>(kSubFrameLength, 0.f)));
-  std::vector<std::vector<std::vector<float>>> output_sub_frame(
-      num_bands, std::vector<std::vector<float>>(
-                     num_channels, std::vector<float>(kSubFrameLength, 0.f)));
-  std::vector<std::vector<rtc::ArrayView<float>>> output_sub_frame_view(
-      num_bands, std::vector<rtc::ArrayView<float>>(num_channels));
-  std::vector<std::vector<rtc::ArrayView<float>>> input_sub_frame_view(
-      num_bands, std::vector<rtc::ArrayView<float>>(num_channels));
-  FrameBlocker blocker(num_bands, num_channels);
-  BlockFramer framer(num_bands, num_channels);
+  std::vector<std::vector<float>> block(num_bands,
+                                        std::vector<float>(kBlockSize, 0.f));
+  std::vector<std::vector<float>> input_sub_frame(
+      num_bands, std::vector<float>(kSubFrameLength, 0.f));
+  std::vector<std::vector<float>> output_sub_frame(
+      num_bands, std::vector<float>(kSubFrameLength, 0.f));
+  std::vector<rtc::ArrayView<float>> output_sub_frame_view(num_bands);
+  std::vector<rtc::ArrayView<float>> input_sub_frame_view(num_bands);
+  FrameBlocker blocker(num_bands);
+  BlockFramer framer(num_bands);
 
   for (size_t sub_frame_index = 0; sub_frame_index < kNumSubFramesToProcess;
        ++sub_frame_index) {
@@ -180,39 +153,28 @@
       blocker.ExtractBlock(&block);
       framer.InsertBlock(block);
     }
-    if (sub_frame_index > 1) {
-      EXPECT_TRUE(VerifySubFrame(sub_frame_index, -64, output_sub_frame_view));
-    }
+    EXPECT_TRUE(VerifySubFrame(sub_frame_index, -64, output_sub_frame_view));
   }
 }
 
 #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
 // Verifies that the FrameBlocker crashes if the InsertSubFrameAndExtractBlock
 // method is called for inputs with the wrong number of bands or band lengths.
-void RunWronglySizedInsertAndExtractParametersTest(
-    int sample_rate_hz,
-    size_t correct_num_channels,
-    size_t num_block_bands,
-    size_t num_block_channels,
-    size_t block_length,
-    size_t num_sub_frame_bands,
-    size_t num_sub_frame_channels,
-    size_t sub_frame_length) {
+void RunWronglySizedInsertAndExtractParametersTest(int sample_rate_hz,
+                                                   size_t num_block_bands,
+                                                   size_t block_length,
+                                                   size_t num_sub_frame_bands,
+                                                   size_t sub_frame_length) {
   const size_t correct_num_bands = NumBandsForRate(sample_rate_hz);
 
-  std::vector<std::vector<std::vector<float>>> block(
-      num_block_bands,
-      std::vector<std::vector<float>>(num_block_channels,
-                                      std::vector<float>(block_length, 0.f)));
-  std::vector<std::vector<std::vector<float>>> input_sub_frame(
-      num_sub_frame_bands,
-      std::vector<std::vector<float>>(
-          num_sub_frame_channels, std::vector<float>(sub_frame_length, 0.f)));
-  std::vector<std::vector<rtc::ArrayView<float>>> input_sub_frame_view(
-      input_sub_frame.size(),
-      std::vector<rtc::ArrayView<float>>(num_sub_frame_channels));
+  std::vector<std::vector<float>> block(num_block_bands,
+                                        std::vector<float>(block_length, 0.f));
+  std::vector<std::vector<float>> input_sub_frame(
+      num_sub_frame_bands, std::vector<float>(sub_frame_length, 0.f));
+  std::vector<rtc::ArrayView<float>> input_sub_frame_view(
+      input_sub_frame.size());
   FillSubFrameView(0, 0, &input_sub_frame, &input_sub_frame_view);
-  FrameBlocker blocker(correct_num_bands, correct_num_channels);
+  FrameBlocker blocker(correct_num_bands);
   EXPECT_DEATH(
       blocker.InsertSubFrameAndExtractBlock(input_sub_frame_view, &block), "");
 }
@@ -220,29 +182,20 @@
 // Verifies that the FrameBlocker crashes if the ExtractBlock method is called
 // for inputs with the wrong number of bands or band lengths.
 void RunWronglySizedExtractParameterTest(int sample_rate_hz,
-                                         size_t correct_num_channels,
                                          size_t num_block_bands,
-                                         size_t num_block_channels,
                                          size_t block_length) {
   const size_t correct_num_bands = NumBandsForRate(sample_rate_hz);
 
-  std::vector<std::vector<std::vector<float>>> correct_block(
-      correct_num_bands,
-      std::vector<std::vector<float>>(correct_num_channels,
-                                      std::vector<float>(kBlockSize, 0.f)));
-  std::vector<std::vector<std::vector<float>>> wrong_block(
-      num_block_bands,
-      std::vector<std::vector<float>>(num_block_channels,
-                                      std::vector<float>(block_length, 0.f)));
-  std::vector<std::vector<std::vector<float>>> input_sub_frame(
-      correct_num_bands,
-      std::vector<std::vector<float>>(
-          correct_num_channels, std::vector<float>(kSubFrameLength, 0.f)));
-  std::vector<std::vector<rtc::ArrayView<float>>> input_sub_frame_view(
-      input_sub_frame.size(),
-      std::vector<rtc::ArrayView<float>>(correct_num_channels));
+  std::vector<std::vector<float>> correct_block(
+      correct_num_bands, std::vector<float>(kBlockSize, 0.f));
+  std::vector<std::vector<float>> wrong_block(
+      num_block_bands, std::vector<float>(block_length, 0.f));
+  std::vector<std::vector<float>> input_sub_frame(
+      correct_num_bands, std::vector<float>(kSubFrameLength, 0.f));
+  std::vector<rtc::ArrayView<float>> input_sub_frame_view(
+      input_sub_frame.size());
   FillSubFrameView(0, 0, &input_sub_frame, &input_sub_frame_view);
-  FrameBlocker blocker(correct_num_bands, correct_num_channels);
+  FrameBlocker blocker(correct_num_bands);
   blocker.InsertSubFrameAndExtractBlock(input_sub_frame_view, &correct_block);
   blocker.InsertSubFrameAndExtractBlock(input_sub_frame_view, &correct_block);
   blocker.InsertSubFrameAndExtractBlock(input_sub_frame_view, &correct_block);
@@ -255,20 +208,17 @@
 // after a wrong number of previous InsertSubFrameAndExtractBlock method calls
 // have been made.
 void RunWrongExtractOrderTest(int sample_rate_hz,
-                              size_t num_channels,
                               size_t num_preceeding_api_calls) {
-  const size_t num_bands = NumBandsForRate(sample_rate_hz);
+  const size_t correct_num_bands = NumBandsForRate(sample_rate_hz);
 
-  std::vector<std::vector<std::vector<float>>> block(
-      num_bands, std::vector<std::vector<float>>(
-                     num_channels, std::vector<float>(kBlockSize, 0.f)));
-  std::vector<std::vector<std::vector<float>>> input_sub_frame(
-      num_bands, std::vector<std::vector<float>>(
-                     num_channels, std::vector<float>(kSubFrameLength, 0.f)));
-  std::vector<std::vector<rtc::ArrayView<float>>> input_sub_frame_view(
-      input_sub_frame.size(), std::vector<rtc::ArrayView<float>>(num_channels));
+  std::vector<std::vector<float>> block(correct_num_bands,
+                                        std::vector<float>(kBlockSize, 0.f));
+  std::vector<std::vector<float>> input_sub_frame(
+      correct_num_bands, std::vector<float>(kSubFrameLength, 0.f));
+  std::vector<rtc::ArrayView<float>> input_sub_frame_view(
+      input_sub_frame.size());
   FillSubFrameView(0, 0, &input_sub_frame, &input_sub_frame_view);
-  FrameBlocker blocker(num_bands, num_channels);
+  FrameBlocker blocker(correct_num_bands);
   for (size_t k = 0; k < num_preceeding_api_calls; ++k) {
     blocker.InsertSubFrameAndExtractBlock(input_sub_frame_view, &block);
   }
@@ -277,10 +227,9 @@
 }
 #endif
 
-std::string ProduceDebugText(int sample_rate_hz, size_t num_channels) {
+std::string ProduceDebugText(int sample_rate_hz) {
   rtc::StringBuilder ss;
   ss << "Sample rate: " << sample_rate_hz;
-  ss << ", number of channels: " << num_channels;
   return ss.Release();
 }
 
@@ -288,183 +237,104 @@
 
 #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
 TEST(FrameBlocker, WrongNumberOfBandsInBlockForInsertSubFrameAndExtractBlock) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (size_t correct_num_channels : {1, 2, 4, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
-      const size_t correct_num_bands = NumBandsForRate(rate);
-      const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
-      RunWronglySizedInsertAndExtractParametersTest(
-          rate, correct_num_channels, wrong_num_bands, correct_num_channels,
-          kBlockSize, correct_num_bands, correct_num_channels, kSubFrameLength);
-    }
-  }
-}
-
-TEST(FrameBlocker,
-     WrongNumberOfChannelsInBlockForInsertSubFrameAndExtractBlock) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (size_t correct_num_channels : {1, 2, 4, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
-      const size_t correct_num_bands = NumBandsForRate(rate);
-      const size_t wrong_num_channels = correct_num_channels + 1;
-      RunWronglySizedInsertAndExtractParametersTest(
-          rate, correct_num_channels, correct_num_bands, wrong_num_channels,
-          kBlockSize, correct_num_bands, correct_num_channels, kSubFrameLength);
-    }
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    const size_t correct_num_bands = NumBandsForRate(rate);
+    const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
+    RunWronglySizedInsertAndExtractParametersTest(
+        rate, wrong_num_bands, kBlockSize, correct_num_bands, kSubFrameLength);
   }
 }
 
 TEST(FrameBlocker,
      WrongNumberOfBandsInSubFrameForInsertSubFrameAndExtractBlock) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (size_t correct_num_channels : {1, 2, 4, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
-      const size_t correct_num_bands = NumBandsForRate(rate);
-      const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
-      RunWronglySizedInsertAndExtractParametersTest(
-          rate, correct_num_channels, correct_num_bands, correct_num_channels,
-          kBlockSize, wrong_num_bands, correct_num_channels, kSubFrameLength);
-    }
-  }
-}
-
-TEST(FrameBlocker,
-     WrongNumberOfChannelsInSubFrameForInsertSubFrameAndExtractBlock) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (size_t correct_num_channels : {1, 2, 4, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
-      const size_t correct_num_bands = NumBandsForRate(rate);
-      const size_t wrong_num_channels = correct_num_channels + 1;
-      RunWronglySizedInsertAndExtractParametersTest(
-          rate, correct_num_channels, correct_num_bands, wrong_num_channels,
-          kBlockSize, correct_num_bands, wrong_num_channels, kSubFrameLength);
-    }
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    const size_t correct_num_bands = NumBandsForRate(rate);
+    const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
+    RunWronglySizedInsertAndExtractParametersTest(
+        rate, correct_num_bands, kBlockSize, wrong_num_bands, kSubFrameLength);
   }
 }
 
 TEST(FrameBlocker,
      WrongNumberOfSamplesInBlockForInsertSubFrameAndExtractBlock) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (size_t correct_num_channels : {1, 2, 4, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
-      const size_t correct_num_bands = NumBandsForRate(rate);
-      RunWronglySizedInsertAndExtractParametersTest(
-          rate, correct_num_channels, correct_num_bands, correct_num_channels,
-          kBlockSize - 1, correct_num_bands, correct_num_channels,
-          kSubFrameLength);
-    }
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    const size_t correct_num_bands = NumBandsForRate(rate);
+    RunWronglySizedInsertAndExtractParametersTest(
+        rate, correct_num_bands, kBlockSize - 1, correct_num_bands,
+        kSubFrameLength);
   }
 }
 
 TEST(FrameBlocker,
      WrongNumberOfSamplesInSubFrameForInsertSubFrameAndExtractBlock) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (size_t correct_num_channels : {1, 2, 4, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
-      const size_t correct_num_bands = NumBandsForRate(rate);
-      RunWronglySizedInsertAndExtractParametersTest(
-          rate, correct_num_channels, correct_num_bands, correct_num_channels,
-          kBlockSize, correct_num_bands, correct_num_channels,
-          kSubFrameLength - 1);
-    }
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    const size_t correct_num_bands = NumBandsForRate(rate);
+    RunWronglySizedInsertAndExtractParametersTest(rate, correct_num_bands,
+                                                  kBlockSize, correct_num_bands,
+                                                  kSubFrameLength - 1);
   }
 }
 
 TEST(FrameBlocker, WrongNumberOfBandsInBlockForExtractBlock) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (size_t correct_num_channels : {1, 2, 4, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
-      const size_t correct_num_bands = NumBandsForRate(rate);
-      const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
-      RunWronglySizedExtractParameterTest(rate, correct_num_channels,
-                                          wrong_num_bands, correct_num_channels,
-                                          kBlockSize);
-    }
-  }
-}
-
-TEST(FrameBlocker, WrongNumberOfChannelsInBlockForExtractBlock) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (size_t correct_num_channels : {1, 2, 4, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
-      const size_t correct_num_bands = NumBandsForRate(rate);
-      const size_t wrong_num_channels = correct_num_channels + 1;
-      RunWronglySizedExtractParameterTest(rate, correct_num_channels,
-                                          correct_num_bands, wrong_num_channels,
-                                          kBlockSize);
-    }
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    const size_t correct_num_bands = NumBandsForRate(rate);
+    const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
+    RunWronglySizedExtractParameterTest(rate, wrong_num_bands, kBlockSize);
   }
 }
 
 TEST(FrameBlocker, WrongNumberOfSamplesInBlockForExtractBlock) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (size_t correct_num_channels : {1, 2, 4, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
-      const size_t correct_num_bands = NumBandsForRate(rate);
-      RunWronglySizedExtractParameterTest(rate, correct_num_channels,
-                                          correct_num_bands,
-                                          correct_num_channels, kBlockSize - 1);
-    }
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    const size_t correct_num_bands = NumBandsForRate(rate);
+    RunWronglySizedExtractParameterTest(rate, correct_num_bands,
+                                        kBlockSize - 1);
   }
 }
 
 TEST(FrameBlocker, WrongNumberOfPreceedingApiCallsForExtractBlock) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (size_t num_channels : {1, 2, 4, 8}) {
-      for (size_t num_calls = 0; num_calls < 4; ++num_calls) {
-        rtc::StringBuilder ss;
-        ss << "Sample rate: " << rate;
-        ss << "Num channels: " << num_channels;
-        ss << ", Num preceeding InsertSubFrameAndExtractBlock calls: "
-           << num_calls;
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    for (size_t num_calls = 0; num_calls < 4; ++num_calls) {
+      rtc::StringBuilder ss;
+      ss << "Sample rate: " << rate;
+      ss << ", Num preceeding InsertSubFrameAndExtractBlock calls: "
+         << num_calls;
 
-        SCOPED_TRACE(ss.str());
-        RunWrongExtractOrderTest(rate, num_channels, num_calls);
-      }
+      SCOPED_TRACE(ss.str());
+      RunWrongExtractOrderTest(rate, num_calls);
     }
   }
 }
 
-// Verifies that the verification for 0 number of channels works.
-TEST(FrameBlocker, ZeroNumberOfChannelsParameter) {
-  EXPECT_DEATH(FrameBlocker(16000, 0), "");
-}
-
-// Verifies that the verification for 0 number of bands works.
-TEST(FrameBlocker, ZeroNumberOfBandsParameter) {
-  EXPECT_DEATH(FrameBlocker(0, 1), "");
-}
-
 // Verifiers that the verification for null sub_frame pointer works.
 TEST(FrameBlocker, NullBlockParameter) {
-  std::vector<std::vector<std::vector<float>>> sub_frame(
-      1, std::vector<std::vector<float>>(
-             1, std::vector<float>(kSubFrameLength, 0.f)));
-  std::vector<std::vector<rtc::ArrayView<float>>> sub_frame_view(
-      sub_frame.size());
+  std::vector<std::vector<float>> sub_frame(
+      1, std::vector<float>(kSubFrameLength, 0.f));
+  std::vector<rtc::ArrayView<float>> sub_frame_view(sub_frame.size());
   FillSubFrameView(0, 0, &sub_frame, &sub_frame_view);
   EXPECT_DEATH(
-      FrameBlocker(1, 1).InsertSubFrameAndExtractBlock(sub_frame_view, nullptr),
+      FrameBlocker(1).InsertSubFrameAndExtractBlock(sub_frame_view, nullptr),
       "");
 }
 
 #endif
 
 TEST(FrameBlocker, BlockBitexactness) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (size_t num_channels : {1, 2, 4, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, num_channels));
-      RunBlockerTest(rate, num_channels);
-    }
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    RunBlockerTest(rate);
   }
 }
 
 TEST(FrameBlocker, BlockerAndFramer) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (size_t num_channels : {1, 2, 4, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate, num_channels));
-      RunBlockerAndFramerTest(rate, num_channels);
-    }
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    RunBlockerAndFramerTest(rate);
   }
 }
 
diff --git a/modules/audio_processing/aec3/main_filter_update_gain_unittest.cc b/modules/audio_processing/aec3/main_filter_update_gain_unittest.cc
index 648762a..34412b8 100644
--- a/modules/audio_processing/aec3/main_filter_update_gain_unittest.cc
+++ b/modules/audio_processing/aec3/main_filter_update_gain_unittest.cc
@@ -42,10 +42,6 @@
                          std::array<float, kBlockSize>* y_last_block,
                          FftData* G_last_block) {
   ApmDataDumper data_dumper(42);
-  constexpr size_t kNumChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
-
   EchoCanceller3Config config;
   config.filter.main.length_blocks = filter_length_blocks;
   config.filter.shadow.length_blocks = filter_length_blocks;
@@ -65,13 +61,11 @@
   MainFilterUpdateGain main_gain(config.filter.main,
                                  config.filter.config_change_duration_blocks);
   Random random_generator(42U);
-  std::vector<std::vector<std::vector<float>>> x(
-      kNumBands, std::vector<std::vector<float>>(
-                     kNumChannels, std::vector<float>(kBlockSize, 0.f)));
+  std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
   std::vector<float> y(kBlockSize, 0.f);
   config.delay.default_delay = 1;
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(config, kSampleRateHz, kNumChannels));
+      RenderDelayBuffer::Create(config, 48000));
   AecState aec_state(config);
   RenderSignalAnalyzer render_signal_analyzer(config);
   absl::optional<DelayEstimate> delay_estimate;
@@ -107,19 +101,11 @@
 
     // Create the render signal.
     if (use_silent_render_in_second_half && k > num_blocks_to_process / 2) {
-      for (size_t band = 0; band < x.size(); ++band) {
-        for (size_t channel = 0; channel < x[band].size(); ++channel) {
-          std::fill(x[band][channel].begin(), x[band][channel].end(), 0.f);
-        }
-      }
+      std::fill(x[0].begin(), x[0].end(), 0.f);
     } else {
-      for (size_t band = 0; band < x.size(); ++band) {
-        for (size_t channel = 0; channel < x[band].size(); ++channel) {
-          RandomizeSampleVector(&random_generator, x[band][channel]);
-        }
-      }
+      RandomizeSampleVector(&random_generator, x[0]);
     }
-    delay_buffer.Delay(x[0][0], y);
+    delay_buffer.Delay(x[0], y);
 
     render_delay_buffer->Insert(x);
     if (k == 0) {
diff --git a/modules/audio_processing/aec3/matched_filter.cc b/modules/audio_processing/aec3/matched_filter.cc
index 5a62b7c..757219d 100644
--- a/modules/audio_processing/aec3/matched_filter.cc
+++ b/modules/audio_processing/aec3/matched_filter.cc
@@ -442,15 +442,15 @@
                                         size_t shift,
                                         size_t downsampling_factor) const {
   size_t alignment_shift = 0;
-  constexpr int kFsBy1000 = 16;
+  const int fs_by_1000 = LowestBandRate(sample_rate_hz) / 1000;
   for (size_t k = 0; k < filters_.size(); ++k) {
     int start = static_cast<int>(alignment_shift * downsampling_factor);
     int end = static_cast<int>((alignment_shift + filters_[k].size()) *
                                downsampling_factor);
     RTC_LOG(LS_INFO) << "Filter " << k << ": start: "
-                     << (start - static_cast<int>(shift)) / kFsBy1000
+                     << (start - static_cast<int>(shift)) / fs_by_1000
                      << " ms, end: "
-                     << (end - static_cast<int>(shift)) / kFsBy1000 << " ms.";
+                     << (end - static_cast<int>(shift)) / fs_by_1000 << " ms.";
     alignment_shift += filter_intra_lag_shift_;
   }
 }
diff --git a/modules/audio_processing/aec3/matched_filter_unittest.cc b/modules/audio_processing/aec3/matched_filter_unittest.cc
index 8f2c5c2..c204af4 100644
--- a/modules/audio_processing/aec3/matched_filter_unittest.cc
+++ b/modules/audio_processing/aec3/matched_filter_unittest.cc
@@ -140,16 +140,11 @@
 // delayed signals.
 TEST(MatchedFilter, LagEstimation) {
   Random random_generator(42U);
-  constexpr size_t kNumChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
-
   for (auto down_sampling_factor : kDownSamplingFactors) {
     const size_t sub_block_size = kBlockSize / down_sampling_factor;
 
-    std::vector<std::vector<std::vector<float>>> render(
-        kNumBands, std::vector<std::vector<float>>(
-                       kNumChannels, std::vector<float>(kBlockSize, 0.f)));
+    std::vector<std::vector<float>> render(3,
+                                           std::vector<float>(kBlockSize, 0.f));
     std::array<float, kBlockSize> capture;
     capture.fill(0.f);
     ApmDataDumper data_dumper(0);
@@ -168,16 +163,12 @@
                            config.delay.delay_candidate_detection_threshold);
 
       std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-          RenderDelayBuffer::Create(config, kSampleRateHz, kNumChannels));
+          RenderDelayBuffer::Create(config, 48000));
 
       // Analyze the correlation between render and capture.
       for (size_t k = 0; k < (600 + delay_samples / sub_block_size); ++k) {
-        for (size_t band = 0; band < kNumBands; ++band) {
-          for (size_t channel = 0; channel < kNumChannels; ++channel) {
-            RandomizeSampleVector(&random_generator, render[band][channel]);
-          }
-        }
-        signal_delay_buffer.Delay(render[0][0], capture);
+        RandomizeSampleVector(&random_generator, render[0]);
+        signal_delay_buffer.Delay(render[0], capture);
         render_delay_buffer->Insert(render);
 
         if (k == 0) {
@@ -254,9 +245,6 @@
 // Verifies that the matched filter does not produce reliable and accurate
 // estimates for uncorrelated render and capture signals.
 TEST(MatchedFilter, LagNotReliableForUncorrelatedRenderAndCapture) {
-  constexpr size_t kNumChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
   Random random_generator(42U);
   for (auto down_sampling_factor : kDownSamplingFactors) {
     EchoCanceller3Config config;
@@ -264,15 +252,14 @@
     config.delay.num_filters = kNumMatchedFilters;
     const size_t sub_block_size = kBlockSize / down_sampling_factor;
 
-    std::vector<std::vector<std::vector<float>>> render(
-        kNumBands, std::vector<std::vector<float>>(
-                       kNumChannels, std::vector<float>(kBlockSize, 0.f)));
+    std::vector<std::vector<float>> render(3,
+                                           std::vector<float>(kBlockSize, 0.f));
     std::array<float, kBlockSize> capture_data;
     rtc::ArrayView<float> capture(capture_data.data(), sub_block_size);
     std::fill(capture.begin(), capture.end(), 0.f);
     ApmDataDumper data_dumper(0);
     std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-        RenderDelayBuffer::Create(config, kSampleRateHz, kNumChannels));
+        RenderDelayBuffer::Create(config, 48000));
     MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size,
                          kWindowSizeSubBlocks, kNumMatchedFilters,
                          kAlignmentShiftSubBlocks, 150,
@@ -281,7 +268,7 @@
 
     // Analyze the correlation between render and capture.
     for (size_t k = 0; k < 100; ++k) {
-      RandomizeSampleVector(&random_generator, render[0][0]);
+      RandomizeSampleVector(&random_generator, render[0]);
       RandomizeSampleVector(&random_generator, capture);
       render_delay_buffer->Insert(render);
       filter.Update(render_delay_buffer->GetDownsampledRenderBuffer(), capture);
@@ -302,16 +289,11 @@
 // render signals of low level.
 TEST(MatchedFilter, LagNotUpdatedForLowLevelRender) {
   Random random_generator(42U);
-  constexpr size_t kNumChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
-
   for (auto down_sampling_factor : kDownSamplingFactors) {
     const size_t sub_block_size = kBlockSize / down_sampling_factor;
 
-    std::vector<std::vector<std::vector<float>>> render(
-        kNumBands, std::vector<std::vector<float>>(
-                       kNumChannels, std::vector<float>(kBlockSize, 0.f)));
+    std::vector<std::vector<float>> render(3,
+                                           std::vector<float>(kBlockSize, 0.f));
     std::array<float, kBlockSize> capture;
     capture.fill(0.f);
     ApmDataDumper data_dumper(0);
@@ -322,17 +304,16 @@
                          config.delay.delay_estimate_smoothing,
                          config.delay.delay_candidate_detection_threshold);
     std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-        RenderDelayBuffer::Create(EchoCanceller3Config(), kSampleRateHz,
-                                  kNumChannels));
+        RenderDelayBuffer::Create(EchoCanceller3Config(), 48000));
     Decimator capture_decimator(down_sampling_factor);
 
     // Analyze the correlation between render and capture.
     for (size_t k = 0; k < 100; ++k) {
-      RandomizeSampleVector(&random_generator, render[0][0]);
-      for (auto& render_k : render[0][0]) {
+      RandomizeSampleVector(&random_generator, render[0]);
+      for (auto& render_k : render[0]) {
         render_k *= 149.f / 32767.f;
       }
-      std::copy(render[0][0].begin(), render[0][0].end(), capture.begin());
+      std::copy(render[0].begin(), render[0].end(), capture.begin());
       std::array<float, kBlockSize> downsampled_capture_data;
       rtc::ArrayView<float> downsampled_capture(downsampled_capture_data.data(),
                                                 sub_block_size);
diff --git a/modules/audio_processing/aec3/matrix_buffer.cc b/modules/audio_processing/aec3/matrix_buffer.cc
index 2fd71b4..bd6daea 100644
--- a/modules/audio_processing/aec3/matrix_buffer.cc
+++ b/modules/audio_processing/aec3/matrix_buffer.cc
@@ -14,22 +14,14 @@
 
 namespace webrtc {
 
-MatrixBuffer::MatrixBuffer(size_t size,
-                           size_t num_bands,
-                           size_t num_channels,
-                           size_t frame_length)
+MatrixBuffer::MatrixBuffer(size_t size, size_t height, size_t width)
     : size(static_cast<int>(size)),
       buffer(size,
-             std::vector<std::vector<std::vector<float>>>(
-                 num_bands,
-                 std::vector<std::vector<float>>(
-                     num_channels,
-                     std::vector<float>(frame_length, 0.f)))) {
-  for (auto& block : buffer) {
-    for (auto& band : block) {
-      for (auto& channel : band) {
-        std::fill(channel.begin(), channel.end(), 0.f);
-      }
+             std::vector<std::vector<float>>(height,
+                                             std::vector<float>(width, 0.f))) {
+  for (auto& c : buffer) {
+    for (auto& b : c) {
+      std::fill(b.begin(), b.end(), 0.f);
     }
   }
 }
diff --git a/modules/audio_processing/aec3/matrix_buffer.h b/modules/audio_processing/aec3/matrix_buffer.h
index 97736a3..8fb96d2 100644
--- a/modules/audio_processing/aec3/matrix_buffer.h
+++ b/modules/audio_processing/aec3/matrix_buffer.h
@@ -21,12 +21,8 @@
 
 // Struct for bundling a circular buffer of two dimensional vector objects
 // together with the read and write indices.
-// TODO(peah): Change name of this class to be more specific to what it does.
 struct MatrixBuffer {
-  MatrixBuffer(size_t size,
-               size_t num_bands,
-               size_t num_channels,
-               size_t frame_length);
+  MatrixBuffer(size_t size, size_t height, size_t width);
   ~MatrixBuffer();
 
   int IncIndex(int index) const {
@@ -53,7 +49,7 @@
   void DecReadIndex() { read = DecIndex(read); }
 
   const int size;
-  std::vector<std::vector<std::vector<std::vector<float>>>> buffer;
+  std::vector<std::vector<std::vector<float>>> buffer;
   int write = 0;
   int read = 0;
 };
diff --git a/modules/audio_processing/aec3/mock/mock_block_processor.h b/modules/audio_processing/aec3/mock/mock_block_processor.h
index cb93714..85b88f7 100644
--- a/modules/audio_processing/aec3/mock/mock_block_processor.h
+++ b/modules/audio_processing/aec3/mock/mock_block_processor.h
@@ -24,13 +24,12 @@
   MockBlockProcessor();
   virtual ~MockBlockProcessor();
 
-  MOCK_METHOD3(
-      ProcessCapture,
-      void(bool level_change,
-           bool saturated_microphone_signal,
-           std::vector<std::vector<std::vector<float>>>* capture_block));
+  MOCK_METHOD3(ProcessCapture,
+               void(bool level_change,
+                    bool saturated_microphone_signal,
+                    std::vector<std::vector<float>>* capture_block));
   MOCK_METHOD1(BufferRender,
-               void(const std::vector<std::vector<std::vector<float>>>& block));
+               void(const std::vector<std::vector<float>>& block));
   MOCK_METHOD1(UpdateEchoLeakageStatus, void(bool leakage_detected));
   MOCK_CONST_METHOD1(GetMetrics, void(EchoControl::Metrics* metrics));
   MOCK_METHOD1(SetAudioBufferDelay, void(size_t delay_ms));
diff --git a/modules/audio_processing/aec3/mock/mock_echo_remover.h b/modules/audio_processing/aec3/mock/mock_echo_remover.h
index f8dd348..5faea26 100644
--- a/modules/audio_processing/aec3/mock/mock_echo_remover.h
+++ b/modules/audio_processing/aec3/mock/mock_echo_remover.h
@@ -32,7 +32,7 @@
                     bool capture_signal_saturation,
                     const absl::optional<DelayEstimate>& delay_estimate,
                     RenderBuffer* render_buffer,
-                    std::vector<std::vector<std::vector<float>>>* capture));
+                    std::vector<std::vector<float>>* capture));
   MOCK_CONST_METHOD0(Delay, absl::optional<int>());
   MOCK_METHOD1(UpdateEchoLeakageStatus, void(bool leakage_detected));
   MOCK_CONST_METHOD1(GetMetrics, void(EchoControl::Metrics* metrics));
diff --git a/modules/audio_processing/aec3/mock/mock_render_delay_buffer.cc b/modules/audio_processing/aec3/mock/mock_render_delay_buffer.cc
index de87000..7526235 100644
--- a/modules/audio_processing/aec3/mock/mock_render_delay_buffer.cc
+++ b/modules/audio_processing/aec3/mock/mock_render_delay_buffer.cc
@@ -13,11 +13,9 @@
 namespace webrtc {
 namespace test {
 
-MockRenderDelayBuffer::MockRenderDelayBuffer(int sample_rate_hz,
-                                             size_t num_channels)
+MockRenderDelayBuffer::MockRenderDelayBuffer(int sample_rate_hz)
     : block_buffer_(GetRenderDelayBufferSize(4, 4, 12),
                     NumBandsForRate(sample_rate_hz),
-                    num_channels,
                     kBlockSize),
       spectrum_buffer_(block_buffer_.buffer.size(), kFftLengthBy2Plus1),
       fft_buffer_(block_buffer_.buffer.size()),
diff --git a/modules/audio_processing/aec3/mock/mock_render_delay_buffer.h b/modules/audio_processing/aec3/mock/mock_render_delay_buffer.h
index 1ad0727..0dd1b91 100644
--- a/modules/audio_processing/aec3/mock/mock_render_delay_buffer.h
+++ b/modules/audio_processing/aec3/mock/mock_render_delay_buffer.h
@@ -24,13 +24,13 @@
 
 class MockRenderDelayBuffer : public RenderDelayBuffer {
  public:
-  MockRenderDelayBuffer(int sample_rate_hz, size_t num_channels);
+  explicit MockRenderDelayBuffer(int sample_rate_hz);
   virtual ~MockRenderDelayBuffer();
 
   MOCK_METHOD0(Reset, void());
   MOCK_METHOD1(Insert,
                RenderDelayBuffer::BufferingEvent(
-                   const std::vector<std::vector<std::vector<float>>>& block));
+                   const std::vector<std::vector<float>>& block));
   MOCK_METHOD0(PrepareCaptureProcessing, RenderDelayBuffer::BufferingEvent());
   MOCK_METHOD1(AlignFromDelay, bool(size_t delay));
   MOCK_METHOD0(AlignFromExternalDelay, void());
diff --git a/modules/audio_processing/aec3/render_buffer.h b/modules/audio_processing/aec3/render_buffer.h
index 8759760..762eab8 100644
--- a/modules/audio_processing/aec3/render_buffer.h
+++ b/modules/audio_processing/aec3/render_buffer.h
@@ -36,8 +36,7 @@
   ~RenderBuffer();
 
   // Get a block.
-  const std::vector<std::vector<std::vector<float>>>& Block(
-      int buffer_offset_blocks) const {
+  const std::vector<std::vector<float>>& Block(int buffer_offset_blocks) const {
     int position =
         block_buffer_->OffsetIndex(block_buffer_->read, buffer_offset_blocks);
     return block_buffer_->buffer[position];
diff --git a/modules/audio_processing/aec3/render_buffer_unittest.cc b/modules/audio_processing/aec3/render_buffer_unittest.cc
index 4437178..fadd600 100644
--- a/modules/audio_processing/aec3/render_buffer_unittest.cc
+++ b/modules/audio_processing/aec3/render_buffer_unittest.cc
@@ -22,7 +22,7 @@
 
 // Verifies the check for non-null fft buffer.
 TEST(RenderBuffer, NullExternalFftBuffer) {
-  MatrixBuffer block_buffer(10, 3, 1, kBlockSize);
+  MatrixBuffer block_buffer(10, 3, kBlockSize);
   VectorBuffer spectrum_buffer(10, kFftLengthBy2Plus1);
   EXPECT_DEATH(RenderBuffer(&block_buffer, &spectrum_buffer, nullptr), "");
 }
@@ -30,7 +30,7 @@
 // Verifies the check for non-null spectrum buffer.
 TEST(RenderBuffer, NullExternalSpectrumBuffer) {
   FftBuffer fft_buffer(10);
-  MatrixBuffer block_buffer(10, 3, 1, kBlockSize);
+  MatrixBuffer block_buffer(10, 3, kBlockSize);
   EXPECT_DEATH(RenderBuffer(&block_buffer, nullptr, &fft_buffer), "");
 }
 
diff --git a/modules/audio_processing/aec3/render_delay_buffer.cc b/modules/audio_processing/aec3/render_delay_buffer.cc
index 379f5a1..11fe450 100644
--- a/modules/audio_processing/aec3/render_delay_buffer.cc
+++ b/modules/audio_processing/aec3/render_delay_buffer.cc
@@ -39,15 +39,12 @@
 
 class RenderDelayBufferImpl final : public RenderDelayBuffer {
  public:
-  RenderDelayBufferImpl(const EchoCanceller3Config& config,
-                        int sample_rate_hz,
-                        size_t num_render_channels);
+  RenderDelayBufferImpl(const EchoCanceller3Config& config, int sample_rate_hz);
   RenderDelayBufferImpl() = delete;
   ~RenderDelayBufferImpl() override;
 
   void Reset() override;
-  BufferingEvent Insert(
-      const std::vector<std::vector<std::vector<float>>>& block) override;
+  BufferingEvent Insert(const std::vector<std::vector<float>>& block) override;
   BufferingEvent PrepareCaptureProcessing() override;
   bool AlignFromDelay(size_t delay) override;
   void AlignFromExternalDelay() override;
@@ -93,11 +90,12 @@
   bool external_audio_buffer_delay_verified_after_reset_ = false;
   size_t min_latency_blocks_ = 0;
   size_t excess_render_detection_counter_ = 0;
+  int sample_rate_hz_;
 
   int MapDelayToTotalDelay(size_t delay) const;
   int ComputeDelay() const;
   void ApplyTotalDelay(int delay);
-  void InsertBlock(const std::vector<std::vector<std::vector<float>>>& block,
+  void InsertBlock(const std::vector<std::vector<float>>& block,
                    int previous_write);
   bool DetectActiveRender(rtc::ArrayView<const float> x) const;
   bool DetectExcessRenderBlocks();
@@ -111,8 +109,7 @@
 int RenderDelayBufferImpl::instance_count_ = 0;
 
 RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config,
-                                             int sample_rate_hz,
-                                             size_t num_render_channels)
+                                             int sample_rate_hz)
     : data_dumper_(
           new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
       optimization_(DetectOptimization()),
@@ -125,7 +122,6 @@
                                        config.delay.num_filters,
                                        config.filter.main.length_blocks),
               NumBandsForRate(sample_rate_hz),
-              num_render_channels,
               kBlockSize),
       spectra_(blocks_.buffer.size(), kFftLengthBy2Plus1),
       ffts_(blocks_.buffer.size()),
@@ -136,7 +132,9 @@
       render_decimator_(down_sampling_factor_),
       fft_(),
       render_ds_(sub_block_size_, 0.f),
-      buffer_headroom_(config.filter.main.length_blocks) {
+      buffer_headroom_(config.filter.main.length_blocks),
+      sample_rate_hz_(sample_rate_hz) {
+  RTC_DCHECK_GE(sample_rate_hz, 8000);
   RTC_DCHECK_EQ(blocks_.buffer.size(), ffts_.buffer.size());
   RTC_DCHECK_EQ(spectra_.buffer.size(), ffts_.buffer.size());
 
@@ -186,7 +184,7 @@
 
 // Inserts a new block into the render buffers.
 RenderDelayBuffer::BufferingEvent RenderDelayBufferImpl::Insert(
-    const std::vector<std::vector<std::vector<float>>>& block) {
+    const std::vector<std::vector<float>>& block) {
   ++render_call_counter_;
   if (delay_) {
     if (!last_call_was_render_) {
@@ -214,7 +212,7 @@
 
   // Detect and update render activity.
   if (!render_activity_) {
-    render_activity_counter_ += DetectActiveRender(block[0][0]) ? 1 : 0;
+    render_activity_counter_ += DetectActiveRender(block[0]) ? 1 : 0;
     render_activity_ = render_activity_counter_ >= 20;
   }
 
@@ -317,7 +315,8 @@
   }
 
   // Convert delay from milliseconds to blocks (rounded down).
-  external_audio_buffer_delay_ = delay_ms >> 2;
+  external_audio_buffer_delay_ =
+      delay_ms >> ((sample_rate_hz_ == 8000) ? 1 : 2);
 }
 
 bool RenderDelayBufferImpl::HasReceivedBufferDelay() {
@@ -360,7 +359,7 @@
 
 // Inserts a block into the render buffers.
 void RenderDelayBufferImpl::InsertBlock(
-    const std::vector<std::vector<std::vector<float>>>& block,
+    const std::vector<std::vector<float>>& block,
     int previous_write) {
   auto& b = blocks_;
   auto& lr = low_rate_;
@@ -373,14 +372,13 @@
     std::copy(block[k].begin(), block[k].end(), b.buffer[b.write][k].begin());
   }
 
-  data_dumper_->DumpWav("aec3_render_decimator_input", block[0][0].size(),
-                        block[0][0].data(), 16000, 1);
-  render_decimator_.Decimate(block[0][0], ds);
+  data_dumper_->DumpWav("aec3_render_decimator_input", block[0].size(),
+                        block[0].data(), 16000, 1);
+  render_decimator_.Decimate(block[0], ds);
   data_dumper_->DumpWav("aec3_render_decimator_output", ds.size(), ds.data(),
                         16000 / down_sampling_factor_, 1);
   std::copy(ds.rbegin(), ds.rend(), lr.buffer.begin() + lr.write);
-  fft_.PaddedFft(block[0][0], b.buffer[previous_write][0][0],
-                 &f.buffer[f.write]);
+  fft_.PaddedFft(block[0], b.buffer[previous_write][0], &f.buffer[f.write]);
   f.buffer[f.write].Spectrum(optimization_, s.buffer[s.write]);
 }
 
@@ -459,9 +457,8 @@
 }  // namespace
 
 RenderDelayBuffer* RenderDelayBuffer::Create(const EchoCanceller3Config& config,
-                                             int sample_rate_hz,
-                                             size_t num_render_channels) {
-  return new RenderDelayBufferImpl(config, sample_rate_hz, num_render_channels);
+                                             int sample_rate_hz) {
+  return new RenderDelayBufferImpl(config, sample_rate_hz);
 }
 
 }  // namespace webrtc
diff --git a/modules/audio_processing/aec3/render_delay_buffer.h b/modules/audio_processing/aec3/render_delay_buffer.h
index e53f6d2..562d2c1 100644
--- a/modules/audio_processing/aec3/render_delay_buffer.h
+++ b/modules/audio_processing/aec3/render_delay_buffer.h
@@ -33,8 +33,7 @@
   };
 
   static RenderDelayBuffer* Create(const EchoCanceller3Config& config,
-                                   int sample_rate_hz,
-                                   size_t num_render_channels);
+                                   int sample_rate_hz);
   virtual ~RenderDelayBuffer() = default;
 
   // Resets the buffer alignment.
@@ -42,7 +41,7 @@
 
   // Inserts a block into the buffer.
   virtual BufferingEvent Insert(
-      const std::vector<std::vector<std::vector<float>>>& block) = 0;
+      const std::vector<std::vector<float>>& block) = 0;
 
   // Updates the buffers one step based on the specified buffer delay. Returns
   // an enum indicating whether there was a special event that occurred.
diff --git a/modules/audio_processing/aec3/render_delay_buffer_unittest.cc b/modules/audio_processing/aec3/render_delay_buffer_unittest.cc
index 35e8131..143980c 100644
--- a/modules/audio_processing/aec3/render_delay_buffer_unittest.cc
+++ b/modules/audio_processing/aec3/render_delay_buffer_unittest.cc
@@ -35,43 +35,36 @@
 // Verifies that the buffer overflow is correctly reported.
 TEST(RenderDelayBuffer, BufferOverflow) {
   const EchoCanceller3Config config;
-  for (auto num_channels : {1, 2, 8}) {
-    for (auto rate : {16000, 32000, 48000}) {
-      SCOPED_TRACE(ProduceDebugText(rate));
-      std::unique_ptr<RenderDelayBuffer> delay_buffer(
-          RenderDelayBuffer::Create(config, rate, num_channels));
-      std::vector<std::vector<std::vector<float>>> block_to_insert(
-          NumBandsForRate(rate),
-          std::vector<std::vector<float>>(num_channels,
-                                          std::vector<float>(kBlockSize, 0.f)));
-      for (size_t k = 0; k < 10; ++k) {
-        EXPECT_EQ(RenderDelayBuffer::BufferingEvent::kNone,
-                  delay_buffer->Insert(block_to_insert));
-      }
-      bool overrun_occurred = false;
-      for (size_t k = 0; k < 1000; ++k) {
-        RenderDelayBuffer::BufferingEvent event =
-            delay_buffer->Insert(block_to_insert);
-        overrun_occurred =
-            overrun_occurred ||
-            RenderDelayBuffer::BufferingEvent::kRenderOverrun == event;
-      }
-
-      EXPECT_TRUE(overrun_occurred);
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    std::unique_ptr<RenderDelayBuffer> delay_buffer(
+        RenderDelayBuffer::Create(config, rate));
+    std::vector<std::vector<float>> block_to_insert(
+        NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
+    for (size_t k = 0; k < 10; ++k) {
+      EXPECT_EQ(RenderDelayBuffer::BufferingEvent::kNone,
+                delay_buffer->Insert(block_to_insert));
     }
+    bool overrun_occurred = false;
+    for (size_t k = 0; k < 1000; ++k) {
+      RenderDelayBuffer::BufferingEvent event =
+          delay_buffer->Insert(block_to_insert);
+      overrun_occurred =
+          overrun_occurred ||
+          RenderDelayBuffer::BufferingEvent::kRenderOverrun == event;
+    }
+
+    EXPECT_TRUE(overrun_occurred);
   }
 }
 
 // Verifies that the check for available block works.
 TEST(RenderDelayBuffer, AvailableBlock) {
-  constexpr size_t kNumChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
-  std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create(
-      EchoCanceller3Config(), kSampleRateHz, kNumChannels));
-  std::vector<std::vector<std::vector<float>>> input_block(
-      kNumBands, std::vector<std::vector<float>>(
-                     kNumChannels, std::vector<float>(kBlockSize, 1.f)));
+  constexpr size_t kNumBands = 1;
+  std::unique_ptr<RenderDelayBuffer> delay_buffer(
+      RenderDelayBuffer::Create(EchoCanceller3Config(), 16000));
+  std::vector<std::vector<float>> input_block(
+      kNumBands, std::vector<float>(kBlockSize, 1.f));
   EXPECT_EQ(RenderDelayBuffer::BufferingEvent::kNone,
             delay_buffer->Insert(input_block));
   delay_buffer->PrepareCaptureProcessing();
@@ -81,7 +74,7 @@
 TEST(RenderDelayBuffer, AlignFromDelay) {
   EchoCanceller3Config config;
   std::unique_ptr<RenderDelayBuffer> delay_buffer(
-      RenderDelayBuffer::Create(config, 16000, 1));
+      RenderDelayBuffer::Create(config, 16000));
   ASSERT_TRUE(delay_buffer->Delay());
   delay_buffer->Reset();
   size_t initial_internal_delay = 0;
@@ -99,55 +92,32 @@
 // tests on test bots has been fixed.
 TEST(RenderDelayBuffer, DISABLED_WrongDelay) {
   std::unique_ptr<RenderDelayBuffer> delay_buffer(
-      RenderDelayBuffer::Create(EchoCanceller3Config(), 48000, 1));
+      RenderDelayBuffer::Create(EchoCanceller3Config(), 48000));
   EXPECT_DEATH(delay_buffer->AlignFromDelay(21), "");
 }
 
 // Verifies the check for the number of bands in the inserted blocks.
 TEST(RenderDelayBuffer, WrongNumberOfBands) {
   for (auto rate : {16000, 32000, 48000}) {
-    for (size_t num_channels : {1, 2, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate));
-      std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create(
-          EchoCanceller3Config(), rate, num_channels));
-      std::vector<std::vector<std::vector<float>>> block_to_insert(
-          NumBandsForRate(rate < 48000 ? rate + 16000 : 16000),
-          std::vector<std::vector<float>>(num_channels,
-                                          std::vector<float>(kBlockSize, 0.f)));
-      EXPECT_DEATH(delay_buffer->Insert(block_to_insert), "");
-    }
-  }
-}
-
-// Verifies the check for the number of channels in the inserted blocks.
-TEST(RenderDelayBuffer, WrongNumberOfChannels) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (size_t num_channels : {1, 2, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate));
-      std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create(
-          EchoCanceller3Config(), rate, num_channels));
-      std::vector<std::vector<std::vector<float>>> block_to_insert(
-          NumBandsForRate(rate),
-          std::vector<std::vector<float>>(num_channels + 1,
-                                          std::vector<float>(kBlockSize, 0.f)));
-      EXPECT_DEATH(delay_buffer->Insert(block_to_insert), "");
-    }
+    SCOPED_TRACE(ProduceDebugText(rate));
+    std::unique_ptr<RenderDelayBuffer> delay_buffer(
+        RenderDelayBuffer::Create(EchoCanceller3Config(), rate));
+    std::vector<std::vector<float>> block_to_insert(
+        NumBandsForRate(rate < 48000 ? rate + 16000 : 16000),
+        std::vector<float>(kBlockSize, 0.f));
+    EXPECT_DEATH(delay_buffer->Insert(block_to_insert), "");
   }
 }
 
 // Verifies the check of the length of the inserted blocks.
 TEST(RenderDelayBuffer, WrongBlockLength) {
-  for (auto rate : {16000, 32000, 48000}) {
-    for (size_t num_channels : {1, 2, 8}) {
-      SCOPED_TRACE(ProduceDebugText(rate));
-      std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create(
-          EchoCanceller3Config(), rate, num_channels));
-      std::vector<std::vector<std::vector<float>>> block_to_insert(
-          NumBandsForRate(rate),
-          std::vector<std::vector<float>>(
-              num_channels, std::vector<float>(kBlockSize - 1, 0.f)));
-      EXPECT_DEATH(delay_buffer->Insert(block_to_insert), "");
-    }
+  for (auto rate : {8000, 16000, 32000, 48000}) {
+    SCOPED_TRACE(ProduceDebugText(rate));
+    std::unique_ptr<RenderDelayBuffer> delay_buffer(
+        RenderDelayBuffer::Create(EchoCanceller3Config(), 48000));
+    std::vector<std::vector<float>> block_to_insert(
+        NumBandsForRate(rate), std::vector<float>(kBlockSize - 1, 0.f));
+    EXPECT_DEATH(delay_buffer->Insert(block_to_insert), "");
   }
 }
 
diff --git a/modules/audio_processing/aec3/render_delay_controller_unittest.cc b/modules/audio_processing/aec3/render_delay_controller_unittest.cc
index 995ecc9..ff3fb7b 100644
--- a/modules/audio_processing/aec3/render_delay_controller_unittest.cc
+++ b/modules/audio_processing/aec3/render_delay_controller_unittest.cc
@@ -53,10 +53,10 @@
     for (auto down_sampling_factor : kDownSamplingFactors) {
       config.delay.down_sampling_factor = down_sampling_factor;
       config.delay.num_filters = num_matched_filters;
-      for (auto rate : {16000, 32000, 48000}) {
+      for (auto rate : {8000, 16000, 32000, 48000}) {
         SCOPED_TRACE(ProduceDebugText(rate));
         std::unique_ptr<RenderDelayBuffer> delay_buffer(
-            RenderDelayBuffer::Create(config, rate, 1));
+            RenderDelayBuffer::Create(config, rate));
         std::unique_ptr<RenderDelayController> delay_controller(
             RenderDelayController::Create(config, rate));
         for (size_t k = 0; k < 100; ++k) {
@@ -72,7 +72,6 @@
 
 // Verifies the basic API call sequence.
 TEST(RenderDelayController, BasicApiCalls) {
-  constexpr size_t kNumChannels = 1;
   std::vector<float> capture_block(kBlockSize, 0.f);
   absl::optional<DelayEstimate> delay_blocks;
   for (size_t num_matched_filters = 4; num_matched_filters == 10;
@@ -81,13 +80,11 @@
       EchoCanceller3Config config;
       config.delay.down_sampling_factor = down_sampling_factor;
       config.delay.num_filters = num_matched_filters;
-      for (auto rate : {16000, 32000, 48000}) {
-        std::vector<std::vector<std::vector<float>>> render_block(
-            NumBandsForRate(rate),
-            std::vector<std::vector<float>>(
-                kNumChannels, std::vector<float>(kBlockSize, 0.f)));
+      for (auto rate : {8000, 16000, 32000, 48000}) {
+        std::vector<std::vector<float>> render_block(
+            NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
         std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-            RenderDelayBuffer::Create(config, rate, kNumChannels));
+            RenderDelayBuffer::Create(config, rate));
         std::unique_ptr<RenderDelayController> delay_controller(
             RenderDelayController::Create(EchoCanceller3Config(), rate));
         for (size_t k = 0; k < 10; ++k) {
@@ -117,45 +114,35 @@
       config.delay.down_sampling_factor = down_sampling_factor;
       config.delay.num_filters = num_matched_filters;
 
-      for (size_t num_render_channels : {1, 2}) {
-        for (auto rate : {16000, 32000, 48000}) {
-          std::vector<std::vector<std::vector<float>>> render_block(
-              NumBandsForRate(rate),
-              std::vector<std::vector<float>>(
-                  num_render_channels, std::vector<float>(kBlockSize, 0.f)));
+      for (auto rate : {8000, 16000, 32000, 48000}) {
+        std::vector<std::vector<float>> render_block(
+            NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
 
-          for (size_t delay_samples : {15, 50, 150, 200, 800, 4000}) {
-            absl::optional<DelayEstimate> delay_blocks;
-            SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
-            std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-                RenderDelayBuffer::Create(config, rate, num_render_channels));
-            std::unique_ptr<RenderDelayController> delay_controller(
-                RenderDelayController::Create(config, rate));
-            DelayBuffer<float> signal_delay_buffer(delay_samples);
-            for (size_t k = 0; k < (400 + delay_samples / kBlockSize); ++k) {
-              for (size_t band = 0; band < render_block.size(); ++band) {
-                for (size_t channel = 0; channel < render_block[band].size();
-                     ++channel) {
-                  RandomizeSampleVector(&random_generator,
-                                        render_block[band][channel]);
-                }
-              }
-              signal_delay_buffer.Delay(render_block[0][0], capture_block);
-              render_delay_buffer->Insert(render_block);
-              render_delay_buffer->PrepareCaptureProcessing();
-              delay_blocks = delay_controller->GetDelay(
-                  render_delay_buffer->GetDownsampledRenderBuffer(),
-                  render_delay_buffer->Delay(), capture_block);
-            }
-            ASSERT_TRUE(!!delay_blocks);
-
-            constexpr int kDelayHeadroomBlocks = 1;
-            size_t expected_delay_blocks =
-                std::max(0, static_cast<int>(delay_samples / kBlockSize) -
-                                kDelayHeadroomBlocks);
-
-            EXPECT_EQ(expected_delay_blocks, delay_blocks->delay);
+        for (size_t delay_samples : {15, 50, 150, 200, 800, 4000}) {
+          absl::optional<DelayEstimate> delay_blocks;
+          SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
+          std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
+              RenderDelayBuffer::Create(config, rate));
+          std::unique_ptr<RenderDelayController> delay_controller(
+              RenderDelayController::Create(config, rate));
+          DelayBuffer<float> signal_delay_buffer(delay_samples);
+          for (size_t k = 0; k < (400 + delay_samples / kBlockSize); ++k) {
+            RandomizeSampleVector(&random_generator, render_block[0]);
+            signal_delay_buffer.Delay(render_block[0], capture_block);
+            render_delay_buffer->Insert(render_block);
+            render_delay_buffer->PrepareCaptureProcessing();
+            delay_blocks = delay_controller->GetDelay(
+                render_delay_buffer->GetDownsampledRenderBuffer(),
+                render_delay_buffer->Delay(), capture_block);
           }
+          ASSERT_TRUE(!!delay_blocks);
+
+          constexpr int kDelayHeadroomBlocks = 1;
+          size_t expected_delay_blocks =
+              std::max(0, static_cast<int>(delay_samples / kBlockSize) -
+                              kDelayHeadroomBlocks);
+
+          EXPECT_EQ(expected_delay_blocks, delay_blocks->delay);
         }
       }
     }
@@ -166,41 +153,35 @@
 // delays.
 TEST(RenderDelayController, NonCausalAlignment) {
   Random random_generator(42U);
-  constexpr size_t kNumRenderChannels = 1;
-  constexpr size_t kNumCaptureChannels = 1;
   for (size_t num_matched_filters = 4; num_matched_filters == 10;
        num_matched_filters++) {
     for (auto down_sampling_factor : kDownSamplingFactors) {
       EchoCanceller3Config config;
       config.delay.down_sampling_factor = down_sampling_factor;
       config.delay.num_filters = num_matched_filters;
-      for (auto rate : {16000, 32000, 48000}) {
-        std::vector<std::vector<std::vector<float>>> render_block(
-            NumBandsForRate(rate),
-            std::vector<std::vector<float>>(
-                kNumRenderChannels, std::vector<float>(kBlockSize, 0.f)));
-        std::vector<std::vector<std::vector<float>>> capture_block(
-            NumBandsForRate(rate),
-            std::vector<std::vector<float>>(
-                kNumCaptureChannels, std::vector<float>(kBlockSize, 0.f)));
+      for (auto rate : {8000, 16000, 32000, 48000}) {
+        std::vector<std::vector<float>> render_block(
+            NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
+        std::vector<std::vector<float>> capture_block(
+            NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
 
         for (int delay_samples : {-15, -50, -150, -200}) {
           absl::optional<DelayEstimate> delay_blocks;
           SCOPED_TRACE(ProduceDebugText(rate, -delay_samples));
           std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-              RenderDelayBuffer::Create(config, rate, kNumRenderChannels));
+              RenderDelayBuffer::Create(config, rate));
           std::unique_ptr<RenderDelayController> delay_controller(
               RenderDelayController::Create(EchoCanceller3Config(), rate));
           DelayBuffer<float> signal_delay_buffer(-delay_samples);
           for (int k = 0;
                k < (400 - delay_samples / static_cast<int>(kBlockSize)); ++k) {
-            RandomizeSampleVector(&random_generator, capture_block[0][0]);
-            signal_delay_buffer.Delay(capture_block[0][0], render_block[0][0]);
+            RandomizeSampleVector(&random_generator, capture_block[0]);
+            signal_delay_buffer.Delay(capture_block[0], render_block[0]);
             render_delay_buffer->Insert(render_block);
             render_delay_buffer->PrepareCaptureProcessing();
             delay_blocks = delay_controller->GetDelay(
                 render_delay_buffer->GetDownsampledRenderBuffer(),
-                render_delay_buffer->Delay(), capture_block[0][0]);
+                render_delay_buffer->Delay(), capture_block[0]);
           }
 
           ASSERT_FALSE(delay_blocks);
@@ -214,7 +195,6 @@
 // simple timeshifts between the signals when there is jitter in the API calls.
 TEST(RenderDelayController, AlignmentWithJitter) {
   Random random_generator(42U);
-  constexpr size_t kNumRenderChannels = 1;
   std::vector<float> capture_block(kBlockSize, 0.f);
   for (size_t num_matched_filters = 4; num_matched_filters == 10;
        num_matched_filters++) {
@@ -222,16 +202,14 @@
       EchoCanceller3Config config;
       config.delay.down_sampling_factor = down_sampling_factor;
       config.delay.num_filters = num_matched_filters;
-      for (auto rate : {16000, 32000, 48000}) {
-        std::vector<std::vector<std::vector<float>>> render_block(
-            NumBandsForRate(rate),
-            std::vector<std::vector<float>>(
-                kNumRenderChannels, std::vector<float>(kBlockSize, 0.f)));
+      for (auto rate : {8000, 16000, 32000, 48000}) {
+        std::vector<std::vector<float>> render_block(
+            NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
         for (size_t delay_samples : {15, 50, 300, 800}) {
           absl::optional<DelayEstimate> delay_blocks;
           SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
           std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-              RenderDelayBuffer::Create(config, rate, kNumRenderChannels));
+              RenderDelayBuffer::Create(config, rate));
           std::unique_ptr<RenderDelayController> delay_controller(
               RenderDelayController::Create(config, rate));
           DelayBuffer<float> signal_delay_buffer(delay_samples);
@@ -242,8 +220,8 @@
                ++j) {
             std::vector<std::vector<float>> capture_block_buffer;
             for (size_t k = 0; k < (kMaxTestJitterBlocks - 1); ++k) {
-              RandomizeSampleVector(&random_generator, render_block[0][0]);
-              signal_delay_buffer.Delay(render_block[0][0], capture_block);
+              RandomizeSampleVector(&random_generator, render_block[0]);
+              signal_delay_buffer.Delay(render_block[0], capture_block);
               capture_block_buffer.push_back(capture_block);
               render_delay_buffer->Insert(render_block);
             }
@@ -281,10 +259,10 @@
       EchoCanceller3Config config;
       config.delay.down_sampling_factor = down_sampling_factor;
       config.delay.num_filters = num_matched_filters;
-      for (auto rate : {16000, 32000, 48000}) {
+      for (auto rate : {8000, 16000, 32000, 48000}) {
         SCOPED_TRACE(ProduceDebugText(rate));
         std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-            RenderDelayBuffer::Create(config, rate, 1));
+            RenderDelayBuffer::Create(config, rate));
 
         std::unique_ptr<RenderDelayController> delay_controller(
             RenderDelayController::Create(config, rate));
@@ -299,10 +277,10 @@
 TEST(RenderDelayController, WrongCaptureSize) {
   std::vector<float> block(kBlockSize - 1, 0.f);
   EchoCanceller3Config config;
-  for (auto rate : {16000, 32000, 48000}) {
+  for (auto rate : {8000, 16000, 32000, 48000}) {
     SCOPED_TRACE(ProduceDebugText(rate));
     std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-        RenderDelayBuffer::Create(config, rate, 1));
+        RenderDelayBuffer::Create(config, rate));
     EXPECT_DEATH(
         std::unique_ptr<RenderDelayController>(
             RenderDelayController::Create(EchoCanceller3Config(), rate))
@@ -320,7 +298,7 @@
     SCOPED_TRACE(ProduceDebugText(rate));
     EchoCanceller3Config config;
     std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-        RenderDelayBuffer::Create(config, rate, 1));
+        RenderDelayBuffer::Create(config, rate));
     EXPECT_DEATH(
         std::unique_ptr<RenderDelayController>(
             RenderDelayController::Create(EchoCanceller3Config(), rate)),
diff --git a/modules/audio_processing/aec3/render_signal_analyzer.cc b/modules/audio_processing/aec3/render_signal_analyzer.cc
index 88bacaf..e3e41a7 100644
--- a/modules/audio_processing/aec3/render_signal_analyzer.cc
+++ b/modules/audio_processing/aec3/render_signal_analyzer.cc
@@ -66,15 +66,13 @@
   }
 
   // Assess the render signal strength.
-  const std::vector<std::vector<std::vector<float>>>& x_latest =
-      render_buffer.Block(0);
-  auto result0 =
-      std::minmax_element(x_latest[0][0].begin(), x_latest[0][0].end());
+  const std::vector<std::vector<float>>& x_latest = render_buffer.Block(0);
+  auto result0 = std::minmax_element(x_latest[0].begin(), x_latest[0].end());
   float max_abs = std::max(fabs(*result0.first), fabs(*result0.second));
 
   if (x_latest.size() > 1) {
     const auto result1 =
-        std::minmax_element(x_latest[1][0].begin(), x_latest[1][0].end());
+        std::minmax_element(x_latest[1].begin(), x_latest[1].end());
     max_abs =
         std::max(max_abs, static_cast<float>(std::max(fabs(*result1.first),
                                                       fabs(*result1.second))));
diff --git a/modules/audio_processing/aec3/render_signal_analyzer_unittest.cc b/modules/audio_processing/aec3/render_signal_analyzer_unittest.cc
index 27a31f0..53a41b1 100644
--- a/modules/audio_processing/aec3/render_signal_analyzer_unittest.cc
+++ b/modules/audio_processing/aec3/render_signal_analyzer_unittest.cc
@@ -33,23 +33,14 @@
 void ProduceSinusoid(int sample_rate_hz,
                      float sinusoidal_frequency_hz,
                      size_t* sample_counter,
-                     std::vector<std::vector<std::vector<float>>>* x) {
+                     rtc::ArrayView<float> x) {
   // Produce a sinusoid of the specified frequency.
   for (size_t k = *sample_counter, j = 0; k < (*sample_counter + kBlockSize);
        ++k, ++j) {
-    for (size_t channel = 0; channel < (*x)[0].size(); ++channel) {
-      (*x)[0][channel][j] =
-          32767.f *
-          std::sin(2.f * kPi * sinusoidal_frequency_hz * k / sample_rate_hz);
-    }
+    x[j] = 32767.f *
+           std::sin(2.f * kPi * sinusoidal_frequency_hz * k / sample_rate_hz);
   }
   *sample_counter = *sample_counter + kBlockSize;
-
-  for (size_t band = 1; band < x->size(); ++band) {
-    for (size_t channel = 0; channel < (*x)[band].size(); ++channel) {
-      std::fill((*x)[band][channel].begin(), (*x)[band][channel].end(), 0.f);
-    }
-  }
 }
 
 }  // namespace
@@ -67,17 +58,15 @@
 TEST(RenderSignalAnalyzer, NoFalseDetectionOfNarrowBands) {
   RenderSignalAnalyzer analyzer(EchoCanceller3Config{});
   Random random_generator(42U);
-  std::vector<std::vector<std::vector<float>>> x(
-      3,
-      std::vector<std::vector<float>>(1, std::vector<float>(kBlockSize, 0.f)));
+  std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
   std::array<float, kBlockSize> x_old;
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(EchoCanceller3Config(), 48000, 1));
+      RenderDelayBuffer::Create(EchoCanceller3Config(), 48000));
   std::array<float, kFftLengthBy2Plus1> mask;
   x_old.fill(0.f);
 
   for (size_t k = 0; k < 100; ++k) {
-    RandomizeSampleVector(&random_generator, x[0][0]);
+    RandomizeSampleVector(&random_generator, x[0]);
 
     render_delay_buffer->Insert(x);
     if (k == 0) {
@@ -100,17 +89,12 @@
 TEST(RenderSignalAnalyzer, NarrowBandDetection) {
   RenderSignalAnalyzer analyzer(EchoCanceller3Config{});
   Random random_generator(42U);
-  constexpr size_t kNumChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
-  std::vector<std::vector<std::vector<float>>> x(
-      kNumBands, std::vector<std::vector<float>>(
-                     kNumChannels, std::vector<float>(kBlockSize, 0.f)));
+  std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
   std::array<float, kBlockSize> x_old;
   Aec3Fft fft;
   EchoCanceller3Config config;
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(config, kSampleRateHz, kNumChannels));
+      RenderDelayBuffer::Create(config, 48000));
 
   std::array<float, kFftLengthBy2Plus1> mask;
   x_old.fill(0.f);
@@ -120,7 +104,7 @@
     size_t sample_counter = 0;
     for (size_t k = 0; k < 100; ++k) {
       ProduceSinusoid(16000, 16000 / 2 * kSinusFrequencyBin / kFftLengthBy2,
-                      &sample_counter, &x);
+                      &sample_counter, x[0]);
 
       render_delay_buffer->Insert(x);
       if (k == 0) {
diff --git a/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc b/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc
index 863f8f8..d277d42 100644
--- a/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc
+++ b/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc
@@ -27,7 +27,7 @@
   EchoCanceller3Config config;
   AecState aec_state(config);
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(config, 48000, 1));
+      RenderDelayBuffer::Create(config, 48000));
   std::vector<std::array<float, kFftLengthBy2Plus1>> H2;
   std::array<float, kFftLengthBy2Plus1> S2_linear;
   std::array<float, kFftLengthBy2Plus1> Y2;
@@ -42,16 +42,12 @@
 // TODO(peah): This test is broken in the sense that it not at all tests what it
 // seems to test. Enable the test once that is adressed.
 TEST(ResidualEchoEstimator, DISABLED_BasicTest) {
-  constexpr size_t kNumChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
-
   EchoCanceller3Config config;
   config.ep_strength.default_len = 0.f;
   ResidualEchoEstimator estimator(config);
   AecState aec_state(config);
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(config, kSampleRateHz, kNumChannels));
+      RenderDelayBuffer::Create(config, 48000));
 
   std::array<float, kFftLengthBy2Plus1> E2_main;
   std::array<float, kFftLengthBy2Plus1> E2_shadow;
@@ -61,9 +57,7 @@
   std::array<float, kFftLengthBy2Plus1> R2;
   EchoPathVariability echo_path_variability(
       false, EchoPathVariability::DelayAdjustment::kNone, false);
-  std::vector<std::vector<std::vector<float>>> x(
-      kNumBands, std::vector<std::vector<float>>(
-                     kNumChannels, std::vector<float>(kBlockSize, 0.f)));
+  std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
   std::vector<std::array<float, kFftLengthBy2Plus1>> H2(10);
   Random random_generator(42U);
   SubtractorOutput output;
@@ -92,8 +86,8 @@
   Y2.fill(kLevel);
 
   for (int k = 0; k < 1993; ++k) {
-    RandomizeSampleVector(&random_generator, x[0][0]);
-    std::for_each(x[0][0].begin(), x[0][0].end(), [](float& a) { a /= 30.f; });
+    RandomizeSampleVector(&random_generator, x[0]);
+    std::for_each(x[0].begin(), x[0].end(), [](float& a) { a /= 30.f; });
     render_delay_buffer->Insert(x);
     if (k == 0) {
       render_delay_buffer->Reset();
diff --git a/modules/audio_processing/aec3/shadow_filter_update_gain_unittest.cc b/modules/audio_processing/aec3/shadow_filter_update_gain_unittest.cc
index b49b00d..7372e5e 100644
--- a/modules/audio_processing/aec3/shadow_filter_update_gain_unittest.cc
+++ b/modules/audio_processing/aec3/shadow_filter_update_gain_unittest.cc
@@ -32,7 +32,6 @@
 // gain functionality.
 void RunFilterUpdateTest(int num_blocks_to_process,
                          size_t delay_samples,
-                         size_t num_render_channels,
                          int filter_length_blocks,
                          const std::vector<int>& blocks_with_saturation,
                          std::array<float, kBlockSize>* e_last_block,
@@ -51,19 +50,17 @@
                                   DetectOptimization(), &data_dumper);
   Aec3Fft fft;
 
-  constexpr int kSampleRateHz = 48000;
   config.delay.default_delay = 1;
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(config, kSampleRateHz, num_render_channels));
+      RenderDelayBuffer::Create(config, 48000));
 
+  std::array<float, kBlockSize> x_old;
+  x_old.fill(0.f);
   ShadowFilterUpdateGain shadow_gain(
       config.filter.shadow, config.filter.config_change_duration_blocks);
   Random random_generator(42U);
-  std::vector<std::vector<std::vector<float>>> x(
-      NumBandsForRate(kSampleRateHz),
-      std::vector<std::vector<float>>(num_render_channels,
-                                      std::vector<float>(kBlockSize, 0.f)));
-  std::array<float, kBlockSize> y;
+  std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
+  std::vector<float> y(kBlockSize, 0.f);
   AecState aec_state(config);
   RenderSignalAnalyzer render_signal_analyzer(config);
   std::array<float, kFftLength> s;
@@ -82,12 +79,8 @@
                   k) != blocks_with_saturation.end();
 
     // Create the render signal.
-    for (size_t band = 0; band < x.size(); ++band) {
-      for (size_t channel = 0; channel < x[band].size(); ++channel) {
-        RandomizeSampleVector(&random_generator, x[band][channel]);
-      }
-    }
-    delay_buffer.Delay(x[0][0], y);
+    RandomizeSampleVector(&random_generator, x[0]);
+    delay_buffer.Delay(x[0], y);
 
     render_delay_buffer->Insert(x);
     if (k == 0) {
@@ -158,30 +151,25 @@
 TEST(ShadowFilterUpdateGain, GainCausesFilterToConverge) {
   std::vector<int> blocks_with_echo_path_changes;
   std::vector<int> blocks_with_saturation;
+  for (size_t filter_length_blocks : {12, 20, 30}) {
+    for (size_t delay_samples : {0, 64, 150, 200, 301}) {
+      SCOPED_TRACE(ProduceDebugText(delay_samples, filter_length_blocks));
 
-  for (size_t num_render_channels : {1, 2, 8}) {
-    for (size_t filter_length_blocks : {12, 20, 30}) {
-      for (size_t delay_samples : {0, 64, 150, 200, 301}) {
-        SCOPED_TRACE(ProduceDebugText(delay_samples, filter_length_blocks));
+      std::array<float, kBlockSize> e;
+      std::array<float, kBlockSize> y;
+      FftData G;
 
-        std::array<float, kBlockSize> e;
-        std::array<float, kBlockSize> y;
-        FftData G;
+      RunFilterUpdateTest(1000, delay_samples, filter_length_blocks,
+                          blocks_with_saturation, &e, &y, &G);
 
-        RunFilterUpdateTest(1000, delay_samples, num_render_channels,
-                            filter_length_blocks, blocks_with_saturation, &e,
-                            &y, &G);
-
-        // Verify that the main filter is able to perform well.
-        // Use different criteria to take overmodelling into account.
-        if (filter_length_blocks == 12) {
-          EXPECT_LT(
-              1000 * std::inner_product(e.begin(), e.end(), e.begin(), 0.f),
-              std::inner_product(y.begin(), y.end(), y.begin(), 0.f));
-        } else {
-          EXPECT_LT(std::inner_product(e.begin(), e.end(), e.begin(), 0.f),
-                    std::inner_product(y.begin(), y.end(), y.begin(), 0.f));
-        }
+      // Verify that the main filter is able to perform well.
+      // Use different criteria to take overmodelling into account.
+      if (filter_length_blocks == 12) {
+        EXPECT_LT(1000 * std::inner_product(e.begin(), e.end(), e.begin(), 0.f),
+                  std::inner_product(y.begin(), y.end(), y.begin(), 0.f));
+      } else {
+        EXPECT_LT(std::inner_product(e.begin(), e.end(), e.begin(), 0.f),
+                  std::inner_product(y.begin(), y.end(), y.begin(), 0.f));
       }
     }
   }
@@ -190,38 +178,36 @@
 // Verifies that the magnitude of the gain on average decreases for a
 // persistently exciting signal.
 TEST(ShadowFilterUpdateGain, DecreasingGain) {
-  for (size_t num_render_channels : {1, 2, 8}) {
-    for (size_t filter_length_blocks : {12, 20, 30}) {
-      SCOPED_TRACE(ProduceDebugText(filter_length_blocks));
-      std::vector<int> blocks_with_echo_path_changes;
-      std::vector<int> blocks_with_saturation;
+  for (size_t filter_length_blocks : {12, 20, 30}) {
+    SCOPED_TRACE(ProduceDebugText(filter_length_blocks));
+    std::vector<int> blocks_with_echo_path_changes;
+    std::vector<int> blocks_with_saturation;
 
-      std::array<float, kBlockSize> e;
-      std::array<float, kBlockSize> y;
-      FftData G_a;
-      FftData G_b;
-      FftData G_c;
-      std::array<float, kFftLengthBy2Plus1> G_a_power;
-      std::array<float, kFftLengthBy2Plus1> G_b_power;
-      std::array<float, kFftLengthBy2Plus1> G_c_power;
+    std::array<float, kBlockSize> e;
+    std::array<float, kBlockSize> y;
+    FftData G_a;
+    FftData G_b;
+    FftData G_c;
+    std::array<float, kFftLengthBy2Plus1> G_a_power;
+    std::array<float, kFftLengthBy2Plus1> G_b_power;
+    std::array<float, kFftLengthBy2Plus1> G_c_power;
 
-      RunFilterUpdateTest(100, 65, num_render_channels, filter_length_blocks,
-                          blocks_with_saturation, &e, &y, &G_a);
-      RunFilterUpdateTest(200, 65, num_render_channels, filter_length_blocks,
-                          blocks_with_saturation, &e, &y, &G_b);
-      RunFilterUpdateTest(300, 65, num_render_channels, filter_length_blocks,
-                          blocks_with_saturation, &e, &y, &G_c);
+    RunFilterUpdateTest(100, 65, filter_length_blocks, blocks_with_saturation,
+                        &e, &y, &G_a);
+    RunFilterUpdateTest(200, 65, filter_length_blocks, blocks_with_saturation,
+                        &e, &y, &G_b);
+    RunFilterUpdateTest(300, 65, filter_length_blocks, blocks_with_saturation,
+                        &e, &y, &G_c);
 
-      G_a.Spectrum(Aec3Optimization::kNone, G_a_power);
-      G_b.Spectrum(Aec3Optimization::kNone, G_b_power);
-      G_c.Spectrum(Aec3Optimization::kNone, G_c_power);
+    G_a.Spectrum(Aec3Optimization::kNone, G_a_power);
+    G_b.Spectrum(Aec3Optimization::kNone, G_b_power);
+    G_c.Spectrum(Aec3Optimization::kNone, G_c_power);
 
-      EXPECT_GT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.),
-                std::accumulate(G_b_power.begin(), G_b_power.end(), 0.));
+    EXPECT_GT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.),
+              std::accumulate(G_b_power.begin(), G_b_power.end(), 0.));
 
-      EXPECT_GT(std::accumulate(G_b_power.begin(), G_b_power.end(), 0.),
-                std::accumulate(G_c_power.begin(), G_c_power.end(), 0.));
-    }
+    EXPECT_GT(std::accumulate(G_b_power.begin(), G_b_power.end(), 0.),
+              std::accumulate(G_c_power.begin(), G_c_power.end(), 0.));
   }
 }
 
@@ -232,23 +218,21 @@
   for (int k = 99; k < 200; ++k) {
     blocks_with_saturation.push_back(k);
   }
-  for (size_t num_render_channels : {1, 2, 8}) {
-    for (size_t filter_length_blocks : {12, 20, 30}) {
-      SCOPED_TRACE(ProduceDebugText(filter_length_blocks));
+  for (size_t filter_length_blocks : {12, 20, 30}) {
+    SCOPED_TRACE(ProduceDebugText(filter_length_blocks));
 
-      std::array<float, kBlockSize> e;
-      std::array<float, kBlockSize> y;
-      FftData G_a;
-      FftData G_a_ref;
-      G_a_ref.re.fill(0.f);
-      G_a_ref.im.fill(0.f);
+    std::array<float, kBlockSize> e;
+    std::array<float, kBlockSize> y;
+    FftData G_a;
+    FftData G_a_ref;
+    G_a_ref.re.fill(0.f);
+    G_a_ref.im.fill(0.f);
 
-      RunFilterUpdateTest(100, 65, num_render_channels, filter_length_blocks,
-                          blocks_with_saturation, &e, &y, &G_a);
+    RunFilterUpdateTest(100, 65, filter_length_blocks, blocks_with_saturation,
+                        &e, &y, &G_a);
 
-      EXPECT_EQ(G_a_ref.re, G_a.re);
-      EXPECT_EQ(G_a_ref.im, G_a.im);
-    }
+    EXPECT_EQ(G_a_ref.re, G_a.re);
+    EXPECT_EQ(G_a_ref.im, G_a.im);
   }
 }
 
diff --git a/modules/audio_processing/aec3/signal_dependent_erle_estimator_unittest.cc b/modules/audio_processing/aec3/signal_dependent_erle_estimator_unittest.cc
index b8c83f7..f27c905 100644
--- a/modules/audio_processing/aec3/signal_dependent_erle_estimator_unittest.cc
+++ b/modules/audio_processing/aec3/signal_dependent_erle_estimator_unittest.cc
@@ -24,7 +24,7 @@
 
 namespace {
 
-void GetActiveFrame(std::vector<std::vector<std::vector<float>>>* x) {
+void GetActiveFrame(rtc::ArrayView<float> x) {
   const std::array<float, kBlockSize> frame = {
       7459.88, 17209.6, 17383,   20768.9, 16816.7, 18386.3, 4492.83, 9675.85,
       6665.52, 14808.6, 9342.3,  7483.28, 19261.7, 4145.98, 1622.18, 13475.2,
@@ -34,12 +34,8 @@
       11405,   15031.4, 14541.6, 19765.5, 18346.3, 19350.2, 3157.47, 18095.8,
       1743.68, 21328.2, 19727.5, 7295.16, 10332.4, 11055.5, 20107.4, 14708.4,
       12416.2, 16434,   2454.69, 9840.8,  6867.23, 1615.75, 6059.9,  8394.19};
-  for (size_t band = 0; band < x->size(); ++band) {
-    for (size_t channel = 0; channel < (*x)[band].size(); ++channel) {
-      RTC_DCHECK_GE((*x)[band][channel].size(), frame.size());
-      std::copy(frame.begin(), frame.end(), (*x)[band][channel].begin());
-    }
-  }
+  RTC_DCHECK_GE(x.size(), frame.size());
+  std::copy(frame.begin(), frame.end(), x.begin());
 }
 
 class TestInputs {
@@ -62,15 +58,13 @@
   std::array<float, kFftLengthBy2Plus1> Y2_;
   std::array<float, kFftLengthBy2Plus1> E2_;
   std::vector<std::array<float, kFftLengthBy2Plus1>> H2_;
-  std::vector<std::vector<std::vector<float>>> x_;
+  std::vector<std::vector<float>> x_;
 };
 
 TestInputs::TestInputs(const EchoCanceller3Config& cfg)
-    : render_delay_buffer_(RenderDelayBuffer::Create(cfg, 16000, 1)),
+    : render_delay_buffer_(RenderDelayBuffer::Create(cfg, 16000)),
       H2_(cfg.filter.main.length_blocks),
-      x_(1,
-         std::vector<std::vector<float>>(1,
-                                         std::vector<float>(kBlockSize, 0.f))) {
+      x_(1, std::vector<float>(kBlockSize, 0.f)) {
   render_delay_buffer_->AlignFromDelay(4);
   render_buffer_ = render_delay_buffer_->GetRenderBuffer();
   for (auto& H : H2_) {
@@ -83,9 +77,9 @@
 
 void TestInputs::Update() {
   if (n_ % 2 == 0) {
-    std::fill(x_[0][0].begin(), x_[0][0].end(), 0.f);
+    std::fill(x_[0].begin(), x_[0].end(), 0.f);
   } else {
-    GetActiveFrame(&x_);
+    GetActiveFrame(x_[0]);
   }
 
   render_delay_buffer_->Insert(x_);
diff --git a/modules/audio_processing/aec3/subtractor_unittest.cc b/modules/audio_processing/aec3/subtractor_unittest.cc
index f29b446..bcf3b27 100644
--- a/modules/audio_processing/aec3/subtractor_unittest.cc
+++ b/modules/audio_processing/aec3/subtractor_unittest.cc
@@ -31,24 +31,19 @@
                         bool uncorrelated_inputs,
                         const std::vector<int>& blocks_with_echo_path_changes) {
   ApmDataDumper data_dumper(42);
-  constexpr size_t kNumChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
   EchoCanceller3Config config;
   config.filter.main.length_blocks = main_filter_length_blocks;
   config.filter.shadow.length_blocks = shadow_filter_length_blocks;
 
   Subtractor subtractor(config, &data_dumper, DetectOptimization());
   absl::optional<DelayEstimate> delay_estimate;
-  std::vector<std::vector<std::vector<float>>> x(
-      kNumBands, std::vector<std::vector<float>>(
-                     kNumChannels, std::vector<float>(kBlockSize, 0.f)));
+  std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
   std::vector<float> y(kBlockSize, 0.f);
   std::array<float, kBlockSize> x_old;
   SubtractorOutput output;
   config.delay.default_delay = 1;
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(config, kSampleRateHz, kNumChannels));
+      RenderDelayBuffer::Create(config, 48000));
   RenderSignalAnalyzer render_signal_analyzer(config);
   Random random_generator(42U);
   Aec3Fft fft;
@@ -63,11 +58,11 @@
 
   DelayBuffer<float> delay_buffer(delay_samples);
   for (int k = 0; k < num_blocks_to_process; ++k) {
-    RandomizeSampleVector(&random_generator, x[0][0]);
+    RandomizeSampleVector(&random_generator, x[0]);
     if (uncorrelated_inputs) {
       RandomizeSampleVector(&random_generator, y);
     } else {
-      delay_buffer.Delay(x[0][0], y);
+      delay_buffer.Delay(x[0], y);
     }
     render_delay_buffer->Insert(x);
     if (k == 0) {
@@ -131,7 +126,7 @@
   EchoCanceller3Config config;
   Subtractor subtractor(config, &data_dumper, DetectOptimization());
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(config, 48000, 1));
+      RenderDelayBuffer::Create(config, 48000));
   RenderSignalAnalyzer render_signal_analyzer(config);
   std::vector<float> y(kBlockSize, 0.f);
 
@@ -147,7 +142,7 @@
   EchoCanceller3Config config;
   Subtractor subtractor(config, &data_dumper, DetectOptimization());
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(config, 48000, 1));
+      RenderDelayBuffer::Create(config, 48000));
   RenderSignalAnalyzer render_signal_analyzer(config);
   std::vector<float> y(kBlockSize - 1, 0.f);
   SubtractorOutput output;
diff --git a/modules/audio_processing/aec3/suppression_filter.cc b/modules/audio_processing/aec3/suppression_filter.cc
index 6679a87..6fe296c 100644
--- a/modules/audio_processing/aec3/suppression_filter.cc
+++ b/modules/audio_processing/aec3/suppression_filter.cc
@@ -79,7 +79,7 @@
     const std::array<float, kFftLengthBy2Plus1>& suppression_gain,
     float high_bands_gain,
     const FftData& E_lowest_band,
-    std::vector<std::vector<std::vector<float>>>* e) {
+    std::vector<std::vector<float>>* e) {
   RTC_DCHECK(e);
   RTC_DCHECK_EQ(e->size(), NumBandsForRate(sample_rate_hz_));
   FftData E;
@@ -111,14 +111,14 @@
 
   fft_.Ifft(E, &e_extended);
   std::transform(e_output_old_[0].begin(), e_output_old_[0].end(),
-                 std::begin(kSqrtHanning) + kFftLengthBy2, (*e)[0][0].begin(),
+                 std::begin(kSqrtHanning) + kFftLengthBy2, (*e)[0].begin(),
                  [&](float a, float b) { return kIfftNormalization * a * b; });
   std::transform(e_extended.begin(), e_extended.begin() + kFftLengthBy2,
                  std::begin(kSqrtHanning), e_extended.begin(),
                  [&](float a, float b) { return kIfftNormalization * a * b; });
-  std::transform((*e)[0][0].begin(), (*e)[0][0].end(), e_extended.begin(),
-                 (*e)[0][0].begin(), std::plus<float>());
-  std::for_each((*e)[0][0].begin(), (*e)[0][0].end(), [](float& x_k) {
+  std::transform((*e)[0].begin(), (*e)[0].end(), e_extended.begin(),
+                 (*e)[0].begin(), std::plus<float>());
+  std::for_each((*e)[0].begin(), (*e)[0].end(), [](float& x_k) {
     x_k = rtc::SafeClamp(x_k, -32768.f, 32767.f);
   });
   std::copy(e_extended.begin() + kFftLengthBy2, e_extended.begin() + kFftLength,
@@ -140,9 +140,8 @@
         0.4f * std::sqrt(1.f - high_bands_gain * high_bands_gain);
 
     std::transform(
-        (*e)[1][0].begin(), (*e)[1][0].end(),
-        time_domain_high_band_noise.begin(), (*e)[1][0].begin(),
-        [&](float a, float b) {
+        (*e)[1].begin(), (*e)[1].end(), time_domain_high_band_noise.begin(),
+        (*e)[1].begin(), [&](float a, float b) {
           return std::max(
               std::min(b * high_bands_noise_scaling + high_bands_gain * a,
                        32767.0f),
@@ -151,16 +150,16 @@
 
     if (e->size() > 2) {
       RTC_DCHECK_EQ(3, e->size());
-      std::for_each((*e)[2][0].begin(), (*e)[2][0].end(), [&](float& a) {
+      std::for_each((*e)[2].begin(), (*e)[2].end(), [&](float& a) {
         a = rtc::SafeClamp(a * high_bands_gain, -32768.f, 32767.f);
       });
     }
 
     std::array<float, kFftLengthBy2> tmp;
     for (size_t k = 1; k < e->size(); ++k) {
-      std::copy((*e)[k][0].begin(), (*e)[k][0].end(), tmp.begin());
+      std::copy((*e)[k].begin(), (*e)[k].end(), tmp.begin());
       std::copy(e_output_old_[k].begin(), e_output_old_[k].end(),
-                (*e)[k][0].begin());
+                (*e)[k].begin());
       std::copy(tmp.begin(), tmp.end(), e_output_old_[k].begin());
     }
   }
diff --git a/modules/audio_processing/aec3/suppression_filter.h b/modules/audio_processing/aec3/suppression_filter.h
index 03b13c8..63569b1 100644
--- a/modules/audio_processing/aec3/suppression_filter.h
+++ b/modules/audio_processing/aec3/suppression_filter.h
@@ -31,7 +31,7 @@
                  const std::array<float, kFftLengthBy2Plus1>& suppression_gain,
                  float high_bands_gain,
                  const FftData& E_lowest_band,
-                 std::vector<std::vector<std::vector<float>>>* e);
+                 std::vector<std::vector<float>>* e);
 
  private:
   const Aec3Optimization optimization_;
diff --git a/modules/audio_processing/aec3/suppression_filter_unittest.cc b/modules/audio_processing/aec3/suppression_filter_unittest.cc
index 1e05a02..80d96ec 100644
--- a/modules/audio_processing/aec3/suppression_filter_unittest.cc
+++ b/modules/audio_processing/aec3/suppression_filter_unittest.cc
@@ -26,23 +26,14 @@
 void ProduceSinusoid(int sample_rate_hz,
                      float sinusoidal_frequency_hz,
                      size_t* sample_counter,
-                     std::vector<std::vector<std::vector<float>>>* x) {
+                     rtc::ArrayView<float> x) {
   // Produce a sinusoid of the specified frequency.
   for (size_t k = *sample_counter, j = 0; k < (*sample_counter + kBlockSize);
        ++k, ++j) {
-    for (size_t channel = 0; channel < (*x)[0].size(); ++channel) {
-      (*x)[0][channel][j] =
-          32767.f *
-          std::sin(2.f * kPi * sinusoidal_frequency_hz * k / sample_rate_hz);
-    }
+    x[j] = 32767.f *
+           std::sin(2.f * kPi * sinusoidal_frequency_hz * k / sample_rate_hz);
   }
   *sample_counter = *sample_counter + kBlockSize;
-
-  for (size_t band = 1; band < x->size(); ++band) {
-    for (size_t channel = 0; channel < (*x)[band].size(); ++channel) {
-      std::fill((*x)[band][channel].begin(), (*x)[band][channel].end(), 0.f);
-    }
-  }
 }
 
 }  // namespace
@@ -84,41 +75,29 @@
   cn_high_bands.re.fill(1.f);
   cn_high_bands.im.fill(1.f);
 
-  std::vector<std::vector<std::vector<float>>> e(
-      3,
-      std::vector<std::vector<float>>(1, std::vector<float>(kBlockSize, 0.f)));
-  std::vector<std::vector<std::vector<float>>> e_ref = e;
+  std::vector<std::vector<float>> e(3, std::vector<float>(kBlockSize, 0.f));
+  std::vector<std::vector<float>> e_ref = e;
 
   FftData E;
-  fft.PaddedFft(e[0][0], e_old_, Aec3Fft::Window::kSqrtHanning, &E);
-  std::copy(e[0][0].begin(), e[0][0].end(), e_old_.begin());
+  fft.PaddedFft(e[0], e_old_, Aec3Fft::Window::kSqrtHanning, &E);
+  std::copy(e[0].begin(), e[0].end(), e_old_.begin());
 
   filter.ApplyGain(cn, cn_high_bands, gain, 1.f, E, &e);
 
-  for (size_t band = 0; band < e.size(); ++band) {
-    for (size_t channel = 0; channel < e[band].size(); ++channel) {
-      for (size_t sample = 0; sample < e[band][channel].size(); ++sample) {
-        EXPECT_EQ(e_ref[band][channel][sample], e[band][channel][sample]);
-      }
-    }
+  for (size_t k = 0; k < e.size(); ++k) {
+    EXPECT_EQ(e_ref[k], e[k]);
   }
 }
 
 // Verifies that the suppressor is able to suppress a signal.
 TEST(SuppressionFilter, SignalSuppression) {
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
-  constexpr size_t kNumChannels = 1;
-
-  SuppressionFilter filter(Aec3Optimization::kNone, kSampleRateHz);
+  SuppressionFilter filter(Aec3Optimization::kNone, 48000);
   FftData cn;
   FftData cn_high_bands;
   std::array<float, kFftLengthBy2> e_old_;
   Aec3Fft fft;
   std::array<float, kFftLengthBy2Plus1> gain;
-  std::vector<std::vector<std::vector<float>>> e(
-      kNumBands, std::vector<std::vector<float>>(
-                     kNumChannels, std::vector<float>(kBlockSize, 0.f)));
+  std::vector<std::vector<float>> e(3, std::vector<float>(kBlockSize, 0.f));
   e_old_.fill(0.f);
 
   gain.fill(1.f);
@@ -134,17 +113,18 @@
   float e0_input = 0.f;
   float e0_output = 0.f;
   for (size_t k = 0; k < 100; ++k) {
-    ProduceSinusoid(16000, 16000 * 40 / kFftLengthBy2 / 2, &sample_counter, &e);
-    e0_input = std::inner_product(e[0][0].begin(), e[0][0].end(),
-                                  e[0][0].begin(), e0_input);
+    ProduceSinusoid(16000, 16000 * 40 / kFftLengthBy2 / 2, &sample_counter,
+                    e[0]);
+    e0_input =
+        std::inner_product(e[0].begin(), e[0].end(), e[0].begin(), e0_input);
 
     FftData E;
-    fft.PaddedFft(e[0][0], e_old_, Aec3Fft::Window::kSqrtHanning, &E);
-    std::copy(e[0][0].begin(), e[0][0].end(), e_old_.begin());
+    fft.PaddedFft(e[0], e_old_, Aec3Fft::Window::kSqrtHanning, &E);
+    std::copy(e[0].begin(), e[0].end(), e_old_.begin());
 
     filter.ApplyGain(cn, cn_high_bands, gain, 1.f, E, &e);
-    e0_output = std::inner_product(e[0][0].begin(), e[0][0].end(),
-                                   e[0][0].begin(), e0_output);
+    e0_output =
+        std::inner_product(e[0].begin(), e[0].end(), e[0].begin(), e0_output);
   }
 
   EXPECT_LT(e0_output, e0_input / 1000.f);
@@ -153,19 +133,13 @@
 // Verifies that the suppressor is able to pass through a desired signal while
 // applying suppressing for some frequencies.
 TEST(SuppressionFilter, SignalTransparency) {
-  constexpr size_t kNumChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
-
-  SuppressionFilter filter(Aec3Optimization::kNone, kSampleRateHz);
+  SuppressionFilter filter(Aec3Optimization::kNone, 48000);
   FftData cn;
   std::array<float, kFftLengthBy2> e_old_;
   Aec3Fft fft;
   FftData cn_high_bands;
   std::array<float, kFftLengthBy2Plus1> gain;
-  std::vector<std::vector<std::vector<float>>> e(
-      kNumBands, std::vector<std::vector<float>>(
-                     kNumChannels, std::vector<float>(kBlockSize, 0.f)));
+  std::vector<std::vector<float>> e(3, std::vector<float>(kBlockSize, 0.f));
   e_old_.fill(0.f);
   gain.fill(1.f);
   std::for_each(gain.begin() + 30, gain.end(), [](float& a) { a = 0.f; });
@@ -180,17 +154,18 @@
   float e0_input = 0.f;
   float e0_output = 0.f;
   for (size_t k = 0; k < 100; ++k) {
-    ProduceSinusoid(16000, 16000 * 10 / kFftLengthBy2 / 2, &sample_counter, &e);
-    e0_input = std::inner_product(e[0][0].begin(), e[0][0].end(),
-                                  e[0][0].begin(), e0_input);
+    ProduceSinusoid(16000, 16000 * 10 / kFftLengthBy2 / 2, &sample_counter,
+                    e[0]);
+    e0_input =
+        std::inner_product(e[0].begin(), e[0].end(), e[0].begin(), e0_input);
 
     FftData E;
-    fft.PaddedFft(e[0][0], e_old_, Aec3Fft::Window::kSqrtHanning, &E);
-    std::copy(e[0][0].begin(), e[0][0].end(), e_old_.begin());
+    fft.PaddedFft(e[0], e_old_, Aec3Fft::Window::kSqrtHanning, &E);
+    std::copy(e[0].begin(), e[0].end(), e_old_.begin());
 
     filter.ApplyGain(cn, cn_high_bands, gain, 1.f, E, &e);
-    e0_output = std::inner_product(e[0][0].begin(), e[0][0].end(),
-                                   e[0][0].begin(), e0_output);
+    e0_output =
+        std::inner_product(e[0].begin(), e[0].end(), e[0].begin(), e0_output);
   }
 
   EXPECT_LT(0.9f * e0_input, e0_output);
@@ -198,19 +173,13 @@
 
 // Verifies that the suppressor delay.
 TEST(SuppressionFilter, Delay) {
-  constexpr size_t kNumChannels = 1;
-  constexpr int kSampleRateHz = 48000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
-
-  SuppressionFilter filter(Aec3Optimization::kNone, kSampleRateHz);
+  SuppressionFilter filter(Aec3Optimization::kNone, 48000);
   FftData cn;
   FftData cn_high_bands;
   std::array<float, kFftLengthBy2> e_old_;
   Aec3Fft fft;
   std::array<float, kFftLengthBy2Plus1> gain;
-  std::vector<std::vector<std::vector<float>>> e(
-      kNumBands, std::vector<std::vector<float>>(
-                     kNumChannels, std::vector<float>(kBlockSize, 0.f)));
+  std::vector<std::vector<float>> e(3, std::vector<float>(kBlockSize, 0.f));
 
   gain.fill(1.f);
 
@@ -220,26 +189,21 @@
   cn_high_bands.im.fill(0.f);
 
   for (size_t k = 0; k < 100; ++k) {
-    for (size_t band = 0; band < kNumBands; ++band) {
-      for (size_t channel = 0; channel < kNumChannels; ++channel) {
-        for (size_t sample = 0; sample < kBlockSize; ++sample) {
-          e[band][channel][sample] = k * kBlockSize + sample + channel;
-        }
+    for (size_t j = 0; j < 3; ++j) {
+      for (size_t i = 0; i < kBlockSize; ++i) {
+        e[j][i] = k * kBlockSize + i;
       }
     }
 
     FftData E;
-    fft.PaddedFft(e[0][0], e_old_, Aec3Fft::Window::kSqrtHanning, &E);
-    std::copy(e[0][0].begin(), e[0][0].end(), e_old_.begin());
+    fft.PaddedFft(e[0], e_old_, Aec3Fft::Window::kSqrtHanning, &E);
+    std::copy(e[0].begin(), e[0].end(), e_old_.begin());
 
     filter.ApplyGain(cn, cn_high_bands, gain, 1.f, E, &e);
     if (k > 2) {
-      for (size_t band = 0; band < kNumBands; ++band) {
-        for (size_t channel = 0; channel < kNumChannels; ++channel) {
-          for (size_t sample = 0; sample < kBlockSize; ++sample) {
-            EXPECT_NEAR(k * kBlockSize + sample - kBlockSize + channel,
-                        e[band][channel][sample], 0.01);
-          }
+      for (size_t j = 0; j < 2; ++j) {
+        for (size_t i = 0; i < kBlockSize; ++i) {
+          EXPECT_NEAR(k * kBlockSize + i - kBlockSize, e[j][i], 0.01);
         }
       }
     }
diff --git a/modules/audio_processing/aec3/suppression_gain.cc b/modules/audio_processing/aec3/suppression_gain.cc
index 89ebe0f..4831b71 100644
--- a/modules/audio_processing/aec3/suppression_gain.cc
+++ b/modules/audio_processing/aec3/suppression_gain.cc
@@ -108,7 +108,7 @@
     const std::array<float, kFftLengthBy2Plus1>& comfort_noise_spectrum,
     const absl::optional<int>& narrow_peak_band,
     bool saturated_echo,
-    const std::vector<std::vector<std::vector<float>>>& render,
+    const std::vector<std::vector<float>>& render,
     const std::array<float, kFftLengthBy2Plus1>& low_band_gain) const {
   RTC_DCHECK_LT(0, render.size());
   if (render.size() == 1) {
@@ -131,12 +131,12 @@
 
   // Compute the upper and lower band energies.
   const auto sum_of_squares = [](float a, float b) { return a + b * b; };
-  const float low_band_energy = std::accumulate(
-      render[0][0].begin(), render[0][0].end(), 0.f, sum_of_squares);
+  const float low_band_energy =
+      std::accumulate(render[0].begin(), render[0].end(), 0.f, sum_of_squares);
   float high_band_energy = 0.f;
   for (size_t k = 1; k < render.size(); ++k) {
-    const float energy = std::accumulate(
-        render[k][0].begin(), render[k][0].end(), 0.f, sum_of_squares);
+    const float energy = std::accumulate(render[k].begin(), render[k].end(),
+                                         0.f, sum_of_squares);
     high_band_energy = std::max(high_band_energy, energy);
   }
 
@@ -317,7 +317,7 @@
     const std::array<float, kFftLengthBy2Plus1>& comfort_noise_spectrum,
     const RenderSignalAnalyzer& render_signal_analyzer,
     const AecState& aec_state,
-    const std::vector<std::vector<std::vector<float>>>& render,
+    const std::vector<std::vector<float>>& render,
     float* high_bands_gain,
     std::array<float, kFftLengthBy2Plus1>* low_band_gain) {
   RTC_DCHECK(high_bands_gain);
@@ -366,10 +366,10 @@
 // Detects when the render signal can be considered to have low power and
 // consist of stationary noise.
 bool SuppressionGain::LowNoiseRenderDetector::Detect(
-    const std::vector<std::vector<std::vector<float>>>& render) {
+    const std::vector<std::vector<float>>& render) {
   float x2_sum = 0.f;
   float x2_max = 0.f;
-  for (auto x_k : render[0][0]) {
+  for (auto x_k : render[0]) {
     const float x2 = x_k * x_k;
     x2_sum += x2;
     x2_max = std::max(x2_max, x2);
diff --git a/modules/audio_processing/aec3/suppression_gain.h b/modules/audio_processing/aec3/suppression_gain.h
index a583ef0..2b34dbe 100644
--- a/modules/audio_processing/aec3/suppression_gain.h
+++ b/modules/audio_processing/aec3/suppression_gain.h
@@ -41,7 +41,7 @@
       const std::array<float, kFftLengthBy2Plus1>& comfort_noise_spectrum,
       const RenderSignalAnalyzer& render_signal_analyzer,
       const AecState& aec_state,
-      const std::vector<std::vector<std::vector<float>>>& render,
+      const std::vector<std::vector<float>>& render,
       float* high_bands_gain,
       std::array<float, kFftLengthBy2Plus1>* low_band_gain);
 
@@ -55,7 +55,7 @@
       const std::array<float, kFftLengthBy2Plus1>& comfort_noise_spectrum,
       const absl::optional<int>& narrow_peak_band,
       bool saturated_echo,
-      const std::vector<std::vector<std::vector<float>>>& render,
+      const std::vector<std::vector<float>>& render,
       const std::array<float, kFftLengthBy2Plus1>& low_band_gain) const;
 
   void GainToNoAudibleEcho(
@@ -84,7 +84,7 @@
 
   class LowNoiseRenderDetector {
    public:
-    bool Detect(const std::vector<std::vector<std::vector<float>>>& render);
+    bool Detect(const std::vector<std::vector<float>>& render);
 
    private:
     float average_power_ = 32768.f * 32768.f;
diff --git a/modules/audio_processing/aec3/suppression_gain_unittest.cc b/modules/audio_processing/aec3/suppression_gain_unittest.cc
index 331b903..7d305f8 100644
--- a/modules/audio_processing/aec3/suppression_gain_unittest.cc
+++ b/modules/audio_processing/aec3/suppression_gain_unittest.cc
@@ -47,9 +47,8 @@
       SuppressionGain(EchoCanceller3Config{}, DetectOptimization(), 16000)
           .GetGain(E2, S2, R2, N2,
                    RenderSignalAnalyzer((EchoCanceller3Config{})), aec_state,
-                   std::vector<std::vector<std::vector<float>>>(
-                       3, std::vector<std::vector<float>>(
-                              1, std::vector<float>(kBlockSize, 0.f))),
+                   std::vector<std::vector<float>>(
+                       3, std::vector<float>(kBlockSize, 0.f)),
                    &high_bands_gain, nullptr),
       "");
 }
@@ -58,11 +57,8 @@
 
 // Does a sanity check that the gains are correctly computed.
 TEST(SuppressionGain, BasicGainComputation) {
-  constexpr size_t kNumChannels = 1;
-  constexpr int kSampleRateHz = 16000;
-  constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
   SuppressionGain suppression_gain(EchoCanceller3Config(), DetectOptimization(),
-                                   kSampleRateHz);
+                                   16000);
   RenderSignalAnalyzer analyzer(EchoCanceller3Config{});
   float high_bands_gain;
   std::array<float, kFftLengthBy2Plus1> E2;
@@ -73,15 +69,13 @@
   std::array<float, kFftLengthBy2Plus1> g;
   SubtractorOutput output;
   std::array<float, kBlockSize> y;
-  std::vector<std::vector<std::vector<float>>> x(
-      kNumBands, std::vector<std::vector<float>>(
-                     kNumChannels, std::vector<float>(kBlockSize, 0.f)));
+  std::vector<std::vector<float>> x(1, std::vector<float>(kBlockSize, 0.f));
   EchoCanceller3Config config;
   AecState aec_state(config);
   ApmDataDumper data_dumper(42);
   Subtractor subtractor(config, &data_dumper, DetectOptimization());
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
-      RenderDelayBuffer::Create(config, kSampleRateHz, kNumChannels));
+      RenderDelayBuffer::Create(config, 48000));
   absl::optional<DelayEstimate> delay_estimate;
 
   // Ensure that a strong noise is detected to mask any echoes.
diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc
index d639fd5..bc61b52 100644
--- a/modules/audio_processing/audio_processing_impl.cc
+++ b/modules/audio_processing/audio_processing_impl.cc
@@ -1849,8 +1849,7 @@
           echo_control_factory_->Create(proc_sample_rate_hz());
     } else {
       private_submodules_->echo_controller = absl::make_unique<EchoCanceller3>(
-          EchoCanceller3Config(), proc_sample_rate_hz(),
-          /*num_render_channels=*/1, /*num_capture_channels=*/1);
+          EchoCanceller3Config(), proc_sample_rate_hz());
     }
 
     capture_nonlocked_.echo_controller_enabled = true;
diff --git a/modules/audio_processing/audio_processing_impl_unittest.cc b/modules/audio_processing/audio_processing_impl_unittest.cc
index 68d17ae..72bd673 100644
--- a/modules/audio_processing/audio_processing_impl_unittest.cc
+++ b/modules/audio_processing/audio_processing_impl_unittest.cc
@@ -60,12 +60,6 @@
     return mock;
   }
 
-  std::unique_ptr<EchoControl> Create(int sample_rate_hz,
-                                      size_t num_render_channels,
-                                      size_t num_capture_channels) override {
-    return Create(sample_rate_hz);
-  }
-
  private:
   std::unique_ptr<MockEchoControl> next_mock_;
 };
diff --git a/modules/audio_processing/audio_processing_unittest.cc b/modules/audio_processing/audio_processing_unittest.cc
index 14ca329..9c30ab0 100644
--- a/modules/audio_processing/audio_processing_unittest.cc
+++ b/modules/audio_processing/audio_processing_unittest.cc
@@ -2513,12 +2513,6 @@
     EXPECT_CALL(*ec, ProcessCapture(::testing::_, ::testing::_)).Times(2);
     return std::unique_ptr<EchoControl>(ec);
   }
-
-  std::unique_ptr<EchoControl> Create(int sample_rate_hz,
-                                      size_t num_render_channels,
-                                      size_t num_capture_channels) {
-    return Create(sample_rate_hz);
-  }
 };
 
 TEST(ApmConfiguration, EchoControlInjection) {