Skip to content

Commit

Permalink
midi: fixes for PR comments:
Browse files Browse the repository at this point in the history
- Renamed 'tracks' section to 'content' (cf. #1004 (comment))
- Removed 'validation only' errors (cf. #1004 (comment))
- Decoded MThd divisions field with SMPTE timecode
  • Loading branch information
twystd committed Sep 6, 2024
1 parent 87c80f5 commit 9c7f7f9
Show file tree
Hide file tree
Showing 21 changed files with 218 additions and 154 deletions.
7 changes: 7 additions & 0 deletions format/midi/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,10 @@ var framerates = scalar.UintMapSymUint{
2: 29,
3: 30,
}

var fps = scalar.UintMapSymUint{
0xe8: 24,
0xe7: 25,
0xe6: 29,
0xe5: 30,
}
32 changes: 13 additions & 19 deletions format/midi/midi.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ func decodeMIDI(d *decode.D) any {
// ... decode header
d.FieldStruct("header", decodeMThd)

// ... decode tracks
d.FieldArray("tracks", func(d *decode.D) {
// ... decode tracks (and other chunks)
d.FieldArray("content", func(d *decode.D) {
for d.BitsLeft() > 0 {
if bytes.Equal(d.PeekBytes(4), []byte("MTrk")) {
d.FieldStruct("track", decodeMTrk)
Expand All @@ -55,36 +55,30 @@ func decodeMIDI(d *decode.D) any {

func decodeMThd(d *decode.D) {
if !bytes.Equal(d.PeekBytes(4), []byte("MThd")) {
d.Errorf("no MThd marker")
d.Errorf("missing MThd tag")
}

d.FieldUTF8("tag", 4)
length := d.FieldS32("length")

d.FramedFn(length*8, func(d *decode.D) {
format := d.FieldU16("format")
if format != 0 && format != 1 && format != 2 {
d.Errorf("invalid MThd format %v (expected 0,1 or 2)", format)
}

tracks := d.FieldU16("tracks")
if format == 0 && tracks > 1 {
d.Errorf("MIDI format 0 expects 1 track (got %v)", tracks)
}
d.FieldU16("format")
d.FieldU16("tracks")

division := d.FieldU16("divisions")
if division&0x8000 == 0x8000 {
SMPTE := (division & 0xff00) >> 8
if SMPTE != 0xe8 && SMPTE != 0xe7 && SMPTE != 0xe6 && SMPTE != 0xe5 {
d.Errorf("invalid MThd division SMPTE timecode type %02X (expected E8,E7, E6 or E5)", SMPTE)
d.FieldStruct("division", func(d *decode.D) {
if division := d.PeekUintBits(16); division&0x8000 == 0x8000 {
d.FieldU8("fps", fps)
d.FieldU8("resolution")
} else {
d.FieldU16("ppqn")
}
}
})
})
}

func decodeMTrk(d *decode.D) {
if !bytes.Equal(d.PeekBytes(4), []byte("MTrk")) {
d.Errorf("no MTrk marker")
d.Errorf("missing MTrk tag")
}

d.FieldUTF8("tag", 4)
Expand Down
1 change: 0 additions & 1 deletion format/midi/midi.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,3 @@ fq -d midi 'grep_by(.event=="note_on") | [.time.tick, .note_on.note] | join(" ")
2. [Standard MIDI Files](https://midi.org/standard-midi-files)
3. [Standard MIDI File (SMF) Format](http://midi.teragonaudio.com/tech/midifile.htm)
4. [MIDI Files Specification](http://www.somascape.org/midi/tech/mfile.html)
5. [github: test-midi-files](https://github.com/jazz-soft/test-midi-files)
4 changes: 0 additions & 4 deletions format/midi/sysex.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ func decodeSysExEvent(d *decode.D, status uint8, ctx *context) {
switch {

case status == 0xf0:
if ctx.casio {
d.Errorf("SysEx message F0 start byte without terminating F7")
}

d.FieldStruct("sysex_event", func(d *decode.D) {
d.FieldStruct("time", delta)
d.FieldU8("event", sysex)
Expand Down
3 changes: 2 additions & 1 deletion format/midi/testdata/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ run: build
go run . -d midi dv format/midi/testdata/reference.mid

debug: build
go run . -d midi dv format/midi/workdir/test-all-gm-percussion.mid
go run . -d midi dv format/midi/testdata/midi/smpte-timecode.mid
go run . -d midi dv format/midi/testdata/midi/format-0.mid

test: build
go test ./format -run TestFormats/midi
Expand Down
17 changes: 10 additions & 7 deletions format/midi/testdata/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,33 @@ Basic MIDI format 1 test file. Contains two tracks, each with only a _track name
4. _format-2.mid_
Basic MIDI format 2 test file. Contains two tracks, each with only a _track name_ and _end-of-track_ events.

5. _empty.mid_
5. _smpte-timecode.mid_
MIDI format 0 test file with an SMPTE timecode for the divisions field.

6. _empty.mid_
Empty MIDI file to verify MIDI decoder handles empty files.

6. _key_signatures.mid_
7. _key_signatures.mid_

Test file with all supported MIDI key signatures.

7. _notes.mid_
8. _notes.mid_

Test file with all supported MIDI notes.

8. _unknown-chunks.mid_
9. _unknown-chunks.mid_

Test file with 'alien' chunks interleaved with the _MTrk_ track chunks.

9. _invalid-MThd-length.mid_
10. _invalid-MThd-length.mid_

Test file with invalid _MThd_ chunk length.

10. _invalid-MTrk-length.mid_
11. _invalid-MTrk-length.mid_

Test file with invalid _MTrk_ chunk length.

11. _twinkle.mid_
12. _twinkle.mid_

Sample valid MIDI file for the example queries in the help.

Expand Down
5 changes: 3 additions & 2 deletions format/midi/testdata/format-0.fqtest
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ $ fq -d midi dv midi/format-0.mid
0x00| 00 00 00 06 | .... | length: 6 0x4-0x8 (4)
0x00| 00 00 | .. | format: 0 0x8-0xa (2)
0x00| 00 01 | .. | tracks: 1 0xa-0xc (2)
0x00| 01 e0 | .. | divisions: 480 0xc-0xe (2)
| | | tracks[0:1]: 0xe-0x26 (24)
| | | division{}: 0xc-0xe (2)
0x00| 01 e0 | .. | ppqn: 480 0xc-0xe (2)
| | | content[0:1]: 0xe-0x26 (24)
| | | [0]{}: track 0xe-0x26 (24)
0x00| 4d 54| MT| tag: "MTrk" 0xe-0x12 (4)
0x10|72 6b |rk |
Expand Down
5 changes: 3 additions & 2 deletions format/midi/testdata/format-1.fqtest
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ $ fq -d midi dv midi/format-1.mid
0x00| 00 00 00 06 | .... | length: 6 0x4-0x8 (4)
0x00| 00 01 | .. | format: 1 0x8-0xa (2)
0x00| 00 02 | .. | tracks: 2 0xa-0xc (2)
0x00| 01 e0 | .. | divisions: 480 0xc-0xe (2)
| | | tracks[0:2]: 0xe-0x45 (55)
| | | division{}: 0xc-0xe (2)
0x00| 01 e0 | .. | ppqn: 480 0xc-0xe (2)
| | | content[0:2]: 0xe-0x45 (55)
| | | [0]{}: track 0xe-0x26 (24)
0x00| 4d 54| MT| tag: "MTrk" 0xe-0x12 (4)
0x10|72 6b |rk |
Expand Down
5 changes: 3 additions & 2 deletions format/midi/testdata/format-2.fqtest
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ $ fq -d midi dv midi/format-2.mid
0x00| 00 00 00 06 | .... | length: 6 0x4-0x8 (4)
0x00| 00 02 | .. | format: 2 0x8-0xa (2)
0x00| 00 02 | .. | tracks: 2 0xa-0xc (2)
0x00| 01 e0 | .. | divisions: 480 0xc-0xe (2)
| | | tracks[0:2]: 0xe-0x43 (53)
| | | division{}: 0xc-0xe (2)
0x00| 01 e0 | .. | ppqn: 480 0xc-0xe (2)
| | | content[0:2]: 0xe-0x43 (53)
| | | [0]{}: track 0xe-0x28 (26)
0x00| 4d 54| MT| tag: "MTrk" 0xe-0x12 (4)
0x10|72 6b |rk |
Expand Down
1 change: 0 additions & 1 deletion format/midi/testdata/help_midi.fqtest
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,3 @@ References
* Standard MIDI Files (https://midi.org/standard-midi-files)
* Standard MIDI File (SMF) Format (http://midi.teragonaudio.com/tech/midifile.htm)
* MIDI Files Specification (http://www.somascape.org/midi/tech/mfile.html)
* github: test-midi-files (https://github.com/jazz-soft/test-midi-files)
28 changes: 14 additions & 14 deletions format/midi/testdata/key-changes.fqtest
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
$ fq -d midi '.. | select(.event=="key_signature")?.key_signature' midi/twinkle.mid
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
0xf0| 00 01 | .. |.tracks[1].events[2].key_signature: "A minor" (1)
0xf0| 00 01 | .. |.content[1].events[2].key_signature: "A minor" (1)
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
0x390| 00 01| ..|.tracks[2].events[2].key_signature: "A minor" (1)
0x390| 00 01| ..|.content[2].events[2].key_signature: "A minor" (1)
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
0x3d0| 00 01 | .. |.tracks[3].events[2].key_signature: "A minor" (1)
0x3d0| 00 01 | .. |.content[3].events[2].key_signature: "A minor" (1)
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
0x550| 00 01 | .. |.tracks[4].events[2].key_signature: "A minor" (1)
0x550| 00 01 | .. |.content[4].events[2].key_signature: "A minor" (1)
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
0x6d0|00 01 |.. |.tracks[5].events[2].key_signature: "A minor" (1)
0x6d0|00 01 |.. |.content[5].events[2].key_signature: "A minor" (1)
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
0x9f0| 00 01 | .. |.tracks[6].events[2].key_signature: "A minor" (1)
0x9f0| 00 01 | .. |.content[6].events[2].key_signature: "A minor" (1)
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
0xeb0| 00 01 | .. |.tracks[7].events[2].key_signature: "A minor" (1)
0xeb0| 00 01 | .. |.content[7].events[2].key_signature: "A minor" (1)
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
0xfa0| 00 01 | .. |.tracks[8].events[2].key_signature: "A minor" (1)
0xfa0| 00 01 | .. |.content[8].events[2].key_signature: "A minor" (1)
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
0x1700| 00 01 | .. |.tracks[9].events[2].key_signature: "A minor" (1)
0x1700| 00 01 | .. |.content[9].events[2].key_signature: "A minor" (1)
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
0x2080| 00 01 | .. |.tracks[10].events[2].key_signature: "A minor" (1)
0x2080| 00 01 | .. |.content[10].events[2].key_signature: "A minor" (1)
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
0x2280| 00| .|.tracks[11].events[2].key_signature: "A minor" (1)
0x2280| 00| .|.content[11].events[2].key_signature: "A minor" (1)
0x2290|01 |. |
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
0x2460| 00 01 | .. |.tracks[12].events[2].key_signature: "A minor" (1)
0x2460| 00 01 | .. |.content[12].events[2].key_signature: "A minor" (1)
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
0x25f0| 00 01 | .. |.tracks[13].events[2].key_signature: "A minor" (1)
0x25f0| 00 01 | .. |.content[13].events[2].key_signature: "A minor" (1)
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
0x2860| 00 01 | .. |.tracks[14].events[2].key_signature: "A minor" (1)
0x2860| 00 01 | .. |.content[14].events[2].key_signature: "A minor" (1)
5 changes: 3 additions & 2 deletions format/midi/testdata/keys.fqtest
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ $ fq -d midi d midi/key-signatures.mid
0x000| 00 00 00 06 | .... | length: 6
0x000| 00 00 | .. | format: 0
0x000| 00 01 | .. | tracks: 1
0x000| 01 e0 | .. | divisions: 480
| | | tracks[0:1]:
| | | division{}:
0x000| 01 e0 | .. | ppqn: 480
| | | content[0:1]:
| | | [0]{}: track
0x000| 4d 54| MT| tag: "MTrk"
0x010|72 6b |rk |
Expand Down
Loading

0 comments on commit 9c7f7f9

Please sign in to comment.