From eb0f3df520e5e0219fe0c78c862edc3a5611ca2c Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Tue, 22 Jul 2025 18:22:16 -0400 Subject: [PATCH 01/10] Add concept --- include/AudioBufferView.h | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/include/AudioBufferView.h b/include/AudioBufferView.h index 56fff10faf8..495ef0fabdf 100644 --- a/include/AudioBufferView.h +++ b/include/AudioBufferView.h @@ -97,15 +97,34 @@ class BufferViewData f_cnt_t m_frames = 0; }; +template +inline constexpr bool OneOf = (std::is_same_v || ...); + } // namespace detail +//! Recognized sample types, either const or non-const +template +concept SampleType = detail::OneOf, + float, + double, + std::int8_t, + std::uint8_t, + std::int16_t, + std::uint16_t, + std::int32_t, + std::uint32_t, + std::int64_t, + std::uint64_t +>; + + /** * Non-owning view for multi-channel interleaved audio data * * TODO C++23: Use std::mdspan? */ -template +template class InterleavedBufferView : public detail::BufferViewData { using Base = detail::BufferViewData; @@ -186,7 +205,7 @@ static_assert(sizeof(InterleavedBufferView) == sizeof(void*) + sizeof( * * TODO C++23: Use std::mdspan? */ -template +template class PlanarBufferView : public detail::BufferViewData { using Base = detail::BufferViewData; From 27229c686d19a846e50605af5a9cc33252f3c6a8 Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Tue, 22 Jul 2025 18:36:27 -0400 Subject: [PATCH 02/10] Make static when possible --- include/AudioBufferView.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/AudioBufferView.h b/include/AudioBufferView.h index 495ef0fabdf..f79d18ad8ab 100644 --- a/include/AudioBufferView.h +++ b/include/AudioBufferView.h @@ -63,7 +63,7 @@ class BufferViewData } constexpr auto data() const noexcept -> SampleT* { return m_data; } - constexpr auto channels() const noexcept -> proc_ch_t { return channelCount; } + static constexpr auto channels() noexcept -> proc_ch_t { return channelCount; } constexpr auto frames() const noexcept -> f_cnt_t { return m_frames; } protected: @@ -156,7 +156,7 @@ class InterleavedBufferView : public detail::BufferViewData bool { - return !this->m_data || this->channels() == 0 || this->m_frames == 0; + return !this->m_data || Base::channels() == 0 || this->m_frames == 0; } //! @return the frame at the given index @@ -164,11 +164,11 @@ class InterleavedBufferView : public detail::BufferViewData{framePtr(index), this->channels()}; + return std::span{framePtr(index), Base::channels()}; } else { - return std::span{framePtr(index), this->channels()}; + return std::span{framePtr(index), Base::channels()}; } } @@ -179,7 +179,7 @@ class InterleavedBufferView : public detail::BufferViewData SampleT* { assert(index < this->m_frames); - return this->m_data + index * this->channels(); + return this->m_data + index * Base::channels(); } /** @@ -237,7 +237,7 @@ class PlanarBufferView : public detail::BufferViewData bool { - return !this->m_data || this->channels() == 0 || this->m_frames == 0; + return !this->m_data || Base::channels() == 0 || this->m_frames == 0; } //! @return the buffer of the given channel @@ -259,7 +259,7 @@ class PlanarBufferView : public detail::BufferViewData SampleT* { - assert(channel < this->channels()); + assert(channel < Base::channels()); assert(this->m_data != nullptr); return this->m_data[channel]; } From cb9115159c65edee9b1786f8d80790c3795f6e23 Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Tue, 22 Jul 2025 18:44:30 -0400 Subject: [PATCH 03/10] Add and to --- include/AudioBufferView.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/include/AudioBufferView.h b/include/AudioBufferView.h index f79d18ad8ab..498399b14f0 100644 --- a/include/AudioBufferView.h +++ b/include/AudioBufferView.h @@ -49,7 +49,7 @@ class BufferViewData constexpr BufferViewData() = default; constexpr BufferViewData(const BufferViewData&) = default; - constexpr BufferViewData(SampleT* data, proc_ch_t channels, f_cnt_t frames) noexcept + constexpr BufferViewData(SampleT* data, [[maybe_unused]] proc_ch_t channels, f_cnt_t frames) noexcept : m_data{data} , m_frames{frames} { @@ -159,6 +159,16 @@ class InterleavedBufferView : public detail::BufferViewDatam_data || Base::channels() == 0 || this->m_frames == 0; } + constexpr auto dataSizeBytes() const noexcept -> std::size_t + { + return Base::channels() * this->m_frames * sizeof(SampleT); + } + + constexpr auto dataView() noexcept -> std::span + { + return std::span{this->m_data, this->m_frames * Base::channels()}; + } + //! @return the frame at the given index constexpr auto frame(f_cnt_t index) const noexcept { From d305e4d52968a4e42d0979f9607e3265027f656c Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Tue, 22 Jul 2025 19:09:08 -0400 Subject: [PATCH 04/10] Support conversions between and --- include/AudioBufferView.h | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/include/AudioBufferView.h b/include/AudioBufferView.h index 498399b14f0..332d2789fce 100644 --- a/include/AudioBufferView.h +++ b/include/AudioBufferView.h @@ -31,6 +31,7 @@ #include #include "LmmsTypes.h" +#include "SampleFrame.h" namespace lmms { @@ -154,6 +155,20 @@ class InterleavedBufferView : public detail::BufferViewData + InterleavedBufferView(std::span buffer) noexcept + requires (std::is_same_v, float> && channelCount == 2) + : Base{reinterpret_cast(buffer.data()), buffer.size()} + { + } + + //! Construct from std::span + InterleavedBufferView(std::span buffer) noexcept + requires (std::is_same_v && channelCount == 2) + : Base{reinterpret_cast(buffer.data()), buffer.size()} + { + } + constexpr auto empty() const noexcept -> bool { return !this->m_data || Base::channels() == 0 || this->m_frames == 0; @@ -200,6 +215,32 @@ class InterleavedBufferView : public detail::BufferViewData SampleFrame& + requires (std::is_same_v && channelCount == 2) + { + assert(index < this->m_frames); + return reinterpret_cast(this->m_data)[index]; + } + + auto sampleFrameAt(f_cnt_t index) const noexcept -> const SampleFrame& + requires (std::is_same_v && channelCount == 2) + { + assert(index < this->m_frames); + return reinterpret_cast(this->m_data)[index]; + } + + auto toSampleFrames() noexcept -> std::span + requires (std::is_same_v && channelCount == 2) + { + return {reinterpret_cast(this->m_data), this->m_frames}; + } + + auto toSampleFrames() const noexcept -> std::span + requires (std::is_same_v && channelCount == 2) + { + return {reinterpret_cast(this->m_data), this->m_frames}; + } }; // Check that the std::span-like space optimization works From 57c20d4e30d94ab1caf54e3aad5ebb3650fa6895 Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Tue, 22 Jul 2025 19:25:00 -0400 Subject: [PATCH 05/10] Support iteration over frames of --- include/AudioBufferView.h | 203 +++++++++++++++++++++++++++++++++++++- 1 file changed, 201 insertions(+), 2 deletions(-) diff --git a/include/AudioBufferView.h b/include/AudioBufferView.h index 332d2789fce..9530f1d2423 100644 --- a/include/AudioBufferView.h +++ b/include/AudioBufferView.h @@ -42,7 +42,7 @@ inline constexpr auto DynamicChannelCount = static_cast(-1); namespace detail { -// For static channel count +// For buffer views with static channel count template class BufferViewData { @@ -72,7 +72,7 @@ class BufferViewData f_cnt_t m_frames = 0; }; -// For dynamic channel count +// For buffer views with dynamic channel count template class BufferViewData { @@ -98,6 +98,174 @@ class BufferViewData f_cnt_t m_frames = 0; }; +// For interleaved frame iterators with static channel count +template +class InterleavedFrameIteratorData +{ +public: + constexpr InterleavedFrameIteratorData() = default; + constexpr explicit InterleavedFrameIteratorData(SampleT* data) noexcept + : m_data{data} + { + } + + static constexpr auto channels() noexcept -> proc_ch_t { return channelCount; } + +protected: + SampleT* m_data = nullptr; +}; + +// For interleaved frame iterators with dynamic channel count +template +class InterleavedFrameIteratorData +{ +public: + constexpr InterleavedFrameIteratorData() = default; + constexpr InterleavedFrameIteratorData(SampleT* data, proc_ch_t channels) noexcept + : m_data{data} + , m_channels{channels} + { + } + + constexpr auto channels() const noexcept -> proc_ch_t { return m_channels; } + +protected: + SampleT* m_data = nullptr; + proc_ch_t m_channels = 0; +}; + +// Allows for iterating over the frames of `InterleavedBufferView` +template +class InterleavedFrameIterator : public InterleavedFrameIteratorData +{ + using Base = InterleavedFrameIteratorData; + +public: + using iterator_concept = std::random_access_iterator_tag; + using value_type = SampleT*; + using difference_type = std::ptrdiff_t; + + constexpr InterleavedFrameIterator() = default; + constexpr InterleavedFrameIterator(const InterleavedFrameIterator&) = default; + + template requires (channelCount != DynamicChannelCount) + constexpr explicit InterleavedFrameIterator(T* data) noexcept + : Base{data} + { + } + + template requires (channelCount == DynamicChannelCount) + constexpr InterleavedFrameIterator(T* data, proc_ch_t channels) noexcept + : Base{data, channels} + { + } + + constexpr auto operator*() const noexcept -> value_type { return this->m_data; } + + constexpr auto operator[](difference_type frames) const noexcept -> value_type + { + return this->m_data[frames * Base::channels()]; + } + + constexpr auto operator++() noexcept -> InterleavedFrameIterator& + { + this->m_data += Base::channels(); + return *this; + } + + constexpr auto operator++(int) noexcept -> InterleavedFrameIterator + { + auto temp = *this; + ++(*this); + return temp; + } + + constexpr auto operator--() noexcept -> InterleavedFrameIterator& + { + this->m_data -= Base::channels(); + return *this; + } + + constexpr auto operator--(int) noexcept -> InterleavedFrameIterator + { + auto temp = *this; + --(*this); + return temp; + } + + constexpr auto operator+=(difference_type channels) noexcept -> InterleavedFrameIterator& + { + this->m_data += channels * Base::channels(); + return *this; + } + + constexpr auto operator-=(difference_type channels) noexcept -> InterleavedFrameIterator& + { + this->m_data -= channels * Base::channels(); + return *this; + } + + friend constexpr auto operator+(InterleavedFrameIterator iter, difference_type frames) noexcept + -> InterleavedFrameIterator + { + if constexpr (channelCount == DynamicChannelCount) + { + return {iter.m_data + frames * Base::channels(), Base::channels()}; + } + else + { + return InterleavedFrameIterator{iter.m_data + frames * Base::channels()}; + } + } + + friend constexpr auto operator+(difference_type frames, InterleavedFrameIterator iter) noexcept + -> InterleavedFrameIterator + { + return iter + frames; + } + + constexpr auto operator-(difference_type frames) const noexcept -> InterleavedFrameIterator + { + if constexpr (channelCount == DynamicChannelCount) + { + return {this->m_data - frames * Base::channels(), Base::channels()}; + } + else + { + return InterleavedFrameIterator{this->m_data - frames * Base::channels()}; + } + } + + constexpr auto operator-(InterleavedFrameIterator other) const noexcept -> difference_type + { + return this->m_data - other.m_data; + } + + constexpr auto operator<=>(InterleavedFrameIterator other) const noexcept + { + return this->m_data <=> other.m_data; + } + + constexpr auto operator==(InterleavedFrameIterator other) const noexcept -> bool + { + return this->m_data == other.m_data; + } + + constexpr auto operator<=>(SampleT* sentinel) const noexcept + { + return this->m_data <=> sentinel; + } + + constexpr auto operator==(SampleT* sentinel) const noexcept -> bool + { + return this->m_data == sentinel; + } + + constexpr auto base() const noexcept -> SampleT* { return this->m_data; } +}; + +static_assert(std::random_access_iterator>); + template inline constexpr bool OneOf = (std::is_same_v || ...); @@ -130,6 +298,9 @@ class InterleavedBufferView : public detail::BufferViewData; + using FrameIter = detail::InterleavedFrameIterator; + using ConstFrameIter = detail::InterleavedFrameIterator; + public: using Base::Base; @@ -216,6 +387,34 @@ class InterleavedBufferView : public detail::BufferViewData std::ranges::subrange + { + const SampleT* end = this->m_data + Base::channels() * this->m_frames; + if constexpr (channelCount == DynamicChannelCount) + { + return std::ranges::subrange{ConstFrameIter{this->m_data, Base::channels()}, end}; + } + else + { + return std::ranges::subrange{ConstFrameIter{this->m_data}, end}; + } + } + + //! @returns a view over the frames. Iterates in chunks containing `channels()` elements. + constexpr auto framesView() noexcept -> std::ranges::subrange + { + SampleT* end = this->m_data + Base::channels() * this->m_frames; + if constexpr (channelCount == DynamicChannelCount) + { + return std::ranges::subrange{FrameIter{this->m_data, Base::channels()}, end}; + } + else + { + return std::ranges::subrange{FrameIter{this->m_data}, end}; + } + } + auto sampleFrameAt(f_cnt_t index) noexcept -> SampleFrame& requires (std::is_same_v && channelCount == 2) { From 7a1235d072ec5f553fc84b14245187e597247372 Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Tue, 22 Jul 2025 19:31:22 -0400 Subject: [PATCH 06/10] Add `subspan` method --------- Co-authored-by: Sotonye Atemie --- include/AudioBufferView.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/AudioBufferView.h b/include/AudioBufferView.h index 9530f1d2423..19a0c43598f 100644 --- a/include/AudioBufferView.h +++ b/include/AudioBufferView.h @@ -387,6 +387,21 @@ class InterleavedBufferView : public detail::BufferViewData InterleavedBufferView + { + assert(offset <= this->m_frames); + assert(offset + frames <= this->m_frames); + if constexpr (channelCount == DynamicChannelCount) + { + return {this->m_data + offset * Base::channels(), Base::channels(), frames}; + } + else + { + return {this->m_data + offset * Base::channels(), frames}; + } + } + //! @returns a const view over the frames. Iterates in chunks containing `channels()` elements. constexpr auto framesView() const noexcept -> std::ranges::subrange { From 4b05ca2bf9b9fc7b6a79eb7f755a7df27b27ddd4 Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Mon, 4 Aug 2025 13:26:22 -0400 Subject: [PATCH 07/10] Rename SampleT template parameter to T --- include/AudioBufferView.h | 140 +++++++++++++++++++------------------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/include/AudioBufferView.h b/include/AudioBufferView.h index 19a0c43598f..61a4df4bce1 100644 --- a/include/AudioBufferView.h +++ b/include/AudioBufferView.h @@ -43,44 +43,44 @@ inline constexpr auto DynamicChannelCount = static_cast(-1); namespace detail { // For buffer views with static channel count -template +template class BufferViewData { public: constexpr BufferViewData() = default; constexpr BufferViewData(const BufferViewData&) = default; - constexpr BufferViewData(SampleT* data, [[maybe_unused]] proc_ch_t channels, f_cnt_t frames) noexcept + constexpr BufferViewData(T* data, [[maybe_unused]] proc_ch_t channels, f_cnt_t frames) noexcept : m_data{data} , m_frames{frames} { assert(channels == channelCount); } - constexpr BufferViewData(SampleT* data, f_cnt_t frames) noexcept + constexpr BufferViewData(T* data, f_cnt_t frames) noexcept : m_data{data} , m_frames{frames} { } - constexpr auto data() const noexcept -> SampleT* { return m_data; } + constexpr auto data() const noexcept -> T* { return m_data; } static constexpr auto channels() noexcept -> proc_ch_t { return channelCount; } constexpr auto frames() const noexcept -> f_cnt_t { return m_frames; } protected: - SampleT* m_data = nullptr; + T* m_data = nullptr; f_cnt_t m_frames = 0; }; // For buffer views with dynamic channel count -template -class BufferViewData +template +class BufferViewData { public: constexpr BufferViewData() = default; constexpr BufferViewData(const BufferViewData&) = default; - constexpr BufferViewData(SampleT* data, proc_ch_t channels, f_cnt_t frames) noexcept + constexpr BufferViewData(T* data, proc_ch_t channels, f_cnt_t frames) noexcept : m_data{data} , m_channels{channels} , m_frames{frames} @@ -88,23 +88,23 @@ class BufferViewData assert(channels != DynamicChannelCount); } - constexpr auto data() const noexcept -> SampleT* { return m_data; } + constexpr auto data() const noexcept -> T* { return m_data; } constexpr auto channels() const noexcept -> proc_ch_t { return m_channels; } constexpr auto frames() const noexcept -> f_cnt_t { return m_frames; } protected: - SampleT* m_data = nullptr; + T* m_data = nullptr; proc_ch_t m_channels = 0; f_cnt_t m_frames = 0; }; // For interleaved frame iterators with static channel count -template +template class InterleavedFrameIteratorData { public: constexpr InterleavedFrameIteratorData() = default; - constexpr explicit InterleavedFrameIteratorData(SampleT* data) noexcept + constexpr explicit InterleavedFrameIteratorData(T* data) noexcept : m_data{data} { } @@ -112,16 +112,16 @@ class InterleavedFrameIteratorData static constexpr auto channels() noexcept -> proc_ch_t { return channelCount; } protected: - SampleT* m_data = nullptr; + T* m_data = nullptr; }; // For interleaved frame iterators with dynamic channel count -template -class InterleavedFrameIteratorData +template +class InterleavedFrameIteratorData { public: constexpr InterleavedFrameIteratorData() = default; - constexpr InterleavedFrameIteratorData(SampleT* data, proc_ch_t channels) noexcept + constexpr InterleavedFrameIteratorData(T* data, proc_ch_t channels) noexcept : m_data{data} , m_channels{channels} { @@ -130,32 +130,32 @@ class InterleavedFrameIteratorData constexpr auto channels() const noexcept -> proc_ch_t { return m_channels; } protected: - SampleT* m_data = nullptr; + T* m_data = nullptr; proc_ch_t m_channels = 0; }; // Allows for iterating over the frames of `InterleavedBufferView` -template -class InterleavedFrameIterator : public InterleavedFrameIteratorData +template +class InterleavedFrameIterator : public InterleavedFrameIteratorData { - using Base = InterleavedFrameIteratorData; + using Base = InterleavedFrameIteratorData; public: using iterator_concept = std::random_access_iterator_tag; - using value_type = SampleT*; + using value_type = T*; using difference_type = std::ptrdiff_t; constexpr InterleavedFrameIterator() = default; constexpr InterleavedFrameIterator(const InterleavedFrameIterator&) = default; - template requires (channelCount != DynamicChannelCount) - constexpr explicit InterleavedFrameIterator(T* data) noexcept + template requires (channelCount != DynamicChannelCount) + constexpr explicit InterleavedFrameIterator(U* data) noexcept : Base{data} { } - template requires (channelCount == DynamicChannelCount) - constexpr InterleavedFrameIterator(T* data, proc_ch_t channels) noexcept + template requires (channelCount == DynamicChannelCount) + constexpr InterleavedFrameIterator(U* data, proc_ch_t channels) noexcept : Base{data, channels} { } @@ -251,17 +251,17 @@ class InterleavedFrameIterator : public InterleavedFrameIteratorDatam_data == other.m_data; } - constexpr auto operator<=>(SampleT* sentinel) const noexcept + constexpr auto operator<=>(T* sentinel) const noexcept { return this->m_data <=> sentinel; } - constexpr auto operator==(SampleT* sentinel) const noexcept -> bool + constexpr auto operator==(T* sentinel) const noexcept -> bool { return this->m_data == sentinel; } - constexpr auto base() const noexcept -> SampleT* { return this->m_data; } + constexpr auto base() const noexcept -> T* { return this->m_data; } }; static_assert(std::random_access_iterator>); @@ -293,27 +293,27 @@ concept SampleType = detail::OneOf, * * TODO C++23: Use std::mdspan? */ -template -class InterleavedBufferView : public detail::BufferViewData +template +class InterleavedBufferView : public detail::BufferViewData { - using Base = detail::BufferViewData; + using Base = detail::BufferViewData; - using FrameIter = detail::InterleavedFrameIterator; - using ConstFrameIter = detail::InterleavedFrameIterator; + using FrameIter = detail::InterleavedFrameIterator; + using ConstFrameIter = detail::InterleavedFrameIterator; public: using Base::Base; //! Contruct const from mutable (static channel count) - template requires (std::is_const_v && channelCount != DynamicChannelCount) - constexpr InterleavedBufferView(InterleavedBufferView, channelCount> other) noexcept + template requires (std::is_const_v && channelCount != DynamicChannelCount) + constexpr InterleavedBufferView(InterleavedBufferView, channelCount> other) noexcept : Base{other.data(), other.frames()} { } //! Contruct const from mutable (dynamic channel count) - template requires (std::is_const_v && channelCount == DynamicChannelCount) - constexpr InterleavedBufferView(InterleavedBufferView, channelCount> other) noexcept + template requires (std::is_const_v && channelCount == DynamicChannelCount) + constexpr InterleavedBufferView(InterleavedBufferView, channelCount> other) noexcept : Base{other.data(), other.channels(), other.frames()} { } @@ -321,21 +321,21 @@ class InterleavedBufferView : public detail::BufferViewData requires (channelCount == DynamicChannelCount && otherChannels != DynamicChannelCount) - constexpr InterleavedBufferView(InterleavedBufferView other) noexcept + constexpr InterleavedBufferView(InterleavedBufferView other) noexcept : Base{other.data(), otherChannels, other.frames()} { } //! Construct from std::span InterleavedBufferView(std::span buffer) noexcept - requires (std::is_same_v, float> && channelCount == 2) + requires (std::is_same_v, float> && channelCount == 2) : Base{reinterpret_cast(buffer.data()), buffer.size()} { } //! Construct from std::span InterleavedBufferView(std::span buffer) noexcept - requires (std::is_same_v && channelCount == 2) + requires (std::is_same_v && channelCount == 2) : Base{reinterpret_cast(buffer.data()), buffer.size()} { } @@ -347,12 +347,12 @@ class InterleavedBufferView : public detail::BufferViewData std::size_t { - return Base::channels() * this->m_frames * sizeof(SampleT); + return Base::channels() * this->m_frames * sizeof(T); } - constexpr auto dataView() noexcept -> std::span + constexpr auto dataView() noexcept -> std::span { - return std::span{this->m_data, this->m_frames * Base::channels()}; + return std::span{this->m_data, this->m_frames * Base::channels()}; } //! @return the frame at the given index @@ -360,11 +360,11 @@ class InterleavedBufferView : public detail::BufferViewData{framePtr(index), Base::channels()}; + return std::span{framePtr(index), Base::channels()}; } else { - return std::span{framePtr(index), Base::channels()}; + return std::span{framePtr(index), Base::channels()}; } } @@ -372,7 +372,7 @@ class InterleavedBufferView : public detail::BufferViewData SampleT* + constexpr auto framePtr(f_cnt_t index) const noexcept -> T* { assert(index < this->m_frames); return this->m_data + index * Base::channels(); @@ -382,13 +382,13 @@ class InterleavedBufferView : public detail::BufferViewData SampleT* + constexpr auto operator[](f_cnt_t index) const noexcept -> T* { return framePtr(index); } //! @returns a subview at the given frame offset `offset` with a frame count of `frames` - constexpr auto subspan(f_cnt_t offset, f_cnt_t frames) const -> InterleavedBufferView + constexpr auto subspan(f_cnt_t offset, f_cnt_t frames) const -> InterleavedBufferView { assert(offset <= this->m_frames); assert(offset + frames <= this->m_frames); @@ -403,9 +403,9 @@ class InterleavedBufferView : public detail::BufferViewData std::ranges::subrange + constexpr auto framesView() const noexcept -> std::ranges::subrange { - const SampleT* end = this->m_data + Base::channels() * this->m_frames; + const T* end = this->m_data + Base::channels() * this->m_frames; if constexpr (channelCount == DynamicChannelCount) { return std::ranges::subrange{ConstFrameIter{this->m_data, Base::channels()}, end}; @@ -417,9 +417,9 @@ class InterleavedBufferView : public detail::BufferViewData std::ranges::subrange + constexpr auto framesView() noexcept -> std::ranges::subrange { - SampleT* end = this->m_data + Base::channels() * this->m_frames; + T* end = this->m_data + Base::channels() * this->m_frames; if constexpr (channelCount == DynamicChannelCount) { return std::ranges::subrange{FrameIter{this->m_data, Base::channels()}, end}; @@ -431,27 +431,27 @@ class InterleavedBufferView : public detail::BufferViewData SampleFrame& - requires (std::is_same_v && channelCount == 2) + requires (std::is_same_v && channelCount == 2) { assert(index < this->m_frames); return reinterpret_cast(this->m_data)[index]; } auto sampleFrameAt(f_cnt_t index) const noexcept -> const SampleFrame& - requires (std::is_same_v && channelCount == 2) + requires (std::is_same_v && channelCount == 2) { assert(index < this->m_frames); return reinterpret_cast(this->m_data)[index]; } auto toSampleFrames() noexcept -> std::span - requires (std::is_same_v && channelCount == 2) + requires (std::is_same_v && channelCount == 2) { return {reinterpret_cast(this->m_data), this->m_frames}; } auto toSampleFrames() const noexcept -> std::span - requires (std::is_same_v && channelCount == 2) + requires (std::is_same_v && channelCount == 2) { return {reinterpret_cast(this->m_data), this->m_frames}; } @@ -465,29 +465,29 @@ static_assert(sizeof(InterleavedBufferView) == sizeof(void*) + sizeof( /** * Non-owning view for multi-channel non-interleaved audio data * - * The data type is `SampleT* const*` which is a 2D array accessed as data[channel][frame index] + * The data type is `T* const*` which is a 2D array accessed as data[channel][frame index] * where each channel's buffer contains `frames()` frames. * * TODO C++23: Use std::mdspan? */ -template -class PlanarBufferView : public detail::BufferViewData +template +class PlanarBufferView : public detail::BufferViewData { - using Base = detail::BufferViewData; + using Base = detail::BufferViewData; public: using Base::Base; //! Contruct const from mutable (static channel count) - template requires (std::is_const_v && channelCount != DynamicChannelCount) - constexpr PlanarBufferView(PlanarBufferView, channelCount> other) noexcept + template requires (std::is_const_v && channelCount != DynamicChannelCount) + constexpr PlanarBufferView(PlanarBufferView, channelCount> other) noexcept : Base{other.data(), other.frames()} { } //! Contruct const from mutable (dynamic channel count) - template requires (std::is_const_v && channelCount == DynamicChannelCount) - constexpr PlanarBufferView(PlanarBufferView, channelCount> other) noexcept + template requires (std::is_const_v && channelCount == DynamicChannelCount) + constexpr PlanarBufferView(PlanarBufferView, channelCount> other) noexcept : Base{other.data(), other.channels(), other.frames()} { } @@ -495,7 +495,7 @@ class PlanarBufferView : public detail::BufferViewData requires (channelCount == DynamicChannelCount && otherChannels != DynamicChannelCount) - constexpr PlanarBufferView(PlanarBufferView other) noexcept + constexpr PlanarBufferView(PlanarBufferView other) noexcept : Base{other.data(), otherChannels, other.frames()} { } @@ -506,14 +506,14 @@ class PlanarBufferView : public detail::BufferViewData std::span + constexpr auto buffer(proc_ch_t channel) const noexcept -> std::span { return {bufferPtr(channel), this->m_frames}; } //! @return the buffer of the given channel template requires (channelCount != DynamicChannelCount) - constexpr auto buffer() const noexcept -> std::span + constexpr auto buffer() const noexcept -> std::span { return {bufferPtr(), this->m_frames}; } @@ -522,7 +522,7 @@ class PlanarBufferView : public detail::BufferViewData SampleT* + constexpr auto bufferPtr(proc_ch_t channel) const noexcept -> T* { assert(channel < Base::channels()); assert(this->m_data != nullptr); @@ -534,7 +534,7 @@ class PlanarBufferView : public detail::BufferViewData requires (channelCount != DynamicChannelCount) - constexpr auto bufferPtr() const noexcept -> SampleT* + constexpr auto bufferPtr() const noexcept -> T* { static_assert(channel < channelCount); assert(this->m_data != nullptr); @@ -545,7 +545,7 @@ class PlanarBufferView : public detail::BufferViewData SampleT* + constexpr auto operator[](proc_ch_t channel) const noexcept -> T* { return bufferPtr(channel); } From 5b1137e29e7d40466bbc39c4949a1bde0dac1f92 Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Mon, 4 Aug 2025 13:30:29 -0400 Subject: [PATCH 08/10] Simplify InterleavedFrameIterator constructors --- include/AudioBufferView.h | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/include/AudioBufferView.h b/include/AudioBufferView.h index 61a4df4bce1..427aa32e76c 100644 --- a/include/AudioBufferView.h +++ b/include/AudioBufferView.h @@ -104,6 +104,8 @@ class InterleavedFrameIteratorData { public: constexpr InterleavedFrameIteratorData() = default; + constexpr InterleavedFrameIteratorData(const InterleavedFrameIteratorData&) = default; + constexpr explicit InterleavedFrameIteratorData(T* data) noexcept : m_data{data} { @@ -121,6 +123,8 @@ class InterleavedFrameIteratorData { public: constexpr InterleavedFrameIteratorData() = default; + constexpr InterleavedFrameIteratorData(const InterleavedFrameIteratorData&) = default; + constexpr InterleavedFrameIteratorData(T* data, proc_ch_t channels) noexcept : m_data{data} , m_channels{channels} @@ -145,20 +149,7 @@ class InterleavedFrameIterator : public InterleavedFrameIteratorData requires (channelCount != DynamicChannelCount) - constexpr explicit InterleavedFrameIterator(U* data) noexcept - : Base{data} - { - } - - template requires (channelCount == DynamicChannelCount) - constexpr InterleavedFrameIterator(U* data, proc_ch_t channels) noexcept - : Base{data, channels} - { - } + using Base::Base; constexpr auto operator*() const noexcept -> value_type { return this->m_data; } From f60bc1422983ce3085247e0c2301023ae4beafe5 Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Tue, 5 Aug 2025 04:08:48 -0400 Subject: [PATCH 09/10] Add AudioBufferView --- include/AudioBufferView.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/AudioBufferView.h b/include/AudioBufferView.h index 427aa32e76c..14b33e66c59 100644 --- a/include/AudioBufferView.h +++ b/include/AudioBufferView.h @@ -27,6 +27,7 @@ #define LMMS_AUDIO_BUFFER_VIEW_H #include +#include #include #include @@ -446,6 +447,9 @@ class InterleavedBufferView : public detail::BufferViewData { return {reinterpret_cast(this->m_data), this->m_frames}; } + + //! Use to distinguish between `InterleavedBufferView` and `PlanarBufferView` when using `AnyBufferView` + static constexpr bool Interleaved = true; }; // Check that the std::span-like space optimization works @@ -540,12 +544,21 @@ class PlanarBufferView : public detail::BufferViewData { return bufferPtr(channel); } + + //! Use to distinguish between `InterleavedBufferView` and `PlanarBufferView` when using `AnyBufferView` + static constexpr bool Interleaved = false; }; // Check that the std::span-like space optimization works static_assert(sizeof(PlanarBufferView) > sizeof(PlanarBufferView)); static_assert(sizeof(PlanarBufferView) == sizeof(void**) + sizeof(f_cnt_t)); + +//! Concept for any buffer view - interleaved or planar +template +concept AnyBufferView = SampleType && (std::convertible_to> + || std::convertible_to>); + } // namespace lmms #endif // LMMS_AUDIO_BUFFER_VIEW_H From ee03af82a50d194e91c2d6f2c117a5af1d109c5e Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Wed, 6 Aug 2025 03:37:50 -0400 Subject: [PATCH 10/10] Rename AnyBufferView to AudioBufferView --- include/AudioBufferView.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/AudioBufferView.h b/include/AudioBufferView.h index 14b33e66c59..f0c0b0864f6 100644 --- a/include/AudioBufferView.h +++ b/include/AudioBufferView.h @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -448,7 +449,7 @@ class InterleavedBufferView : public detail::BufferViewData return {reinterpret_cast(this->m_data), this->m_frames}; } - //! Use to distinguish between `InterleavedBufferView` and `PlanarBufferView` when using `AnyBufferView` + //! Use to distinguish between `InterleavedBufferView` and `PlanarBufferView` when using `AudioBufferView` static constexpr bool Interleaved = true; }; @@ -545,7 +546,7 @@ class PlanarBufferView : public detail::BufferViewData return bufferPtr(channel); } - //! Use to distinguish between `InterleavedBufferView` and `PlanarBufferView` when using `AnyBufferView` + //! Use to distinguish between `InterleavedBufferView` and `PlanarBufferView` when using `AudioBufferView` static constexpr bool Interleaved = false; }; @@ -554,9 +555,9 @@ static_assert(sizeof(PlanarBufferView) > sizeof(PlanarBufferView) == sizeof(void**) + sizeof(f_cnt_t)); -//! Concept for any buffer view - interleaved or planar +//! Concept for any audio buffer view, interleaved or planar template -concept AnyBufferView = SampleType && (std::convertible_to> +concept AudioBufferView = SampleType && (std::convertible_to> || std::convertible_to>); } // namespace lmms