Skip to content

Python 3 compatibility #109

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

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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: 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?')
5 changes: 4 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 All @@ -15,12 +16,14 @@
'ext_modules':[],
'ext_package':'',
'scripts':['scripts/mididump.py', 'scripts/mididumphw.py', 'scripts/midiplay.py'],
'install_requires':['future'],
}

# this kludge ensures we run the build_ext first before anything else
# otherwise, we will be missing generated files during the copy
class Install_Command_build_ext_first(setuptools.command.install.install):
def run(self):
setuptools.command.install.install.do_egg_install(self)
self.run_command("build_ext")
return setuptools.command.install.install.run(self)

Expand Down Expand Up @@ -62,7 +65,7 @@ def configure_platform():
setup_alsa(ns)
pass
else:
print "No sequencer available for '%s' platform." % platform
print("No sequencer available for '%s' platform." % platform)
return ns

if __name__ == "__main__":
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
54 changes: 30 additions & 24 deletions src/events.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import math
from future.utils import with_metaclass
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally would be inclined to use six instead of future -- it seems less intrusive. Although the actual with_metaclass implementation is just 8 lines of code, so maybe just copy it here to avoid dealing with extra dependencies?


class EventRegistry(object):
Events = {}
Expand All @@ -15,21 +16,22 @@ def register_event(cls, event, bases):
"Event %s already registered" % event.name
cls.MetaEvents[event.metacommand] = event
else:
raise ValueError, "Unknown bases class in event type: "+event.name
raise ValueError("Unknown bases class in event type: ", event.name)
register_event = classmethod(register_event)


class AbstractEvent(object):
class RegisterEventMeta(type):
def __init__(cls, name, bases, dict):
if name not in ['AbstractEvent', 'Event', 'MetaEvent', 'NoteEvent',
'MetaEventWithText']:
EventRegistry.register_event(cls, bases)

class AbstractEvent(with_metaclass(RegisterEventMeta,object)):
__slots__ = ['tick', 'data']
name = "Generic MIDI Event"
length = 0
statusmsg = 0x0

class __metaclass__(type):
def __init__(cls, name, bases, dict):
if name not in ['AbstractEvent', 'Event', 'MetaEvent', 'NoteEvent',
'MetaEventWithText']:
EventRegistry.register_event(cls, bases)

def __init__(self, **kw):
if type(self.length) == int:
Expand All @@ -41,10 +43,18 @@ def __init__(self, **kw):
for key in kw:
setattr(self, key, kw[key])

def __cmp__(self, other):
if self.tick < other.tick: return -1
elif self.tick > other.tick: return 1
return cmp(self.data, other.data)
def __lt__(self, other):
if self.tick < other.tick:
return True
return self.data < other.data
Copy link

@mgedmin mgedmin Oct 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be defining all six rich comparison operators, or using the functools.total_ordering class decorator to get them.


def __eq__(self, other):
return (self.__class__ is other.__class__ and
self.tick == other.tick and
self.data == other.data)

def __ne__(self, other):
return not self.__eq__(other)

def __baserepr__(self, keys=[]):
keys = ['tick'] + keys + ['data']
Expand Down Expand Up @@ -75,10 +85,14 @@ def copy(self, **kw):
_kw.update(kw)
return self.__class__(**_kw)

def __cmp__(self, other):
if self.tick < other.tick: return -1
elif self.tick > other.tick: return 1
return 0
def __lt__(self, other):
return (super(Event, self).__lt__(other) or
(super(Event, self).__eq__(other) and
self.channel < other.channel))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the logic instead of just porting to Python 3. Intentional?

(Also, see above re: functools.total_ordering)


def __eq__(self, other):
return super(Event, self).__eq__(other) and \
self.channel == other.channel

def __repr__(self):
return self.__baserepr__(['channel'])
Expand Down Expand Up @@ -111,7 +125,6 @@ def is_event(cls, statusmsg):
"""

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

def get_pitch(self):
Expand Down Expand Up @@ -152,7 +165,6 @@ def set_value(self, val):
value = property(get_value, set_value)

class ControlChangeEvent(Event):
__slots__ = ['control', 'value']
statusmsg = 0xB0
length = 2
name = 'Control Change'
Expand All @@ -170,7 +182,6 @@ def get_value(self):
value = property(get_value, set_value)

class ProgramChangeEvent(Event):
__slots__ = ['value']
statusmsg = 0xC0
length = 1
name = 'Program Change'
Expand All @@ -182,7 +193,6 @@ def get_value(self):
value = property(get_value, set_value)

class ChannelAfterTouchEvent(Event):
__slots__ = ['value']
statusmsg = 0xD0
length = 1
name = 'Channel After Touch'
Expand All @@ -194,7 +204,6 @@ def get_value(self):
value = property(get_value, set_value)

class PitchWheelEvent(Event):
__slots__ = ['pitch']
statusmsg = 0xE0
length = 2
name = 'Pitch Wheel'
Expand Down Expand Up @@ -302,7 +311,6 @@ class EndOfTrackEvent(MetaEvent):
metacommand = 0x2F

class SetTempoEvent(MetaEvent):
__slots__ = ['bpm', 'mpqn']
name = 'Set Tempo'
metacommand = 0x51
length = 3
Expand All @@ -315,7 +323,7 @@ def get_bpm(self):

def get_mpqn(self):
assert(len(self.data) == 3)
vals = [self.data[x] << (16 - (8 * x)) for x in xrange(3)]
vals = [self.data[x] << (16 - (8 * x)) for x in range(3)]
return sum(vals)
def set_mpqn(self, val):
self.data = [(val >> (16 - (8 * x)) & 0xFF) for x in range(3)]
Expand All @@ -326,7 +334,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 +363,6 @@ def set_thirtyseconds(self, val):
thirtyseconds = property(get_thirtyseconds, set_thirtyseconds)

class KeySignatureEvent(MetaEvent):
__slots__ = ['alternatives', 'minor']
name = 'Key Signature'
metacommand = 0x59
length = 2
Expand Down
Loading