Skip to content

Feature/python3 #111

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 16 commits into
base: master
Choose a base branch
from
Open
18 changes: 18 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion README.mediawiki
Original file line number Diff line number Diff line change
Expand Up @@ -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]:

Expand Down Expand Up @@ -107,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.
Expand Down
33 changes: 25 additions & 8 deletions examples/example_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,36 @@
# 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)
#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=[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, velocity=20, pitch=midi.G_3)
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=100, pitch=midi.G_3)
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
print(pattern)
# Save the pattern to disk
midi.write_midifile("example.mid", pattern)
2 changes: 1 addition & 1 deletion examples/example_2.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import midi
pattern = midi.read_midifile("example.mid")
print pattern
print(pattern)
30 changes: 30 additions & 0 deletions examples/timidity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env pypy
import midi
import subprocess as s

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):
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()



if __name__ == '__main__':
main()
4 changes: 2 additions & 2 deletions scripts/mididump.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
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))
2 changes: 1 addition & 1 deletion scripts/mididumphw.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@

s = sequencer.SequencerHardware()

print s
print(s)
4 changes: 2 additions & 2 deletions scripts/midilisten.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
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 +21,4 @@
while True:
event = seq.event_read()
if event is not None:
print event
print(event)
4 changes: 2 additions & 2 deletions scripts/midiplay.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
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 @@ -43,4 +43,4 @@
time.sleep(.5)
time.sleep(30)

print 'The end?'
print('The end?')
24 changes: 12 additions & 12 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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':'[email protected]',
'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': '[email protected]',
'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)
Expand All @@ -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__":
Expand Down
8 changes: 4 additions & 4 deletions src/__init__.py
Original file line number Diff line number Diff line change
@@ -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 *
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
8 changes: 4 additions & 4 deletions src/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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 '), )
30 changes: 10 additions & 20 deletions src/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,23 @@ 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 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 type(self.length) == int:
if isinstance(self.length, int):
defdata = [0] * self.length
else:
defdata = []
Expand Down Expand Up @@ -60,7 +59,6 @@ def __repr__(self):


class Event(AbstractEvent):
__slots__ = ['channel']
name = 'Event'

def __init__(self, **kw):
Expand Down Expand Up @@ -110,7 +108,6 @@ def is_event(cls, statusmsg):
"""

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

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

class SetTempoEvent(MetaEvent):
__slots__ = ['bpm', 'mpqn']
name = 'Set Tempo'
metacommand = 0x51
length = 3
Expand All @@ -304,7 +296,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 @@ -315,7 +307,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 @@ -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
Expand Down
Loading