146 if (config.channels == 0 || config.declared_frames == 0 || config.sample_rate == 0 ||
147 config.max_pull_frames == 0 || !producer)
149 if (config.initial_epoch == 0)
151 if (config.preroll_frames > config.declared_frames)
153 const bool declares_loop = config.loop_start_frame != 0 || config.loop_end_frame != 0;
154 if (declares_loop && (config.loop_start_frame >= config.loop_end_frame ||
155 config.loop_end_frame > config.declared_frames))
158 const std::uint64_t declared_lookahead =
159 std::max<std::uint64_t>(
161 if (config.ring_capacity_frames != 0 &&
162 (config.ring_capacity_frames < config.max_pull_frames ||
163 config.ring_capacity_frames < config.preroll_frames))
167 declared_frames_ = config.declared_frames;
168 preroll_frames_ = config.preroll_frames;
169 loop_start_frame_ = config.loop_start_frame;
170 loop_end_frame_ = config.loop_end_frame;
171 declared_lookahead_ = declared_lookahead;
173 producer_ = std::move(producer);
174 epoch_.store(config.initial_epoch, std::memory_order_relaxed);
176 if (!start_stream()) {
188 producer_stop_requested_.store(
true, std::memory_order_release);
190 producer_stop_requested_.store(
false, std::memory_order_release);
192 declared_frames_ = 0;
194 loop_start_frame_ = 0;
196 declared_lookahead_ = 0;
199 epoch_.store(0, std::memory_order_relaxed);
202 produced_frames_.store(0, std::memory_order_relaxed);
203 starved_frames_.store(0, std::memory_order_relaxed);
204 starvation_events_.store(0, std::memory_order_relaxed);
222 if (frame >= declared_frames_)
224 if (preroll_frames_ > declared_frames_ - frame)
230 epoch_.store(epoch, std::memory_order_release);
231 producer_stop_requested_.store(
true, std::memory_order_release);
233 producer_stop_requested_.store(
false, std::memory_order_release);
235 if (!start_stream()) {
247 if (!prepared_ || (loop_start_frame_ == 0 && loop_end_frame_ == 0))
249 return seek(loop_start_frame_, epoch);
262 producer_stop_requested_.store(
true, std::memory_order_release);
263 while (producer_calls_in_flight_.load(std::memory_order_acquire) != 0)
264 std::this_thread::yield();
265 producer_stop_requested_.store(
false, std::memory_order_release);
272 frames = std::min<std::uint64_t>(frames, destination.num_samples());
273 const std::uint64_t
position = base_frame_ + stream_.position();
274 const std::uint64_t remaining =
276 const std::uint64_t expected = std::min(frames, remaining);
277 const std::uint64_t produced = stream_.pull(destination, frames);
278 produced_frames_.fetch_add(produced, std::memory_order_relaxed);
279 if (produced < expected) {
280 starved_frames_.fetch_add(expected - produced, std::memory_order_relaxed);
281 starvation_events_.fetch_add(1, std::memory_order_relaxed);
290 return stream_.pump_background();
300 return declared_frames_;
304 return epoch_.load(std::memory_order_acquire);
308 return loop_start_frame_;
311 return loop_end_frame_;
315 return base_frame_ + stream_.position();
319 return position() >= declared_frames_;
323 const auto stream_stats = stream_.stats();
325 result.
produced_frames = produced_frames_.load(std::memory_order_relaxed);
326 result.
starved_frames = starved_frames_.load(std::memory_order_relaxed);
336 std::uint32_t sample_rate)
noexcept {
337 return declaration.lookahead_ms *
static_cast<std::uint64_t
>(sample_rate) / 1000u;
345 bool start_stream() {
348 stream_config.
total_frames = declared_frames_ - base_frame_;
356 : std::max({declared_lookahead_, config_.
max_pull_frames, preroll_frames_});
365 std::max(declared_lookahead_, preroll_frames_);
370 return produce_for_stream(stream_frame, destination, frames, stop_token);
372 if (!stream_.
prepare(stream_config, std::move(binding)))
376 if (preroll_frames_ > 0 &&
391 std::uint64_t produce_for_stream(std::uint64_t stream_frame,
392 audio::BufferView<float> destination,
393 std::uint64_t frames,
394 audio::FrameReaderStopToken stream_stop_token) {
395 if (stream_stop_token.stop_requested())
398 producer_calls_in_flight_.fetch_add(1, std::memory_order_acq_rel);
400 std::atomic<std::uint32_t>&
count;
402 count.fetch_sub(1, std::memory_order_acq_rel);
404 } in_flight{producer_calls_in_flight_};
405 const std::uint64_t produced =
406 producer_(call_epoch, base_frame_ + stream_frame, destination, frames,
407 audio::FrameReaderStopToken{producer_stop_requested_});
410 if (producer_stop_requested_.load(std::memory_order_acquire))
415 audio::StreamingSampleSource stream_;
416 timeline::ProductionDeclaration declaration_;
417 std::uint64_t declared_frames_ = 0;
418 std::uint64_t preroll_frames_ = 0;
419 std::uint64_t loop_start_frame_ = 0;
420 std::uint64_t loop_end_frame_ = 0;
421 std::uint64_t declared_lookahead_ = 0;
422 BufferedContentSourceConfig config_{};
427 std::uint64_t base_frame_ = 0;
428 bool prepared_ =
false;
430 std::atomic<BufferedContentEpoch> epoch_{0};
431 std::atomic<bool> producer_stop_requested_{
false};
432 std::atomic<std::uint32_t> producer_calls_in_flight_{0};
434 std::atomic<std::uint64_t> produced_frames_{0};
435 std::atomic<std::uint64_t> starved_frames_{0};
436 std::atomic<std::uint64_t> starvation_events_{0};
Non-owning view over multi-channel audio sample data.
Definition buffer.hpp:30
Cooperative cancellation signal passed to a streaming frame reader.
Definition streaming_sample_source.hpp:45
Stats stats() const noexcept
void release() noexcept
Stop the reader thread and free all storage. Control thread only.
bool prepare(const StreamingSampleSourceConfig &config, FrameReader reader)
Allocate buffers, synchronously fill the preload window via reader, and (optionally) start the backgr...
bool exhausted() const noexcept
True once every declared frame has been accounted for, produced or not.
Definition buffered_content_source.hpp:318
static std::uint64_t lookahead_frames(const timeline::ProductionDeclaration &declaration, std::uint32_t sample_rate) noexcept
Frames the declaration's wall-clock lookahead covers at sample_rate.
Definition buffered_content_source.hpp:335
bool prepare(const timeline::ProductionDeclaration &declaration, const BufferedContentSourceConfig &config, BufferedContentProducer producer)
Control thread.
Definition buffered_content_source.hpp:138
~BufferedContentSource()
Definition buffered_content_source.hpp:113
std::uint64_t declared_frames() const noexcept
Definition buffered_content_source.hpp:299
std::uint64_t pump_background() noexcept
Background or host thread.
Definition buffered_content_source.hpp:289
BufferedContentSource()=default
BufferedContentSource & operator=(const BufferedContentSource &)=delete
bool seek(std::uint64_t frame, BufferedContentEpoch epoch)
Control thread; no pull() may be in flight.
Definition buffered_content_source.hpp:217
void release() noexcept
Control thread. Stops the producer and frees all storage.
Definition buffered_content_source.hpp:185
BufferedContentEpoch current_epoch() const noexcept
The epoch production currently belongs to. RT-safe.
Definition buffered_content_source.hpp:303
std::uint64_t loop_end_frame() const noexcept
Definition buffered_content_source.hpp:310
std::uint64_t loop_start_frame() const noexcept
Declared loop region; both zero when no loop is declared.
Definition buffered_content_source.hpp:307
const timeline::ProductionDeclaration & declaration() const noexcept
Definition buffered_content_source.hpp:296
bool prepared() const noexcept
Definition buffered_content_source.hpp:293
void cancel_production() noexcept
Control thread.
Definition buffered_content_source.hpp:259
BufferedContentSource(const BufferedContentSource &)=delete
Stats stats() const noexcept
Definition buffered_content_source.hpp:322
std::uint64_t position() const noexcept
Audio-thread position in content frames of the current epoch.
Definition buffered_content_source.hpp:314
bool loop_wrap(BufferedContentEpoch epoch)
Control thread.
Definition buffered_content_source.hpp:246
std::uint64_t pull(audio::BufferView< float > destination, std::uint64_t frames) noexcept
Audio thread.
Definition buffered_content_source.hpp:271
ProductionDeclarationErrorCode validate_production_declaration(const ProductionDeclaration &declaration) noexcept
Validate a declaration. Returns None when the declaration is coherent.
@ Buffered
Produced ahead of the playhead on another thread and consumed through a ring.
Definition production_mode.hpp:38
@ None
Definition production_mode.hpp:98
@ AdvancePosition
Definition streaming_sample_source.hpp:96
@ count
sentinel: number of real capabilities (keep LAST)
Definition canvas_capability.hpp:38
Definition audio_renderer.hpp:20
std::uint64_t BufferedContentEpoch
Identifies one continuous production interval.
Definition buffered_content_source.hpp:58
std::function< std::uint64_t(BufferedContentEpoch epoch, std::uint64_t start_frame, audio::BufferView< float > destination, std::uint64_t frames, audio::FrameReaderStopToken stop_token)> BufferedContentProducer
Caller-supplied producer of buffered content.
Definition buffered_content_source.hpp:70
How content is produced, and how honestly a render of it can be replayed.
Real-time-safe streaming sample playback primitive.
Definition streaming_sample_source.hpp:89
StoppableFrameReader read
Definition streaming_sample_source.hpp:90
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 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
StreamingUnderrunPolicy underrun_policy
Whether a streaming underrun preserves the source cursor or consumes its missing interval.
Definition streaming_sample_source.hpp:130
Definition buffered_content_source.hpp:75
BufferedContentEpoch initial_epoch
First production epoch.
Definition buffered_content_source.hpp:104
std::uint64_t max_pull_frames
Largest block the audio thread will request in one pull.
Definition buffered_content_source.hpp:83
std::uint64_t loop_start_frame
Declared loop region [loop_start_frame, loop_end_frame).
Definition buffered_content_source.hpp:100
std::uint64_t declared_frames
Frames the content declares it will produce.
Definition buffered_content_source.hpp:80
std::uint32_t sample_rate
Definition buffered_content_source.hpp:81
std::uint64_t preroll_frames
Frames synchronously produced into the ring during prepare() and again after every seek,...
Definition buffered_content_source.hpp:95
bool start_background_thread
Spawn an owned producer thread.
Definition buffered_content_source.hpp:107
std::uint32_t channels
Definition buffered_content_source.hpp:76
std::uint64_t produce_chunk_frames
How much the pump asks the producer for per refill step.
Definition buffered_content_source.hpp:89
std::uint64_t ring_capacity_frames
Ring capacity in frames.
Definition buffered_content_source.hpp:87
std::uint64_t loop_end_frame
Definition buffered_content_source.hpp:101
Definition buffered_content_source.hpp:118
std::uint64_t ring_underrun_frames
Ring-level underruns reported by the underlying stream.
Definition buffered_content_source.hpp:128
std::uint64_t starved_frames
Declared frames the audio thread had to zero-fill because they had not been produced in time.
Definition buffered_content_source.hpp:123
std::uint64_t producer_errors
Producer exceptions.
Definition buffered_content_source.hpp:133
std::uint64_t starvation_events
Pull calls that came up short of what the declaration promised.
Definition buffered_content_source.hpp:125
std::uint64_t produced_frames
Frames the producer supplied and the audio thread consumed.
Definition buffered_content_source.hpp:120
std::uint64_t ring_available_frames
Frames currently ready for the audio thread.
Definition buffered_content_source.hpp:130
What a piece of content declares about its own production.
Definition production_mode.hpp:88