Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
standalone.hpp
Go to the documentation of this file.
1#pragma once
2
5#if PULP_ENABLE_AUDIO_PROBES
8#endif
9#include <pulp/format/detail/playhead_diff.hpp>
14#include <pulp/midi/device.hpp>
20
21#include <atomic>
22#include <array>
23#include <chrono>
24#include <cstddef>
25#include <cstdint>
26#include <functional>
27#include <memory>
28#include <mutex>
29#include <optional>
30#include <span>
31#include <string>
32#include <vector>
33
34namespace pulp::view {
36class CommandRegistry;
37} // namespace pulp::view
38
39namespace pulp::format {
40
41namespace detail {
42class StandaloneMusicalTyping;
43}
44
46 std::string audio_device_id;
47 std::string midi_input_id;
48 double sample_rate = 48000.0;
49 int buffer_size = 256;
52 // Optional host constraints. When non-empty, the standalone settings UI
53 // only offers these values and persisted settings outside the list are
54 // clamped back to the first allowed value.
55 std::vector<double> allowed_sample_rates;
56 std::vector<int> allowed_buffer_sizes;
57 // Capability flag for apps/instruments that do not consume audio input.
58 // The standalone host derives this from the processor descriptor at
59 // startup, and the settings UI uses it to avoid presenting an unusable
60 // input-device workflow for instrument-only apps.
62 // Effects usually want the test signal as input. Instruments have no
63 // audio input, so their test tone should exercise the selected output
64 // device directly.
66 // When true, run_with_editor() avoids showing/activating the native
67 // window. Use with screenshot_path for CI/test smoke runs.
68 bool headless = false;
69 // When false, run_with_editor() hosts the editor directly and omits the
70 // built-in Settings tab.
71 bool show_settings_tab = true;
72
73 // Remember the user's audio/MIDI device selection (+ sample rate, buffer, transport)
74 // across launches, keyed by the plugin name. On by default; a developer can set this
75 // false to always start from the configured defaults. Saved whenever settings change,
76 // restored at startup (the first launch keeps the configured defaults).
77 bool persist_settings = true;
78
79 // Development Inspector activation. Empty/"off" is inert, "local" owns
80 // only the in-window overlay, and agent profiles start an authenticated
81 // endpoint.
82 // Capability ids are only used with the "custom" profile. Kept as plain
83 // strings so public standalone headers remain independent of inspector SDK
84 // types when inspector support is compiled out.
85 std::string inspector_profile;
86 std::vector<std::string> inspector_capabilities;
87 // When non-empty, run_with_editor() installs a one-shot idle callback
88 // that captures the first painted frame via WindowHost::capture_png()
89 // and writes to this path, then closes the window. Codified in the SDK
90 // (not per-app) so every pulp::format::StandaloneApp consumer gets
91 // headless screenshot capture for free. Set via set_config() or by
92 // parsing `--screenshot=PATH` from argv in main().
93 std::string screenshot_path;
94 // Frames to wait before capture. Default 30 (~0.5s @60fps) gives the
95 // first React-driven layout + effects pass time to settle.
97
98 // A screenshot-only launch creates NO audio system and NO audio device: it
99 // paints a few frames, writes the PNG, and exits, so the render callback
100 // could only ever push silence at whoever is at the machine. Set this true
101 // (or export PULP_SCREENSHOT_KEEP_AUDIO=1) when the capture must show a UI
102 // driven by live audio — a meter or scope reading real signal. Requesting
103 // any live readout (audio_probe_json_path, audio_scope_json_path,
104 // audio_capture_wav_path, audio_capture_rolling_path) keeps audio on its
105 // own; this flag is for the case where only the pixels need it.
107
108 // Synthetic key-sequence driver for the test harness. When non-empty,
109 // run_with_editor() presses each key in turn through the host's REAL key
110 // path once the UI has settled, so a keyboard-driven behaviour can be
111 // proven on the shipping surface instead of only in a unit test. The
112 // spec is comma-separated and accepts modifiers, e.g.
113 // "down,down,cmd+a,return". Set via set_config() or PULP_TEST_KEY_SEQUENCE.
114 // An unparseable spec is rejected at startup rather than partially run.
115 std::string test_key_sequence;
116 // Frames to wait before the first key and between consecutive keys. The
117 // gap has to outlast whatever the UI does in response to a press
118 // (animation, relayout, a script tick) or the next key lands mid-update.
120 // When non-empty, capture a PNG into this directory after every key,
121 // named "key-<NN>-<name>.png", plus "key-00-initial.png" before the first
122 // press. That per-press series is what turns "the sequence ran" into
123 // evidence of what each individual press did.
124 std::string test_key_shot_dir;
125
126 // When non-empty, run_with_editor() arms the same one-shot frame-delay
127 // path as `screenshot_path` and, after the delay, writes the live output
128 // probe's latest snapshot (peak/RMS/dBFS/clip/NaN/silence counters) as a
129 // JSON object to this path, then exits. This is the programmatic readout
130 // of the live Audio Inspector for agents and CI — distinct from the
131 // offline `pulp audio validate` Doctor. Set via set_config() or by reading
132 // PULP_AUDIO_PROBE_JSON. Only meaningful when PULP_ENABLE_AUDIO_PROBES is
133 // ON; probes-off standalone builds reject this request before opening the
134 // editor so callers do not mistake an unsupported binary for silence.
136
137 // Programmatic live scope capture over the output-boundary probe's copied
138 // capture ring. Like audio_probe_json_path, this is a dev/agent readout and
139 // is only meaningful when PULP_ENABLE_AUDIO_PROBES is ON.
142 std::string audio_scope_trigger = "rising-zero";
144
145 // Programmatic live capture-to-WAV over the same output-boundary probe ring.
146 // Writes a WAV the offline `pulp audio validate` verbs can read, then the
147 // one-shot exits. Only meaningful when PULP_ENABLE_AUDIO_PROBES is ON.
148 // `audio_capture_wav_frames` is the ring window in samples (0 = as much as the
149 // ring holds, clamped to the capture cap). Captures all output channels.
152
153 // Programmatic live ROLLING capture-to-WAV. Unlike audio_capture_wav_path
154 // (which dumps the probe FIFO's EARLIEST window), this taps the output
155 // boundary into a RollingAudioCaptureBuffer and writes the LAST
156 // `audio_capture_rolling_frames` frames as a float WAV the offline `pulp
157 // audio validate` verbs can read — the steady-state window `doctor`/`compare`
158 // want, with no int16 quantization floor. The one-shot then exits. Only
159 // meaningful when PULP_ENABLE_AUDIO_PROBES is ON. Captures all output channels.
162 // Rolling capture sample format: false → float32 (default, full precision),
163 // true → int24 (≈ −144 dBFS floor, smaller file, universal DAW compatibility).
165
166 // Built-in tempo source. The standalone host has no DAW providing
167 // transport, so it acts as one: it surfaces `tempo_bpm` / time signature on
168 // every ProcessContext block, and advances `position_beats` from
169 // `position_samples` when the transport is rolling. Plugins that branch on
170 // `is_playing` or read `position_beats` therefore behave the same way in
171 // `pulp run` as they do in a host without any per-plugin glue.
172 double tempo_bpm = 120.0;
175 bool transport_playing = true; // default-on so MIDI/tempo plugins are immediately useful
177
178 // Adds Pulp's floating Musical Typing Keyboard to this standalone app.
179 //
180 // OPT-IN, AND OFF BY DEFAULT. This flag gates the ENTIRE feature, not just
181 // the keyboard's visibility: leave it false and the app registers no menu
182 // command and installs no key handler, so Cmd+K is inert and nothing
183 // appears in any menu. That combination is the tell — a broken shortcut
184 // would still leave the menu item behind, so a missing menu item means
185 // this flag, not a handler bug. Said plainly because reading it as
186 // "keyboard starts hidden" costs an afternoon debugging a key route that
187 // was never installed.
188 //
189 // Once enabled, the keyboard stays hidden until the user picks
190 // "Musical Typing Keyboard" from the application or Window menu, or presses Cmd+K
191 // (Ctrl+K off Apple). Notes enter through the standalone host's lock-free
192 // UI MIDI path, so they reach the processor only if it accepts MIDI input;
193 // on a processor that ignores MIDI the keys are silent by construction.
195
196 // Separate high-risk acknowledgement for arbitrary evaluation in the live
197 // scripted UI realm. No profile or persisted setting implies this bit.
198 // Appended to preserve every legacy positional aggregate initializer.
200
201 // Appended to preserve every legacy positional aggregate initializer.
202 // Remember the plug-in's own state (parameters plus anything it saves with
203 // serialize_plugin_state) across launches, in the same settings file as the
204 // device selection. Saved when the app quits and shortly after the state
205 // stops changing; restored at startup before the editor opens. Applies only
206 // while `persist_settings` is on, and never to a headless or screenshot
207 // launch, which must stay reproducible. A developer who wants device memory
208 // without state memory sets this false. States over 8 MiB are not saved.
210 // How long the state must stay unchanged before it is saved, in
211 // milliseconds. A knob being dragged keeps pushing the save back; one that
212 // has been changing for ten seconds is saved regardless.
214};
215
216namespace detail {
217
218inline constexpr std::size_t kStandaloneMidiInputQueueCapacity = 2048;
219inline constexpr std::size_t kStandaloneTestMidiQueueCapacity = 256;
220inline constexpr std::size_t kStandaloneTestMidiNoteCount = 16 * 128;
221inline constexpr std::size_t kStandaloneMidiBufferEventCapacity =
227
230
232 midi::MidiBuffer& out) {
233 std::size_t popped = 0;
234 std::size_t drained = 0;
235 while (popped < kStandaloneMidiInputQueueCapacity) {
236 auto event = queue.try_pop();
237 if (!event) break;
238 ++popped;
239 if (out.add(*event)) ++drained;
240 }
241 return drained;
242}
243
244enum class StandaloneTestMidiKind : std::uint8_t {
247};
248
252 std::uint8_t channel = 1;
253 std::uint8_t note = 0;
254 std::uint8_t velocity = 0;
255};
256
258 std::optional<bool> playing;
259 std::optional<std::int64_t> position_samples;
260 std::optional<double> tempo_bpm;
261};
262
264 bool playing = true;
265 double tempo_bpm = 120.0;
266 std::int64_t position_samples = 0;
267};
268
274
283 public:
284 static constexpr double kMinimumTempoBpm = 20.0;
285 static constexpr double kMaximumTempoBpm = 400.0;
286
290
294 void release_test_input() noexcept;
295
297 std::uint64_t midi_overflow_count() const noexcept;
298
303 void set_playing(bool playing) noexcept;
305 bool commanded_playing() const noexcept;
306
309 void prepare(bool playing, double tempo_bpm) noexcept;
310 std::size_t drain_midi_into(midi::MidiBuffer& out,
311 int block_size_samples) noexcept;
313 void end_audio_block(int block_size_samples) noexcept;
314
315 private:
316 struct QueuedNote {
318 std::uint64_t generation = 0;
319 };
320 struct TransportCommand {
321 bool playing = true;
322 double tempo_bpm = 120.0;
323 std::int64_t position_samples = 0;
324 std::uint64_t position_revision = 0;
325 };
326
327 bool release_active_notes(midi::MidiBuffer& out,
328 int sample_offset) noexcept;
329
331 std::atomic<std::uint64_t> midi_generation_{1};
332 std::uint64_t audio_midi_generation_ = 1;
333 std::array<std::array<bool, 128>, 16> active_notes_{};
334 bool note_release_pending_ = false;
335
336 TransportCommand control_transport_{};
337 mutable std::mutex control_transport_mutex_;
338 std::optional<TransportCommand> transport_before_control_;
339 runtime::TripleBuffer<TransportCommand> transport_commands_{control_transport_};
340 StandaloneTestTransportState audio_transport_{};
341 runtime::SeqLock<StandaloneTestTransportState> observed_transport_{audio_transport_};
342 std::uint64_t audio_position_revision_ = 0;
343 bool prepared_ = false;
344};
345
346} // namespace detail
347
349public:
352
353 void set_config(const StandaloneConfig& config) { config_ = config; }
354 const StandaloneConfig& config() const { return config_; }
355
356 bool start();
357 void stop();
358 bool is_running() const { return running_.load(); }
359 std::uint64_t audio_xrun_count() const {
360 return audio_device_ ? audio_device_->xrun_count() : 0;
361 }
362 bool run_with_editor(bool use_gpu = false);
363
377 std::function<void(const std::vector<std::string>&)> handler) {
378 open_files_handler_ = std::move(handler);
379 }
380
382 bool apply_config(const StandaloneConfig& new_config);
383
384 Processor* processor() { return processor_.get(); }
385 state::StateStore& state() { return store_; }
386
391 bool audio_skipped_for_capture() const { return audio_skipped_for_capture_; }
392
393 TestSignalSource& test_signal() { return test_signal_; }
394 view::AudioBridge& input_meter_bridge() { return input_meter_bridge_; }
395 view::AudioBridge& output_meter_bridge() { return output_meter_bridge_; }
396
397#if PULP_ENABLE_AUDIO_PROBES
405 audio::AudioProbe& output_probe() { return output_probe_; }
406#endif
407 audio::AudioSystem* audio_system() { return audio_system_.get(); }
408 midi::MidiSystem* midi_system() { return midi_system_.get(); }
409
410 // UI-thread MIDI injection. Virtual-keyboard widgets, scripting
411 // hooks, and test harnesses push MIDI events through this collector;
412 // the audio callback drains them into each block's MidiBuffer at the
413 // correct sample offsets. Identical thread-safety surface that
414 // pulp::midi::MidiMessageCollector documents — push_now is non-blocking.
415 midi::MidiMessageCollector<>& ui_midi_collector() { return ui_midi_collector_; }
416 detail::StandaloneTestInputHost& test_input_host() { return test_input_host_; }
418 return test_input_host_;
419 }
420
421 // Persist + restore StandaloneConfig under
422 // ApplicationProperties so `pulp run` opens the user's last device,
423 // sample-rate, buffer-size, MIDI input, and built-in transport
424 // settings on the next launch. Returns false when the storage layer
425 // failed to write (e.g. read-only profile, missing app name).
426 // Overlays any persisted keys onto `base` and returns it — so unsaved fields keep the
427 // caller's defaults (the first launch, with no file, returns `base` unchanged).
428 static StandaloneConfig load_persisted_config(std::string_view app_name,
429 StandaloneConfig base = {});
430 static bool save_persisted_config(std::string_view app_name,
431 const StandaloneConfig& config);
432
433 // The plug-in state blob kept next to the persisted device selection (the
434 // host-facing blob plugin_state_io produces, base64 in the settings file).
435 // load returns nullopt when nothing is saved or the stored text is not valid
436 // base64; save returns false for an empty app name, a blob over
437 // kMaxPersistedStateBytes, or a settings location that cannot be written.
438 static constexpr std::size_t kMaxPersistedStateBytes = 8u * 1024u * 1024u;
439 static std::optional<std::vector<uint8_t>> load_persisted_state(std::string_view app_name);
440 static bool save_persisted_state(std::string_view app_name, std::span<const uint8_t> blob);
441
447 std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now());
448
452
455 bool plugin_state_persistence_active() const noexcept {
456 return state_persist_.active;
457 }
458
461 std::uint32_t plugin_state_save_count() const noexcept {
462 return state_persist_.saves;
463 }
464
465 private:
466 // run_with_editor() for a processor with an editor factory: the editor-host
467 // contract editor in its own native window (detail/standalone_contract_editor.hpp).
468 bool run_contract_editor(EditorFactory& factory);
469
470 // The view layer. standalone_view_layer.cpp defines these for
471 // pulp::standalone and pulp::standalone-native; standalone_view_layer_none.cpp
472 // defines them for pulp::standalone-contract, the standalone of a UI package
473 // plug-in, which links no Pulp view, canvas or render code (its editor is
474 // the package's, opened through the editor-host contract).
475 void attach_view_layer();
476 void prepare_view_input(const StandaloneConfig& config);
477 // Audio thread: append the musical typing keyboard's recovery events.
478 void drain_view_input(midi::MidiBuffer& midi, int frames) noexcept;
479 void shutdown_view_input() noexcept;
480 int view_waveform_capacity() const noexcept;
481 // run_with_editor() for a processor without an editor factory: its
482 // create_view() editor in a Pulp window.
483 bool run_view_editor(bool use_gpu, const StandaloneConfig& effective_config);
484 std::function<void(const std::vector<std::string>&)> open_files_handler_;
485
486 // Declared FIRST so it is destroyed LAST: the final detach cancels and
487 // joins the tracing auto-flush timer and writes the .pftrace, which must
488 // happen after every span this app can still emit. Tracing used to be
489 // wired into VST3 ONLY, so a Perfetto capture of a standalone run
490 // recorded nothing. No-op unless PULP_TRACING=ON.
491 runtime::ScopedTracingAttachment tracing_;
492
493 // Tear down the audio + MIDI devices but KEEP the processor instance alive.
494 // apply_config() uses this so a settings change does not recreate the
495 // Processor out from under an editor ViewBridge holding a Processor&.
496 void stop_audio_keep_processor();
497
498 // Device-independent render-state preparation: sizes the processor and every
499 // audio-thread scratch buffer / probe to `max_callback_block_`. Factored out
500 // of start() (which calls it after the device refreshes config_) so a test
501 // can prepare a StandaloneApp for a render without opening real hardware.
502 void prepare_render_state();
503
504 // The body of the audio device callback, hoisted verbatim out of the start()
505 // lambda so it is reachable off the device thread. Drives one block: MIDI
506 // drain, transport derivation, and the ScopedNoAlloc-guarded
507 // Processor::process(). RT-safe once prepare_render_state() has run.
508 void render_audio_block(const audio::BufferView<const float>& input,
509 audio::BufferView<float>& output,
510 const audio::CallbackContext& ctx);
511
512 // Test-only accessor (defined in test/test_standalone_rt.cpp) that drives
513 // prepare_render_state() + render_audio_block() headlessly to assert the
514 // render path is allocation/lock-free. Mirrors the @internal hook precedent.
516
517 // Test-only accessor (defined in test/test_standalone_capture_audio.cpp)
518 // that injects `audio_system_factory_` so the device lifecycle can be
519 // asserted without opening real hardware.
521
522 ProcessorFactory factory_;
523 // The store is declared before the Processor so it is destroyed after it.
524 // `Processor::state()` dereferences a pointer to this store, and a Processor
525 // may read it from its destructor or from a worker thread that destructor is
526 // about to join. Reversing these two lines hands that thread a freed store.
527 state::StateStore store_;
528 std::unique_ptr<Processor> processor_;
529 // Panic requests (Processor::request_panic), serviced before each block.
530 std::shared_ptr<PanicState> panic_state_;
531 // Present in every StandaloneApp, but populated only by an executable that
532 // explicitly links and registers a canonical control host adapter.
533 std::unique_ptr<StandaloneControlHost> control_host_;
534 StandaloneConfig config_;
535 bool persisted_config_loaded_ = false; // overlay persisted settings once, not on soft restarts
536
537 // Plug-in state persistence. Main thread only.
538 struct StatePersistence {
539 bool active = false;
540 bool pending = false; // a change is waiting out its debounce
541 std::uint32_t saves = 0; // times the settings file was written
542 std::uint64_t saved_fingerprint = 0; // what the settings file holds
543 std::uint64_t observed_fingerprint = 0;
544 std::chrono::steady_clock::time_point first_change{};
545 std::chrono::steady_clock::time_point last_change{};
546 std::chrono::steady_clock::time_point last_poll{};
547 } state_persist_;
548 void restore_persisted_state();
549 std::uint64_t state_fingerprint() const;
550
555 detail::PlayheadSnapshot playhead_prev_{};
556
557 std::unique_ptr<audio::AudioSystem> audio_system_;
558 std::unique_ptr<audio::AudioDevice> audio_device_;
559 // Set by start(): this launch is a screenshot-only capture, so it created
560 // no audio system and no device. Reported by audio_skipped_for_capture().
561 bool audio_skipped_for_capture_ = false;
562 // Test seam: when set, start() builds the audio system from this factory
563 // instead of audio::create_audio_system(). Never set in shipped code.
564 std::function<std::unique_ptr<audio::AudioSystem>()> audio_system_factory_;
565 std::unique_ptr<midi::MidiSystem> midi_system_;
566 std::unique_ptr<midi::MidiInput> midi_input_;
567
568 detail::StandaloneMidiInputQueue hardware_midi_queue_;
569 midi::MidiBuffer midi_in_;
570 midi::MidiBuffer midi_out_;
571 midi::MidiMessageCollector<> ui_midi_collector_;
572 detail::StandaloneTestInputHost test_input_host_;
573 std::atomic<bool> running_{false};
574
575 TestSignalSource test_signal_;
576 view::AudioBridge input_meter_bridge_;
577 view::AudioBridge output_meter_bridge_;
578 // View-layer objects, owned through a deleter the view layer supplies, so
579 // a StandaloneApp built without a view layer (pulp::standalone-contract)
580 // never names their destructors. Null there.
581 template <typename T> using ViewLayerPtr = std::unique_ptr<T, void (*)(T*)>;
582 ViewLayerPtr<view::CommandRegistry> command_registry_{nullptr, nullptr};
583 ViewLayerPtr<detail::StandaloneMusicalTyping> musical_typing_{nullptr, nullptr};
584#if PULP_ENABLE_AUDIO_PROBES
585 // Realtime output-boundary probe. prepare()d in start() with the
586 // device's channel/buffer/rate; analyze_output() is called from the audio
587 // callback right after processor render. RT-safe (scalar-only).
588 audio::AudioProbe output_probe_;
589 // Pre-allocated channel-pointer array for the probe view (no audio-thread
590 // allocation). Sized in start() to the output channel count.
591 std::vector<const float*> output_probe_ptrs_;
592
593 // Rolling last-N output capture, separate from the probe FIFO. prepare()d in
594 // start() only when audio_capture_rolling_path is set; the audio callback
595 // append()s each output block (RT-safe), and the one-shot materializes the
596 // last window to a float WAV. `rolling_capture_active_` gates the
597 // audio-thread append so there is zero added work when the flag is off.
598 audio::RollingAudioCaptureBuffer rolling_capture_;
599 bool rolling_capture_active_ = false;
600 // Channel count the audio callback actually delivers (set on the audio
601 // thread). The rolling ring is prepared to the configured output_channels;
602 // if the device under-delivers, the writer trims the WAV to this so it has
603 // no phantom silent channels. Relaxed: a single int published once per run.
604 std::atomic<int> rolling_capture_channels_{0};
605
606 // Developer Audio Inspector tool window. A separate floating
607 // window that reads `output_probe_.latest()` each UI tick and renders the
608 // live meters / waveform / probe-stage status. Lives on the app so it
609 // outlives the idle callback that polls it; `output_probe_` is declared
610 // before it so the probe outlives the window on teardown (the window holds
611 // a raw probe pointer). Constructed in run_with_editor() only when a real
612 // window exists, behind a shared CommandRegistry routed by
613 // `route_global_keys` (Cmd/Ctrl+Shift+A). Opened when PULP_AUDIO_INSPECTOR
614 // is set in the environment.
615 ViewLayerPtr<view::AudioInspectorWindow> audio_inspector_{nullptr, nullptr};
616#endif
617 // The MAXIMUM frames the audio callback may deliver in one block. This is
618 // NOT the nominal buffer_size: when the device runs at a different sample
619 // rate than the app, CoreAudio (and other backends) insert a resampler that
620 // pulls the render callback in variable, larger-than-nominal blocks. The
621 // processor and every scratch buffer are sized to THIS, and the callback
622 // guards against any block beyond it, so an oversized pull can never trip
623 // Processor::process()'s `num_samples <= max_block` assert or overflow the
624 // pre-allocated buffers.
625 int max_callback_block_ = 0;
626 audio::Buffer<float> test_buffer_; // Pre-allocated for audio callback
627 audio::Buffer<float> silence_buffer_; // Pre-allocated silence for missing input
628 std::vector<float*> test_ptrs_; // Pre-allocated channel pointers
629 std::vector<float*> direct_output_ptrs_; // Pre-allocated for output test signal
630 std::vector<const float*> silence_ptrs_; // Pre-allocated silence channel pointers
631 std::vector<const float*> meter_ptrs_; // Pre-allocated for meter analysis
632 std::vector<const float*> output_meter_ptrs_;
633};
634
635} // namespace pulp::format
Realtime-safe audio output probe.
Realtime output-boundary probe.
Definition audio_probe.hpp:53
Definition device.hpp:149
Definition audio_bridge.hpp:122
Builds a processor's editors.
Definition editor_host.hpp:576
Lock-free panic request and output-note state for one Processor.
Definition panic.hpp:73
Definition processor.hpp:174
void set_config(const StandaloneConfig &config)
Definition standalone.hpp:353
bool save_plugin_state_now()
Main thread only.
bool plugin_state_persistence_active() const noexcept
True when this run restores and saves the plug-in state (see StandaloneConfig::persist_plugin_state).
Definition standalone.hpp:455
void set_open_files_handler(std::function< void(const std::vector< std::string > &)> handler)
Install a handler for files the OS asks this app to open — a Finder double-click, a drag onto the app...
Definition standalone.hpp:376
Processor * processor()
Definition standalone.hpp:384
const StandaloneConfig & config() const
Definition standalone.hpp:354
static std::optional< std::vector< uint8_t > > load_persisted_state(std::string_view app_name)
state::StateStore & state()
Definition standalone.hpp:385
void service_state_persistence(std::chrono::steady_clock::time_point now=std::chrono::steady_clock::now())
Main thread only.
bool run_with_editor(bool use_gpu=false)
bool is_running() const
Definition standalone.hpp:358
std::uint32_t plugin_state_save_count() const noexcept
How many times this run has written the plug-in state to the settings file.
Definition standalone.hpp:461
bool audio_skipped_for_capture() const
True when this launch skipped the audio backend entirely because it is a screenshot-only capture (see...
Definition standalone.hpp:391
view::AudioBridge & input_meter_bridge()
Definition standalone.hpp:394
StandaloneApp(ProcessorFactory factory)
static constexpr std::size_t kMaxPersistedStateBytes
Definition standalone.hpp:438
bool apply_config(const StandaloneConfig &new_config)
Restart audio with a new config (stop → reconfigure → start).
TestSignalSource & test_signal()
Definition standalone.hpp:393
const detail::StandaloneTestInputHost & test_input_host() const
Definition standalone.hpp:417
detail::StandaloneTestInputHost & test_input_host()
Definition standalone.hpp:416
audio::AudioSystem * audio_system()
Definition standalone.hpp:407
static StandaloneConfig load_persisted_config(std::string_view app_name, StandaloneConfig base={})
static bool save_persisted_state(std::string_view app_name, std::span< const uint8_t > blob)
friend struct StandaloneAudioDeviceTestAccess
Definition standalone.hpp:520
view::AudioBridge & output_meter_bridge()
Definition standalone.hpp:395
static bool save_persisted_config(std::string_view app_name, const StandaloneConfig &config)
midi::MidiMessageCollector & ui_midi_collector()
Definition standalone.hpp:415
friend struct StandaloneRenderTestAccess
Definition standalone.hpp:515
std::uint64_t audio_xrun_count() const
Definition standalone.hpp:359
midi::MidiSystem * midi_system()
Definition standalone.hpp:408
Optional host-side control composition for one ordinary StandaloneApp.
Definition standalone_control_host.hpp:36
Generates test audio for the standalone host: sine tone or file playback.
Definition test_signal.hpp:24
Bounded control-thread to audio-thread bridge for standalone inspector test input.
Definition standalone.hpp:282
void prepare(bool playing, double tempo_bpm) noexcept
void end_audio_block(int block_size_samples) noexcept
void release_test_input() noexcept
Invalidate all queued input from the current controller and restore the transport snapshot from befor...
StandaloneTestTransportState begin_audio_block() noexcept
static constexpr double kMaximumTempoBpm
Definition standalone.hpp:285
std::uint64_t midi_overflow_count() const noexcept
std::size_t drain_midi_into(midi::MidiBuffer &out, int block_size_samples) noexcept
static constexpr double kMinimumTempoBpm
Definition standalone.hpp:284
StandaloneTestInputResult inject_note(StandaloneTestMidiNote note)
bool commanded_playing() const noexcept
The playing state last commanded, whether or not audio has run since.
StandaloneTestInputResult update_transport(const StandaloneTestTransportUpdate &update)
StandaloneTestTransportState transport_snapshot() const noexcept
void set_playing(bool playing) noexcept
The standalone's own play/stop (Space in its window): the playing state the next audio block takes.
Collection of timestamped MIDI events within a single audio buffer period.
Definition buffer.hpp:31
bool add(const MidiEvent &event)
Append a MIDI event to the buffer.
Definition buffer.hpp:98
Definition message_collector.hpp:61
static constexpr std::size_t pending_capacity()
Compile-time size of the consumer-owned pending ring.
Definition message_collector.hpp:214
static constexpr std::size_t capacity()
Compile-time capacity of the producer queue.
Definition message_collector.hpp:205
Definition device.hpp:52
Definition spsc_queue.hpp:23
bool try_pop(T &item)
Definition spsc_queue.hpp:45
Centralized parameter storage with lock-free value access.
Definition store.hpp:164
Floating Audio Inspector window.
Definition audio_inspector_window.hpp:46
Central command dispatcher.
Definition command_registry.hpp:207
SPSC MIDI accumulator for UI-thread → audio-thread MIDI delivery.
Definition analyzer_provider.hpp:12
The plugin processor interface.
Definition device_quotas.hpp:67
constexpr std::size_t kStandaloneMidiInputQueueCapacity
Definition standalone.hpp:218
runtime::SpscQueue< midi::MidiEvent, kStandaloneMidiInputQueueCapacity > StandaloneMidiInputQueue
Definition standalone.hpp:228
constexpr std::size_t kStandaloneMidiBufferEventCapacity
Definition standalone.hpp:221
constexpr std::size_t kStandaloneTestMidiQueueCapacity
Definition standalone.hpp:219
StandaloneTestInputResult
Definition standalone.hpp:269
@ InvalidArgument
Definition standalone.hpp:271
@ Applied
Definition standalone.hpp:270
@ QueueFull
Definition standalone.hpp:272
constexpr std::size_t kStandaloneTestMidiNoteCount
Definition standalone.hpp:220
std::size_t drain_standalone_midi_input(StandaloneMidiInputQueue &queue, midi::MidiBuffer &out)
Definition standalone.hpp:231
StandaloneTestMidiKind
Definition standalone.hpp:244
@ NoteOn
Definition standalone.hpp:245
@ NoteOff
Definition standalone.hpp:246
What the product grants a device: the quota table indexed by capability tier and live thermal state.
Definition aax_editor.hpp:31
@ vector
a block of samples for a scope-style display
Definition editor_host.hpp:237
std::unique_ptr< Processor >(*)() ProcessorFactory
Factory function type — plugins provide this to create processor instances.
Definition processor.hpp:1416
Definition custom_node_events.hpp:26
Definition generated_event_source.hpp:33
Hover-cursor tracking for platform window hosts.
Definition fwd.hpp:27
STL namespace.
Lock-free sequence-lock for coherent multi-field snapshots.
Definition standalone.hpp:45
std::string audio_capture_wav_path
Definition standalone.hpp:150
std::string audio_scope_trigger
Definition standalone.hpp:142
std::string audio_device_id
Definition standalone.hpp:46
bool audio_capture_rolling_int24
Definition standalone.hpp:164
bool persist_settings
Definition standalone.hpp:77
bool headless
Definition standalone.hpp:68
int audio_capture_wav_frames
Definition standalone.hpp:151
int audio_capture_rolling_frames
Definition standalone.hpp:161
bool screenshot_keeps_audio
Definition standalone.hpp:106
bool show_settings_tab
Definition standalone.hpp:71
int buffer_size
Definition standalone.hpp:49
int audio_scope_window_samples
Definition standalone.hpp:141
bool transport_recording
Definition standalone.hpp:176
bool persist_plugin_state
Definition standalone.hpp:209
int state_save_debounce_ms
Definition standalone.hpp:213
bool supports_audio_input
Definition standalone.hpp:61
std::string test_key_shot_dir
Definition standalone.hpp:124
int time_sig_denominator
Definition standalone.hpp:174
std::string test_key_sequence
Definition standalone.hpp:115
std::string audio_scope_json_path
Definition standalone.hpp:140
int input_channels
Definition standalone.hpp:51
double tempo_bpm
Definition standalone.hpp:172
std::string midi_input_id
Definition standalone.hpp:47
bool transport_playing
Definition standalone.hpp:175
std::vector< std::string > inspector_capabilities
Definition standalone.hpp:86
std::string audio_probe_json_path
Definition standalone.hpp:135
int output_channels
Definition standalone.hpp:50
int test_key_frame_delay
Definition standalone.hpp:119
int time_sig_numerator
Definition standalone.hpp:173
std::vector< int > allowed_buffer_sizes
Definition standalone.hpp:56
int audio_scope_channel
Definition standalone.hpp:143
bool enable_musical_typing_keyboard
Definition standalone.hpp:194
std::string screenshot_path
Definition standalone.hpp:93
std::string audio_capture_rolling_path
Definition standalone.hpp:160
bool route_test_signal_to_output
Definition standalone.hpp:65
double sample_rate
Definition standalone.hpp:48
std::vector< double > allowed_sample_rates
Definition standalone.hpp:55
bool inspector_runtime_eval
Definition standalone.hpp:199
int screenshot_frame_delay
Definition standalone.hpp:96
std::string inspector_profile
Definition standalone.hpp:85
std::uint8_t velocity
Definition standalone.hpp:254
StandaloneTestMidiKind kind
Definition standalone.hpp:250
std::uint8_t note
Definition standalone.hpp:253
std::uint8_t channel
Protocol-facing MIDI channel number (1..16).
Definition standalone.hpp:252
double tempo_bpm
Definition standalone.hpp:265
bool playing
Definition standalone.hpp:264
std::int64_t position_samples
Definition standalone.hpp:266
std::optional< bool > playing
Definition standalone.hpp:258
std::optional< double > tempo_bpm
Definition standalone.hpp:260
std::optional< std::int64_t > position_samples
Definition standalone.hpp:259
Lock-free latest-value publication between two threads.
Source-compatible spelling of <pulp/dataplane/audio_bridge.hpp>.