+++ /dev/null
-# -*- coding: ISO-8859-1 -*-\r
-\r
-from MidiOutStream import MidiOutStream\r
-from RawOutstreamFile import RawOutstreamFile\r
-\r
-from constants import *\r
-from DataTypeConverters import fromBytes, writeVar\r
-\r
-class MidiOutFile(MidiOutStream):\r
-\r
-\r
- """\r
- MidiOutFile is an eventhandler that subclasses MidiOutStream.\r
- """\r
-\r
-\r
- def __init__(self, raw_out=''):\r
-\r
- self.raw_out = RawOutstreamFile(raw_out)\r
- MidiOutStream.__init__(self)\r
- \r
- \r
- def write(self):\r
- self.raw_out.write()\r
-\r
-\r
- def event_slice(self, slc):\r
- """\r
- Writes the slice of an event to the current track. Correctly \r
- inserting a varlen timestamp too.\r
- """\r
- trk = self._current_track_buffer\r
- trk.writeVarLen(self.rel_time())\r
- trk.writeSlice(slc)\r
- \r
- \r
- #####################\r
- ## Midi events\r
-\r
-\r
- def note_on(self, channel=0, note=0x40, velocity=0x40):\r
-\r
- """\r
- channel: 0-15\r
- note, velocity: 0-127\r
- """\r
- slc = fromBytes([NOTE_ON + channel, note, velocity])\r
- self.event_slice(slc)\r
-\r
-\r
- def note_off(self, channel=0, note=0x40, velocity=0x40):\r
-\r
- """\r
- channel: 0-15\r
- note, velocity: 0-127\r
- """\r
- slc = fromBytes([NOTE_OFF + channel, note, velocity])\r
- self.event_slice(slc)\r
-\r
-\r
- def aftertouch(self, channel=0, note=0x40, velocity=0x40):\r
-\r
- """\r
- channel: 0-15\r
- note, velocity: 0-127\r
- """\r
- slc = fromBytes([AFTERTOUCH + channel, note, velocity])\r
- self.event_slice(slc)\r
-\r
-\r
- def continuous_controller(self, channel, controller, value):\r
-\r
- """\r
- channel: 0-15\r
- controller, value: 0-127\r
- """\r
- slc = fromBytes([CONTINUOUS_CONTROLLER + channel, controller, value])\r
- self.event_slice(slc)\r
- # These should probably be implemented\r
- # http://users.argonet.co.uk/users/lenny/midi/tech/spec.html#ctrlnums\r
-\r
-\r
- def patch_change(self, channel, patch):\r
-\r
- """\r
- channel: 0-15\r
- patch: 0-127\r
- """\r
- slc = fromBytes([PATCH_CHANGE + channel, patch])\r
- self.event_slice(slc)\r
-\r
-\r
- def channel_pressure(self, channel, pressure):\r
-\r
- """\r
- channel: 0-15\r
- pressure: 0-127\r
- """\r
- slc = fromBytes([CHANNEL_PRESSURE + channel, pressure])\r
- self.event_slice(slc)\r
-\r
-\r
- def pitch_bend(self, channel, value):\r
-\r
- """\r
- channel: 0-15\r
- value: 0-16383\r
- """\r
- msb = (value>>7) & 0xFF\r
- lsb = value & 0xFF\r
- slc = fromBytes([PITCH_BEND + channel, msb, lsb])\r
- self.event_slice(slc)\r
-\r
-\r
-\r
-\r
- #####################\r
- ## System Exclusive\r
-\r
-# def sysex_slice(sysex_type, data):\r
-# ""\r
-# sysex_len = writeVar(len(data)+1)\r
-# self.event_slice(SYSTEM_EXCLUSIVE + sysex_len + data + END_OFF_EXCLUSIVE)\r
-#\r
- def system_exclusive(self, data):\r
-\r
- """\r
- data: list of values in range(128)\r
- """\r
- sysex_len = writeVar(len(data)+1)\r
- self.event_slice(chr(SYSTEM_EXCLUSIVE) + sysex_len + data + chr(END_OFF_EXCLUSIVE))\r
-\r
-\r
- #####################\r
- ## Common events\r
-\r
- def midi_time_code(self, msg_type, values):\r
- """\r
- msg_type: 0-7\r
- values: 0-15\r
- """\r
- value = (msg_type<<4) + values\r
- self.event_slice(fromBytes([MIDI_TIME_CODE, value]))\r
-\r
-\r
- def song_position_pointer(self, value):\r
-\r
- """\r
- value: 0-16383\r
- """\r
- lsb = (value & 0x7F)\r
- msb = (value >> 7) & 0x7F\r
- self.event_slice(fromBytes([SONG_POSITION_POINTER, lsb, msb]))\r
-\r
-\r
- def song_select(self, songNumber):\r
-\r
- """\r
- songNumber: 0-127\r
- """\r
- self.event_slice(fromBytes([SONG_SELECT, songNumber]))\r
-\r
-\r
- def tuning_request(self):\r
-\r
- """\r
- No values passed\r
- """\r
- self.event_slice(chr(TUNING_REQUEST))\r
-\r
- \r
- #########################\r
- # header does not really belong here. But anyhoo!!!\r
- \r
- def header(self, format=0, nTracks=1, division=96):\r
-\r
- """\r
- format: type of midi file in [0,1,2]\r
- nTracks: number of tracks. 1 track for type 0 file\r
- division: timing division ie. 96 ppq.\r
- \r
- """ \r
- raw = self.raw_out\r
- raw.writeSlice('MThd')\r
- bew = raw.writeBew\r
- bew(6, 4) # header size\r
- bew(format, 2)\r
- bew(nTracks, 2)\r
- bew(division, 2)\r
-\r
-\r
- def eof(self):\r
-\r
- """\r
- End of file. No more events to be processed.\r
- """\r
- # just write the file then.\r
- self.write()\r
-\r
-\r
- #####################\r
- ## meta events\r
-\r
-\r
- def meta_slice(self, meta_type, data_slice):\r
- "Writes a meta event"\r
- slc = fromBytes([META_EVENT, meta_type]) + \\r
- writeVar(len(data_slice)) + data_slice\r
- self.event_slice(slc)\r
-\r
-\r
- def meta_event(self, meta_type, data):\r
- """\r
- Handles any undefined meta events\r
- """\r
- self.meta_slice(meta_type, fromBytes(data))\r
-\r
-\r
- def start_of_track(self, n_track=0):\r
- """\r
- n_track: number of track\r
- """\r
- self._current_track_buffer = RawOutstreamFile()\r
- self.reset_time()\r
- self._current_track += 1\r
-\r
-\r
- def end_of_track(self):\r
- """\r
- Writes the track to the buffer.\r
- """\r
- raw = self.raw_out\r
- raw.writeSlice(TRACK_HEADER)\r
- track_data = self._current_track_buffer.getvalue()\r
- # wee need to know size of track data.\r
- eot_slice = writeVar(self.rel_time()) + fromBytes([META_EVENT, END_OF_TRACK, 0])\r
- raw.writeBew(len(track_data)+len(eot_slice), 4)\r
- # then write\r
- raw.writeSlice(track_data)\r
- raw.writeSlice(eot_slice)\r
- \r
-\r
-\r
- def sequence_number(self, value):\r
-\r
- """\r
- value: 0-65535\r
- """\r
- self.meta_slice(meta_type, writeBew(value, 2))\r
-\r
-\r
- def text(self, text):\r
- """\r
- Text event\r
- text: string\r
- """\r
- self.meta_slice(TEXT, text)\r
-\r
-\r
- def copyright(self, text):\r
-\r
- """\r
- Copyright notice\r
- text: string\r
- """\r
- self.meta_slice(COPYRIGHT, text)\r
-\r
-\r
- def sequence_name(self, text):\r
- """\r
- Sequence/track name\r
- text: string\r
- """\r
- self.meta_slice(SEQUENCE_NAME, text)\r
-\r
-\r
- def instrument_name(self, text):\r
-\r
- """\r
- text: string\r
- """\r
- self.meta_slice(INSTRUMENT_NAME, text)\r
-\r
-\r
- def lyric(self, text):\r
-\r
- """\r
- text: string\r
- """\r
- self.meta_slice(LYRIC, text)\r
-\r
-\r
- def marker(self, text):\r
-\r
- """\r
- text: string\r
- """\r
- self.meta_slice(MARKER, text)\r
-\r
-\r
- def cuepoint(self, text):\r
-\r
- """\r
- text: string\r
- """\r
- self.meta_slice(CUEPOINT, text)\r
-\r
-\r
- def midi_ch_prefix(self, channel):\r
-\r
- """\r
- channel: midi channel for subsequent data\r
- (deprecated in the spec)\r
- """\r
- self.meta_slice(MIDI_CH_PREFIX, chr(channel))\r
-\r
-\r
- def midi_port(self, value):\r
-\r
- """\r
- value: Midi port (deprecated in the spec)\r
- """\r
- self.meta_slice(MIDI_CH_PREFIX, chr(value))\r
-\r
-\r
- def tempo(self, value):\r
-\r
- """\r
- value: 0-2097151\r
- tempo in us/quarternote\r
- (to calculate value from bpm: int(60,000,000.00 / BPM))\r
- """\r
- hb, mb, lb = (value>>16 & 0xff), (value>>8 & 0xff), (value & 0xff)\r
- self.meta_slice(TEMPO, fromBytes([hb, mb, lb]))\r
-\r
-\r
- def smtp_offset(self, hour, minute, second, frame, framePart):\r
-\r
- """\r
- hour,\r
- minute,\r
- second: 3 bytes specifying the hour (0-23), minutes (0-59) and \r
- seconds (0-59), respectively. The hour should be \r
- encoded with the SMPTE format, just as it is in MIDI \r
- Time Code.\r
- frame: A byte specifying the number of frames per second (one \r
- of : 24, 25, 29, 30).\r
- framePart: A byte specifying the number of fractional frames, \r
- in 100ths of a frame (even in SMPTE-based tracks \r
- using a different frame subdivision, defined in the \r
- MThd chunk).\r
- """\r
- self.meta_slice(SMTP_OFFSET, fromBytes([hour, minute, second, frame, framePart]))\r
-\r
-\r
-\r
- def time_signature(self, nn, dd, cc, bb):\r
-\r
- """\r
- nn: Numerator of the signature as notated on sheet music\r
- dd: Denominator of the signature as notated on sheet music\r
- The denominator is a negative power of 2: 2 = quarter \r
- note, 3 = eighth, etc.\r
- cc: The number of MIDI clocks in a metronome click\r
- bb: The number of notated 32nd notes in a MIDI quarter note \r
- (24 MIDI clocks) \r
- """\r
- self.meta_slice(TIME_SIGNATURE, fromBytes([nn, dd, cc, bb]))\r
-\r
-\r
-\r
-\r
- def key_signature(self, sf, mi):\r
-\r
- """\r
- sf: is a byte specifying the number of flats (-ve) or sharps \r
- (+ve) that identifies the key signature (-7 = 7 flats, -1 \r
- = 1 flat, 0 = key of C, 1 = 1 sharp, etc).\r
- mi: is a byte specifying a major (0) or minor (1) key.\r
- """\r
- self.meta_slice(KEY_SIGNATURE, fromBytes([sf, mi]))\r
-\r
-\r
-\r
- def sequencer_specific(self, data):\r
-\r
- """\r
- data: The data as byte values\r
- """\r
- self.meta_slice(SEQUENCER_SPECIFIC, data)\r
-\r
-\r
-\r
-\r
-\r
-# #####################\r
-# ## realtime events\r
-\r
-# These are of no use in a midi file, so they are ignored!!!\r
-\r
-# def timing_clock(self):\r
-# def song_start(self):\r
-# def song_stop(self):\r
-# def song_continue(self):\r
-# def active_sensing(self):\r
-# def system_reset(self):\r
-\r
-\r
-\r
-if __name__ == '__main__':\r
-\r
- out_file = 'test/midifiles/midiout.mid'\r
- midi = MidiOutFile(out_file)\r
-\r
-#format: 0, nTracks: 1, division: 480\r
-#----------------------------------\r
-#\r
-#Start - track #0\r
-#sequence_name: Type 0\r
-#tempo: 500000\r
-#time_signature: 4 2 24 8\r
-#note_on - ch:00, note:48, vel:64 time:0\r
-#note_off - ch:00, note:48, vel:40 time:480\r
-#End of track\r
-#\r
-#End of file\r
-\r
-\r
- midi.header(0, 1, 480)\r
- \r
- midi.start_of_track()\r
- midi.sequence_name('Type 0')\r
- midi.tempo(750000)\r
- midi.time_signature(4, 2, 24, 8)\r
- ch = 0\r
- for i in range(127):\r
- midi.note_on(ch, i, 0x64)\r
- midi.update_time(96)\r
- midi.note_off(ch, i, 0x40)\r
- midi.update_time(0)\r
- \r
- midi.update_time(0)\r
- midi.end_of_track()\r
- \r
- midi.eof() # currently optional, should it do the write instead of write??\r
-\r
-\r
- midi.write()
\ No newline at end of file