Use audio views in Interleave() and Deinterleave()

Interleave and Deinterleave now accept two parameters, one for the
interleaved buffer and another for the deinterleaved one.

The previous versions of the functions still need to exist for test
code that uses ChannelBuffer.

Bug: chromium:335805780
Change-Id: I20371ab6408766d21e6901e6a04000afa05b3553
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/351664
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Reviewed-by: Per Ã…hgren <peah@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42412}
diff --git a/common_audio/audio_util_unittest.cc b/common_audio/audio_util_unittest.cc
index a215a12..fc446d1 100644
--- a/common_audio/audio_util_unittest.cc
+++ b/common_audio/audio_util_unittest.cc
@@ -124,20 +124,23 @@
 }
 
 TEST(AudioUtilTest, InterleavingStereo) {
-  const int16_t kInterleaved[] = {2, 3, 4, 9, 8, 27, 16, 81};
-  const size_t kSamplesPerChannel = 4;
-  const int kNumChannels = 2;
-  const size_t kLength = kSamplesPerChannel * kNumChannels;
-  int16_t left[kSamplesPerChannel], right[kSamplesPerChannel];
-  int16_t* deinterleaved[] = {left, right};
-  Deinterleave(kInterleaved, kSamplesPerChannel, kNumChannels, deinterleaved);
+  constexpr int16_t kInterleaved[] = {2, 3, 4, 9, 8, 27, 16, 81};
+  constexpr size_t kSamplesPerChannel = 4;
+  constexpr int kNumChannels = 2;
+  constexpr size_t kLength = kSamplesPerChannel * kNumChannels;
+  int16_t deinterleaved[kLength] = {};
+  DeinterleavedView<int16_t> deinterleaved_view(
+      &deinterleaved[0], kSamplesPerChannel, kNumChannels);
+  Deinterleave({&kInterleaved[0], kSamplesPerChannel, kNumChannels},
+               deinterleaved_view);
   const int16_t kRefLeft[] = {2, 4, 8, 16};
   const int16_t kRefRight[] = {3, 9, 27, 81};
-  ExpectArraysEq(kRefLeft, left, kSamplesPerChannel);
-  ExpectArraysEq(kRefRight, right, kSamplesPerChannel);
+  ExpectArraysEq(kRefLeft, deinterleaved_view[0].data(), kSamplesPerChannel);
+  ExpectArraysEq(kRefRight, deinterleaved_view[1].data(), kSamplesPerChannel);
 
   int16_t interleaved[kLength];
-  Interleave(deinterleaved, kSamplesPerChannel, kNumChannels, interleaved);
+  Interleave<int16_t>({&deinterleaved[0], kSamplesPerChannel, kNumChannels},
+                      {&interleaved[0], kSamplesPerChannel, kNumChannels});
   ExpectArraysEq(kInterleaved, interleaved, kLength);
 }
 
@@ -146,12 +149,16 @@
   const size_t kSamplesPerChannel = 5;
   const int kNumChannels = 1;
   int16_t mono[kSamplesPerChannel];
-  int16_t* deinterleaved[] = {mono};
-  Deinterleave(kInterleaved, kSamplesPerChannel, kNumChannels, deinterleaved);
-  ExpectArraysEq(kInterleaved, mono, kSamplesPerChannel);
+  DeinterleavedView<int16_t> deinterleaved_view(&mono[0], kSamplesPerChannel,
+                                                kNumChannels);
+  Deinterleave({kInterleaved, kSamplesPerChannel, kNumChannels},
+               deinterleaved_view);
+  ExpectArraysEq(kInterleaved, deinterleaved_view.AsMono().data(),
+                 kSamplesPerChannel);
 
   int16_t interleaved[kSamplesPerChannel];
-  Interleave(deinterleaved, kSamplesPerChannel, kNumChannels, interleaved);
+  Interleave<int16_t>(deinterleaved_view,
+                      {&interleaved[0], kSamplesPerChannel, kNumChannels});
   ExpectArraysEq(mono, interleaved, kSamplesPerChannel);
 }
 
diff --git a/common_audio/include/audio_util.h b/common_audio/include/audio_util.h
index 4cbea67..e004245 100644
--- a/common_audio/include/audio_util.h
+++ b/common_audio/include/audio_util.h
@@ -18,6 +18,7 @@
 #include <cstring>
 #include <limits>
 
+#include "api/audio/audio_view.h"
 #include "rtc_base/checks.h"
 
 namespace webrtc {
@@ -111,7 +112,26 @@
 // by `deinterleaved`. There must be sufficient space allocated in the
 // `deinterleaved` buffers (`num_channel` buffers with `samples_per_channel`
 // per buffer).
-// TODO: b/335805780 - Accept ArrayView.
+template <typename T>
+void Deinterleave(const InterleavedView<const T>& interleaved,
+                  const DeinterleavedView<T>& deinterleaved) {
+  RTC_DCHECK_EQ(NumChannels(interleaved), NumChannels(deinterleaved));
+  RTC_DCHECK_EQ(SamplesPerChannel(interleaved),
+                SamplesPerChannel(deinterleaved));
+  const auto num_channels = NumChannels(interleaved);
+  const auto samples_per_channel = SamplesPerChannel(interleaved);
+  for (size_t i = 0; i < num_channels; ++i) {
+    MonoView<T> channel = deinterleaved[i];
+    size_t interleaved_idx = i;
+    for (size_t j = 0; j < samples_per_channel; ++j) {
+      channel[j] = interleaved[interleaved_idx];
+      interleaved_idx += num_channels;
+    }
+  }
+}
+
+// TODO: b/335805780 - Move into test code where this is used once PushResampler
+// has been changed to use a single allocation for deinterleaved audio buffers.
 template <typename T>
 void Deinterleave(const T* interleaved,
                   size_t samples_per_channel,
@@ -130,12 +150,32 @@
 // Interleave audio from the channel buffers pointed to by `deinterleaved` to
 // `interleaved`. There must be sufficient space allocated in `interleaved`
 // (`samples_per_channel` * `num_channels`).
-// TODO: b/335805780 - Accept ArrayView.
+template <typename T>
+void Interleave(const DeinterleavedView<const T>& deinterleaved,
+                const InterleavedView<T>& interleaved) {
+  RTC_DCHECK_EQ(NumChannels(interleaved), NumChannels(deinterleaved));
+  RTC_DCHECK_EQ(SamplesPerChannel(interleaved),
+                SamplesPerChannel(deinterleaved));
+  for (size_t i = 0; i < deinterleaved.num_channels(); ++i) {
+    const auto channel = deinterleaved[i];
+    size_t interleaved_idx = i;
+    for (size_t j = 0; j < deinterleaved.samples_per_channel(); ++j) {
+      interleaved[interleaved_idx] = channel[j];
+      interleaved_idx += deinterleaved.num_channels();
+    }
+  }
+}
+
+// `Interleave()` variant for cases where the deinterleaved channels aren't
+// represented by a `DeinterleavedView`.
+// TODO: b/335805780 - Move into test code where this is used.
 template <typename T>
 void Interleave(const T* const* deinterleaved,
                 size_t samples_per_channel,
                 size_t num_channels,
-                T* interleaved) {
+                InterleavedView<T>& interleaved) {
+  RTC_DCHECK_EQ(NumChannels(interleaved), num_channels);
+  RTC_DCHECK_EQ(SamplesPerChannel(interleaved), samples_per_channel);
   for (size_t i = 0; i < num_channels; ++i) {
     const T* channel = deinterleaved[i];
     size_t interleaved_idx = i;
diff --git a/common_audio/resampler/push_resampler.cc b/common_audio/resampler/push_resampler.cc
index adcd518..53d7593 100644
--- a/common_audio/resampler/push_resampler.cc
+++ b/common_audio/resampler/push_resampler.cc
@@ -54,6 +54,11 @@
   dst_sample_rate_hz_ = dst_sample_rate_hz;
   num_channels_ = num_channels;
 
+  // TODO: b/335805780 - Change this to use a single buffer for source and
+  // destination and initialize each ChannelResampler() with a pointer to
+  // channels in each deinterleaved buffer. That way, DeinterleavedView can be
+  // used for the two buffers.
+
   const size_t src_size_10ms_mono =
       static_cast<size_t>(src_sample_rate_hz / 100);
   const size_t dst_size_10ms_mono =
@@ -109,9 +114,9 @@
     channel_data_array_[ch] = channel_resamplers_[ch].destination.data();
   }
 
-  // TODO: b/335805780 - Interleave should accept InterleavedView<> as dst.
+  // TODO: b/335805780 - Interleave should accept DeInterleavedView<> as src.
   Interleave(channel_data_array_.data(), dst.samples_per_channel(),
-             num_channels_, &dst[0]);
+             num_channels_, dst);
   return static_cast<int>(dst.size());
 }
 
diff --git a/modules/audio_processing/audio_processing_unittest.cc b/modules/audio_processing/audio_processing_unittest.cc
index 02e87ec..adb8891 100644
--- a/modules/audio_processing/audio_processing_unittest.cc
+++ b/modules/audio_processing/audio_processing_unittest.cc
@@ -2060,16 +2060,25 @@
           StreamConfig(output_rate, num_output_channels), out_cb.channels()));
 
       // Dump forward output to file.
-      Interleave(out_cb.channels(), out_cb.num_frames(), out_cb.num_channels(),
-                 float_data.get());
+      RTC_DCHECK_EQ(out_cb.num_bands(), 1u);  // Assumes full frequency band.
+      DeinterleavedView<const float> deinterleaved_src(
+          out_cb.channels()[0], out_cb.num_frames(), out_cb.num_channels());
+      InterleavedView<float> interleaved_dst(
+          float_data.get(), out_cb.num_frames(), out_cb.num_channels());
+      Interleave(deinterleaved_src, interleaved_dst);
       size_t out_length = out_cb.num_channels() * out_cb.num_frames();
 
       ASSERT_EQ(out_length, fwrite(float_data.get(), sizeof(float_data[0]),
                                    out_length, out_file));
 
       // Dump reverse output to file.
-      Interleave(rev_out_cb.channels(), rev_out_cb.num_frames(),
-                 rev_out_cb.num_channels(), float_data.get());
+      RTC_DCHECK_EQ(rev_out_cb.num_bands(), 1u);
+      deinterleaved_src = DeinterleavedView<const float>(
+          rev_out_cb.channels()[0], rev_out_cb.num_frames(),
+          rev_out_cb.num_channels());
+      interleaved_dst = InterleavedView<float>(
+          float_data.get(), rev_out_cb.num_frames(), rev_out_cb.num_channels());
+      Interleave(deinterleaved_src, interleaved_dst);
       size_t rev_out_length =
           rev_out_cb.num_channels() * rev_out_cb.num_frames();
 
diff --git a/modules/audio_processing/test/test_utils.cc b/modules/audio_processing/test/test_utils.cc
index 9aeebe5..c6b02b2 100644
--- a/modules/audio_processing/test/test_utils.cc
+++ b/modules/audio_processing/test/test_utils.cc
@@ -46,8 +46,12 @@
 void ChannelBufferWavWriter::Write(const ChannelBuffer<float>& buffer) {
   RTC_CHECK_EQ(file_->num_channels(), buffer.num_channels());
   interleaved_.resize(buffer.size());
-  Interleave(buffer.channels(), buffer.num_frames(), buffer.num_channels(),
-             &interleaved_[0]);
+  InterleavedView<float> view(&interleaved_[0], buffer.num_frames(),
+                              buffer.num_channels());
+  const float* samples = buffer.channels()[0];
+  DeinterleavedView<const float> source(samples, buffer.num_frames(),
+                                        buffer.num_channels());
+  Interleave(source, view);
   FloatToFloatS16(&interleaved_[0], interleaved_.size(), &interleaved_[0]);
   file_->WriteSamples(&interleaved_[0], interleaved_.size());
 }
@@ -62,8 +66,10 @@
 void ChannelBufferVectorWriter::Write(const ChannelBuffer<float>& buffer) {
   // Account for sample rate changes throughout a simulation.
   interleaved_buffer_.resize(buffer.size());
+  InterleavedView<float> view(&interleaved_buffer_[0], buffer.num_frames(),
+                              buffer.num_channels());
   Interleave(buffer.channels(), buffer.num_frames(), buffer.num_channels(),
-             interleaved_buffer_.data());
+             view);
   size_t old_size = output_->size();
   output_->resize(old_size + interleaved_buffer_.size());
   FloatToFloatS16(interleaved_buffer_.data(), interleaved_buffer_.size(),
diff --git a/rtc_tools/unpack_aecdump/unpack.cc b/rtc_tools/unpack_aecdump/unpack.cc
index e8bb587..42c143c 100644
--- a/rtc_tools/unpack_aecdump/unpack.cc
+++ b/rtc_tools/unpack_aecdump/unpack.cc
@@ -146,7 +146,8 @@
                     RawFile* raw_file) {
   size_t length = num_channels * samples_per_channel;
   std::unique_ptr<float[]> buffer(new float[length]);
-  Interleave(data, samples_per_channel, num_channels, buffer.get());
+  InterleavedView<float> view(buffer.get(), samples_per_channel, num_channels);
+  Interleave(data, samples_per_channel, num_channels, view);
   if (raw_file) {
     raw_file->WriteSamples(buffer.get(), length);
   }