270 lines
9.1 KiB
C
270 lines
9.1 KiB
C
|
/*
|
||
|
* Copyright (c) 2024 The WebRTC project authors. All Rights Reserved.
|
||
|
*
|
||
|
* Use of this source code is governed by a BSD-style license
|
||
|
* that can be found in the LICENSE file in the root of the source
|
||
|
* tree. An additional intellectual property rights grant can be found
|
||
|
* in the file PATENTS. All contributing project authors may
|
||
|
* be found in the AUTHORS file in the root of the source tree.
|
||
|
*/
|
||
|
|
||
|
#ifndef API_AUDIO_AUDIO_VIEW_H_
|
||
|
#define API_AUDIO_AUDIO_VIEW_H_
|
||
|
|
||
|
#include "api/array_view.h"
|
||
|
#include "api/audio/channel_layout.h"
|
||
|
#include "rtc_base/checks.h"
|
||
|
|
||
|
namespace webrtc {
|
||
|
|
||
|
// This file contains 3 types of view classes:
|
||
|
//
|
||
|
// * MonoView<>: A single channel contiguous buffer of samples.
|
||
|
//
|
||
|
// * InterleavedView<>: Channel samples are interleaved (side-by-side) in
|
||
|
// the buffer. A single channel InterleavedView<> is the same thing as a
|
||
|
// MonoView<>
|
||
|
//
|
||
|
// * DeinterleavedView<>: Each channel's samples are contiguous within the
|
||
|
// buffer. Channels can be enumerated and accessing the individual channel
|
||
|
// data is done via MonoView<>.
|
||
|
//
|
||
|
// The views are comparable to and built on rtc::ArrayView<> but add
|
||
|
// audio specific properties for the dimensions of the buffer and the above
|
||
|
// specialized [de]interleaved support.
|
||
|
//
|
||
|
// There are also a few generic utility functions that can simplify
|
||
|
// generic code for supporting more than one type of view.
|
||
|
|
||
|
// MonoView<> represents a view over a single contiguous, audio buffer. This
|
||
|
// can be either an single channel (mono) interleaved buffer (e.g. AudioFrame),
|
||
|
// or a de-interleaved channel (e.g. from AudioBuffer).
|
||
|
template <typename T>
|
||
|
using MonoView = rtc::ArrayView<T>;
|
||
|
|
||
|
// InterleavedView<> is a view over an interleaved audio buffer (e.g. from
|
||
|
// AudioFrame).
|
||
|
template <typename T>
|
||
|
class InterleavedView {
|
||
|
public:
|
||
|
using value_type = T;
|
||
|
|
||
|
InterleavedView() = default;
|
||
|
|
||
|
template <typename U>
|
||
|
InterleavedView(U* data, size_t samples_per_channel, size_t num_channels)
|
||
|
: num_channels_(num_channels),
|
||
|
samples_per_channel_(samples_per_channel),
|
||
|
data_(data, num_channels * samples_per_channel) {
|
||
|
RTC_DCHECK_LE(num_channels_, kMaxConcurrentChannels);
|
||
|
RTC_DCHECK(num_channels_ == 0u || samples_per_channel_ != 0u);
|
||
|
}
|
||
|
|
||
|
// Construct an InterleavedView from a C-style array. Samples per channels
|
||
|
// is calculated based on the array size / num_channels.
|
||
|
template <typename U, size_t N>
|
||
|
InterleavedView(U (&array)[N], // NOLINT
|
||
|
size_t num_channels)
|
||
|
: InterleavedView(array, N / num_channels, num_channels) {
|
||
|
RTC_DCHECK_EQ(N % num_channels, 0u);
|
||
|
}
|
||
|
|
||
|
template <typename U>
|
||
|
InterleavedView(const InterleavedView<U>& other)
|
||
|
: num_channels_(other.num_channels()),
|
||
|
samples_per_channel_(other.samples_per_channel()),
|
||
|
data_(other.data()) {}
|
||
|
|
||
|
size_t num_channels() const { return num_channels_; }
|
||
|
size_t samples_per_channel() const { return samples_per_channel_; }
|
||
|
rtc::ArrayView<T> data() const { return data_; }
|
||
|
bool empty() const { return data_.empty(); }
|
||
|
size_t size() const { return data_.size(); }
|
||
|
|
||
|
MonoView<T> AsMono() const {
|
||
|
RTC_DCHECK_EQ(num_channels(), 1u);
|
||
|
RTC_DCHECK_EQ(data_.size(), samples_per_channel_);
|
||
|
return data_;
|
||
|
}
|
||
|
|
||
|
// A simple wrapper around memcpy that includes checks for properties.
|
||
|
// TODO(tommi): Consider if this can be utility function for both interleaved
|
||
|
// and deinterleaved views.
|
||
|
template <typename U>
|
||
|
void CopyFrom(const InterleavedView<U>& source) {
|
||
|
static_assert(sizeof(T) == sizeof(U), "");
|
||
|
RTC_DCHECK_EQ(num_channels(), source.num_channels());
|
||
|
RTC_DCHECK_EQ(samples_per_channel(), source.samples_per_channel());
|
||
|
RTC_DCHECK_GE(data_.size(), source.data().size());
|
||
|
const auto data = source.data();
|
||
|
memcpy(&data_[0], &data[0], data.size() * sizeof(U));
|
||
|
}
|
||
|
|
||
|
T& operator[](size_t idx) const { return data_[idx]; }
|
||
|
T* begin() const { return data_.begin(); }
|
||
|
T* end() const { return data_.end(); }
|
||
|
const T* cbegin() const { return data_.cbegin(); }
|
||
|
const T* cend() const { return data_.cend(); }
|
||
|
std::reverse_iterator<T*> rbegin() const { return data_.rbegin(); }
|
||
|
std::reverse_iterator<T*> rend() const { return data_.rend(); }
|
||
|
std::reverse_iterator<const T*> crbegin() const { return data_.crbegin(); }
|
||
|
std::reverse_iterator<const T*> crend() const { return data_.crend(); }
|
||
|
|
||
|
private:
|
||
|
// TODO(tommi): Consider having these both be stored as uint16_t to
|
||
|
// save a few bytes per view. Use `dchecked_cast` to support size_t during
|
||
|
// construction.
|
||
|
size_t num_channels_ = 0u;
|
||
|
size_t samples_per_channel_ = 0u;
|
||
|
rtc::ArrayView<T> data_;
|
||
|
};
|
||
|
|
||
|
template <typename T>
|
||
|
class DeinterleavedView {
|
||
|
public:
|
||
|
using value_type = T;
|
||
|
|
||
|
DeinterleavedView() = default;
|
||
|
|
||
|
template <typename U>
|
||
|
DeinterleavedView(U* data, size_t samples_per_channel, size_t num_channels)
|
||
|
: num_channels_(num_channels),
|
||
|
samples_per_channel_(samples_per_channel),
|
||
|
data_(data, num_channels * samples_per_channel_) {}
|
||
|
|
||
|
template <typename U>
|
||
|
DeinterleavedView(const DeinterleavedView<U>& other)
|
||
|
: num_channels_(other.num_channels()),
|
||
|
samples_per_channel_(other.samples_per_channel()),
|
||
|
data_(other.data()) {}
|
||
|
|
||
|
// Returns a deinterleaved channel where `idx` is the zero based index,
|
||
|
// in the range [0 .. num_channels()-1].
|
||
|
MonoView<T> operator[](size_t idx) const {
|
||
|
RTC_DCHECK_LT(idx, num_channels_);
|
||
|
return MonoView<T>(&data_[idx * samples_per_channel_],
|
||
|
samples_per_channel_);
|
||
|
}
|
||
|
|
||
|
size_t num_channels() const { return num_channels_; }
|
||
|
size_t samples_per_channel() const { return samples_per_channel_; }
|
||
|
rtc::ArrayView<T> data() const { return data_; }
|
||
|
bool empty() const { return data_.empty(); }
|
||
|
size_t size() const { return data_.size(); }
|
||
|
|
||
|
// Returns the first (and possibly only) channel.
|
||
|
MonoView<T> AsMono() const {
|
||
|
RTC_DCHECK_GE(num_channels(), 1u);
|
||
|
return (*this)[0];
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
// TODO(tommi): Consider having these be stored as uint16_t to save a few
|
||
|
// bytes per view. Use `dchecked_cast` to support size_t during construction.
|
||
|
size_t num_channels_ = 0u;
|
||
|
size_t samples_per_channel_ = 0u;
|
||
|
rtc::ArrayView<T> data_;
|
||
|
};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr size_t NumChannels(const MonoView<T>& view) {
|
||
|
return 1u;
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
size_t NumChannels(const InterleavedView<T>& view) {
|
||
|
return view.num_channels();
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
size_t NumChannels(const DeinterleavedView<T>& view) {
|
||
|
return view.num_channels();
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool IsMono(const MonoView<T>& view) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool IsInterleavedView(const MonoView<T>& view) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool IsInterleavedView(const InterleavedView<T>& view) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool IsInterleavedView(const DeinterleavedView<const T>& view) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
bool IsMono(const InterleavedView<T>& view) {
|
||
|
return NumChannels(view) == 1u;
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
bool IsMono(const DeinterleavedView<T>& view) {
|
||
|
return NumChannels(view) == 1u;
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
size_t SamplesPerChannel(const MonoView<T>& view) {
|
||
|
return view.size();
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
size_t SamplesPerChannel(const InterleavedView<T>& view) {
|
||
|
return view.samples_per_channel();
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
size_t SamplesPerChannel(const DeinterleavedView<T>& view) {
|
||
|
return view.samples_per_channel();
|
||
|
}
|
||
|
// A simple wrapper around memcpy that includes checks for properties.
|
||
|
// The parameter order is the same as for memcpy(), first destination then
|
||
|
// source.
|
||
|
template <typename D, typename S>
|
||
|
void CopySamples(D& destination, const S& source) {
|
||
|
static_assert(
|
||
|
sizeof(typename D::value_type) == sizeof(typename S::value_type), "");
|
||
|
// Here we'd really like to do
|
||
|
// static_assert(IsInterleavedView(destination) == IsInterleavedView(source),
|
||
|
// "");
|
||
|
// but the compiler doesn't like it inside this template function for
|
||
|
// some reason. The following check is an approximation but unfortunately
|
||
|
// means that copying between a MonoView and single channel interleaved or
|
||
|
// deinterleaved views wouldn't work.
|
||
|
// static_assert(sizeof(destination) == sizeof(source),
|
||
|
// "Incompatible view types");
|
||
|
RTC_DCHECK_EQ(NumChannels(destination), NumChannels(source));
|
||
|
RTC_DCHECK_EQ(SamplesPerChannel(destination), SamplesPerChannel(source));
|
||
|
RTC_DCHECK_GE(destination.size(), source.size());
|
||
|
memcpy(&destination[0], &source[0],
|
||
|
source.size() * sizeof(typename S::value_type));
|
||
|
}
|
||
|
|
||
|
// Sets all the samples in a view to 0. This template function is a simple
|
||
|
// wrapper around `memset()` but adds the benefit of automatically calculating
|
||
|
// the byte size from the number of samples and sample type.
|
||
|
template <typename T>
|
||
|
void ClearSamples(T& view) {
|
||
|
memset(&view[0], 0, view.size() * sizeof(typename T::value_type));
|
||
|
}
|
||
|
|
||
|
// Same as `ClearSamples()` above but allows for clearing only the first
|
||
|
// `sample_count` number of samples.
|
||
|
template <typename T>
|
||
|
void ClearSamples(T& view, size_t sample_count) {
|
||
|
RTC_DCHECK_LE(sample_count, view.size());
|
||
|
memset(&view[0], 0, sample_count * sizeof(typename T::value_type));
|
||
|
}
|
||
|
|
||
|
} // namespace webrtc
|
||
|
|
||
|
#endif // API_AUDIO_AUDIO_VIEW_H_
|