overdub
FirmwareM-VAVE FM-1

fmfm

mpancia/fmfm · ★ 1

Generative music firmware for the M-VAVE FM-1, written in Lean 4 with 313 theorems about it

README from GitHub, GPL-3.0

FORMALFRED

The HOME screen: four lanes, their bars, the key and the chord

Firmware for the M-VAVE FM-1 that makes music on its own. Four lanes of generative rhythm and melody, Braids voices, an FM voice, an 808, a filter, reverb and delay. The sequencer, the voices' arithmetic, the effects and the patch format are Lean 4, and 315 theorems about that code are checked on every build. Alfred, a little pixel-art butler, bows at boot and waits in the corner when you stop. Made for the Vibecheck hackathon.

Hear it now, no hardware: https://mpancia.github.io/fmfm/sim/ (Chrome or Edge for MIDI). The site also has the installer and the pitch deck.

Or build it:

make install && make run

Open http://localhost:8000/, click Power on, and it plays. The simulator is the real firmware (fw/firmware/src/dinghy.c) compiled to WebAssembly: same screen, keys, knobs, audio, MIDI in and out. Tour walks you through the panel in 80 seconds, pressing the buttons itself. It can record what you hear, save the screen as a PNG, and give you a link to the patch you're on (?patch=N). Space is PLAY, PageUp and PageDown change patch, hover a button for what it does. docs/SIMULATOR.md.

To put it on an FM-1: the installer on the site, and docs/FM1.md for the hardware and the backup to take first.

Using it · How it works · What is proved · Prerequisites · Targets · What is trusted · docs/: reviewer's tour, sequencers, effects, simulator, hardware, Lean on the chip, engines, budget, proof ledger

Using it

The panel works like Mutable Dreams. Each group button opens its pages; press it again for the next page. The four knobs set the four values on screen.

GroupPages (knobs 1 to 4)
HOMEthe four lanes: hits, steps, speed, chance
SEQRHYTHM: hits, steps, speed, chance, the bar drawn as a circle. FEEL: rhythm source (Euclid, rotate with accents, cellular automaton, ratchet) and its knobs. NOTES: note source (random, walk, up, down, bounce, loop, turing, step, markov, grammar, chord) and its knobs. RANGE: scale, low note, chord follow. docs/SEQUENCERS.md
EDITSOUND: shape (48 Braids, 8 FM, 10 drums), timbre, colour, level. FILTER: cutoff, resonance, envelope, low/band/high. ENVELOPE: attack, release, envelope to timbre. MIX: lane levels. PAN
LFOtarget (OFF, CHANCE, LEVEL, TIMBRE, COLOUR), rate in bars, depth, for the chosen lane (lfo_modulate_in_range)
FXREVERB: send, decay, bright, return. DELAY: time, feedback, tone, return. docs/FX.md
GLOHARMONY: key, scale lock, chord progression, bars per chord. SYSTEM: tempo, keys octave, LOOK (seven colour schemes), SWING. MIDI: channels, INT or EXT clock. BANKS: patch, save slot, DEMO, AUTO (plays the demo songs in turn, changing a lane every 8 bars)
ControlDoes
SELECTpick a lane (in a browser: the group)
PRESETSthe browser: sounds on EDIT (255, by engine or by type), patches elsewhere (142 factory, 8 user). ALGO changes the grouping while it is open
ALGORITHMtempo
PLAYstart or stop; stopping silences everything
SELmute the chosen lane
RECPUNCH: while the page is up the keys are momentary effects on the mix, like the OP-XY's punch-in FX. HALF and DOUBLE time, STUTTER, ROLL, OCT- and OCT+, SHIFT, GATE, CRUSH, WASH (reverb up), THROW (delay up), BREAK (all muted), then mute lane 1 to 4, then re-roll lane 1 to 4, then KEEP 1 to 4 (the bar a lane last played becomes its STEP loop, played again every bar until MUTATE or a ROLL lets it drift), FREEZE and FRZ ALL (the chosen lane, or all four, repeats the bar it is in until pressed again or re-rolled), FILL (held: every lane at chance 100 with two more hits). The knobs set the rates. Let go and the effect is gone: with no key held every effect is the identity (punch_nothing_held_changes_nothing); the gate gain fades over 1.5 ms (punch_gate_glides)
SAVEsave the patch to the slot chosen on GLO > BANKS
ARPheld keys play one at a time in step with the clock, on the chosen lane's sound (MODE, RATE, OCTAVES, GATE). ARP again while holding keys latches them. ARP with nothing held opens QUANT: OFF, SCALE (white keys are the scale degrees) or CHORD (white keys are the current chord). keys_in_scale, keys_keep_order
keys, OCT- OCT+play the chosen lane's sound

How it works

A lane divides the clock into a rhythm, and on every hit picks a note and snaps it to the scale. Rhythms come from Euclidean patterns (rotated and accented if you like), a cellular automaton, or ratchets. Notes come from the eleven sources above; MARKOV learns from what you play, GRAMMAR rewrites a phrase, TURING is a shift register with a flip chance.

Chord progressions (GLO > HARMONY) are written as degrees: 1-4-5-1 is I IV V in a major key and i iv v in a minor one. A lane follows them with 7TH or TRIAD on SEQ > RANGE. The chords are stacked from the key's scale, so a lane that follows one of the eight table progressions stays in key (harmony_follow_in_key; the three walks below leave it on purpose). COMPL does the opposite: only the notes outside the key. Those have the scale's intervals, each shifted by the same count (two fewer for a seven-note scale, two more for a pentatonic): Babbitt's hexachord theorem in general form (harmony_complement_interval_content), and why BLACK KEYS still sounds like C major.

The last three progressions walk the Tonnetz (Firmware/Tonnetz.lean): each move is P, L or R, which keeps two notes of the triad and changes one. WALK LR visits all 24 major and minor triads and comes back; WALK PL is a hexatonic cycle of six, WALK PR an octatonic cycle of eight. Proofs/Tonnetz.lean does the group theory: P, L and R generate the dihedral group of order 24, which is why every walk comes home and sounds the same in every key.

The voices are all 48 Braids shapes from Mutable's source (voices/), each engine's sample arithmetic moved into Lean and checked against the C++ sample for sample (make test-lean). Then a four-operator FM voice (Firmware/Fm.lean: piano, bell, bass, brass, glass, mallet, organ, clang) whose sine is a polynomial, not a table, so its bound, symmetry and continuity are checked at every phase (fm_sine_bounded, fm_sine_half_wave, fm_sine_continuous). Then ten 808 drums (Firmware/Drum.lean) made from a swept sine, a 16-bit noise generator, one-pole filters and the 808's six-square metal bank. TIMBRE is decay, COLOUR is tone. Every sound goes through a state-variable filter (Firmware/Filter.lean), which at its default is an exact bypass. make audit checks the level of every factory sound.

What is proved

315 theorems, checked by Lean's kernel and by make comparator. Generated from the docstrings in Proofs/ by make proofs; docs/PROOFS.md has the full statements.

The sequencer: 106 theorems

The four lanes. No note is left sounding when you stop, the lanes stay in phase, at play chance 100 a bar plays exactly the hits set and never a rest below it, and every note is in the scale and the range.

TheoremSaysProtects against
lane_make_validmake gives a valid lane for any settings.out-of-range values from the screen or a saved file cannot produce an invalid lane.
lane_reconfigure_validreconfigure keeps a valid lane valid for any settings.turning a knob mid-song cannot push a position or timer out of range.
lane_set_notes_validsetNotes keeps a valid lane valid for any settings.switching note algorithm mid-song cannot leave the loop position outside the loop.
lane_tick_validA tick keeps a valid lane valid.counters, note memory and the sounding note stay consistent for any number of ticks.
lane_gate_end_stops_noteWhen a sounding note's gate runs out, the tick reports it as stopped.no stuck notes.
lane_new_note_stops_oldA tick that starts a note also stops the one sounding before it.a lane never has two notes on at once.
lane_note_only_on_hitA note starts only on a step the rhythm marks as a hit (for a ratchet sub-hit, the step that just pulsed).a rest never plays, regardless of note algorithm or play chance.
lane_note_in_scaleA note a tick starts is at most 127 and is in the lane's scale.the lane never plays a note the voices reject or one outside the chosen key.
lane_silence_validsilence keeps a valid lane valid.stopping the clock cannot leave a timer without a note or a note without a timer.
lane_silence_stops_notesilence reports the sounding note as stopped and leaves nothing sounding.stop never leaves a note hanging.
lane_reconfigure_selfApplying a lane's own settings back to it changes nothing, for a valid lane whose settings are within knob range and whose gate is gateFor div.saving and reloading a patch, or redrawing a knob at its current value, does not change the lane.
lane_set_rhythm_validsetRhythm keeps a valid lane valid for any settings.switching rhythm source mid-song cannot break the lane.
lane_learn_validlearn (a key press) keeps a valid lane valid.playing along or programming the step sequencer cannot break the lane.
lane_from_patch_keeps_feelA patch lane that fits (PatchLane.Fits) gives a lane with exactly the patch's rhythm settings: no clamp in Lane.make or Rhythm.make changes a fitting value.the FEEL settings a patch saves are the ones it plays back.
run_every_note_on_has_a_note_offAfter any sequence of ticks and setting changes from a fresh lane, note-ons equal note-offs plus the one note still sounding; silence then stops that note and leaves nothing sounding.no stuck notes over a whole performance, not just one tick.
run_clock_never_driftsAfter n ticks from a fresh lane the step position is (n / div) % steps, regardless of the notes played.lanes at different speeds stay in phase; notes cannot make a lane drift.
run_bar_plays_the_rhythmA lane with the Euclidean, rotated or automaton rhythm (any kind but ratchet) at play chance 100 plays, over the steps * div ticks of a bar from a fresh lane, exactly the hits its rhythm marks in that bar (for the automaton, the hits of its row at the start of the bar).for every non-ratchet FEEL setting the bar plays exactly the row of hits the screen shows.
run_bar_plays_every_hitA plain lane (Euclidean source, play chance 100) has exactly hits note-ons over the steps * div ticks of a bar from a fresh lane, for any settings (make clamps them to knob range).the safety theorems say a lane never plays on a rest; this one says it plays on every hit.
run_bar_plays_every_hit_turnedA rotated lane (Euclidean source turned by ROTATE, play chance 100) also plays exactly hits notes in a bar.ROTATE never loses or doubles a hit.
run_bar_plays_every_ratchetA ratchet lane (kind 3, play chance 100) with ratchet ≤ div plays, over the steps * div ticks of a bar from a fresh lane, exactly hits times the number of sub-hits that play; every hit step rolls the same sub-hits, each on its own tick.ratchets play exactly the rolls the knobs ask for, so every rhythm kind has a liveness theorem.
run_plays_by_the_wall_clockA plain lane (Euclidean source, play chance 100) under the main loop in straight time at full tempo, over passes whose gaps are each under 20 s at a tempo of at most 240: with ticks the elapsed time's worth of ticks, it plays hits notes for every whole bar of steps * div ticks, plus the hits among the steps before the step it stands on at the end.the number of notes a lane plays in a stretch of wall-clock time follows from the tempo, the hits and the division alone, so no pass of the main loop can lose or double a note.
rhythm_automaton_row_fitsOne automaton generation stays inside the bar: the new row has no bit at or beyond steps (steps ≤ 32).a hit can never land on a step that does not exist, however long the automaton runs.
rhythm_accent_only_on_hitAn accent only falls on a hit.accents cannot add a note on a rest.
rhythm_rotate_keeps_hitsA rotated bar (ROTATE) has exactly hits hits, like the plain one.ROTATE never adds or loses a note.
rhythm_rule_zero_diesRule 0 kills the row: after one generation nothing plays.rule 0 is silence; the reseed on a dead row is what brings the bar back.
rhythm_rule_full_fillsRule 255 fills the bar: after one generation every step plays.the RULE knob spans from silence (0) to every step (255).
rhythm_rule_204_freezesRule 204 freezes the bar: a row that fits the bar comes back unchanged every generation.at rule 204 the automaton plays exactly the Euclidean bar it was seeded with.
swing_tick_in_rangeEvery tick is between 1/3 and 5/3 of a straight tick, at every swing setting.swing cannot make a tick vanish (two steps firing at once) or stretch one past the next eighth.
swing_keeps_timeA tick of the first eighth plus a tick of the second is exactly two straight ticks, so a pair of eighths (12 ticks) takes exactly 24 straight ticks at any swing.swing only moves notes within each pair of eighths; bar length and lane phase are unchanged.
swing_quarter_note_exactA swung quarter note is exactly 24 straight ticks long at every setting, and each tick inside it is at least 3334 long, so ticks never coincide.swing does not change the tempo, and an outgoing MIDI clock stays in step.
divider_pulse_countA divide-by-div counter started at 0 gives exactly ticks / div pulses after ticks ticks.the clock divider never drops or doubles a beat.
divider_count_boundedThe count stays below div.the counter stays bounded however long it runs.
euclid_hit_countA bar of steps steps with hits hits (hits ≤ steps) has exactly hits hits.the pattern has exactly the density HITS asks for.
euclid_last_step_hitsThe last step of a bar is a hit whenever hits > 0.every Euclidean pattern ends on a hit; the ratchet's first sub-hit relies on this.
euclid_window_balancedAny window of len steps, wherever it starts (wrapping across the bar line), contains len * hits / steps hits or one more.the hits never bunch; this is Clough–Douthett maximal evenness for the pattern the firmware plays.
euclid_windows_within_oneTwo windows of the same length differ by at most one hit.any two bars, half-bars or beats carry the same number of hits or one apart.
euclid_is_christoffelFor hits < steps, step k is a hit exactly when (k + 1) * hits wraps modulo steps: the pattern is the lower Christoffel word of slope hits / steps in Berstel's form (hits = steps is every step).known results on Christoffel words (balance, conjugates are the Euclidean rhythms, reversal is the upper word) apply to these patterns.
euclid_bjorklund_is_rotationBjorklund's algorithm (Toussaint's form) gives a rotation of Euclid.hit, for every bar of up to 32 steps and every hit count, checked case by case.the firmware's floor-function pattern is the usual "Euclidean rhythm" up to rotation, which ROTATE reaches.
euclid_major_scale_is_seven_of_twelveThe major scale mask is the 7-of-12 Euclidean pattern started on step 6; the major pentatonic mask is the 5-of-12 pattern started on step 7.the SCALE table and the Euclidean rhythm are one construction (Clough–Douthett).
notes_start_validA fresh note memory is valid for any span from 1 to 65535 and any loop length from 1 to 16.the lane starts in a state the algorithms can rely on, whatever the seed.
notes_choose_stays_in_rangeWhichever algorithm picks, the offset is below span and the memory stays valid.no algorithm can pick outside the range or corrupt the memory the next pick depends on.
notes_up_visits_every_positionUP climbs one position a note and wraps: from position p the i-th note is at (p + i) % span, so the first span notes visit every position once.an UP lane covers its whole range with no position skipped or repeated.
notes_down_visits_every_positionDOWN falls one position a note and wraps: from p the i-th note is at (p + (span - 1) * (i + 1)) % span, so the first span notes visit every position once.a DOWN lane covers its whole range with no position skipped or repeated.
notes_bounce_moves_oneBOUNCE moves at most one position a note, for a span of at least 2.a BOUNCE lane never leaps.
notes_walk_boundedWALK moves at most amount positions a note (the step is drawn below 2 * amount + 1).AMOUNT bounds how far a WALK lane can jump between notes.
notes_loop_repeatsLOOP with MUTATE at 0 repeats the same amount notes: the memory never changes and the slot wraps.MUTATE 0 is an exact loop.
notes_markov_moves_at_most_threeMARKOV moves at most three positions a note, jump or not (move classes are -3 to +3; a doubled move is clamped to that range).a MARKOV lane never leaps, whatever it learnt from the keys or the style.
notes_grammar_moves_at_most_twoGRAMMAR moves at most two positions a note: its symbols step or skip, up or down.a GRAMMAR lane never leaps, for any grammar and depth.
notes_learn_keeps_validlearn (a key press) keeps the memory valid for any note: the step sequencer stores it in range, the markov chain counts it.nothing played on the keys can corrupt the memory.
notes_turing_locked_repeatsTURING with FLIP at 0 is a locked loop: after len picks the register's len-bit window is unchanged, so the notes repeat every len picks (once the bits above the window, which the first picks still read, have been shifted out).FLIP 0 is an exact loop of LENGTH notes; only FLIP changes it.
notes_grammar_phrase_never_emptyA grammar's phrase is non-empty at every depth.the GRAMMAR source never has to fall back to a default symbol.
notes_scale_tone_in_scaleThe n-th scale tone above low is in the scale whenever the scale has more than n notes in the five octaves above low.the CHORD source climbs real scale (or chord) tones; the snap has nothing to fix.
quantizer_snap_keeps_scale_notesA note already in the scale is unchanged by snap.quantizing an in-key melody changes nothing.
quantizer_snap_in_scalesnap gives a note of the scale, for a non-empty 12-bit mask and a note of at least 6 (so the 6-semitone search down cannot run off the bottom).the lane never plays a note outside the chosen key.
quantizer_snap_is_nearsnap moves a note at most 6 semitones (same conditions as above).quantizing keeps the shape of the melody.
quantizer_snap_monotonesnap is monotone: for two notes of at least 6 and a non-empty mask, the higher never snaps below the lower (same conditions as the other snap theorems).quantizing can merge two notes onto one scale note but cannot reverse a line's direction.
random_below_ltRandom.below returns a value below limit (for 0 < limit ≤ 65535).a pick is always a valid index into a list of limit choices.
random_never_stands_stillNo state maps to itself under Random.next: next x = x would need (a - 1) * x ≡ -c mod 2^32, but a - 1 is a multiple of 4 and c is odd.the generator can never get stuck on one value, whatever the seed.
harmony_mask_never_emptyThe mask handed to a lane is non-empty and fits in 12 bits, for every scale, key, progression, chord step and follow mode, Tonnetz walks included.lane_note_in_scale needs a non-empty 12-bit mask; a chord change can never leave a lane with nothing to play.
harmony_follow_in_keyOn a table progression with chord follow 7TH or TRIAD, every note a lane may play is in the key's scale: the chord is stacked from the key's scale at the chord root, not a transposed shape.chord follow on a table progression stays in key.
harmony_progression_chord_has_rootWith chord follow 7TH or TRIAD the chord's root is among the lane's notes, for every scale and key, on tables and walks alike.a following lane can play the root, not only upper notes of the chord.
harmony_chord_root_in_keyOn a table progression every chord root is a note of the key's scale (below an octave).progressions are written as degrees, so 1-4-5-1 is I IV V in major and i iv v in minor; a table chord never lands outside the key.
harmony_walk_chord_is_triadOn a Tonnetz walk (progressions 8 and up) with chord follow 7TH or TRIAD, a lane plays exactly the walk's triad transposed to the key: three notes, the chord the screen names.walks leave the key on purpose, and the lane plays exactly the named triad.
harmony_walk_keeps_two_notesConsecutive chords of any Tonnetz walk share exactly two pitch classes.P, L and R each change one note of a triad, so a walk moves one note at a time.
harmony_complement_outside_keyCOMPL (chord follow 3) plays exactly the notes outside the key's scale, for every scale, key and chord; the chromatic scale plays whole.a COMPL lane never shares a note with a lane in the key.
harmony_complement_interval_contentThe complement theorem (Lewin's generalisation of Babbitt's hexachord theorem) for any cycle Z_n and any set: the ordered count of pairs at distance d in the complement equals the count in the set plus n - 2 * size.a scale and the notes it leaves out share their interval content, which is why COMPL sounds like the key it was cut from.
harmony_hexachord_theoremBabbitt's hexachord theorem on the 12-bit masks: every six-note mask has the interval vector of its complement, checked for all 4096 masks.the n = 12, size = 6 instance of harmony_complement_interval_content on the masks the lanes play from.
tonnetz_moves_are_involutionsEach of P, L and R is an involution.a walk can always be retraced; this is the first dihedral relation.
tonnetz_group_has_order_24The group generated by P, L and R has exactly 24 elements: every word acts as one of the 24 listed elements, and no two of those act alike.the walks live in a small, fully known group (the neo-Riemannian PLR group).
tonnetz_simply_transitiveThe group acts simply transitively on the triads: from any triad exactly one element reaches any other.every triad is reachable from every other, and a word's effect is fixed by where it sends one triad.
tonnetz_commutes_with_transposition_and_inversionThe PLR group commutes with every transposition and every inversion (Lewin's duality).a walk sounds the same in every key, and a walk from a minor triad is the inversion of the walk from a major one.
tonnetz_group_is_dihedralThe dihedral relations: L R has order exactly 12, L has order 2, and L conjugates L R to its inverse R L.with tonnetz_group_has_order_24 this identifies the group as the dihedral group of order 24.
tonnetz_walks_are_cyclesFrom any start, the L R walk visits all 24 triads once and returns after 24 moves; the P L walk is a 6-cycle (hexatonic) and the P R walk an 8-cycle (octatonic).L R is a Hamiltonian cycle on the Tonnetz; P L and P R are the two short cycles.
arp_note_is_heldEvery note the arpeggiator starts is a held note raised by fewer than octaves octaves.the arpeggiator never adds a note of its own, in any mode.
arp_make_validArp.make gives a valid arpeggiator for any div.the arpeggiator starts in a state the rest of the firmware can rely on.
arp_set_validArp.set keeps a valid arpeggiator valid for any settings.turning the ARP knobs mid-phrase cannot push a timer or counter out of range.
arp_keys_validPressing, releasing or clearing keys keeps a valid arpeggiator valid.the held set stays playable and bounded however many keys are pressed.
arp_tick_validA tick keeps a valid arpeggiator valid.counters stay in range and the sounding note and its timer agree for any number of ticks.
arp_every_note_on_has_a_note_offAfter any sequence of ticks, keys and setting changes from a fresh arpeggiator, note-ons equal note-offs plus the one note still sounding, which silence stops.releasing the keys or stopping never leaves an arpeggio note hanging.
arp_up_plays_patternUP with div = 1 from position 0 plays the pattern through in order: as many ticks as the pattern has notes start exactly the pattern.UP plays each held note once, in order, per lap; the position returns to 0 so later laps read the same.
machine_every_note_on_has_a_note_offAcross the four lanes and the arpeggiator, after any lists of ticks, knob changes and keys from a quiet machine, note-ons equal note-offs plus what is still sounding, and stop leaves nothing sounding.no stuck notes for the whole instrument, not one part at a time.
lfo_modulate_in_rangeA modulated value is at most 100, for any base, depth and phase.an LFO cannot push a chance, level or timbre setting out of range.
lfo_triangle_symmetricLfo.triangle (~~~phase) = Lfo.triangle phase: the triangle is symmetric about its peak.the modulator rises and falls by the same path, with no jump at the turn.
lfo_modulate_centredAt the middle of the swing (phase 2^30) the modulator returns the base unchanged (for base, depth ≤ 100), and depth 0 never changes anything.the knob's value is the centre of the motion, so an LFO does not shift the average; depth 0 gives exactly the set value.
punch_nothing_held_changes_nothingWith no key held every effect is the identity: tempo 100 percent, no re-strike, note unchanged, gate open at full gain, crush holding nothing, reverb and delay at their settings, no lane muted.there is no effect state beyond the keys; releasing the last key restores the performance exactly.
punch_note_in_rangeA punched note is at most 127, for any keys, shift and note.OCTAVE and SHIFT cannot push a note past the range the voices accept (play drops notes above 127); edge notes are clamped instead.
punch_tempo_in_rangeThe clock speed is 50, 100 or 200 percent.HALF and DOUBLE cannot stop the clock or run it away.
punch_gate_half_openOver any whole period that does not cross the tick counter's end, the gate is open for exactly half the ticks.GATE is an even square at the rate the knob says, however the period lines up with the bar.
punch_gate_glidesThe gate gain stays between 0 and 32768 and moves toward its target by at most 512 a sample, landing on it exactly.a chop is a 1.5 ms slope, so GATE never clicks, and the gain never exceeds unity.
punch_crush_repeatsA crushed sample is either the fresh sample or the held one.CRUSH adds nothing new, so the output stays inside every bound the mix had.
punch_restrikes_on_the_beatA non-zero re-strike period divides the quarter note (24 ticks).STUTTER and ROLL land on the beat's divisions and come back to the beat.
keys_off_is_itselfQUANT off returns the key unchanged (clamped to 127).OFF is exactly the plain keyboard.
keys_in_rangeA quantized note is at most 127, for any mode, mask, key and note.a high degree from the top of the keyboard is clamped at 127, not wrapped.
keys_in_scaleWith QUANT on and a non-empty mask, every quantized note below 127 has its pitch class in the mask.in SCALE or CHORD mode the keyboard cannot play a note outside the scale or chord.
keys_keep_orderWithin an octave a higher key never plays a lower note, in any mode.quantizing cannot turn a rising line into a falling one.
keys_triad_three_an_octaveWith CHORD on a Tonnetz walk the lane's mask is a triad, and white key w + 3 plays white key w an octave up.the white keys are the chord of the moment, three to the octave.
keys_triad_is_the_chordWith CHORD on a Tonnetz walk, each of the white keys C E G plays a note of the walk's triad.the first three white keys can only sound chord tones.
keys_triad_white_keys_are_the_chordWith CHORD on a Tonnetz walk, the pitch classes of white keys C E G together are exactly the walk's triad, in rising order from the key's root (so degree 0 is the triad's root only when the walk is on the tonic).the first three white keys are the whole chord and nothing else, laid out low to high.
punch_gate_never_amplifiesThe gate never amplifies: with gain at most 32768 (Q15 unity) and a sample within 524287, the output is no larger than the sample and within 524287.a sample inside the limiter's range stays inside it through the gate and cannot wrap.
punch_crush_keeps_limitThe crush returns one of its two inputs, so a mix and a held sample both within 524287 give a sample within 524287.CRUSH cannot wrap.
punch_master_never_wrapsThe whole PUNCH audio path keeps the master within 524287, given a mix sample within 524287 (what mix_machine_sample_bounded delivers), a held sample within the same bound, any crush factor and index, and a gate gain of at most 32768.with every punch-in on at once, nothing on the PUNCH page can wrap.
clock_advance_never_wrapsThe clock accumulator never wraps: for a stall of up to 20 s, tempo up to 240 and a tempo factor up to 200 percent, starting from any residue below the longest swung tick, the 32-bit sum is the exact sum.after a long main-loop stall (patch load, save, redraw) the clock makes up the missed time instead of jumping the song.
clock_division_is_the_loopticksDue and remainder describe the firmware's while (acc >= len) { acc -= len; tick(); } loop: ticks due times the length plus the remainder is the accumulator, and the remainder is below one tick.every fact about ticksDue and remainder is a fact about the loop in dinghy.c.
clock_half_time_is_halfOver the same milliseconds from the same residue, HALF fires half the ticks of straight time to within one tick of rounding: 2 * half ≤ straight + 2 and straight ≤ 2 * half + 1 (straight ticks of 10000, swing 0).a bar under HALF is two bars long to the tick, so lanes, swing and chord changes stay in step.
clock_double_time_is_doubleOver the same milliseconds from the same residue, DOUBLE fires twice the ticks of straight time to within one tick of rounding: double ≤ 2 * straight + 1 and 2 * straight ≤ double + 2 (straight ticks of 10000, swing 0).a bar under DOUBLE is half a bar long to the tick, so lanes, swing and chord changes stay in step.
clock_loop_follows_the_wall_clockStraight time at full tempo: over any run of main-loop passes whose gaps are each under 20 s and add up to ms milliseconds, at a tempo of at most 240, the loop from an empty accumulator feeds the lanes exactly ms * bpm * 4 / 10000 ticks, whatever the gaps are.the song follows elapsed time, not how often the main loop gets round to polling, so a slow redraw or a patch load leaves the tempo alone.
The voices: 133 theorems

Braids' engines in Lean, the FM voice, the 808 and the filter. Table reads stay inside their tables, the shipped tables meet what the proofs assume, and a sample is the arithmetic clipped, not wrapped, except where Braids itself overflows (docs/LEAN-ENGINES.md).

TheoremSaysProtects against
pitch_table_reads_insideEvery pitch reads two neighbouring entries of the 97-entry phase step table, both inside it.the table is C data read without a check; the clamp to note 128 and the fold into one octave keep the read inside for any 16-bit pitch, including the negative ones a detuned FM operator asks for.
pitch_increment_boundedThe step is below half a cycle per sample.the table's top entry is above half a cycle, so the bound comes from the clamp, which puts every pitch at least one octave below the table; a step below half a cycle keeps the oscillators under Nyquist.
tables_phase_reads_inside_257A phase reads two neighbouring entries of a 257-entry table, both inside it, for any phase.the sine, bell, cutoff, damping and comb tables all have 257 entries, so every blend of them (Analog.sineAt, Digital.lutAt, Analog.buzz) stays inside without a check.
tables_shape_reads_inside_257A 16-bit index reads two neighbouring entries of a 257-entry wave shaper, both inside it.the overdrive and fold shapers have 257 entries; Analog.shape is safe for any index below 65536, which is why its callers mask or bound what they pass.
tables_resonator_reads_inside_129A pitch, after the resonator's clamp, reads inside the 129-entry resonator tables.without the clamp to 16383 a high note would read past them.
tables_wave_reads_inside_129A wave phase, halved as the wavetable engines do, reads inside a 129-byte wave.Braids' waves are 129 bytes, half what the phase could address; the halving keeps the read inside.
tables_wave_line_reads_inside_129A wave phase, halved and then masked to fewer steps as the wave line's rough reading does, reads inside a 129-byte wave.the mask can only lower the phase, so the rough reading stays inside like the plain one (tables_wave_reads_inside_129), for any mask.
tables_formant_reads_inside_256A formant phase and an amplitude of 0 to 15 read inside the 256-entry formant tables (16 phases by 16 amplitudes).the vowel's formant amplitudes are 4-bit by construction of its data, so no phase can read past the formant sine or square table.
braids_data_increments_risingThe phase step table rises from one entry to the next for every index the code reads (below 96: pitch_table_reads_inside); the entry after the last is the copy's tail, so it is excluded.pitch_increment_bounded blends two neighbouring entries and needs the second to be the larger; this checks it against the data Braids ships.
braids_data_increments_closeTwo neighbouring phase step entries differ by less than 2^27 for every index the code reads (below 96).the blend multiplies that difference by a 4-bit fraction in signed 32-bit arithmetic, and 2^27 * 15 is the most that fits.
braids_data_increments_topNo phase step entry passes 2588618730, the table's last, at any index.the setup shifts the entry down at least one octave, and this top puts the result below half a cycle (pitch_increment_bounded).
braids_data_cutoff_risingThe filter cutoff table rises from one entry to the next for every index a phase reads (below 256: tables_phase_reads_inside_257).filters_setup_cutoff_in_range blends two neighbouring entries and needs the second to be the larger; this checks it against the data Braids ships.
braids_data_cutoff_topNo filter cutoff entry passes 25078, the table's last, at any index.the swarm's high-pass coefficient is a blend of two entries, so this is its range, which the filters' sample theorems take.
braids_data_rate_topNo grain envelope rate entry passes 32768, the table's last, at any index.noise_setup_grain_rate_bounded shifts the entry up three places and needs the result below 2^18 to keep the grain's envelope reads inside their table.
braids_data_vowel_amplitudes_are_nibblesEvery vowel formant amplitude is a nibble (at most 15), for any vowel and formant.the formant tables are indexed by the phase's top nibble or-ed with the amplitude (tables_formant_reads_inside_256); an amplitude above 15 would read past them.
braids_data_wavetable_steps_at_most_16Every wavetable definition has at most 16 steps, for any table index.wavetables_setup_table_in_table multiplies the parameter by the step count and needs the pointer's step and the one after it inside the 17 steps of a definition; Braids ships 4, 8 or 16.
fm_sine_boundedThe sine is within ±32767 for every phase.an operator never hands its modulators or the mix more than full scale; there is no table, and the polynomial is checked for every input.
fm_sine_exact_at_quartersThe sine is 0 at the start, 32767 at a quarter, 0 at a half and -32767 at three quarters (32767 is one).an operator at rest is zero and a peak is exactly full scale, so the shapes' levels and depths mean what they say.
fm_sine_continuousFrom one position to the next the quarter sine rises by at most two and falls by at most one (the fall is rounding), for every position.the polynomial has no step, so no click or buzz of its own; checked at all 32767 positions.
fm_operator_never_wrapsAn operator's output is its sine through its envelope, at most 32767 in size for any envelope up to one (2^24), and nothing wraps.an operator's output is in range whatever its modulators did, so no depth setting can overflow it.
fm_carry_never_amplifiesA carrier's share is at most its output, for a level at most one (32767).the mix of carriers is at most the sum of their levels, so a shape with levels summing to one cannot clip (fm_shapes_in_range).
fm_saturate_in_rangeThe voice's output sample is within ±32767, and a sum already in range passes unchanged.the hold at full scale is a saturation, not a wrap.
fm_envelope_only_fallsThe envelope one block on is no higher, and no lower than 15/16 of itself, for any rate below 65536 and envelope up to one; nothing wraps.an operator's envelope only falls, by at most a sixteenth a block.
fm_increment_below_nyquistAn operator's increment is at most half a cycle a sample for any pitch and ratio.an operator asked for more than Nyquist is held there instead of wrapping to a wrong pitch.
fm_algorithms_well_foundedIn every algorithm a modulator is a higher-numbered operator than the one it modulates, and operator 1 always carries.computing operators 4 down to 1, every modulator's output is already this sample's (no one-sample lag and no cycle except the declared feedback on 4), and every algorithm makes a sound.
fm_shapes_in_rangeEvery shape's settings are in the ranges the theorems above need: ratios at most 16 (4096 in Q8), levels at most one (32767), rates below 65536, and a shape's carrier levels sum to at most one.with fm_carry_never_amplifies the eight shapes cannot clip at full envelope, and fm_envelope_only_falls applies to their rates.
fm_harmonic_ratios_lockWith both starting at phase 0, an operator at integer ratio r (Q8 value r * 256) has phase exactly r times the carrier's after any number of samples, modulo 2^32.an integer ratio is exactly harmonic; the two phases never drift apart or beat.
fm_integer_ratio_periodicThe operator's waveform at integer ratio r is a function of the carrier's phase alone.carrier plus operator is then periodic with the carrier, so the spectrum is exactly harmonic.
fm_organ_and_bass_lockFM ORGAN (shape 54) and FM BASS (shape 50) use integer ratios on every operator; FM BELL (shape 49) does not.the organ and bass are exactly harmonic by fm_harmonic_ratios_lock; the bell's ratios of 3.5, 7.2 and 1.4 are inharmonic by design.
fm_sine_half_waveThe sine half a cycle on is the negative of the sine: sineAt (phase + 2^31) = -sineAt phase for every phase (the quadrant moves by two, the place in the quarter is unchanged, the sign flips).a half-wave-symmetric waveform has no DC and no even harmonics, so the polynomial's error sits at odd harmonics only.
fm_sine_quarter_waveThe sine is mirrored about the quarter points: sineAt (0x7FFFFFFF - phase) = sineAt phase for every phase.the engine evaluates the polynomial on 0 to 32767 only and reads the other three quarters by mirror and sign; this symmetry is what makes that exact.
fm_sine_pairs_cancelTwo samples half a cycle apart cancel exactly: sineAt p + sineAt (p + 2^31) = 0 as integers for every phase (the negation cannot wrap, the sine being within 32767).this is the pairing behind fm_sine_has_no_dc.
fm_sine_has_no_dcAny 256 samples of the sine spaced a 256th of a cycle apart sum to zero, from any starting phase: sample k and sample k + 128 are half a cycle apart and cancel (fm_sine_half_wave).a carrier at any pitch and phase adds no DC to the mix over a cycle, and the polynomial's error has no even harmonics.
fm_envelope_after_boundedThe envelope after k blocks at rate is at most its start times ((2^20 - rate) / 2^20)^k plus 17 counts a block for rounding (both sides scaled by 2^20^k to stay in whole numbers), for any start up to one (2^24) and rate below 65536; and it never passes one.an envelope decays geometrically with time constant 2^20 / rate blocks; rounding cannot pile up into a floor.
fm_held_envelope_holdsAt rate 0 the envelope holds exactly.0 in the rate table means hold with no drift from rounding (the piano's body, every organ operator).
fm_shape_tailsFor every shape's decaying operators, from a full envelope (2^24) at the table's rate: the first block at which the envelope is under a tenth, and under a hundredth, stepped block by block in the kernel, with the block before still above (a block is 16 samples, 0.36 ms at 44.1 kHz).these are the decay times a player hears, computed from the arithmetic the chip runs.
fm_no_operator_idleIn every algorithm every operator either carries or modulates some operator; none of the four is wired to nothing.an operator that neither sounds nor modulates would be a quarter of the voice computed every sample for nothing.
fm_algorithms_distinctThe eight algorithms are pairwise different: any two differ in some modulation link or in which operators carry.no two positions of the algorithm knob wire the operators the same way.
fm_every_shape_uses_four_operatorsIn every shape every operator is either a carrier of the shape's algorithm or has nonzero depth and modulates some operator in it; no shape has a silent modulator.a modulator with depth zero would run its sine and envelope every sample for nothing.
fm_sine_lipschitzThe sine is Lipschitz in the phase: between two phases it moves by at most two per position (2^15 phase units) crossed, plus one more for the partial positions at either end.a bounded phase step reads a bounded change in value, which is what bounds a modulator's slew and a carrier's frequency deviation (fm_modulator_slew, fm_carrier_frequency_deviation).
fm_modulator_slewAn unmodulated operator at increment incM and envelope env (at most one) changes by at most 2 (incM / 2^15 + 1) + 1 a sample: two per position its phase crosses, one for the envelope rounding.a modulator's slew is proportional to its increment, which is what makes FM's frequency deviation proportional to the modulating frequency.
fm_carrier_frequency_deviationA carrier at increment incC pushed by a modulator (Fm.push 0 out depth, out the modulator's output each sample): the push changes from one sample to the next by depth times the change in out (modulo 2^32), so the pushed phase advances by incC plus that term; the change in out is at most 2 (incM / 2^15 + 1) + 1 (fm_modulator_slew), so the change in the push, as an integer, is at most depth times that.the carrier's peak frequency deviation is at most depth (2 (incM / 2^15 + 1) + 1) phase units a sample, proportional to modulation index times modulator frequency, as Carson's rule says.
fm_cap_is_a_holdThe cap is a saturation of the exact value: the increment is the product shifted by eight, or half a cycle, whichever is smaller.above the keyboard an operator holds at Nyquist instead of wrapping to a wrong pitch; below it nothing is lost.
fm_increment_exact_below_capBelow the cap the increment is the exact product shifted by eight: a product below 2^39 shifts to below 2^31, so neither the hold nor the narrowing to 32 bits changes it.an operator's pitch is exactly the voice's times the shape's ratio, with no rounding beyond the Q8 shift.
fm_no_cap_on_the_keyboardFor MIDI notes 0 to 88 (up to E6, 1319 Hz) every operator's increment is the exact product of the voice's increment and its shape's ratio, shifted by eight; the Nyquist cap never engages.every FM shape's ratios are exact across the playable keyboard; above it the engine saturates at Nyquist (fm_cap_is_a_hold).
drum_noise_periodThe noise register from state 1 returns to 1 after exactly 65535 steps and not before: its period is 65535, every nonzero 16-bit state (the taps are a primitive polynomial, checked by walking the orbit in the kernel).the noise behind the kick's click, the snare, the clap and the toms repeats only every 1.5 s, longer than any hit.
drum_noise_never_diesA nonzero 16-bit noise state steps to a nonzero 16-bit state, checked for every state.the glue seeds the register with 1 and never resets it; a register at zero would stay there.
drum_noise_in_rangeThe noise sample is within 32767 for any state.the noise feeds the filters and the gains at the same scale as a sine, so the bounds below cover it.
drum_one_pole_stays_betweenA one-pole step is exact, y + (x - y) * a / 16384 rounded down, and lies between the state y and the input x, for a coefficient from 0 to 16384 and signals within 65535; so the high-pass (input minus low-pass) is within `x
drum_envelope_only_fallsThe envelope one sample on is no higher, and no lower than 15/16 of itself less one count, for any rate below 65536 and envelope up to one; rate 0 holds exactly; nothing wraps.a drum's envelope only falls, by at most a sixteenth a sample, and rate 0 is a hold.
drum_sweep_landsThe swept increment never rises and never goes below its target: from above it falls by rate / 2^20 of the distance a sample, at or below it stays on the target; for any increment below half a cycle a sample and any rate below 8192, nothing wraps.the kick's and toms' pitch sweeps land on the note with no undershoot, from a start up to five times above it.
drum_sweep_start_below_nyquistA sweep's start is below half a cycle a sample for any tuning and colour.drum_sweep_lands needs the swept increment below Nyquist, and the start is its highest value; a note above the 1.4 kHz hold starts on its tuning.
drum_metal_sum_fitsSix squares of ±5461 sum within 32766 for any six phases.the hi-hats and the cymbal enter their filters in the same range as a sine or the noise, so the same filter and gain bounds cover them.
drum_square_in_rangeA square is ±5461.the cowbell's two squares sum within 10922, inside the bound the band filter needs.
drum_gain_never_amplifiesA gain is at most its level, for any envelope up to one and level up to 32767.a shape's loudest moment is its level table, which drum_output_never_wraps relies on.
drum_metal_chain_boundedSix squares through the band filter and the gain: the sum is within 32766 (drum_metal_sum_fits), the first low-pass state within 32767, the high-pass within 65534, the second low-pass within 65535 and the gained output within 262140, for filter coefficients from 0 to 16384 (Q14) and a gain up to 32767 (Q13); no stage wraps and the states handed on satisfy the same bounds.the hi-hats and the cymbal are the most arithmetic of any shape, and no colour setting can overflow their filters.
drum_output_never_wrapsEvery shape's mix, as voices/drum_glue.c composes it, is exact and within 524280 (under 2^19), for filter states within their bounds (first stage within 32767, second within 65535), coefficients from 0 to 16384 and gains up to 32767: the kick and toms (Drum.tunedOut), the snare (Drum.snareOut), the clap (Drum.clapOut), the hi-hats and cowbell (Drum.bandOut, with drum_metal_chain_bounded and drum_square_in_range) and the cymbal (Drum.cymbalOut); the sample out is the mix clipped to 16 bits, unchanged when already within 32767.no drum's arithmetic wraps at any knob, note or phase; an overloaded shape clips.
drum_shapes_in_rangeEvery shape's tables are in the ranges the theorems above need: rates below 65536 at every timbre (drum_envelope_only_falls), sweep rates below 8192 (drum_sweep_lands), levels up to 32767 (drum_gain_never_amplifies) and filter coefficients up to 16384 (drum_one_pole_stays_between) at every colour.the table values are checked against the bounds, so a knob at either end still meets every hypothesis.
drum_tailsEach shape's tail from a full envelope at its nominal rate, stepped sample by sample in the kernel: the first sample at which the envelope is under a hundredth, with the sample before still above.these are the decay times a player hears, computed from the arithmetic the chip runs.
analog_this_blep_rangeThe correction a step edge adds to its own sample is between 0 and 16383.the band-limiting correction cannot push a sample out of range.
analog_next_blep_rangeThe correction a step edge leaves for the next sample is between -16383 and 0.the band-limiting correction cannot push a sample out of range.
analog_triangle_half_rangeEach half of a triangle step lies between -16384 and 16383.the two halves add without leaving the 16-bit range.
analog_triangle_never_wrapsThe triangle's sample is exactly the sum of its two halves; nothing wraps.the triangle cannot click from an overflow at any pitch or phase.
analog_sine_between_entriesThe sine at any phase lies between the two table entries it blends.the sine, and everything FM-modulated through it, stays within the table's range, so the cut to 16 bits in Analog.sineAt never changes a value.
analog_phase_exactAn oscillator's phase after n samples is exactly phase + n * inc, wrapped at 2^32.the frequency is exactly inc / 2^32 of the sample rate with no rounding error building up, so a long note does not drift.
digital_mix_between_inputsA crossfade of two 16-bit values stays between them (give or take one rounding step toward zero), for any balance up to 65535.the macro shapes and the wavetable engines mix with this; it cannot exceed its louder input, so it cannot wrap.
digital_clip_never_wrapsBraids' CLIP gives a value between -32767 and 32767, and the 16-bit sample written is exactly that value.the engines that end in a clip (saw swarm, harmonics, FOF vowel, question mark) are limited by the clip only, never a wrap.
digital_fm_never_wrapsEach of the three FM engines (FM, feedback FM, chaotic FM) writes exactly the blended sine at its modulated phase; the cut to 16 bits changes nothing.an FM sample is always a sine table value (analog_sine_between_entries), so no modulation depth, feedback or sync can overflow it.
digital_ring_product_never_wrapsThe ring modulator's product of two 16-bit signals is their exact product divided by 65536, between -16384 and 16384; the multiplication never wraps and the cut to 16 bits changes nothing.the ring modulator multiplies three full-scale sines in a row, and the soft clipper after them gets a value of at most half scale.
digital_ring_mod_never_wrapsThe ring modulator writes exactly its soft-clipped triple product; the cut to 16 bits changes nothing.the sample is always a value of the overdrive shaper, so it cannot overflow.
digital_toy_never_wrapsThe toy's smoothing filter writes exactly the weighted average of its four held values, centred on zero (between -28208 and 27987); the unsigned subtraction wraps for a low average and the read as a 16-bit value undoes that wrap exactly.the wrap Braids relies on is proved harmless; the sample is never off by 65536.
blend_eight_betweenBlending two 16-bit entries by an 8-bit fraction (Interpolate88, Analog.blend88) lands between them.a wave shaper's output stays within its table's range without relying on the 16-bit cut.
blend_sixteen_betweenBlending two 16-bit values by a 16-bit fraction (Interpolate824) lands between them, for any two values.every table read and wave crossfade uses this blend; the overflow in the original is proved harmless.
filters_swarm_sum_rangeThe sum of the swarm's seven saws lies between -28672 and 28665 at any phases.the sum is offset by 32768 and read from the overdrive shaper with no mask; this keeps the read inside its 257 entries (tables_shape_reads_inside_257).
filters_harmonic_glide_approachesA harmonic's amplitude moves toward its target and never passes it (shown for an amplitude below its target; the other direction is symmetric), for values within a quarter of the 32-bit range.a fading harmonic cannot overshoot.
filters_swarm_filter_never_wrapsThe saw swarm's high-pass writes exactly its clipped output, between -32767 and 32767.the swarm's sample is limited by the clip only; a resonance peak cannot wrap.
filters_windowed_carrier_never_wrapsThe windowed carrier of the digital filter engines is the exact product the formula means, for a 16-bit carrier and a window of 0 to 65535.the digital filter engines never write a wrapped carrier; the overflow in the original is proved harmless.
filters_vosim_never_wrapsVosim writes exactly its windowed formant sum, centred on zero; the two formants under the bell window never pass 49150 before the centring, so nothing wraps at any pitch or formant setting.vosim cannot overflow for any formant frequencies.
filters_harmonics_never_wrapsThe harmonics engine writes exactly its clipped sum, and the in-between sample exactly the average of that with the last sample.the sum is limited by the clip only and the half-rate smoothing cannot wrap.
filter_open_is_identityWith the cutoff fully open (100) and no resonance the filter is inactive: a step returns the sample and the states unchanged, for any state, mode and sample.these are the default settings on every voice, so every existing sound is bit for bit what it was.
filter_states_stay_boundedThe states and nodes stay within ±65536 and nothing in a sample wraps: a state after a sample is within ±65536 for any inputs (the hold); and with both states within ±65536, the sample within ±65536 and the coefficients in range (g, a1, a2 at most 32767, k at most 16384) the band node before its hold is exactly a1 ic1 / 2^15 + a2 ((x - ic2) / 2) / 2^14, the held band node v1 is within ±65536, the low-pass node v2 is exactly ic2 + g v1 / 2^15 and within ±131072, the high-pass output before its clip is exactly x - k v1 / 2^13 - v2, the raw output of every mode is within ±327680 (5 · 2^16), and both state updates 2 v - ic are exact.the filter's arithmetic saturates at twice full scale and never wraps, whatever the engine feeds it; the clip to ±32767 at the output is the only other thing that touches a sample.
filter_coefficients_in_rangeThe coefficients are in the ranges filter_states_stay_bounded needs and the cutoff sweep is monotone: every entry of the cutoff table is at most 32767 and the table rises from each setting to the next below 100; the damping is between 884 (0.108 in Q13) and 16384 (2.0) for any resonance setting; a1 is at most 32767 for any g and k, and a2 is at most g for any g at most 32767.turning the cutoff up always opens the filter, and no cutoff or resonance setting hands the sample path a coefficient that could overflow a product.
filter_settled_dc_holdsA settled constant is a fixed point of the active filter: with the band state at rest (ic1 = 0) and the low-pass state at the sample (ic2 = x), a step gives low-pass output x, band and high outputs 0, and the same states again, for any cutoff and resonance and any sample within ±32767.a steady level passes through the low-pass unchanged at any cutoff, with no drift from rounding, and the band and high outputs reject it.
filter_lowpass_steps_toward_inputA low-pass step with the band node at rest (ic1 = 0) lands between the low-pass state and the sample, within one count of rounding below: min x ic2 - 1 ≤ v2 ≤ max x ic2, for any g and a2 at most 32767, any a1, and states and sample within ±65536.the low-pass integrator only moves toward the input; a resonant peak comes from the band state, which is held.
noise_particle_amplitude_fadesBetween hits, a particle burst only fades: its amplitude after a sample is at most what it was.particle noise dies away between hits; the burst cannot grow by itself.
noise_overdrive_index_in_tableA signed 16-bit value plus 32768 is one of the overdrive curve's 65536 indices.the filtered and twin-peaks noises read the curve by this index unchecked; it is the condition tables_shape_reads_inside_257 needs.
noise_corrected_never_wrapsThe filtered noise's gain correction is exact and within 16 bits: the mix clipped to ±32767, scaled by gain parts in 32768, for a gain from 0 to 32767.the result indexes the overdrive curve; an oversized value would read outside it.
noise_boost_never_wrapsThe twin peaks' boost is exact: the resonators' sum plus makeup parts in 8192 of itself, clipped to ±32767, for a sum within 65536 and a makeup gain from 0 to 8191.the boost is the last arithmetic before the overdrive curve; it clips, not wraps.
noise_resonate_never_wrapsOne resonator step is exact: the input scaled by s parts in 65536 with its sign kept, plus the last output times c parts in 32768, minus the one before times q2 parts in 32768, clipped to ±32767.a wrap in the resonators' feedback would be a burst of noise that never decays.
noise_burst_never_wrapsA particle burst's sample is exact: the noise word's low 16 bits as a signed number, times the amplitude, divided by 65536, which is within 16 bits.this is the input to the three resonators, in the range they are proved for.
noise_particle_never_wrapsThe particle noise's sample is exact: the three resonators' outputs added and clipped to ±32767, with nothing wrapping in the sum.each resonator is clipped on its own, so the sum of three needs its own clip.
noise_cloud_out_never_wrapsThe cloud's limiter is exact: the sample is the grains' sum clipped to -32768..32767.the cut to 16 bits after the limit changes nothing.
noise_grain_never_wrapsA grain's share of the sample is exact: the sine at its new phase times its envelope entry, divided by 131072, which is within ±16384, so the cloud's limiter sees the exact sum of its four grains.the cloud adds four of these every sample; a wrapped share would be a click.
noise_grain_envelope_index_in_tableA grain reads inside its 513-entry envelope when its envelope phase is at most 2^24 + 2^22 and its rate at most 2^18 per sample.the envelope is a C array read without a bounds check.
percussion_excite_decaysBetween hits an exciter's pulse stays at or below where it was and never below zero, for a pulse at most 2^19 (the loudest strike is 15 times 32768) and a decay at most 4096 (one, in 12 bits).the exciter cannot grow on its own or go negative.
percussion_decay_never_growsBetween blocks a partial's new amplitude is at least zero and at most what it was, for an amplitude up to 32767, decays up to 65535 (the short one at most the long one) and any setting from 0 to 32767.the decay cannot make a partial louder or flip its sign, so amplitudes stay in the range the partial arithmetic is proved for.
percussion_bell_partial_never_wrapsA bell partial's share of the sample is exact: the sine at its new phase times its amplitude, divided by 131072, for an amplitude from 0 to 32767 (Braids' bell partials start at most at 21872 and only decay).eleven of these are added every sample of a bell; a wrapped share would be a click.
percussion_drum_partial_never_wrapsA drum partial's share of the sample is exact: the amplitude moves from amplitude toward target by fade parts in 32768, and the sine at its new phase times that, divided by 65536, is the share.six of these are added every sample of a drum; a wrapped share would be a click.
percussion_snare_never_wrapsThe snare's sample is exact: the two filtered exciters, each with a sixteenth of its raw pulse, at their gains, plus the filtered noise, clipped to ±32767.the sum of three loud signals clips, not wraps.
percussion_cymbal_never_wrapsThe cymbal's sample is exact: the metallic and white noises each clipped to ±32767, then the blend from the first toward the second by xfade parts in 32768, which lands between them and fits 16 bits.the cymbal's blend cannot wrap at any blend setting.
percussion_drum_noise_stays_boundedThe drum's noise filter's three states stay within 16384 from one sample to the next, for noise of any size and a cutoff from 0 to 32768.the filtered noise ring-modulates the drum's partials every sample; a growing state would wrap that product.
percussion_drum_mix_never_wrapsThe drum's sample is exact: the first partial, the second and fourth ring-modulated by the filtered noise at the noise gain, and all partials at the harmonics gain, clipped to ±32767.the ring modulation multiplies two signals and a gain; a louder second or fourth partial would pass 32 bits.
physical_mix_never_wrapsBraids' Mix of two 16-bit delay-line samples by a 16-bit fraction is exact: the weighted sum reaches at most 32768 * 65535 and stays inside 32 bits.every delay-line read of the bowed string, blown pipe and flute uses this blend.
physical_comb_never_wrapsThe comb filter's output and feedback samples are exact: the delayed sample is the blend of its two neighbours and each output is that arithmetic clipped to ±32767, for 16-bit input and line samples, a fraction from 0 to 65535 and a 16-bit resonance.a wrapped sample in the comb's feedback would ring round the loop.
physical_comb_resonance_index_in_tableThe comb filter's resonance curve read is inside the curve's 257 entries for every setting.the curve is a C array read without a bounds check.
physical_pluck_fill_never_wrapsFilling a new string never wraps: each entry is a quarter of the old sample plus three quarters of the noise, which fits 16 bits for 16-bit inputs.a pluck starts from this fill.
physical_pluck_average_no_gainOne step of the plucked string's loop stays within the larger of the two neighbours it averages, for 16-bit neighbours and a loss from 0 to 32768.the Karplus-Strong loop feeds back into itself; a step with gain would make the string grow instead of decay.
physical_pluck_read_never_wrapsReading the plucked string between two 16-bit entries gives exactly their blend at any phase.this read is the plucked string's sample; the overflow in the original is harmless.
physical_half_rate_never_wrapsA half-rate engine's two samples are exact: the sample is the voices' sum clipped to ±32767, and the in-between sample is the mean of that and the previous 16-bit sample, rounded down.the plucked and bowed strings hand every other sample through here.
physical_bowed_never_wrapsThe bowed string's sample is exact: the body filter on the bridge sample (the blend of two 8-bit line samples times 256) and its two previous outputs, clipped to ±32767, for previous outputs within 2^18 (which physical_bowed_body_stays_bounded keeps true from one sample to the next).the body filter runs on every sample of a bowed note.
physical_bowed_lowpass_stays_boundedThe bowed string's bridge low-pass state stays within 16 bits from one sample to the next, for 8-bit line samples and any fraction up to 65535.this state feeds the string back to itself; a growing state would wrap the bow's arithmetic.
physical_friction_index_in_tableThe bowed string's friction curve read is inside the curve's 257 entries, for a bow pressure up to 1024 and a bow-to-string speed difference up to 2^20 (Braids uses at most 172 and 2^17).the curve is a C array read without a bounds check; the cap at 131071 before taking the top 8 bits keeps it inside.
physical_blown_filter_stays_betweenThe blown pipe's output filter lands between the bore's new sample and its own last output, so it fits 16 bits, for a coefficient from 0 to 4096 and a 16-bit last output.this filter is the blown pipe's sample; it cannot add a wrapped value.
physical_fluted_never_wrapsThe flute's sample is exact: the bore's blended 8-bit sample times 256, clipped to ±32767, for 8-bit line samples and any fraction up to 65535.nothing on the flute's output path can wrap.
physical_jet_index_in_tableThe flute's jet curve read is inside the curve's 257 entries for every jet value.the curve is a C array read without a bounds check; the clamp to 0..65535 before taking the top 8 bits keeps it inside.
physical_bowed_body_stays_boundedThe body filter state the bowed string carries to the next sample is within 2^18 for any inputs.with physical_bowed_never_wraps this closes the loop, so the body filter never wraps over a whole note.
physical_comb_read_in_lineThe comb filter's two reads (integral and integral + 1 samples back) are inside its 8192-entry line.the glue indexes the line unchecked.
physical_comb_write_in_lineThe comb filter's write position after a sample is inside its 8192-entry line.the glue writes unmasked.
physical_pluck_read_in_lineThe plucked string's read entry and its neighbour one further on are both within a string of size entries, the neighbour at most on the guard copy dl[size].the read blends two neighbours, which is why the line is one entry longer than the string.
physical_pluck_next_in_lineWhere the plucked string's low-pass loop stops this sample, and each next entry it visits, are within a string of size entries.the loop reads and writes dl[write_ptr] and dl[next] unchecked; with size zero the mask would be all ones.
physical_pluck_fill_in_lineFilling a new string writes inside a string of size entries when the fill counter started at or below size.the glue indexes the line with the fill counter unchecked.
physical_bowed_lines_in_lineThe bowed string's reads and writes are inside its lines: the bridge line is 1024 long, the neck line 4096.both lines are int8_t arrays indexed unchecked, and the delays come from the pitch without a clamp.
physical_blown_bore_in_lineThe blown pipe's read and write are inside its 2048-entry bore line.the bore is indexed unchecked and the delay comes from the pitch without a clamp.
physical_fluted_lines_in_lineThe flute's reads and writes are inside its lines: the bore line is 4096 long, the jet line 1024.both lines are indexed unchecked and the delays come from the pitch without a clamp.
vowels_fof_contribution_never_wrapsEach formant filter of the FOF vowel contributes exactly its band-pass state times the amplitude (a 16-bit value) divided by 131072, between -8192 and 8191, with nothing wrapping.the five contributions are summed in 32 bits in C and never pass 40960, so the clip after them is the only limit.
vowels_fof_out_never_wrapsThe FOF vowel writes exactly its clipped filter sum, and the in-between sample exactly the average of that with the last sample.the sum is limited by the clip only and the half-rate smoothing cannot wrap.
vowels_question_drive_never_wrapsThe question mark's distortion term is exactly the square-law formula, between 0 and 65535, for a clipped sample and a drive of 0 to 32767; neither product wraps.full drive cannot overflow and flip the sample's sign; the clip after it is the only limit.
wavetables_wavetable_never_wrapsA wavetable sample is exactly the sum of its two half-rate readings, each halved; nothing wraps.the wavetable engine cannot overflow for any waves, phase or morph position.
wavetables_crossfade_between_wavesA crossfade of two waves at the same phase lies between the two waves' values there, for any two waves.morphing cannot exceed the louder wave, even where the waves are far apart and Braids' C++ product wraps (blend_sixteen_between).
wavetables_read_between_bytesA wave read lies between the two bytes it blends (each scaled to 16 bits), for any wave and phase.a wave stays within its bytes' range; the overflow in the original is proved harmless.
wavetables_wave_map_never_wrapsA wave map sample is exactly the sum of its two half-rate readings, each already halved; nothing wraps.the wave map engine cannot overflow for any square, phase or position in the square.
wavetables_wave_line_never_wrapsA wave line sample is exactly the average of its two half steps; nothing wraps.the wave line engine cannot overflow for any waves, masks, crossfades or balance.
wavetables_paraphonic_never_wrapsA paraphonic sample is exactly the sum of its four voices divided by four; nothing wraps.four full-scale voices added in 32 bits stay far inside the range at any chord, phase or morph position.
Setting a voice up: 42 theorems

Per-note and per-block setup: parameters land in range, smoothing does not overshoot, table reads are in bounds.

TheoremSaysProtects against
voice_setup_pitch_in_rangeThe pitch Braids gets for a MIDI note (play in fw/firmware/src/dinghy.c sends the note times 128) is exactly that below note 128, where the phase step clamps, and a timbre value of 0 to 32767 (the sounds' range, and what the envelope's boost is held to) reaches Braids unchanged.Braids holds pitch and timbre in 16 bits; the firmware's range never reaches the cut, so no note plays the wrong pitch and no parameter reads past the tables.
voice_setup_shape_in_tableThe shape the voice plays is one of Braids' 48, whatever index the firmware sends.the macro oscillator picks its render function from a 48-entry table without a check.
smooth_parameter_betweenA smoothed parameter lies between start and target, for parameters 0 to 32767 (Braids' range) and any crossfade position 0 to 32767, and nothing in the arithmetic wraps.a moving knob glides the timbre between the two settings without overshoot or a wrapped jump.
smooth_parameter_reaches_targetWhen the block ends, the smoothed parameter is within size of its target: the last crossfade position is 32767 - 32767 % size rather than 32768, so it stops just short.the step from the end of one block to the start of the next is at most size, so block boundaries are inaudible.
smooth_step_glide_boundWhile a block smooths the phase step from previous to target, the step never rises above the larger of the two nor falls more than size below the smaller.a pitch glide cannot pass the note by more than 16 units of 2^32 per sample, under a millionth of a semitone.
smooth_step_lands_near_targetWhen the block ends, the smoothed phase step is at or below its target and within size of it: rising, it lands (target - previous) % size short; falling or level, between 1 and size below.after every block the pitch is within 16 units of 2^32 per sample of the note asked for and never sharp.
analog_setup_pulse_width_in_rangeFor a non-negative parameter (Braids' parameters are 0 to 32767), the square's pulse turns on between 768/32768 and 32768/32768 of the cycle and the variable saw's second slope starts between 1024/32768 and 32767/32768 of it.the sample code places its edge correction by dividing the distance to the edge by the phase step; an edge at phase 0 would be corrected twice and one past the cycle never reached.
analog_setup_buzz_zone_in_tableThe buzz reads two of the 15 comb tables, both inside the set, for any pitch and parameter.the comb tables are C data read without a check; the clamp to the last zone keeps a high or unclamped pitch from reading past them.
digital_setup_fm_quantizer_in_tableA non-negative parameter reads two neighbouring entries of the 129-entry FM ratio quantizer, both inside it.the quantizer is C data read without a check, once per block for the three FM engines.
digital_setup_increments_half_cycleEvery phase step the setup hands to the FM, ring modulator, toy and digital modulation sample code is below half a cycle per sample.a step at or above half a cycle would alias, and the modulators' detunes can reach pitches far outside the carrier's range.
digital_setup_delay_in_tableEvery pitch reads two neighbouring entries of the 97-entry delay table, both inside it.the table is C data read without a check; the clamp to note 128 and the fold into one octave keep the read inside for any 16-bit pitch, including the lowest a filtered comb pitch reaches.
macro_setup_parameters_in_rangeEvery parameter the macro oscillator hands an analog oscillator (morph's pulse width, the sine and triangle fold amounts) is between 0 and 32767, given a non-negative first parameter (Braids' parameters are 0 to 32767).the pulse widths and fold tables are built for 0 to 32767; an out-of-range parameter would put a pulse edge outside the cycle or read a shaper past its end.
macro_setup_cutoff_in_tableMorph's low-pass cutoff, as a pitch, is 0 to 32767 for any pitch and parameter, and its two reads of the 257-entry cutoff table are inside it.the filter table is C data read without a check, once per block for the morph engine.
macro_setup_triple_intervals_in_tableA triple oscillator's non-negative parameter reads two entries of the 65-entry interval table, both inside it.the interval table is C data read without a check, twice per block for the four triple engines.
filters_setup_cutoff_in_tableThe swarm's high-pass reads two neighbouring entries of the 257-entry cutoff table, both inside it, for every pitch and parameter.the cutoff pitch is the oscillator's pitch plus the parameter, which can run far past the table; the clamp in swarmHpCutoff keeps the read inside.
filters_setup_table_blend_le_topA table of 16-bit entries that rise from one to the next across its 257 entries and never pass top (at most 32767) blends to a value at most top at any phase (Digital.lutAt).a cutoff or damping coefficient read this way is between 0 and the table's top entry, the range the filters' sample theorems assume; the cut to 16 bits inside the blend changes nothing.
filters_setup_cutoff_in_rangeThe swarm's high-pass coefficient is between 0 and 25078.the coefficient multiplies the band-pass state on every sample; a positive 15-bit coefficient keeps that product in step with the original.
noise_setup_cutoff_in_tableThe filtered noise's cutoff, damping and scale read two neighbouring entries of their 257-entry tables, both inside, for any pitch and parameter; the index is the top byte of the shifted value.the three tables are C data read without a check, and the pitch can be any 16-bit value.
noise_setup_gain_correction_in_rangeThe filtered noise's gain correction is between 0 and 32767, the hypothesis of noise_corrected_never_wraps.the correction multiplies every sample; above 32767 the product could leave 32 bits.
noise_setup_grain_rate_in_tableA grain's rate reads inside the 257-entry rate table for any parameter from 0 to 32767.the table is C data read without a check; the index is the parameter's top eight bits.
noise_setup_grain_rate_boundedA grain's rate is at most 2^18 per sample.noise_grain_envelope_index_in_table needs the rate below 2^18 to keep a block's envelope reads inside the envelope table.
noise_setup_grain_resetAfter the setup, a grain's rate is at most 2^18 and its envelope will not pass 2^24 + 2^22 within the block's 16 samples, given a block that started that way (the first block starts at 0).these are the two hypotheses of noise_grain_envelope_index_in_table, so the envelope reads stay inside their table from the first block on.
noise_setup_clock_period_positiveThe clocked noise's quantization step is between 2048 and 32768 for any held parameter from 0 to 32767.the loop reduces every random value modulo this step (Noise.clocked); a step of 0 would be a division by zero in C, and the setup never asks for fewer than 2 steps.
percussion_setup_partial_decay_in_tableEvery bell and drum partial's two decays are 16-bit, the short one at most the long one; these are the decayFits hypotheses of percussion_decay_never_grows.a decay above 65535, or a short one above the long one, would make Percussion.decay a gain and the partials would grow.
percussion_setup_amplitudes_in_rangeEvery bell and drum partial starts within 16 bits, and the drum's second partial starts at or below 4096 and its fourth at or below 8192.the drum ring-modulates its second and fourth partials by the noise and then by a gain; a louder start would pass 32 bits in the mix.
percussion_setup_drum_cutoff_in_tableThe drum's noise filter reads its two cutoff entries inside the 257-entry cutoff table; the clamped cutoff, shifted by 16, has a top byte of at most 127.the drum's cutoff is the pitch plus a parameter, and the clamp keeps that sum's table read inside the data.
percussion_setup_gains_in_rangeThe gains handed to the drum and snare mixes are in the ranges their sample theorems assume: the drum's noise gain 0 to 12888 and harmonics gain 0 to 16983 (percussion_drum_mix_never_wraps), the snare's two gains 0 to 22000 and 0 to 38383 (percussion_snare_never_wraps), for parameters from 0 to 32767.the mixes multiply a signal by these gains and add; a gain outside its range could push the sum past 32 bits.
physical_setup_pluck_size_validA new plucked string meets the line theorems' hypotheses: its oversampling is at most 9 (oversampled in physical_pluck_read_in_line and physical_pluck_next_in_line; pluckSize and pluckMask are 1024 >>> shift and size - 1 by definition), and its fill count is at most its size (fits in physical_pluck_fill_in_line; the glue only fills while the count is nonzero, that theorem's started).these numbers decide where the plucked string reads and writes its 1025-entry line.
physical_setup_comb_delay_in_lineThe comb filter's whole-sample delay is at most 8192, its line's length.the positions are masked, so a longer delay would not read outside the line but would read the wrong sample; the clamp i