# -*- coding: ISO-8859-1 -*-
from MidiOutStream import MidiOutStream
from RawOutstreamFile import RawOutstreamFile
from constants import *
from DataTypeConverters import fromBytes, writeVar
class MidiOutFile(MidiOutStream):
"""
MidiOutFile is an eventhandler that subclasses MidiOutStream.
"""
def __init__(self, raw_out=''):
self.raw_out = RawOutstreamFile(raw_out)
MidiOutStream.__init__(self)
def write(self):
self.raw_out.write()
def event_slice(self, slc):
"""
Writes the slice of an event to the current track. Correctly
inserting a varlen timestamp too.
"""
trk = self._current_track_buffer
trk.writeVarLen(self.rel_time())
trk.writeSlice(slc)
#####################
## Midi events
def note_on(self, channel=0, note=0x40, velocity=0x40):
"""
channel: 0-15
note, velocity: 0-127
"""
slc = fromBytes([NOTE_ON + channel, note, velocity])
self.event_slice(slc)
def note_off(self, channel=0, note=0x40, velocity=0x40):
"""
channel: 0-15
note, velocity: 0-127
"""
slc = fromBytes([NOTE_OFF + channel, note, velocity])
self.event_slice(slc)
def aftertouch(self, channel=0, note=0x40, velocity=0x40):
"""
channel: 0-15
note, velocity: 0-127
"""
slc = fromBytes([AFTERTOUCH + channel, note, velocity])
self.event_slice(slc)
def continuous_controller(self, channel, controller, value):
"""
channel: 0-15
controller, value: 0-127
"""
slc = fromBytes([CONTINUOUS_CONTROLLER + channel, controller, value])
self.event_slice(slc)
# These should probably be implemented
# http://users.argonet.co.uk/users/lenny/midi/tech/spec.html#ctrlnums
def patch_change(self, channel, patch):
"""
channel: 0-15
patch: 0-127
"""
slc = fromBytes([PATCH_CHANGE + channel, patch])
self.event_slice(slc)
def channel_pressure(self, channel, pressure):
"""
channel: 0-15
pressure: 0-127
"""
slc = fromBytes([CHANNEL_PRESSURE + channel, pressure])
self.event_slice(slc)
def pitch_bend(self, channel, value):
"""
channel: 0-15
value: 0-16383
"""
msb = (value>>7) & 0xFF
lsb = value & 0xFF
slc = fromBytes([PITCH_BEND + channel, msb, lsb])
self.event_slice(slc)
#####################
## System Exclusive
# def sysex_slice(sysex_type, data):
# ""
# sysex_len = writeVar(len(data)+1)
# self.event_slice(SYSTEM_EXCLUSIVE + sysex_len + data + END_OFF_EXCLUSIVE)
#
def system_exclusive(self, data):
"""
data: list of values in range(128)
"""
sysex_len = writeVar(len(data)+1)
self.event_slice(chr(SYSTEM_EXCLUSIVE) + sysex_len + data + chr(END_OFF_EXCLUSIVE))
#####################
## Common events
def midi_time_code(self, msg_type, values):
"""
msg_type: 0-7
values: 0-15
"""
value = (msg_type<<4) + values
self.event_slice(fromBytes([MIDI_TIME_CODE, value]))
def song_position_pointer(self, value):
"""
value: 0-16383
"""
lsb = (value & 0x7F)
msb = (value >> 7) & 0x7F
self.event_slice(fromBytes([SONG_POSITION_POINTER, lsb, msb]))
def song_select(self, songNumber):
"""
songNumber: 0-127
"""
self.event_slice(fromBytes([SONG_SELECT, songNumber]))
def tuning_request(self):
"""
No values passed
"""
self.event_slice(chr(TUNING_REQUEST))
#########################
# header does not really belong here. But anyhoo!!!
def header(self, format=0, nTracks=1, division=96):
"""
format: type of midi file in [0,1,2]
nTracks: number of tracks. 1 track for type 0 file
division: timing division ie. 96 ppq.
"""
raw = self.raw_out
raw.writeSlice('MThd')
bew = raw.writeBew
bew(6, 4) # header size
bew(format, 2)
bew(nTracks, 2)
bew(division, 2)
def eof(self):
"""
End of file. No more events to be processed.
"""
# just write the file then.
self.write()
#####################
## meta events
def meta_slice(self, meta_type, data_slice):
"Writes a meta event"
slc = fromBytes([META_EVENT, meta_type]) + \
writeVar(len(data_slice)) + data_slice
self.event_slice(slc)
def meta_event(self, meta_type, data):
"""
Handles any undefined meta events
"""
self.meta_slice(meta_type, fromBytes(data))
def start_of_track(self, n_track=0):
"""
n_track: number of track
"""
self._current_track_buffer = RawOutstreamFile()
self.reset_time()
self._current_track += 1
def end_of_track(self):
"""
Writes the track to the buffer.
"""
raw = self.raw_out
raw.writeSlice(TRACK_HEADER)
track_data = self._current_track_buffer.getvalue()
# wee need to know size of track data.
eot_slice = writeVar(self.rel_time()) + fromBytes([META_EVENT, END_OF_TRACK, 0])
raw.writeBew(len(track_data)+len(eot_slice), 4)
# then write
raw.writeSlice(track_data)
raw.writeSlice(eot_slice)
def sequence_number(self, value):
"""
value: 0-65535
"""
self.meta_slice(meta_type, writeBew(value, 2))
def text(self, text):
"""
Text event
text: string
"""
self.meta_slice(TEXT, text)
def copyright(self, text):
"""
Copyright notice
text: string
"""
self.meta_slice(COPYRIGHT, text)
def sequence_name(self, text):
"""
Sequence/track name
text: string
"""
self.meta_slice(SEQUENCE_NAME, text)
def instrument_name(self, text):
"""
text: string
"""
self.meta_slice(INSTRUMENT_NAME, text)
def lyric(self, text):
"""
text: string
"""
self.meta_slice(LYRIC, text)
def marker(self, text):
"""
text: string
"""
self.meta_slice(MARKER, text)
def cuepoint(self, text):
"""
text: string
"""
self.meta_slice(CUEPOINT, text)
def midi_ch_prefix(self, channel):
"""
channel: midi channel for subsequent data
(deprecated in the spec)
"""
self.meta_slice(MIDI_CH_PREFIX, chr(channel))
def midi_port(self, value):
"""
value: Midi port (deprecated in the spec)
"""
self.meta_slice(MIDI_CH_PREFIX, chr(value))
def tempo(self, value):
"""
value: 0-2097151
tempo in us/quarternote
(to calculate value from bpm: int(60,000,000.00 / BPM))
"""
hb, mb, lb = (value>>16 & 0xff), (value>>8 & 0xff), (value & 0xff)
self.meta_slice(TEMPO, fromBytes([hb, mb, lb]))
def smtp_offset(self, hour, minute, second, frame, framePart):
"""
hour,
minute,
second: 3 bytes specifying the hour (0-23), minutes (0-59) and
seconds (0-59), respectively. The