Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
anticipation_lane.hpp
Go to the documentation of this file.
1#pragma once
2
9
10#include <atomic>
11#include <cstdint>
12#include <functional>
13#include <vector>
14
15namespace pulp::host {
16
17class PluginSlot;
18
19// Renders an anticipation sub-graph AHEAD of the audio deadline into a ring, so
20// the live graph reads the pre-computed boundary signals instead of paying the
21// sub-graph's cost on the critical block. Single-producer /
22// single-consumer:
23// - render_ahead() runs OFF the audio thread (a background/idle worker). It
24// advances the interior's plugin state and pushes whole blocks into the ring.
25// - consume() runs ON the audio thread. It pops one pre-rendered block, or
26// reports underrun so the caller can fall back to a synchronous render.
27// The interior plugins are advanced ONLY here; a live splice that uses a lane
28// must not also process those nodes, or their state double-advances.
29//
30// Determinism: the producer renders the sub-graph for blocks in order, so the
31// block sequence the consumer pops is bit-identical to rendering the same
32// sub-graph synchronously block by block.
34public:
35 AnticipationLane() = default;
38
39 // Off-RT, and requires no concurrent render_ahead/consume (quiescent, like the
40 // underlying ring). Build the executor snapshot for `sub` (resolving each
41 // interior node's gain atomic / plugin slot), size the scratch pool for
42 // `block_frames`, and size an output-channel ring holding `lead_blocks` blocks.
43 // The lane operates on ONE fixed block size — pinning it here (before any
44 // producer/consumer thread exists) keeps the producer and consumer in lockstep
45 // so the consumed sequence is deterministic, and avoids a cross-thread block-
46 // size field. Returns false if `sub` isn't renderable (ineligible interior, no
47 // outputs) or any sizing fails. block_frames and lead_blocks must be >= 1.
49 const std::function<std::atomic<float>*(NodeId)>& gain_for,
50 const std::function<PluginSlot*(NodeId)>& plugin_for,
51 double sample_rate, int block_frames, int lead_blocks,
52 const std::function<ParameterEventInjectionBinding(NodeId)>&
53 parameter_events_for = {});
54
55 bool prepared() const noexcept { return prepared_; }
56 std::size_t output_channels() const noexcept { return out_channels_; }
57 int block_frames() const noexcept { return block_frames_; }
58 std::uint64_t buffered_frames() const noexcept { return ring_.available_frames(); }
59 std::uint64_t lead_capacity_frames() const noexcept { return ring_.capacity_frames(); }
60
61 // PRODUCER (off the audio thread): render fixed-size blocks until the ring
62 // can't hold another whole block or `max_blocks` have been rendered, whichever
63 // first. Advances interior plugin state. Returns the number of blocks rendered.
64 // Runs off the realtime thread (the per-block bus setup may allocate); never
65 // call it from the audio callback. SINGLE PRODUCER: all render_ahead calls
66 // (including any priming) must be serialized — they share unsynchronized
67 // executor/pool/scratch + plugin state with each other (only the ring mediates
68 // against the consumer).
69 int render_ahead(int max_blocks);
70
71 // CONSUMER (audio thread): pop one block into `out`, which must have
72 // output_channels() channels and at least block_frames() samples. Returns true
73 // on a hit; false on underrun (a whole block isn't buffered — the caller should
74 // render synchronously this block) or a malformed `out`. Real-time-safe: no
75 // allocation, no locks.
77
78private:
79 bool render_one_block();
80
82 std::vector<PluginBindingContext> plugin_ctx_;
83 PluginRoutingScratch scratch_;
87
88 // Producer-side scratch (pre-allocated): a zeroed main input bus the interior
89 // ignores, and the capture buffers process_routed writes the boundary channels
90 // into before they go to the ring.
91 std::vector<std::vector<float>> prod_in_;
92 std::vector<std::vector<float>> prod_out_;
93 std::vector<const float*> prod_in_ptrs_;
94 std::vector<float*> prod_out_ptrs_;
95
96 double sample_rate_ = 0.0;
97 int block_frames_ = 0;
98 std::size_t out_channels_ = 0;
99 bool prepared_ = false;
100};
101
102} // namespace pulp::host
Non-owning view over multi-channel audio sample data.
Definition buffer.hpp:30
Definition planar_audio_ring_buffer.hpp:22
Pre-allocated scratch buffers backing the routing process() path.
Definition graph_runtime_executor.hpp:297
Definition graph_runtime_executor.hpp:738
Control-thread-built graph snapshot for GraphRuntimeExecutor.
Definition graph_runtime_executor.hpp:159
bool prepare(const AnticipationSubgraph &sub, const std::function< std::atomic< float > *(NodeId)> &gain_for, const std::function< PluginSlot *(NodeId)> &plugin_for, double sample_rate, int block_frames, int lead_blocks, const std::function< ParameterEventInjectionBinding(NodeId)> &parameter_events_for={})
int render_ahead(int max_blocks)
std::uint64_t lead_capacity_frames() const noexcept
Definition anticipation_lane.hpp:59
bool prepared() const noexcept
Definition anticipation_lane.hpp:55
AnticipationLane & operator=(const AnticipationLane &)=delete
std::size_t output_channels() const noexcept
Definition anticipation_lane.hpp:56
std::uint64_t buffered_frames() const noexcept
Definition anticipation_lane.hpp:58
int block_frames() const noexcept
Definition anticipation_lane.hpp:57
bool consume(pulp::audio::BufferView< float > &out) noexcept
AnticipationLane(const AnticipationLane &)=delete
Definition plugin_slot.hpp:94
std::uint32_t NodeId
Definition graph_types.hpp:7
Bounded event contracts shared by host ingress and graph-authored nodes.
Definition anticipation_eligibility.hpp:9
Definition anticipation_subgraph.hpp:28
Definition signal_graph_executor_routing.hpp:74
Definition signal_graph_executor_routing.hpp:38