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.
日本語の概要は準備中です。原文の説明を表示しています。
Search, suggest, add, and browse third-party audio packages. Handles "suggest packages", "add a package", "what packages are available", "search for pitch detection", and package browsing.
インストール方法を見るインストールする前に、エージェントに与えられる指示の中身を確認できます。
Help users discover, evaluate, and add third-party audio libraries to their Pulp projects.
Search the registry for packages matching a need:
./build/pulp search "<query>"
./build/pulp search "<query>" --refresh
./build/pulp suggest --description "<what the user needs>"
./build/pulp suggest --description "<what the user needs>" --include-license-gated
pulp suggest hides packages that require license review or a commercial
override by default. Add --include-license-gated only when the user explicitly
wants to inspect those candidates; otherwise prefer permissive defaults.
Or read the registry directly for full details:
cat tools/packages/registry.json | python3 -m json.tool
| Category | Packages | What They Provide |
|---|---|---|
| dsp | aubio, btrack, cycfi-q, daisysp, essentia, fftw, fluidsynth, freeverb, kfr, kissfft, libpd, pffft, rnnoise, rubber-band, signalsmith-dsp, signalsmith-stretch, soundtouch, speexdsp, stk, tinysoundfont | Pitch/time stretch, filters, FFT, pitch detection, physical modeling, noise reduction, synthesis |
| audio-io | alac, dr-libs, fdk-aac, lame, libflac, libsamplerate, libsndfile, libvorbis, miniaudio, oboe, opus, r8brain-free-src | File codecs, sample-rate conversion, device/audio I/O |
| music-theory | mts-esp, sst-tuning-library | Optional microtuning client integration, direct Scala SCL/KBM file parsing |
| ml | rtneural | Real-time neural network inference |
| ui | fontaudio | Audio icon font |
| utilities | libremidi, rtmidi | MIDI I/O helpers |
A package that provides a custom control for the design importer declares it
under the optional design_controls array on its registry entry
(registry-schema.json → design-control). Each entry is FLAT — a Figma identity
(component_set_key — authoritative — or name_prefix — fallback) at the top
level alongside factory_id (NOT nested under a match object). This shape is in
lockstep with the TS DesignControlEntry (library-registry.ts) and the flat
library-manifest.json convention; the schema's anyOf requires at least one
identity field. Each entry maps that identity to
a factory_id the package's pulp::view::View registers at host startup via
register_design_control_factory (see the import-design skill, P7 Tier-3).
End-to-end the importer consumes these fragments automatically: when it resolves
a design, it discovers the project's packages.lock.json + registry.json,
gathers each installed package's design_controls into the recognition merge
layer (RecognitionResolver::add_source, one source per package), and when an
imported node's identity matches, emits a kind=custom interactive element
carrying that factory_id. DesignFrameView::build_overlays then builds the
package's control; an unregistered factory renders inert + diagnoses (never a
silent knob). With no custom-control package installed, nothing is gathered and
import behavior is unchanged. The TS resolver customControlFactoryId in
library-registry.ts mirrors the same flat-entry shape for the in-Figma plugin
lane. This is the "give-back" path's long tail: an obvious common control is
promoted into core pulp::view + the shipped library-manifest.json instead.
./build/pulp add <package-id>
This will:
cmake/pulp-packages.cmake with FetchContent declarationspackages.lock.json, DEPENDENCIES.md, NOTICE.mdRegistry entries can describe packages where upstream does not export the exact
CMake target Pulp wants. Use cmake.sources for source files that should be
compiled into a generated static target after FetchContent populates the source
tree. Keep cmake.include_dir rooted at the fetched source directory and make
the generated target position-independent so plugins can link it safely.
mts-esp is the reference pattern: it fetches ODDSound's source, compiles
Client/libMTSClient.cpp into mts_esp_client, links ${CMAKE_DL_LIBS} when
needed, and stays opt-in. When adding another source-backed package, update the
registry schema, package-command generation tests, dependency manifest,
DEPENDENCIES.md, NOTICE.md, and docs/reference/licensing.md together.
Header-only packages whose upstream CMake builds tools/tests instead of a
consumer-ready target can set cmake.add_subdirectory=false; the generated
pulp-packages.cmake uses FetchContent_MakeAvailable() with an inert
SOURCE_SUBDIR so the source tree is populated without adding upstream
tools/tests, then creates the declared interface target. sst-tuning-library
is the reference pattern for source-only SCL/KBM parsing.
Before suggesting a package, check if Pulp's built-in core/signal/ already covers the need:
The hand-written guide set covers the original curated packages. The registry is
larger, so use pulp search <query> or inspect tools/packages/registry.json
for the full inventory:
docs/guides/packages/index.md — category overview
docs/guides/packages/<package-id>.md — per-package guide
Read these to answer questions about specific packages.
MIT/BSD/Apache/ISC/zlib/BSL/public-domain packages install without prompts.
MPL-2.0 and other unlisted licenses require manual review. GPL/LGPL/AGPL
packages are restricted: the CLI blocks until the user explicitly accepts the
package SPDX with --accept-license <SPDX> or declares a commercial-license
basis with --license-override commercial. SSPL and proprietary packages are
blocked by default, but the current CLI also accepts
--license-override commercial for them, records a compliance warning, and
leaves distribution responsibility with the project. Prefer permissive
alternatives from the registry when available.
tools/deps/audit.py runs four invariants; the first two are --strict,
the last two also need --verify-licenses (both are in gates.sh):
manifest.json entry must appear in
DEPENDENCIES.md, NOTICE.md, and docs/reference/licensing.md.requirements-docs.txt, mkdocs.yml, root + bindings/python
FetchContent_Declare, external/<dir>/) must be represented in
manifest.json via name or external_names alias."offline_fetch": {"cache_name": ...}
(WebGPU-distribution today) must download only through FetchContent_Declare,
never a raw file(DOWNLOAD): changed-surface bounded runs configure their
base with FETCHCONTENT_FULLY_DISCONNECTED=ON, which governs nothing else.
Its tree is found as <cache_name>-<pin>... in the shared FetchContent cache.Checks 1 and 2 only ask whether a dep is named in each file. That is why
both of these passed a green --strict run for months: NOTICE.md reproduced
MIT without its warranty disclaimer for 21 entries, and the VST3 SDK was
labeled MIT on all four surfaces while the pinned tree (v3.7.12) was
"Steinberg VST3 License OR GPLv3". A name being present says nothing about
the text being right — when a license claim matters, read the tree.
Copy the license verbatim from the dependency's own tree or pinned upstream
ref — never paste a template. MIT looks like boilerplate but is not uniform
in practice: of 21 deps audited, mkdocs-material writes NON-INFRINGEMENT
where the rest write NONINFRINGEMENT, and Catch2's Boost license has a
materially different disclaimer ("TITLE AND NON-INFRINGEMENT", "ANYONE
DISTRIBUTING THE SOFTWARE"). The entry must carry the whole notice —
permission grant, inclusion condition, and warranty disclaimer.
Traps when extending the checks:
"to deal\nin the Software"). Raw matching
reports entries as missing clauses they plainly contain — use
audit.flatten().GPL. It false-positives on identifiers such as
gPluginFactory. Match "General Public License" — and note v3.7.12 words it
"General Public License (GPL) Version 3", never "GNU General Public License".LICENSE files, not source headers, which only
reference the nearest LICENSE.external/ before the FetchContent cache. external/<dep> is what the
build compiles; the cache accumulates every ref ever fetched, so a stale
cache dir can shadow the real tree and get the audit verifying a version the
repo does not use. A cache hit must match the pinned version.When adding a dep, always touch all four attribution files (manifest,
DEPENDENCIES, NOTICE, licensing) plus — if the CMake / pip / vendored
alias differs from the canonical name — add the alias to
DEFAULT_ALIASES in tools/deps/audit.py or the external_names
list on the manifest entry.
Some prebuilt toolchains carry bundled third-party components that are not
separate source directories in external/. For example, the Skia prebuilt
toolchain used by visual tests bundles Dawn, HarfBuzz, and ICU through Skia's
DEPS file. Track those exact nested revisions in the parent manifest entry
with a structured field such as determinism.bundled_pins, and mirror the
human-readable version in DEPENDENCIES.md, external/<toolchain>/VERSION.md,
and any relevant reference doc.
Do not add a separate manifest entry for a nested toolchain component unless Pulp fetches, vendors, or redistributes it independently. Otherwise the audit will require standalone NOTICE/licensing rows for something whose license and distribution boundary are already covered by the parent prebuilt.
An independently redistributed runtime is different from a nested toolchain
component. The pinned three.js payload is fetched or supplied through
PULP_THREEJS_RUNTIME_DIR when PULP_ENABLE_THREEJS_RUNTIME=ON (the default
with GPU support) and is staged under share/pulp/threejs for installed-SDK
consumers. Treat a change to that install boundary as a dependency-inventory
change: keep tools/deps/manifest.json, DEPENDENCIES.md, NOTICE.md, and
docs/reference/licensing.md truthful together, even when the revision and
license themselves do not change.
Same rule for Skia modules (e.g. skottie/sksg, linked only when the opt-in
PULP_LOTTIE CMake option is enabled): they ship inside the existing Skia
prebuilt under the same Skia BSD-3-Clause entry — clarify their use in the Skia
row of DEPENDENCIES.md, do not add a separate dependency/NOTICE entry.
One manifest row can cover several published binaries of the same dependency,
and they are not required to have the same contents. The Skia wasm slice is
the live example: it is Ganesh on WebGL2 (no Dawn, no Graphite) and it
excludes skottie/sksg, so Lottie is simply unavailable there even though the
same DEPENDENCIES.md row says the modules exist. Note per-slice divergences in
the row rather than letting a reader generalize from the native slice — the
alternative is someone enabling PULP_LOTTIE for wasm and getting an
undefined-skjson link failure with no explanation.
A prebuilt binary is only reproducible against the toolchain it was validated
with. The Skia wasm slice is verified against a specific Emscripten and wasi-sdk
version, recorded as determinism.web_toolchain in tools/deps/manifest.json
and mirrored in the Skia row of DEPENDENCIES.md. This is not bookkeeping:
.github/workflows/web-plugins.yml reads the slice's URL + sha256 out of the
manifest and keys its cache on the manifest hash, so the audit and the CI lane
physically cannot disagree about which binary is in use — and a pin bump
self-invalidates the cache. When you bump either the slice or the toolchain,
bump them in the manifest, never by editing a URL into a workflow.
The Skia toolchain itself is pinned at chrome/m153 via the
danielraffel/skia-builder fork (see tools/deps/manifest.json →
determinism.skia_builder_fork). The fork tracks upstream
olilarkin/skia-builder's tag pattern and additionally publishes iOS
device, iOS simulator, visionOS device, visionOS simulator, mac-x86_64,
and Skia.xcframework slices upstream does not. While upstream stays on
m144, this fork is the active dependency; revisit when upstream catches up.
Skia/Dawn and V8 are independently consumable prebuilts. If Skia advances while
the complete matched V8 release is unavailable, keep V8's structured
paired_skia / paired_dawn fields describing the V8 artifact that was
actually built; record the newer active Skia/Dawn provider and temporary mixed
selection in the human-readable dependency notes. Never relabel old V8 bytes
as matched to the new Skia milestone.
iOS-specific layout gotcha: unlike the mac / linux / windows slices, the iOS
zips ship libs under a per-arch subdir
(build/ios-gpu/lib/Release/{device-arm64,simulator-arm64,simulator-x86_64}/libskia.a)
because the device and fat-simulator zips would otherwise collide on
identical lib names when unpacked into one tree. FindSkia.cmake picks
the right subdir based on CMAKE_OSX_SYSROOT + CMAKE_OSX_ARCHITECTURES;
tools/scripts/fetch_skia_for_release.py keeps the subdir intact for
ios-device-arm64 and ios-simulator-arm64-x86_64 matrix slices via
_IOS_PRESERVE_ARCH_SUBDIR. Do not extend the flatten step to iOS, and
do not register a single combined iOS slice without per-arch handling.
Multi-arch simulator builds (-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64,
the default in tools/cmake/ios.toolchain.cmake with
ONLY_ACTIVE_ARCH=NO) are intentionally rejected by FindSkia.cmake
because each Skia archive is single-arch — silently picking
simulator-arm64 would arch-mismatch the x86_64 link. The fix is
either to build a single arch (-DCMAKE_OSX_ARCHITECTURES=arm64
with ONLY_ACTIVE_ARCH=YES) or to pre-fatten the two simulator
archives with lipo into a combined simulator/libskia.a upstream
of find_package(Skia). The default Pulp iOS smoke (pulp-ios-sim,
Apple Silicon dev machines) only needs arm64.
The V8 toolchain follows the same prebuilt-pin pattern (added 2026-06).
It is the optional JS engine backend (PULP_JS_ENGINE=v8), a sealed
embeddable libv8 pinned at tag v8-m153-15.3.76.5-26cef0256b0e via the
danielraffel/v8-builder fork (tools/deps/manifest.json → V8 entry,
determinism.release_assets per-platform URL + sha256). Fetched by
tools/scripts/fetch_v8_for_release.py into external/v8-build/<platform>/
and resolved by tools/cmake/FindV8.cmake. Like Skia, it bundles
third-party components internally — ICU, zlib, and Abseil — so it gets one
manifest row for V8 (BSD-3-Clause) plus the bundled-component NOTICE
attribution (ICU/zlib/Abseil), NOT separate manifest rows for the nested
libs (their distribution boundary is the parent libv8). Re-verify the
NOTICE list against the v8-builder source and artifact evidence when the V8 pin
bumps; the m153 assets do not carry a separate third-party manifest, so absence
of that file is not evidence of an empty bundled dependency set. iOS publishes
an actual jitless simulator V8.framework marked library: false: Pulp
validates its provenance and headers but has no device/AUv3 runtime acceptance
or packaging contract, so QuickJS remains the default and JSC remains opt-in.
Full rollout/governance:
planning/2026-06-06-v8-sealed-libv8-provider-migration-plan.md.
Bundled fonts under external/fonts/*.ttf (Inter, JetBrains Mono,
Noto Color Emoji) are first-class manifest entries with category: fonts
and a SHA-256 in the notes field. They are NOT the same as the
"bundled-toolchain" pattern above — each font is fetched / vendored
independently and Pulp redistributes it directly.
Add a new bundled font the same way as any other dep:
external/fonts/<Name>.ttf. .gitignore has an
!/external/fonts/ exception; before it existed the tracked fonts had all
been git add -f'd past /external/*/, so a new one looked ignored while
its siblings were tracked.external/fonts/README.md with the SHA-256, source URL,
and any gating notes (e.g. Noto Color Emoji is gated by
PULP_BUNDLE_NOTO_COLOR_EMOJI and ships in its own
pulp_add_binary_data static lib so macOS/Windows release builds
can drop the payload).DEPENDENCIES.md, NOTICE.md, and
tools/deps/manifest.json.pulp_add_binary_data(...) block in
core/canvas/CMakeLists.txt, register the resulting target in
PULP_SDK_TARGETS (parent CMakeLists.txt), and add the
accompanying C++ TU that calls
pulp::canvas::register_font(...) or
pulp::canvas::register_emoji_fallback(...) at startup.bundled_blobs() in core/canvas/src/bundled_fonts.cpp is an
std::array<BundledBlob, N> — grow N, and bump the bundled_font_count()
expectation in test/test_canvas_fonts.cpp, which exists to catch an
accidental drop.
Bundling a face does not make it PAINT. There are two resolution paths and
they are bridged separately. FontResolver (measurement, and make_font) sees
the bundled faces; SkParagraph — which is what fill_text actually draws
through — resolves from a FontCollection built in
core/canvas/src/text_font_context.cpp. Until 2026-08-02 that collection only
learned the bundled faces when the platform had no font database of its own, so
on macOS a bundled family measured correctly and painted as a system fallback.
The symptom is silent and looks like anything but a font bug: correct advances,
correct wrap points, one universal painted face. If you add a font and the
render does not change, measure the painted ink per family rather than trusting
the resolver — four families that measure four widths and paint one is the
signature.
Gate large bundles (≥ 1 MB) behind a CMake option that defaults OFF
where the platform provides a usable equivalent (e.g. emoji typefaces
on macOS / Windows) and ON for headless / CI / Linux / Android.
Synthetic missing-dep test: tools/deps/test_audit.py:: ManifestSourceScannerTests::test_uncovered_detection_catches_missing_pip_dep
— don't delete it. If the completeness gate regresses, this is the
regression test that catches it.
tools/packages/tool-registry.json ToolDescriptor carries optional importer
fields — frameworks[], spi_min/spi_max, sdk_min/sdk_max,
capabilities[], health_check — for category: "importer" tools (the
framework→Pulp project importers resolved by pulp import). They're parsed by
tools/cli/tool_registry.cpp and ignored for non-importer tools. The importers
themselves install via the tool lane (pulp tool install <importer>), never as
project packages.lock.json dependencies.
tools/packages/validate_registry.py also validates tool-registry.json: its
validate_tool_registry rule fails if any managed_by_pulp tool ships without
a non-empty pinned_version. That pin is the anchor for pulp tool update and
for the user version override (--version / PULP_TOOL_<ID>_VERSION /
$PULP_HOME/tool-overrides.json), so a managed tool without it would be one
users can't update or override. When you add a managed_by_pulp entry, include
its pinned_version in the same change. Rule + rationale:
extending-pulp.md.
The check runs anywhere validate_registry.py runs (its
test_package_validation_tools.py unit suite covers the new rule).
For python_pip tools, the managed venv manifest is also the installed-version
authority. pulp tool install may reuse an existing venv only when its wrapper
exists and manifest.json records the current registry pin; a missing or stale
manifest must reinstall. Never report the registry pin as the installed version
without checking that manifest, or a pin bump can leave users running old code
while the CLI claims they are current.
packages, kits, content, and installable-tools are Pulp's four ways to
extend a project or machine, and they share one lifecycle contract: add is
validated, remove is confirmed + confined to the surface's own area + names what
it deleted, and both add and remove ship tests. Pick the right surface and read
the shared contract in
extending-pulp.md.
packages — third-party audio DSP libraries → a projectkits — reusable Pulp code/UI/templates → a projectcontent — data-only packs (presets/samples) → an installed plugininstallable-tools — machine-level dev/agent tooling under ~/.pulp/tools/, plus the shared validate-and-uninstall-from-outside-a-checkout barDEPENDENCIES.md, NOTICE.md, and tools/deps/manifest.json describe which JS
engine a build actually links, so their wording is an attribution claim rather
than a description. Keep them on the real contract: QuickJS is the default,
JSC is opt-in on Apple (PULP_JS_ENGINE=jsc) and is a system framework that
must never be implied by "Apple" alone, and V8 is an optional sealed prebuilt
that is unavailable on iOS because JIT is forbidden there. Phrasing such as
"default is QuickJS, JSC on Apple" or "iOS is JSC-only" overstates what is
linked and misattributes a framework the build may not use at all. When the
engine contract changes, update all three together; python3 tools/deps/audit.py --strict checks pins and notice coverage, not whether the prose matches the
build.
When a prebuilt Skia release gains a Windows platform slice, keep the exact
asset URL and SHA-256 in tools/deps/manifest.json and mirror the digest in
external/skia-build/VERSION.md; a dependency inventory entry alone is not a
complete platform publication.
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
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.
日本語の概要は準備中です。原文の説明を表示しています。