Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
planar_audio_ring_buffer.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <atomic>
5#include <cstdint>
6#include <limits>
7#include <memory>
8#include <type_traits>
9#include <vector>
10
11#include <pulp/audio/buffer.hpp>
13
14namespace pulp::audio {
15
17 std::uint64_t underrun_frames = 0;
18 std::uint64_t overrun_frames = 0;
19 std::uint64_t dropped_write_frames = 0;
20};
21
23public:
25
26 // Offline/quiescent only. Allocates fixed storage and FIFO state.
27 bool prepare(std::uint32_t num_channels, std::uint64_t usable_capacity_frames);
28 void release() noexcept;
29 void reset() noexcept;
30
31 std::uint32_t num_channels() const noexcept { return num_channels_; }
32 std::uint64_t capacity_frames() const noexcept { return usable_capacity_frames_; }
33 std::uint64_t available_frames() const noexcept;
34 std::uint64_t free_frames() const noexcept;
36
37 // RT-safe after prepare for one producer and one consumer. Short reads
38 // zero-fill and report underrun stats; short writes drop excess frames.
39 std::uint64_t write(BufferView<const float> source, std::uint64_t frames) noexcept {
40 return write_impl(source, frames);
41 }
42
43 std::uint64_t write(BufferView<float> source, std::uint64_t frames) noexcept {
44 return write_impl(source, frames);
45 }
46
47 bool read(BufferView<float> destination, std::uint64_t frames) noexcept;
48 std::uint64_t drain(std::uint64_t max_frames) noexcept;
49
50private:
51 void clear_unprepared() noexcept;
52 static bool valid_capacity(std::uint64_t usable_capacity_frames) noexcept;
53 static int clamp_to_fifo_count(std::uint64_t frames) noexcept;
54 float* channel_storage(std::uint32_t channel) noexcept;
55 const float* channel_storage(std::uint32_t channel) const noexcept;
56
57 template<typename SampleType>
58 void copy_source_segment(BufferView<SampleType> source,
59 int destination_start,
60 std::uint64_t source_offset,
61 int frame_count) noexcept;
62
63 template<typename SampleType>
64 std::uint64_t write_impl(BufferView<SampleType> source, std::uint64_t frames) noexcept;
65
66 std::vector<float> storage_;
67 std::unique_ptr<runtime::SpscRingIndex> fifo_;
68 std::uint32_t num_channels_ = 0;
69 std::uint64_t usable_capacity_frames_ = 0;
70 std::uint64_t internal_capacity_frames_ = 0;
71
72 std::atomic<std::uint64_t> underrun_frames_{0};
73 std::atomic<std::uint64_t> overrun_frames_{0};
74 std::atomic<std::uint64_t> dropped_write_frames_{0};
75};
76
77template<typename SampleType>
78void PlanarAudioRingBuffer::copy_source_segment(BufferView<SampleType> source,
79 int destination_start,
80 std::uint64_t source_offset,
81 int frame_count) noexcept {
82 static_assert(std::is_same_v<std::remove_const_t<SampleType>, float>,
83 "PlanarAudioRingBuffer only supports float sample views");
84
85 if (frame_count <= 0) return;
86
87 for (std::uint32_t ch = 0; ch < num_channels_; ++ch) {
88 float* destination = channel_storage(ch) + destination_start;
89 if (ch < source.num_channels()) {
90 const SampleType* source_ptr = source.channel_ptr(ch) + source_offset;
91 std::copy_n(source_ptr, static_cast<std::size_t>(frame_count), destination);
92 } else {
93 std::fill_n(destination, static_cast<std::size_t>(frame_count), 0.0f);
94 }
95 }
96}
97
98template<typename SampleType>
99std::uint64_t PlanarAudioRingBuffer::write_impl(BufferView<SampleType> source,
100 std::uint64_t frames) noexcept {
101 static_assert(std::is_same_v<std::remove_const_t<SampleType>, float>,
102 "PlanarAudioRingBuffer only supports float sample views");
103
104 if (!fifo_ || num_channels_ == 0 || frames == 0 ||
105 source.num_channels() == 0 || source.num_samples() == 0) {
106 return 0;
107 }
108
109 const auto requested = std::min(frames, static_cast<std::uint64_t>(source.num_samples()));
110 int start_1 = 0;
111 int size_1 = 0;
112 int start_2 = 0;
113 int size_2 = 0;
114 fifo_->prepare_to_write(clamp_to_fifo_count(requested), start_1, size_1, start_2, size_2);
115
116 const auto written = static_cast<std::uint64_t>(size_1 + size_2);
117 if (written == 0) {
118 if (requested > 0) {
119 overrun_frames_.fetch_add(requested, std::memory_order_relaxed);
120 dropped_write_frames_.fetch_add(requested, std::memory_order_relaxed);
121 }
122 return 0;
123 }
124
125 copy_source_segment(source, start_1, 0, size_1);
126 copy_source_segment(source, start_2, static_cast<std::uint64_t>(size_1), size_2);
127 fifo_->finish_write(static_cast<int>(written));
128
129 // Stats describe the completed write; they are not a publication handshake.
130 // Consumers that require whole-block continuity must preflight capacity or
131 // carry a separate generation protocol, as VisualizationBridge does.
132 if (written < requested) {
133 const auto dropped = requested - written;
134 overrun_frames_.fetch_add(dropped, std::memory_order_relaxed);
135 dropped_write_frames_.fetch_add(dropped, std::memory_order_relaxed);
136 }
137
138 return written;
139}
140
141} // namespace pulp::audio
Non-owning view over multi-channel audio sample data.
Definition buffer.hpp:30
std::uint32_t num_channels() const noexcept
Definition planar_audio_ring_buffer.hpp:31
bool prepare(std::uint32_t num_channels, std::uint64_t usable_capacity_frames)
bool read(BufferView< float > destination, std::uint64_t frames) noexcept
PlanarAudioRingBufferStats stats() const noexcept
std::uint64_t free_frames() const noexcept
std::uint64_t write(BufferView< float > source, std::uint64_t frames) noexcept
Definition planar_audio_ring_buffer.hpp:43
std::uint64_t drain(std::uint64_t max_frames) noexcept
std::uint64_t write(BufferView< const float > source, std::uint64_t frames) noexcept
Definition planar_audio_ring_buffer.hpp:39
std::uint64_t available_frames() const noexcept
std::uint64_t capacity_frames() const noexcept
Definition planar_audio_ring_buffer.hpp:32
Definition analyzer_provider.hpp:12
Definition generated_event_source.hpp:33
STL namespace.
Definition planar_audio_ring_buffer.hpp:16
std::uint64_t underrun_frames
Definition planar_audio_ring_buffer.hpp:17
std::uint64_t overrun_frames
Definition planar_audio_ring_buffer.hpp:18
std::uint64_t dropped_write_frames
Definition planar_audio_ring_buffer.hpp:19