apirrone aa1a8126e9 Chorale: notes as the score, a text format, and MIDI import
All three, because they are one change: `Score` now holds `Vec<Note>` rather than
`Vec<Gesture>`, and that is what lets there be more than one way to write a song.

**Notes, not gestures.** `Gesture` is now one *front end* — the hand-authoring one,
where "assemble this chord from the bass up" is the intent you want in the source.
A MIDI file has no gestures to recover: staggered entries and solos are already
plain note timings. Both paths converge on notes, and everything downstream reads
one thing.

**A text format** (`sounds/scores/*.duckscore`), loaded at runtime, note names
rather than MIDI numbers, running state for what a score marks once and means until
further notice. The shipped piece *is* `scores/wistful.duckscore`, embedded with
`include_str!`, so the worked example and the default score cannot drift apart.
Errors name the line and quote it — a score is hand-written data, so a parse error
is the normal way to find out you mistyped a note name.

**MIDI import**, no dependency: an SMF is a length-prefixed chunk format and the
subset a score needs is note-on, note-off, tempo and track name. This is the front
end that actually unlocks writing music — MuseScore becomes the score editor, and
the whole public-domain four-part repertoire is already available as MIDI.

Tracks become parts by **mean pitch**, not track order. Track order is wrong twice
over: notation editors write top staff first (the reverse of a voicing), and a DAW
file may have the tempo track, empty tracks, or any order at all. A track *named*
"Soprano" is believed first, since a human wrote it down. A single polyphonic track
— what "export MIDI" gives you from many tools — is split by pitch rank per chord.
Velocity becomes the dynamic. A tempo change is sung at the first tempo and *says*
it was, because four robots honouring a tempo map over a network is a promise this
cannot keep.

Also: notes carry a dynamic and a vowel. Ducks cannot pronounce words, but a vowel
is most of what a listener hears as singing rather than humming — `oo` and `ee` are
unmistakably different even with only a formant boost on one harmonic of seven, and
the mouth opening a vowel implies is the *same number* the beak servo gets, so it is
visible as well as audible. `mm` sits back in the mix, which is how a real choir
holds a chord behind a soloist.

One rule worth writing down: a tie survives a change of *dynamic* but not of
*vowel*. A mark over a sounding note is a crescendo — re-attacking it would be the
one thing a crescendo is not — while a new syllable is a new note. Found because
`dynamic f` mid-phrase was chopping held notes into pieces.

Verified end to end rather than only in unit tests: a hand-built 4-track SMF in
notation-editor order imports with its parts right, its tempo read, four different
rhythms intact, and all four fundamentals in tune by FFT.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 16:33:11 +02:00
..