1
2
3
4 from struct import unpack
5
6
7
8 from constants import *
9
10 from EventDispatcher import EventDispatcher
11
13
14 """
15
16 The MidiFileParser is the lowest level parser that see the data as
17 midi data. It generates events that gets triggered on the outstream.
18
19 """
20
22
23 """
24 raw_data is the raw content of a midi file as a string.
25 """
26
27
28 self.raw_in = raw_in
29 self.dispatch = EventDispatcher(outstream)
30
31
32 self._running_status = None
33
34
35
36
38
39 "Parses the header chunk"
40
41 raw_in = self.raw_in
42
43 header_chunk_type = raw_in.nextSlice(4)
44 header_chunk_zise = raw_in.readBew(4)
45
46
47 if header_chunk_type != 'MThd':
48 raise TypeError, "It is not a valid midi file!"
49
50
51 self.format = raw_in.readBew(2)
52 self.nTracks = raw_in.readBew(2)
53 self.division = raw_in.readBew(2)
54
55
56
57
58 if header_chunk_zise > 6:
59 raw_in.moveCursor(header_chunk_zise-6)
60
61
62 self.dispatch.header(self.format, self.nTracks, self.division)
63
64
65
67
68 "Parses a track chunk. This is the most important part of the parser."
69
70
71 self.dispatch.reset_time()
72
73 dispatch = self.dispatch
74 raw_in = self.raw_in
75
76
77 dispatch.start_of_track(self._current_track)
78
79 raw_in.moveCursor(4)
80
81 tracklength = raw_in.readBew(4)
82 track_endposition = raw_in.getCursor() + tracklength
83
84 while raw_in.getCursor() < track_endposition:
85
86
87 time = raw_in.readVarLen()
88 dispatch.update_time(time)
89
90
91 peak_ahead = raw_in.readBew(move_cursor=0)
92 if (peak_ahead & 0x80):
93
94
95 status = self._running_status = raw_in.readBew()
96 else:
97
98 status = self._running_status
99
100
101
102
103
104
105
106
107 hi_nible, lo_nible = status & 0xF0, status & 0x0F
108
109
110
111
112
113
114 if status == META_EVENT:
115 meta_type = raw_in.readBew()
116 meta_length = raw_in.readVarLen()
117 meta_data = raw_in.nextSlice(meta_length)
118 dispatch.meta_event(meta_type, meta_data)
119
120
121
122 elif status == SYSTEM_EXCLUSIVE:
123
124 sysex_length = raw_in.readVarLen()
125
126 sysex_data = raw_in.nextSlice(sysex_length-1)
127
128
129 if raw_in.readBew(move_cursor=0) == END_OFF_EXCLUSIVE:
130 eo_sysex = raw_in.readBew()
131 dispatch.sysex_event(sysex_data)
132
133
134
135
136 elif hi_nible == 0xF0:
137 data_sizes = {
138 MTC:1,
139 SONG_POSITION_POINTER:2,
140 SONG_SELECT:1,
141 }
142 data_size = data_sizes.get(hi_nible, 0)
143 common_data = raw_in.nextSlice(data_size)
144 common_type = lo_nible
145 dispatch.system_common(common_type, common_data)
146
147
148
149 else:
150 data_sizes = {
151 PATCH_CHANGE:1,
152 CHANNEL_PRESSURE:1,
153 NOTE_OFF:2,
154 NOTE_ON:2,
155 AFTERTOUCH:2,
156 CONTINUOUS_CONTROLLER:2,
157 PITCH_BEND:2,
158 }
159 data_size = data_sizes.get(hi_nible, 0)
160 channel_data = raw_in.nextSlice(data_size)
161 event_type, channel = hi_nible, lo_nible
162 dispatch.channel_messages(event_type, channel, channel_data)
163
164
166 "Parses all track chunks."
167 for t in range(self.nTracks):
168 self._current_track = t
169 self.parseMTrkChunk()
170 self.dispatch.eof()
171
172
173
174 if __name__ == '__main__':
175
176
177 test_file = 'test/midifiles/minimal.mid'
178 test_file = 'test/midifiles/cubase-minimal.mid'
179 test_file = 'test/midifiles/Lola.mid'
180
181
182
183
184
185
186 from MidiToText import MidiToText
187 from RawInstreamFile import RawInstreamFile
188
189 midi_in = MidiFileParser(RawInstreamFile(test_file), MidiToText())
190 midi_in.parseMThdChunk()
191 midi_in.parseMTrkChunks()
192