11package pl.lemanski.mikroSoundFont.io.midi
22
3+ import pl.lemanski.mikroSoundFont.InvalidMidiDataException
34import pl.lemanski.mikroSoundFont.getLogger
45import pl.lemanski.mikroSoundFont.midi.MidiMessage
56import pl.lemanski.mikroSoundFont.midi.MidiMessage.Type
6- import pl.lemanski.mikroSoundFont.midi.MidiMessageControlChange
7- import pl.lemanski.mikroSoundFont.midi.MidiMessageNoteOff
8- import pl.lemanski.mikroSoundFont.midi.MidiMessageNoteOn
9- import pl.lemanski.mikroSoundFont.midi.MidiMessageProgramChange
107
118class MidiFileParser {
129 private val logger = getLogger()
1310
14- fun parseMidiFile (data : ByteArray ): List <MidiMessage > {
15- val header = parseMidiHeader(data)
16- val events = mutableListOf<MidiMessage >()
17-
18- var trackStart = 14 // Header ends at byte 14
19- repeat(header.numTracks) {
20- events.addAll(parseTrack(data, trackStart))
21- trackStart + = 8 // Skip over track header and length (MTrk + track length)
22- }
23-
24- return events
25- }
26-
2711 private fun readVariableLengthQuantity (data : ByteArray , index : Int ): Pair <Int , Int > {
2812 var value = 0
2913 var i = index
@@ -38,17 +22,6 @@ class MidiFileParser {
3822 return Pair (value, i)
3923 }
4024
41- private fun parseMidiHeader (data : ByteArray ): MidiFileHeader {
42- val chunkType = data.sliceArray(0 .. 3 ).map { it.toInt().toChar() }.joinToString(" " )
43- if (chunkType != " MThd" ) throw IllegalArgumentException (" Invalid MIDI header" )
44-
45- val format = ((data[8 ].toInt() and 0xFF ) shl 8 ) or (data[9 ].toInt() and 0xFF )
46- val numTracks = ((data[10 ].toInt() and 0xFF ) shl 8 ) or (data[11 ].toInt() and 0xFF )
47- val division = ((data[12 ].toInt() and 0xFF ) shl 8 ) or (data[13 ].toInt() and 0xFF )
48-
49- return MidiFileHeader (format, numTracks, division)
50- }
51-
5225 fun parseTrack (data : ByteArray , trackStart : Int ): List <MidiMessage > {
5326 val events = mutableListOf<MidiMessage >()
5427 var index = trackStart + 8 // Skip "MTrk" and track length
@@ -59,6 +32,8 @@ class MidiFileParser {
5932 val (deltaTime, newIndex) = readVariableLengthQuantity(data, index)
6033 index = newIndex
6134
35+ if (index >= data.size - 1 ) break
36+
6237 val statusByte = data[index].toInt() and 0xFF
6338 index++
6439
@@ -73,44 +48,147 @@ class MidiFileParser {
7348 val channel = lastStatusByte and 0x0F
7449
7550 when (command.toType()) {
76- Type .NOTE_OFF -> {
77- val key = data[index].toInt() and 0xFF
78- events.add(MidiMessageNoteOff (deltaTime, channel, key))
79- index + = 2
80- }
81- Type .NOTE_ON -> {
82- val key = data[index].toInt() and 0xFF
83- val velocity = data[index + 1 ].toInt() and 0xFF
84- events.add(MidiMessageNoteOn (deltaTime, channel, key, velocity))
85- index + = 2
86- }
87- Type .PROGRAM_CHANGE -> {
88- val program = data[index].toInt() and 0xFF
89- events.add(MidiMessageProgramChange (deltaTime, channel, program))
90- index++
91- }
92- Type .CONTROL_CHANGE -> {
93- val control = data[index].toInt() and 0xFF
94- val value = data[index + 1 ].toInt() and 0xFF
95- events.add(MidiMessageControlChange (deltaTime, channel, control, value))
96- index + = 2
97- }
98- Type .KEY_PRESSURE -> {
99- logger.log(" KEY_PRESSURE" )
100- }
101- Type .CHANNEL_PRESSURE -> {
102- logger.log(" CHANNEL_PRESSURE" )
103- }
104- Type .PITCH_BEND -> {
105- logger.log(" PITCH_BEND" )
106- }
107- Type .SET_TEMPO -> {
108- logger.log(" SET_TEMPO" )
109- }
51+ Type .ControlChange .TML_BANK_SELECT_MSB -> TODO ()
52+ Type .ControlChange .TML_MODULATIONWHEEL_MSB -> TODO ()
53+ Type .ControlChange .TML_BREATH_MSB -> TODO ()
54+ Type .ControlChange .TML_FOOT_MSB -> TODO ()
55+ Type .ControlChange .TML_PORTAMENTO_TIME_MSB -> TODO ()
56+ Type .ControlChange .TML_DATA_ENTRY_MSB -> TODO ()
57+ Type .ControlChange .TML_VOLUME_MSB -> TODO ()
58+ Type .ControlChange .TML_BALANCE_MSB -> TODO ()
59+ Type .ControlChange .TML_PAN_MSB -> TODO ()
60+ Type .ControlChange .TML_EXPRESSION_MSB -> TODO ()
61+ Type .ControlChange .TML_EFFECTS1_MSB -> TODO ()
62+ Type .ControlChange .TML_EFFECTS2_MSB -> TODO ()
63+ Type .ControlChange .TML_GPC1_MSB -> TODO ()
64+ Type .ControlChange .TML_GPC2_MSB -> TODO ()
65+ Type .ControlChange .TML_GPC3_MSB -> TODO ()
66+ Type .ControlChange .TML_GPC4_MSB -> TODO ()
67+ Type .ControlChange .TML_BANK_SELECT_LSB -> TODO ()
68+ Type .ControlChange .TML_MODULATIONWHEEL_LSB -> TODO ()
69+ Type .ControlChange .TML_BREATH_LSB -> TODO ()
70+ Type .ControlChange .TML_FOOT_LSB -> TODO ()
71+ Type .ControlChange .TML_PORTAMENTO_TIME_LSB -> TODO ()
72+ Type .ControlChange .TML_DATA_ENTRY_LSB -> TODO ()
73+ Type .ControlChange .TML_VOLUME_LSB -> TODO ()
74+ Type .ControlChange .TML_BALANCE_LSB -> TODO ()
75+ Type .ControlChange .TML_PAN_LSB -> TODO ()
76+ Type .ControlChange .TML_EXPRESSION_LSB -> TODO ()
77+ Type .ControlChange .TML_EFFECTS1_LSB -> TODO ()
78+ Type .ControlChange .TML_EFFECTS2_LSB -> TODO ()
79+ Type .ControlChange .TML_GPC1_LSB -> TODO ()
80+ Type .ControlChange .TML_GPC2_LSB -> TODO ()
81+ Type .ControlChange .TML_GPC3_LSB -> TODO ()
82+ Type .ControlChange .TML_GPC4_LSB -> TODO ()
83+ Type .ControlChange .TML_SUSTAIN_SWITCH -> TODO ()
84+ Type .ControlChange .TML_PORTAMENTO_SWITCH -> TODO ()
85+ Type .ControlChange .TML_SOSTENUTO_SWITCH -> TODO ()
86+ Type .ControlChange .TML_SOFT_PEDAL_SWITCH -> TODO ()
87+ Type .ControlChange .TML_LEGATO_SWITCH -> TODO ()
88+ Type .ControlChange .TML_HOLD2_SWITCH -> TODO ()
89+ Type .ControlChange .TML_SOUND_CTRL1 -> TODO ()
90+ Type .ControlChange .TML_SOUND_CTRL2 -> TODO ()
91+ Type .ControlChange .TML_SOUND_CTRL3 -> TODO ()
92+ Type .ControlChange .TML_SOUND_CTRL4 -> TODO ()
93+ Type .ControlChange .TML_SOUND_CTRL5 -> TODO ()
94+ Type .ControlChange .TML_SOUND_CTRL6 -> TODO ()
95+ Type .ControlChange .TML_SOUND_CTRL7 -> TODO ()
96+ Type .ControlChange .TML_SOUND_CTRL8 -> TODO ()
97+ Type .ControlChange .TML_SOUND_CTRL9 -> TODO ()
98+ Type .ControlChange .TML_SOUND_CTRL10 -> TODO ()
99+ Type .ControlChange .TML_GPC5 -> TODO ()
100+ Type .ControlChange .TML_GPC6 -> TODO ()
101+ Type .ControlChange .TML_GPC7 -> TODO ()
102+ Type .ControlChange .TML_GPC8 -> TODO ()
103+ Type .ControlChange .TML_PORTAMENTO_CTRL -> TODO ()
104+ Type .ControlChange .TML_FX_REVERB -> TODO ()
105+ Type .ControlChange .TML_FX_TREMOLO -> TODO ()
106+ Type .ControlChange .TML_FX_CHORUS -> TODO ()
107+ Type .ControlChange .TML_FX_CELESTE_DETUNE -> TODO ()
108+ Type .ControlChange .TML_FX_PHASER -> TODO ()
109+ Type .ControlChange .TML_DATA_ENTRY_INCR -> TODO ()
110+ Type .ControlChange .TML_DATA_ENTRY_DECR -> TODO ()
111+ Type .ControlChange .TML_NRPN_LSB -> TODO ()
112+ Type .ControlChange .TML_NRPN_MSB -> TODO ()
113+ Type .ControlChange .TML_RPN_LSB -> TODO ()
114+ Type .ControlChange .TML_RPN_MSB -> TODO ()
115+ Type .ControlChange .TML_ALL_SOUND_OFF -> TODO ()
116+ Type .ControlChange .TML_ALL_CTRL_OFF -> TODO ()
117+ Type .ControlChange .TML_LOCAL_CONTROL -> TODO ()
118+ Type .ControlChange .TML_ALL_NOTES_OFF -> TODO ()
119+ Type .ControlChange .TML_OMNI_OFF -> TODO ()
120+ Type .ControlChange .TML_OMNI_ON -> TODO ()
121+ Type .ControlChange .TML_POLY_OFF -> TODO ()
122+ Type .ControlChange .TML_POLY_ON -> TODO ()
123+ Type .Message .NOTE_OFF -> TODO ()
124+ Type .Message .NOTE_ON -> TODO ()
125+ Type .Message .KEY_PRESSURE -> TODO ()
126+ Type .Message .CONTROL_CHANGE -> TODO ()
127+ Type .Message .PROGRAM_CHANGE -> TODO ()
128+ Type .Message .CHANNEL_PRESSURE -> TODO ()
129+ Type .Message .PITCH_BEND -> TODO ()
130+ Type .Message .SET_TEMPO -> TODO ()
131+ Type .System .TEXT -> TODO ()
132+ Type .System .COPYRIGHT -> TODO ()
133+ Type .System .TRACK_NAME -> TODO ()
134+ Type .System .INST_NAME -> TODO ()
135+ Type .System .LYRIC -> TODO ()
136+ Type .System .MARKER -> TODO ()
137+ Type .System .CUE_POINT -> TODO ()
138+ Type .System .EOT -> TODO ()
139+ Type .System .SMPTE_OFFSET -> TODO ()
140+ Type .System .TIME_SIGNATURE -> TODO ()
141+ Type .System .KEY_SIGNATURE -> TODO ()
142+ Type .System .SEQUENCER_EVENT -> TODO ()
143+ Type .System .SYSEX -> TODO ()
144+ Type .System .TIME_CODE -> TODO ()
145+ Type .System .SONG_POSITION -> TODO ()
146+ Type .System .SONG_SELECT -> TODO ()
147+ Type .System .TUNE_REQUEST -> TODO ()
148+ Type .System .EOX -> TODO ()
149+ Type .System .SYNC -> TODO ()
150+ Type .System .TICK -> TODO ()
151+ Type .System .START -> TODO ()
152+ Type .System .CONTINUE -> TODO ()
153+ Type .System .STOP -> TODO ()
154+ Type .System .ACTIVE_SENSING -> TODO ()
155+ Type .System .SYSTEM_RESET -> TODO ()
110156 }
111157 }
112158 return events
113159 }
114160
115- private fun Int.toType (): Type = Type .entries.find { it.value == this } ? : throw IllegalArgumentException (" Unknown MIDI event: $this " )
161+ private fun Int.toType (): Type {
162+ println (" event: $this " )
163+ Type .ControlChange .entries.find { it.controllerNumber == this }?.let { return it }
164+ Type .Message .entries.find { it.value == this }?.let { return it }
165+ Type .System .entries.find { it.value == this }?.let { return it }
166+
167+ throw InvalidMidiDataException (" Unknown event type: $this " )
168+ }
169+
170+ private fun readVariableLength (buffer : ByteArray , startIndex : Int ): Pair <Int , Int > {
171+ var result = 0
172+ var index = startIndex // Track current position in the array
173+ var i = 0
174+
175+ while (i < 4 ) {
176+ if (index >= buffer.size) {
177+ throw IllegalArgumentException (" Unexpected end of file" )
178+ }
179+
180+ val c = buffer[index]
181+ index++ // Move the pointer to the next byte
182+
183+ if (c.toInt() and 0x80 != 0 ) {
184+ result = (result or (c.toInt() and 0x7F )) shl 7
185+ } else {
186+ return Pair (result or c.toInt(), index) // Return the result and the new index
187+ }
188+
189+ i++
190+ }
191+
192+ throw IllegalArgumentException (" Invalid variable length byte count" )
193+ }
116194}
0 commit comments