| /* |
| * 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_ |