diff --git a/examples/example_1.py b/examples/example_1.py index 60acb87..d311ac6 100644 --- a/examples/example_1.py +++ b/examples/example_1.py @@ -1,3 +1,4 @@ +from __future__ import print_function import midi # Instantiate a MIDI Pattern (contains a list of tracks) pattern = midi.Pattern() @@ -15,6 +16,6 @@ eot = midi.EndOfTrackEvent(tick=1) track.append(eot) # Print out the pattern -print pattern +print(pattern) # Save the pattern to disk midi.write_midifile("example.mid", pattern) diff --git a/examples/example_2.py b/examples/example_2.py index ecc4a87..2306eda 100644 --- a/examples/example_2.py +++ b/examples/example_2.py @@ -1,3 +1,4 @@ +from __future__ import print_function import midi pattern = midi.read_midifile("example.mid") -print pattern +print(pattern) diff --git a/scripts/mididump.py b/scripts/mididump.py index 1497a3d..cca828b 100644 --- a/scripts/mididump.py +++ b/scripts/mididump.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +from __future__ import print_function + """ Print a description of a MIDI file. """ @@ -6,9 +8,9 @@ import sys if len(sys.argv) != 2: - print "Usage: {0} ".format(sys.argv[0]) + print("Usage: {0} ".format(sys.argv[0])) sys.exit(2) midifile = sys.argv[1] pattern = midi.read_midifile(midifile) -print repr(pattern) +print(repr(pattern)) diff --git a/scripts/mididumphw.py b/scripts/mididumphw.py index 42aef0b..a40cc69 100644 --- a/scripts/mididumphw.py +++ b/scripts/mididumphw.py @@ -2,8 +2,9 @@ """ Print a description of the available devices. """ +from __future__ import print_function import midi.sequencer as sequencer s = sequencer.SequencerHardware() -print s +print(s) diff --git a/scripts/midilisten.py b/scripts/midilisten.py index b86ee5c..2cb44ef 100644 --- a/scripts/midilisten.py +++ b/scripts/midilisten.py @@ -2,13 +2,15 @@ """ Attach to a MIDI device and print events to standard output. """ +from __future__ import print_function + import sys import time import midi import midi.sequencer as sequencer if len(sys.argv) != 3: - print "Usage: {0} ".format(sys.argv[0]) + print("Usage: {0} ".format(sys.argv[0])) exit(2) client = sys.argv[1] @@ -21,4 +23,4 @@ while True: event = seq.event_read() if event is not None: - print event + print(event) diff --git a/scripts/midiplay.py b/scripts/midiplay.py index 3fb6121..2169d79 100644 --- a/scripts/midiplay.py +++ b/scripts/midiplay.py @@ -2,13 +2,15 @@ """ Attach to a MIDI device and send the contents of a MIDI file to it. """ +from __future__ import print_function + import sys import time import midi import midi.sequencer as sequencer if len(sys.argv) != 4: - print "Usage: {0} ".format(sys.argv[0]) + print("Usage: {0} ".format(sys.argv[0])) exit(2) client = sys.argv[1] @@ -45,4 +47,4 @@ seq.drain() time.sleep(.5) -print 'The end?' +print('The end?') diff --git a/setup.py b/setup.py index bb5b591..556d36b 100755 --- a/setup.py +++ b/setup.py @@ -1,5 +1,6 @@ #!/usr/bin/env python +from __future__ import print_function import os from setuptools import setup, Extension import setuptools.command.install @@ -15,12 +16,14 @@ 'ext_modules':[], 'ext_package':'', 'scripts':['scripts/mididump.py', 'scripts/mididumphw.py', 'scripts/midiplay.py'], + 'install_requires':['future'], } # this kludge ensures we run the build_ext first before anything else # otherwise, we will be missing generated files during the copy class Install_Command_build_ext_first(setuptools.command.install.install): def run(self): + setuptools.command.install.install.do_egg_install(self) self.run_command("build_ext") return setuptools.command.install.install.run(self) @@ -62,7 +65,7 @@ def configure_platform(): setup_alsa(ns) pass else: - print "No sequencer available for '%s' platform." % platform + print("No sequencer available for '%s' platform." % platform) return ns if __name__ == "__main__": diff --git a/src/__init__.py b/src/__init__.py index 0092665..4c0b995 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,5 +1,6 @@ -from containers import * -from events import * from struct import unpack, pack -from util import * -from fileio import * + +from .containers import * +from .events import * +from .util import * +from .fileio import * diff --git a/src/constants.py b/src/constants.py index fb329b7..96c6816 100644 --- a/src/constants.py +++ b/src/constants.py @@ -3,13 +3,13 @@ ## OCTAVE_MAX_VALUE = 12 -OCTAVE_VALUES = range( OCTAVE_MAX_VALUE ) +OCTAVE_VALUES = list(range( OCTAVE_MAX_VALUE)) -NOTE_NAMES = ['C','Cs','D','Ds','E','F','Fs','G','Gs','A','As','B'] +NOTE_NAMES = ['C', 'Cs', 'D', 'Ds', 'E', 'F', 'Fs', 'G', 'Gs', 'A', 'As', 'B'] WHITE_KEYS = [0, 2, 4, 5, 7, 9, 11] BLACK_KEYS = [1, 3, 6, 8, 10] NOTE_PER_OCTAVE = len( NOTE_NAMES ) -NOTE_VALUES = range( OCTAVE_MAX_VALUE * NOTE_PER_OCTAVE ) +NOTE_VALUES = list(range( OCTAVE_MAX_VALUE * NOTE_PER_OCTAVE)) NOTE_NAME_MAP_FLAT = {} NOTE_VALUE_MAP_FLAT = [] NOTE_NAME_MAP_SHARP = {} diff --git a/src/containers.py b/src/containers.py index 006dbac..4a79b63 100644 --- a/src/containers.py +++ b/src/containers.py @@ -25,7 +25,7 @@ def __getitem__(self, item): if isinstance(item, slice): indices = item.indices(len(self)) return Pattern(resolution=self.resolution, format=self.format, - tracks=(super(Pattern, self).__getitem__(i) for i in xrange(*indices))) + tracks=(super(Pattern, self).__getitem__(i) for i in range(*indices))) else: return super(Pattern, self).__getitem__(item) @@ -58,7 +58,7 @@ def make_ticks_rel(self): def __getitem__(self, item): if isinstance(item, slice): indices = item.indices(len(self)) - return Track((super(Track, self).__getitem__(i) for i in xrange(*indices))) + return Track((super(Track, self).__getitem__(i) for i in range(*indices))) else: return super(Track, self).__getitem__(item) diff --git a/src/events.py b/src/events.py index 24f41eb..60260dd 100644 --- a/src/events.py +++ b/src/events.py @@ -1,4 +1,5 @@ import math +from future.utils import with_metaclass class EventRegistry(object): Events = {} @@ -15,21 +16,22 @@ def register_event(cls, event, bases): "Event %s already registered" % event.name cls.MetaEvents[event.metacommand] = event else: - raise ValueError, "Unknown bases class in event type: "+event.name + raise ValueError("Unknown bases class in event type: ", event.name) register_event = classmethod(register_event) -class AbstractEvent(object): +class RegisterEventMeta(type): + def __init__(cls, name, bases, dict): + if name not in ['AbstractEvent', 'Event', 'MetaEvent', 'NoteEvent', + 'MetaEventWithText']: + EventRegistry.register_event(cls, bases) + +class AbstractEvent(with_metaclass(RegisterEventMeta,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', - 'MetaEventWithText']: - EventRegistry.register_event(cls, bases) def __init__(self, **kw): if type(self.length) == int: @@ -41,10 +43,18 @@ def __init__(self, **kw): 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 __lt__(self, other): + if self.tick < other.tick: + return True + return self.data < other.data + + def __eq__(self, other): + return (self.__class__ is other.__class__ and + self.tick == other.tick and + self.data == other.data) + + def __ne__(self, other): + return not self.__eq__(other) def __baserepr__(self, keys=[]): keys = ['tick'] + keys + ['data'] @@ -75,10 +85,14 @@ def copy(self, **kw): _kw.update(kw) return self.__class__(**_kw) - def __cmp__(self, other): - if self.tick < other.tick: return -1 - elif self.tick > other.tick: return 1 - return 0 + def __lt__(self, other): + return (super(Event, self).__lt__(other) or + (super(Event, self).__eq__(other) and + self.channel < other.channel)) + + def __eq__(self, other): + return super(Event, self).__eq__(other) and \ + self.channel == other.channel def __repr__(self): return self.__baserepr__(['channel']) @@ -111,7 +125,6 @@ def is_event(cls, statusmsg): """ class NoteEvent(Event): - __slots__ = ['pitch', 'velocity'] length = 2 def get_pitch(self): @@ -152,7 +165,6 @@ def set_value(self, val): value = property(get_value, set_value) class ControlChangeEvent(Event): - __slots__ = ['control', 'value'] statusmsg = 0xB0 length = 2 name = 'Control Change' @@ -170,7 +182,6 @@ def get_value(self): value = property(get_value, set_value) class ProgramChangeEvent(Event): - __slots__ = ['value'] statusmsg = 0xC0 length = 1 name = 'Program Change' @@ -182,7 +193,6 @@ def get_value(self): value = property(get_value, set_value) class ChannelAfterTouchEvent(Event): - __slots__ = ['value'] statusmsg = 0xD0 length = 1 name = 'Channel After Touch' @@ -194,7 +204,6 @@ def get_value(self): value = property(get_value, set_value) class PitchWheelEvent(Event): - __slots__ = ['pitch'] statusmsg = 0xE0 length = 2 name = 'Pitch Wheel' @@ -302,7 +311,6 @@ class EndOfTrackEvent(MetaEvent): metacommand = 0x2F class SetTempoEvent(MetaEvent): - __slots__ = ['bpm', 'mpqn'] name = 'Set Tempo' metacommand = 0x51 length = 3 @@ -315,7 +323,7 @@ def get_bpm(self): def get_mpqn(self): assert(len(self.data) == 3) - vals = [self.data[x] << (16 - (8 * x)) for x in xrange(3)] + vals = [self.data[x] << (16 - (8 * x)) for x in range(3)] return sum(vals) def set_mpqn(self, val): self.data = [(val >> (16 - (8 * x)) & 0xFF) for x in range(3)] @@ -326,7 +334,6 @@ class SmpteOffsetEvent(MetaEvent): metacommand = 0x54 class TimeSignatureEvent(MetaEvent): - __slots__ = ['numerator', 'denominator', 'metronome', 'thirtyseconds'] name = 'Time Signature' metacommand = 0x58 length = 4 @@ -356,7 +363,6 @@ def set_thirtyseconds(self, val): thirtyseconds = property(get_thirtyseconds, set_thirtyseconds) class KeySignatureEvent(MetaEvent): - __slots__ = ['alternatives', 'minor'] name = 'Key Signature' metacommand = 0x59 length = 2 diff --git a/src/fileio.py b/src/fileio.py index c709819..4841ae5 100644 --- a/src/fileio.py +++ b/src/fileio.py @@ -1,10 +1,11 @@ from warnings import * -from containers import * -from events import * from struct import unpack, pack -from constants import * -from util import * + +from .containers import * +from .events import * +from .constants import * +from .util import * class FileReader(object): def read(self, midifile): @@ -16,8 +17,8 @@ def read(self, midifile): def parse_file_header(self, midifile): # First four bytes are MIDI header magic = midifile.read(4) - if magic != 'MThd': - raise TypeError, "Bad header in MIDI file." + if magic != b'MThd': + raise TypeError("Bad header in MIDI file.") # next four bytes are header size # next two bytes specify the format version # next two bytes specify the number of tracks @@ -37,8 +38,8 @@ def parse_file_header(self, midifile): def parse_track_header(self, midifile): # First four bytes are Track header magic = midifile.read(4) - if magic != 'MTrk': - raise TypeError, "Bad track header in MIDI file: " + magic + if magic != b'MTrk': + raise TypeError("Bad track header in MIDI file: " + magic) # next four bytes are track size trksz = unpack(">L", midifile.read(4))[0] return trksz @@ -46,7 +47,7 @@ def parse_track_header(self, midifile): def parse_track(self, midifile, track): self.RunningStatus = None trksz = self.parse_track_header(midifile) - trackdata = iter(midifile.read(trksz)) + trackdata = iter(bytearray(midifile.read(trksz))) while True: try: event = self.parse_midi_event(trackdata) @@ -58,23 +59,23 @@ def parse_midi_event(self, trackdata): # first datum is varlen representing delta-time tick = read_varlen(trackdata) # next byte is status message - stsmsg = ord(trackdata.next()) + stsmsg = next(trackdata) # is the event a MetaEvent? if MetaEvent.is_event(stsmsg): - cmd = ord(trackdata.next()) + cmd = next(trackdata) if cmd not in EventRegistry.MetaEvents: - warn("Unknown Meta MIDI Event: " + `cmd`, Warning) + warn("Unknown Meta MIDI Event: " + repr(cmd), Warning) cls = UnknownMetaEvent else: cls = EventRegistry.MetaEvents[cmd] datalen = read_varlen(trackdata) - data = [ord(trackdata.next()) for x in range(datalen)] + data = [next(trackdata) for x in range(datalen)] return cls(tick=tick, data=data, metacommand=cmd) # is this event a Sysex Event? elif SysexEvent.is_event(stsmsg): data = [] while True: - datum = ord(trackdata.next()) + datum = next(trackdata) if datum == 0xF7: break data.append(datum) @@ -89,15 +90,15 @@ def parse_midi_event(self, trackdata): cls = EventRegistry.Events[key] channel = self.RunningStatus & 0x0F data.append(stsmsg) - data += [ord(trackdata.next()) for x in range(cls.length - 1)] + data += [next(trackdata) for x in range(cls.length - 1)] return cls(tick=tick, channel=channel, data=data) else: self.RunningStatus = stsmsg cls = EventRegistry.Events[key] channel = self.RunningStatus & 0x0F - data = [ord(trackdata.next()) for x in range(cls.length)] + data = [next(trackdata) for x in range(cls.length)] return cls(tick=tick, channel=channel, data=data) - raise Warning, "Unknown MIDI Event: " + `stsmsg` + raise Warning("Unknown MIDI Event: " + repr(stsmsg)) class FileWriter(object): def write(self, midifile, pattern): @@ -111,10 +112,10 @@ def write_file_header(self, midifile, pattern): pattern.format, len(pattern), pattern.resolution) - midifile.write('MThd%s' % packdata) + midifile.write(b'MThd' + packdata) def write_track(self, midifile, track): - buf = '' + buf = b'' self.RunningStatus = None for event in track: buf += self.encode_midi_event(event) @@ -122,41 +123,41 @@ def write_track(self, midifile, track): midifile.write(buf) def encode_track_header(self, trklen): - return 'MTrk%s' % pack(">L", trklen) + return b'MTrk' + pack(">L", trklen) def encode_midi_event(self, event): - ret = '' + ret = bytearray() ret += write_varlen(event.tick) # is the event a MetaEvent? if isinstance(event, MetaEvent): - ret += chr(event.statusmsg) + chr(event.metacommand) + ret += bytearray(event.statusmsg + event.metacommand) ret += write_varlen(len(event.data)) - ret += str.join('', map(chr, event.data)) + ret += bytearray(event.data) # is this event a Sysex Event? elif isinstance(event, SysexEvent): - ret += chr(0xF0) - ret += str.join('', map(chr, event.data)) - ret += chr(0xF7) + ret.append(0xF0) + ret += bytearray(event.data) + ret.append(0xF7) # not a Meta MIDI event or a Sysex event, must be a general message elif isinstance(event, Event): if not self.RunningStatus or \ self.RunningStatus.statusmsg != event.statusmsg or \ self.RunningStatus.channel != event.channel: self.RunningStatus = event - ret += chr(event.statusmsg | event.channel) - ret += str.join('', map(chr, event.data)) + ret.append(event.statusmsg | event.channel) + ret += bytearray(event.data) else: - raise ValueError, "Unknown MIDI Event: " + str(event) + raise ValueError("Unknown MIDI Event: " + str(event)) return ret def write_midifile(midifile, pattern): - if type(midifile) in (str, unicode): + if not hasattr(midifile, "write"): midifile = open(midifile, 'wb') writer = FileWriter() return writer.write(midifile, pattern) def read_midifile(midifile): - if type(midifile) in (str, unicode): + if not hasattr(midifile, "read"): midifile = open(midifile, 'rb') reader = FileReader() return reader.read(midifile) diff --git a/src/sequencer.py b/src/sequencer.py index c7842d1..2f51e81 100644 --- a/src/sequencer.py +++ b/src/sequencer.py @@ -50,9 +50,9 @@ def __init__(self, stream, window): self.ttpts.append(stream.endoftrack.tick) self.ttpts = iter(self.ttpts) # Setup next tempo timepoint - self.ttp = self.ttpts.next() + self.ttp = next(self.ttpts) self.tempomap = iter(self.stream.tempomap) - self.tempo = self.tempomap.next() + self.tempo = next(self.tempomap) self.endoftrack = False def __iter__(self): @@ -67,7 +67,7 @@ def __next_edge(self): # We're past the tempo-marker. oldttp = self.ttp try: - self.ttp = self.ttpts.next() + self.ttp = next(self.ttpts) except StopIteration: # End of Track! self.window_edge = self.ttp @@ -77,11 +77,11 @@ def __next_edge(self): # account the tempo change. msused = (oldttp - lastedge) * self.tempo.mpt msleft = self.window_length - msused - self.tempo = self.tempomap.next() + self.tempo = next(self.tempomap) ticksleft = msleft / self.tempo.mpt self.window_edge = ticksleft + self.tempo.tick - def next(self): + def __next__(self): ret = [] self.__next_edge() if self.leftover: diff --git a/src/sequencer_alsa/sequencer.py b/src/sequencer_alsa/sequencer.py index 9a9c3ef..cf979e7 100644 --- a/src/sequencer_alsa/sequencer.py +++ b/src/sequencer_alsa/sequencer.py @@ -1,3 +1,4 @@ +from __future__ import print_function import select import sequencer_alsa as S import midi @@ -204,7 +205,7 @@ def output_pending(self): ## EVENT HANDLERS ## def event_write(self, event, direct=False, relative=False, tick=False): - #print event.__class__, event + #print(event.__class__, event) ## Event Filter if isinstance(event, midi.EndOfTrackEvent): return @@ -269,7 +270,7 @@ def event_write(self, event, direct=False, relative=False, tick=False): seqev.data.control.value = event.pitch ## Unknown else: - print "Warning :: Unknown event type: %s" % event + print("Warning :: Unknown event type: %s" % event) return None err = S.snd_seq_event_output(self.client, seqev) diff --git a/src/sequencer_osx/test.py b/src/sequencer_osx/test.py index 97873e0..e39c9b5 100644 --- a/src/sequencer_osx/test.py +++ b/src/sequencer_osx/test.py @@ -1,9 +1,11 @@ +from __future__ import print_function import sequencer_osx -print "MIDIGetNumberOfDevices:", sequencer_osx._MIDIGetNumberOfDevices() + +print("MIDIGetNumberOfDevices:", sequencer_osx._MIDIGetNumberOfDevices()) client = sequencer_osx._MIDIClientCreate("python") endpoint = sequencer_osx._MIDISourceCreate(client, "python-source") port = sequencer_osx._MIDIOutputPortCreate(client, "python-port") sequencer_osx._MIDIPortConnectSource(port, endpoint) -print client, endpoint, endpoint +print(client, endpoint, endpoint) raw_input() #sequencer_osx._MIDIClientDispose(handle) diff --git a/src/util.py b/src/util.py index 688730b..8f4f581 100644 --- a/src/util.py +++ b/src/util.py @@ -3,7 +3,7 @@ def read_varlen(data): NEXTBYTE = 1 value = 0 while NEXTBYTE: - chr = ord(data.next()) + chr = next(data) # is the hi-bit set? if not (chr & 0x80): # no next BYTE @@ -17,22 +17,9 @@ def read_varlen(data): return value def write_varlen(value): - chr1 = chr(value & 0x7F) - value >>= 7 - if value: - chr2 = chr((value & 0x7F) | 0x80) + result = bytearray() + while value > 0x7F: + result.append((value & 0x7F) | 0x80) value >>= 7 - if value: - chr3 = chr((value & 0x7F) | 0x80) - value >>= 7 - if value: - chr4 = chr((value & 0x7F) | 0x80) - res = chr4 + chr3 + chr2 + chr1 - else: - res = chr3 + chr2 + chr1 - else: - res = chr2 + chr1 - else: - res = chr1 - return res - + result.append(value) + return result