class EventRegistry(object): Events = {} MetaEvents = {} def register_event(cls, event, bases): if MetaEvent in bases: assert event.metacommand not in cls.MetaEvents, \ "Event %s already registered" % event.name cls.MetaEvents[event.metacommand] = event elif (Event in bases) or (NoteEvent in bases): assert event.statusmsg not in cls.Events, \ "Event %s already registered" % event.name cls.Events[event.statusmsg] = event else: raise ValueError, "Unknown bases class in event type: "+event.name register_event = classmethod(register_event) """ EventMIDI : Concrete class used to describe MIDI Events. Inherits from Event. """ class AbstractEvent(object): __slots__ = ['tick', 'data'] name = "Generic MIDI Event" length = 0 statusmsg = 0x0 class __metaclass__(type): def __init__(cls, name, bases, dict): if name not in ['AbstractEvent', 'Event', 'MetaEvent', 'NoteEvent']: EventRegistry.register_event(cls, bases) def __init__(self, **kw): if type(self.length) == int: defdata = [0] * self.length else: defdata = [] self.tick = 0 self.data = defdata for key in kw: setattr(self, key, kw[key]) def __cmp__(self, other): if self.tick < other.tick: return -1 elif self.tick > other.tick: return 1 return cmp(self.data, other.data) def __baserepr__(self, keys=[]): keys = ['tick'] + keys + ['data'] body = [] for key in keys: val = getattr(self, key) keyval = "%s=%s" % (key, val) body.append(keyval) body = str.join(', ', body) return "midi.%s(%s)" % (self.__class__.__name__, body) def __repr__(self): return self.__baserepr__() """ MetaEvent is a special subclass of Event that is not meant to be used as a concrete class. It defines a subset of Events known as the Meta events. """ class Event(AbstractEvent): __slots__ = ['channel'] name = 'Event' def __init__(self, **kw): if 'channel' not in kw: kw = kw.copy() kw['channel'] = 0 super(Event, self).__init__(**kw) def __cmp__(self, other): if self.tick < other.tick: return -1 elif self.tick > other.tick: return 1 if self.channel < other.channel: return -1 elif self.channel > other.channel: return 1 return cmp(self.data, other.data) def __repr__(self): return self.__baserepr__(['channel']) def is_event(cls, statusmsg): return (cls.statusmsg == (statusmsg & 0xF0)) is_event = classmethod(is_event) """ MetaEvent is a special subclass of Event that is not meant to be used as a concrete class. It defines a subset of Events known as the Meta events. """ class MetaEvent(AbstractEvent): statusmsg = 0xFF metacommand = 0x0 name = 'Meta Event' def is_event(cls, statusmsg): return (statusmsg == 0xFF) is_event = classmethod(is_event) """ NoteEvent is a special subclass of Event that is not meant to be used as a concrete class. It defines the generalities of NoteOn and NoteOff events. """ class NoteEvent(Event): __slots__ = ['pitch', 'velocity'] length = 2 def get_pitch(self): return self.data[0] def set_pitch(self, val): self.data[0] = val pitch = property(get_pitch, set_pitch) def get_velocity(self): return self.data[1] def set_velocity(self, val): self.data[1] = val velocity = property(set_velocity, get_velocity) class NoteOnEvent(NoteEvent): statusmsg = 0x90 name = 'Note On' class NoteOffEvent(NoteEvent): statusmsg = 0x80 name = 'Note Off' class AfterTouchEvent(Event): statusmsg = 0xA0 length = 2 name = 'After Touch' class ControlChangeEvent(Event): __slots__ = ['control', 'value'] statusmsg = 0xB0 length = 2 name = 'Control Change' def set_control(self, val): self.data[0] = val def get_control(self): return self.data[0] control = property(set_control, get_control) def set_value(self, val): self.data[1] = val def get_value(self): return self.data[1] value = property(set_value, get_value) class ProgramChangeEvent(Event): __slots__ = ['value'] statusmsg = 0xC0 length = 1 name = 'Program Change' def set_value(self, val): self.data[1] = val def get_value(self): return self.data[1] value = property(set_value, get_value) class ChannelAfterTouchEvent(Event): __slots__ = ['value'] statusmsg = 0xD0 length = 1 name = 'Channel After Touch' def set_value(self, val): self.data[1] = val def get_value(self): return self.data[1] value = property(set_value, get_value) class PitchWheelEvent(Event): __slots__ = ['pitch'] statusmsg = 0xE0 length = 2 name = 'Pitch Wheel' def get_pitch(self): return ((self.data[1] << 7) | self.data[0]) - 0x2000 def set_pitch(self, pitch): value = pitch + 0x2000 self.data[0] = value & 0xFF self.data[1] = (value >> 7) & 0xFF pitch = property(get_pitch, set_pitch) class SysexEvent(Event): statusmsg = 0xF0 name = 'SysEx' length = 'varlen' def is_event(cls, statusmsg): return (cls.statusmsg == statusmsg) is_event = classmethod(is_event) class SequenceNumberMetaEvent(MetaEvent): name = 'Sequence Number' metacommand = 0x00 length = 2 class TextMetaEvent(MetaEvent): name = 'Text' metacommand = 0x01 length = 'varlen' class CopyrightMetaEvent(MetaEvent): name = 'Copyright Notice' metacommand = 0x02 length = 'varlen' class TrackNameEvent(MetaEvent): name = 'Track Name' metacommand = 0x03 length = 'varlen' class InstrumentNameEvent(MetaEvent): name = 'Instrument Name' metacommand = 0x04 length = 'varlen' class LryricsEvent(MetaEvent): name = 'Lyrics' metacommand = 0x05 length = 'varlen' class MarkerEvent(MetaEvent): name = 'Marker' metacommand = 0x06 length = 'varlen' class CuePointEvent(MetaEvent): name = 'Cue Point' metacommand = 0x07 length = 'varlen' class SomethingEvent(MetaEvent): name = 'Something' metacommand = 0x09 class ChannelPrefixEvent(MetaEvent): name = 'Channel Prefix' metacommand = 0x20 length = 1 class PortEvent(MetaEvent): name = 'MIDI Port/Cable' metacommand = 0x21 class TrackLoopEvent(MetaEvent): name = 'Track Loop' metacommand = 0x2E class EndOfTrackEvent(MetaEvent): name = 'End of Track' metacommand = 0x2F class SetTempoEvent(MetaEvent): __slots__ = ['bpm', 'mpqn'] name = 'Set Tempo' metacommand = 0x51 length = 3 def set_bpm(self, bpm): self.mpqn = int(float(6e7) / bpm) def get_bpm(self): return float(6e7) / self.mpqn bpm = property(get_bpm, set_bpm) def get_mpqn(self): assert(len(self.data) == 3) vals = [x << (16 - (8 * self.data.index(x))) for x in self.data] return sum(vals) def set_mpqn(self, val): self.data = [(val >> (16 - (8 * x)) & 0xFF) for x in range(3)] mpqn = property(get_mpqn, set_mpqn) class SmpteOffsetEvent(MetaEvent): name = 'SMPTE Offset' metacommand = 0x54 class TimeSignatureEvent(MetaEvent): __slots__ = ['numerator', 'denominator', 'metronome', 'thirtyseconds'] name = 'Time Signature' metacommand = 0x58 length = 4 def get_numerator(self): return self.data[0] def set_numerator(self, val): self.data[0] = val numerator = property(get_numerator, set_numerator) def get_denominator(self): return 2 ** self.data[1] def set_denominator(self, val): self.data[1] = int(math.sqrt(val)) denominator = property(get_denominator, set_denominator) def get_metronome(self): return self.data[2] def set_metronome(self, val): self.data[2] = val metronome = property(get_metronome, set_metronome) def get_thirtyseconds(self): return self.data[3] def set_thirtyseconds(self, val): self.data[3] = val thirtyseconds = property(get_thirtyseconds, set_thirtyseconds) class KeySignatureEvent(MetaEvent): name = 'Key Signature' metacommand = 0x59 class SequencerSpecificEvent(MetaEvent): name = 'Sequencer Specific' metacommand = 0x7F