Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
program_compiler.hpp
Go to the documentation of this file.
1#pragma once
2
9
10#include <chrono>
11#include <cstdint>
12#include <memory>
13#include <mutex>
14#include <optional>
15#include <utility>
16#include <vector>
17
18namespace pulp::timeline {
19struct CommitResult;
20}
21
22namespace pulp::playback {
23
29
37 CompileInvalidationInput(std::shared_ptr<const CompileContextRegistry> registry,
38 const timeline::CommitResult& committed);
39
43 static CompileInvalidationInput baseline(std::shared_ptr<const CompileContextRegistry> registry,
44 std::shared_ptr<const timeline::Project> snapshot,
45 std::uint64_t document_revision);
46
47 std::uint64_t captured_registry_revision() const noexcept {
48 return captured_registry_revision_;
49 }
50 const void* captured_registry_generation() const noexcept {
51 return registry_generation_.get();
52 }
53 const CompileContextRegistry* registry_snapshot() const noexcept {
54 return registry_snapshot_.get();
55 }
56 const std::shared_ptr<const CompileContextRegistry>& registry_snapshot_owner() const noexcept {
57 return registry_snapshot_;
58 }
59 std::shared_ptr<const void> captured_registry_generation_owner() const noexcept {
60 return registry_generation_;
61 }
62
63 private:
64 CompileInvalidationInput(std::shared_ptr<const CompileContextRegistry> registry,
65 std::shared_ptr<const timeline::Project> snapshot,
66 std::uint64_t document_revision, timeline::DirtySet committed,
67 std::shared_ptr<const timeline::Project> predecessor_snapshot,
68 bool baseline);
71 timeline::DirtySet committed_;
72 std::shared_ptr<const timeline::Project> snapshot_;
73 std::shared_ptr<const timeline::Project> predecessor_snapshot_;
74 std::uint64_t document_revision_ = 0;
75 std::shared_ptr<const detail::ContextRegistryGeneration> registry_generation_;
76 std::shared_ptr<const CompileContextRegistry> registry_snapshot_;
77 std::uint64_t captured_registry_revision_ = 0;
78 bool baseline_ = false;
79};
80
82 static constexpr std::size_t default_maximum_note_events_per_track = 1'000'000;
83
84 std::shared_ptr<const timeline::Project> project;
86 std::shared_ptr<const timebase::CompiledTempoMap> tempo_map;
90 std::uint64_t document_revision = 0;
92 std::vector<TrackCompilePolicy> track_policies;
93 std::shared_ptr<const DecodedAudioAssetPool> audio_assets;
96 std::uint64_t max_expanded_note_events = 1'000'000u;
97 std::uint64_t max_expanded_clips = 1'000'000u;
99 // Appended to preserve positional aggregate initialization of the original
100 // request fields. Snapshot and revision must match this request.
101 std::optional<CompileInvalidationInput> invalidation;
102 // Registry used by manual/full compilation callers that do not provide a
103 // transaction-backed invalidation input. submit() captures a private
104 // immutable copy plus the original generation/revision watermark before it
105 // queues work. When invalidation is present its earlier captured snapshot
106 // is authoritative instead.
107 std::shared_ptr<const CompileContextRegistry> content_compilers;
108};
109
110enum class CompileErrorCode : std::uint8_t {
122 // A nested clip needed trimming while its MIDI content carried controller
123 // or expression lanes. Trimming has no defined answer for a lane: a point
124 // before the retained window can still be the value sounding inside it, so
125 // dropping it and carrying it are both wrong. Lowering such a clip requires
126 // the chase rule that decides which point the window inherits.
128 // A clip carried controller or expression lanes into the note program,
129 // which has no representation for them. Compiling it would emit the notes
130 // and drop every authored controller point with nothing to read the loss
131 // from, so the clip is refused instead. Distinct from the trimmed refusal
132 // above: this one is answered by a renderer that chases and emits lane
133 // values, whereas trimming still has no defined inherited value even once
134 // that renderer exists.
140 // A nested SequenceRef trims a MIDI leaf whose owner has an authored groove
141 // that does not state canonical no-feel. The flatten-first path cannot
142 // decide whether displaced events outside the retained source window should
143 // chase, clip, or disappear, so it refuses rather than leaking events.
148 // A nested child track carries a stereo balance. Flattening folds the child
149 // into the parent track, and a clip has no pan of its own, so the only
150 // panner left in the result is the parent's — which also serves every other
151 // clip on that track. Folding the child's pan into it would repan unrelated
152 // material, and dropping it would silently centre the child, so the balance
153 // is refused until a flattened leaf can carry its own placement.
155 // A composed nested gain had nowhere to land. Gain composes by multiplying
156 // into the flattened leaf's own clip gain, which only reaches a renderer
157 // for content that reads it; note, registered and opaque leaves compile to
158 // events no renderer scales by clip gain. Refuse rather than fold a child
159 // fader into a value nothing will read.
161 // A SequenceRef placement's fade had nowhere to land. The envelope itself
162 // composes: it travels beside each flattened leaf as the ramp it is, and
163 // the leaf is read at its own position within it. But a fade is a
164 // time-varying gain, so it needs the sink a static gain needs, and the leaf
165 // kinds that have none are the same ones NestedGainSinkUnsupported names.
166 // Refuse rather than play such a leaf at full level through an envelope the
167 // author wrote. Only a leaf a ramp actually reaches refuses; one lying
168 // wholly past a ramp reads unity and compiles.
170 // A nested child track is frozen. Freeze substitutes a sealed rendered
171 // artifact for everything the track would otherwise play, which is why the
172 // top-level walk returns Freeze content and discards the arrangement. The
173 // nested walk has no such substitution: it descends into the child's clips,
174 // so honouring the freeze would mean emitting the artifact in place of
175 // material the walk is already reading, and ignoring it would play the very
176 // arrangement the author froze. Refuse until a flattened leaf can carry
177 // rendered media standing in for a whole child track.
179 // A nested child track has a take lane selected. An active lane substitutes
180 // its comped takes for the arrangement, exactly as freeze substitutes an
181 // artifact, and the nested walk reads the arrangement instead. Playing the
182 // arrangement would sound the take the author deselected, so the selection
183 // is refused rather than silently inverted. Dormant lanes do not refuse:
184 // an unselected lane changes nothing at either level.
186 // A nested child track carries a device chain. A device is bound to the
187 // track it processes, and flattening dissolves the child track entirely:
188 // its clips become leaves on the parent, whose own chain already serves
189 // every other clip there. Running the child's devices over the parent
190 // would process unrelated material, and dropping them would play the
191 // child dry, so the chain is refused. Lifting it needs a sub-bus a
192 // flattened group can keep its own processing on, not a wider flatten.
194 // A nested child track automates its pan. The curve is a moving stereo
195 // balance, and flattening dissolves the child track, leaving only the
196 // parent's single pan — which also serves every other clip on that track.
197 // This is the time-varying form of what NestedMixerPanUnsupported refuses
198 // for a static balance, and it waits on strictly more than that one does:
199 // a clip needs a pan of its own before an envelope over it can mean
200 // anything.
202 // A nested child track automates its gain, over a flattened leaf that
203 // consumes no clip gain. Note, registered and opaque leaves compile to
204 // events no renderer scales by the gain of the clip that carried them, so
205 // the curve has nowhere to land for exactly the reason
206 // NestedGainSinkUnsupported gives a static product. A per-clip gain
207 // envelope would not lift this on its own: the leaf kind would still read
208 // nothing written there.
210 // A nested child track automates its gain, over a flattened leaf that does
211 // consume clip gain. Media and empty leaves read
212 // ClipPlaybackProperties::gain_linear, which is a single scalar, so writing
213 // the curve there would freeze a moving value at one point. Of the three
214 // automation refusals this is the one a per-clip gain envelope closes by
215 // itself, because the sink is the only missing part.
217 // A nested SequenceRef trims a media leaf whose content conforms to the
218 // timeline. Both conform kinds break differently under a partial view.
219 // Resample maps source to timeline by tick phase, while the nested trim
220 // path advances a raw source-frame offset from elapsed samples, so a left
221 // trim starts the clip at the wrong audio. Stretch keys its rendered
222 // artifact to the clip's own authored tick range, and a trimmed window is
223 // not that range. Refuse until the renderer owns a conform-aware
224 // source-range mapping and a windowed stretch artifact.
226 // A leaf clip inside a nested sequence is absolute-anchored while the
227 // nesting that reaches it is musical. Flattening has to place that leaf on
228 // the owner's musical timeline, but its position is defined in samples and
229 // must stay fixed as tempo moves, so the result would have to be musical
230 // and absolute at once. The lowered program has no such hybrid domain to
231 // write. Distinct from the placement guard above, which a SequenceRef clip
232 // cannot reach: this one a document can genuinely author. What it waits on
233 // is a product decision about whether nesting re-anchors such a leaf or
234 // preserves it, not only a renderer construct.
236};
237
248
250 std::uint64_t revision = 0;
251 std::uint64_t submission_epoch = 0;
252};
253
255 std::uint64_t latest_submitted_revision = 0;
256 std::uint64_t latest_published_revision = 0;
258 std::uint64_t submitted_requests = 0;
259 std::uint64_t coalesced_requests = 0;
260 std::uint64_t rejected_requests = 0;
261 bool busy = false;
262 std::uint64_t active_tracks_completed = 0;
263 bool has_error = false;
265 // Compiler-instance-scoped terminal watermark. `>= ticket.epoch` means the
266 // ticket published or was superseded; equality means that exact ticket was
267 // the instance's latest successful publication.
268 std::uint64_t latest_submitted_epoch = 0;
269 std::uint64_t latest_published_epoch = 0;
281 std::uint64_t active_tracks_recompiled = 0;
282 std::uint64_t active_tracks_reused = 0;
283
291
297};
298
299struct PlaybackProgramCompilerCore;
300
302 public:
311 PlaybackProgramStore& store, CompileExecutor& executor,
312 std::chrono::microseconds coalescing_window = std::chrono::milliseconds(10));
316
319
320 private:
321 std::shared_ptr<PlaybackProgramCompilerCore> core_;
322};
323
324} // namespace pulp::playback
The declaration side of the compile-context subscription contract.
Definition compile_context_registry.hpp:148
Definition compile_executor.hpp:23
runtime::Result< CompileTicket, CompileError > submit(ProgramCompileRequest request)
PlaybackProgramCompiler(const PlaybackProgramCompiler &)=delete
PlaybackProgramCompiler & operator=(const PlaybackProgramCompiler &)=delete
PlaybackProgramCompiler(PlaybackProgramStore &store, CompileExecutor &executor, std::chrono::microseconds coalescing_window=std::chrono::milliseconds(10))
The store and executor must outlive every task they accept.
Definition program.hpp:371
Result<T, E> — either holds a T or an E, never both.
Definition result.hpp:76
Owned canonical collection of incremental invalidations.
Definition transaction.hpp:74
Definition audio_renderer.hpp:20
RendererStatePolicy
Definition program.hpp:59
@ CarryByItemId
Definition program.hpp:59
@ InvalidRequest
Definition generated_event_source.hpp:140
std::uint64_t ProgramGeneration
Definition program_identity.hpp:10
@ StaleRevision
Definition generated_event_source.hpp:99
CompileErrorCode
Definition program_compiler.hpp:110
@ NestedFrozenTrackUnsupported
Definition program_compiler.hpp:178
@ CompilerAlreadyBound
Definition program_compiler.hpp:116
@ RegisteredContentCompileFailed
Definition program_compiler.hpp:146
@ ExpansionBudgetExceeded
Definition program_compiler.hpp:119
@ NestedAbsoluteChildUnsupported
Definition program_compiler.hpp:235
@ ExecutorUnavailable
Definition program_compiler.hpp:113
@ TrimmedGrooveUnsupported
Definition program_compiler.hpp:144
@ MidiExpressionLaneUnsupported
Definition program_compiler.hpp:135
@ NestedAutomationPanUnsupported
Definition program_compiler.hpp:201
@ TrimmedMidiLaneUnsupported
Definition program_compiler.hpp:127
@ NestedActiveTakeUnsupported
Definition program_compiler.hpp:185
@ NestedAutomationGainEventLeafUnsupported
Definition program_compiler.hpp:209
@ AudioProgramInvalid
Definition program_compiler.hpp:117
@ MidiExpressionLaneBudgetExceeded
The arrangement authors more controller values than one program may carry.
Definition program_compiler.hpp:139
@ NestedGainSinkUnsupported
Definition program_compiler.hpp:160
@ UnresolvedRegisteredContent
Definition program_compiler.hpp:145
@ NestedDeviceChainUnsupported
Definition program_compiler.hpp:193
@ NestedMixerPanUnsupported
Definition program_compiler.hpp:154
@ NestedConformedTrimUnsupported
Definition program_compiler.hpp:225
@ InvalidRequest
Definition program_compiler.hpp:111
@ GenerationExhausted
Definition program_compiler.hpp:115
@ NestedPlacementFadeUnsupported
Definition program_compiler.hpp:169
@ InvalidStructure
Definition program_compiler.hpp:114
@ RegisteredContentFragmentQuotaExceeded
Definition program_compiler.hpp:147
@ AutomationProgramInvalid
Definition program_compiler.hpp:118
@ NoteProgramCapacityExceeded
Definition program_compiler.hpp:120
@ NestedAutomationGainMediaUnsupported
Definition program_compiler.hpp:216
AudioRendererErrorCode
Definition audio_renderer.hpp:29
@ OfflineStretchFailed
Definition audio_renderer.hpp:41
@ InvalidAsset
Definition audio_renderer.hpp:33
ContentFragmentErrorCode
Definition compile_context_registry.hpp:48
@ RendererFailed
Definition compile_context_registry.hpp:49
OfflineStretchErrorCode
Definition offline_stretch_artifact.hpp:71
@ None
Definition offline_stretch_artifact.hpp:72
Definition program_compiler.hpp:18
Shared compile and RT ceilings.
Definition audio_renderer_limits.hpp:10
Preparation and callback-work ceilings shared by document compilation and track automation rendering.
Definition automation_limits.hpp:12
static constexpr AutomationPlaybackLimits platform_defaults() noexcept
Definition automation_limits.hpp:42
Definition program_compiler.hpp:238
ContentFragmentErrorCode content_fragment_detail
Definition program_compiler.hpp:244
std::uint64_t actual
Definition program_compiler.hpp:245
std::uint64_t revision
Definition program_compiler.hpp:241
CompileErrorCode code
Definition program_compiler.hpp:239
OfflineStretchErrorCode offline_stretch_detail
Definition program_compiler.hpp:243
std::uint64_t limit
Definition program_compiler.hpp:246
timeline::ItemId item
Definition program_compiler.hpp:240
AudioRendererErrorCode audio_detail
Definition program_compiler.hpp:242
const void * captured_registry_generation() const noexcept
Definition program_compiler.hpp:50
const CompileContextRegistry * registry_snapshot() const noexcept
Definition program_compiler.hpp:53
static CompileInvalidationInput baseline(std::shared_ptr< const CompileContextRegistry > registry, std::shared_ptr< const timeline::Project > snapshot, std::uint64_t document_revision)
Pin registry state without claiming a document delta.
std::uint64_t captured_registry_revision() const noexcept
Definition program_compiler.hpp:47
const std::shared_ptr< const CompileContextRegistry > & registry_snapshot_owner() const noexcept
Definition program_compiler.hpp:56
CompileInvalidationInput(std::shared_ptr< const CompileContextRegistry > registry, const timeline::CommitResult &committed)
std::shared_ptr< const void > captured_registry_generation_owner() const noexcept
Definition program_compiler.hpp:59
friend class PlaybackProgramCompiler
Definition program_compiler.hpp:69
friend struct PlaybackProgramCompilerCore
Definition program_compiler.hpp:70
Definition program_compiler.hpp:249
std::uint64_t revision
Definition program_compiler.hpp:250
std::uint64_t submission_epoch
Definition program_compiler.hpp:251
Definition program_compiler.hpp:254
std::uint64_t active_tracks_completed
Definition program_compiler.hpp:262
std::uint64_t latest_published_epoch
Definition program_compiler.hpp:269
CompileError last_error
Definition program_compiler.hpp:264
bool has_error
Definition program_compiler.hpp:263
ProgramGeneration latest_published_generation
Definition program_compiler.hpp:257
std::uint64_t submitted_requests
Definition program_compiler.hpp:258
std::uint64_t latest_published_revision
Definition program_compiler.hpp:256
std::uint64_t active_tracks_reused
Definition program_compiler.hpp:282
std::uint64_t coalesced_requests
Definition program_compiler.hpp:259
bool busy
Definition program_compiler.hpp:261
std::uint64_t latest_submitted_revision
Definition program_compiler.hpp:255
std::uint64_t rejected_requests
Definition program_compiler.hpp:260
void count_track_completed(bool reused)
Record one finished track on whichever side of the partition it fell.
Definition program_compiler.hpp:293
void clear_active_track_counts()
Clear the three track counters together, so the partition can never be left describing a previous req...
Definition program_compiler.hpp:286
std::uint64_t active_tracks_recompiled
Partition of active_tracks_completed for the request currently being compiled, or for the last one if...
Definition program_compiler.hpp:281
std::uint64_t latest_submitted_epoch
Definition program_compiler.hpp:268
Definition dirty_track_resolver.hpp:9
Definition program_compiler.hpp:81
std::vector< TrackCompilePolicy > track_policies
Definition program_compiler.hpp:92
timebase::RationalRate sample_rate
The render rate requested by the caller.
Definition program_compiler.hpp:89
std::size_t maximum_note_events_per_track
Definition program_compiler.hpp:98
static constexpr std::size_t default_maximum_note_events_per_track
Definition program_compiler.hpp:82
std::shared_ptr< const timebase::CompiledTempoMap > tempo_map
Definition program_compiler.hpp:86
std::shared_ptr< const CompileContextRegistry > content_compilers
Definition program_compiler.hpp:107
std::shared_ptr< const DecodedAudioAssetPool > audio_assets
Definition program_compiler.hpp:93
timeline::ItemId sequence_id
Definition program_compiler.hpp:85
AudioRendererLimits audio_limits
Definition program_compiler.hpp:94
std::uint64_t max_expanded_clips
Definition program_compiler.hpp:97
AutomationPlaybackLimits automation_limits
Definition program_compiler.hpp:95
std::shared_ptr< const timeline::Project > project
Definition program_compiler.hpp:84
DirtyTrackSet dirty
Definition program_compiler.hpp:91
std::optional< CompileInvalidationInput > invalidation
Definition program_compiler.hpp:101
std::uint64_t max_expanded_note_events
Definition program_compiler.hpp:96
std::uint64_t document_revision
Definition program_compiler.hpp:90
Definition program_compiler.hpp:24
RendererStatePolicy state_policy
Definition program_compiler.hpp:27
ProviderSelectorProgram provider
Definition program_compiler.hpp:26
timeline::ItemId track_id
Definition program_compiler.hpp:25
Definition rational_time.hpp:14
Immutable state published by a successful session commit.
Definition transaction.hpp:187
Nonzero monotonic identity for a Timeline-owned object.
Definition item_id.hpp:19