1 # -*- coding: ISO-8859-1 -*-
3 from MidiOutStream
import MidiOutStream
4 from RawOutstreamFile
import RawOutstreamFile
6 from constants
import *
7 from DataTypeConverters
import fromBytes
, writeVar
9 class MidiOutFile(MidiOutStream
):
13 MidiOutFile is an eventhandler that subclasses MidiOutStream.
17 def __init__(self
, raw_out
=''):
19 self
.raw_out
= RawOutstreamFile(raw_out
)
20 MidiOutStream
.__init
__(self
)
27 def event_slice(self
, slc
):
29 Writes the slice of an event to the current track. Correctly
30 inserting a varlen timestamp too.
32 trk
= self
._current
_track
_buffer
33 trk
.writeVarLen(self
.rel_time())
41 def note_on(self
, channel
=0, note
=0x40, velocity
=0x40):
47 slc
= fromBytes([NOTE_ON
+ channel
, note
, velocity
])
51 def note_off(self
, channel
=0, note
=0x40, velocity
=0x40):
57 slc
= fromBytes([NOTE_OFF
+ channel
, note
, velocity
])
61 def aftertouch(self
, channel
=0, note
=0x40, velocity
=0x40):
67 slc
= fromBytes([AFTERTOUCH
+ channel
, note
, velocity
])
71 def continuous_controller(self
, channel
, controller
, value
):
75 controller, value: 0-127
77 slc
= fromBytes([CONTINUOUS_CONTROLLER
+ channel
, controller
, value
])
79 # These should probably be implemented
80 # http://users.argonet.co.uk/users/lenny/midi/tech/spec.html#ctrlnums
83 def patch_change(self
, channel
, patch
):
89 slc
= fromBytes([PATCH_CHANGE
+ channel
, patch
])
93 def channel_pressure(self
, channel
, pressure
):
99 slc
= fromBytes([CHANNEL_PRESSURE
+ channel
, pressure
])
100 self
.event_slice(slc
)
103 def pitch_bend(self
, channel
, value
):
109 msb
= (value
>>7) & 0xFF
111 slc
= fromBytes([PITCH_BEND
+ channel
, msb
, lsb
])
112 self
.event_slice(slc
)
117 #####################
120 # def sysex_slice(sysex_type, data):
122 # sysex_len = writeVar(len(data)+1)
123 # self.event_slice(SYSTEM_EXCLUSIVE + sysex_len + data + END_OFF_EXCLUSIVE)
125 def system_exclusive(self
, data
):
128 data: list of values in range(128)
130 sysex_len
= writeVar(len(data
)+1)
131 self
.event_slice(chr(SYSTEM_EXCLUSIVE
) + sysex_len
+ data
+ chr(END_OFF_EXCLUSIVE
))
134 #####################
137 def midi_time_code(self
, msg_type
, values
):
142 value
= (msg_type
<<4) + values
143 self
.event_slice(fromBytes([MIDI_TIME_CODE
, value
]))
146 def song_position_pointer(self
, value
):
152 msb
= (value
>> 7) & 0x7F
153 self
.event_slice(fromBytes([SONG_POSITION_POINTER
, lsb
, msb
]))
156 def song_select(self
, songNumber
):
161 self
.event_slice(fromBytes([SONG_SELECT
, songNumber
]))
164 def tuning_request(self
):
169 self
.event_slice(chr(TUNING_REQUEST
))
172 #########################
173 # header does not really belong here. But anyhoo!!!
175 def header(self
, format
=0, nTracks
=1, division
=96):
178 format: type of midi file in [0,1,2]
179 nTracks: number of tracks. 1 track for type 0 file
180 division: timing division ie. 96 ppq.
184 raw
.writeSlice('MThd')
186 bew(6, 4) # header size
195 End of file. No more events to be processed.
197 # just write the file then.
201 #####################
205 def meta_slice(self
, meta_type
, data_slice
):
206 "Writes a meta event"
207 slc
= fromBytes([META_EVENT
, meta_type
]) + \
208 writeVar(len(data_slice
)) + data_slice
209 self
.event_slice(slc
)
212 def meta_event(self
, meta_type
, data
):
214 Handles any undefined meta events
216 self
.meta_slice(meta_type
, fromBytes(data
))
219 def start_of_track(self
, n_track
=0):
221 n_track: number of track
223 self
._current
_track
_buffer
= RawOutstreamFile()
225 self
._current
_track
+= 1
228 def end_of_track(self
):
230 Writes the track to the buffer.
233 raw
.writeSlice(TRACK_HEADER
)
234 track_data
= self
._current
_track
_buffer
.getvalue()
235 # wee need to know size of track data.
236 eot_slice
= writeVar(self
.rel_time()) + fromBytes([META_EVENT
, END_OF_TRACK
, 0])
237 raw
.writeBew(len(track_data
)+len(eot_slice
), 4)
239 raw
.writeSlice(track_data
)
240 raw
.writeSlice(eot_slice
)
244 def sequence_number(self
, value
):
249 self
.meta_slice(meta_type
, writeBew(value
, 2))
252 def text(self
, text
):
257 self
.meta_slice(TEXT
, text
)
260 def copyright(self
, text
):
266 self
.meta_slice(COPYRIGHT
, text
)
269 def sequence_name(self
, text
):
274 self
.meta_slice(SEQUENCE_NAME
, text
)
277 def instrument_name(self
, text
):
282 self
.meta_slice(INSTRUMENT_NAME
, text
)
285 def lyric(self
, text
):
290 self
.meta_slice(LYRIC
, text
)
293 def marker(self
, text
):
298 self
.meta_slice(MARKER
, text
)
301 def cuepoint(self
, text
):
306 self
.meta_slice(CUEPOINT
, text
)
309 def midi_ch_prefix(self
, channel
):
312 channel: midi channel for subsequent data
313 (deprecated in the spec)
315 self
.meta_slice(MIDI_CH_PREFIX
, chr(channel
))
318 def midi_port(self
, value
):
321 value: Midi port (deprecated in the spec)
323 self
.meta_slice(MIDI_CH_PREFIX
, chr(value
))
326 def tempo(self
, value
):
330 tempo in us/quarternote
331 (to calculate value from bpm: int(60,000,000.00 / BPM))
333 hb
, mb
, lb
= (value
>>16 & 0xff), (value
>>8 & 0xff), (value
& 0xff)
334 self
.meta_slice(TEMPO
, fromBytes([hb
, mb
, lb
]))
337 def smtp_offset(self
, hour
, minute
, second
, frame
, framePart
):
342 second: 3 bytes specifying the hour (0-23), minutes (0-59) and
343 seconds (0-59), respectively. The hour should be
344 encoded with the SMPTE format, just as it is in MIDI
346 frame: A byte specifying the number of frames per second (one
347 of : 24, 25, 29, 30).
348 framePart: A byte specifying the number of fractional frames,
349 in 100ths of a frame (even in SMPTE-based tracks
350 using a different frame subdivision, defined in the
353 self
.meta_slice(SMTP_OFFSET
, fromBytes([hour
, minute
, second
, frame
, framePart
]))
357 def time_signature(self
, nn
, dd
, cc
, bb
):
360 nn: Numerator of the signature as notated on sheet music
361 dd: Denominator of the signature as notated on sheet music
362 The denominator is a negative power of 2: 2 = quarter
363 note, 3 = eighth, etc.
364 cc: The number of MIDI clocks in a metronome click
365 bb: The number of notated 32nd notes in a MIDI quarter note
368 self
.meta_slice(TIME_SIGNATURE
, fromBytes([nn
, dd
, cc
, bb
]))
373 def key_signature(self
, sf
, mi
):
376 sf: is a byte specifying the number of flats (-ve) or sharps
377 (+ve) that identifies the key signature (-7 = 7 flats, -1
378 = 1 flat, 0 = key of C, 1 = 1 sharp, etc).
379 mi: is a byte specifying a major (0) or minor (1) key.
381 self
.meta_slice(KEY_SIGNATURE
, fromBytes([sf
, mi
]))
385 def sequencer_specific(self
, data
):
388 data: The data as byte values
390 self
.meta_slice(SEQUENCER_SPECIFIC
, data
)
396 # #####################
399 # These are of no use in a midi file, so they are ignored!!!
401 # def timing_clock(self):
402 # def song_start(self):
403 # def song_stop(self):
404 # def song_continue(self):
405 # def active_sensing(self):
406 # def system_reset(self):
410 if __name__
== '__main__':
412 out_file
= 'test/midifiles/midiout.mid'
413 midi
= MidiOutFile(out_file
)
415 #format: 0, nTracks: 1, division: 480
416 #----------------------------------
419 #sequence_name: Type 0
421 #time_signature: 4 2 24 8
422 #note_on - ch:00, note:48, vel:64 time:0
423 #note_off - ch:00, note:48, vel:40 time:480
429 midi
.header(0, 1, 480)
431 midi
.start_of_track()
432 midi
.sequence_name('Type 0')
434 midi
.time_signature(4, 2, 24, 8)
437 midi
.note_on(ch
, i
, 0x64)
439 midi
.note_off(ch
, i
, 0x40)
445 midi
.eof() # currently optional, should it do the write instead of write??