1 # -*- coding: ISO-8859-1 -*-
4 from struct
import unpack
7 from DataTypeConverters
import readBew
, readVar
, varLen
, toBytes
9 # uhh I don't really like this, but there are so many constants to
11 from constants
import *
14 class EventDispatcher
:
17 def __init__(self
, outstream
):
21 The event dispatcher generates events on the outstream.
25 # internal values, don't mess with 'em directly
26 self
.outstream
= outstream
30 # A note_on with a velocity of 0x00 is actually the same as a
31 # note_off with a velocity of 0x40. When
32 # "convert_zero_velocity" is set, the zero velocity note_on's
33 # automatically gets converted into note_off's. This is a less
34 # suprising behaviour for those that are not into the intimate
35 # details of the midi spec.
36 self
.convert_zero_velocity
= 1
38 # If dispatch_continuos_controllers is true, continuos
39 # controllers gets dispatched to their defined handlers. Else
40 # they just trigger the "continuous_controller" event handler.
41 self
.dispatch_continuos_controllers
= 1 # NOT IMPLEMENTED YET
43 # If dispatch_meta_events is true, meta events get's dispatched
44 # to their defined events. Else they all they trigger the
45 # "meta_event" handler.
46 self
.dispatch_meta_events
= 1
50 def header(self
, format
, nTracks
, division
):
51 "Triggers the header event"
52 self
.outstream
.header(format
, nTracks
, division
)
55 def start_of_track(self
, current_track
):
56 "Triggers the start of track event"
58 # I do this twice so that users can overwrite the
59 # start_of_track event handler without worrying whether the
60 # track number is updated correctly.
61 self
.outstream
.set_current_track(current_track
)
62 self
.outstream
.start_of_track(current_track
)
65 def sysex_event(self
, data
):
66 "Dispatcher for sysex events"
67 self
.outstream
.sysex_event(data
)
75 def update_time(self
, new_time
=0, relative
=1):
76 "Updates relative/absolute time."
77 self
.outstream
.update_time(new_time
, relative
)
81 "Updates relative/absolute time."
82 self
.outstream
.reset_time()
85 # Event dispatchers for similar types of events
88 def channel_messages(self
, hi_nible
, channel
, data
):
90 "Dispatches channel messages"
92 stream
= self
.outstream
95 if (NOTE_ON
& 0xF0) == hi_nible
:
97 # note_on with velocity 0x00 are same as note
98 # off with velocity 0x40 according to spec!
99 if velocity
==0 and self
.convert_zero_velocity
:
100 stream
.note_off(channel
, note
, 0x40)
102 stream
.note_on(channel
, note
, velocity
)
104 elif (NOTE_OFF
& 0xF0) == hi_nible
:
105 note
, velocity
= data
106 stream
.note_off(channel
, note
, velocity
)
108 elif (AFTERTOUCH
& 0xF0) == hi_nible
:
109 note
, velocity
= data
110 stream
.aftertouch(channel
, note
, velocity
)
112 elif (CONTINUOUS_CONTROLLER
& 0xF0) == hi_nible
:
113 controller
, value
= data
114 # A lot of the cc's are defined, so we trigger those directly
115 if self
.dispatch_continuos_controllers
:
116 self
.continuous_controllers(channel
, controller
, value
)
118 stream
.continuous_controller(channel
, controller
, value
)
120 elif (PATCH_CHANGE
& 0xF0) == hi_nible
:
122 stream
.patch_change(channel
, program
)
124 elif (CHANNEL_PRESSURE
& 0xF0) == hi_nible
:
126 stream
.channel_pressure(channel
, pressure
)
128 elif (PITCH_BEND
& 0xF0) == hi_nible
:
129 hibyte
, lobyte
= data
130 value
= (hibyte
<<7) + lobyte
131 stream
.pitch_bend(channel
, value
)
135 raise ValueError, 'Illegal channel message!'
139 def continuous_controllers(self
, channel
, controller
, value
):
141 "Dispatches channel messages"
143 stream
= self
.outstream
145 # I am not really shure if I ought to dispatch continuous controllers
146 # There's so many of them that it can clutter up the OutStream
149 # So I just trigger the default event handler
150 stream
.continuous_controller(channel
, controller
, value
)
154 def system_commons(self
, common_type
, common_data
):
156 "Dispatches system common messages"
158 stream
= self
.outstream
160 # MTC Midi time code Quarter value
161 if common_type
== MTC
:
162 data
= readBew(common_data
)
163 msg_type
= (data
& 0x07) >> 4
164 values
= (data
& 0x0F)
165 stream
.midi_time_code(msg_type
, values
)
167 elif common_type
== SONG_POSITION_POINTER
:
168 hibyte
, lobyte
= toBytes(common_data
)
169 value
= (hibyte
<<7) + lobyte
170 stream
.song_position_pointer(value
)
172 elif common_type
== SONG_SELECT
:
173 data
= readBew(common_data
)
174 stream
.song_select(data
)
176 elif common_type
== TUNING_REQUEST
:
178 stream
.tuning_request(time
=None)
182 def meta_event(self
, meta_type
, data
):
184 "Dispatches meta events"
186 stream
= self
.outstream
188 # SEQUENCE_NUMBER = 0x00 (00 02 ss ss (seq-number))
189 if meta_type
== SEQUENCE_NUMBER
:
190 number
= readBew(data
)
191 stream
.sequence_number(number
)
193 # TEXT = 0x01 (01 len text...)
194 elif meta_type
== TEXT
:
197 # COPYRIGHT = 0x02 (02 len text...)
198 elif meta_type
== COPYRIGHT
:
199 stream
.copyright(data
)
201 # SEQUENCE_NAME = 0x03 (03 len text...)
202 elif meta_type
== SEQUENCE_NAME
:
203 stream
.sequence_name(data
)
205 # INSTRUMENT_NAME = 0x04 (04 len text...)
206 elif meta_type
== INSTRUMENT_NAME
:
207 stream
.instrument_name(data
)
209 # LYRIC = 0x05 (05 len text...)
210 elif meta_type
== LYRIC
:
213 # MARKER = 0x06 (06 len text...)
214 elif meta_type
== MARKER
:
217 # CUEPOINT = 0x07 (07 len text...)
218 elif meta_type
== CUEPOINT
:
219 stream
.cuepoint(data
)
221 # PROGRAM_NAME = 0x08 (05 len text...)
222 elif meta_type
== PROGRAM_NAME
:
223 stream
.program_name(data
)
225 # DEVICE_NAME = 0x09 (09 len text...)
226 elif meta_type
== DEVICE_NAME
:
227 stream
.device_name(data
)
229 # MIDI_CH_PREFIX = 0x20 (20 01 channel)
230 elif meta_type
== MIDI_CH_PREFIX
:
231 channel
= readBew(data
)
232 stream
.midi_ch_prefix(channel
)
234 # MIDI_PORT = 0x21 (21 01 port (legacy stuff))
235 elif meta_type
== MIDI_PORT
:
237 stream
.midi_port(port
)
239 # END_OFF_TRACK = 0x2F (2F 00)
240 elif meta_type
== END_OF_TRACK
:
241 stream
.end_of_track()
243 # TEMPO = 0x51 (51 03 tt tt tt (tempo in us/quarternote))
244 elif meta_type
== TEMPO
:
245 b1
, b2
, b3
= toBytes(data
)
246 # uses 3 bytes to represent time between quarter
247 # notes in microseconds
248 stream
.tempo((b1
<<16) + (b2
<<8) + b3
)
250 # SMTP_OFFSET = 0x54 (54 05 hh mm ss ff xx)
251 elif meta_type
== SMTP_OFFSET
:
252 hour
, minute
, second
, frame
, framePart
= toBytes(data
)
254 hour
, minute
, second
, frame
, framePart
)
256 # TIME_SIGNATURE = 0x58 (58 04 nn dd cc bb)
257 elif meta_type
== TIME_SIGNATURE
:
258 nn
, dd
, cc
, bb
= toBytes(data
)
259 stream
.time_signature(nn
, dd
, cc
, bb
)
261 # KEY_SIGNATURE = 0x59 (59 02 sf mi)
262 elif meta_type
== KEY_SIGNATURE
:
263 sf
, mi
= toBytes(data
)
264 stream
.key_signature(sf
, mi
)
266 # SPECIFIC = 0x7F (Sequencer specific event)
267 elif meta_type
== SPECIFIC
:
268 meta_data
= toBytes(data
)
269 stream
.sequencer_specific(meta_data
)
271 # Handles any undefined meta events
272 else: # undefined meta type
273 meta_data
= toBytes(data
)
274 stream
.meta_event(meta_type
, meta_data
)
280 if __name__
== '__main__':
283 from MidiToText
import MidiToText
285 outstream
= MidiToText()
286 dispatcher
= EventDispatcher(outstream
)
287 dispatcher
.channel_messages(NOTE_ON
, 0x00, '\x40\x40')