Files
bass-foundry/docs/rivet
nvrl 444b9d93da
Check and Windows preview / check (anvil) (push) Successful in 2m11s
Check and Windows preview / check (crucible) (push) Successful in 1m43s
Check and Windows preview / check (knurl) (push) Successful in 1m37s
Check and Windows preview / check (lathe) (push) Successful in 1m39s
Check and Windows preview / check (patina) (push) Successful in 2m25s
Check and Windows preview / check (rivet) (push) Successful in 1m50s
Check and Windows preview / check (temper) (push) Successful in 1m45s
Check and Windows preview / check (weld) (push) Successful in 3m9s
Check and Windows preview / previews (push) Successful in 1m50s
Check and Windows preview / windows (push) Successful in 2m46s
Independent plugin release / publish (push) Successful in 9s
Independent plugin release / plan (push) Successful in 8s
Independent plugin release / build (push) Successful in 2m10s
feat!: change CLAP IDs to io.narl.bass-foundry.<product>, release all plugins
The io.storyboard.* IDs were a leftover from Motif Storyboard and named
old functions (motif, sub-anchor, layer-align) instead of the products.
Every plugin now uses io.narl.bass-foundry.<product>, and IDs are frozen
from here on, even across renames.

Hosts see the new IDs as different plugins: delete the old .clap files,
rescan, and add the plugins again in existing projects. Rivet session
JSON and Patina parameter IDs are unchanged.

- Rivet 0.5.0, Patina 0.3.0; Anvil, Crucible, Lathe, Temper, Weld and
  Knurl 0.2.0.
- Docs, changelogs and the release table describe the change.
- Website: upgrade note in the Install section.
2026-11-06 11:51:11 +01:00
..
2026-11-05 23:58:00 +01:00

Rivet

Rivet joins short motifs into connected musical phrases, like a fastener joining individual pieces. Formerly Motif Storyboard; current version 0.4.0.

A Rust composition companion for a producer developing their own melodic ideas. Version 0.4 is built around a storyboard: a timeline of blocks that tell one short idea as a story. Each block has a role (statement, answer, departure, return, rest) and a length; Rivet writes each block from your motif and joins the seams so the result plays as one phrase.

Rivet editor

Write with it

  1. Bring in a motif. Import a monophonic MIDI clip of one to eight bars, or press Demo. The bin on the left holds up to 16 motifs. When the bin is empty, the file's time signature and tempo become the session's.
  2. Lock what defines it. Select the motif; the big roll shows it. Click notes to lock the whole note, its pitch only, or its rhythm only. Locked notes survive into every block.
  3. Lay out the story. A new motif starts as an arc: statement, answer, departure, return. Build from this motif applies other templates (question and answer, breathe with rests, build with two departures). Add block inserts any role after the selected block. Blocks are 1–8 bars; a storyboard holds up to 32 bars in 2/4, 3/4, 4/4, 5/4, 6/8, 7/8 or 12/8.
  4. Shape each block. Click a block in the timeline or the roll. The inspector on the right sets its role, motif, length and how it hands over to the next block. It also lists up to three options written for that role; click one to use it, or Audition it alone. Another take writes three new options for that block only. Keep freezes a block so later changes to key, reach or harmony leave it alone.
  5. Join the seams. Click the small circle between two blocks to cycle its join: Cut (as written), Breath (the last note ends early to leave air), Pickup (two short notes lead into the next block) or Approach (the last note steps next to the next block's first note).
  6. Play and export. Play (or Space) loops the whole storyboard. While it plays, edits are heard from the next bar line. Export MIDI… writes the storyboard to drag into FL Studio. Undo/Redo (Ctrl+Z, Ctrl+Shift+Z) step back through the last 32 edits.

The tabs under the roll hold the less frequent settings:

  • Generation: reach (how far options move from the motif), keep groove, keep contour, a settled or open ending for the last block, the register, the seed, and the audition output.
  • Harmony: import a chord or bass MIDI track, or start from an editable progression. Harmony guides strong notes and arrivals; audition plays the melody only. Imported voicings keep their pitch classes; Rivet does not guess chord names.
  • Sound roles: split the phrase between two synth parts (see below).

Options come from eight transformation families: displaced groove, fragment, room to breathe, short call and long answer, familiar return, changed ending, higher peak and held back. Each role prefers the families that fit it, and the statement always offers the motif as written. Options are deterministic for a given seed, take and position; they are suggestions, not promises of an emotion.

Audition and commit

Internal voice provides two simple reference voices. MIDI output sends notes and optional CC curves on the configured channels to routed external synths and mutes the internal voice. Solo defaults to channel 1. Looping is optional and enabled for new sessions. When audition is already running, another selection waits until the next bar line. Note-offs precede repeated same-pitch note-ons; stop, replacement, loop boundaries, reset, restoration, and mode changes release active output notes on the processing thread. Queued auditions are canceled by Stop.

Host tempo follows tempo, with the local BPM as fallback. It does not align the initial audition to the DAW's song position: start it at the desired point in the project loop. Host start never initiates audition; host stop silences it. Exported MIDI is in musical ticks and carries the local BPM as its tempo event. Native OS MIDI drag-out is not implemented; export files to commit a phrase. See FL Studio setup and acceptance checks.

Open Sound roles to pass existing notes between two patches: alternate events, alternate question/answer bars, or divide strong beats from replies. These modes distribute a monophonic phrase rather than generate a second independent melody. Assign separate MIDI channels and explicitly map each optional CC lane to a synth macro. Curves are normalized over the whole phrase, with editable points and rise/fall/arch/pulse starting shapes. Names describe your intent; the plugin does not infer feelings.

Patch profiles set register offset, gate, velocity, and estimated attack/release. MIDI audition uses a short warmup for early triggers. Re-audition after a tempo change to recompile millisecond offsets. Compensation estimates trigger timing; it does not measure patch latency or model its sound. Default export retains musical positions; compensated export adds one bar of lead-in. Role tracks carry names, channels, and CC curves. Stop resets mapped controllers to their chosen stop values. Internal audition does not reproduce mapped timbre. See the theory and producer workflow.

Sessions save inside the host project and can be saved/loaded as JSON. Sessions from versions 0.1–0.3 open as a storyboard: the old source motif becomes the first motif in the bin, a kept phrase and saved gestures follow it, and an eight-bar phrase becomes an arc (a four-bar phrase becomes statement and return). Branch history is not carried over; undo replaces it. JSON state is bounded at 16 MiB. File export/save refuses to silently overwrite an existing file.

Build and offline preview

Run from the repository root:

cargo run -p rivet-preview -- demo /tmp/rivet-demo
cargo run -p rivet-preview -- develop motif.mid 0 /tmp/phrase
cargo run -p rivet-preview -- develop motif.mid 0 /tmp/with-chords chords.mid 0
cargo run -p rivet-preview -- session saved-session.json /tmp/rendered-session
python scripts/build-plugin.py --plugin rivet --target x86_64-pc-windows-gnu

The preview builds an arc storyboard from the motif and writes the motif and storyboard as MIDI, a session JSON, a WAV render using the same engine as the plugin, and a table of blocks with their chosen options. Track indices exclude conductor-only tracks and start at zero. Optional harmony arguments accept a separate polyphonic MIDI file. See development for checks, dependencies, architecture, and releases for independent release tags.

Design and references

Theory findings and implemented design connect the research to practical controls. Implementation plan records the musical and technical choices. Devath reference measurements document analysis of the seven supplied FLACs, with reproducible plots and explicit limits. Original reference audio is excluded from source distribution.

  • rivet-core: meters, motifs, the storyboard (blocks, roles, joins, templates), role-aware option writing, harmonic context, session migration, and bounded playback clips.
  • rivet-midi: bounded SMF 0/1 import/export. Rejects SMPTE, unsupported meters, incomplete notes, and timing not exactly representable at 960 PPQ. Reports ignored controllers/pitch bends. Polyphonic tracks work as harmonic context; motif import requires monophony.
  • rivet-app: worker jobs, revision handling, bounded queues, sample-accurate preview/MIDI scheduling, and Windows/Zenity dialogs.
  • rivet: CLAP wrapper, host persistence, editor, and standalone.

Tests cover musical constraints/diversity, harmony, every meter, storyboard limits, joins, kept blocks, legacy state migration, MIDI round trips, sample timing, looping, queued switches, note releases, and zero allocation/deallocation during rendering and queued clip replacement. The CLAP ABI test verifies discovery, note-output port exposure, state restoration, and silent startup at several sample rates and buffer sizes.

Existing projects

The CLAP ID is now io.narl.bass-foundry.rivet (earlier builds used io.storyboard.motif). Hosts treat Rivet as a new plugin, so add it again in projects saved with an earlier build. The story-session-v1 host field and the session JSON format are preserved, so saved sessions still open. Delete the old plugin file, place Rivet.clap, and rescan FL Studio.