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??
b'\\ No newline at end of file'