Package echonest :: Package remix :: Package support :: Package midi :: Module MidiOutFile
[hide private]
[frames] | no frames]

Source Code for Module echonest.remix.support.midi.MidiOutFile

  1  # -*- coding: ISO-8859-1 -*-
 
  2  
 
  3  from MidiOutStream import MidiOutStream 
  4  from RawOutstreamFile import RawOutstreamFile 
  5  
 
  6  from constants import * 
  7  from DataTypeConverters import fromBytes, writeVar 
  8  
 
9 -class MidiOutFile(MidiOutStream):
10 11 12 """ 13 MidiOutFile is an eventhandler that subclasses MidiOutStream. 14 """ 15 16
17 - def __init__(self, raw_out=''):
18 19 self.raw_out = RawOutstreamFile(raw_out) 20 MidiOutStream.__init__(self)
21 22
23 - def write(self):
24 self.raw_out.write()
25 26
27 - def event_slice(self, slc):
28 """ 29 Writes the slice of an event to the current track. Correctly 30 inserting a varlen timestamp too. 31 """ 32 trk = self._current_track_buffer 33 trk.writeVarLen(self.rel_time()) 34 trk.writeSlice(slc)
35 36 37 ##################### 38 ## Midi events 39 40
41 - def note_on(self, channel=0, note=0x40, velocity=0x40):
42 43 """ 44 channel: 0-15 45 note, velocity: 0-127 46 """ 47 slc = fromBytes([NOTE_ON + channel, note, velocity]) 48 self.event_slice(slc)
49 50
51 - def note_off(self, channel=0, note=0x40, velocity=0x40):
52 53 """ 54 channel: 0-15 55 note, velocity: 0-127 56 """ 57 slc = fromBytes([NOTE_OFF + channel, note, velocity]) 58 self.event_slice(slc)
59 60
61 - def aftertouch(self, channel=0, note=0x40, velocity=0x40):
62 63 """ 64 channel: 0-15 65 note, velocity: 0-127 66 """ 67 slc = fromBytes([AFTERTOUCH + channel, note, velocity]) 68 self.event_slice(slc)
69 70
71 - def continuous_controller(self, channel, controller, value):
72 73 """ 74 channel: 0-15 75 controller, value: 0-127 76 """ 77 slc = fromBytes([CONTINUOUS_CONTROLLER + channel, controller, value]) 78 self.event_slice(slc)
79 # These should probably be implemented 80 # http://users.argonet.co.uk/users/lenny/midi/tech/spec.html#ctrlnums 81 82
83 - def patch_change(self, channel, patch):
84 85 """ 86 channel: 0-15 87 patch: 0-127 88 """ 89 slc = fromBytes([PATCH_CHANGE + channel, patch]) 90 self.event_slice(slc)
91 92
93 - def channel_pressure(self, channel, pressure):
94 95 """ 96 channel: 0-15 97 pressure: 0-127 98 """ 99 slc = fromBytes([CHANNEL_PRESSURE + channel, pressure]) 100 self.event_slice(slc)
101 102
103 - def pitch_bend(self, channel, value):
104 105 """ 106 channel: 0-15 107 value: 0-16383 108 """ 109 msb = (value>>7) & 0xFF 110 lsb = value & 0xFF 111 slc = fromBytes([PITCH_BEND + channel, msb, lsb]) 112 self.event_slice(slc)
113 114 115 116 117 ##################### 118 ## System Exclusive 119 120 # def sysex_slice(sysex_type, data): 121 # "" 122 # sysex_len = writeVar(len(data)+1) 123 # self.event_slice(SYSTEM_EXCLUSIVE + sysex_len + data + END_OFF_EXCLUSIVE) 124 #
125 - def system_exclusive(self, data):
126 127 """ 128 data: list of values in range(128) 129 """ 130 sysex_len = writeVar(len(data)+1) 131 self.event_slice(chr(SYSTEM_EXCLUSIVE) + sysex_len + data + chr(END_OFF_EXCLUSIVE))
132 133 134 ##################### 135 ## Common events 136
137 - def midi_time_code(self, msg_type, values):
138 """ 139 msg_type: 0-7 140 values: 0-15 141 """ 142 value = (msg_type<<4) + values 143 self.event_slice(fromBytes([MIDI_TIME_CODE, value]))
144 145
146 - def song_position_pointer(self, value):
147 148 """ 149 value: 0-16383 150 """ 151 lsb = (value & 0x7F) 152 msb = (value >> 7) & 0x7F 153 self.event_slice(fromBytes([SONG_POSITION_POINTER, lsb, msb]))
154 155
156 - def song_select(self, songNumber):
157 158 """ 159 songNumber: 0-127 160 """ 161 self.event_slice(fromBytes([SONG_SELECT, songNumber]))
162 163
164 - def tuning_request(self):
165 166 """ 167 No values passed 168 """ 169 self.event_slice(chr(TUNING_REQUEST))
170 171 172 ######################### 173 # header does not really belong here. But anyhoo!!! 174
175 - def header(self, format=0, nTracks=1, division=96):
176 177 """ 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. 181 182 """ 183 raw = self.raw_out 184 raw.writeSlice('MThd') 185 bew = raw.writeBew 186 bew(6, 4) # header size 187 bew(format, 2) 188 bew(nTracks, 2) 189 bew(division, 2)
190 191
192 - def eof(self):
193 194 """ 195 End of file. No more events to be processed. 196 """ 197 # just write the file then. 198 self.write()
199 200 201 ##################### 202 ## meta events 203 204
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)
210 211
212 - def meta_event(self, meta_type, data):
213 """ 214 Handles any undefined meta events 215 """ 216 self.meta_slice(meta_type, fromBytes(data))
217 218
219 - def start_of_track(self, n_track=0):
220 """ 221 n_track: number of track 222 """ 223 self._current_track_buffer = RawOutstreamFile() 224 self.reset_time() 225 self._current_track += 1
226 227
228 - def end_of_track(self):
229 """ 230 Writes the track to the buffer. 231 """ 232 raw = self.raw_out 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) 238 # then write 239 raw.writeSlice(track_data) 240 raw.writeSlice(eot_slice)
241 242 243
244 - def sequence_number(self, value):
245 246 """ 247 value: 0-65535 248 """ 249 self.meta_slice(meta_type, writeBew(value, 2))
250 251
252 - def text(self, text):
253 """ 254 Text event 255 text: string 256 """ 257 self.meta_slice(TEXT, text)
258 259
260 - def copyright(self, text):
261 262 """ 263 Copyright notice 264 text: string 265 """ 266 self.meta_slice(COPYRIGHT, text)
267 268
269 - def sequence_name(self, text):
270 """ 271 Sequence/track name 272 text: string 273 """ 274 self.meta_slice(SEQUENCE_NAME, text)
275 276
277 - def instrument_name(self, text):
278 279 """ 280 text: string 281 """ 282 self.meta_slice(INSTRUMENT_NAME, text)
283 284
285 - def lyric(self, text):
286 287 """ 288 text: string 289 """ 290 self.meta_slice(LYRIC, text)
291 292
293 - def marker(self, text):
294 295 """ 296 text: string 297 """ 298 self.meta_slice(MARKER, text)
299 300
301 - def cuepoint(self, text):
302 303 """ 304 text: string 305 """ 306 self.meta_slice(CUEPOINT, text)
307 308
309 - def midi_ch_prefix(self, channel):
310 311 """ 312 channel: midi channel for subsequent data 313 (deprecated in the spec) 314 """ 315 self.meta_slice(MIDI_CH_PREFIX, chr(channel))
316 317
318 - def midi_port(self, value):
319 320 """ 321 value: Midi port (deprecated in the spec) 322 """ 323 self.meta_slice(MIDI_CH_PREFIX, chr(value))
324 325
326 - def tempo(self, value):
327 328 """ 329 value: 0-2097151 330 tempo in us/quarternote 331 (to calculate value from bpm: int(60,000,000.00 / BPM)) 332 """ 333 hb, mb, lb = (value>>16 & 0xff), (value>>8 & 0xff), (value & 0xff) 334 self.meta_slice(TEMPO, fromBytes([hb, mb, lb]))
335 336
337 - def smtp_offset(self, hour, minute, second, frame, framePart):
338 339 """ 340 hour, 341 minute, 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 345 Time Code. 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 351 MThd chunk). 352 """ 353 self.meta_slice(SMTP_OFFSET, fromBytes([hour, minute, second, frame, framePart]))
354 355 356
357 - def time_signature(self, nn, dd, cc, bb):
358 359 """ 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 366 (24 MIDI clocks) 367 """ 368 self.meta_slice(TIME_SIGNATURE, fromBytes([nn, dd, cc, bb]))
369 370 371 372
373 - def key_signature(self, sf, mi):
374 375 """ 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. 380 """ 381 self.meta_slice(KEY_SIGNATURE, fromBytes([sf, mi]))
382 383 384
385 - def sequencer_specific(self, data):
386 387 """ 388 data: The data as byte values 389 """ 390 self.meta_slice(SEQUENCER_SPECIFIC, data)
391 392 393 394 395 396 # ##################### 397 # ## realtime events 398 399 # These are of no use in a midi file, so they are ignored!!! 400 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): 407 408 409 410 if __name__ == '__main__': 411 412 out_file = 'test/midifiles/midiout.mid' 413 midi = MidiOutFile(out_file) 414 415 #format: 0, nTracks: 1, division: 480 416 #---------------------------------- 417 # 418 #Start - track #0 419 #sequence_name: Type 0 420 #tempo: 500000 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 424 #End of track 425 # 426 #End of file 427 428 429 midi.header(0, 1, 480) 430 431 midi.start_of_track() 432 midi.sequence_name('Type 0') 433 midi.tempo(750000) 434 midi.time_signature(4, 2, 24, 8) 435 ch = 0 436 for i in range(127): 437 midi.note_on(ch, i, 0x64) 438 midi.update_time(96) 439 midi.note_off(ch, i, 0x40) 440 midi.update_time(0) 441 442 midi.update_time(0) 443 midi.end_of_track() 444 445 midi.eof() # currently optional, should it do the write instead of write?? 446 447 448 midi.write() 449