Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
streaming_sample_source.hpp
Go to the documentation of this file.
1#pragma once
2
31
32#include <atomic>
33#include <cstdint>
34#include <functional>
35#include <thread>
36
37#include <pulp/audio/buffer.hpp>
40
41namespace pulp::audio {
42
46public:
47 FrameReaderStopToken() noexcept = default;
48
50 const std::atomic<bool>& stop_requested) noexcept
51 : stop_requested_(&stop_requested) {}
52
53 bool stop_requested() const noexcept {
54 return stop_requested_ != nullptr &&
55 stop_requested_->load(std::memory_order_acquire);
56 }
57
58private:
59 const std::atomic<bool>* stop_requested_ = nullptr;
60};
61
74 std::function<std::uint64_t(std::uint64_t start_frame,
76 std::uint64_t frames)>;
77
78enum class FrameReaderStopMode : std::uint8_t {
81};
82
84 std::function<std::uint64_t(std::uint64_t start_frame,
86 std::uint64_t frames,
87 FrameReaderStopToken stop_token)>;
88
93
94enum class StreamingUnderrunPolicy : std::uint8_t {
97};
98
100 std::uint32_t channels = 0;
101 std::uint64_t total_frames = 0;
102 std::uint32_t sample_rate = 0;
103
107 std::uint64_t preload_frames = 0;
108
111 std::uint64_t ring_capacity_frames = 0;
112
115 std::uint64_t read_chunk_frames = 8192;
116
119 bool loop = false;
120 std::uint64_t loop_start = 0;
121 std::uint64_t loop_end = 0;
122
126
132
137 std::uint64_t max_read_ahead_frames = 0;
138};
139
141public:
144
147
153 FrameReaderBinding reader);
154
156 void release() noexcept;
157
161 bool reset() noexcept;
162
163 // ── Audio thread (RT-safe after prepare) ────────────────────────────────
164
169 std::uint64_t pull(BufferView<float> dest, std::uint64_t frames) noexcept;
170
173 bool finished() const noexcept;
174
176 std::uint64_t position() const noexcept {
177 return play_pos_.load(std::memory_order_relaxed);
178 }
179
181 bool fully_resident() const noexcept { return fully_resident_; }
182
183 bool prepared() const noexcept { return prepared_; }
184 std::uint32_t channels() const noexcept { return channels_; }
185 std::uint64_t total_frames() const noexcept { return total_frames_; }
186
187 // ── Background refill ───────────────────────────────────────────────────
188
194 std::uint64_t pump_background() noexcept;
195
196 struct Stats {
197 std::uint64_t underrun_frames = 0;
198 std::uint64_t ring_available_frames = 0;
199 std::uint64_t streamed_frames = 0;
202 std::uint64_t read_errors = 0;
203 bool streaming_active = false;
204 };
205 Stats stats() const noexcept;
206
207private:
208 struct DeadlinePolicy {
209 bool enabled = false;
210 std::uint64_t max_read_ahead_frames = 0;
211 std::atomic<std::uint64_t> ring_start_frame{0};
212
213 void configure(const StreamingSampleSourceConfig& config,
214 std::uint64_t initial_frame) noexcept;
215 void clear() noexcept;
216 std::uint64_t effective_end(std::uint64_t total_frames,
217 std::uint64_t eos_frame) const noexcept;
218 void apply_underrun(std::atomic<std::uint64_t>& play_pos,
219 std::uint64_t next_position) const noexcept;
220 bool zero_is_backpressure() const noexcept;
221 std::uint64_t producer_cursor(std::uint64_t play_pos,
222 std::uint64_t reader_pos) const noexcept;
223 bool discard_stale(PlanarAudioRingBuffer& ring,
224 std::uint64_t play_pos) noexcept;
225 void consumed(std::uint64_t ring_start,
226 std::uint64_t frames) noexcept;
227 bool align_reader(PlanarAudioRingBuffer& ring,
228 std::uint64_t play_pos,
229 std::uint64_t& reader_pos) noexcept;
230 std::uint64_t read_ahead_room(std::uint64_t total_frames,
231 std::uint64_t play_pos,
232 std::uint64_t reader_pos) const noexcept;
233 void publish_start_if_empty(PlanarAudioRingBuffer& ring,
234 std::uint64_t frame) noexcept;
235 };
236
237 void stop_thread() noexcept;
238 void reader_loop() noexcept;
239 void notify_reader() noexcept;
240 // Copy [src_start, src_start+n) from the resident preload window into the
241 // [dest_offset, dest_offset+n) region of dest. Audio thread.
242 void copy_from_preload(BufferView<float>& dest, std::uint64_t dest_offset,
243 std::uint64_t src_start, std::uint64_t n) noexcept;
244
245 // Immutable after prepare.
246 std::uint32_t channels_ = 0;
247 std::uint64_t total_frames_ = 0;
248 std::uint32_t sample_rate_ = 0;
249 std::uint64_t preload_valid_ = 0;
250 std::uint64_t read_chunk_ = 0;
251 bool loop_ = false;
252 std::uint64_t loop_start_ = 0;
253 std::uint64_t loop_end_ = 0;
254 bool fully_resident_ = false;
255 bool prepared_ = false;
256 bool use_thread_ = false;
257 DeadlinePolicy deadline_;
258
259 Buffer<float> preload_;
261 Buffer<float> read_scratch_;
262 FrameReaderBinding reader_;
263 std::atomic<bool> reader_stop_requested_{false};
264
265 std::atomic<std::uint64_t> play_pos_{0};
266 std::atomic<std::uint64_t> reader_pos_{0};
267 // Effective end-of-stream in source frames: starts at total_frames_ and is
268 // shrunk by the background reader to the realized length if the FrameReader
269 // signals an early end/error mid-stream (a short/zero return before the
270 // declared total). The audio thread reads it to terminate one-shot playback
271 // and report finished() at the real end instead of stalling forever waiting
272 // for frames that will never arrive. Written only by the background reader,
273 // read by the audio thread — hence atomic (total_frames_ itself stays an
274 // immutable-after-prepare plain member that the audio thread can read freely).
275 std::atomic<std::uint64_t> eos_frame_{0};
276 std::atomic<std::uint64_t> streamed_frames_{0};
277 std::atomic<std::uint64_t> read_errors_{0};
278 std::atomic<std::uint64_t> underrun_frames_{0};
279
280 std::thread thread_;
281 std::atomic<bool> run_{false};
282 // pull() (audio thread) and stop_thread() -> the background reader.
283 runtime::WakeSignal wake_;
284};
285
286} // namespace pulp::audio
Non-owning view over multi-channel audio sample data.
Definition buffer.hpp:30
Owning multi-channel audio buffer with contiguous storage.
Definition buffer.hpp:119
Cooperative cancellation signal passed to a streaming frame reader.
Definition streaming_sample_source.hpp:45
FrameReaderStopToken() noexcept=default
bool stop_requested() const noexcept
Definition streaming_sample_source.hpp:53
Definition planar_audio_ring_buffer.hpp:22
StreamingSampleSource(const StreamingSampleSource &)=delete
bool fully_resident() const noexcept
True when the whole source fits in the preload window (no streaming).
Definition streaming_sample_source.hpp:181
Stats stats() const noexcept
std::uint64_t pump_background() noexcept
Top the streamed-tail ring up toward full using the FrameReader.
bool reset() noexcept
Rewind to the start of the source (or loop_start) and refill the tail.
void release() noexcept
Stop the reader thread and free all storage. Control thread only.
std::uint64_t total_frames() const noexcept
Definition streaming_sample_source.hpp:185
std::uint32_t channels() const noexcept
Definition streaming_sample_source.hpp:184
bool prepared() const noexcept
Definition streaming_sample_source.hpp:183
bool prepare(const StreamingSampleSourceConfig &config, FrameReader reader)
Allocate buffers, synchronously fill the preload window via reader, and (optionally) start the backgr...
StreamingSampleSource & operator=(const StreamingSampleSource &)=delete
bool prepare(const StreamingSampleSourceConfig &config, FrameReaderBinding reader)
std::uint64_t pull(BufferView< float > dest, std::uint64_t frames) noexcept
Emit frames sequential frames into dest, advancing the play cursor.
std::uint64_t position() const noexcept
Current play position in source frames. RT-safe.
Definition streaming_sample_source.hpp:176
bool finished() const noexcept
True once a non-looping source has played to its end and the tail is drained.
Definition analyzer_provider.hpp:12
std::function< std::uint64_t(std::uint64_t start_frame, BufferView< float > dest, std::uint64_t frames, FrameReaderStopToken stop_token)> StoppableFrameReader
Definition streaming_sample_source.hpp:83
std::function< std::uint64_t(std::uint64_t start_frame, BufferView< float > dest, std::uint64_t frames)> FrameReader
Caller-supplied random-access frame reader.
Definition streaming_sample_source.hpp:73
StreamingUnderrunPolicy
Definition streaming_sample_source.hpp:94
@ AdvancePosition
Definition streaming_sample_source.hpp:96
@ PreservePosition
Definition streaming_sample_source.hpp:95
FrameReaderStopMode
Definition streaming_sample_source.hpp:78
@ Cooperative
Definition streaming_sample_source.hpp:80
@ JoinOnly
Definition streaming_sample_source.hpp:79
STL namespace.
Definition streaming_sample_source.hpp:89
StoppableFrameReader read
Definition streaming_sample_source.hpp:90
FrameReaderStopMode stop_mode
Definition streaming_sample_source.hpp:91
Definition streaming_sample_source.hpp:99
std::uint32_t sample_rate
Source sample rate (carried, not resampled here).
Definition streaming_sample_source.hpp:102
std::uint64_t total_frames
Total length of the source in frames.
Definition streaming_sample_source.hpp:101
std::uint64_t preload_frames
Resident head length in frames.
Definition streaming_sample_source.hpp:107
std::uint64_t ring_capacity_frames
Streamed-tail ring capacity in frames.
Definition streaming_sample_source.hpp:111
bool start_background_thread
Spawn the owned background reader thread in prepare().
Definition streaming_sample_source.hpp:125
std::uint64_t loop_start
Loop start frame (resident-loop only).
Definition streaming_sample_source.hpp:120
std::uint64_t max_read_ahead_frames
Maximum distance the reader may produce ahead of the play cursor.
Definition streaming_sample_source.hpp:137
std::uint64_t read_chunk_frames
Background read granularity in frames (how much the reader pulls per refill step).
Definition streaming_sample_source.hpp:115
std::uint32_t channels
Source channel count (1 or 2 typical).
Definition streaming_sample_source.hpp:100
std::uint64_t loop_end
Loop end frame, exclusive; 0 means total_frames.
Definition streaming_sample_source.hpp:121
bool loop
Loop the playback.
Definition streaming_sample_source.hpp:119
StreamingUnderrunPolicy underrun_policy
Whether a streaming underrun preserves the source cursor or consumes its missing interval.
Definition streaming_sample_source.hpp:130
Definition streaming_sample_source.hpp:196
std::uint64_t streamed_frames
Source frames pushed through the ring since prepare/reset.
Definition streaming_sample_source.hpp:199
std::uint64_t read_errors
Reader exceptions, plus zero returns when preserve-position mode treats them as terminal.
Definition streaming_sample_source.hpp:202
std::uint64_t underrun_frames
Frames the audio thread had to zero-fill mid-stream.
Definition streaming_sample_source.hpp:197
std::uint64_t ring_available_frames
Definition streaming_sample_source.hpp:198
bool streaming_active
Tail not yet fully streamed.
Definition streaming_sample_source.hpp:203
Wakes a sleeping consumer thread from a realtime producer without a lock, and without a system call w...