aax
無料Optional AAX support for Pulp, including developer-supplied Avid SDK setup, CMake enablement, DigiShell/AAX Validator workflows, and local AAX builds on macOS or Windows.
日本語の概要は準備中です。原文の説明を表示しています。
Real-DAW (REAPER) functional smoke for reload/editor/format-adapter changes — opt-in, scoped, headless-safe, zero-pollution
インストールする前に、エージェントに与えられる指示の中身を確認できます。
Use this when you need proof that a reload / editor / format-adapter behavior
actually works inside a host, beyond what auval / pluginval /
clap-validator give (those prove scan+load, not functional behavior).
Harness: tools/testing/daw-smoke/reaper_smoke.py (+ insert_and_float.lua and
sequence_loop_seek.lua). The scraper + mode dispatch are unit-tested with no
REAPER in tools/testing/daw-smoke/test_reaper_smoke.py.
Full rules: docs/guides/daw-smoke.md. CLAUDE.md has the one-paragraph policy.
--mode, default reload)reload — the original flow. Seed watched DSP variant A, insert+float the
FX, copy variant B over the watched path, scrape swapped DSP / reload rejected.live-plugin-swap — a live plugin-INSTANCE swap inside a Pulp host that
hosts another plugin in a SignalGraph. Insert+float the host FX, write a swap
request to --watched-swap-request, then scrape [live-swap] committed (PASS)
vs live plugin swap refused (FAIL). The success marker is NOT logged by the
core swap path — SignalGraph::prepare_swap logs ONLY refusals. The host plugin
must emit [live-swap] committed itself from its
NodeLiveSwapPolicy::on_instance_swapped observer (that is the seam the smoke
scrapes and the headless test exercises). The DAW-free, CI-runnable mirror is
test/test_signal_graph_live_swap_continuity.cpp, which drives the same
stage + prepare_swap commit through process() and asserts sample continuity
(no dropout/xrun) across the swap block for every hosted format (VST3/AU/CLAP/
LV2). Use the REAPER mode as the local-only in-host confirmation.sequence-loop-seek — set a loop region on the REAPER timeline, play, and
perform scripted seeks (into/out of the loop and across the wrap) against a
plugin that embeds a sequence. sequence_loop_seek.lua drives the transport
via a deferred pump (GetSet_LoopTimeRange + GetSetRepeat + OnPlayButton +
SetEditCurPos seeks), handshaking FX_SHOWN→SEEKS_DONE through the status
file. The plugin emits per-block markers [seq-loop] blk host_qn=.. seq_qn=.. active=.. jump=.. dropout=..; the scraper (analyze_seq_loop_log) asserts the
sequence read position TRACKED the host playhead within --pos-tolerance-qn on
every block — a free-running counter that ignores the host jump is caught as
drift → FAIL. PASS requires >=1 wrap AND >=1 seek, note activity, and no
dropout; otherwise INCONCLUSIVE (a SKIP/INCONCLUSIVE is never a PASS). This is
the harness half of Phase-2 DoD Proof #2 — the full proof also needs the
embedded-sequence plugin (Gate-5/6) and a real REAPER run. The scraper is
unit-tested with synthetic logs (incl. a negative drift/dropout case), so the
parse logic is proven without REAPER.editor-open — insert the plugin, open its editor, confirm it rendered. For a
product whose editor is the product, neither a hot-swap nor a transport scenario
applies; the only question is whether it loads in a real host and draws. This is
precisely the check the format validators cannot make: auval and clap-validator
prove a plugin scans and instantiates, never that its window comes up. After
FX_SHOWN it waits before tearing down, because an editor that opens and then
throws reports itself in the seconds after the window appears.editor-build — generate a patch from inside the host. editor-open proves
the window comes up; it does not prove the product behind it works. The generator is
spawned by the plugin, and an editor that draws perfectly can still fail to reach it
— the standalone once did exactly that, because an app launched from Finder inherits
no PATH. Nothing short of triggering a real build inside the host tests that path.
The trigger is a file the shell reads (FORGE_MODULAR_TEST_PROMPT), not
synthetic clicks — see the gotcha below. The verdict is the generator's own success
line plus the file it names, scraped from the plugin's log; never "the click
appeared to land."keyboard-routing — press keys AT a focused plugin editor and ask the HOST
what it received. Keyboard routing has two halves, and only one of them can be
tested from inside the plugin: that the editor consumes its own shortcuts. The
half that breaks is the other one — an editor that also swallows the keys it
did NOT handle looks identical from every vantage point in the plugin, and the
first report is a musician saying the spacebar stopped working. keyboard_routing.lua
journals GetPlayState + Undo_CanUndo2 to PULP_DAW_SMOKE_JOURNAL on a
defer pump, so the verdict is the HOST's own state: Space at a focused editor
must start the transport and a second Space must stop it, and a ⌘Z the editor
owns must NOT move REAPER's undo history. Refuses to post a synthetic key until
_reaper_editor_is_front proves REAPER is the frontmost process AND its front
window is the plugin editor — a global key event posted at a guess lands in
somebody else's terminal on a shared machine. A macOS Accessibility refusal is
a SKIP with that reason, never a PASS. Its decision logic is unit-tested
against a synthetic journal (including the swallowed-Space FAIL), so the
scoring is proven without REAPER. The DAW-free mirrors are
test/test_plugin_key_routing.cpp (the shared policy) and the
[host-forward] case in test_plugin_view_host_key_focus.mm (the real
PulpPluginView seam, counting what reaches the host view).ReaperSession): fresh portable
dir, temp scan path, pre-warm scan, scripted insert+float, guaranteed teardown.
They differ only in what they SEED, the TRIGGER they fire once the FX is shown,
and how they VERIFY the captured log. Add a mode by writing seed/trigger/verify
around ReaperSession, not by duplicating the launch plumbing.render_to_png capture PASSES but you're not sure it
works in a host. It usually doesn't tell the whole story — see the gotcha below.~/.config/pulp/daw-smoke.toml; a
ship gate only when enabled + gate + the diff hits the allowlist. NOT every
build. DAW-Smoke: skip reason="..." trailer bypasses a single commit.ditto, never shutil.copytreeA macOS code signature lives partly in extended attributes, and copytree does
not carry them. The copy the harness handed REAPER verified as "code object is
not signed at all" while the original was clean under --deep --strict. Use
ditto, and verify the COPY rather than the source:
ditto "$SRC" "$DST" && codesign --verify --deep --strict "$DST"
(The failure named a subcomponent — a non-executable .json in
Contents/MacOS/, where bundle convention expects only executables. Worth
knowing when reading such an error; --deep --strict treats everything in
MacOS/ as a subcomponent.)
editor-open (and any mode needing the FX inserted) currently reports
INCONCLUSIVE on REAPER 7.78 with:
pre-warm scan did not publish the target into REAPER's plugin cache before timeout
REAPER did not open the FX within timeout (flaky launch) — INCONCLUSIVE
Reproduced four times at 90s, 150s and 240s timeouts, before AND after the
ditto fix, and with the plugin also present at the standard VST3 path — the
harness redirects REAPER to its temp scan_dir regardless, so installing
normally does not change the outcome. The user's own REAPER cache DOES contain
these plugins, so REAPER scans them fine through its ordinary paths; it is the
harness's vstpath_arm64=<temp> redirection that is not taking effect on 7.78.
So a SKIP or INCONCLUSIVE here is not a PASS, and right now it is the expected result rather than evidence about the plugin. Note these modes also exit 0 while reporting INCONCLUSIVE, so anything reading exit codes scores them as passes. Until the scan-path redirection is fixed, prove editors another way and say plainly that this rung is unproven.
parameter contract differs) while every headless capture passed. Only REAPER surfaced it (fixed
4a6e048f4: the contract gate now excludes an adapter-synthesized bypass). If you
change reload/editor/adapter code, a real-DAW smoke is the check that catches this.screencapture returns BLACK frames in an agent/SSH context (no Screen-
Recording TCC grant). Do NOT verify visually via screencapture. Verify by scraping
the plugin reload log from REAPER's captured stdout (swapped DSP = applied,
reload rejected + reject-diff = fail, loaded initial logic = it loaded). Use
render_to_png (e.g. examples/hot-reload-morph capture) for human-viewable visuals.STATUS: None/
no-FX-load run is INCONCLUSIVE, not PASS.pkill -9 -x REAPER to clear
"stragglers" kills every REAPER on the machine — including the one the person at
the keyboard is working in, with their unsaved project. It was reported as "REAPER
keeps restarting"; it was being killed and relaunched underneath them. Keep the
owned child as a Popen object and stop it through poll / terminate / bounded
wait / kill / wait; never send a later raw signal to its saved numeric PID,
which may have been recycled after the child exited. PULP_DAW_SMOKE_KILL_ALL=1
restores the blunt behaviour for a dedicated box with nobody at it.reaper.ini is what keeps a run from polluting somebody's install, but an
empty one makes REAPER come up as a fresh install on every launch: licence prompt,
first-run preferences, audio-hardware setup dialog, over and over. Copy the real
config and licence in, override only the plugin-scan paths, and never write back.
Two specifics that bite:
lastproject=/loadlastproj=0. Seeding carries lastproject, so the
"isolated" REAPER opens the user's actual session — and if that session references
a plugin the smoke did not install, REAPER sits on a modal Project Load Warning
forever. The scripted insert never runs and the smoke reports INCONCLUSIVE naming
neither the dialog nor the project, while a stranger's work is on screen.[Recent]. It is not needed to run a smoke, and copying it puts the
user's project filenames into a temp dir and into REAPER's menus for the run.auval -a lists nothing, REAPER's scan finds nothing, and the run ends
INCONCLUSIVE — which reads as flakiness and sends the next person hunting a scan bug
that does not exist. Detect SSH_CONNECTION/SSH_TTY and SKIP with that reason.
VST3 and CLAP are unaffected: they are scanned from a directory.~/Library/Audio/Plug-Ins/Components/<name> exists makes the AU leg unrunnable
on every machine anyone would actually test on. If the installed component resolves
to the same path as the one under test, use it in place and leave the "we installed
it" flag unset so teardown does not uninstall something this run did not install.
Only a different component at that path is a real collision.TrackFX_AddByName with a bare name lets REAPER pick whichever format it finds
first, so --format clap inserted the installed AU and the run reported a CLAP
pass for an AU — caught only by a crash report naming the .au bundle during a clap
run. Prefix the name with VST3: / CLAP: / AU:. Three legs that all load the
same plugin are one leg wearing three hats.editor-build proves — that the generator runs when the plugin spawns
it, inheriting the host's environment — is just as true asked through a file as
asked through a button, so editor-build asks through a file and touches nothing.
If you are about to add screen-driving to a mode, reach for a file handshake first.
The two traps below are why, and they still apply to any mode that genuinely must
drive the UI:
screencaptureui, not "Screenshot". screencapture leaves the capture
UI running with a floating window that then sits over the plugin editor, so a run
that failed and grabbed a screenshot arms the obstruction that blocks the next run.
Quitting the Screenshot app does not touch it — pkill -x screencaptureui does.reaper.ini vstpath/clap_path), never ~/Library/Audio/Plug-Ins. AU can't be
scanned from a custom path, so --format au installs to the real Components folder
and MUST uninstall on exit. Cleanup ALWAYS runs (finally): kill REAPER, rm temp
dirs, uninstall AU.--format au is the only lane that
tests anything. Measured 2026-07-30 on REAPER/macOS arm64: REAPER ignores the
vstpath/clap_path keys written into the portable reaper.ini. It scanned the
real plugin folders instead (124 CLAP + 104 VST3 entries in the portable caches)
and never looked at the staged copy, so the bundle under test is absent from the
scan cache and the FX is never found. Because a not-found FX is INCONCLUSIVE rather
than FAIL, this degrades silently: the run reports "flaky launch" forever while
testing nothing. Confirm a CLAP/VST3 run really scanned your plugin by grepping the
portable reaper-clap-*.ini / reaper-vstplugins_*.ini for its name before
believing any verdict. Until the key names are fixed, use --format au.tools/scripts/gpu_dpr_runner.py; the DAW
adapter must emit the exact format, binary, scan-cache, raw-sample, artifact,
and machine receipts. Metrics must retain measured/derived/unavailable
provenance, and the input expectation must come from the frozen scenario—not
from the DAW producer that reports the observed event/hit. Two same-content
image artifacts and numeric fidelity oracles replace producer-authored pass
booleans. Timeout is INCONCLUSIVE and durable for resumption.
In v2, the runner snapshots the AUv2/VST3/CLAP product executable and all
eight cell artifacts before acceptance, binds the actual adapter producer PID
plus product/fresh-process IDs, and rejects reused paths/inodes. A DAW receipt
outside the runner root, a script presented as the product, or an identity
document that merely claims the expected host/format cannot complete a cell.
Finalization accepts only the runner directory and fixed terminal A2T/A3
receipts; follow docs/validation/gpu-dpr/README.md.~/.config/pulp/secrets/reaper-license.txt,
personal non-commercial) — never commit, echo, or bake into a TartCI golden. Local
Macs (m1/m3/m5) are already serialized; local is the primary lane.For a product declaring gpu.health.read, bind the response to the exact DAW
instance/publication and preserve the returned snapshot. A valid response is
not automatically a passing startup trial. A visible DAW campaign must declare
native-compositor-presentation and bind an independent compositor timestamp;
capture completion cannot substitute. Missing source/shader or stage timings
are instrumentation coverage rather than event loss and remain nullable only
under the closed A3 verifier's passing no-change or failing investigation rules.
Preserve the correlated A2T trace. Produce the seeded blank receipt with the
focused real-Standalone test command in
docs/validation/gpu-first-visible-a3-acceptance.md; merely setting
PULP_GPU_HEALTH_SEED_BLANK_FRAME=1 does not bind a control artifact. Keep
SKIP/INCONCLUSIVE and absent format scans as pending, never PASS.
Use reaper_smoke.py --mode editor-open --reaper-bin /exact/REAPER as the exact-format prerequisite for a
DAW A3 adapter, then execute that adapter through
gpu_first_visible_a3_campaign.py run-role --role daw. The smoke result cannot
be copied into the campaign: the adapter still owes 10 real cold and 10 real
warm editor lifecycles, independent native presentation timing, exact product
and host artifacts, the same-instance trace, and durable cache provenance.
Translate REAPER's --format au identity to the receipt's auv2; VST3 and CLAP
retain their names. An adapter timeout, SKIP, or INCONCLUSIVE produces a
nonterminal run.json, never a passing campaign.
Use the checked-in gpu_first_visible_a3_external_adapter.py as the immutable
envelope and select gpu_first_visible_a3_reaper_producer.py. Configure its
exact source-bound smoke/Lua helpers, REAPER, plugin binary/bundle, role driver,
checked-in source-bound trace analyzer, embedded-build verifier, and
digest-bound product build attestation/receipt. Also configure a reviewed
source-build driver from the exact clean revision; it must reproduce the
measured plugin executable and complete bundle in a fresh output without being
given the measured path. The analyzer is a sealed prepared binary, and every
REAPER lifecycle answers the producer's live-host nonce with exact executable
and start identity;
the driver must resolve to the reviewed file at the requested clean Pulp
revision. The producer runs the qualified-format preflight and then requires
the driver to perform all 20 editor opens, bind the trace to one challenged
live REAPER PID, and terminate every reported REAPER PID. It retains and rehashes those inputs with the
exact driver request/receipt in the host evidence tar. Missing automation,
build provenance, analyzer replay, or native-present truth is nonterminal;
the producer cannot promote the editor-open smoke or a screenshot completion
into campaign evidence.
The measured plugin executable must carry the canonical embedded build marker,
and the source-bound analyzer must preserve structural unverified separately
from the campaign budget verdict. Before terminal A3, also run the four-state
product trace-producer overhead protocol in the acceptance guide. Its exact
pre-change, compile-out, compiled-in idle, and active measurements require zero
xruns/audio-thread trace events; the DAW role campaign alone does not waive it.
For the DAW-selected overhead row, run the same reviewed REAPER lifecycle
driver through gpu_first_visible_a3_trace_producer_overhead.py collect-state;
do not time the smoke wrapper or relabel its editor-open result. Every one of
the 55 samples/state must answer the collector while the challenged REAPER PID
and exact plug-in executable are alive, then terminate cleanly. Active replay
must find the health producer and bind the complete b4ba exact 20-signature
state/render/js package from the A3 acceptance guide.
gpu_acquire/gpu_submit/gpu_present are mandatory per sample; every other
signature remains counted and a zero count is reported not-covered, not
zero-cost. Require zero xruns and zero producer events on the audio TIDs.
Pin the candidate-relative state_build_driver too. The collector exports and
default-deny rebuilds the exact source row without the measured plug-in,
ambient build tree, or network, then requires identical executable bytes and
the requested tracing sentinel state. Keep its source/build/toolchain artifacts
with the campaign; an already-built bundle or receipt assertion is not proof.
A3 v2 additionally binds each DAW campaign's sample provenance and exact trace bytes, then reruns the prepared analyzer and checks campaign, instance, process, GPU-evidence, trace-evidence, category, and completeness identity. A JSON sidecar alone is never terminal proof.
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Optional AAX support for Pulp, including developer-supplied Avid SDK setup, CMake enablement, DigiShell/AAX Validator workflows, and local AAX builds on macOS or Windows.
日本語の概要は準備中です。原文の説明を表示しています。
Configure, implement, and test Pulp's optional desktop Ableton Link tempo-sync adapter while preserving the developer-supplied SDK, licensing, realtime, latency-compensation, and no-install boundaries.
日本語の概要は準備中です。原文の説明を表示しています。
Maintain Pulp's installed design-time agent capability manifest and public-surface ledger. Use when adding, removing, renaming, or materially changing public audio, MIDI, signal, timebase, or sequence APIs; registering a new algorithm for generators; changing capability support or deprecation state; or repairing agent-capabilities freshness, schema, fingerprint, tombstone, or installed-SDK tests.
日本語の概要は準備中です。原文の説明を表示しています。
Android platform development for Pulp — NDK cross-compilation, Oboe audio, Dawn/Skia GPU rendering, JNI bridge, touch interaction, emulator workflows, and end-to-end smoke validation. Covers build, deploy, debug, and the gotchas discovered during bringup.
日本語の概要は準備中です。原文の説明を表示しています。
Optional ARA support for Pulp, including developer-supplied ARA SDK setup, CMake enablement, adapter companion APIs, validation, and ARA-aware plugin implementation guidance.
日本語の概要は準備中です。原文の説明を表示しています。
The measurement surface for ALL Pulp DSP and audio-pipeline work — read it BEFORE writing or gating DSP, not only when something already sounds wrong. Covers the C++ harness (signal generators, metrics, assertions, RenderScenario, contracts), the offline Audio Doctor (magnitude/frequency response, THD/THD+N, phase/group delay), and their Python sibling the Audio Quality Lab (tools/audio/quality-lab — null residual + alignment, LTAS log-spectral distance, spectral flux/centroid, HNR, Theil-Sen drift slope, Kaiser-sinc resampling, license-guarded corpus, regression-net ratchet). TRIGGER on AUTHORING work — "build/design an oscillator/filter/synth/effect", "add a DSP module", "what should the acceptance gate be", "how do I measure aliasing / anti-aliasing / alias floor", "null against a reference", "is this DSP correct", "choose a tolerance", "golden/regression corpus for audio", "measure drift or jitter", "A/B two renders" — AND on DEBUGGING work — "is there sound / no audio / I hear nothing", "does this filter/compressor/synth/delay produce the right signal", "prove the DSP / prove the contract", "measure the frequency response", "what's the THD / is it distorting", "what's the group delay / phase response / measured latency", "magnitude response curve", "render a test tone and assert", "audio regression", "64-frame works but 128 is silent", "sample-rate change pitch-shifted it", "describe what's in this buffer", "audio doctor", "compare before/after a DSP refactor". Reach for this BEFORE hand-rolling any FFT, null test, alias measurement, pitch tracker, or golden-render script — most of it already exists in one of the two lanes. Test/tool layer over HeadlessHost — deterministic, no audio device, no speakers. Off the realtime thread entirely.
日本語の概要は準備中です。原文の説明を表示しています。