From 4da0384820cf8d6ed08772a8333be7ae937c7ede Mon Sep 17 00:00:00 2001 From: user Date: Sat, 25 Apr 2015 18:37:30 -0700 Subject: [PATCH 01/15] 2to3 on this stuff --- examples/example_1.py | 2 +- examples/example_2.py | 2 +- scripts/mididump.py | 4 +- scripts/mididumphw.py | 2 +- scripts/midilisten.py | 4 +- scripts/midiplay.py | 4 +- setup.py | 24 +++++------ src/__init__.py | 8 ++-- src/constants.py | 6 +-- src/containers.py | 8 ++-- src/events.py | 6 +-- src/fileio.py | 40 +++++++++---------- src/sequencer.py | 10 ++--- src/sequencer_alsa/__init__.py | 2 +- src/sequencer_alsa/sequencer.py | 38 +++++++++--------- .../cfstring-typemaps-example-1.0/setup.py | 4 +- src/sequencer_osx/sequencer_osx.py | 18 ++++----- src/sequencer_osx/test.py | 6 +-- src/util.py | 2 +- test.py | 8 ++-- tests/tests.py | 2 +- 21 files changed, 100 insertions(+), 100 deletions(-) diff --git a/examples/example_1.py b/examples/example_1.py index 60acb87..52078db 100644 --- a/examples/example_1.py +++ b/examples/example_1.py @@ -15,6 +15,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..1d09fb7 100644 --- a/examples/example_2.py +++ b/examples/example_2.py @@ -1,3 +1,3 @@ import midi pattern = midi.read_midifile("example.mid") -print pattern +print(pattern) diff --git a/scripts/mididump.py b/scripts/mididump.py index 1497a3d..fff59ff 100644 --- a/scripts/mididump.py +++ b/scripts/mididump.py @@ -6,9 +6,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..640f754 100644 --- a/scripts/mididumphw.py +++ b/scripts/mididumphw.py @@ -6,4 +6,4 @@ s = sequencer.SequencerHardware() -print s +print(s) diff --git a/scripts/midilisten.py b/scripts/midilisten.py index b86ee5c..03eba64 100644 --- a/scripts/midilisten.py +++ b/scripts/midilisten.py @@ -8,7 +8,7 @@ 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 +21,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 fdddcce..51221eb 100644 --- a/scripts/midiplay.py +++ b/scripts/midiplay.py @@ -8,7 +8,7 @@ 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] @@ -43,4 +43,4 @@ time.sleep(.5) time.sleep(30) -print 'The end?' +print('The end?') diff --git a/setup.py b/setup.py index a7e5b4b..cbba66f 100755 --- a/setup.py +++ b/setup.py @@ -3,22 +3,22 @@ from distutils.core import setup, Extension __base__ = { - 'name':'midi', - 'version':'v0.2.3', - 'description':'Python MIDI API', - 'author':'giles hall', - 'author_email':'ghall@csh.rit.edu', - 'package_dir':{'midi':'src'}, - 'py_modules':['midi.containers', 'midi.__init__', 'midi.events', 'midi.util', 'midi.fileio', 'midi.constants'], - 'ext_modules':[], - 'ext_package':'', - 'scripts':['scripts/mididump.py', 'scripts/mididumphw.py', 'scripts/midiplay.py'], + 'name': 'midi', + 'version': 'v0.2.3', + 'description': 'Python MIDI API', + 'author': 'giles hall', + 'author_email': 'ghall@csh.rit.edu', + 'package_dir': {'midi':'src'}, + 'py_modules': ['midi.containers', 'midi.__init__', 'midi.events', 'midi.util', 'midi.fileio', 'midi.constants'], + 'ext_modules': [], + 'ext_package': '', + 'scripts': ['scripts/mididump.py', 'scripts/mididumphw.py', 'scripts/midiplay.py'], } def setup_alsa(ns): srclist = ["src/sequencer_alsa/sequencer_alsa.i"] extns = { - 'libraries':['asound'], + 'libraries': ['asound'], #'extra_compile_args':['-DSWIGRUNTIME_DEBUG'] } ext = Extension('_sequencer_alsa', srclist, **extns) @@ -38,7 +38,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..3ebfa3d 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,5 +1,5 @@ -from containers import * -from events import * +from .containers import * +from .events import * from struct import unpack, pack -from util import * -from fileio 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..9bffcdd 100644 --- a/src/containers.py +++ b/src/containers.py @@ -25,14 +25,14 @@ 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) def __getslice__(self, i, j): # The deprecated __getslice__ is still called when subclassing built-in types # for calls of the form List[i:j] - return self.__getitem__(slice(i,j)) + return self.__getitem__(slice(i, j)) class Track(list): def __init__(self, events=[], tick_relative=True): @@ -58,14 +58,14 @@ 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) def __getslice__(self, i, j): # The deprecated __getslice__ is still called when subclassing built-in types # for calls of the form List[i:j] - return self.__getitem__(slice(i,j)) + return self.__getitem__(slice(i, j)) def __repr__(self): return "midi.Track(\\\n %s)" % (pformat(list(self)).replace('\n', '\n '), ) diff --git a/src/events.py b/src/events.py index a62fbad..67f3290 100644 --- a/src/events.py +++ b/src/events.py @@ -14,7 +14,7 @@ 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) @@ -31,7 +31,7 @@ def __init__(cls, name, bases, dict): EventRegistry.register_event(cls, bases) def __init__(self, **kw): - if type(self.length) == int: + if isinstance(self.length, int): defdata = [0] * self.length else: defdata = [] @@ -304,7 +304,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)] diff --git a/src/fileio.py b/src/fileio.py index 2c1d793..8bf58ee 100644 --- a/src/fileio.py +++ b/src/fileio.py @@ -1,8 +1,8 @@ -from containers import * -from events import * +from .containers import * +from .events import * from struct import unpack, pack -from constants import * -from util import * +from .constants import * +from .util import * class FileReader(object): def read(self, midifile): @@ -15,7 +15,7 @@ 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." + 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 @@ -36,7 +36,7 @@ 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 + raise TypeError("Bad track header in MIDI file: " + magic) # next four bytes are track size trksz = unpack(">L", midifile.read(4))[0] return trksz @@ -56,21 +56,21 @@ 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 = ord(next(trackdata)) # is the event a MetaEvent? if MetaEvent.is_event(stsmsg): - cmd = ord(trackdata.next()) + cmd = ord(next(trackdata)) if cmd not in EventRegistry.MetaEvents: - raise Warning, "Unknown Meta MIDI Event: " + `cmd` + raise Warning("Unknown Meta MIDI Event: " + repr(cmd)) cls = EventRegistry.MetaEvents[cmd] datalen = read_varlen(trackdata) - data = [ord(trackdata.next()) for x in range(datalen)] + data = [ord(next(trackdata)) for x in range(datalen)] return cls(tick=tick, data=data) # is this event a Sysex Event? elif SysexEvent.is_event(stsmsg): data = [] while True: - datum = ord(trackdata.next()) + datum = ord(next(trackdata)) if datum == 0xF7: break data.append(datum) @@ -85,15 +85,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 += [ord(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 = [ord(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): @@ -127,11 +127,11 @@ def encode_midi_event(self, event): if isinstance(event, MetaEvent): ret += chr(event.statusmsg) + chr(event.metacommand) ret += write_varlen(len(event.data)) - ret += str.join('', map(chr, event.data)) + ret += str.join('', list(map(chr, event.data))) # is this event a Sysex Event? elif isinstance(event, SysexEvent): ret += chr(0xF0) - ret += str.join('', map(chr, event.data)) + ret += str.join('', list(map(chr, event.data))) ret += chr(0xF7) # not a Meta MIDI event or a Sysex event, must be a general message elif isinstance(event, Event): @@ -140,19 +140,19 @@ def encode_midi_event(self, event): self.RunningStatus.channel != event.channel: self.RunningStatus = event ret += chr(event.statusmsg | event.channel) - ret += str.join('', map(chr, event.data)) + ret += str.join('', list(map(chr, 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 type(midifile) in (str, str): midifile = open(midifile, 'wb') writer = FileWriter() return writer.write(midifile, pattern) def read_midifile(midifile): - if type(midifile) in (str, unicode): + if type(midifile) in (str, str): 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/__init__.py b/src/sequencer_alsa/__init__.py index 55dea93..fd22b26 100644 --- a/src/sequencer_alsa/__init__.py +++ b/src/sequencer_alsa/__init__.py @@ -1 +1 @@ -from sequencer import * +from .sequencer import * diff --git a/src/sequencer_alsa/sequencer.py b/src/sequencer_alsa/sequencer.py index 792d5c3..4f7ade2 100644 --- a/src/sequencer_alsa/sequencer.py +++ b/src/sequencer_alsa/sequencer.py @@ -1,8 +1,8 @@ import select -import sequencer_alsa as S +from . import sequencer_alsa as S import midi -__SWIG_NS_SET__ = set(['__class__', '__del__', '__delattr__', '__dict__', '__doc__', '__getattr__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__swig_getmethods__', '__swig_setmethods__', '__weakref__', 'this', 'thisown']) +__SWIG_NS_SET__ = {'__class__', '__del__', '__delattr__', '__dict__', '__doc__', '__getattr__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__swig_getmethods__', '__swig_setmethods__', '__weakref__', 'this', 'thisown'} def stringify(name, obj, indent=0): retstr = '' @@ -21,16 +21,16 @@ def stringify(name, obj, indent=0): class Sequencer(object): __ARGUMENTS__ = { - 'alsa_sequencer_name':'__sequencer__', - 'alsa_sequencer_stream':S.SND_SEQ_OPEN_DUPLEX, - 'alsa_sequencer_mode':S.SND_SEQ_NONBLOCK, - 'alsa_sequencer_type':'default', - 'alsa_port_name':'__port__', - 'alsa_port_caps':S.SND_SEQ_PORT_CAP_READ, - 'alsa_port_type':S.SND_SEQ_PORT_TYPE_MIDI_GENERIC, - 'alsa_queue_name':'__queue__', - 'sequencer_tempo':120, - 'sequencer_resolution':1000, + 'alsa_sequencer_name': '__sequencer__', + 'alsa_sequencer_stream': S.SND_SEQ_OPEN_DUPLEX, + 'alsa_sequencer_mode': S.SND_SEQ_NONBLOCK, + 'alsa_sequencer_type': 'default', + 'alsa_port_name': '__port__', + 'alsa_port_caps': S.SND_SEQ_PORT_CAP_READ, + 'alsa_port_type': S.SND_SEQ_PORT_TYPE_MIDI_GENERIC, + 'alsa_queue_name': '__queue__', + 'sequencer_tempo': 120, + 'sequencer_resolution': 1000, } DefaultArguments = {} @@ -71,7 +71,7 @@ def get_nonblock(self): def _error(self, errcode): strerr = S.snd_strerror(errcode) msg = "ALSAError[%d]: %s" % (errcode, strerr) - raise RuntimeError, msg + raise RuntimeError(msg) def _init_handle(self): ret = S.open_client(self.alsa_sequencer_name, @@ -268,7 +268,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) @@ -323,7 +323,7 @@ def add_port(self, port, name, caps): self._ports[name] = port def __iter__(self): - return self._ports.itervalues() + return iter(self._ports.values()) def __len__(self): return len(self._ports) @@ -359,7 +359,7 @@ def init(self): self._query_clients() def __iter__(self): - return self._clients.itervalues() + return iter(self._clients.values()) def __len__(self): return len(self._clients) @@ -403,9 +403,9 @@ def _query_clients(self): class SequencerRead(Sequencer): DefaultArguments = { - 'sequencer_name':'__SequencerRead__', - 'sequencer_stream':not S.SND_SEQ_NONBLOCK, - 'alsa_port_caps':S.SND_SEQ_PORT_CAP_WRITE | S.SND_SEQ_PORT_CAP_SUBS_WRITE, + 'sequencer_name': '__SequencerRead__', + 'sequencer_stream': not S.SND_SEQ_NONBLOCK, + 'alsa_port_caps': S.SND_SEQ_PORT_CAP_WRITE | S.SND_SEQ_PORT_CAP_SUBS_WRITE, } def subscribe_port(self, client, port): diff --git a/src/sequencer_osx/cfstring-typemaps-example-1.0/setup.py b/src/sequencer_osx/cfstring-typemaps-example-1.0/setup.py index 945841e..c907fa4 100644 --- a/src/sequencer_osx/cfstring-typemaps-example-1.0/setup.py +++ b/src/sequencer_osx/cfstring-typemaps-example-1.0/setup.py @@ -4,5 +4,5 @@ setup(name = "cfstring-typemaps-example", version = "1.0", ext_modules = [Extension("_example", - ["example.i","example.c"], - extra_link_args=['-framework','CoreFoundation'])]) + ["example.i", "example.c"], + extra_link_args=['-framework', 'CoreFoundation'])]) diff --git a/src/sequencer_osx/sequencer_osx.py b/src/sequencer_osx/sequencer_osx.py index bb8228b..cde0408 100644 --- a/src/sequencer_osx/sequencer_osx.py +++ b/src/sequencer_osx/sequencer_osx.py @@ -17,21 +17,21 @@ def _swig_setattr_nondynamic(self,class_type,name,value,static=1): if type(value).__name__ == 'PySwigObject': self.__dict__[name] = value return - method = class_type.__swig_setmethods__.get(name,None) - if method: return method(self,value) - if (not static) or hasattr(self,name): + method = class_type.__swig_setmethods__.get(name, None) + if method: return method(self, value) + if (not static) or hasattr(self, name): self.__dict__[name] = value else: raise AttributeError("You cannot add attributes to %s" % self) -def _swig_setattr(self,class_type,name,value): - return _swig_setattr_nondynamic(self,class_type,name,value,0) +def _swig_setattr(self, class_type, name, value): + return _swig_setattr_nondynamic(self, class_type, name, value, 0) -def _swig_getattr(self,class_type,name): +def _swig_getattr(self, class_type, name): if (name == "thisown"): return self.this.own() - method = class_type.__swig_getmethods__.get(name,None) + method = class_type.__swig_getmethods__.get(name, None) if method: return method(self) - raise AttributeError,name + raise AttributeError(name) def _swig_repr(self): try: strthis = "proxy of " + self.this.__repr__() @@ -40,7 +40,7 @@ def _swig_repr(self): import types try: - _object = types.ObjectType + _object = object _newclass = 1 except AttributeError: class _object : pass diff --git a/src/sequencer_osx/test.py b/src/sequencer_osx/test.py index 97873e0..9ac90ae 100644 --- a/src/sequencer_osx/test.py +++ b/src/sequencer_osx/test.py @@ -1,9 +1,9 @@ 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 -raw_input() +print(client, endpoint, endpoint) +input() #sequencer_osx._MIDIClientDispose(handle) diff --git a/src/util.py b/src/util.py index 688730b..ee50fd5 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 = ord(next(data)) # is the hi-bit set? if not (chr & 0x80): # no next BYTE diff --git a/test.py b/test.py index a0719dc..b3d5db4 100644 --- a/test.py +++ b/test.py @@ -2,12 +2,12 @@ import pprint x = midi.FileReader() p = midi.read('a.mid') -print p -raw_input() +print(p) +input() midi.write('aa.mid', p) p = midi.read('aa.mid') -print p -raw_input() +print(p) +input() #midi.write('aaa.mid', p) #p = midi.read('aaa.mid') diff --git a/tests/tests.py b/tests/tests.py index 3e49b7c..97add8f 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -5,7 +5,7 @@ class TestMIDI(unittest.TestCase): def test_varlen(self): maxval = 0x0FFFFFFF - for inval in xrange(0, maxval, maxval / 1000): + for inval in range(0, maxval, maxval / 1000): datum = midi.write_varlen(inval) outval = midi.read_varlen(iter(datum)) self.assertEqual(inval, outval) From 6dd67d28fe5ffe3bc3c56be08ff00b16cd0d4fb7 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 25 Apr 2015 18:48:02 -0700 Subject: [PATCH 02/15] Fixing specifics bytes not str, metaclasses, slots suck etc --- src/events.py | 24 +++++++----------------- src/fileio.py | 20 ++++++++++---------- src/util.py | 2 +- 3 files changed, 18 insertions(+), 28 deletions(-) diff --git a/src/events.py b/src/events.py index 67f3290..c049afe 100644 --- a/src/events.py +++ b/src/events.py @@ -17,19 +17,18 @@ def register_event(cls, event, bases): raise ValueError("Unknown bases class in event type: "+event.name) register_event = classmethod(register_event) +class AutoRegister(type): + def __init__(cls, name, bases, dict): + if name not in {'AbstractEvent', 'Event', 'MetaEvent', 'NoteEvent', + 'MetaEventWithText'}: + EventRegistry.register_event(cls, bases) -class AbstractEvent(object): - __slots__ = ['tick', 'data'] + +class AbstractEvent(object,metaclass=AutoRegister): 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 isinstance(self.length, int): defdata = [0] * self.length @@ -60,7 +59,6 @@ def __repr__(self): class Event(AbstractEvent): - __slots__ = ['channel'] name = 'Event' def __init__(self, **kw): @@ -110,7 +108,6 @@ def is_event(cls, statusmsg): """ class NoteEvent(Event): - __slots__ = ['pitch', 'velocity'] length = 2 def get_pitch(self): @@ -151,7 +148,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' @@ -169,7 +165,6 @@ def get_value(self): value = property(get_value, set_value) class ProgramChangeEvent(Event): - __slots__ = ['value'] statusmsg = 0xC0 length = 1 name = 'Program Change' @@ -181,7 +176,6 @@ def get_value(self): value = property(get_value, set_value) class ChannelAfterTouchEvent(Event): - __slots__ = ['value'] statusmsg = 0xD0 length = 1 name = 'Channel After Touch' @@ -193,7 +187,6 @@ def get_value(self): value = property(get_value, set_value) class PitchWheelEvent(Event): - __slots__ = ['pitch'] statusmsg = 0xE0 length = 2 name = 'Pitch Wheel' @@ -291,7 +284,6 @@ class EndOfTrackEvent(MetaEvent): metacommand = 0x2F class SetTempoEvent(MetaEvent): - __slots__ = ['bpm', 'mpqn'] name = 'Set Tempo' metacommand = 0x51 length = 3 @@ -315,7 +307,6 @@ class SmpteOffsetEvent(MetaEvent): metacommand = 0x54 class TimeSignatureEvent(MetaEvent): - __slots__ = ['numerator', 'denominator', 'metronome', 'thirtyseconds'] name = 'Time Signature' metacommand = 0x58 length = 4 @@ -345,7 +336,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 8bf58ee..cecacc9 100644 --- a/src/fileio.py +++ b/src/fileio.py @@ -14,8 +14,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.",magic) # next four bytes are header size # next two bytes specify the format version # next two bytes specify the number of tracks @@ -35,8 +35,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 @@ -56,21 +56,21 @@ def parse_midi_event(self, trackdata): # first datum is varlen representing delta-time tick = read_varlen(trackdata) # next byte is status message - stsmsg = ord(next(trackdata)) + stsmsg = next(trackdata) # is the event a MetaEvent? if MetaEvent.is_event(stsmsg): - cmd = ord(next(trackdata)) + cmd = next(trackdata) if cmd not in EventRegistry.MetaEvents: raise Warning("Unknown Meta MIDI Event: " + repr(cmd)) cls = EventRegistry.MetaEvents[cmd] datalen = read_varlen(trackdata) - data = [ord(next(trackdata)) for x in range(datalen)] + data = [next(trackdata) for x in range(datalen)] return cls(tick=tick, data=data) # is this event a Sysex Event? elif SysexEvent.is_event(stsmsg): data = [] while True: - datum = ord(next(trackdata)) + datum = next(trackdata) if datum == 0xF7: break data.append(datum) @@ -85,13 +85,13 @@ def parse_midi_event(self, trackdata): cls = EventRegistry.Events[key] channel = self.RunningStatus & 0x0F data.append(stsmsg) - data += [ord(next(trackdata)) 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(next(trackdata)) 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: " + repr(stsmsg)) diff --git a/src/util.py b/src/util.py index ee50fd5..25bef1e 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(next(data)) + chr = next(data) # is the hi-bit set? if not (chr & 0x80): # no next BYTE From 608c7dc37bb96c193b09b7caac79a68fec3ab891 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 25 Apr 2015 19:31:14 -0700 Subject: [PATCH 03/15] Example piping to timidity --- examples/example_1.py | 20 ++++++++++-- examples/timidity.py | 22 +++++++++++++ src/fileio.py | 72 +++++++++++++++++++++++++++---------------- src/util.py | 16 +++++----- 4 files changed, 93 insertions(+), 37 deletions(-) create mode 100644 examples/timidity.py diff --git a/examples/example_1.py b/examples/example_1.py index 52078db..e640f66 100644 --- a/examples/example_1.py +++ b/examples/example_1.py @@ -5,11 +5,27 @@ track = midi.Track() # Append the track to the pattern pattern.append(track) +track.append(midi.TimeSignatureEvent(tick=0,numerator=4,denominator=4, + metronome=24, + thirtyseconds=8)) +track.append(midi.KeySignatureEvent(tick=0)) +track = midi.Track() +#reverb +track.append(midi.ControlChangeEvent(tick=0,data=[91,58])) +track.append(midi.ControlChangeEvent(tick=0,data=[10,69])) +#msb +track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[0,0])) +#lsb +track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[0,0])) +track.append(midi.ProgramChangeEvent(tick=0,channel=0,data=[24])) # Instantiate a MIDI note on event, append it to the track -on = midi.NoteOnEvent(tick=0, velocity=20, pitch=midi.G_3) +on = midi.NoteOnEvent(tick=0, channel=0, data=[43,20]) +on = midi.NoteOnEvent(tick=10, channel=0, data=[43,20]) +on = midi.NoteOnEvent(tick=100, channel=0, data=[43,20]) +on = midi.NoteOnEvent(tick=200, channel=0, data=[43,20]) track.append(on) # Instantiate a MIDI note off event, append it to the track -off = midi.NoteOffEvent(tick=100, pitch=midi.G_3) +off = midi.NoteOffEvent(tick=1000, data=[43,0]) track.append(off) # Add the end of track event, append it to the track eot = midi.EndOfTrackEvent(tick=1) diff --git a/examples/timidity.py b/examples/timidity.py new file mode 100644 index 0000000..1edc6f2 --- /dev/null +++ b/examples/timidity.py @@ -0,0 +1,22 @@ +#!/usr/bin/env pypy +import midi +import subprocess as s + +def main(): + p = s.Popen(["timidity","-"],stdin=s.PIPE) + writer = midi.FileWriter(p.stdin) + writer.write_file_header(midi.Pattern()) + writer.write_track_header(1) + + def song(): + for i in range(200): + yield midi.NoteOnEvent(tick=i*100, velocity=200, pitch=midi.G_3) + for event in song(): + writer.write_midi_event(event) + p.stdin.close() + p.wait() + + + +if __name__ == '__main__': + main() diff --git a/src/fileio.py b/src/fileio.py index cecacc9..1994b4d 100644 --- a/src/fileio.py +++ b/src/fileio.py @@ -96,63 +96,81 @@ def parse_midi_event(self, trackdata): raise Warning("Unknown MIDI Event: " + repr(stsmsg)) class FileWriter(object): - def write(self, midifile, pattern): - self.write_file_header(midifile, pattern) + RunningStatus = None + def __init__(self,file): + self.file = file + def write(self, pattern): + self.write_file_header(pattern) for track in pattern: - self.write_track(midifile, track) + self.write_track(track) - def write_file_header(self, midifile, pattern): + def write_file_header(self, pattern): # First four bytes are MIDI header packdata = pack(">LHHH", 6, pattern.format, len(pattern), pattern.resolution) - midifile.write('MThd%s' % packdata) + self.file.write(b'MThd' + packdata) - def write_track(self, midifile, track): - buf = '' - self.RunningStatus = None + + def write_track(self, track): + hlen = len(self.encode_track_header(0)) + buf = bytearray(b'0'*hlen) for event in track: - buf += self.encode_midi_event(event) - buf = self.encode_track_header(len(buf)) + buf - midifile.write(buf) + buf.extend(self.encode_midi_event(event)) + buf[:hlen] = self.encode_track_header(len(buf)-hlen) + self.file.write(buf) + def write_track_header(self,track=None): + trklen = 1 if track is None else track if isinstance(track,int) else len(track) + self.file.write(self.encode_track_header(trklen)) + def encode_track_header(self, trklen): - return 'MTrk%s' % pack(">L", trklen) + return b'MTrk' + pack(">L", trklen) + def write_midi_event(self, event): + # be sure to write the track and pattern headers first + # can stream to timidity or fluidsynth this way + self.file.write(self.encode_midi_event(event)) + def encode_midi_event(self, event): - ret = '' - ret += write_varlen(event.tick) + ret = bytearray() + #assert hasattr(event,'tick'), event + assert isinstance(event.tick,int), event.tick + ret.extend(write_varlen(event.tick)) # is the event a MetaEvent? if isinstance(event, MetaEvent): - ret += chr(event.statusmsg) + chr(event.metacommand) - ret += write_varlen(len(event.data)) - ret += str.join('', list(map(chr, event.data))) + ret.append(event.statusmsg) + ret.append(event.metacommand) + ret.extend(write_varlen(len(event.data))) + ret.extend(event.data) # is this event a Sysex Event? elif isinstance(event, SysexEvent): - ret += chr(0xF0) - ret += str.join('', list(map(chr, event.data))) - ret += chr(0xF7) + ret.append(0xF0) + ret.extend(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('', list(map(chr, event.data))) + ret.append(event.statusmsg | event.channel) + ret.extend(event.data) else: raise ValueError("Unknown MIDI Event: " + str(event)) return ret def write_midifile(midifile, pattern): if type(midifile) in (str, str): - midifile = open(midifile, 'wb') - writer = FileWriter() - return writer.write(midifile, pattern) + with open(midifile, 'wb') as out: + return write_midifile(out,pattern) + writer = FileWriter(midifile) + return writer.write(pattern) def read_midifile(midifile): - if type(midifile) in (str, str): - midifile = open(midifile, 'rb') + if type(midifile) in (str, bytes): + with open(midifile, 'rb') as inp: + return read_midifile(inp) reader = FileReader() return reader.read(midifile) diff --git a/src/util.py b/src/util.py index 25bef1e..33898b0 100644 --- a/src/util.py +++ b/src/util.py @@ -17,22 +17,22 @@ def read_varlen(data): return value def write_varlen(value): - chr1 = chr(value & 0x7F) + b1 = value & 0x7F value >>= 7 if value: - chr2 = chr((value & 0x7F) | 0x80) + b2 = (value & 0x7F) | 0x80 value >>= 7 if value: - chr3 = chr((value & 0x7F) | 0x80) + b3 = (value & 0x7F) | 0x80 value >>= 7 if value: - chr4 = chr((value & 0x7F) | 0x80) - res = chr4 + chr3 + chr2 + chr1 + b4 = (value & 0x7F) | 0x80 + res = bytes((b4,b3,b2,b1)) else: - res = chr3 + chr2 + chr1 + res = bytes((b3,b2,b1)) else: - res = chr2 + chr1 + res = bytes((b2,b1)) else: - res = chr1 + res = bytes((b1,)) return res From 0a033d499f58c4318bda5acbdefd605906a6a02a Mon Sep 17 00:00:00 2001 From: user Date: Sat, 25 Apr 2015 20:43:26 -0700 Subject: [PATCH 04/15] Making sure example_1 produces a midi file that plays. --- examples/example_1.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/examples/example_1.py b/examples/example_1.py index e640f66..9c9f0b8 100644 --- a/examples/example_1.py +++ b/examples/example_1.py @@ -2,34 +2,35 @@ # Instantiate a MIDI Pattern (contains a list of tracks) pattern = midi.Pattern() # Instantiate a MIDI Track (contains a list of MIDI events) +pattern.append(midi.Track(( + midi.TimeSignatureEvent(tick=0,numerator=4,denominator=4, + metronome=24, + thirtyseconds=8), + midi.KeySignatureEvent(tick=0), + midi.EndOfTrackEvent(tick=1) + ))) track = midi.Track() -# Append the track to the pattern pattern.append(track) -track.append(midi.TimeSignatureEvent(tick=0,numerator=4,denominator=4, - metronome=24, - thirtyseconds=8)) -track.append(midi.KeySignatureEvent(tick=0)) -track = midi.Track() #reverb track.append(midi.ControlChangeEvent(tick=0,data=[91,58])) track.append(midi.ControlChangeEvent(tick=0,data=[10,69])) #msb track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[0,0])) #lsb -track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[0,0])) +track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[32,0])) track.append(midi.ProgramChangeEvent(tick=0,channel=0,data=[24])) # Instantiate a MIDI note on event, append it to the track -on = midi.NoteOnEvent(tick=0, channel=0, data=[43,20]) -on = midi.NoteOnEvent(tick=10, channel=0, data=[43,20]) -on = midi.NoteOnEvent(tick=100, channel=0, data=[43,20]) -on = midi.NoteOnEvent(tick=200, channel=0, data=[43,20]) -track.append(on) +track.append(midi.NoteOnEvent(tick=0, velocity=200, pitch=midi.G_4)) # Instantiate a MIDI note off event, append it to the track -off = midi.NoteOffEvent(tick=1000, data=[43,0]) -track.append(off) +track.append(midi.NoteOffEvent(tick=250, velocity=200, pitch=midi.G_4)) +track.append(midi.EndOfTrackEvent(tick=100)) +# some more notes +pattern.append(midi.Track(( + midi.NoteOnEvent(tick=200, velocity=200, pitch=(midi.F_4+1)), + midi.NoteOnEvent(tick=200, velocity=200, pitch=midi.A_4), + midi.NoteOnEvent(tick=200, velocity=200, pitch=midi.C_5), + midi.EndOfTrackEvent(tick=1)))) # Add the end of track event, append it to the track -eot = midi.EndOfTrackEvent(tick=1) -track.append(eot) # Print out the pattern print(pattern) # Save the pattern to disk From 81387ec2787bbf105b1a269515605cf4927d581b Mon Sep 17 00:00:00 2001 From: user Date: Sat, 25 Apr 2015 21:05:50 -0700 Subject: [PATCH 05/15] Specify number of tracks when writing the file header If you can specify how many tracks, instead of using len(pattern), you could stream tracks to the destination (timidity or whatever). There's little point in doing this for tracks, since their length has to be the BYTE count of all their events. If you can count how many bytes a collection of MIDI events is going to be, without accumulating them into a byte buffer, you're a better soul than I. --- examples/example_1.py | 10 +++++----- examples/timidity.py | 22 +++++++++++++++------- src/fileio.py | 14 ++++++++------ 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/examples/example_1.py b/examples/example_1.py index 9c9f0b8..f30b4ac 100644 --- a/examples/example_1.py +++ b/examples/example_1.py @@ -12,13 +12,13 @@ track = midi.Track() pattern.append(track) #reverb -track.append(midi.ControlChangeEvent(tick=0,data=[91,58])) -track.append(midi.ControlChangeEvent(tick=0,data=[10,69])) +#track.append(midi.ControlChangeEvent(tick=0,data=[91,58])) +#track.append(midi.ControlChangeEvent(tick=0,data=[10,69])) #msb -track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[0,0])) +#track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[0,0])) #lsb -track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[32,0])) -track.append(midi.ProgramChangeEvent(tick=0,channel=0,data=[24])) +#track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[32,0])) +#track.append(midi.ProgramChangeEvent(tick=0,channel=0,data=[24])) # Instantiate a MIDI note on event, append it to the track track.append(midi.NoteOnEvent(tick=0, velocity=200, pitch=midi.G_4)) # Instantiate a MIDI note off event, append it to the track diff --git a/examples/timidity.py b/examples/timidity.py index 1edc6f2..8bb6701 100644 --- a/examples/timidity.py +++ b/examples/timidity.py @@ -3,16 +3,24 @@ import subprocess as s def main(): - p = s.Popen(["timidity","-"],stdin=s.PIPE) + p = s.Popen(["timidity"]+['--verbose']*1+["-"],stdin=s.PIPE) writer = midi.FileWriter(p.stdin) - writer.write_file_header(midi.Pattern()) - writer.write_track_header(1) + writer.write_file_header(midi.Pattern(),2) + track = midi.Track(( + midi.TimeSignatureEvent(tick=0,numerator=4,denominator=4, + metronome=24, + thirtyseconds=8), + midi.KeySignatureEvent(tick=0), + midi.EndOfTrackEvent(tick=1) + )) + writer.write_track(track) def song(): - for i in range(200): - yield midi.NoteOnEvent(tick=i*100, velocity=200, pitch=midi.G_3) - for event in song(): - writer.write_midi_event(event) + for i in range(5): + yield midi.NoteOnEvent(tick=i*100, velocity=200, pitch=(midi.G_5+i)) + yield midi.EndOfTrackEvent(tick=1) + writer.write_track(midi.Track(song())) + p.stdin.flush() p.stdin.close() p.wait() diff --git a/src/fileio.py b/src/fileio.py index 1994b4d..464b148 100644 --- a/src/fileio.py +++ b/src/fileio.py @@ -100,16 +100,18 @@ class FileWriter(object): def __init__(self,file): self.file = file def write(self, pattern): - self.write_file_header(pattern) + self.write_file_header(pattern,len(pattern)) for track in pattern: self.write_track(track) - def write_file_header(self, pattern): + def write_file_header(self, pattern, length=None): + if length is None: + length = len(pattern) # First four bytes are MIDI header - packdata = pack(">LHHH", 6, - pattern.format, - len(pattern), - pattern.resolution) + packdata = pack(">LHHH", 6, + pattern.format, + length, + pattern.resolution) self.file.write(b'MThd' + packdata) From dfe19b278453946439e2f0f344b7ee18252f20e1 Mon Sep 17 00:00:00 2001 From: James Sully Date: Tue, 26 May 2015 14:52:41 +1000 Subject: [PATCH 06/15] add to installation instructions in readme --- README.mediawiki | 1 + 1 file changed, 1 insertion(+) diff --git a/README.mediawiki b/README.mediawiki index 50fff4a..ffb4778 100644 --- a/README.mediawiki +++ b/README.mediawiki @@ -25,6 +25,7 @@ feature set to offer. * A reader and writer, so you can read and write your MIDI tracks to disk. ==Installation== +First, make sure you have swig installed (www.swig.org) Follow the [http://docs.python.org/2/install/index.html normal procedure for Python module installation]: From 0ff6a7d73539cc7e5634fb8954c1cfd38e4054c2 Mon Sep 17 00:00:00 2001 From: James Sully Date: Tue, 26 May 2015 15:35:41 +1000 Subject: [PATCH 07/15] convert floating point division back to integer division Semantics of division changed in python 3 (PEP 238 https://www.python.org/dev/peps/pep-0238/) in python 2, divion of int by int returned int via floor division, now returns float --- tests/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests.py b/tests/tests.py index 97add8f..d66771d 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -5,7 +5,7 @@ class TestMIDI(unittest.TestCase): def test_varlen(self): maxval = 0x0FFFFFFF - for inval in range(0, maxval, maxval / 1000): + for inval in range(0, maxval, maxval // 1000): datum = midi.write_varlen(inval) outval = midi.read_varlen(iter(datum)) self.assertEqual(inval, outval) From 1189731c10e7e6bed271fbf1ab63483d83bfa8e1 Mon Sep 17 00:00:00 2001 From: James Sully Date: Tue, 26 May 2015 15:54:44 +1000 Subject: [PATCH 08/15] ticks per beat is TPB, rather than TPM --- README.mediawiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.mediawiki b/README.mediawiki index ffb4778..c6907c9 100644 --- a/README.mediawiki +++ b/README.mediawiki @@ -108,7 +108,7 @@ think about things in terms of milliseconds, or ticks, if you care about the bea A tick represents the lowest level resolution of a MIDI track. Tempo is always analogous with Beats per Minute (BPM) which is the same thing as Quarter notes per Minute (QPM). The Resolution is also known as the Pulses per Quarter note -(PPQ). It analogous to Ticks per Beat (TPM). +(PPQ). It analogous to Ticks per Beat (TPB). Tempo is set by two things. First, a saved MIDI file encodes an initial Resolution and Tempo. You use these values to initialize the sequencer timer. From 7d50f2984cfe23901bcaf28eaab846f7610bbe33 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 25 Apr 2015 18:37:30 -0700 Subject: [PATCH 09/15] 2to3 on this stuff --- examples/example_1.py | 2 +- examples/example_2.py | 2 +- scripts/mididump.py | 4 +- scripts/mididumphw.py | 2 +- scripts/midilisten.py | 4 +- scripts/midiplay.py | 4 +- setup.py | 24 +++++------ src/__init__.py | 8 ++-- src/constants.py | 6 +-- src/containers.py | 8 ++-- src/events.py | 6 +-- src/fileio.py | 40 +++++++++---------- src/sequencer.py | 10 ++--- src/sequencer_alsa/__init__.py | 2 +- src/sequencer_alsa/sequencer.py | 38 +++++++++--------- .../cfstring-typemaps-example-1.0/setup.py | 4 +- src/sequencer_osx/sequencer_osx.py | 18 ++++----- src/sequencer_osx/test.py | 6 +-- src/util.py | 2 +- test.py | 8 ++-- tests/tests.py | 2 +- 21 files changed, 100 insertions(+), 100 deletions(-) diff --git a/examples/example_1.py b/examples/example_1.py index 60acb87..52078db 100644 --- a/examples/example_1.py +++ b/examples/example_1.py @@ -15,6 +15,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..1d09fb7 100644 --- a/examples/example_2.py +++ b/examples/example_2.py @@ -1,3 +1,3 @@ import midi pattern = midi.read_midifile("example.mid") -print pattern +print(pattern) diff --git a/scripts/mididump.py b/scripts/mididump.py index 1497a3d..fff59ff 100644 --- a/scripts/mididump.py +++ b/scripts/mididump.py @@ -6,9 +6,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..640f754 100644 --- a/scripts/mididumphw.py +++ b/scripts/mididumphw.py @@ -6,4 +6,4 @@ s = sequencer.SequencerHardware() -print s +print(s) diff --git a/scripts/midilisten.py b/scripts/midilisten.py index b86ee5c..03eba64 100644 --- a/scripts/midilisten.py +++ b/scripts/midilisten.py @@ -8,7 +8,7 @@ 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 +21,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 fdddcce..51221eb 100644 --- a/scripts/midiplay.py +++ b/scripts/midiplay.py @@ -8,7 +8,7 @@ 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] @@ -43,4 +43,4 @@ time.sleep(.5) time.sleep(30) -print 'The end?' +print('The end?') diff --git a/setup.py b/setup.py index a7e5b4b..cbba66f 100755 --- a/setup.py +++ b/setup.py @@ -3,22 +3,22 @@ from distutils.core import setup, Extension __base__ = { - 'name':'midi', - 'version':'v0.2.3', - 'description':'Python MIDI API', - 'author':'giles hall', - 'author_email':'ghall@csh.rit.edu', - 'package_dir':{'midi':'src'}, - 'py_modules':['midi.containers', 'midi.__init__', 'midi.events', 'midi.util', 'midi.fileio', 'midi.constants'], - 'ext_modules':[], - 'ext_package':'', - 'scripts':['scripts/mididump.py', 'scripts/mididumphw.py', 'scripts/midiplay.py'], + 'name': 'midi', + 'version': 'v0.2.3', + 'description': 'Python MIDI API', + 'author': 'giles hall', + 'author_email': 'ghall@csh.rit.edu', + 'package_dir': {'midi':'src'}, + 'py_modules': ['midi.containers', 'midi.__init__', 'midi.events', 'midi.util', 'midi.fileio', 'midi.constants'], + 'ext_modules': [], + 'ext_package': '', + 'scripts': ['scripts/mididump.py', 'scripts/mididumphw.py', 'scripts/midiplay.py'], } def setup_alsa(ns): srclist = ["src/sequencer_alsa/sequencer_alsa.i"] extns = { - 'libraries':['asound'], + 'libraries': ['asound'], #'extra_compile_args':['-DSWIGRUNTIME_DEBUG'] } ext = Extension('_sequencer_alsa', srclist, **extns) @@ -38,7 +38,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..3ebfa3d 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,5 +1,5 @@ -from containers import * -from events import * +from .containers import * +from .events import * from struct import unpack, pack -from util import * -from fileio 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..9bffcdd 100644 --- a/src/containers.py +++ b/src/containers.py @@ -25,14 +25,14 @@ 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) def __getslice__(self, i, j): # The deprecated __getslice__ is still called when subclassing built-in types # for calls of the form List[i:j] - return self.__getitem__(slice(i,j)) + return self.__getitem__(slice(i, j)) class Track(list): def __init__(self, events=[], tick_relative=True): @@ -58,14 +58,14 @@ 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) def __getslice__(self, i, j): # The deprecated __getslice__ is still called when subclassing built-in types # for calls of the form List[i:j] - return self.__getitem__(slice(i,j)) + return self.__getitem__(slice(i, j)) def __repr__(self): return "midi.Track(\\\n %s)" % (pformat(list(self)).replace('\n', '\n '), ) diff --git a/src/events.py b/src/events.py index a62fbad..67f3290 100644 --- a/src/events.py +++ b/src/events.py @@ -14,7 +14,7 @@ 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) @@ -31,7 +31,7 @@ def __init__(cls, name, bases, dict): EventRegistry.register_event(cls, bases) def __init__(self, **kw): - if type(self.length) == int: + if isinstance(self.length, int): defdata = [0] * self.length else: defdata = [] @@ -304,7 +304,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)] diff --git a/src/fileio.py b/src/fileio.py index 2c1d793..8bf58ee 100644 --- a/src/fileio.py +++ b/src/fileio.py @@ -1,8 +1,8 @@ -from containers import * -from events import * +from .containers import * +from .events import * from struct import unpack, pack -from constants import * -from util import * +from .constants import * +from .util import * class FileReader(object): def read(self, midifile): @@ -15,7 +15,7 @@ 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." + 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 @@ -36,7 +36,7 @@ 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 + raise TypeError("Bad track header in MIDI file: " + magic) # next four bytes are track size trksz = unpack(">L", midifile.read(4))[0] return trksz @@ -56,21 +56,21 @@ 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 = ord(next(trackdata)) # is the event a MetaEvent? if MetaEvent.is_event(stsmsg): - cmd = ord(trackdata.next()) + cmd = ord(next(trackdata)) if cmd not in EventRegistry.MetaEvents: - raise Warning, "Unknown Meta MIDI Event: " + `cmd` + raise Warning("Unknown Meta MIDI Event: " + repr(cmd)) cls = EventRegistry.MetaEvents[cmd] datalen = read_varlen(trackdata) - data = [ord(trackdata.next()) for x in range(datalen)] + data = [ord(next(trackdata)) for x in range(datalen)] return cls(tick=tick, data=data) # is this event a Sysex Event? elif SysexEvent.is_event(stsmsg): data = [] while True: - datum = ord(trackdata.next()) + datum = ord(next(trackdata)) if datum == 0xF7: break data.append(datum) @@ -85,15 +85,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 += [ord(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 = [ord(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): @@ -127,11 +127,11 @@ def encode_midi_event(self, event): if isinstance(event, MetaEvent): ret += chr(event.statusmsg) + chr(event.metacommand) ret += write_varlen(len(event.data)) - ret += str.join('', map(chr, event.data)) + ret += str.join('', list(map(chr, event.data))) # is this event a Sysex Event? elif isinstance(event, SysexEvent): ret += chr(0xF0) - ret += str.join('', map(chr, event.data)) + ret += str.join('', list(map(chr, event.data))) ret += chr(0xF7) # not a Meta MIDI event or a Sysex event, must be a general message elif isinstance(event, Event): @@ -140,19 +140,19 @@ def encode_midi_event(self, event): self.RunningStatus.channel != event.channel: self.RunningStatus = event ret += chr(event.statusmsg | event.channel) - ret += str.join('', map(chr, event.data)) + ret += str.join('', list(map(chr, 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 type(midifile) in (str, str): midifile = open(midifile, 'wb') writer = FileWriter() return writer.write(midifile, pattern) def read_midifile(midifile): - if type(midifile) in (str, unicode): + if type(midifile) in (str, str): 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/__init__.py b/src/sequencer_alsa/__init__.py index 55dea93..fd22b26 100644 --- a/src/sequencer_alsa/__init__.py +++ b/src/sequencer_alsa/__init__.py @@ -1 +1 @@ -from sequencer import * +from .sequencer import * diff --git a/src/sequencer_alsa/sequencer.py b/src/sequencer_alsa/sequencer.py index 792d5c3..4f7ade2 100644 --- a/src/sequencer_alsa/sequencer.py +++ b/src/sequencer_alsa/sequencer.py @@ -1,8 +1,8 @@ import select -import sequencer_alsa as S +from . import sequencer_alsa as S import midi -__SWIG_NS_SET__ = set(['__class__', '__del__', '__delattr__', '__dict__', '__doc__', '__getattr__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__swig_getmethods__', '__swig_setmethods__', '__weakref__', 'this', 'thisown']) +__SWIG_NS_SET__ = {'__class__', '__del__', '__delattr__', '__dict__', '__doc__', '__getattr__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__swig_getmethods__', '__swig_setmethods__', '__weakref__', 'this', 'thisown'} def stringify(name, obj, indent=0): retstr = '' @@ -21,16 +21,16 @@ def stringify(name, obj, indent=0): class Sequencer(object): __ARGUMENTS__ = { - 'alsa_sequencer_name':'__sequencer__', - 'alsa_sequencer_stream':S.SND_SEQ_OPEN_DUPLEX, - 'alsa_sequencer_mode':S.SND_SEQ_NONBLOCK, - 'alsa_sequencer_type':'default', - 'alsa_port_name':'__port__', - 'alsa_port_caps':S.SND_SEQ_PORT_CAP_READ, - 'alsa_port_type':S.SND_SEQ_PORT_TYPE_MIDI_GENERIC, - 'alsa_queue_name':'__queue__', - 'sequencer_tempo':120, - 'sequencer_resolution':1000, + 'alsa_sequencer_name': '__sequencer__', + 'alsa_sequencer_stream': S.SND_SEQ_OPEN_DUPLEX, + 'alsa_sequencer_mode': S.SND_SEQ_NONBLOCK, + 'alsa_sequencer_type': 'default', + 'alsa_port_name': '__port__', + 'alsa_port_caps': S.SND_SEQ_PORT_CAP_READ, + 'alsa_port_type': S.SND_SEQ_PORT_TYPE_MIDI_GENERIC, + 'alsa_queue_name': '__queue__', + 'sequencer_tempo': 120, + 'sequencer_resolution': 1000, } DefaultArguments = {} @@ -71,7 +71,7 @@ def get_nonblock(self): def _error(self, errcode): strerr = S.snd_strerror(errcode) msg = "ALSAError[%d]: %s" % (errcode, strerr) - raise RuntimeError, msg + raise RuntimeError(msg) def _init_handle(self): ret = S.open_client(self.alsa_sequencer_name, @@ -268,7 +268,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) @@ -323,7 +323,7 @@ def add_port(self, port, name, caps): self._ports[name] = port def __iter__(self): - return self._ports.itervalues() + return iter(self._ports.values()) def __len__(self): return len(self._ports) @@ -359,7 +359,7 @@ def init(self): self._query_clients() def __iter__(self): - return self._clients.itervalues() + return iter(self._clients.values()) def __len__(self): return len(self._clients) @@ -403,9 +403,9 @@ def _query_clients(self): class SequencerRead(Sequencer): DefaultArguments = { - 'sequencer_name':'__SequencerRead__', - 'sequencer_stream':not S.SND_SEQ_NONBLOCK, - 'alsa_port_caps':S.SND_SEQ_PORT_CAP_WRITE | S.SND_SEQ_PORT_CAP_SUBS_WRITE, + 'sequencer_name': '__SequencerRead__', + 'sequencer_stream': not S.SND_SEQ_NONBLOCK, + 'alsa_port_caps': S.SND_SEQ_PORT_CAP_WRITE | S.SND_SEQ_PORT_CAP_SUBS_WRITE, } def subscribe_port(self, client, port): diff --git a/src/sequencer_osx/cfstring-typemaps-example-1.0/setup.py b/src/sequencer_osx/cfstring-typemaps-example-1.0/setup.py index 945841e..c907fa4 100644 --- a/src/sequencer_osx/cfstring-typemaps-example-1.0/setup.py +++ b/src/sequencer_osx/cfstring-typemaps-example-1.0/setup.py @@ -4,5 +4,5 @@ setup(name = "cfstring-typemaps-example", version = "1.0", ext_modules = [Extension("_example", - ["example.i","example.c"], - extra_link_args=['-framework','CoreFoundation'])]) + ["example.i", "example.c"], + extra_link_args=['-framework', 'CoreFoundation'])]) diff --git a/src/sequencer_osx/sequencer_osx.py b/src/sequencer_osx/sequencer_osx.py index bb8228b..cde0408 100644 --- a/src/sequencer_osx/sequencer_osx.py +++ b/src/sequencer_osx/sequencer_osx.py @@ -17,21 +17,21 @@ def _swig_setattr_nondynamic(self,class_type,name,value,static=1): if type(value).__name__ == 'PySwigObject': self.__dict__[name] = value return - method = class_type.__swig_setmethods__.get(name,None) - if method: return method(self,value) - if (not static) or hasattr(self,name): + method = class_type.__swig_setmethods__.get(name, None) + if method: return method(self, value) + if (not static) or hasattr(self, name): self.__dict__[name] = value else: raise AttributeError("You cannot add attributes to %s" % self) -def _swig_setattr(self,class_type,name,value): - return _swig_setattr_nondynamic(self,class_type,name,value,0) +def _swig_setattr(self, class_type, name, value): + return _swig_setattr_nondynamic(self, class_type, name, value, 0) -def _swig_getattr(self,class_type,name): +def _swig_getattr(self, class_type, name): if (name == "thisown"): return self.this.own() - method = class_type.__swig_getmethods__.get(name,None) + method = class_type.__swig_getmethods__.get(name, None) if method: return method(self) - raise AttributeError,name + raise AttributeError(name) def _swig_repr(self): try: strthis = "proxy of " + self.this.__repr__() @@ -40,7 +40,7 @@ def _swig_repr(self): import types try: - _object = types.ObjectType + _object = object _newclass = 1 except AttributeError: class _object : pass diff --git a/src/sequencer_osx/test.py b/src/sequencer_osx/test.py index 97873e0..9ac90ae 100644 --- a/src/sequencer_osx/test.py +++ b/src/sequencer_osx/test.py @@ -1,9 +1,9 @@ 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 -raw_input() +print(client, endpoint, endpoint) +input() #sequencer_osx._MIDIClientDispose(handle) diff --git a/src/util.py b/src/util.py index 688730b..ee50fd5 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 = ord(next(data)) # is the hi-bit set? if not (chr & 0x80): # no next BYTE diff --git a/test.py b/test.py index a0719dc..b3d5db4 100644 --- a/test.py +++ b/test.py @@ -2,12 +2,12 @@ import pprint x = midi.FileReader() p = midi.read('a.mid') -print p -raw_input() +print(p) +input() midi.write('aa.mid', p) p = midi.read('aa.mid') -print p -raw_input() +print(p) +input() #midi.write('aaa.mid', p) #p = midi.read('aaa.mid') diff --git a/tests/tests.py b/tests/tests.py index 3e49b7c..97add8f 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -5,7 +5,7 @@ class TestMIDI(unittest.TestCase): def test_varlen(self): maxval = 0x0FFFFFFF - for inval in xrange(0, maxval, maxval / 1000): + for inval in range(0, maxval, maxval / 1000): datum = midi.write_varlen(inval) outval = midi.read_varlen(iter(datum)) self.assertEqual(inval, outval) From 6fa74aceac86f4e10d49cf0ac58af3de2dcf809f Mon Sep 17 00:00:00 2001 From: user Date: Sat, 25 Apr 2015 18:48:02 -0700 Subject: [PATCH 10/15] Fixing specifics bytes not str, metaclasses, slots suck etc --- src/events.py | 24 +++++++----------------- src/fileio.py | 20 ++++++++++---------- src/util.py | 2 +- 3 files changed, 18 insertions(+), 28 deletions(-) diff --git a/src/events.py b/src/events.py index 67f3290..c049afe 100644 --- a/src/events.py +++ b/src/events.py @@ -17,19 +17,18 @@ def register_event(cls, event, bases): raise ValueError("Unknown bases class in event type: "+event.name) register_event = classmethod(register_event) +class AutoRegister(type): + def __init__(cls, name, bases, dict): + if name not in {'AbstractEvent', 'Event', 'MetaEvent', 'NoteEvent', + 'MetaEventWithText'}: + EventRegistry.register_event(cls, bases) -class AbstractEvent(object): - __slots__ = ['tick', 'data'] + +class AbstractEvent(object,metaclass=AutoRegister): 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 isinstance(self.length, int): defdata = [0] * self.length @@ -60,7 +59,6 @@ def __repr__(self): class Event(AbstractEvent): - __slots__ = ['channel'] name = 'Event' def __init__(self, **kw): @@ -110,7 +108,6 @@ def is_event(cls, statusmsg): """ class NoteEvent(Event): - __slots__ = ['pitch', 'velocity'] length = 2 def get_pitch(self): @@ -151,7 +148,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' @@ -169,7 +165,6 @@ def get_value(self): value = property(get_value, set_value) class ProgramChangeEvent(Event): - __slots__ = ['value'] statusmsg = 0xC0 length = 1 name = 'Program Change' @@ -181,7 +176,6 @@ def get_value(self): value = property(get_value, set_value) class ChannelAfterTouchEvent(Event): - __slots__ = ['value'] statusmsg = 0xD0 length = 1 name = 'Channel After Touch' @@ -193,7 +187,6 @@ def get_value(self): value = property(get_value, set_value) class PitchWheelEvent(Event): - __slots__ = ['pitch'] statusmsg = 0xE0 length = 2 name = 'Pitch Wheel' @@ -291,7 +284,6 @@ class EndOfTrackEvent(MetaEvent): metacommand = 0x2F class SetTempoEvent(MetaEvent): - __slots__ = ['bpm', 'mpqn'] name = 'Set Tempo' metacommand = 0x51 length = 3 @@ -315,7 +307,6 @@ class SmpteOffsetEvent(MetaEvent): metacommand = 0x54 class TimeSignatureEvent(MetaEvent): - __slots__ = ['numerator', 'denominator', 'metronome', 'thirtyseconds'] name = 'Time Signature' metacommand = 0x58 length = 4 @@ -345,7 +336,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 8bf58ee..cecacc9 100644 --- a/src/fileio.py +++ b/src/fileio.py @@ -14,8 +14,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.",magic) # next four bytes are header size # next two bytes specify the format version # next two bytes specify the number of tracks @@ -35,8 +35,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 @@ -56,21 +56,21 @@ def parse_midi_event(self, trackdata): # first datum is varlen representing delta-time tick = read_varlen(trackdata) # next byte is status message - stsmsg = ord(next(trackdata)) + stsmsg = next(trackdata) # is the event a MetaEvent? if MetaEvent.is_event(stsmsg): - cmd = ord(next(trackdata)) + cmd = next(trackdata) if cmd not in EventRegistry.MetaEvents: raise Warning("Unknown Meta MIDI Event: " + repr(cmd)) cls = EventRegistry.MetaEvents[cmd] datalen = read_varlen(trackdata) - data = [ord(next(trackdata)) for x in range(datalen)] + data = [next(trackdata) for x in range(datalen)] return cls(tick=tick, data=data) # is this event a Sysex Event? elif SysexEvent.is_event(stsmsg): data = [] while True: - datum = ord(next(trackdata)) + datum = next(trackdata) if datum == 0xF7: break data.append(datum) @@ -85,13 +85,13 @@ def parse_midi_event(self, trackdata): cls = EventRegistry.Events[key] channel = self.RunningStatus & 0x0F data.append(stsmsg) - data += [ord(next(trackdata)) 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(next(trackdata)) 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: " + repr(stsmsg)) diff --git a/src/util.py b/src/util.py index ee50fd5..25bef1e 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(next(data)) + chr = next(data) # is the hi-bit set? if not (chr & 0x80): # no next BYTE From 0957ef6c69ba9f135de0ebc79a0883b738ee7ab1 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 25 Apr 2015 19:31:14 -0700 Subject: [PATCH 11/15] Example piping to timidity --- examples/example_1.py | 20 ++++++++++-- examples/timidity.py | 22 +++++++++++++ src/fileio.py | 72 +++++++++++++++++++++++++++---------------- src/util.py | 16 +++++----- 4 files changed, 93 insertions(+), 37 deletions(-) create mode 100644 examples/timidity.py diff --git a/examples/example_1.py b/examples/example_1.py index 52078db..e640f66 100644 --- a/examples/example_1.py +++ b/examples/example_1.py @@ -5,11 +5,27 @@ track = midi.Track() # Append the track to the pattern pattern.append(track) +track.append(midi.TimeSignatureEvent(tick=0,numerator=4,denominator=4, + metronome=24, + thirtyseconds=8)) +track.append(midi.KeySignatureEvent(tick=0)) +track = midi.Track() +#reverb +track.append(midi.ControlChangeEvent(tick=0,data=[91,58])) +track.append(midi.ControlChangeEvent(tick=0,data=[10,69])) +#msb +track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[0,0])) +#lsb +track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[0,0])) +track.append(midi.ProgramChangeEvent(tick=0,channel=0,data=[24])) # Instantiate a MIDI note on event, append it to the track -on = midi.NoteOnEvent(tick=0, velocity=20, pitch=midi.G_3) +on = midi.NoteOnEvent(tick=0, channel=0, data=[43,20]) +on = midi.NoteOnEvent(tick=10, channel=0, data=[43,20]) +on = midi.NoteOnEvent(tick=100, channel=0, data=[43,20]) +on = midi.NoteOnEvent(tick=200, channel=0, data=[43,20]) track.append(on) # Instantiate a MIDI note off event, append it to the track -off = midi.NoteOffEvent(tick=100, pitch=midi.G_3) +off = midi.NoteOffEvent(tick=1000, data=[43,0]) track.append(off) # Add the end of track event, append it to the track eot = midi.EndOfTrackEvent(tick=1) diff --git a/examples/timidity.py b/examples/timidity.py new file mode 100644 index 0000000..1edc6f2 --- /dev/null +++ b/examples/timidity.py @@ -0,0 +1,22 @@ +#!/usr/bin/env pypy +import midi +import subprocess as s + +def main(): + p = s.Popen(["timidity","-"],stdin=s.PIPE) + writer = midi.FileWriter(p.stdin) + writer.write_file_header(midi.Pattern()) + writer.write_track_header(1) + + def song(): + for i in range(200): + yield midi.NoteOnEvent(tick=i*100, velocity=200, pitch=midi.G_3) + for event in song(): + writer.write_midi_event(event) + p.stdin.close() + p.wait() + + + +if __name__ == '__main__': + main() diff --git a/src/fileio.py b/src/fileio.py index cecacc9..1994b4d 100644 --- a/src/fileio.py +++ b/src/fileio.py @@ -96,63 +96,81 @@ def parse_midi_event(self, trackdata): raise Warning("Unknown MIDI Event: " + repr(stsmsg)) class FileWriter(object): - def write(self, midifile, pattern): - self.write_file_header(midifile, pattern) + RunningStatus = None + def __init__(self,file): + self.file = file + def write(self, pattern): + self.write_file_header(pattern) for track in pattern: - self.write_track(midifile, track) + self.write_track(track) - def write_file_header(self, midifile, pattern): + def write_file_header(self, pattern): # First four bytes are MIDI header packdata = pack(">LHHH", 6, pattern.format, len(pattern), pattern.resolution) - midifile.write('MThd%s' % packdata) + self.file.write(b'MThd' + packdata) - def write_track(self, midifile, track): - buf = '' - self.RunningStatus = None + + def write_track(self, track): + hlen = len(self.encode_track_header(0)) + buf = bytearray(b'0'*hlen) for event in track: - buf += self.encode_midi_event(event) - buf = self.encode_track_header(len(buf)) + buf - midifile.write(buf) + buf.extend(self.encode_midi_event(event)) + buf[:hlen] = self.encode_track_header(len(buf)-hlen) + self.file.write(buf) + def write_track_header(self,track=None): + trklen = 1 if track is None else track if isinstance(track,int) else len(track) + self.file.write(self.encode_track_header(trklen)) + def encode_track_header(self, trklen): - return 'MTrk%s' % pack(">L", trklen) + return b'MTrk' + pack(">L", trklen) + def write_midi_event(self, event): + # be sure to write the track and pattern headers first + # can stream to timidity or fluidsynth this way + self.file.write(self.encode_midi_event(event)) + def encode_midi_event(self, event): - ret = '' - ret += write_varlen(event.tick) + ret = bytearray() + #assert hasattr(event,'tick'), event + assert isinstance(event.tick,int), event.tick + ret.extend(write_varlen(event.tick)) # is the event a MetaEvent? if isinstance(event, MetaEvent): - ret += chr(event.statusmsg) + chr(event.metacommand) - ret += write_varlen(len(event.data)) - ret += str.join('', list(map(chr, event.data))) + ret.append(event.statusmsg) + ret.append(event.metacommand) + ret.extend(write_varlen(len(event.data))) + ret.extend(event.data) # is this event a Sysex Event? elif isinstance(event, SysexEvent): - ret += chr(0xF0) - ret += str.join('', list(map(chr, event.data))) - ret += chr(0xF7) + ret.append(0xF0) + ret.extend(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('', list(map(chr, event.data))) + ret.append(event.statusmsg | event.channel) + ret.extend(event.data) else: raise ValueError("Unknown MIDI Event: " + str(event)) return ret def write_midifile(midifile, pattern): if type(midifile) in (str, str): - midifile = open(midifile, 'wb') - writer = FileWriter() - return writer.write(midifile, pattern) + with open(midifile, 'wb') as out: + return write_midifile(out,pattern) + writer = FileWriter(midifile) + return writer.write(pattern) def read_midifile(midifile): - if type(midifile) in (str, str): - midifile = open(midifile, 'rb') + if type(midifile) in (str, bytes): + with open(midifile, 'rb') as inp: + return read_midifile(inp) reader = FileReader() return reader.read(midifile) diff --git a/src/util.py b/src/util.py index 25bef1e..33898b0 100644 --- a/src/util.py +++ b/src/util.py @@ -17,22 +17,22 @@ def read_varlen(data): return value def write_varlen(value): - chr1 = chr(value & 0x7F) + b1 = value & 0x7F value >>= 7 if value: - chr2 = chr((value & 0x7F) | 0x80) + b2 = (value & 0x7F) | 0x80 value >>= 7 if value: - chr3 = chr((value & 0x7F) | 0x80) + b3 = (value & 0x7F) | 0x80 value >>= 7 if value: - chr4 = chr((value & 0x7F) | 0x80) - res = chr4 + chr3 + chr2 + chr1 + b4 = (value & 0x7F) | 0x80 + res = bytes((b4,b3,b2,b1)) else: - res = chr3 + chr2 + chr1 + res = bytes((b3,b2,b1)) else: - res = chr2 + chr1 + res = bytes((b2,b1)) else: - res = chr1 + res = bytes((b1,)) return res From d2ff7210931305969803dc18d8b35bc77af3be52 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 25 Apr 2015 20:43:26 -0700 Subject: [PATCH 12/15] Making sure example_1 produces a midi file that plays. --- examples/example_1.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/examples/example_1.py b/examples/example_1.py index e640f66..9c9f0b8 100644 --- a/examples/example_1.py +++ b/examples/example_1.py @@ -2,34 +2,35 @@ # Instantiate a MIDI Pattern (contains a list of tracks) pattern = midi.Pattern() # Instantiate a MIDI Track (contains a list of MIDI events) +pattern.append(midi.Track(( + midi.TimeSignatureEvent(tick=0,numerator=4,denominator=4, + metronome=24, + thirtyseconds=8), + midi.KeySignatureEvent(tick=0), + midi.EndOfTrackEvent(tick=1) + ))) track = midi.Track() -# Append the track to the pattern pattern.append(track) -track.append(midi.TimeSignatureEvent(tick=0,numerator=4,denominator=4, - metronome=24, - thirtyseconds=8)) -track.append(midi.KeySignatureEvent(tick=0)) -track = midi.Track() #reverb track.append(midi.ControlChangeEvent(tick=0,data=[91,58])) track.append(midi.ControlChangeEvent(tick=0,data=[10,69])) #msb track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[0,0])) #lsb -track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[0,0])) +track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[32,0])) track.append(midi.ProgramChangeEvent(tick=0,channel=0,data=[24])) # Instantiate a MIDI note on event, append it to the track -on = midi.NoteOnEvent(tick=0, channel=0, data=[43,20]) -on = midi.NoteOnEvent(tick=10, channel=0, data=[43,20]) -on = midi.NoteOnEvent(tick=100, channel=0, data=[43,20]) -on = midi.NoteOnEvent(tick=200, channel=0, data=[43,20]) -track.append(on) +track.append(midi.NoteOnEvent(tick=0, velocity=200, pitch=midi.G_4)) # Instantiate a MIDI note off event, append it to the track -off = midi.NoteOffEvent(tick=1000, data=[43,0]) -track.append(off) +track.append(midi.NoteOffEvent(tick=250, velocity=200, pitch=midi.G_4)) +track.append(midi.EndOfTrackEvent(tick=100)) +# some more notes +pattern.append(midi.Track(( + midi.NoteOnEvent(tick=200, velocity=200, pitch=(midi.F_4+1)), + midi.NoteOnEvent(tick=200, velocity=200, pitch=midi.A_4), + midi.NoteOnEvent(tick=200, velocity=200, pitch=midi.C_5), + midi.EndOfTrackEvent(tick=1)))) # Add the end of track event, append it to the track -eot = midi.EndOfTrackEvent(tick=1) -track.append(eot) # Print out the pattern print(pattern) # Save the pattern to disk From 8cfa12027529f587bb672310d839bae9ea03be51 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 25 Apr 2015 21:05:50 -0700 Subject: [PATCH 13/15] Specify number of tracks when writing the file header If you can specify how many tracks, instead of using len(pattern), you could stream tracks to the destination (timidity or whatever). There's little point in doing this for tracks, since their length has to be the BYTE count of all their events. If you can count how many bytes a collection of MIDI events is going to be, without accumulating them into a byte buffer, you're a better soul than I. --- examples/example_1.py | 10 +++++----- examples/timidity.py | 22 +++++++++++++++------- src/fileio.py | 14 ++++++++------ 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/examples/example_1.py b/examples/example_1.py index 9c9f0b8..f30b4ac 100644 --- a/examples/example_1.py +++ b/examples/example_1.py @@ -12,13 +12,13 @@ track = midi.Track() pattern.append(track) #reverb -track.append(midi.ControlChangeEvent(tick=0,data=[91,58])) -track.append(midi.ControlChangeEvent(tick=0,data=[10,69])) +#track.append(midi.ControlChangeEvent(tick=0,data=[91,58])) +#track.append(midi.ControlChangeEvent(tick=0,data=[10,69])) #msb -track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[0,0])) +#track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[0,0])) #lsb -track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[32,0])) -track.append(midi.ProgramChangeEvent(tick=0,channel=0,data=[24])) +#track.append(midi.ControlChangeEvent(tick=0,channel=0,data=[32,0])) +#track.append(midi.ProgramChangeEvent(tick=0,channel=0,data=[24])) # Instantiate a MIDI note on event, append it to the track track.append(midi.NoteOnEvent(tick=0, velocity=200, pitch=midi.G_4)) # Instantiate a MIDI note off event, append it to the track diff --git a/examples/timidity.py b/examples/timidity.py index 1edc6f2..8bb6701 100644 --- a/examples/timidity.py +++ b/examples/timidity.py @@ -3,16 +3,24 @@ import subprocess as s def main(): - p = s.Popen(["timidity","-"],stdin=s.PIPE) + p = s.Popen(["timidity"]+['--verbose']*1+["-"],stdin=s.PIPE) writer = midi.FileWriter(p.stdin) - writer.write_file_header(midi.Pattern()) - writer.write_track_header(1) + writer.write_file_header(midi.Pattern(),2) + track = midi.Track(( + midi.TimeSignatureEvent(tick=0,numerator=4,denominator=4, + metronome=24, + thirtyseconds=8), + midi.KeySignatureEvent(tick=0), + midi.EndOfTrackEvent(tick=1) + )) + writer.write_track(track) def song(): - for i in range(200): - yield midi.NoteOnEvent(tick=i*100, velocity=200, pitch=midi.G_3) - for event in song(): - writer.write_midi_event(event) + for i in range(5): + yield midi.NoteOnEvent(tick=i*100, velocity=200, pitch=(midi.G_5+i)) + yield midi.EndOfTrackEvent(tick=1) + writer.write_track(midi.Track(song())) + p.stdin.flush() p.stdin.close() p.wait() diff --git a/src/fileio.py b/src/fileio.py index 1994b4d..464b148 100644 --- a/src/fileio.py +++ b/src/fileio.py @@ -100,16 +100,18 @@ class FileWriter(object): def __init__(self,file): self.file = file def write(self, pattern): - self.write_file_header(pattern) + self.write_file_header(pattern,len(pattern)) for track in pattern: self.write_track(track) - def write_file_header(self, pattern): + def write_file_header(self, pattern, length=None): + if length is None: + length = len(pattern) # First four bytes are MIDI header - packdata = pack(">LHHH", 6, - pattern.format, - len(pattern), - pattern.resolution) + packdata = pack(">LHHH", 6, + pattern.format, + length, + pattern.resolution) self.file.write(b'MThd' + packdata) From 878910ceb53c05236675adbb33454d5ee7b44358 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 25 Apr 2015 22:34:49 -0700 Subject: [PATCH 14/15] File corruption bug Wasn't writing the byte if the record status hadn't changed, which broke things. --- examples/timidity.py | 8 -------- src/fileio.py | 12 ++++++------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/examples/timidity.py b/examples/timidity.py index 8bb6701..64f35ff 100644 --- a/examples/timidity.py +++ b/examples/timidity.py @@ -6,14 +6,6 @@ def main(): p = s.Popen(["timidity"]+['--verbose']*1+["-"],stdin=s.PIPE) writer = midi.FileWriter(p.stdin) writer.write_file_header(midi.Pattern(),2) - track = midi.Track(( - midi.TimeSignatureEvent(tick=0,numerator=4,denominator=4, - metronome=24, - thirtyseconds=8), - midi.KeySignatureEvent(tick=0), - midi.EndOfTrackEvent(tick=1) - )) - writer.write_track(track) def song(): for i in range(5): diff --git a/src/fileio.py b/src/fileio.py index 464b148..92da246 100644 --- a/src/fileio.py +++ b/src/fileio.py @@ -79,7 +79,7 @@ def parse_midi_event(self, trackdata): else: key = stsmsg & 0xF0 if key not in EventRegistry.Events: - assert self.RunningStatus, "Bad byte value" + assert self.RunningStatus, ("Bad byte value", tick, stsmsg, bytes(trackdata)) data = [] key = self.RunningStatus & 0xF0 cls = EventRegistry.Events[key] @@ -153,11 +153,11 @@ def encode_midi_event(self, event): 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.append(event.statusmsg | event.channel) + # why in the heeeeeeeeelp would you not write the status message + # here? doesn't matter if it's the same as last time. the byte + # needs to be there! + + ret.append(event.statusmsg | event.channel) ret.extend(event.data) else: raise ValueError("Unknown MIDI Event: " + str(event)) From abb85028c97b433f74621be899a0b399cd100aaa Mon Sep 17 00:00:00 2001 From: Giles Hall Date: Tue, 7 Jul 2015 15:51:38 -0400 Subject: [PATCH 15/15] travis for python3 branch --- .travis.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..8ac9db7 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,18 @@ +language: python +python: + - 2.6 + - 2.7 + - 3.2 + - 3.3 + - 3.4 + - nightly +before_install: + - date -u + - uname -a + - lsb_release -a + - sudo apt-get -qq update + - sudo apt-get install build-essential python-dev swig libasound2-dev +install: + - python setup.py install +script: + - nosetests