/* * 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 using MonoView = rtc::ArrayView; // InterleavedView<> is a view over an interleaved audio buffer (e.g. from // AudioFrame). template class InterleavedView { public: using value_type = T; InterleavedView() = default; template 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 InterleavedView(U (&array)[N], // NOLINT size_t num_channels) : InterleavedView(array, N / num_channels, num_channels) { RTC_DCHECK_EQ(N % num_channels, 0u); } template InterleavedView(const InterleavedView& 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 data() const { return data_; } bool empty() const { return data_.empty(); } size_t size() const { return data_.size(); } MonoView 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 void CopyFrom(const InterleavedView& 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 rbegin() const { return data_.rbegin(); } std::reverse_iterator rend() const { return data_.rend(); } std::reverse_iterator crbegin() const { return data_.crbegin(); } std::reverse_iterator 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 data_; }; template class DeinterleavedView { public: using value_type = T; DeinterleavedView() = default; template 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 DeinterleavedView(const DeinterleavedView& 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 operator[](size_t idx) const { RTC_DCHECK_LT(idx, num_channels_); return MonoView(&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 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 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 data_; }; template constexpr size_t NumChannels(const MonoView& view) { return 1u; } template size_t NumChannels(const InterleavedView& view) { return view.num_channels(); } template size_t NumChannels(const DeinterleavedView& view) { return view.num_channels(); } template constexpr bool IsMono(const MonoView& view) { return true; } template constexpr bool IsInterleavedView(const MonoView& view) { return true; } template constexpr bool IsInterleavedView(const InterleavedView& view) { return true; } template constexpr bool IsInterleavedView(const DeinterleavedView& view) { return false; } template bool IsMono(const InterleavedView& view) { return NumChannels(view) == 1u; } template bool IsMono(const DeinterleavedView& view) { return NumChannels(view) == 1u; } template size_t SamplesPerChannel(const MonoView& view) { return view.size(); } template size_t SamplesPerChannel(const InterleavedView& view) { return view.samples_per_channel(); } template size_t SamplesPerChannel(const DeinterleavedView& 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 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 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 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_