Check and Windows preview / check (anvil) (push) Successful in 2m15s
Check and Windows preview / check (braze) (push) Successful in 2m26s
Check and Windows preview / check (crucible) (push) Successful in 1m52s
Check and Windows preview / check (knurl) (push) Successful in 1m42s
Check and Windows preview / check (lathe) (push) Successful in 1m44s
Check and Windows preview / check (patina) (push) Successful in 2m38s
Check and Windows preview / check (planish) (push) Successful in 1m29s
Check and Windows preview / check (rivet) (push) Successful in 1m39s
Check and Windows preview / check (temper) (push) Successful in 1m37s
Check and Windows preview / check (weld) (push) Canceled after 2s
Check and Windows preview / previews (push) Canceled after 0s
Check and Windows preview / windows (push) Canceled after 0s
Adds a detuned copy of the bass whose beating stays on the tempo grid on every note. The chosen harmonic k beats once per Rate note value, and harmonic n beats n/k times as fast, so every harmonic is on the grid. - The copy is read from a delay line whose delay falls one period of the tracked note per beat cycle (r = 1 + B/(k f0)) and jumps back exactly one period, crossfaded over 6 ms. On a periodic note the jump has no seam: the output matches the ideal sum within 0.003 on harmonics 1-4. - The delay follows the host's beat position, shifted by the lookahead, so beat peaks land on the grid; free-running when stopped. - Low cut: LR4 high-pass on the copy, the bass split by the same crossover, so the sub does not beat and keeps its level. - Depth, Width, Phase, Gate, Output, Snap, Bypass. Pitch from audio (about 67 ms lookahead) or MIDI. - Editor: one bar of the beat per harmonic with the grid and a playhead. - Catalog, workflows, editor-check, docs, signal chain and site copy.
161 lines
7.5 KiB
Markdown
161 lines
7.5 KiB
Markdown
# 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](../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, Knurl, Planish and Braze, 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
|
||
|
||
```sh
|
||
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`, `planish`, `braze`.
|
||
|
||
`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`).
|
||
|
||
```sh
|
||
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
|
||
|
||
```sh
|
||
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` |
|
||
| Planish | (new) | `io.narl.bass-foundry.planish` |
|
||
| Braze | (new) | `io.narl.bass-foundry.braze` |
|
||
|
||
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](releases.md) for tagged publishing. Runs on the real Gitea runner
|
||
and checks in FL Studio are separate from the local checks.
|