Skip to content

Commit 91a172c

Browse files
committed
MidiEventDecoder uses classes per meta event
1 parent afd694a commit 91a172c

File tree

3 files changed

+111
-99
lines changed

3 files changed

+111
-99
lines changed

MidiEventDecoder.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,15 @@ def midiEvent(self, midiData):
3535
midiData = tempData[temp:]
3636
#Meta Event
3737
if midiData[0:1] == b'\xff':
38-
return MetaEvent(Util.varLenVal(deltaTime), midiData)
38+
if midiData[1] in EventDictionaries.META_EVENT_DICTIONARY:
39+
metaEventClass = EventDictionaries.META_EVENT_DICTIONARY[midiData[1]]
40+
else:
41+
metaEventClass = MetaEvent
42+
metaEvent = metaEventClass()
43+
metaEvent.setDeltaTimeFromBytes(deltaTime)
44+
metaEvent.setFromBytes(midiData)
45+
return metaEvent
46+
#return MetaEvent(Util.varLenVal(deltaTime), midiData)
3947
#System Event
4048
if midiData[0:1] == b'\xf0' or midiData[0:1] == b'\xf7':
4149
return SystemEvemt(Util.varLenVal(deltaTime), midiData)

MidiEvents.py

Lines changed: 85 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,7 @@ def __str__(self):
3636
return "Track Header " + str(self.midiData)
3737

3838
class MidiEvent:
39-
#data contained:
40-
#eventType
41-
# chunkID
42-
# (midi): Note off, Note on, Note aftertouch, Controller, Program Change
43-
# Channel Aftertouch, Pitch Bend
44-
# meta
45-
# sysEx
46-
def __init__(self, deltaTime):
47-
self.deltaTime = deltaTime
48-
#set the delta time from the bytes representing delta time
39+
#set the delta time in clock ticks from the bytes representing delta time
4940
def setDeltaTimeFromBytes(self, deltaTimeBytes):
5041
self.deltaTime = Util.varLenVal(deltaTimeBytes)
5142
#sets the event data from the bytes (bytes should not include delta time)
@@ -60,104 +51,95 @@ def setStartTime(self, startTime): #set start time in ms
6051

6152
######################### Meta Event ################################################
6253
class MetaEvent(MidiEvent):
63-
#delaTime in clock ticks
64-
def __init__(self, deltaTime, midiData):
65-
super().__init__(deltaTime)
66-
self.eventClass = "Meta"
67-
self.midiData = midiData
68-
self.startTime = None
69-
self.eventType = None
70-
self.text = None
71-
self.dataLength = midiData[2:3]
72-
i = 2
73-
while Util.msbIsOne(midiData[i:i+1]):
74-
self.dataLength = self.dataLength + midiData[i+1:i+2]
75-
i = i+1
76-
i = i+1
77-
self.data = midiData[i:]
78-
#coverts bytes to a string
79-
self.data = str(self.data)[2:len(str(self.data))-1]
80-
if midiData[1] in Util.MetaEventDict:
81-
self.eventType = Util.MetaEventDict[midiData[1]]
82-
if self.eventType == "SequenceNumber":
83-
self.sequenceNumber = Util.intFromBytes(midiData[3:])
84-
if self.eventType == "SetTempo":
85-
#micro seconds per quarter note
86-
self.usPerQuarter = Util.intFromBytes(midiData[3:])
87-
if self.eventType == "Sequence/TrackName" or self.eventType == "DeviceName":
88-
self.text = midiData[3:].decode()
89-
def setFromBytes(self, midiDataBytes):
54+
def setFromBytes(self, midiData):
9055
print("Set bytes called on parent event class!")
9156
def __str__(self):
92-
s = ("Meta " + str(self.midiData) + " deltaTime: "
93-
+ str(self.deltaTime) + " eventType: " + self.eventType)
94-
if self.eventType == "SetTempo":
95-
s = s + "\n\t usPerQuarter: " + str(self.usPerQuarter)
96-
if self.eventType == "Sequence/TrackName":
97-
s = s + "\n\t Sequence/TrackName: " + self.text
98-
if self.eventType == "DeviceName":
99-
s = s + "\n\t Device Name: " + self.text
100-
return s
57+
return "Meta deltaTime: " + str(self.deltaTime)
10158

10259
class SequenceNumberEvent(MetaEvent):
10360
def setFromBytes(self, midiData):
104-
self.sequenceNumber = int.from_bytes(midiData[3:], "big")
61+
eventData = Util.stripLeadingVariableLength(midiData[2:])
62+
self.sequenceNumber = int.from_bytes(eventData, "big")
10563
def __str__(self):
10664
return (super().__str__() + " eventType: Sequence Number" +
10765
"\n\t Sequence Number: " + str(self.sequenceNumber))
10866

10967
class TextEvent(MetaEvent):
11068
def setFromBytes(self, midiData):
111-
self.text = midiData[3:].decode()
69+
eventData = Util.stripLeadingVariableLength(midiData[2:])
70+
self.text = eventData.decode()
11271
def __str__(self):
11372
return (super().__str__() + " eventType: Text" +
11473
"\n\t Text: " + str(self.text))
11574

11675
class CopyrightNoticeEvent(MetaEvent):
11776
def setFromBytes(self, midiData):
118-
self.copyrightNotice = midiData[3:].decode()
77+
eventData = Util.stripLeadingVariableLength(midiData[2:])
78+
self.copyrightNotice = eventData.decode()
11979
def __str__(self):
12080
return (super().__str__() + " eventType: Copyright Notice" +
12181
"\n\t Copyright Notice: " + str(self.copyrightNotice))
12282

123-
class TrackNameEevent(MetaEvent):
83+
class TrackNameEvent(MetaEvent):
12484
def setFromBytes(self, midiData):
125-
self.trackName = midiData[3:].decode()
85+
eventData = Util.stripLeadingVariableLength(midiData[2:])
86+
self.trackName = eventData.decode()
12687
def __str__(self):
12788
return (super().__str__() + " eventType: Sequence/Track Name" +
12889
"\n\t Track Name: " + str(self.trackName))
12990

13091
class InstrumentNameEvent(MetaEvent):
13192
def setFromBytes(self, midiData):
132-
self.instrumentName = midiData[3:].decode()
93+
eventData = Util.stripLeadingVariableLength(midiData[2:])
94+
self.instrumentName = eventData.decode()
13395
def __str__(self):
13496
return (super().__str__() + " eventType: Instrument Name" +
13597
"\n\t Instrument Name: " + str(self.instrumentName))
13698

13799
class LyricsEvent(MetaEvent):
138100
def setFromBytes(self, midiData):
139-
self.lyrics = midiData[3:].decode()
101+
eventData = Util.stripLeadingVariableLength(midiData[2:])
102+
self.lyrics = eventData.decode()
140103
def __str__(self):
141104
return (super().__str__() + " eventType: Lyrics" +
142105
"\n\t Lyrics: " + str(self.lyrics))
143106

144107
class MarkerEvent(MetaEvent):
145108
def setFromBytes(self, midiData):
146-
self.marker = midiData[3:].decode()
109+
eventData = Util.stripLeadingVariableLength(midiData[2:])
110+
self.marker = eventData.decode()
147111
def __str__(self):
148112
return (super().__str__() + " eventType:Marker" +
149113
"\n\t Marker: " + str(self.marker))
150114

151115
class CuePointEvent(MetaEvent):
152116
def setFromBytes(self, midiData):
153-
self.cuePoint = midiData[3:].decode()
117+
eventData = Util.stripLeadingVariableLength(midiData[2:])
118+
self.cuePoint = eventData.decode()
154119
def __str__(self):
155120
return (super().__str__() + " eventType: Cue Point" +
156121
"\n\t Cue Point: " + str(self.cuePoint))
122+
123+
class ProgramNameEvent(MetaEvent):
124+
def setFromBytes(self, midiData):
125+
eventData = Util.stripLeadingVariableLength(midiData[2:])
126+
self.programName = eventData.decode()
127+
def __str__(self):
128+
return (super().__str__() + " eventType: Program Name" +
129+
"\n\t Program Name: " + str(self.programName))
130+
131+
class DeviceNameEvent(MetaEvent):
132+
def setFromBytes(self, midiData):
133+
eventData = Util.stripLeadingVariableLength(midiData[2:])
134+
self.deviceName = eventData.decode()
135+
def __str__(self):
136+
return (super().__str__() + " eventType: Device Name" +
137+
"\n\t Device Name: " + str(self.deviceName))
157138

158139
class MidiChannelPrefixEvent(MetaEvent):
159140
def setFromBytes(self, midiData):
160-
self.channel = Util.intFromBytes(midiData[3:])
141+
eventData = Util.stripLeadingVariableLength(midiData[2:])
142+
self.channel = Util.intFromBytes(eventData)
161143
def __str__(self):
162144
return (super().__str__() + " eventType: Midi Channel Prefix"
163145
+ "\n\t Channel: " + str(self.channel))
@@ -172,15 +154,17 @@ def __str__(self):
172154
class SetTempoEvent(MetaEvent):
173155
def setFromBytes(self, midiData):
174156
#tempo is in microseconds per quarter note
175-
self.tempo = Util.intFromBytes(midiData[3:])
157+
eventData = Util.stripLeadingVariableLength(midiData[2:])
158+
self.tempo = Util.intFromBytes(eventData)
176159
def __str__(self):
177160
return (super().__str__() + " eventType: Set Tempo"
178161
+ "\n\t Tempo (microseconds per quarter note): "
179162
+ str(self.tempo))
180163

181164
class SMPTEOffsetEvent(MetaEvent):
182165
def setFromBytes(self, midiData):
183-
frameRateIdentifier = Util.intFromBytes(midiData[3:4] & int('e0',16)) / 64
166+
eventData = Util.stripLeadingVariableLength(midiData[2:])
167+
frameRateIdentifier = (eventData[0] & int('e0',16)) / 64
184168
#frame rate in fps
185169
self.frameRate = None
186170
self.dropFrame = False
@@ -193,60 +177,62 @@ def setFromBytes(self, midiData):
193177
self.dropFrame = True
194178
if (frameRateIdentifier == 11):
195179
self.frameRate = 30
196-
self.hour = Util.intFromBytes(midiData[3:4] & int('1f', 16))
197-
self.minute = Util.intFromBytes(midiData[4:5])
198-
self.second = Util.intFromBytes(midiData[5:6])
199-
self.frame = Util.intFromBytes(midiData[6:7] )
180+
self.hour = eventData[0] & int('1f', 16)
181+
self.minute = Util.intFromBytes(eventData[1:2])
182+
self.second = Util.intFromBytes(eventData[2:3])
183+
self.frame = Util.intFromBytes(eventData[3:4] )
200184
#always 100 sub-frames per frame
201-
self.subFrame = Util.intFromBytes(midiData[7:])
185+
self.subFrame = Util.intFromBytes(eventData[4:])
202186
def __str__(self):
203187
return (super().__str__() + " eventType: SMPTE Offset"
204188
+ "\n\t Frame Rate: " + str(self.frameRate)
205-
+ "Drop Frame: " + str(self.dropFrame)
189+
+ " Drop Frame: " + str(self.dropFrame)
206190
+ "\n\t Hour: " + str(self.hour)
207-
+ "Minute: " + str(self.minute)
191+
+ " Minute: " + str(self.minute)
208192
+ "\n\t Frame: " + str(self.frame)
209-
+ "Sub-Frame: " + str(self.subFrame))
193+
+ " Sub-Frame: " + str(self.subFrame))
210194

211195
class TimeSignatureEvent(MetaEvent):
212196
def setFromBytes(self, midiData):
197+
eventData = Util.stripLeadingVariableLength(midiData[2:])
213198
#default is 4
214-
self.numerator = Util.intFromBytes(midiData[3:4])
199+
self.numerator = Util.intFromBytes(eventData[0:1])
215200
#default is 4 (or encoded 2 since 2^2 is 4)
216-
self.denominator = math.pow(2, Util.intFromBytes(midiData[4:5]))
201+
self.denominator = math.pow(2, Util.intFromBytes(eventData[1:2]))
217202
#default is 1 (or 24 encoded since 24/24 = 1)
218-
self.beatsPerTick = Util.intFromBytes(midiData[5:6]) / 24
203+
self.beatsPerTick = Util.intFromBytes(eventData[2:3]) / 24
219204
#default is 8
220-
self.thirtySecondNotesPerBeat = Util.intFromBytes(midiData[6:])
205+
self.thirtySecondNotesPerBeat = Util.intFromBytes(eventData[3:])
221206
def __str__(self):
222207
return (super().__str__() + " eventType: Time Signature"
223208
+ "\n\t Time Signature: " + str(self.numerator)
224209
+ "/" + str(self.denominator)
225-
+ "\n\t Beats per tick: " + str(self.hour)
226-
+ "32nd notes per beat: " + str(self.thirtySecondNotesPerBeat))
210+
+ "\n\t Beats per tick: " + str(self.beatsPerTick)
211+
+ " 32nd notes per beat: " + str(self.thirtySecondNotesPerBeat))
227212

228213
class KeySignatureEvent(MetaEvent):
229214
def setFromBytes(self, midiData):
215+
eventData = Util.stripLeadingVariableLength(midiData[2:])
230216
#True for major, False for minor
231-
self.majorKey = (Util.intFromBytes(midiData[4:5]) == 0)
217+
self.majorKey = (Util.intFromBytes(eventData[1:2]) == 0)
232218
#true for sharps, false for flats
233-
self.sharpKey = (Util.intFromBytes(midiData[3:4]) > 0)
234-
self.numberOfAccidentals = abs(Util.intFromBytes(midiData[3:4]))
219+
self.sharpKey = (Util.intFromBytes(eventData[0:1], True) > 0)
220+
self.numberOfAccidentals = abs(Util.intFromBytes(eventData[0:1], True))
235221
def __str__(self):
236222
sharpsOrFlats = "sharps" if self.sharpKey else "flats"
237223
majorOrMinor = ""
238224
if (self.numberOfAccidentals > 0):
239225
majorOrMinor = ", major" if self.majorKey else ", minor"
240226
return (super().__str__() + " eventType: Key Signature"
241227
+ "\n\t Number of " + str(sharpsOrFlats) + ": "
242-
+ "/" + str(self.numberOfAccidentals) + majorOrMinor)
228+
+ str(self.numberOfAccidentals) + majorOrMinor)
243229

244230
class SequencerSpecificEvent():
245231
def setFromBytes(self, midiData):
246-
self.data = midiData
232+
self.eventData = Util.stripLeadingVariableLength(midiData[2:])
247233
def __str__(self):
248234
return (super().__str__() + " eventType: Sequencer Specific"
249-
+ "\n\t Raw data " + str(self.data))
235+
+ "\n\t Raw data (without variable-length)" + str(self.eventData))
250236

251237

252238
############################ System Event #########################################
@@ -265,7 +251,7 @@ def __str__(self):
265251
######################### Channel Event ############################################
266252
class ChannelEvent(MidiEvent):
267253
def __init__(self, deltaTime, midiData):
268-
super().__init__(deltaTime)
254+
self.deltaTime = deltaTime
269255
self.midiData = midiData
270256
self.eventClass = "Channel"
271257
self.startTime = None
@@ -328,3 +314,23 @@ def __str__(self):
328314

329315
#class variable used for running status
330316
prevEventData = ()
317+
318+
class EventDictionaries:
319+
#maps a meta event type to its class
320+
META_EVENT_DICTIONARY = MetaEventDict = {0 : SequenceNumberEvent,
321+
1 : TextEvent,
322+
2 : CopyrightNoticeEvent,
323+
3 : TrackNameEvent,
324+
4 : InstrumentNameEvent,
325+
5 : LyricsEvent,
326+
6 : MarkerEvent,
327+
7 : CuePointEvent,
328+
8 : ProgramNameEvent,
329+
9 : DeviceNameEvent,
330+
32 : MidiChannelPrefixEvent,
331+
47 : EndOfTrackEvent,
332+
81 : SetTempoEvent,
333+
84 : SMPTEOffsetEvent,
334+
88 : TimeSignatureEvent,
335+
89 : KeySignatureEvent,
336+
127: SequencerSpecificEvent}

Util.py

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ def lshiftByteArray(sourceByteArray, numBits):
2121

2222
#takes a bytes object formatted in variable length and
2323
#returns the int value represented
24+
#does not check if the variable length value is valid, as
25+
#it ignores the msb of every byte
2426
@staticmethod
2527
def varLenVal(varLenBytes):
2628
if len(varLenBytes) == 0:
@@ -33,15 +35,27 @@ def varLenVal(varLenBytes):
3335
returnValBytes = Util.lshiftByteArray(returnValBytes, 7)
3436
returnValBytes[len(returnValBytes)-1] = (
3537
returnValBytes[len(returnValBytes)-1] | nextByte)
36-
return int.from_bytes(returnValBytes, "big")
38+
return int.from_bytes(returnValBytes, "big")
3739

3840
@staticmethod
3941
def msbIsOne(byte): #returns true if the msb of a bytes object is 1
4042
return (byte[0] & int('80',16)) > 0
4143

44+
#strips a variable length quanity off of a byte array
45+
#and returns the rest
4246
@staticmethod
43-
def intFromBytes(byteArray):
44-
return int.from_bytes(byteArray, "big")
47+
def stripLeadingVariableLength(byteArray):
48+
varLenEndIndex = 0
49+
while (varLenEndIndex < len(byteArray) and
50+
Util.msbIsOne(byteArray[varLenEndIndex:varLenEndIndex + 1])):
51+
varLenEndIndex = varnLenEndIndex + 1
52+
#the last byte of a variable length value has msb 0
53+
varLenEndIndex = varLenEndIndex + 1
54+
return byteArray[varLenEndIndex:]
55+
56+
@staticmethod
57+
def intFromBytes(byteArray, signed = False):
58+
return int.from_bytes(byteArray, "big", signed = signed)
4559

4660
#maps [byte with event type and channel] & b'\xf0' to event type
4761
ChannelEventDict = {int('80', 16) : "NoteOff",
@@ -51,19 +65,3 @@ def intFromBytes(byteArray):
5165
int('c0', 16) : "ProgramChange",
5266
int('d0', 16) : "ChannelAftertouch",
5367
int('e0', 16) : "PitchBend"}
54-
MetaEventDict = {0 : "SequenceNumber",
55-
1 : "TextEvent",
56-
2 : "CopyrightNotice",
57-
3 : "Sequence/TrackName",
58-
4 : "InstrumentName",
59-
5 : "Lyrics",
60-
6 : "Marker",
61-
7 : "CuePoint",
62-
9 : "DeviceName",
63-
32 : "MidiChannelPrefix",
64-
47 : "EndOfTrack",
65-
81 : "SetTempo",
66-
84 : "SMPTEOffset",
67-
88 : "TimeSignature",
68-
89 : "KeySignature",
69-
127: "SequencerSpecific"}

0 commit comments

Comments
 (0)