mikkeli 454e80bcd5 fix: separate cargo AUTHOR from Plugin::VENDOR, and rewrite sites precisely
Integrating this toolkit into Bari 206 surfaced two things the template
could not express, both of which would have produced wrong output.

**AUTHOR.** Cargo's authors entry names whoever wrote the plugin; VENDOR
is the organisation that ships it. Bari 206 separates them - "Mikkeli
Matlock" authored it, "Novoyuuparosk" vends it - while this template only
happened to work because its two placeholders were identical. AUTHOR is
now its own value defaulting to VENDOR, pinned in branding.conf only when
it actually differs so the file keeps showing that it derives.

**Site-directed rewriting.** That default is exactly what broke the
rename: with AUTHOR defaulting to VENDOR, both fields hold the same old
string, and a global value substitution cannot tell which occurrence
belongs to which. Renaming the vendor put the org name in the authors
field.

The site table already holds the old resolved literal for every site it
owns, and regeneration produces the new one, so rewriting by that pair is
unambiguous. brand-plugin.sh now does that first and falls back to value
substitution for everything the table does not enumerate - doc comments,
use sites in other modules, prose. The order matters and is load-bearing:
run the global pass first and it consumes the old literals, leaving the
precise pass nothing to match.

Verified on the ambiguous case: renaming with a distinct author now puts
the person in Cargo.toml's authors and the org in Plugin::VENDOR, where
the previous implementation put the org in both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WHt4DykxXaQnyaJfyS8KCn
2026-09-02 16:36:34 +09:00
2026-08-29 10:25:02 +09:00

NIH-plug Template

Minimal scaffold for new NIH-plug audio plugins (Rust + CLAP/VST3 + egui).

Based on patterns from musiikki/codename-206.

Quick Start

# 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
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

# 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:

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

# 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.

S
Description
Minimal NIH-plug audio plugin scaffold (Rust + CLAP/VST3 + egui)
Readme GPL-3.0 219 KiB
Languages
Shell 83.7%
Rust 16.3%