One note tracker reads the input and Lathe, Temper and Knurl follow it as modules, in any of six orders, each on or off. Every module gets the tracker's events delayed by the latency of the modules in front of it. - One module alone matches its standalone plugin sample for sample in every order. - With Knurl's sub-octave pattern in front, Lathe in Forge follows the played note; a standalone Lathe after Knurl reads it an octave low. - About 72 ms latency against about 206 ms for the three in a row. The module pages reuse the standalone editors. Register Forge in the catalog, CI, README, docs and website, and drop the built Ballast from the site's idea list.
224 lines
13 KiB
Markdown
224 lines
13 KiB
Markdown
# Bass Foundry
|
||
|
||
**Rust CLAP plugins for writing, designing and mixing bass music in FL Studio.**
|
||
|
||
Website: [fl.narl.io](https://fl.narl.io)
|
||
|
||

|
||

|
||

|
||

|
||

|
||
|
||
Riddim and dubstep basses change patch on every switch-up. Fixed-frequency tools
|
||
then act differently on every note: the sub jumps, an EQ cut lands on another
|
||
harmonic, the distortion gets fizzy on one note and dull on the next. Most plugins
|
||
in this suite follow the played note, so a setting means the same thing on every
|
||
note. Each plugin is a separate product with its own version, guide and release.
|
||
|
||
## Plugins
|
||
|
||
| Plugin | What it does | Load it on | Latency | Docs |
|
||
| --- | --- | --- | --- | --- |
|
||
| **[Rivet](docs/rivet/README.md)** | Tells a MIDI motif as a story on a storyboard of roles and joins the seams into one phrase | Channel Rack | none | [Guide](docs/rivet/README.md) · [Changes](docs/rivet/CHANGELOG.md) |
|
||
| **[Patina](docs/patina/README.md)** | Smooths harsh, ringing resonances while plain harmonic tone and transients pass | Any mixer track | one analysis frame | [Guide](docs/patina/README.md) · [Changes](docs/patina/CHANGELOG.md) |
|
||
| **[Anvil](docs/anvil/README.md)** | Replaces the sub with a clean mono sine: one level and one start phase on every note, locked to the kick | Bass bus, kick as sidechain | 80 ms | [Guide](docs/anvil/README.md) · [Changes](docs/anvil/CHANGELOG.md) |
|
||
| **[Crucible](docs/crucible/README.md)** | Records the last bars all the time, then slices, cleans and exports key-labelled samples with one button | End of the chain to resample | none | [Guide](docs/crucible/README.md) · [Changes](docs/crucible/CHANGELOG.md) |
|
||
| **[Lathe](docs/lathe/README.md)** | EQ with one band on each harmonic of the played note: a cut on harmonic 3 stays on harmonic 3 | Bass, after the synth | ≈ 67 ms | [Guide](docs/lathe/README.md) · [Changes](docs/lathe/CHANGELOG.md) |
|
||
| **[Temper](docs/temper/README.md)** | Distortion that drives every note the same: level and tone filters follow the note | Bass | ≈ 67 ms + oversampling | [Guide](docs/temper/README.md) · [Changes](docs/temper/CHANGELOG.md) |
|
||
| **[Weld](docs/weld/README.md)** | Phase-aligns one bass layer to another layer through the sidechain, so the layers sum in the mids instead of cancelling | One layer's track, other layer as sidechain | none | [Guide](docs/weld/README.md) · [Changes](docs/weld/CHANGELOG.md) |
|
||
| **[Knurl](docs/knurl/README.md)** | Gives every cycle of the note its own level, timing and grit: in-tune subharmonic growl, rattle and fry on every note | Bass | ≈ 71 ms | [Guide](docs/knurl/README.md) · [Changes](docs/knurl/CHANGELOG.md) |
|
||
| **[Planish](docs/planish/README.md)** | Upward and downward compression (OTT) in bands set by harmonic number: band 2 holds harmonics 2–4 on every note | Bass | ≈ 67 ms | [Guide](docs/planish/README.md) · [Changes](docs/planish/CHANGELOG.md) |
|
||
| **[Braze](docs/braze/README.md)** | Adds a detuned copy whose beating stays on the tempo grid on every note: reese movement in time | Bass | ≈ 67 ms | [Guide](docs/braze/README.md) · [Changes](docs/braze/CHANGELOG.md) |
|
||
| **[Quench](docs/quench/README.md)** | Clips the top as hard as you like while the sub passes clean: the low end keeps its level under a loud master | Master or bass bus, last | < 1 ms (oversampling) | [Guide](docs/quench/README.md) · [Changes](docs/quench/CHANGELOG.md) |
|
||
| **[Ballast](docs/ballast/README.md)** | Levels every note or switch-up to one loudness with a single gain each: no compression inside a part | Bass, after the sound design | ≈ 172 ms | [Guide](docs/ballast/README.md) · [Changes](docs/ballast/CHANGELOG.md) |
|
||
| **[Forge](docs/forge/README.md)** | Lathe, Temper and Knurl as one channel strip behind one note tracker, in any order: one lookahead, and no module hears Knurl's sub-octave | Bass, after the synth | ≈ 72 ms | [Guide](docs/forge/README.md) · [Changes](docs/forge/CHANGELOG.md) |
|
||
|
||
Plugins with latency report it to the host, so FL Studio compensates the delay.
|
||
Anvil, Lathe, Temper, Knurl, Planish, Braze, Ballast and Forge detect notes from the audio. They can also take the
|
||
pitch from MIDI. The order of the plugins matters: see [Signal chain](docs/chain.md).
|
||
|
||
The names come from metalwork: a **rivet** joins pieces; **patina** changes a
|
||
surface and keeps its character; an **anvil** is the solid base that everything
|
||
is struck on; a **crucible** melts material down to be cast again; a **lathe**
|
||
turns a piece so each cut lands the same all the way round; **tempering** sets
|
||
how hard and how tough a metal ends up; a **weld** fuses two pieces into one;
|
||
**knurling** presses a ridged grip pattern into a surface; **planishing**
|
||
hammers a surface smooth and even; **brazing** joins two metals with a filler; **quenching** hardens hot metal in one
|
||
fast step; **ballast** is the weight that keeps a ship level; a **forge** is the hearth where
|
||
every piece is heated and worked.
|
||
The suite is the **foundry**: the workshop where the bass gets cast, shaped and
|
||
finished. Hosts list the plugins under the vendor name *Bass Foundry*.
|
||
|
||
## Editors
|
||
|
||
<table>
|
||
<tr>
|
||
<td width="50%"><a href="docs/rivet/README.md"><img src="docs/rivet/assets/editor.png" alt="Rivet editor"></a><br><b>Rivet</b>: storyboard composition</td>
|
||
<td width="50%"><a href="docs/patina/README.md"><img src="docs/patina/assets/editor.png" alt="Patina editor"></a><br><b>Patina</b>: resonance smoothing</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="docs/anvil/README.md"><img src="docs/anvil/assets/editor.png" alt="Anvil editor"></a><br><b>Anvil</b>: sub anchor</td>
|
||
<td><a href="docs/crucible/README.md"><img src="docs/crucible/assets/editor.png" alt="Crucible editor"></a><br><b>Crucible</b>: resampler</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="docs/lathe/README.md"><img src="docs/lathe/assets/editor.png" alt="Lathe editor"></a><br><b>Lathe</b>: harmonic EQ</td>
|
||
<td><a href="docs/temper/README.md"><img src="docs/temper/assets/editor.png" alt="Temper editor"></a><br><b>Temper</b>: tracked distortion</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="docs/weld/README.md"><img src="docs/weld/assets/editor.png" alt="Weld editor"></a><br><b>Weld</b>: layer phase alignment</td>
|
||
<td><a href="docs/knurl/README.md"><img src="docs/knurl/assets/editor.png" alt="Knurl editor"></a><br><b>Knurl</b>: cycle texture</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="docs/planish/README.md"><img src="docs/planish/assets/editor.png" alt="Planish editor"></a><br><b>Planish</b>: harmonic OTT</td>
|
||
<td><a href="docs/braze/README.md"><img src="docs/braze/assets/editor.png" alt="Braze editor"></a><br><b>Braze</b>: tempo-locked beating</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="docs/quench/README.md"><img src="docs/quench/assets/editor.png" alt="Quench editor"></a><br><b>Quench</b>: sub-safe clipper</td>
|
||
<td><a href="docs/ballast/README.md"><img src="docs/ballast/assets/editor.png" alt="Ballast editor"></a><br><b>Ballast</b>: switch-up leveller</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="docs/forge/README.md"><img src="docs/forge/assets/editor.png" alt="Forge editor"></a><br><b>Forge</b>: tracked channel strip</td>
|
||
</tr>
|
||
</table>
|
||
|
||
The previews are rendered headless from the real editor code by
|
||
`tools/editor-check`, so they match what the plugin draws.
|
||
|
||
## Install
|
||
|
||
1. Open the plugin's release on Gitea. Every plugin has its own release tag,
|
||
for example `lathe-v0.1.0`.
|
||
2. Download `<plugin>-windows-x86_64-<version>.zip`. You can check it against
|
||
the `.zip.sha256` file next to it.
|
||
3. Extract the ZIP and copy the `.clap` file into:
|
||
|
||
```text
|
||
C:\Program Files\Common Files\CLAP\
|
||
```
|
||
|
||
4. In FL Studio 2024.1 or newer, open **Options → Manage plugins**, enable
|
||
**Verify plugins**, and click **Find installed plugins**.
|
||
5. Load Rivet in the Channel Rack. Load the other plugins in mixer effect slots.
|
||
|
||
Keep the font license (`licenses/`) with redistributed packages.
|
||
|
||
Every branch and pull-request pipeline also builds a combined Windows preview
|
||
ZIP, `bass-foundry-windows-x86_64.zip`, in its **windows** job. Use it to test unreleased changes.
|
||
|
||
> **Upgrading from an earlier build?** The CLAP IDs changed from
|
||
> `io.storyboard.*` to `io.narl.bass-foundry.<product>`, so hosts see each
|
||
> plugin as new. Delete the old `.clap` files, rescan, and add the plugins again
|
||
> in existing projects. Motif Storyboard and Resonance Compressor are now Rivet
|
||
> and Patina, and the vendor is "Bass Foundry".
|
||
|
||
## Build from source
|
||
|
||
You need:
|
||
|
||
- Rust stable and Python 3.11 or newer.
|
||
- For the Windows target on Linux: the `x86_64-pc-windows-gnu` Rust target and
|
||
MinGW (`x86_64-w64-mingw32-gcc`).
|
||
- For the native wrapper tests on Linux: pkg-config and the X11/XCB and OpenGL
|
||
development headers. Rivet's optional standalone also needs ALSA/JACK headers.
|
||
|
||
```sh
|
||
# Tests for all engines, editors and tools
|
||
cargo test --workspace --locked
|
||
|
||
# Formatting, tests and lints for one product, including its CLAP wrapper
|
||
python3 scripts/check-plugin.py --plugin lathe
|
||
|
||
# Windows build, then the ZIPs (all plugins, or --plugin <name>)
|
||
python3 scripts/build-plugin.py --plugin lathe --target x86_64-pc-windows-gnu
|
||
python3 scripts/package-windows.py
|
||
|
||
# Headless editor preview
|
||
cargo run --manifest-path tools/editor-check/Cargo.toml -- target/editor/lathe.png --plugin lathe
|
||
```
|
||
|
||
Plugin names for the scripts are `rivet`, `patina`, `anvil`, `crucible`,
|
||
`lathe`, `temper`, `weld`, `knurl`, `planish`, `braze`, `quench`, `ballast` and `forge`. Builds go to `dist/<target>/<Name>.clap`. No
|
||
script installs anything into a DAW. [Development and testing](docs/development.md)
|
||
has the full command list.
|
||
|
||
## Quality checks
|
||
|
||
Every push and pull request runs these checks in CI
|
||
(`.gitea/workflows/check.yml`):
|
||
|
||
- **Engine tests.** Every engine has tests for its signal behaviour and latency
|
||
against measured targets.
|
||
- **Realtime safety.** Every audio effect has a test that fails if the audio
|
||
thread allocates memory.
|
||
- **Host tests.** Each wrapper is loaded through the exported CLAP ABI, without
|
||
a DAW. The tests check the ports, the reported latency, state save and load,
|
||
and processing at 44.1, 48 and 96 kHz.
|
||
- **Lints.** `rustfmt` and `clippy -D warnings` for every package and wrapper.
|
||
- **Editor previews.** All editors are rendered headless and kept as CI
|
||
artifacts.
|
||
- **Windows builds.** Every `.clap` is cross-compiled and packaged.
|
||
|
||
Release ZIPs are deterministic and contain checksums for every file. Things CI
|
||
cannot check stay manual: FL Studio routing, native window behaviour, and
|
||
listening.
|
||
|
||
## Repository
|
||
|
||
```text
|
||
plugins/
|
||
catalog.json Product metadata for the build, check and release tools
|
||
rivet/ core · midi · app · preview · plugin
|
||
patina/ core · ui · render · plugin
|
||
anvil/ core · ui · plugin
|
||
crucible/ core · ui · plugin
|
||
lathe/ core · ui · plugin
|
||
temper/ core · ui · plugin
|
||
weld/ core · ui · plugin
|
||
knurl/ core · ui · plugin
|
||
planish/ core · ui · plugin
|
||
braze/ core · ui · plugin
|
||
quench/ core · ui · plugin
|
||
ballast/ core · ui · plugin
|
||
forge/ core · ui · plugin
|
||
common/
|
||
ui/ Shared look: palette, embedded fonts, knobs, panels, meters
|
||
dsp/ Shared filters, pitch tracking, oversampling, meters, WAV files
|
||
tools/editor-check/ Headless editor renderer for visual QA
|
||
scripts/ Build, check, packaging, release and analysis tools
|
||
docs/<plugin>/ Guide, changelog and editor preview for each plugin
|
||
docs/ideas.md Problems in bass-music production and the plugin ideas for them
|
||
references/ Local reference audio; never distributed
|
||
website/ The fl.narl.io site: Astro, nginx container, reads catalog and previews
|
||
```
|
||
|
||
Each product follows the same split:
|
||
|
||
- `core` is the engine. It is plain Rust, does not depend on a plugin SDK, and
|
||
is tested on its own.
|
||
- `ui` is the egui editor, built on `common-ui`.
|
||
- `plugin` is the CLAP wrapper on a pinned NIH-plug revision.
|
||
|
||
The root Cargo workspace holds the engines, editors and tools. Each wrapper is
|
||
its own workspace with its own lockfile, so SDK dependencies stay separate.
|
||
`plugins/catalog.json` is the single list of products. The scripts and CI read
|
||
it, so adding a plugin starts there.
|
||
|
||
## Releases
|
||
|
||
Each plugin is versioned and released on its own. Its version is the `version`
|
||
in `plugins/<plugin>/plugin/Cargo.toml`. A tag `<plugin>-v<version>`, for example
|
||
`temper-v0.1.0`, starts the release workflow. The workflow builds, checks and
|
||
publishes that plugin's Windows ZIP to a Gitea release. See
|
||
[docs/releases.md](docs/releases.md).
|
||
|
||
## Roadmap
|
||
|
||
[docs/ideas.md](docs/ideas.md) lists the production problems the suite targets,
|
||
the released plugins and the ideas for the next ones.
|
||
|
||
## License
|
||
|
||
ISC, see [LICENSE](LICENSE). The embedded Roboto Condensed fonts are under the
|
||
SIL Open Font License ([OFL.txt](plugins/common/ui/assets/OFL.txt)).
|