Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python 3 support, continued #130

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a1e5d63
Py3k compat: make imports of sub modules relativ
florianfesti Jan 10, 2017
a4ef006
Py3k compat: Remove errous __slot__ definitions
florianfesti Jan 10, 2017
2db4b81
Py3k compat: Use with_metaclass for compatible meta classing
florianfesti Jan 11, 2017
1fc0bd6
Py3k compat: Instantiate Exceptions and use repr() instead of backticks
florianfesti Jan 12, 2017
dcd62bc
Py3k compat: make data read a bytearray in both Python2 and Python3
florianfesti Jan 12, 2017
e78bd9a
Py3k compat: wrap ranges with list()
florianfesti Jan 12, 2017
b22f61f
Py3k compat: Change Filewriter to use bytes and bytearrays
florianfesti Jan 12, 2017
5e85385
Py3k compat: Use rich comparisons
florianfesti Jan 12, 2017
7ae6ec3
Py3k compat: Use print_function from __future__
florianfesti Jan 12, 2017
6d63385
Py3k compat: replace xrange by range
florianfesti Jan 12, 2017
5db02c1
Include own copy of with_metaclass()
mgedmin Oct 10, 2017
a346920
Add a tox.ini
mgedmin Oct 10, 2017
6372115
Work around issue #129 so I can run tests
mgedmin Oct 10, 2017
7834abc
Fix byte ordering issues in write_varlen()
mgedmin Oct 10, 2017
6e38251
Fix MIDI writing
mgedmin Oct 10, 2017
8888c95
Work around AttributeError: 'module' object has no attribute 'Sequencer'
mgedmin Oct 10, 2017
6e1c2a7
Fix tests under Python 3
mgedmin Oct 10, 2017
7aa5e7b
Enable Python 3 on Travis
mgedmin Oct 10, 2017
db46eb1
Make comparison logic match git master
mgedmin Oct 10, 2017
4ba6d3c
Python 2 iterator protocol compatibility
mgedmin Oct 10, 2017
2f7f9db
Use relative imports for the sequencer as well
mgedmin Oct 10, 2017
3f2d806
Python 3: instantiate exceptions
mgedmin Oct 10, 2017
0597aff
Python 3: there's no itervalues() any more
mgedmin Oct 10, 2017
12d5b40
Figure out SWIG exception raising
mgedmin Oct 10, 2017
c2bf158
Fix SWIG exception handling for all functions
mgedmin Oct 10, 2017
d0d01c4
Check whether /dev/snd/seq is readable/writable
mgedmin Oct 10, 2017
fbde410
Make event_input ignore EAGAIN errors
mgedmin Oct 10, 2017
fec1e8f
Fix compiler warning about incompatible return types
mgedmin Oct 10, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ build
*.pyc
.coverage
.*.sw?
.tox/
*.egg-info/
_sequencer_alsa.so
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
language: python
python:
- 2.7
- 3.4
- 3.5
- 3.6
before_install:
- date -u
- uname -a
Expand Down
3 changes: 2 additions & 1 deletion examples/example_1.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from __future__ import print_function
import midi
# Instantiate a MIDI Pattern (contains a list of tracks)
pattern = midi.Pattern()
Expand All @@ -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)
3 changes: 2 additions & 1 deletion examples/example_2.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from __future__ import print_function
import midi
pattern = midi.read_midifile("example.mid")
print pattern
print(pattern)
6 changes: 4 additions & 2 deletions scripts/mididump.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
#!/usr/bin/env python
from __future__ import print_function

"""
Print a description of a MIDI file.
"""
import midi
import sys

if len(sys.argv) != 2:
print "Usage: {0} <midifile>".format(sys.argv[0])
print("Usage: {0} <midifile>".format(sys.argv[0]))
sys.exit(2)

midifile = sys.argv[1]
pattern = midi.read_midifile(midifile)
print repr(pattern)
print(repr(pattern))
3 changes: 2 additions & 1 deletion scripts/mididumphw.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
6 changes: 4 additions & 2 deletions scripts/midilisten.py
Original file line number Diff line number Diff line change
Expand Up @@ -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} <client> <port>".format(sys.argv[0])
print("Usage: {0} <client> <port>".format(sys.argv[0]))
exit(2)

client = sys.argv[1]
Expand All @@ -21,4 +23,4 @@
while True:
event = seq.event_read()
if event is not None:
print event
print(event)
6 changes: 4 additions & 2 deletions scripts/midiplay.py
Original file line number Diff line number Diff line change
Expand Up @@ -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} <client> <port> <file>".format(sys.argv[0])
print("Usage: {0} <client> <port> <file>".format(sys.argv[0]))
exit(2)

client = sys.argv[1]
Expand Down Expand Up @@ -45,4 +47,4 @@
seq.drain()
time.sleep(.5)

print 'The end?'
print('The end?')
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python

from __future__ import print_function
import os
from setuptools import setup, Extension
import setuptools.command.install
Expand Down Expand Up @@ -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__":
Expand Down
9 changes: 5 additions & 4 deletions src/__init__.py
Original file line number Diff line number Diff line change
@@ -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 *
6 changes: 3 additions & 3 deletions src/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {}
Expand Down
4 changes: 2 additions & 2 deletions src/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)

Expand Down
64 changes: 40 additions & 24 deletions src/events.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import math
from functools import total_ordering


class EventRegistry(object):
Events = {}
Expand All @@ -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:
Expand All @@ -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']
Expand All @@ -60,6 +82,7 @@ def __repr__(self):
return self.__baserepr__()


@total_ordering
class Event(AbstractEvent):
__slots__ = ['channel']
name = 'Event'
Expand All @@ -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'])
Expand Down Expand Up @@ -111,7 +135,6 @@ def is_event(cls, statusmsg):
"""

class NoteEvent(Event):
__slots__ = ['pitch', 'velocity']
length = 2

def get_pitch(self):
Expand Down Expand Up @@ -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'
Expand All @@ -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'
Expand All @@ -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'
Expand All @@ -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'
Expand Down Expand Up @@ -302,7 +321,6 @@ class EndOfTrackEvent(MetaEvent):
metacommand = 0x2F

class SetTempoEvent(MetaEvent):
__slots__ = ['bpm', 'mpqn']
name = 'Set Tempo'
metacommand = 0x51
length = 3
Expand All @@ -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)]
Expand All @@ -326,7 +344,6 @@ class SmpteOffsetEvent(MetaEvent):
metacommand = 0x54

class TimeSignatureEvent(MetaEvent):
__slots__ = ['numerator', 'denominator', 'metronome', 'thirtyseconds']
name = 'Time Signature'
metacommand = 0x58
length = 4
Expand Down Expand Up @@ -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
Expand Down
Loading