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:
parent
5889cf5888
commit
cfdbb0d371
@ -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.
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user