diff --git a/.gitignore b/.gitignore index f656a0b..3657113 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ build *.pyc .coverage .*.sw? +.tox/ +*.egg-info/ +_sequencer_alsa.so diff --git a/.travis.yml b/.travis.yml index be38855..c825138 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,9 @@ language: python python: - 2.7 + - 3.4 + - 3.5 + - 3.6 before_install: - date -u - uname -a 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..29879ca 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 @@ -62,7 +63,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..d87134d 100644 --- a/src/events.py +++ b/src/events.py @@ -1,4 +1,6 @@ import math +from functools import total_ordering + class EventRegistry(object): Events = {} @@ -15,21 +17,40 @@ 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) + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(type): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +@total_ordering +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 +62,11 @@ 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): + return (self.tick, self.data) < (other.tick, other.data) + + def __eq__(self, other): + return (self.tick, self.data) == (other.tick, other.data) def __baserepr__(self, keys=[]): keys = ['tick'] + keys + ['data'] @@ -60,6 +82,7 @@ def __repr__(self): return self.__baserepr__() +@total_ordering class Event(AbstractEvent): __slots__ = ['channel'] name = 'Event' @@ -75,10 +98,11 @@ 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 self.tick < other.tick + + def __eq__(self, other): + return self.tick == other.tick def __repr__(self): return self.__baserepr__(['channel']) @@ -111,7 +135,6 @@ def is_event(cls, statusmsg): """ class NoteEvent(Event): - __slots__ = ['pitch', 'velocity'] length = 2 def get_pitch(self): @@ -152,7 +175,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 +192,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 +203,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 +214,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 +321,6 @@ class EndOfTrackEvent(MetaEvent): metacommand = 0x2F class SetTempoEvent(MetaEvent): - __slots__ = ['bpm', 'mpqn'] name = 'Set Tempo' metacommand = 0x51 length = 3 @@ -315,7 +333,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 +344,6 @@ class SmpteOffsetEvent(MetaEvent): metacommand = 0x54 class TimeSignatureEvent(MetaEvent): - __slots__ = ['numerator', 'denominator', 'metronome', 'thirtyseconds'] name = 'Time Signature' metacommand = 0x58 length = 4 @@ -356,7 +373,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..8deaf8f 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..14be6a7 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: @@ -96,3 +96,4 @@ def next(self): ret.append(event) return ret + next = __next__ diff --git a/src/sequencer_alsa/__init__.py b/src/sequencer_alsa/__init__.py index f9ca70d..dcd2c6f 100644 --- a/src/sequencer_alsa/__init__.py +++ b/src/sequencer_alsa/__init__.py @@ -1,4 +1,4 @@ try: - from sequencer import * + from .sequencer import * except ImportError: pass diff --git a/src/sequencer_alsa/sequencer.py b/src/sequencer_alsa/sequencer.py index 9a9c3ef..b9550a9 100644 --- a/src/sequencer_alsa/sequencer.py +++ b/src/sequencer_alsa/sequencer.py @@ -1,6 +1,7 @@ +from __future__ import print_function import select -import sequencer_alsa as S import midi +from . import sequencer_alsa as S __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']) @@ -72,17 +73,13 @@ 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, - self.alsa_sequencer_type, - self.alsa_sequencer_stream, - self.alsa_sequencer_mode) - if ret == None: - # XXX: global error - self._error(ret) - self.client = ret + self.client = S.open_client(self.alsa_sequencer_name, + self.alsa_sequencer_type, + self.alsa_sequencer_stream, + self.alsa_sequencer_mode) self.client_id = S.snd_seq_client_id(self.client) self.output_buffer_size = S.snd_seq_get_output_buffer_size(self.client) self.input_buffer_size = S.snd_seq_get_input_buffer_size(self.client) @@ -204,7 +201,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 +266,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) @@ -279,7 +276,6 @@ def event_write(self, event, direct=False, relative=False, tick=False): def event_read(self): ev = S.event_input(self.client) - if ev and (ev < 0): self._error(ev) if ev and ev.type in (S.SND_SEQ_EVENT_NOTEON, S.SND_SEQ_EVENT_NOTEOFF): if ev.type == S.SND_SEQ_EVENT_NOTEON: mev = midi.NoteOnEvent() @@ -324,7 +320,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) @@ -360,7 +356,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) diff --git a/src/sequencer_alsa/sequencer_alsa.i b/src/sequencer_alsa/sequencer_alsa.i index 1ee63af..9e2e357 100644 --- a/src/sequencer_alsa/sequencer_alsa.i +++ b/src/sequencer_alsa/sequencer_alsa.i @@ -14,7 +14,7 @@ open_client(const char *name, const char *type, int stream, int mode) err = snd_seq_open(&handle, type, stream, mode); if (err < 0) { - /* XXX: set global error */ + PyErr_SetString(PyExc_IOError, snd_strerror(err)); return NULL; } snd_seq_set_client_name(handle, name); @@ -37,10 +37,14 @@ event_input(snd_seq_t *handle) int err; snd_seq_event_t *ev; err = snd_seq_event_input(handle, &ev); + if (err == -EAGAIN) + { + Py_INCREF(Py_None); + return (snd_seq_event_t*)Py_None; + } if (err < 0) { - /* XXX: does SWIG prevent us from raising an exception? */ - /* PyErr_SetString(PyExc_IOError, snd_strerror(err)); */ + PyErr_SetString(PyExc_IOError, snd_strerror(err)); return NULL; } return ev; @@ -79,6 +83,7 @@ new_queue_status(snd_seq_t *handle, int queue) int err; err = snd_seq_queue_status_malloc(&qstatus); if (err < 0){ + PyErr_SetString(PyExc_IOError, snd_strerror(err)); return NULL; } return qstatus; @@ -97,6 +102,7 @@ new_client_info(void) int err; err = snd_seq_client_info_malloc(&cinfo); if (err < 0){ + PyErr_SetString(PyExc_IOError, snd_strerror(err)); return NULL; } return cinfo; @@ -109,6 +115,7 @@ new_port_info(void) int err; err = snd_seq_port_info_malloc(&pinfo); if (err < 0){ + PyErr_SetString(PyExc_IOError, snd_strerror(err)); return NULL; } return pinfo; @@ -121,27 +128,49 @@ new_port_subscribe(void) int err; err = snd_seq_port_subscribe_malloc(&subs); if (err < 0){ + PyErr_SetString(PyExc_IOError, snd_strerror(err)); return NULL; } return subs; } %} +%exception { + $action + if (!result) { + SWIG_fail; + } +} + snd_seq_t *open_client(const char *name, const char *type, int stream, int mode); snd_seq_port_subscribe_t *new_port_subscribe(); snd_seq_queue_status_t *new_queue_status(snd_seq_t *handle, int queue); -void free_queue_status(snd_seq_queue_status_t *qstatus); snd_seq_port_info_t *new_port_info(); snd_seq_client_info_t *new_client_info(); +PyObject *client_poll_descriptors(snd_seq_t *handle); + +%exception event_input { + $action + if (result == (snd_seq_event_t*)Py_None) { + return Py_None; + } + if (!result) { + SWIG_fail; + } +} + snd_seq_event_t *event_input(snd_seq_t *handle); + +%exception; + +void free_queue_status(snd_seq_queue_status_t *qstatus); int snd_seq_control_queue_eventless(snd_seq_t *handle, int queue, int type, int value); int init_queue_tempo(snd_seq_t *handle, int queue, int bpm, int ppq); -PyObject *client_poll_descriptors(snd_seq_t *handle); %typemap(out) ssize_t { $result = PyInt_FromLong($1); } %typemap(in) ssize_t { $1 = PyInt_AsLong($input); } 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..c2161cf 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,11 @@ 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() + hi_bit = 0 + while value > 0x7F: + result.append((value & 0x7F) | hi_bit) 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 - + hi_bit = 0x80 + result.append(value | hi_bit) + return result[::-1] diff --git a/tests/tests.py b/tests/tests.py index 2dc9b2c..1469b83 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -6,21 +6,22 @@ try: import midi.sequencer as sequencer -except ImportError: +except (ImportError, AttributeError): sequencer = None def get_sequencer_type(): - if sequencer == None: + if not hasattr(sequencer, 'Sequencer'): return None return sequencer.Sequencer.SEQUENCER_TYPE 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) + self.assertEqual(inval, outval, '0x%x -> %r -> 0x%x' % ( + inval, datum, outval)) def test_mary(self): midi.write_midifile("mary.mid", mary_test.MARY_MIDI) @@ -61,7 +62,7 @@ def get_writer_sequencer(self): return seq @unittest.skipIf(get_sequencer_type() != "alsa", "ALSA Sequencer not found, skipping test") - @unittest.skipIf(not os.path.exists("/dev/snd/seq"), "/dev/snd/seq is not available, skipping test") + @unittest.skipIf(not os.access("/dev/snd/seq", os.R_OK | os.W_OK), "/dev/snd/seq is not available, skipping test") def test_loopback_sequencer(self): rseq = self.get_reader_sequencer() wseq = self.get_writer_sequencer() diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..457f0a4 --- /dev/null +++ b/tox.ini @@ -0,0 +1,8 @@ +[tox] +envlist = py27,py33,py34,py35,py36 + +[testenv] +deps = + nose +commands = + nosetests tests