1
1
package pl.lemanski.mikroSoundFont.io.midi
2
2
3
+ import pl.lemanski.mikroSoundFont.InvalidMidiDataException
3
4
import pl.lemanski.mikroSoundFont.getLogger
4
5
import pl.lemanski.mikroSoundFont.midi.MidiMessage
5
6
import 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
10
7
11
8
class MidiFileParser {
12
9
private val logger = getLogger()
13
10
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
-
27
11
private fun readVariableLengthQuantity (data : ByteArray , index : Int ): Pair <Int , Int > {
28
12
var value = 0
29
13
var i = index
@@ -38,17 +22,6 @@ class MidiFileParser {
38
22
return Pair (value, i)
39
23
}
40
24
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
-
52
25
fun parseTrack (data : ByteArray , trackStart : Int ): List <MidiMessage > {
53
26
val events = mutableListOf<MidiMessage >()
54
27
var index = trackStart + 8 // Skip "MTrk" and track length
@@ -59,6 +32,8 @@ class MidiFileParser {
59
32
val (deltaTime, newIndex) = readVariableLengthQuantity(data, index)
60
33
index = newIndex
61
34
35
+ if (index >= data.size - 1 ) break
36
+
62
37
val statusByte = data[index].toInt() and 0xFF
63
38
index++
64
39
@@ -73,44 +48,147 @@ class MidiFileParser {
73
48
val channel = lastStatusByte and 0x0F
74
49
75
50
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 ()
110
156
}
111
157
}
112
158
return events
113
159
}
114
160
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
+ }
116
194
}
0 commit comments