Files
nvrl 444b9d93da
Check and Windows preview / check (anvil) (push) Successful in 2m11s
Check and Windows preview / check (crucible) (push) Successful in 1m43s
Check and Windows preview / check (knurl) (push) Successful in 1m37s
Check and Windows preview / check (lathe) (push) Successful in 1m39s
Check and Windows preview / check (patina) (push) Successful in 2m25s
Check and Windows preview / check (rivet) (push) Successful in 1m50s
Check and Windows preview / check (temper) (push) Successful in 1m45s
Check and Windows preview / check (weld) (push) Successful in 3m9s
Check and Windows preview / previews (push) Successful in 1m50s
Check and Windows preview / windows (push) Successful in 2m46s
Independent plugin release / publish (push) Successful in 9s
Independent plugin release / plan (push) Successful in 8s
Independent plugin release / build (push) Successful in 2m10s
feat!: change CLAP IDs to io.narl.bass-foundry.<product>, release all plugins
The io.storyboard.* IDs were a leftover from Motif Storyboard and named
old functions (motif, sub-anchor, layer-align) instead of the products.
Every plugin now uses io.narl.bass-foundry.<product>, and IDs are frozen
from here on, even across renames.

Hosts see the new IDs as different plugins: delete the old .clap files,
rescan, and add the plugins again in existing projects. Rivet session
JSON and Patina parameter IDs are unchanged.

- Rivet 0.5.0, Patina 0.3.0; Anvil, Crucible, Lathe, Temper, Weld and
  Knurl 0.2.0.
- Docs, changelogs and the release table describe the change.
- Website: upgrade note in the Install section.
2026-11-06 11:51:11 +01:00

7.4 KiB
Raw Permalink Blame History

Development

Run commands from the repository root. You need Rust stable and Python 3.11 or newer. Lockfiles are committed for the root workspace, every CLAP wrapper and the headless editor tool. Use --locked so a build never changes them silently.

Product boundaries

plugins/catalog.json is the single list of products. For each product it defines the display name, the wrapper manifest, the library name, the documentation folder, the CLAP ID, the internal packages and the licenses to ship. The build, check, package and release scripts and the script tests read it. The version in each wrapper's Cargo.toml is the release version.

Every product has the same split:

Folder Content Workspace
plugins/<product>/core/ Engine. Plain Rust, no plugin SDK, tested on its own root
plugins/<product>/ui/ egui editor, built on common-ui root
plugins/<product>/plugin/ CLAP wrapper on a pinned NIH-plug revision own

Rivet has more crates in the root workspace (midi, app, preview), and Patina has render for offline WAV evaluation. The shared crates are:

  • common-ui: palette, embedded fonts, header, panels, knobs, meters.
  • common-dsp: filters, YIN pitch tracking, the note tracker used by Anvil, Lathe, Temper and Knurl, 4× oversampling, meters and WAV files.

The wrappers stay separate workspaces, each with its own lockfile, so the NIH-plug Git dependency stays out of the root workspace. Host identities and saved state are separate for every product.

The engines run allocation-free on the audio thread. Analysis that is too heavy for the audio thread (Rivet's generation jobs, Weld's cross-spectrum and fit) runs on a worker thread. Data goes to the editor through lock-free queues.

Checks

cargo test --workspace --locked
cargo clippy --workspace --all-targets --locked -- -D warnings
python3 scripts/check-plugin.py --plugin <product>
python3 -m unittest discover -s scripts/tests -v

Products: rivet, patina, anvil, crucible, lathe, temper, weld, knurl.

check-plugin.py runs formatting, tests and clippy for one product's packages and its wrapper, without compiling the other products. Run it for every product when you change a shared crate or the root workspace. Offline builds work once the dependencies are cached; add --offline to manual Cargo commands.

The test types are:

  • Engine tests (core/tests/dsp.rs and similar) measure signal behaviour and latency against fixed targets.
  • Realtime tests (core/tests/realtime.rs) install a counting allocator and fail if the audio-thread path allocates.
  • Host tests (plugin/tests/host.rs) load the wrapper through the exported CLAP ABI without a DAW. They check the ports, latency, state save and load, and processing at 44.1, 48 and 96 kHz with odd block sizes.
  • Script tests (scripts/tests/) cover the catalog, release planning, packaging and the publish client with fake transports. They also fail when a catalog product is missing from a CI workflow.

The native wrapper tests on Linux need pkg-config and the X11/XCB and OpenGL development headers. Rivet's optional standalone also needs ALSA/JACK headers. The Gitea check jobs install these headers.

Windows builds

On Windows, install Rust and the MSVC build tools. To cross-compile on Linux, install the Rust x86_64-pc-windows-gnu target and MinGW (x86_64-w64-mingw32-gcc).

python3 scripts/build-plugin.py --plugin <product> --target x86_64-pc-windows-gnu
python3 scripts/package-windows.py                    # combined preview ZIP
python3 scripts/package-windows.py --plugin <product> # one product's release ZIP

Builds go to dist/<target>/<Name>.clap, with the docs and licenses in the same folder structure. CARGO_TARGET_DIR is supported for sharing one build directory between wrappers. MSVC packaging accepts --target x86_64-pc-windows-msvc.

  • The combined ZIP is dist/bass-foundry-windows-x86_64.zip. It holds every product that has been built.
  • A product ZIP is <product>-windows-x86_64-<version>.zip and holds only that product.

Every ZIP has a SHA256SUMS file inside and a .zip.sha256 file next to it. ZIP metadata is deterministic; the binaries still depend on the toolchain and the build environment. Packaging takes only the selected catalog entries, never old binaries left in dist.

Linux builds are for local development; CI produces only Windows binaries. No script installs a plugin into a DAW.

Editor previews

mkdir -p target/editor
cargo run --manifest-path tools/editor-check/Cargo.toml -- target/editor/<product>.png --plugin <product>
cargo run --manifest-path tools/editor-check/Cargo.toml -- target/editor/rivet-creative.png --plugin rivet --creative
cargo run --manifest-path tools/editor-check/Cargo.toml -- target/editor/patina-compact.png --plugin patina --compact

Patina also accepts --silent, --delta and --bypass. The tool compiles the real editor code, feeds it synthetic analysis data and renders it in software without a display. Copy a checked render to docs/<product>/assets/editor.png to update the guide. These renders do not prove native host embedding, keyboard routing or how the plugin sounds; use the FL Studio checks in each product guide.

Add a product

  1. Create plugins/<product>/{core,ui,plugin}. Copy the wrapper Cargo.toml and Cargo.lock of a similar product so the NIH-plug revision matches.
  2. Add core and ui to the root members, the wrapper to exclude, and a [profile.dev.package.<product>-core] opt-level = 2 entry if the tests run heavy DSP.
  3. Add the product to plugins/catalog.json with a new, unique CLAP ID.
  4. Add it to .gitea/workflows/check.yml (check matrix, editor preview, Windows build) and to the tag list in release.yml. The script tests fail until you do.
  5. Add a --plugin <product> fixture to tools/editor-check.
  6. Write docs/<product>/README.md, CHANGELOG.md and assets/editor.png, and add the product to the root README.

Compatibility and naming

The suite is called Bass Foundry; this is the vendor name hosts show. Hosts identify plugins by CLAP ID, so the vendor name does not affect saved projects.

Product Former display name CLAP ID
Rivet Motif Storyboard io.narl.bass-foundry.rivet
Patina Resonance Compressor io.narl.bass-foundry.patina
Anvil (new) io.narl.bass-foundry.anvil
Crucible (new) io.narl.bass-foundry.crucible
Lathe (new) io.narl.bass-foundry.lathe
Temper (new) io.narl.bass-foundry.temper
Weld (new) io.narl.bass-foundry.weld
Knurl (new) io.narl.bass-foundry.knurl

CLAP IDs use the form io.narl.bass-foundry.<product>. Builds tagged before this scheme used io.storyboard.* IDs; hosts treat the new IDs as different plugins, so projects saved with those builds need the plugin added again. From now on an ID is frozen: if a product is renamed, its CLAP ID stays. A new plugin gets io.narl.bass-foundry.<slug> with its product slug.

Rivet keeps story-session-v1 and its session migration. Patina keeps all parameter IDs. The copyright notice keeps its original holder name. Delete old plugin files before rescanning, so FL Studio does not list both builds.

See releases for tagged publishing. Runs on the real Gitea runner and checks in FL Studio are separate from the local checks.