Skip to content

Commit

Permalink
Python3 (#3)
Browse files Browse the repository at this point in the history
* Python3 port by Marius Gedminas

See:
- https://github.com/mgedmin/python-midi
- vishnubob#130

* Re-applied adaptations for samplerbox

* Add buffered writing of 1-track midi files (#2)
  • Loading branch information
hansehv authored Feb 25, 2022
1 parent 3d565e5 commit 6889a98
Show file tree
Hide file tree
Showing 20 changed files with 225 additions and 127 deletions.
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 @@ -3,13 +3,15 @@
Attach to a MIDI device and send the contents of a MIDI file to it.
2019 - HansEhv: adapted to work with modified sequencer.py
"""
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 @@ -47,4 +49,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

0 comments on commit 6889a98

Please sign in to comment.