Merge branch 'fix/template-api-and-tooling'
Make the template actually usable: it did not compile, its build docs described output paths the bundler never produces, and its declared licence conflicted with the artifact its default build emitted. Also brings over codename-206's branding checker, extended so that renaming — the first thing anyone does with a template — verifies itself.
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
# Shell scripts must keep LF endings. core.autocrlf=true on Windows would
|
||||||
|
# otherwise check them out CRLF, which a strict POSIX sh (dash, CI containers)
|
||||||
|
# rejects at the shebang.
|
||||||
|
*.sh text eol=lf
|
||||||
Generated
+2534
File diff suppressed because it is too large
Load Diff
+10
-1
@@ -10,6 +10,14 @@ license = "Apache-2.0"
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["xtask"]
|
members = ["xtask"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
# CLAP only by default. NIH-plug's VST3 bindings (vst3-sys) are GPLv3, so linking
|
||||||
|
# them makes the resulting binary GPLv3 regardless of what this package declares.
|
||||||
|
# Opt in explicitly — and relicense this crate — if you intend to ship a VST3:
|
||||||
|
# cargo xtask bundle nih_plug_template --release --features vst3
|
||||||
|
default = []
|
||||||
|
vst3 = ["nih_plug/vst3"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
# `cdylib` is the plugin binary; `lib` (rlib) lets `cargo test` link the unit tests.
|
# `cdylib` is the plugin binary; `lib` (rlib) lets `cargo test` link the unit tests.
|
||||||
crate-type = ["cdylib", "lib"]
|
crate-type = ["cdylib", "lib"]
|
||||||
@@ -17,7 +25,8 @@ crate-type = ["cdylib", "lib"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
# Pinned to the same commit as musiikki/codename-206.
|
# Pinned to the same commit as musiikki/codename-206.
|
||||||
# Update the `rev` when you want to update NIH-plug.
|
# Update the `rev` when you want to update NIH-plug.
|
||||||
nih_plug = { git = "https://github.com/robbert-vdh/nih-plug.git", rev = "f36931f7af4646065488a9845d8f8c2f95252c23", features = ["assert_process_allocs"] }
|
# `default-features = false` drops nih_plug's default `vst3` feature; see [features] above.
|
||||||
|
nih_plug = { git = "https://github.com/robbert-vdh/nih-plug.git", rev = "f36931f7af4646065488a9845d8f8c2f95252c23", default-features = false, features = ["assert_process_allocs"] }
|
||||||
nih_plug_egui = { git = "https://github.com/robbert-vdh/nih-plug.git", rev = "f36931f7af4646065488a9845d8f8c2f95252c23" }
|
nih_plug_egui = { git = "https://github.com/robbert-vdh/nih-plug.git", rev = "f36931f7af4646065488a9845d8f8c2f95252c23" }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
|||||||
@@ -13,24 +13,63 @@ cd my-plugin
|
|||||||
|
|
||||||
# Edit: src/lib.rs, Cargo.toml, bundler.toml — replace placeholder values
|
# 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
|
# 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
|
||||||
|
|
||||||
|
Branding is duplicated across `Cargo.toml`, `bundler.toml`, `src/lib.rs` and the docs,
|
||||||
|
and nothing makes those agree — so a half-finished rename builds a bundle under one name
|
||||||
|
while the docs and deploy script look for another.
|
||||||
|
|
||||||
|
`check-branding.sh` declares the identity once at the top and asserts every copy matches:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sh check-branding.sh # exit 0 = consistent, 1 = drift found
|
||||||
|
```
|
||||||
|
|
||||||
|
To rename: edit the canonical block at the top of the script, run it, and fix everything
|
||||||
|
it reports. The placeholder spellings are pre-seeded into its `STALE` list, and each one
|
||||||
|
arms itself as soon as you change the matching canonical value — so leftovers surface
|
||||||
|
without you having to track them. It also asserts `VST3_CLASS_ID` is exactly 16 bytes and
|
||||||
|
that `CLAP_ID` is reverse-DNS safe, both of which are otherwise a compile error or a
|
||||||
|
silent identity change between machines.
|
||||||
|
|
||||||
|
Checks for files the template doesn't ship (e.g. a `deploy.ps1`) report as `skip` and
|
||||||
|
start enforcing once you add them.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install Rust via rustup: https://rustup.rs
|
# Install Rust via rustup: https://rustup.rs
|
||||||
# No VST3 SDK needed — NIH-plug bundles its own bindings.
|
# No VST3 SDK needed — NIH-plug bundles its own bindings.
|
||||||
|
|
||||||
# Build + bundle the VST3 and CLAP
|
# Build + bundle the CLAP (default)
|
||||||
cargo xtask bundle nih_plug_template --release
|
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/nih_plug_template.vst3` and `nih_plug_template.clap`
|
**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
|
## Deploy
|
||||||
|
|
||||||
### FL Studio
|
### 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**
|
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.
|
(so a symlinked bundle is invisible to its scanner), and caches failed scans.
|
||||||
|
|
||||||
@@ -41,25 +80,27 @@ FL Studio scans `C:\Program Files\Common Files\VST3` by default, **ignores direc
|
|||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# VST3
|
|
||||||
cp target/bundled/nih_plug_template.vst3 ~/.vst3/
|
|
||||||
|
|
||||||
# CLAP
|
# CLAP
|
||||||
cp target/bundled/nih_plug_template.clap ~/.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:
|
# Or globally:
|
||||||
# sudo cp target/bundled/nih_plug_template.vst3 /usr/lib/vst3/
|
# sudo cp -r "target/bundled/Plugin Name.clap" /usr/lib/clap/
|
||||||
# sudo cp target/bundled/nih_plug_template.clap /usr/lib/clap/
|
# sudo cp -r "target/bundled/Plugin Name.vst3" /usr/lib/vst3/
|
||||||
```
|
```
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
Cargo.toml # Package deps — NIH-plug pinned to codename-206's rev
|
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`
|
bundler.toml # Bundle name for `cargo xtask bundle`
|
||||||
src/lib.rs # Plugin skeleton — fill in params, DSP, editor
|
src/lib.rs # Plugin skeleton — params, DSP, and egui editor, with one example gain param
|
||||||
xtask/ # Bundler xtask (cargo xtask bundle)
|
xtask/ # Bundler xtask (cargo xtask bundle)
|
||||||
.cargo/config.toml # xtask alias
|
.cargo/config.toml # xtask alias
|
||||||
|
check-branding.sh # Asserts the plugin's identity strings agree everywhere (see Renaming)
|
||||||
|
.gitattributes # Pins *.sh to LF so Windows checkouts keep a valid shebang
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
@@ -68,6 +109,16 @@ Apache-2.0
|
|||||||
|
|
||||||
## VST3 Licensing Note
|
## VST3 Licensing Note
|
||||||
|
|
||||||
You must accept Steinberg's VST3 SDK licence before distributing VST3 binaries.
|
NIH-plug's VST3 bindings (`vst3-sys`) are GPLv3. Linking them makes the resulting binary
|
||||||
NIH-plug's VST3 bindings are GPLv3; if you distribute, the plugin must also be GPLv3
|
GPLv3 regardless of what `Cargo.toml` declares, so **VST3 is behind an opt-in `vst3` feature
|
||||||
(or you need a commercial Steinberg licence). CLAP has no such restriction.
|
and this template builds CLAP only by default.** That keeps the default build consistent with
|
||||||
|
the Apache-2.0 licence in `Cargo.toml`/`LICENSE`.
|
||||||
|
|
||||||
|
If you want to ship a VST3:
|
||||||
|
|
||||||
|
1. Build with `--features vst3`.
|
||||||
|
2. Relicense your plugin as GPLv3 (or obtain a commercial Steinberg licence) and update
|
||||||
|
`Cargo.toml`, `LICENSE`, and the SPDX headers in `src/`.
|
||||||
|
3. Accept Steinberg's VST3 SDK licence.
|
||||||
|
|
||||||
|
CLAP has no such restriction.
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
# Metadata for NIH-plug's `cargo xtask bundle <package>` bundler.
|
# Metadata for NIH-plug's `cargo xtask bundle <package>` bundler.
|
||||||
#
|
#
|
||||||
# [package_name]
|
# [package_name]
|
||||||
# name = "Human Readable Plugin Name" # defaults to <package_name>
|
# name = "Human Readable Bundle Name" # defaults to <package_name>
|
||||||
|
|
||||||
[nih_plug_template]
|
[nih_plug_template]
|
||||||
name = "Plugin Name"
|
name = "Plugin Name"
|
||||||
|
|||||||
Executable
+147
@@ -0,0 +1,147 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# check-branding.sh - verify the plugin's branding/identity strings agree everywhere.
|
||||||
|
#
|
||||||
|
# Plugin branding is duplicated across the build manifest, the bundler manifest,
|
||||||
|
# the source, the docs and (once you add one) the deploy script. Nothing makes
|
||||||
|
# them agree, so a rename drifts silently: the bundle builds under one name and
|
||||||
|
# the deploy script looks for another. This asserts they match, and that the
|
||||||
|
# identity constants are legal.
|
||||||
|
#
|
||||||
|
# In a template this matters more than usual: renaming is the first thing you do
|
||||||
|
# with this repo, and it touches every file below. Run this immediately after,
|
||||||
|
# and the STALE machinery will list whatever you missed.
|
||||||
|
#
|
||||||
|
# Usage: sh check-branding.sh (exit 0 = consistent, 1 = drift found)
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# CANONICAL IDENTITY - the single source of truth. Edit these to rename, then
|
||||||
|
# run this script; every failure it prints is a place still holding the old name.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
PKG="nih_plug_template" # Cargo package name == bundler.toml table key
|
||||||
|
DISPLAY="Plugin Name" # bundle name -> "<DISPLAY>.vst3" / "<DISPLAY>.clap"
|
||||||
|
PLUGIN_NAME="Plugin Name" # Plugin::NAME - what the DAW displays
|
||||||
|
VENDOR="Your Name" # Plugin::VENDOR
|
||||||
|
EMAIL="your@email.com" # Plugin::EMAIL
|
||||||
|
HOMEPAGE="https://github.com/YOUR-USERNAME/YOUR-PLUGIN-NAME" # Cargo homepage -> Plugin::URL
|
||||||
|
CLAP_ID="com.your-domain.plugin-name" # CLAP_ID - stable plugin identity
|
||||||
|
VST3_CLASS_ID="Unique16CharId!!" # VST3_CLASS_ID - stable identity, MUST be 16 bytes
|
||||||
|
|
||||||
|
# Spellings that must no longer appear anywhere in tracked files. Seeded with
|
||||||
|
# this template's own placeholders, so that renaming is self-checking: each entry
|
||||||
|
# is skipped while it is still one of the canonical values above, and becomes a
|
||||||
|
# live check the moment you change that value. Add your own old names here after
|
||||||
|
# a later rename.
|
||||||
|
STALE="nih_plug_template
|
||||||
|
Plugin Name
|
||||||
|
Your Name
|
||||||
|
your@email.com
|
||||||
|
com.your-domain.plugin-name
|
||||||
|
Unique16CharId!!
|
||||||
|
YOUR-USERNAME
|
||||||
|
YOUR-PLUGIN-NAME
|
||||||
|
your-domain"
|
||||||
|
|
||||||
|
set -u
|
||||||
|
cd "$(dirname "$0")" || exit 1
|
||||||
|
|
||||||
|
pass=0; fail=0; skip=0
|
||||||
|
ok() { pass=$((pass+1)); printf ' ok %s\n' "$1"; }
|
||||||
|
bad() { fail=$((fail+1)); printf ' FAIL %s\n' "$1"; [ $# -gt 1 ] && printf ' %s\n' "$2"; return 0; }
|
||||||
|
note() { skip=$((skip+1)); printf ' skip %s\n' "$1"; }
|
||||||
|
|
||||||
|
# has FILE PATTERN -> fixed-string search
|
||||||
|
has() { grep -qF "$2" "$1" 2>/dev/null; }
|
||||||
|
|
||||||
|
check() { # check LABEL FILE LITERAL
|
||||||
|
if [ ! -f "$2" ]; then bad "$1" "missing file: $2"; return; fi
|
||||||
|
if has "$2" "$3"; then ok "$1"; else bad "$1" "$2 does not contain: $3"; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Same as check, but a missing file is "not applicable yet" rather than drift.
|
||||||
|
# The template ships without a deploy script; this starts checking once you add one.
|
||||||
|
opt_check() { # opt_check LABEL FILE LITERAL
|
||||||
|
if [ ! -f "$2" ]; then note "$1 ($2 not present)"; return; fi
|
||||||
|
if has "$2" "$3"; then ok "$1"; else bad "$1" "$2 does not contain: $3"; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
printf 'branding check (canonical: %s / "%s")\n\n' "$PKG" "$DISPLAY"
|
||||||
|
|
||||||
|
printf 'build manifests\n'
|
||||||
|
check "Cargo.toml package name" Cargo.toml "name = \"$PKG\""
|
||||||
|
check "Cargo.toml homepage" Cargo.toml "homepage = \"$HOMEPAGE\""
|
||||||
|
check "bundler.toml table key" bundler.toml "[$PKG]"
|
||||||
|
check "bundler.toml display name" bundler.toml "name = \"$DISPLAY\""
|
||||||
|
|
||||||
|
printf '\nsource identity (src/lib.rs)\n'
|
||||||
|
check "Plugin::NAME" src/lib.rs "const NAME: &'static str = \"$PLUGIN_NAME\""
|
||||||
|
check "Plugin::VENDOR" src/lib.rs "const VENDOR: &'static str = \"$VENDOR\""
|
||||||
|
check "Plugin::EMAIL" src/lib.rs "const EMAIL: &'static str = \"$EMAIL\""
|
||||||
|
check "CLAP_ID" src/lib.rs "const CLAP_ID: &'static str = \"$CLAP_ID\""
|
||||||
|
check "VST3_CLASS_ID" src/lib.rs "const VST3_CLASS_ID: [u8; 16] = *b\"$VST3_CLASS_ID\""
|
||||||
|
|
||||||
|
# Plugin::URL is env!("CARGO_PKG_HOMEPAGE"), so the manifest is the only place
|
||||||
|
# it is spelled. Assert the indirection is still in place rather than hardcoded.
|
||||||
|
check "Plugin::URL from manifest" src/lib.rs "const URL: &'static str = env!(\"CARGO_PKG_HOMEPAGE\")"
|
||||||
|
|
||||||
|
# VST3 class IDs are a fixed 16-byte array. A shorter or longer literal is a
|
||||||
|
# compile error, but it is far clearer to say so here than to read rustc on it.
|
||||||
|
n=$(printf '%s' "$VST3_CLASS_ID" | wc -c | tr -d ' ')
|
||||||
|
if [ "$n" -eq 16 ]; then ok "VST3_CLASS_ID is 16 bytes"
|
||||||
|
else bad "VST3_CLASS_ID is 16 bytes" "got $n bytes: \"$VST3_CLASS_ID\""; fi
|
||||||
|
|
||||||
|
# CLAP IDs are reverse-DNS by convention and are compared verbatim by hosts, so
|
||||||
|
# a stray space or capital is a silent identity change between machines.
|
||||||
|
case "$CLAP_ID" in
|
||||||
|
*\ *|*[A-Z]*) bad "CLAP_ID is reverse-DNS safe" "no spaces or capitals allowed: \"$CLAP_ID\"" ;;
|
||||||
|
*.*) ok "CLAP_ID is reverse-DNS safe" ;;
|
||||||
|
*) bad "CLAP_ID is reverse-DNS safe" "expected dotted reverse-DNS, got: \"$CLAP_ID\"" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
printf '\ndocs\n'
|
||||||
|
opt_check "README bundle command" README.md "cargo xtask bundle $PKG"
|
||||||
|
|
||||||
|
printf '\ndeploy script\n'
|
||||||
|
opt_check "deploy.ps1 default package" deploy.ps1 "\$Package = \"$PKG\""
|
||||||
|
opt_check "deploy.ps1 VST3 bundle" deploy.ps1 "\$vst3Name = \"$DISPLAY.vst3\""
|
||||||
|
opt_check "deploy.ps1 CLAP bundle" deploy.ps1 "\$clapName = \"$DISPLAY.clap\""
|
||||||
|
|
||||||
|
printf '\nstale spellings\n'
|
||||||
|
# Every canonical spelling concatenated, for the substring test below.
|
||||||
|
CANONICAL_BLOB="$PKG
|
||||||
|
$DISPLAY
|
||||||
|
$PLUGIN_NAME
|
||||||
|
$VENDOR
|
||||||
|
$EMAIL
|
||||||
|
$HOMEPAGE
|
||||||
|
$CLAP_ID
|
||||||
|
$VST3_CLASS_ID"
|
||||||
|
# Fed by here-doc rather than a pipe on purpose: a piped `while` runs in a
|
||||||
|
# subshell, and the pass/fail counters it increments would be discarded.
|
||||||
|
while IFS= read -r s; do
|
||||||
|
[ -n "$s" ] || continue
|
||||||
|
|
||||||
|
# Still part of a canonical value? Then it is current branding, not a leftover.
|
||||||
|
# This is what lets the list ship pre-seeded: entries arm themselves as you
|
||||||
|
# rename. Substring, not equality, because placeholders nest - "your-domain"
|
||||||
|
# lives inside the canonical CLAP_ID, and "YOUR-USERNAME" inside the homepage.
|
||||||
|
# Over-skipping is the safe direction: a false FAIL on a name you still use
|
||||||
|
# would train you to ignore this section.
|
||||||
|
case "$CANONICAL_BLOB" in
|
||||||
|
*"$s"*) note "\"$s\" (still canonical)"; continue ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# This script names every placeholder by definition, so it can never be a hit.
|
||||||
|
hits=$(git ls-files 2>/dev/null | while read -r f; do
|
||||||
|
[ "$f" = "check-branding.sh" ] || { [ -f "$f" ] && grep -lF "$s" "$f" 2>/dev/null; }
|
||||||
|
done)
|
||||||
|
if [ -z "$hits" ]; then ok "no occurrences of \"$s\""
|
||||||
|
else bad "no occurrences of \"$s\"" "still present in: $(echo "$hits" | tr '\n' ' ')"; fi
|
||||||
|
done <<STALE_LIST
|
||||||
|
$STALE
|
||||||
|
STALE_LIST
|
||||||
|
|
||||||
|
printf '\n%d passed, %d failed' "$pass" "$fail"
|
||||||
|
[ "$skip" -gt 0 ] && printf ', %d skipped' "$skip"
|
||||||
|
printf '\n'
|
||||||
|
[ "$fail" -eq 0 ] || exit 1
|
||||||
+59
-49
@@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use nih_plug::prelude::*;
|
use nih_plug::prelude::*;
|
||||||
|
use nih_plug_egui::{create_egui_editor, egui, widgets, EguiState};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
// ──────────────────────────────────────────────
|
// ──────────────────────────────────────────────
|
||||||
@@ -8,31 +9,36 @@ use std::sync::Arc;
|
|||||||
// See musiikki/codename-206/src/params.rs for a real example.
|
// See musiikki/codename-206/src/params.rs for a real example.
|
||||||
// ──────────────────────────────────────────────
|
// ──────────────────────────────────────────────
|
||||||
|
|
||||||
struct PluginParams;
|
#[derive(Params)]
|
||||||
|
struct PluginParams {
|
||||||
|
/// Editor window size, persisted with the plugin state.
|
||||||
|
#[persist = "editor-state"]
|
||||||
|
editor_state: Arc<EguiState>,
|
||||||
|
|
||||||
impl Params for PluginParams {
|
/// Example parameter — replace with your own. The `id` is the stable key the host
|
||||||
fn as_slice(&self) -> &[AtomicParam] {
|
/// stores automation against, so never change it once you've shipped.
|
||||||
&[]
|
#[id = "gain"]
|
||||||
}
|
gain: FloatParam,
|
||||||
|
}
|
||||||
|
|
||||||
fn as_mut_slice(&mut self) -> &mut [AtomicParam] {
|
impl Default for PluginParams {
|
||||||
&mut []
|
fn default() -> Self {
|
||||||
}
|
Self {
|
||||||
|
editor_state: EguiState::from_size(320, 160),
|
||||||
fn param_by_id(&self, _id: ParamId) -> Option<&AtomicParam> {
|
gain: FloatParam::new(
|
||||||
None
|
"Gain",
|
||||||
}
|
util::db_to_gain(0.0),
|
||||||
|
FloatRange::Skewed {
|
||||||
fn param_by_id_mut(&mut self, _id: ParamId) -> Option<&mut AtomicParam> {
|
min: util::db_to_gain(-30.0),
|
||||||
None
|
max: util::db_to_gain(30.0),
|
||||||
}
|
factor: FloatRange::gain_skew_factor(-30.0, 30.0),
|
||||||
|
},
|
||||||
fn param_by_name(&self, _name: &str) -> Option<&AtomicParam> {
|
)
|
||||||
None
|
.with_smoother(SmoothingStyle::Logarithmic(50.0))
|
||||||
}
|
.with_unit(" dB")
|
||||||
|
.with_value_to_string(formatters::v2s_f32_gain_to_db(2))
|
||||||
fn param_by_name_mut(&mut self, _name: &str) -> Option<&mut AtomicParam> {
|
.with_string_to_value(formatters::s2v_f32_gain_to_db()),
|
||||||
None
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,18 +46,11 @@ impl Params for PluginParams {
|
|||||||
// Plugin
|
// Plugin
|
||||||
// ──────────────────────────────────────────────
|
// ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
struct MyPlugin {
|
struct MyPlugin {
|
||||||
params: Arc<PluginParams>,
|
params: Arc<PluginParams>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MyPlugin {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
params: Arc::new(PluginParams),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Plugin for MyPlugin {
|
impl Plugin for MyPlugin {
|
||||||
// TODO: Replace these placeholder values with your plugin's metadata.
|
// TODO: Replace these placeholder values with your plugin's metadata.
|
||||||
const NAME: &'static str = "Plugin Name";
|
const NAME: &'static str = "Plugin Name";
|
||||||
@@ -61,20 +60,18 @@ impl Plugin for MyPlugin {
|
|||||||
|
|
||||||
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
const AUDIO_IO_LAYOUTS: &'static [AudioIOLayout] = &[
|
const AUDIO_IO_LAYOUTS: &'static [AudioIOLayout] = &[AudioIOLayout {
|
||||||
AudioIOLayout {
|
main_input_channels: NonZeroU32::new(2),
|
||||||
main_input_channels: NonZeroU32::new(2),
|
main_output_channels: NonZeroU32::new(2),
|
||||||
main_output_channels: NonZeroU32::new(2),
|
..AudioIOLayout::const_default()
|
||||||
..AudioIOLayout::const_default()
|
}];
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const MIDI_INPUT: MidiConfig = MidiConfig::None;
|
const MIDI_INPUT: MidiConfig = MidiConfig::None;
|
||||||
const MIDI_OUTPUT: MidiConfig = MidiConfig::None;
|
const MIDI_OUTPUT: MidiConfig = MidiConfig::None;
|
||||||
|
|
||||||
const SAMPLE_ACCURATE_AUTOMATION: bool = true;
|
const SAMPLE_ACCURATE_AUTOMATION: bool = true;
|
||||||
|
|
||||||
type SysSysexMessage = ();
|
type SysExMessage = ();
|
||||||
type BackgroundTask = ();
|
type BackgroundTask = ();
|
||||||
|
|
||||||
fn params(&self) -> Arc<dyn Params> {
|
fn params(&self) -> Arc<dyn Params> {
|
||||||
@@ -82,9 +79,19 @@ impl Plugin for MyPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn editor(&mut self, _async_executor: AsyncExecutor<Self>) -> Option<Box<dyn Editor>> {
|
fn editor(&mut self, _async_executor: AsyncExecutor<Self>) -> Option<Box<dyn Editor>> {
|
||||||
// TODO: Implement your egui editor here.
|
// TODO: Build your own egui layout here.
|
||||||
// return Some(Box::new(MyEditor::new(self.params.clone())));
|
let params = self.params.clone();
|
||||||
None
|
create_egui_editor(
|
||||||
|
self.params.editor_state.clone(),
|
||||||
|
(),
|
||||||
|
|_, _| {},
|
||||||
|
move |egui_ctx, setter, _state| {
|
||||||
|
egui::CentralPanel::default().show(egui_ctx, |ui| {
|
||||||
|
ui.label("Gain");
|
||||||
|
ui.add(widgets::ParamSlider::for_param(¶ms.gain, setter));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize(
|
fn initialize(
|
||||||
@@ -104,19 +111,21 @@ impl Plugin for MyPlugin {
|
|||||||
_aux: &mut AuxiliaryBuffers,
|
_aux: &mut AuxiliaryBuffers,
|
||||||
_context: &mut impl ProcessContext<Self>,
|
_context: &mut impl ProcessContext<Self>,
|
||||||
) -> ProcessStatus {
|
) -> ProcessStatus {
|
||||||
// TODO: Implement your DSP here.
|
// TODO: Implement your DSP here. This applies the example gain parameter; note that
|
||||||
// This is a passthrough — replace with your audio processing.
|
// the smoother is advanced once per frame so automation doesn't zipper.
|
||||||
for mut frame in buffer.iter_samples() {
|
for mut frame in buffer.iter_samples() {
|
||||||
for ch in 0..frame.len() {
|
let gain = self.params.gain.smoothed.next();
|
||||||
*frame.get_mut(ch).unwrap() = *frame.get(ch).unwrap();
|
for sample in frame.iter_mut() {
|
||||||
|
*sample *= gain;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessStatus::Normal
|
ProcessStatus::Normal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClapPlugin for MyPlugin {
|
impl ClapPlugin for MyPlugin {
|
||||||
// TODO: Replace with a unique CLAP ID, e.g. "com.your-domain.your-plugin-name"
|
// TODO: Replace with a unique CLAP ID in reverse-DNS form, e.g. "com.acme.fuzzbox"
|
||||||
const CLAP_ID: &'static str = "com.your-domain.plugin-name";
|
const CLAP_ID: &'static str = "com.your-domain.plugin-name";
|
||||||
const CLAP_DESCRIPTION: Option<&'static str> = Some("Your plugin description");
|
const CLAP_DESCRIPTION: Option<&'static str> = Some("Your plugin description");
|
||||||
const CLAP_MANUAL_URL: Option<&'static str> = Some(Self::URL);
|
const CLAP_MANUAL_URL: Option<&'static str> = Some(Self::URL);
|
||||||
@@ -124,13 +133,14 @@ impl ClapPlugin for MyPlugin {
|
|||||||
const CLAP_FEATURES: &'static [ClapFeature] = &[ClapFeature::AudioEffect];
|
const CLAP_FEATURES: &'static [ClapFeature] = &[ClapFeature::AudioEffect];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "vst3")]
|
||||||
impl Vst3Plugin for MyPlugin {
|
impl Vst3Plugin for MyPlugin {
|
||||||
// TODO: Replace with a unique 16-byte Class ID.
|
// TODO: Replace with a unique 16-byte Class ID.
|
||||||
// Must be exactly 16 bytes — padded with '!' if shorter.
|
// Must be exactly 16 bytes — padded with '!' if shorter.
|
||||||
const VST3_CLASS_ID: [u8; 16] = *b"Unique16CharId!!";
|
const VST3_CLASS_ID: [u8; 16] = *b"Unique16CharId!!";
|
||||||
const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] =
|
const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = &[Vst3SubCategory::Fx];
|
||||||
&[Vst3SubCategory::Fx];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nih_export_clap!(MyPlugin);
|
nih_export_clap!(MyPlugin);
|
||||||
|
#[cfg(feature = "vst3")]
|
||||||
nih_export_vst3!(MyPlugin);
|
nih_export_vst3!(MyPlugin);
|
||||||
|
|||||||
Reference in New Issue
Block a user