2024-08-11 04:25:14 +03:00
|
|
|
package midi
|
|
|
|
|
|
|
|
// https://www.midi.org/specifications/item/the-midi-1-0-specification
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"embed"
|
2024-08-13 05:32:03 +03:00
|
|
|
"fmt"
|
2024-08-11 04:25:14 +03:00
|
|
|
|
|
|
|
"github.com/wader/fq/format"
|
|
|
|
"github.com/wader/fq/pkg/decode"
|
|
|
|
"github.com/wader/fq/pkg/interp"
|
|
|
|
)
|
|
|
|
|
|
|
|
//go:embed midi.md
|
|
|
|
var midiFS embed.FS
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
interp.RegisterFormat(
|
|
|
|
format.MIDI,
|
|
|
|
&decode.Format{
|
|
|
|
Description: "Standard MIDI file",
|
|
|
|
DecodeFn: decodeMIDI,
|
|
|
|
})
|
|
|
|
|
|
|
|
interp.RegisterFS(midiFS)
|
|
|
|
}
|
|
|
|
|
2024-08-11 06:32:44 +03:00
|
|
|
func decodeMIDI(d *decode.D) any {
|
|
|
|
d.Endian = decode.BigEndian
|
|
|
|
|
|
|
|
decodeMIDIFile(d)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-08-11 04:25:14 +03:00
|
|
|
func decodeMIDIFile(d *decode.D) {
|
|
|
|
d.FieldStruct("header", decodeMThd)
|
|
|
|
|
|
|
|
d.FieldArray("tracks", func(d *decode.D) {
|
2024-08-11 06:32:44 +03:00
|
|
|
for d.BitsLeft() > 0 {
|
2024-08-11 04:25:14 +03:00
|
|
|
d.FieldStruct("track", decodeMTrk)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func decodeMThd(d *decode.D) {
|
|
|
|
d.AssertLeastBytesLeft(8)
|
|
|
|
|
|
|
|
if !bytes.Equal(d.PeekBytes(4), []byte("MThd")) {
|
|
|
|
d.Errorf("no MThd marker")
|
|
|
|
}
|
|
|
|
|
|
|
|
d.FieldArray("header", func(d *decode.D) {
|
2024-08-11 06:32:44 +03:00
|
|
|
d.FieldUTF8NullFixedLen("tag", 4)
|
2024-08-11 04:25:14 +03:00
|
|
|
length := d.FieldU32("length")
|
|
|
|
|
2024-08-11 06:32:44 +03:00
|
|
|
d.FramedFn(int64(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)
|
|
|
|
}
|
2024-08-11 04:25:14 +03:00
|
|
|
|
2024-08-11 06:32:44 +03:00
|
|
|
tracks := d.FieldU16("tracks")
|
|
|
|
if format == 0 && tracks > 1 {
|
|
|
|
d.Errorf("MIDI format 0 expects 1 track (got %v)", tracks)
|
2024-08-11 04:25:14 +03:00
|
|
|
}
|
|
|
|
|
2024-08-11 06:32:44 +03:00
|
|
|
division := d.FieldU16("division")
|
|
|
|
if division&0x8000 == 0x8000 {
|
|
|
|
SMPTE := (division & 0xff00) >> 8
|
|
|
|
if SMPTE != 0xe8 && SMPTE != SMPTE && SMPTE != 0xe6 && SMPTE != 0xe5 {
|
|
|
|
d.Errorf("invalid MThd division SMPTE timecode type %02X (expected E8,E7, E6 or E5)", SMPTE)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2024-08-11 04:25:14 +03:00
|
|
|
})
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func decodeMTrk(d *decode.D) {
|
|
|
|
d.AssertLeastBytesLeft(8)
|
|
|
|
|
|
|
|
if !bytes.Equal(d.PeekBytes(4), []byte("MTrk")) {
|
|
|
|
d.Errorf("no MTrk marker")
|
|
|
|
}
|
|
|
|
|
2024-08-11 06:32:44 +03:00
|
|
|
d.FieldUTF8NullFixedLen("tag", 4)
|
2024-08-11 04:25:14 +03:00
|
|
|
length := d.FieldU32("length")
|
|
|
|
|
2024-08-11 06:32:44 +03:00
|
|
|
d.FieldArray("events", func(d *decode.D) {
|
|
|
|
d.FramedFn(int64(length)*8, func(d *decode.D) {
|
2024-08-13 05:05:15 +03:00
|
|
|
for d.BitsLeft() > 0 {
|
2024-08-13 05:42:23 +03:00
|
|
|
// d.FieldStruct("event", decodeEvent)
|
|
|
|
decodeEvent(d)
|
2024-08-13 05:05:15 +03:00
|
|
|
}
|
2024-08-11 06:32:44 +03:00
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func decodeEvent(d *decode.D) {
|
2024-08-13 05:05:15 +03:00
|
|
|
_, status, event := peekEvent(d)
|
2024-08-11 06:32:44 +03:00
|
|
|
|
2024-08-13 05:32:03 +03:00
|
|
|
fmt.Printf(">> status:%02x event:%02x\n", status, event)
|
|
|
|
|
2024-08-11 06:32:44 +03:00
|
|
|
// ... meta event?
|
2024-08-13 05:05:15 +03:00
|
|
|
if status == 0xff {
|
|
|
|
switch MetaEventType(event) {
|
2024-08-11 06:32:44 +03:00
|
|
|
case TypeTrackName:
|
|
|
|
d.FieldStruct("TrackName", decodeTrackName)
|
|
|
|
return
|
2024-08-12 08:22:20 +03:00
|
|
|
|
|
|
|
case TypeTempo:
|
|
|
|
d.FieldStruct("Tempo", decodeTempo)
|
|
|
|
return
|
2024-08-13 05:32:03 +03:00
|
|
|
|
|
|
|
case TypeTimeSignature:
|
|
|
|
d.FieldStruct("TimeSignature", decodeTimeSignature)
|
|
|
|
return
|
2024-08-14 06:24:19 +03:00
|
|
|
|
|
|
|
case TypeEndOfTrack:
|
|
|
|
d.FieldStruct("EndOfTrack", decodeEndOfTrack)
|
|
|
|
return
|
|
|
|
|
|
|
|
// TypeSequenceNumber MetaEventType = 0x00
|
|
|
|
// TypeText MetaEventType = 0x01
|
|
|
|
// TypeCopyright MetaEventType = 0x02
|
|
|
|
// TypeInstrumentName MetaEventType = 0x04
|
|
|
|
// TypeLyric MetaEventType = 0x05
|
|
|
|
// TypeMarker MetaEventType = 0x06
|
|
|
|
// TypeCuePoint MetaEventType = 0x07
|
|
|
|
// TypeProgramName MetaEventType = 0x08
|
|
|
|
// TypeDeviceName MetaEventType = 0x09
|
|
|
|
// TypeMIDIChannelPrefix MetaEventType = 0x20
|
|
|
|
// TypeMIDIPort MetaEventType = 0x21
|
|
|
|
// TypeSMPTEOffset MetaEventType = 0x54
|
|
|
|
// TypeKeySignature MetaEventType = 0x59
|
|
|
|
// TypeSequencerSpecificEvent MetaEventType = 0x7f
|
2024-08-11 06:32:44 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-14 06:24:19 +03:00
|
|
|
// ... midi event?
|
|
|
|
switch MidiEventType(status & 0xf0) {
|
|
|
|
case TypeProgramChange:
|
|
|
|
d.FieldStruct("ProgramChange", decodeProgramChange)
|
|
|
|
return
|
|
|
|
|
|
|
|
// TypeNoteOff MidiEventType = 0x80
|
|
|
|
// TypeNoteOn MidiEventType = 0x90
|
|
|
|
// TypePolyphonicPressure MidiEventType = 0xa0
|
|
|
|
// TypeController MidiEventType = 0xb0
|
|
|
|
// TypeProgramChange MidiEventType = 0xc0
|
|
|
|
// TypeChannelPressure MidiEventType = 0xd0
|
|
|
|
// TypePitchBend MidiEventType = 0xe0
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Printf(">>>>>>>>>>> UNKNOWN: %02x %02x\n", status, event)
|
|
|
|
|
2024-08-13 05:05:15 +03:00
|
|
|
// ... unknown event - flush remaining data
|
|
|
|
var N int = int(d.BitsLeft())
|
|
|
|
|
|
|
|
d.Bits(N)
|
|
|
|
}
|
|
|
|
|
|
|
|
func peekEvent(d *decode.D) (uint64, uint8, uint8) {
|
|
|
|
N := 3
|
|
|
|
|
|
|
|
for {
|
|
|
|
bytes := d.PeekBytes(N)
|
|
|
|
|
|
|
|
// ... peek at delta value
|
|
|
|
delta := uint64(0)
|
|
|
|
|
|
|
|
for i, b := range bytes[:N-2] {
|
|
|
|
delta <<= 7
|
|
|
|
delta += uint64(b & 0x7f)
|
|
|
|
|
|
|
|
if b&0x80 == 0 {
|
|
|
|
status := bytes[i+1]
|
|
|
|
event := bytes[i+2]
|
|
|
|
return delta, status, event
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
N++
|
|
|
|
}
|
2024-08-11 04:25:14 +03:00
|
|
|
}
|
|
|
|
|
2024-08-11 06:32:44 +03:00
|
|
|
func vlq(d *decode.D) uint64 {
|
|
|
|
vlq := uint64(0)
|
2024-08-11 04:25:14 +03:00
|
|
|
|
2024-08-11 06:32:44 +03:00
|
|
|
for {
|
|
|
|
b := d.BytesLen(1)
|
2024-08-11 04:25:14 +03:00
|
|
|
|
2024-08-11 06:32:44 +03:00
|
|
|
vlq <<= 7
|
|
|
|
vlq += uint64(b[0] & 0x7f)
|
|
|
|
|
|
|
|
if b[0]&0x80 == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return vlq
|
|
|
|
}
|
|
|
|
|
2024-08-12 08:22:20 +03:00
|
|
|
func vlf(d *decode.D) []uint8 {
|
2024-08-11 06:32:44 +03:00
|
|
|
N := int(vlq(d))
|
|
|
|
|
|
|
|
return d.BytesLen(N)
|
2024-08-11 04:25:14 +03:00
|
|
|
}
|