|  | /* | 
|  | *  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 <cstddef> | 
|  | #include <iterator> | 
|  | #include <variant> | 
|  | #include <vector> | 
|  |  | 
|  | #include "api/array_view.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 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 = ArrayView<T>; | 
|  |  | 
|  | // The maximum number of audio channels supported by WebRTC encoders, decoders | 
|  | // and the AudioFrame class. | 
|  | // TODO(peah, tommi): Should kMaxNumberOfAudioChannels be 16 rather than 24? | 
|  | // The reason is that AudioFrame's max number of samples is 7680, which can | 
|  | // hold 16 10ms 16bit channels at 48 kHz (and not 24 channels). | 
|  | static constexpr size_t kMaxNumberOfAudioChannels = 24; | 
|  |  | 
|  | // 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_, kMaxNumberOfAudioChannels); | 
|  | 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_; } | 
|  | 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; | 
|  | ArrayView<T> data_; | 
|  | }; | 
|  |  | 
|  | template <typename T> | 
|  | class DeinterleavedView { | 
|  | public: | 
|  | using value_type = T; | 
|  |  | 
|  | DeinterleavedView() = default; | 
|  |  | 
|  | // Construct a view where all the channels are coallocated in a single buffer. | 
|  | 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) {} | 
|  |  | 
|  | // Construct a view from an array of channel pointers where the channels | 
|  | // may all be allocated seperately. | 
|  | template <typename U> | 
|  | DeinterleavedView(U* const* channels, | 
|  | size_t samples_per_channel, | 
|  | size_t num_channels) | 
|  | : num_channels_(num_channels), | 
|  | samples_per_channel_(samples_per_channel), | 
|  | data_(channels) {} | 
|  |  | 
|  | // Construct a view from an array of channel pointers where the pointers are | 
|  | // helt in a `std::vector<>`. | 
|  | template <typename U> | 
|  | DeinterleavedView(const std::vector<U*>& channels, size_t samples_per_channel) | 
|  | : num_channels_(channels.size()), | 
|  | samples_per_channel_(samples_per_channel), | 
|  | data_(channels.data()) {} | 
|  |  | 
|  | // Construct a view from another view. Note that the type of | 
|  | // the other view may be different from the current type and | 
|  | // therefore the internal data types may not be exactly the | 
|  | // same, but still compatible. | 
|  | // E.g.: | 
|  | // DeinterleavedView<float> mutable_view; | 
|  | // DeinterleavedView<const float> const_view(mutable_view); | 
|  | template <typename U> | 
|  | DeinterleavedView(const DeinterleavedView<U>& other) | 
|  | : num_channels_(other.num_channels_), | 
|  | samples_per_channel_(other.samples_per_channel()) { | 
|  | if (other.is_ptr_array()) { | 
|  | data_ = std::get<U* const*>(other.data_); | 
|  | } else { | 
|  | data_ = std::get<U*>(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()); | 
|  | if (is_ptr_array()) | 
|  | return MonoView<T>(std::get<T* const*>(data_)[idx], samples_per_channel_); | 
|  | return MonoView<T>(&std::get<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_; } | 
|  | bool empty() const { | 
|  | return num_channels_ == 0u || samples_per_channel_ == 0u; | 
|  | } | 
|  | size_t size() const { return num_channels_ * samples_per_channel_; } | 
|  |  | 
|  | // Returns the first (and possibly only) channel. | 
|  | MonoView<T> AsMono() const { | 
|  | RTC_DCHECK_GE(num_channels(), 1u); | 
|  | return (*this)[0]; | 
|  | } | 
|  |  | 
|  | // Zeros out all samples in channels represented by the view. | 
|  | void Clear() { | 
|  | for (size_t i = 0u; i < num_channels_; ++i) { | 
|  | MonoView<T> view = (*this)[i]; | 
|  | ClearSamples(view); | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool is_ptr_array() const { return std::holds_alternative<T* const*>(data_); } | 
|  |  | 
|  | template <typename U> | 
|  | friend class DeinterleavedView; | 
|  |  | 
|  | size_t num_channels_ = 0u; | 
|  | size_t samples_per_channel_ = 0u; | 
|  | std::variant<T* const*, 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_ |