Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100755 308 lines (245 sloc) 7.403 kb
83cf304 @TeamTeamUSA Inital import
authored
1
2 # Placed into Public Domain in June 2006 by Sean D. Spencer
3
4 # Sean D. Spencer
5 # sean_don4@lycos.com
6 # 2/19/2006
7 # Last Revision: 4/19/2007
8
9 # MIDI Parsing Library for Python.
10
11 import sys
12
13 TRUE = -1
14 FALSE = 0
15
16 class format:
17 SingleTrack = 0
18 MultipleTracksSync = 1
19 MultipleTracksAsync = 2
20
21 class voice:
22 NoteOff = 0x80
23 NoteOn = 0x90
24 PolyphonicKeyPressure = 0xA0 # note aftertouch
25 ControllerChange = 0xB0
26 ProgramChange = 0xC0
27 ChannelPressure = 0xD0
28 PitchBend = 0xE0
29
30 class meta:
31 FileMetaEvent = 0xFF
32 SMPTEOffsetMetaEvent = 0x54
33 SystemExclusive = 0xF0
34 SystemExclusivePacket = 0xF7
35 SequenceNumber = 0x00
36 TextMetaEvent = 0x01
37 CopyrightMetaEvent = 0x02
38 TrackName = 0x03
39 InstrumentName = 0x04
40 Lyric = 0x05
41 Marker = 0x06
42 CuePoint = 0x07
43 ChannelPrefix = 0x20
44 MidiPort = 0x21
45 EndTrack = 0x2F
46 SetTempo = 0x51
47 TimeSignature = 0x58
48 KeySignature = 0x59
49 SequencerSpecificMetaEvent = 0x7F
50
51 class EventNote:
52 def __init__(self):
53 self.note_no = None
54 self.velocity = None
55
56 class EventValue:
57 def __init__(self):
58 self.type = None
59 self.value = None
60
61 class EventAmount:
62 def __init__(self):
63 self.amount = None
64
65 class MetaEventKeySignature:
66 def __init__(self):
67 self.fifths = None
68 self.mode = None
69
70 class MetaEventTimeSignature:
71 def __init__(self):
72 self.numerator = None
73 self.log_denominator = None
74 self.midi_clocks = None
75 self.thirty_seconds = None
76
77 class MetaEventText:
78 def __init__(self):
79 self.length = None
80 self.text = None
81
82 class MetaEventSMPTEOffset:
83 def __init__(self):
84 self.hour = None
85 self.minute = None
86 self.second = None
87 self.frame = None
88 self.sub_frame = None
89
90 class MetaValues:
91 def __init__(self):
92 self.length = None
93 self.values = None
94
95 def getNumber(theString, length):
96 # MIDI uses big-endian for everything
97 sum = 0
98 #print "Length: " + str(length) + " strlen: " + str(len(theString))
99 for i in range(length):
100 #sum = (sum *256) + int(str[i])
101 sum = (sum << 8) + ord(theString[i])
102 return sum, theString[length:]
103
104 def getVariableLengthNumber(str):
105 sum = 0
106 i = 0
107 while 1:
108 x = ord(str[i])
109 i = i + 1
110 # sum = (sum * 127) + (x (mask) 127) # mask off the 7th bit
111 sum = (sum << 7) + (x & 0x7F)
112 # Is 7th bit clear?
113 if not (x & 0x80):
114 return sum, str[i:]
115
116 def getValues(str, n=16):
117 temp = []
118 for x in str[:n]:
119 temp.append(repr(ord(x)))
120 return temp
121
122 class File:
123 def __init__(self, file):
124 self.file = file
125 self.format = None
126 self.num_tracks = None
127 self.division = None
128 self.tracks = []
129
130 self.file = open(self.file, 'rb')
131 str = self.file.read()
132 self.file.close()
133
134 self.read(str)
135
136 def read(self, str):
137 assert str[:4] == "MThd"
138 str = str[4:]
139
140 length, str = getNumber(str, 4)
141 assert length == 6
142
143 self.format, str = getNumber(str, 2)
144
145 self.num_tracks, str = getNumber(str, 2)
146 self.division, str = getNumber(str, 2)
147
148 for i in range(self.num_tracks):
149 track = Track(i+1)
150 str = track.read(str)
151 self.tracks.append(track)
152
153 class Track:
154 def __init__(self, index):
155 self.number = index
156 self.length = None
157 self.events = []
158
159 def read(self, str):
160 self.length, str = getNumber(str[4:], 4)
161 track_str = str[:self.length]
162
163 prev_absolute = 0
164 prev_status = 0
165
166 i = 0
167 while track_str:
168 event = Event(self.number, i+1)
169 track_str = event.read(prev_absolute, prev_status, track_str)
170
171 prev_absolute += event.delta
172 prev_status = event.status
173 self.events.append(event)
174 i += 1
175
176 return str[self.length:]
177
178 class Event:
179 def __init__(self, track, index):
180 self.number = index
181 self.type = None
182 self.delta = None
183 self.absolute = None
184 self.status = None
185 self.channel = None
186
187 def read(self, prev_time, prev_status, str):
188 self.delta, str = getVariableLengthNumber(str)
189 self.absolute = prev_time + self.delta
190
191 # use running status?
192 if not (ord(str[0]) & 0x80):
193 # squeeze a duplication of the running status into the data string
194 str = prev_status + str
195
196 self.status = str[0]
197 self.channel = ord(self.status) & 0xF
198
199 # increment one byte, past the status
200 str = str[1:]
201
202 has_channel = has_meta = TRUE
203
204 # handle voice events
205 channel_msg = ord(self.status) & 0xF0
206 if channel_msg == voice.NoteOn or \
207 channel_msg == voice.NoteOff or \
208 channel_msg == voice.PolyphonicKeyPressure:
209 self.detail = EventNote()
210 self.detail.note_no = ord(str[0])
211 self.detail.velocity = ord(str[1])
212 str = str[2:]
213
214 elif channel_msg == voice.ControllerChange:
215 self.detail = EventValue()
216 self.detail.type = ord(str[0])
217 self.detail.value = ord(str[1])
218 str = str[2:]
219
220 elif channel_msg == voice.ProgramChange or \
221 channel_msg == voice.ChannelPressure:
222
223 self.detail = EventAmount()
224 self.detail.amount = ord(str[0])
225 str = str[1:]
226
227 elif channel_msg == voice.PitchBend:
228 # Pitch bend uses high accuracy 14 bit unsigned integer.
229 self.detail = EventAmount()
230 self.detail.amount = (ord(str[0]) << 7) | ord(str[1])
231 str = str[2:]
232
233 else: has_channel = FALSE
234
235 # handle meta events
236 meta_msg = ord(self.status)
237 if meta_msg == meta.FileMetaEvent:
238
239 meta_msg = type = ord(str[0])
240 length, str = getVariableLengthNumber(str[1:])
241
242 if type == meta.SetTempo or \
243 type == meta.ChannelPrefix:
244
245 self.detail = EventAmount()
246 self.detail.tempo, str = getNumber(str, length)
247
248 elif type == meta.KeySignature:
249 self.detail = MetaEventKeySignature()
250 self.detail.fifths = ord(str[0])
251
252 if ord(str[1]): self.detail.mode = "minor"
253 else: self.detail.mode = "major"
254
255 str = str[length:]
256
257 elif type == meta.TimeSignature:
258 self.detail = MetaEventTimeSignature()
259 self.detail.numerator = ord(str[0])
260 self.detail.log_denominator = ord(str[1])
261 self.detail.midi_clocks = ord(str[2])
262 self.detail.thirty_seconds = ord(str[3])
263 str = str[length:]
264
265 elif type == meta.TrackName or \
266 type == meta.TextMetaEvent or \
267 type == meta.Lyric or \
268 type == meta.CuePoint or \
269 type == meta.CopyrightMetaEvent:
270
271 self.detail = MetaEventText()
272 self.detail.length = length
273 self.detail.text = str[:length]
274 str = str[length:]
275
276 elif type == meta.SMPTEOffsetMetaEvent:
277 self.detail = MetaEventSMPTEOffset()
278 self.detail.hour = ord(str[0])
279 self.detail.minute = ord(str[1])
280 self.detail.second = ord(str[2])
281 self.detail.frame = ord(str[3])
282 self.detail.sub_frame = ord(str[4])
283 str = str[length:]
284
285 elif type == meta.EndTrack:
286 str = str[length:] # pass on to next track
287
288 else: has_meta = FALSE
289
290 elif meta_msg == meta.SystemExclusive or \
291 meta_msg == meta.SystemExclusivePacket:
292 self.detail = MetaValues()
293 self.detail.length, str = getVariableLengthNumber(str)
294 self.detail.values = getValues(str, self.detail.length)
295 str = str[self.detail.length:]
296
297 else: has_meta = FALSE
298
299 if has_channel:
300 self.type = channel_msg
301 elif has_meta:
302 self.type = meta_msg
303 else:
304 #raise "Unknown event."
305 self.type = None
306 return str
307
Something went wrong with that request. Please try again.