8c24b83ea3
Three changes that only make sense together. **tools/**. check-branding.sh, brand-plugin.sh and branding.conf were accumulating in the repo root, and a deploy.sh/bat/ps1 set would land there too. They now live in tools/ and resolve the repo root explicitly rather than assuming their own directory is it, so they behave the same however they are invoked. **One name drives the rest.** branding.conf now derives the package name, the Rust types and the display name from PLUGIN_NAME, each overridable. Derivation-by-default removes a class of drift; the override matters because the derivation is sometimes invalid — "206 Prototype" derives to "206_prototype", which cargo rejects outright — and because PLUGIN_NAME is cosmetic while the package name is structural. A display tweak should not silently rename the crate, its bundle lookup, the CI command and the lockfile. This template overrides three of them, which a real plugin normally will not: its names are the template's own rather than derived from a product name. An overridden PKG that no longer matches the derivation is reported at info level, never failed. The override is usually deliberate; a display rename that forgot the package looks identical from here, and is not. **The site table is data.** branding-sites.tsv lists every place a name must appear and in what form; check-branding.sh iterates it instead of carrying 29 check calls in shell. The scripts are now identical across every plugin built from this template, with only the table and conf differing — which matters because the template and codename-206 had already diverged by two checks, so every sync was a shell merge. brand-plugin.sh regenerates the rows it owns and preserves the rest, so a hand-added row for a file this template does not ship survives a rename. Custom rows are value-substituted like any other tracked file, so they track the rename rather than going stale — the first cut excluded the whole table from substitution and got that wrong. brand-plugin.sh also gained an interactive mode: run it with no arguments and it prompts for each value with the current one as the default, so Enter keeps it and a run that answers nothing changes nothing. It only engages when there is a terminal to ask at, so CI gets the usage message and exit 2 rather than a hang. Verified by applying a full rename to a sandboxed copy: 8 files rewritten, 29 owned rows regenerated, 1 custom row preserved and substituted, checker green at 48/0/7, and the result compiles as bari_206. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WHt4DykxXaQnyaJfyS8KCn
177 lines
7.8 KiB
Markdown
177 lines
7.8 KiB
Markdown
# NIH-plug Template
|
|
|
|
Minimal scaffold for new [NIH-plug](https://github.com/robbert-vdh/nih-plug) audio plugins (Rust + CLAP/VST3 + egui).
|
|
|
|
Based on patterns from [`musiikki/codename-206`](http://192.168.2.199:3005/musiikki/codename-206).
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# Clone this repo as your starting point
|
|
git clone http://192.168.2.199:3005/musiikki/nih-plug-template my-plugin
|
|
cd my-plugin
|
|
|
|
# Edit: src/lib.rs, Cargo.toml, bundler.toml — replace placeholder values
|
|
# See musiikki/codename-206 for a real-world example of how to fill these in
|
|
|
|
# Then verify the rename landed everywhere
|
|
sh check-branding.sh
|
|
```
|
|
|
|
## Renaming
|
|
|
|
Identity is duplicated across `Cargo.toml`, `bundler.toml`, `src/lib.rs`, `NOTICE`,
|
|
`Cargo.lock`, the CI workflow and the docs. Nothing makes those agree, so a half-finished
|
|
rename builds a bundle under one name while CI bundles another.
|
|
|
|
Everything for it lives in `tools/`:
|
|
|
|
```
|
|
tools/branding.conf the names, and what derives from what
|
|
tools/branding-sites.tsv where each name has to appear, and in what form
|
|
tools/brand-plugin.sh changes names
|
|
tools/check-branding.sh verifies them
|
|
```
|
|
|
|
```bash
|
|
sh tools/brand-plugin.sh # interactive; Enter keeps a value
|
|
sh tools/brand-plugin.sh --display "Fuzz Box" # or non-interactive; dry run
|
|
sh tools/brand-plugin.sh --display "Fuzz Box" --apply
|
|
sh tools/check-branding.sh # exit 0 = consistent, 1 = drift
|
|
```
|
|
|
|
**One name drives the rest.** `PLUGIN_NAME` derives the package name (`Fuzz Box` →
|
|
`fuzz_box`), the Rust types (`FuzzBox`, `FuzzBoxParams`) and the display name. Each is
|
|
overridable in `branding.conf`, because the derivation is sometimes wrong and sometimes
|
|
outright invalid — `206 Prototype` derives to `206_prototype`, which Cargo rejects since a
|
|
package name cannot start with a digit. The helper refuses those, along with Rust keywords
|
|
and built-in crate names.
|
|
|
|
**The checker does both halves.** Every canonical value is asserted *present* where it
|
|
belongs, and every retired spelling is swept for. Absence alone is not enough: renaming to
|
|
the wrong new spelling would pass a stale-sweep and still break CI.
|
|
|
|
**Two things it will not do for you.** `CLAP_ID` and `VST3_CLASS_ID` are how hosts recognise
|
|
saved projects and presets — change either and every existing session silently loses the
|
|
plugin, with no error. They need `--allow-identity-change`, and the 16-byte class ID is
|
|
*proposed* rather than derived, because that choice is yours. And `CLAP_DESCRIPTION` is
|
|
rejected if it carries a retired name or the repo codename: hosts show it to users who never
|
|
knew your development history.
|
|
|
|
**The repo name is deliberately not derived.** It is fixed when the repo is created and keeps
|
|
its development codename while the plugin gets a product name. `CODENAME` records it so it
|
|
can be kept out of user-facing text.
|
|
|
|
Adding a site the template doesn't ship is one row in `branding-sites.tsv` with owner
|
|
`custom` — no shell to edit, and it survives regeneration. Rows the helper owns are rewritten
|
|
on each rename; custom rows are carried through and value-substituted like any other file.
|
|
Both files can be hand-edited; the helper is what keeps them agreeing, so run the checker
|
|
after if you do.
|
|
|
|
## Build
|
|
|
|
```bash
|
|
# Install Rust via rustup: https://rustup.rs
|
|
# No VST3 SDK needed — NIH-plug bundles its own bindings.
|
|
|
|
# Build + bundle the CLAP (default)
|
|
cargo xtask bundle nih_plug_template --release
|
|
|
|
# Add the VST3 as well — see the licensing note below before you do this
|
|
cargo xtask bundle nih_plug_template --release --features vst3
|
|
```
|
|
|
|
**Output:** `target/bundled/<name>.clap` (and `<name>.vst3` with `--features vst3`), where
|
|
`<name>` is the `name` set in `bundler.toml` — **not** the crate name. Out of the box that
|
|
means `target/bundled/Plugin Name.clap`.
|
|
|
|
On Linux the egui editor needs the GUI development headers:
|
|
|
|
```bash
|
|
sudo apt install pkg-config libgl-dev libx11-dev libxcursor-dev libxcb1-dev \
|
|
libxcb-icccm4-dev libxcb-dri2-0-dev libx11-xcb-dev libxkbcommon-dev libxkbcommon-x11-dev
|
|
```
|
|
|
|
## Deploy
|
|
|
|
### FL Studio
|
|
|
|
Build with `--features vst3` first if you want the VST3 (see the licensing note below).
|
|
|
|
FL Studio scans `C:\Program Files\Common Files\VST3` by default, **ignores directory junctions**
|
|
(so a symlinked bundle is invisible to its scanner), and caches failed scans.
|
|
|
|
1. Copy the `.vst3` bundle to a folder FL scans (e.g. `C:\Program Files\Common Files\VST3`).
|
|
2. Copy the `.clap` bundle to a folder FL scans (e.g. `C:\Program Files\Common Files\CLAP`).
|
|
3. In FL Studio: **Options → Manage plugins → tick "Rescan previously failed plugins" → Find installed plugins**, then search for your plugin name (set in `bundler.toml`).
|
|
|
|
### Linux
|
|
|
|
```bash
|
|
# CLAP
|
|
cp -r "target/bundled/Plugin Name.clap" ~/.clap/
|
|
|
|
# VST3 (only exists if you built with --features vst3)
|
|
cp -r "target/bundled/Plugin Name.vst3" ~/.vst3/
|
|
|
|
# Or globally:
|
|
# sudo cp -r "target/bundled/Plugin Name.clap" /usr/lib/clap/
|
|
# sudo cp -r "target/bundled/Plugin Name.vst3" /usr/lib/vst3/
|
|
```
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
Cargo.toml # Package deps + the opt-in `vst3` feature — NIH-plug pinned to codename-206's rev
|
|
bundler.toml # Bundle name for `cargo xtask bundle`
|
|
src/lib.rs # Plugin skeleton — params, DSP, and egui editor, with one example gain param
|
|
xtask/ # Bundler xtask (cargo xtask bundle)
|
|
.cargo/config.toml # xtask alias
|
|
tools/ # Branding toolkit — see Renaming
|
|
.gitattributes # Pins *.sh to LF so Windows checkouts keep a valid shebang
|
|
.github/workflows/ # CI: branding check, both feature sets build, test, bundle
|
|
```
|
|
|
|
## CI
|
|
|
|
`.github/workflows/ci.yaml` gates pushes and PRs on the branding check, a build of both
|
|
feature sets, `cargo test`, and a bundle — enough to catch a rename that only half-landed,
|
|
a `cfg(feature = "vst3")` gate that stopped compiling, or a `bundler.toml` that no longer
|
|
matches the crate name.
|
|
|
|
It targets `runs-on: rust-audio`, an **org-scoped** runner label. A plugin cloned into a
|
|
different org will not see that runner and its jobs will queue indefinitely rather than fail
|
|
— register a runner advertising the label for that org, or change `runs-on`.
|
|
|
|
Deliberately not covered: plugin performance. Realtime cost can only be measured meaningfully
|
|
against a real audio deadline, and `Plugin::process()` cannot be called from a test at all
|
|
(`ProcessContext` requires a `Transport`, whose constructor and fields are `pub(crate)` to
|
|
nih-plug). Benchmark your DSP types directly if you need it.
|
|
|
|
## License
|
|
|
|
**GPL-3.0-or-later.** See `LICENSE` for the terms and `NOTICE` for third-party attribution.
|
|
|
|
Plugins cloned from this template inherit GPL-3.0-or-later, because the template's own code
|
|
is under it. If you need a permissive plugin, this is the wrong starting point — write the
|
|
skeleton yourself against NIH-plug directly, which is ISC.
|
|
|
|
Parts of `src/lib.rs` derive from Robbert van der Helm's NIH-plug cookiecutter template and
|
|
`gain` example, both ISC. ISC is GPL-compatible, so that notice is retained in `NOTICE`
|
|
rather than superseded.
|
|
|
|
## VST3 and Steinberg
|
|
|
|
`vst3-sys`, which `nih_export_vst3!()` needs, is GPLv3-or-later — chosen by its own authors,
|
|
who describe their port as a derivative of Steinberg's SDK that "was not made in isolation."
|
|
That obligation is what makes a distributed VST3 build GPL-licensed, and it holds regardless
|
|
of any question about Steinberg's own rights. Being GPL-3.0-or-later already, this template
|
|
satisfies it.
|
|
|
|
No Steinberg SDK source is vendored or redistributed here, so there is nothing to accept at
|
|
build time. Separately from copyright, **"VST" is a trademark of Steinberg Media Technologies
|
|
GmbH**; describing a distributed product as a VST plugin is governed by their trademark terms.
|
|
If you intend to distribute commercially under that name, that is the part worth legal advice.
|
|
|
|
CLAP carries none of this — no GPL dependency and no trademark encumbrance.
|