mirror of
https://github.com/wader/fq.git
synced 2024-11-29 04:10:01 +03:00
87c80f5776
- Reworked event types as snake-case (cf. https://github.com/wader/fq/pull/1004#discussion_r1745101588) - Decoded 'alien' chunks (cf. https://github.com/wader/fq/pull/1004#discussion_r1745082089)
123 lines
2.8 KiB
Go
123 lines
2.8 KiB
Go
package midi
|
|
|
|
import (
|
|
"github.com/wader/fq/pkg/decode"
|
|
"github.com/wader/fq/pkg/scalar"
|
|
)
|
|
|
|
const (
|
|
NoteOff uint64 = 0x80
|
|
NoteOn uint64 = 0x90
|
|
PolyphonicPressure uint64 = 0xa0
|
|
Controller uint64 = 0xb0
|
|
ProgramChange uint64 = 0xc0
|
|
ChannelPressure uint64 = 0xd0
|
|
PitchBend uint64 = 0xe0
|
|
)
|
|
|
|
var midievents = scalar.UintMapSymStr{
|
|
NoteOff: "note_off",
|
|
NoteOn: "note_on",
|
|
PolyphonicPressure: "polyphonic_pressure",
|
|
Controller: "controller",
|
|
ProgramChange: "program_change",
|
|
ChannelPressure: "channel_pressure",
|
|
PitchBend: "pitch_bend",
|
|
}
|
|
|
|
var midifns = map[uint64]func(d *decode.D){
|
|
NoteOff: decodeNoteOff,
|
|
NoteOn: decodeNoteOn,
|
|
PolyphonicPressure: decodePolyphonicPressure,
|
|
Controller: decodeController,
|
|
ProgramChange: decodeProgramChange,
|
|
ChannelPressure: decodeChannelPressure,
|
|
PitchBend: decodePitchBend,
|
|
}
|
|
|
|
func decodeMIDIEvent(d *decode.D, status uint8, ctx *context) {
|
|
if status < 0x80 {
|
|
status = ctx.running
|
|
}
|
|
|
|
ctx.running = status
|
|
ctx.casio = false
|
|
|
|
delta := func(d *decode.D) {
|
|
ctx.tick += d.FieldUintFn("delta", vlq)
|
|
d.FieldValueUint("tick", ctx.tick)
|
|
}
|
|
|
|
if fn, ok := midifns[uint64(status&0x00f0)]; ok {
|
|
d.FieldStruct("midi_event", func(d *decode.D) {
|
|
d.FieldStruct("time", delta)
|
|
|
|
b := d.PeekBytes(1)
|
|
if b[0] >= 0x80 {
|
|
d.FieldUintFn("event", func(d *decode.D) uint64 {
|
|
return d.U4() << 4
|
|
}, midievents)
|
|
d.FieldU4("channel")
|
|
} else {
|
|
d.FieldValueUint("event", uint64(status&0x00f0), midievents)
|
|
d.FieldValueUint("channel", uint64(status&0x000f))
|
|
}
|
|
|
|
fn(d)
|
|
})
|
|
} else {
|
|
flush(d, "unknown MIDI event (%02x)", status&0xf0)
|
|
}
|
|
}
|
|
|
|
func decodeNoteOff(d *decode.D) {
|
|
d.FieldStruct("note_off", func(d *decode.D) {
|
|
d.FieldU8("note", notes)
|
|
d.FieldUintFn("velocity", func(d *decode.D) uint64 {
|
|
return d.U8() & 0x7f
|
|
})
|
|
})
|
|
}
|
|
|
|
func decodeNoteOn(d *decode.D) {
|
|
d.FieldStruct("note_on", func(d *decode.D) {
|
|
d.FieldU8("note", notes)
|
|
d.FieldUintFn("velocity", func(d *decode.D) uint64 {
|
|
return d.U8() & 0x7f
|
|
})
|
|
})
|
|
}
|
|
|
|
func decodePolyphonicPressure(d *decode.D) {
|
|
d.FieldU8("polyphonic_pressure")
|
|
}
|
|
|
|
func decodeController(d *decode.D) {
|
|
d.FieldStruct("controller", func(d *decode.D) {
|
|
d.FieldU8("controller", controllers)
|
|
d.FieldU8("value")
|
|
})
|
|
}
|
|
|
|
func decodeProgramChange(d *decode.D) {
|
|
d.FieldU8("program_change")
|
|
}
|
|
|
|
func decodeChannelPressure(d *decode.D) {
|
|
d.FieldU8("channel_pressure")
|
|
}
|
|
|
|
func decodePitchBend(d *decode.D) {
|
|
d.FieldSintFn("pitch_bend", func(d *decode.D) int64 {
|
|
// ... 14 bit range i.e. [0..16383]
|
|
bytes := d.BytesLen(2)
|
|
|
|
bend := uint64(bytes[0]) & 0x7f
|
|
bend <<= 7
|
|
bend |= uint64(bytes[1]) & 0x7f
|
|
|
|
// ... centre value (0) is 0x2000 (81920)
|
|
return int64(bend) - 8192
|
|
})
|
|
}
|