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.
日本語の概要は準備中です。原文の説明を表示しています。
Keep a plugin/demo repo's screenshots in sync with its UX. When editor / UI / design-token code changes in an opted-in repo (one carrying .pulp/screenshots.toml), re-capture and refresh every place each shot is consumed — README images, web-demo Open Graph (og.png) images, and gallery thumbnails — consistently across the WAM and WCLAP demo sites. Two capture routes: native headless (render_to_png / pulp-screenshot, Skia) for README / native-editor shots, and live-web headless Chrome (playwright-core) for WAM/WCLAP demo OG + gallery images. Backfills demos missing OG images. This is a PORTING / consistency tool — it re-shoots a repo's OWN UI, it never clones another product's art.
インストール方法を見るインストールする前に、エージェントに与えられる指示の中身を確認できます。
Keep a repo's screenshots in lockstep with its UI. When the UX changes, the
README shot, the web-demo Open Graph preview (og.png), and the gallery
thumbnail all have to change with it — this skill re-captures them and the
screenshot_sync_check.py gate fails a PR that forgot to.
Read the screenshot skill first for the raw capture
mechanics (backends, the image-compositing trap, the content-floor oracle,
vision-probe). This skill is the sync/consume orchestration on top of it.
.pulp/screenshots.toml had a UX-path change and the
screenshot-sync gate (pre-push / CI) or PostToolUse hint fired, ORScreenshots are a plugin-with-releases concern, not a core/tooling one.
Check for .pulp/screenshots.toml at the repo root. Absent → the repo is not
opted in; stop. Do not invent screenshots for a library/tool/core repo. Opt-in
is per-repo by the file's presence — the same pattern
~/.config/pulp/daw-smoke.toml and .shipyard.local/config.toml use.
Default opt-in set: the packaged-demo and packaged-examples consumers
(gpu-nam, bendr, superconvolver, spectral-lab, tempo-sampler, classic-effects,
pulp-example-plugins). Everything else — pulp core included — stays off.
.pulp/screenshots.tomlOne file per opted-in repo, at its root. It lists the UX paths that should trigger a re-shoot and, for each screenshot target, its capture route and every place the image is consumed — so one re-shoot fans out to README + OG
schema_version = 1
# UX paths whose change should trigger a re-shoot (repo-relative, gitignore-style
# globs — the same dialect skill_path_map.json uses). A design-token change
# reskins every widget, so a trigger hit invalidates ALL targets in the repo.
[trigger]
paths = [
"src/**/*_editor.cpp",
"ui/**/*.js",
"assets/design-system/**/*.css",
"assets/design-system/**/*.tokens.json",
]
# ── Native README / editor shot (capture route A) ──
[[target]]
id = "mono-synth-editor"
route = "native" # render_to_png / pulp-screenshot, Skia/gpu
tool = "pulp-mono-synth-shot" # per-editor dumper target (brew_shots.cpp pattern)
size = "720x460"
scale = 2.0 # match the native editor baseline (test_editors.cpp)
backend = "skia" # or "gpu" for requires_gpu_host() views
consumes = [
{ kind = "readme", path = "docs/img/mono-synth.png" },
]
# ── Web demo OG + gallery (capture route B) ──
[[target]]
id = "mono-synth-web"
route = "web"
url = "example-plugins/mono-synth/index.html"
device_scale_factor = 2
consumes = [
{ kind = "og", path = "example-plugins/mono-synth/og.png" },
{ kind = "gallery", path = "docs/gallery/mono-synth.png" },
{ kind = "og_meta", html = "example-plugins/mono-synth/index.html", rel = "./og.png" },
]
[[target]]
id = "example-gallery-web"
route = "web"
url = "example-plugins/index.html"
device_scale_factor = 2
# A target may narrow the global trigger to just the paths that affect IT:
trigger = [ "ui/**/*.js", "assets/design-system/**" ]
consumes = [
{ kind = "og", path = "example-plugins/og.png" },
{ kind = "og_meta", html = "example-plugins/index.html", rel = "./og.png" },
]
Schema:
schema_version — currently 1.[trigger].paths — global UX globs; a match invalidates every target unless a
target sets its own trigger list.[[target]] — id (required, unique), route (native | web, required),
optional trigger (per-target override), plus route-specific keys
(tool/size/scale/backend for native; url/device_scale_factor for
web) that document how to re-capture.consumes[] — { kind, path } or { kind, html, rel }:
readme / gallery — the PNG a markdown/gallery references.og — the PNG the page's og:image / twitter:image point at.og_meta — the HTML head that must declare the OG/Twitter block pointing at
that og.png (this is what backfills a demo missing OG images).The gate verifies the readme / gallery / og PNG paths were refreshed
when a trigger fired; og_meta is documentation for the head-template edit.
python3 tools/scripts/screenshot_sync_check.py --base origin/main --mode=report
It lists each [[target]] a diff invalidated. Read the hook output if it already
fired. A token change lists every target.
For a plugin's native editor rendered to a PNG with no window. See the
screenshot skill for backend selection; the essentials:
pulp-bitches-brew repo's
ui/brew_shots.cpp → brew-shots pattern; each plugin repo copies it as pulp-<name>-shot,
named by the manifest tool =), or pulp-screenshot --demo for SDK demos.gpu backend (offscreen Dawn+Skia HeadlessSurface) for
requires_gpu_host() views and any headless/CI/SSH capture — never the live
WindowHost::capture_png() headless (it needs an interactive WindowServer
session and hangs).test_editors.cpp in the
example repos) so README shots align with the visual-regression baselines.ScreenshotStats::passes_content_floor — never commit a blank /
near-blank PNG (a "file written" check misses the empty-frame bug).consumes.kind = readme|gallery path.pulp-screenshot is an Apple-only target; the per-editor dumper is the portable
route and must be added to each plugin repo.
The WAM/WCLAP plugin UI is a WASM build running in a browser, so the faithful
capture is a real browser rendering the demo page — not native
render_to_png. Pulp already ships this path (playwright-core against the system
Chrome, no browser download); the OG generators reuse it.
Convention (match WAM exactly). WAM's OG image is a device-scale-2
element screenshot of the demo page itself, saved as a colocated og.png:
#panel) — ~1280×1234.wrap) — ~1720×2002The reference generators:
web/gen-og-images.mjs (renders each page,
presses the player's window.__start() seam, waits for window.__demo.started,
screenshots the element at deviceScaleFactor: 2 with --mute-audio --autoplay-policy=no-user-gesture-required) + web/gen-og.mjs (injects the
og:image/twitter:card=summary_large_image block only when a sibling og.png
exists) — both in pulp-example-plugins / pulp-classic-effects./example-plugins/ + /classic-effects/
galleries): generated by
examples/web-demos/wclap-build/cloudflare/assemble-gallery.mjs in pulp core.
Its pluginPage() / galleryPage() emit the OG head block; the sibling
gen-og-images.mjs renders each page's og.png. WCLAP pages need
cross-origin isolation (COOP/COEP) to instantiate the threaded-wasm shared
memory, so serve them with cloudflare/serve-headers.mjs (which applies the
_headers rules), never a plain static server.Steps:
assemble-gallery.mjs for WCLAP; the
per-plugin .wasm modules are the only heavyweight input — they can be
rebuilt with wasi-sdk or fetched from the live deploy).serve-headers.mjs for COOP/COEP).deviceScaleFactor: 2, waiting on the ready
predicate. Do NOT resume audio for its own sake — the demos are
click-to-start; if a shot needs the started editor, drive the __start()
seam under --mute-audio (see the local-dev audio etiquette in CLAUDE.md).og.png; assert it is non-blank before trusting it.og:image / og:url / twitter:card /
twitter:image → the colocated og.png (the og_meta consume). Edit the
shared page template once, not each page by hand.og:image URL points at PRODUCTION, so the unfurl is staleassemble-gallery.mjs stamps og:image / og:url from SITE_BASE, which DEFAULTS to the
production origin (https://pulp-wclap-demos.pages.dev). Deploy to a preview alias
(wrangler pages deploy --branch <name> → <name>.pulp-wclap-demos.pages.dev) and the
page's og:image still points at PRODUCTION — so a crawler unfurling the PREVIEW link
fetches the production og.png (the OLD image; your fresh capture sits on the preview alias
and is never seen). Symptom: you regenerate og.png, confirm it live at
<preview>/…/og.png, and the link preview STILL shows the old art — because the meta URL
and the image are on different origins. Fix: rebuild the preview with the alias as base —
SITE_BASE=https://<name>.pulp-wclap-demos.pages.dev node assemble-gallery.mjs — then
redeploy; verify with curl -s <preview>/…/ | grep og:image. Production needs no override
(default base is correct once merged). Separately, social caches (iMessage, Slack) hold the
OLD unfurl until they re-crawl — changing the og:image URL forces a re-fetch, but a client
may still show its cached card until the link is re-sent or its preview cache clears.
assemble-gallery.mjs + wrangler deploy does NOT refresh og.pngog.png is content-hash cached — gen-og-images.mjs writes an og.png.key beside each
image and SKIPS a page whose inputs (its own HTML + the assets it loads: wasm module,
player, pulp-ui.js) are unchanged, re-shooting only pages whose hash moved. The trap:
the normal deploy loop is assemble-gallery.mjs → wrangler pages deploy, and neither
runs gen-og-images.mjs. So after you change an editor and redeploy, the share card
keeps showing the OLD capture — a full-canvas editor shipped for a while with an og.png of
the pre-editor knob grid. To refresh: run gen-og-images.mjs (or delete the page's
og.png + og.png.key to force a re-shoot) BEFORE deploying. A full-canvas Skia editor
only renders under the ANGLE/GPU Chrome flags, and the shot is a DEFAULT-state capture — it
will not reflect interactive states (a knob moved, an engine toggled).
gen-og.mjs's
ogBlock(); WCLAP: assemble-gallery.mjs's page emitters) so WAM and WCLAP
can't drift.og:image / og:url /
twitter:card=summary_large_image / twitter:image lines to the page
template + generating the colocated og.png. Social crawlers fetch the raw
HTML and do NOT run JS, so the tags must be present at rest.Re-run screenshot_sync_check.py --mode=report → clean.
Vision-probe before reading any capture for a visual judgment (screenshot skill).
Commit the PNGs + any head-template edit in the SAME PR as the UX change, or add a bypass trailer on the tip commit:
Screenshot-Sync: skip target=<id|all> reason="..."
Legitimate uses: a provably-invisible refactor, a docs-only touch that trips a
glob, or a batch whose shots regenerate in a follow-up. Same philosophy as
Skill-Update: skip / Version-Bump: skip.
tools/scripts/screenshot_sync_check.py reads the repo's .pulp/screenshots.toml,
diffs the change range against [trigger].paths, and checks each triggered
target's consumed PNGs were refreshed (or bypassed). It DETECTS staleness; it
does not capture (capturing is heavy + non-deterministic across machines — the
re-shoot is the explicit step above). Wired exactly like the skill-sync gate:
| Layer | Where | Mode |
|---|---|---|
| 1 hint | PostToolUse (hooks/scripts/cli-plugin-sync.sh) | --mode=hint — advisory |
| 2 report | .githooks/pre-push | --mode=report — enforcing (demote with PULP_DISABLE_PREPUSH_GATES=1) |
| 3 CI | .github/workflows/version-skill-check.yml | --mode=report — authoritative, no env demotion |
The check script ships from pulp core and is installed into consumer repos the
same way the githooks are (tools/scripts/install-githooks.sh); the trigger
map is repo-local (.pulp/screenshots.toml), because each plugin repo has its
own layout.
gen-og-images.mjs by hand. Only the
galleries auto-discover their demos (they read the plugins = [...] array back
out of the assembled index.html). Every non-gallery page — the standalone pages,
super-convolver, super-convolver-gpu — is a hardcoded entry. Add a page to
assemble-gallery.mjs without adding it there and assemble-gallery.mjs still
bakes an absolute og:image URL for it, pointing at a PNG that nobody ever
generated. And Cloudflare serves a missing asset as its 404 page with HTTP 200,
so this does not fail as a broken link — it fails as a blank unfurl, and a
naive curl -w %{http_code} check says 200 and confirms the lie. That is the
exact bug that shipped. check-og-images.mjs (file exists, real PNG magic bytes,
aspect ≈ 1.91:1) and check-unfurl.mjs (fetch the LIVE URL as a crawler and
follow the og:image) are the backstops — but the fix is to register the page.
Doubly easy to miss when the page is emitted conditionally, like
super-convolver-gpu, which only appears when its GPU DSP build tree exists.?v= on either side forks the name and the node never
constructs — the page loads, the UI paints, and there is simply no audio. The
corollary bites in the other direction too: every module the ?v= IS stamped
on must be an input to the hash (assemble-gallery.mjs's playerVersion()
hashes the shell, widgets, both adapters, and the UI entry). Add a bustable
module without adding it to the hash and its next change ships behind a stale
cache entry — an OG shot regenerated against a page that is still serving the
old bundle is the worst version of this, because it looks correct.--wam-build) is absent, and builds pages with no
custom UI when the wasm UI module (--ui-build) is absent — the player then
renders its generated parameter grid. A browser with no WebGL2 context lands on
that same grid at runtime. So before re-shooting a demo, confirm which UI the
page actually mounted; otherwise you will capture the parameter grid, commit it
as the plugin's og.png, and have "wrong" screenshots with a green build.mountPulpUi appends the canvas immediately and then fetches the wasm module,
so "the canvas exists" is true a full second before anything is drawn on it.
Waiting on it shoots the loading screen. The real signal is the loading class
being gone (.pw-customui-loading). gen-og-images.mjs additionally
refuses to shoot if that class is still present at timeout — a card is
worse than no card, because a blank unfurl looks like a broken product and
nothing in the build is red.check-og-images.mjs
decodes it and requires ≥2% bright pixels in the control band — the live broken
card measured 0.60%, every real one 4.2–7.0%. And note Cloudflare serves a
missing asset as its 404 page with HTTP 200, so a fetch-and-check-status
probe confirms the lie; you have to look at the bytes.og.png.key and skip when it matches: 78 s → 2 s across
a 30-page site when nothing moved. Verify the invalidation too — a cache that
never busts silently ships yesterday's UI.Content-Encoding on a plain-copied file breaks the page, not just
the byte count. The GPU page's 10.4 MB .data was copied raw while _headers
claimed brotli; the browser failed to decode it, the UI module never loaded, and
the only symptom anyone saw was an OG card reading "Loading editor…". One shared
copyUiFile() compresses every page's copy — don't hand-roll a second copy path.gpu backend, not the live host (which hangs
headless).serve-headers.mjs.pulp-example-plugins / pulp-classic-effects web/gen-og*.mjs); the WCLAP
combined-site template lives in pulp core (cloudflare/assemble-gallery.mjs).gen-og-images.mjs shoots each page, then the site assembler runs again to bake the
og:image / twitter block into the HTML — a page gets its tags only when its og.png already
exists on disk, which is only true on that second pass.
Run the second pass bare (no --wam-build / --ui-build / --gpu-build) and the assembler
cannot find the build trees, so it skips every page that needs one — quietly, in a one-line
note — and therefore never rewrites their HTML, which is the only thing that pass exists to do.
/super-convolver-gpu/'s og.png was shot perfectly, then never referenced: the page shipped
with no og:image at all and unfurled bare. The image existing on disk proves nothing;
check the HTML.
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
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.
日本語の概要は準備中です。原文の説明を表示しています。