Check and Windows preview / check (anvil) (push) Successful in 2m22s
Check and Windows preview / check (crucible) (push) Successful in 1m55s
Check and Windows preview / check (knurl) (push) Successful in 1m42s
Check and Windows preview / check (lathe) (push) Successful in 1m41s
Check and Windows preview / check (patina) (push) Successful in 2m43s
Check and Windows preview / check (planish) (push) Successful in 2m20s
Check and Windows preview / check (rivet) (push) Successful in 1m42s
Check and Windows preview / check (temper) (push) Successful in 1m39s
Check and Windows preview / check (weld) (push) Successful in 2m40s
Check and Windows preview / previews (push) Successful in 1m37s
Check and Windows preview / windows (push) Successful in 2m34s
Independent plugin release / publish (push) Successful in 9s
Independent plugin release / plan (push) Successful in 7s
Independent plugin release / build (push) Successful in 3m8s
- Crucible 0.3.0: Keep takes. - Patina 0.4.0: Tone guard and the Bass line preset. - Planish 0.1.0 is already at its first version. - Update the current version in every plugin guide; several still named an older release.
153 lines
8.6 KiB
Markdown
153 lines
8.6 KiB
Markdown
# Rivet
|
||
|
||
Rivet joins short motifs into connected musical phrases, like a fastener joining
|
||
individual pieces. Formerly **Motif Storyboard**; current version **0.5.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.
|
||
|
||

|
||
|
||
## 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](fl-studio.md).
|
||
|
||
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](theory/README.md).
|
||
|
||
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:
|
||
|
||
```sh
|
||
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](../development.md) for checks, dependencies, architecture, and
|
||
[releases](../releases.md) for independent release tags.
|
||
|
||
## Design and references
|
||
|
||
[Theory findings and implemented design](theory/README.md) connect the research
|
||
to practical controls. [Implementation plan](implementation.md) records the musical and technical
|
||
choices. [Devath reference measurements](research/devath.md) 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.
|