Update PushResampler to use a single buffer for source, destination.

PushResampler now uses a single buffer for the deinterleaved
channel sources and another for the deinterleaved destinations.
Before, there was a dedicated buffer per channel (source and dest).
This reduces allocations and allows for using DeinterleavedView for
both which simplifies some checks.

Bug: chromium:335805780
Change-Id: I553a36164109127fa332ab17918d53832d442303
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/351542
Reviewed-by: Per Åhgren <peah@webrtc.org>
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42415}
This commit is contained in:
Tommi 2024-05-30 13:30:33 +02:00 committed by WebRTC LUCI CQ
parent 5889cf5888
commit cfdbb0d371
6 changed files with 94 additions and 136 deletions

View File

@ -23,6 +23,44 @@
namespace webrtc { namespace webrtc {
// TODO: b/335805780 - Remove this method. Instead, use Deinterleave() from
// audio_util.h which requires size checked buffer views.
template <typename T>
void Deinterleave(const T* interleaved,
size_t samples_per_channel,
size_t num_channels,
T* const* deinterleaved) {
for (size_t i = 0; i < num_channels; ++i) {
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;
}
}
}
// `Interleave()` variant for cases where the deinterleaved channels aren't
// represented by a `DeinterleavedView`.
// TODO: b/335805780 - Remove this method. Instead, use Deinterleave() from
// audio_util.h which requires size checked buffer views.
template <typename T>
void Interleave(const T* const* deinterleaved,
size_t samples_per_channel,
size_t num_channels,
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;
for (size_t j = 0; j < samples_per_channel; ++j) {
interleaved[interleaved_idx] = channel[j];
interleaved_idx += num_channels;
}
}
}
// Helper to encapsulate a contiguous data buffer, full or split into frequency // Helper to encapsulate a contiguous data buffer, full or split into frequency
// bands, with access to a pointer arrays of the deinterleaved channels and // bands, with access to a pointer arrays of the deinterleaved channels and
// bands. The buffer is zero initialized at creation. // bands. The buffer is zero initialized at creation.

View File

@ -130,23 +130,6 @@ void Deinterleave(const InterleavedView<const T>& interleaved,
} }
} }
// 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,
size_t num_channels,
T* const* deinterleaved) {
for (size_t i = 0; i < num_channels; ++i) {
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;
}
}
}
// Interleave audio from the channel buffers pointed to by `deinterleaved` to // Interleave audio from the channel buffers pointed to by `deinterleaved` to
// `interleaved`. There must be sufficient space allocated in `interleaved` // `interleaved`. There must be sufficient space allocated in `interleaved`
// (`samples_per_channel` * `num_channels`). // (`samples_per_channel` * `num_channels`).
@ -166,44 +149,8 @@ void Interleave(const DeinterleavedView<const T>& deinterleaved,
} }
} }
// `Interleave()` variant for cases where the deinterleaved channels aren't // TODO: b/335805780 - Accept DeInterleavedView and MonoView.
// represented by a `DeinterleavedView`. // Possibly just delete this method if it isn't used.
// 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,
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;
for (size_t j = 0; j < samples_per_channel; ++j) {
interleaved[interleaved_idx] = channel[j];
interleaved_idx += num_channels;
}
}
}
// Copies audio from a single channel buffer pointed to by `mono` to each
// channel of `interleaved`. There must be sufficient space allocated in
// `interleaved` (`samples_per_channel` * `num_channels`).
// TODO: b/335805780 - Accept ArrayView.
template <typename T>
void UpmixMonoToInterleaved(const T* mono,
int num_frames,
int num_channels,
T* interleaved) {
int interleaved_idx = 0;
for (int i = 0; i < num_frames; ++i) {
for (int j = 0; j < num_channels; ++j) {
interleaved[interleaved_idx++] = mono[i];
}
}
}
// TODO: b/335805780 - Accept ArrayView.
template <typename T, typename Intermediate> template <typename T, typename Intermediate>
void DownmixToMono(const T* const* input_channels, void DownmixToMono(const T* const* input_channels,
size_t num_frames, size_t num_frames,
@ -220,7 +167,8 @@ void DownmixToMono(const T* const* input_channels,
// Downmixes an interleaved multichannel signal to a single channel by averaging // Downmixes an interleaved multichannel signal to a single channel by averaging
// all channels. // all channels.
// TODO: b/335805780 - Accept ArrayView. // TODO: b/335805780 - Accept InterleavedView and DeinterleavedView.
// Possibly just delete this method if it isn't used.
template <typename T, typename Intermediate> template <typename T, typename Intermediate>
void DownmixInterleavedToMonoImpl(const T* interleaved, void DownmixInterleavedToMonoImpl(const T* interleaved,
size_t num_frames, size_t num_frames,
@ -243,14 +191,14 @@ void DownmixInterleavedToMonoImpl(const T* interleaved,
} }
} }
// TODO: b/335805780 - Accept ArrayView. // TODO: b/335805780 - Accept InterleavedView and DeinterleavedView.
template <typename T> template <typename T>
void DownmixInterleavedToMono(const T* interleaved, void DownmixInterleavedToMono(const T* interleaved,
size_t num_frames, size_t num_frames,
int num_channels, int num_channels,
T* deinterleaved); T* deinterleaved);
// TODO: b/335805780 - Accept ArrayView. // TODO: b/335805780 - Accept InterleavedView and DeinterleavedView.
template <> template <>
void DownmixInterleavedToMono<int16_t>(const int16_t* interleaved, void DownmixInterleavedToMono<int16_t>(const int16_t* interleaved,
size_t num_frames, size_t num_frames,

View File

@ -22,7 +22,6 @@ class PushSincResampler;
// Wraps PushSincResampler to provide stereo support. // Wraps PushSincResampler to provide stereo support.
// Note: This implementation assumes 10ms buffer sizes throughout. // Note: This implementation assumes 10ms buffer sizes throughout.
// TODO(ajm): add support for an arbitrary number of channels.
template <typename T> template <typename T>
class PushResampler { class PushResampler {
public: public:
@ -40,22 +39,12 @@ class PushResampler {
int Resample(InterleavedView<const T> src, InterleavedView<T> dst); int Resample(InterleavedView<const T> src, InterleavedView<T> dst);
private: private:
int src_sample_rate_hz_; std::unique_ptr<T[]> source_;
int dst_sample_rate_hz_; std::unique_ptr<T[]> destination_;
size_t num_channels_; DeinterleavedView<T> source_view_;
// Vector that is needed to provide the proper inputs and outputs to the DeinterleavedView<T> destination_view_;
// interleave/de-interleave methods used in Resample. This needs to be
// heap-allocated on the state to support an arbitrary number of channels
// without doing run-time heap-allocations in the Resample method.
std::vector<T*> channel_data_array_;
struct ChannelResampler { std::vector<std::unique_ptr<PushSincResampler>> resamplers_;
std::unique_ptr<PushSincResampler> resampler;
std::vector<T> source;
std::vector<T> destination;
};
std::vector<ChannelResampler> channel_resamplers_;
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -23,11 +23,10 @@
namespace webrtc { namespace webrtc {
template <typename T> template <typename T>
PushResampler<T>::PushResampler() PushResampler<T>::PushResampler() = default;
: src_sample_rate_hz_(0), dst_sample_rate_hz_(0), num_channels_(0) {}
template <typename T> template <typename T>
PushResampler<T>::~PushResampler() {} PushResampler<T>::~PushResampler() = default;
template <typename T> template <typename T>
int PushResampler<T>::InitializeIfNeeded(int src_sample_rate_hz, int PushResampler<T>::InitializeIfNeeded(int src_sample_rate_hz,
@ -35,45 +34,36 @@ int PushResampler<T>::InitializeIfNeeded(int src_sample_rate_hz,
size_t num_channels) { size_t num_channels) {
// These checks used to be factored out of this template function due to // These checks used to be factored out of this template function due to
// Windows debug build issues with clang. http://crbug.com/615050 // Windows debug build issues with clang. http://crbug.com/615050
RTC_DCHECK_GT(src_sample_rate_hz, 0); RTC_CHECK_GT(src_sample_rate_hz, 0);
RTC_DCHECK_GT(dst_sample_rate_hz, 0); RTC_CHECK_GT(dst_sample_rate_hz, 0);
RTC_DCHECK_GT(num_channels, 0); RTC_CHECK_GT(num_channels, 0);
if (src_sample_rate_hz == src_sample_rate_hz_ &&
dst_sample_rate_hz == dst_sample_rate_hz_ &&
num_channels == num_channels_) {
// No-op if settings haven't changed.
return 0;
}
if (src_sample_rate_hz <= 0 || dst_sample_rate_hz <= 0 || num_channels <= 0) {
return -1;
}
src_sample_rate_hz_ = 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 = const size_t src_size_10ms_mono =
static_cast<size_t>(src_sample_rate_hz / 100); static_cast<size_t>(src_sample_rate_hz / 100);
const size_t dst_size_10ms_mono = const size_t dst_size_10ms_mono =
static_cast<size_t>(dst_sample_rate_hz / 100); static_cast<size_t>(dst_sample_rate_hz / 100);
channel_resamplers_.clear();
for (size_t i = 0; i < num_channels; ++i) { if (src_size_10ms_mono == SamplesPerChannel(source_view_) &&
channel_resamplers_.push_back(ChannelResampler()); dst_size_10ms_mono == SamplesPerChannel(destination_view_) &&
auto channel_resampler = channel_resamplers_.rbegin(); num_channels == NumChannels(source_view_)) {
channel_resampler->resampler = std::make_unique<PushSincResampler>( // No-op if settings haven't changed.
src_size_10ms_mono, dst_size_10ms_mono); return 0;
channel_resampler->source.resize(src_size_10ms_mono);
channel_resampler->destination.resize(dst_size_10ms_mono);
} }
channel_data_array_.resize(num_channels_); // Allocate two buffers for all source and destination channels.
// Then organize source and destination views together with an array of
// resamplers for each channel in the deinterlaved buffers.
source_.reset(new T[src_size_10ms_mono * num_channels]);
destination_.reset(new T[dst_size_10ms_mono * num_channels]);
source_view_ =
DeinterleavedView<T>(source_.get(), src_size_10ms_mono, num_channels);
destination_view_ = DeinterleavedView<T>(destination_.get(),
dst_size_10ms_mono, num_channels);
resamplers_.resize(num_channels);
for (size_t i = 0; i < num_channels; ++i) {
resamplers_[i] = std::make_unique<PushSincResampler>(src_size_10ms_mono,
dst_size_10ms_mono);
}
return 0; return 0;
} }
@ -81,42 +71,27 @@ int PushResampler<T>::InitializeIfNeeded(int src_sample_rate_hz,
template <typename T> template <typename T>
int PushResampler<T>::Resample(InterleavedView<const T> src, int PushResampler<T>::Resample(InterleavedView<const T> src,
InterleavedView<T> dst) { InterleavedView<T> dst) {
RTC_DCHECK_EQ(NumChannels(src), num_channels_); RTC_DCHECK_EQ(NumChannels(src), NumChannels(source_view_));
RTC_DCHECK_EQ(NumChannels(dst), num_channels_); RTC_DCHECK_EQ(NumChannels(dst), NumChannels(destination_view_));
RTC_DCHECK_EQ(SamplesPerChannel(src), RTC_DCHECK_EQ(SamplesPerChannel(src), SamplesPerChannel(source_view_));
SampleRateToDefaultChannelSize(src_sample_rate_hz_)); RTC_DCHECK_EQ(SamplesPerChannel(dst), SamplesPerChannel(destination_view_));
RTC_DCHECK_EQ(SamplesPerChannel(dst),
SampleRateToDefaultChannelSize(dst_sample_rate_hz_));
if (src_sample_rate_hz_ == dst_sample_rate_hz_) { if (SamplesPerChannel(src) == SamplesPerChannel(dst)) {
// The old resampler provides this memcpy facility in the case of matching // The old resampler provides this memcpy facility in the case of matching
// sample rates, so reproduce it here for the sinc resampler. // sample rates, so reproduce it here for the sinc resampler.
CopySamples(dst, src); CopySamples(dst, src);
return static_cast<int>(src.data().size()); return static_cast<int>(src.data().size());
} }
for (size_t ch = 0; ch < num_channels_; ++ch) { Deinterleave(src, source_view_);
channel_data_array_[ch] = channel_resamplers_[ch].source.data();
for (size_t i = 0; i < resamplers_.size(); ++i) {
size_t dst_length_mono =
resamplers_[i]->Resample(source_view_[i], destination_view_[i]);
RTC_DCHECK_EQ(dst_length_mono, SamplesPerChannel(dst));
} }
// TODO: b/335805780 - Deinterleave should accept InterleavedView<> as input. Interleave<T>(destination_view_, dst);
Deinterleave(&src.data()[0], src.samples_per_channel(), src.num_channels(),
channel_data_array_.data());
for (auto& resampler : channel_resamplers_) {
size_t dst_length_mono = resampler.resampler->Resample(
resampler.source.data(), src.samples_per_channel(),
resampler.destination.data(), dst.samples_per_channel());
RTC_DCHECK_EQ(dst_length_mono, dst.samples_per_channel());
}
for (size_t ch = 0; ch < num_channels_; ++ch) {
channel_data_array_[ch] = channel_resamplers_[ch].destination.data();
}
// TODO: b/335805780 - Interleave should accept DeInterleavedView<> as src.
Interleave(channel_data_array_.data(), dst.samples_per_channel(),
num_channels_, dst);
return static_cast<int>(dst.size()); return static_cast<int>(dst.size());
} }

View File

@ -16,6 +16,7 @@
#include <memory> #include <memory>
#include "api/audio/audio_view.h"
#include "common_audio/resampler/sinc_resampler.h" #include "common_audio/resampler/sinc_resampler.h"
namespace webrtc { namespace webrtc {
@ -40,6 +41,12 @@ class PushSincResampler : public SincResamplerCallback {
// at least as large as `destination_frames`. Returns the number of samples // at least as large as `destination_frames`. Returns the number of samples
// provided in destination (for convenience, since this will always be equal // provided in destination (for convenience, since this will always be equal
// to `destination_frames`). // to `destination_frames`).
template <typename S, typename D>
size_t Resample(const MonoView<S>& source, const MonoView<D>& destination) {
return Resample(&source[0], SamplesPerChannel(source), &destination[0],
SamplesPerChannel(destination));
}
size_t Resample(const int16_t* source, size_t Resample(const int16_t* source,
size_t source_frames, size_t source_frames,
int16_t* destination, int16_t* destination,

View File

@ -25,6 +25,7 @@
#include "absl/flags/flag.h" #include "absl/flags/flag.h"
#include "absl/flags/parse.h" #include "absl/flags/parse.h"
#include "api/function_view.h" #include "api/function_view.h"
#include "common_audio/channel_buffer.h"
#include "common_audio/include/audio_util.h" #include "common_audio/include/audio_util.h"
#include "common_audio/wav_file.h" #include "common_audio/wav_file.h"
#include "modules/audio_processing/test/protobuf_utils.h" #include "modules/audio_processing/test/protobuf_utils.h"