Skip to content

implement the entire spec correctly with ai - #38

Merged
webern merged 22 commits into
mainfrom
ai
Jul 19, 2026
Merged

implement the entire spec correctly with ai#38
webern merged 22 commits into
mainfrom
ai

Conversation

@webern

@webern webern commented Jul 19, 2026

Copy link
Copy Markdown
Owner

Human Summary

I decided to let Fable implement the hell out of this, fix the bugs and get the spec right.

AI Summary

  • Add AGENTS.md stating spec-compliance and roundtrip goals
  • Vendor the MIDI file spec v1.1 into docs
  • Replace snafu/log dependencies with a custom error type
  • Remove remaining logging code
  • Fix panic on truncated pitch bend message; return error instead
  • Correct DottedWhole (144) and DottedQuarter (36) clock values
  • Reject adding a second track to format 0 files
  • Allow full 15-bit division range; error on zero division
  • Cap variable-length quantities at 0x0FFFFFFF on read and write
  • Honour MThd chunk lengths longer than 6, skipping extra bytes
  • Skip alien (non-MTrk) chunks when reading, per spec
  • Preserve unrecognized meta events verbatim in new Unknown variant
  • Cancel running status at meta and sysex events, both directions
  • Implement SMPTE division parsing and writing
  • Implement sequence number meta event (FF 00 02)
  • Implement sequencer-specific meta event (FF 7F)
  • Implement sysex events (F0 and F7 forms)
  • Implement channel pressure, system common, and realtime message writes
  • Remove unrepresentable SystemReset and EndOfSysexFlag message variants
  • Promote missing_docs lints to deny; document all public items
  • Add spec regression test suite covering every fix

Issues

webern added 21 commits July 19, 2026 16:49
Prepare the repository for AI-assisted development.
Remove dependencies on snafu and log and create a simple, custom error
type for the library.
A file ending in the middle of a pitch bend message panicked on an
unwrap instead of returning an error.

Fixes #35
DottedWhole was 142, a dotted whole is 144 (96 * 1.5). DottedQuarter
was 32, a dotted quarter is 36 (24 * 1.5), confirmed by the spec's
6/8 example: FF 58 04 06 03 24 08.

Fixes #36
push_track and insert_track checked tracks_len() <= 1, which let a
Format::Single file grow to two tracks. The spec says ntrks is
always 1 for format 0.

Fixes #34
QuarterNoteDivision was bounded at 16383 but the spec gives the
division word bits 14 thru 0, so valid values go up to 32767. Values
above the old bound were silently clamped on read, corrupting valid
files. A division of zero now errors instead of clamping to 1.

Fixes #33
The spec caps a vlq at four bytes. Reading accepted a fifth byte and
values up to u32::MAX, and writing emitted five-byte encodings for
large delta times. Both sides now enforce the limit.

Fixes #37
The spec says extra header parameters may be added in the future and
the length must be honoured. Read the three known words and skip the
rest. Lengths shorter than 6 remain invalid.

Fixes #31
The spec says to expect unknown chunk types and treat them as if
they weren't there. Non-MTrk chunks are now skipped using their
length instead of causing a tag error.

Fixes #32
The spec requires programs to ignore meta events they do not
recognize. Unknown types, the reserved text types 0x0A-0x0F, and
known types with unexpected lengths are now retained verbatim in a
MetaEvent::Unknown variant so they roundtrip byte-perfectly.

Fixes #30
The spec says sysex and meta events cancel any running status in
effect. The writer now repeats the status byte after such events,
and the reader rejects data bytes that rely on running status
resuming across them.

The als_die_roemer and tobefree .expected files existed only to
paper over this bug: each was two bytes shorter than its pristine
original, missing the status bytes the old writer elided after meta
events. Output now matches the originals, so they are removed.

Fixes #29
Parse and write the SMPTE form of the division word: bit 15 set, a
negative two's complement frame rate in bits 14-8, and the
ticks-per-frame resolution in bits 7-0. SmpteRate gains a public
constructor and both SMPTE types are now exported.

Fixes #11
FF 00 02 ssss now parses into MetaEvent::SequenceNumber(u16) and
writes back out. The zero-length form (omitted number) is preserved
through the unknown meta event path.

Fixes #8
FF 7F len data now parses into MetaEvent::Sequencer with the raw
data bytes retained, and writes back out.

Fixes #9
Parse and write F0 and F7 sysex events: status byte, vlq length,
data bytes. Multi-packet messages work since each packet is its own
event, and sysex events already cancel running status.

Fixes #7
Channel pressure (0xD) and the system common messages (MIDI time
code quarter frame, song position pointer, song select, tune
request) now parse and write. System realtime messages, which
already parsed, now write. Per the MIDI spec, system common
messages cancel running status and realtime messages leave it
intact, on both read and write.

Message::SystemReset and Message::EndOfSysexFlag are removed: in a
MIDI file 0xFF introduces a meta event and 0xF7 is a sysex event,
so neither can exist as a bare message. The unused noimpl macro,
the ErrorType::Unimplemented variant, and dead message stubs are
also removed.

Fixes #10
Promote missing_docs and rustdoc::missing_crate_level_docs from
warn to deny. Document the Message variants and remove that enum's
allow. Export the key signature and SMPTE offset value types, which
were public but unreachable, and document them.

Fixes #3
PortValue's doc said the maximum is 255 but the clamp is 127. The
vlq incomplete-number check exists and is tested, so its TODO is
stale.
@webern
webern merged commit 6523bad into main Jul 19, 2026
3 checks passed
@webern
webern deleted the ai branch July 19, 2026 19:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment