From f58ded7cf086700208e50a6b382b46ee70a5f7ad Mon Sep 17 00:00:00 2001 From: Tommi Date: Thu, 30 May 2024 13:29:11 +0200 Subject: [PATCH] Use audio views in Interleave() and Deinterleave() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Per Ã…hgren Cr-Commit-Position: refs/heads/main@{#42412} --- common_audio/audio_util_unittest.cc | 35 ++++++++------ common_audio/include/audio_util.h | 46 +++++++++++++++++-- common_audio/resampler/push_resampler.cc | 9 +++- .../audio_processing_unittest.cc | 17 +++++-- modules/audio_processing/test/test_utils.cc | 12 +++-- rtc_tools/unpack_aecdump/unpack.cc | 3 +- 6 files changed, 95 insertions(+), 27 deletions(-) diff --git a/common_audio/audio_util_unittest.cc b/common_audio/audio_util_unittest.cc index a215a123b1..fc446d1ff8 100644 --- a/common_audio/audio_util_unittest.cc +++ b/common_audio/audio_util_unittest.cc @@ -124,20 +124,23 @@ TEST(AudioUtilTest, FloatS16ToDbfs) { } 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 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({&deinterleaved[0], kSamplesPerChannel, kNumChannels}, + {&interleaved[0], kSamplesPerChannel, kNumChannels}); ExpectArraysEq(kInterleaved, interleaved, kLength); } @@ -146,12 +149,16 @@ TEST(AudioUtilTest, InterleavingMonoIsIdentical) { 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 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(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 4cbea67bf6..e0042455d8 100644 --- a/common_audio/include/audio_util.h +++ b/common_audio/include/audio_util.h @@ -18,6 +18,7 @@ #include #include +#include "api/audio/audio_view.h" #include "rtc_base/checks.h" namespace webrtc { @@ -111,7 +112,26 @@ void CopyAudioIfNeeded(const T* const* src, // 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 +void Deinterleave(const InterleavedView& interleaved, + const DeinterleavedView& 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 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 void Deinterleave(const T* interleaved, size_t samples_per_channel, @@ -130,12 +150,32 @@ void Deinterleave(const T* interleaved, // 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 +void Interleave(const DeinterleavedView& deinterleaved, + const InterleavedView& 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 void Interleave(const T* const* deinterleaved, size_t samples_per_channel, size_t num_channels, - T* interleaved) { + InterleavedView& 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 adcd518ff3..53d759340c 100644 --- a/common_audio/resampler/push_resampler.cc +++ b/common_audio/resampler/push_resampler.cc @@ -54,6 +54,11 @@ int PushResampler::InitializeIfNeeded(int src_sample_rate_hz, 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(src_sample_rate_hz / 100); const size_t dst_size_10ms_mono = @@ -109,9 +114,9 @@ int PushResampler::Resample(InterleavedView src, 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(dst.size()); } diff --git a/modules/audio_processing/audio_processing_unittest.cc b/modules/audio_processing/audio_processing_unittest.cc index 02e87ece70..adb8891dfa 100644 --- a/modules/audio_processing/audio_processing_unittest.cc +++ b/modules/audio_processing/audio_processing_unittest.cc @@ -2060,16 +2060,25 @@ class AudioProcessingTest 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 deinterleaved_src( + out_cb.channels()[0], out_cb.num_frames(), out_cb.num_channels()); + InterleavedView 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( + rev_out_cb.channels()[0], rev_out_cb.num_frames(), + rev_out_cb.num_channels()); + interleaved_dst = InterleavedView( + 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 9aeebe5155..c6b02b2d6f 100644 --- a/modules/audio_processing/test/test_utils.cc +++ b/modules/audio_processing/test/test_utils.cc @@ -46,8 +46,12 @@ ChannelBufferWavWriter::~ChannelBufferWavWriter() = default; void ChannelBufferWavWriter::Write(const ChannelBuffer& 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 view(&interleaved_[0], buffer.num_frames(), + buffer.num_channels()); + const float* samples = buffer.channels()[0]; + DeinterleavedView 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 @@ ChannelBufferVectorWriter::~ChannelBufferVectorWriter() = default; void ChannelBufferVectorWriter::Write(const ChannelBuffer& buffer) { // Account for sample rate changes throughout a simulation. interleaved_buffer_.resize(buffer.size()); + InterleavedView 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 e8bb587cfa..42c143c7a8 100644 --- a/rtc_tools/unpack_aecdump/unpack.cc +++ b/rtc_tools/unpack_aecdump/unpack.cc @@ -146,7 +146,8 @@ void WriteFloatData(const float* const* data, RawFile* raw_file) { size_t length = num_channels * samples_per_channel; std::unique_ptr buffer(new float[length]); - Interleave(data, samples_per_channel, num_channels, buffer.get()); + InterleavedView 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); }