mido-1.2.9/ 0000775 0001750 0001750 00000000000 13355643235 012704 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/tests/ 0000775 0001750 0001750 00000000000 13355643235 014046 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/tests/midifiles/ 0000775 0001750 0001750 00000000000 13355643235 016013 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/tests/midifiles/test_midifiles.py 0000664 0001750 0001750 00000011167 13355500373 021372 0 ustar olemb olemb 0000000 0000000 import io
from pytest import raises
from mido.messages import Message
from mido.midifiles.midifiles import MidiFile
from mido.midifiles.meta import MetaMessage, KeySignatureError
HEADER_ONE_TRACK = """
4d 54 68 64 # MThd
00 00 00 06 # Chunk size
00 01 # Type 1
00 01 # 1 track
00 78 # 120 ticks per beat
"""
def parse_hexdump(hexdump):
data = bytearray()
for line in hexdump.splitlines():
data += bytearray.fromhex(line.split('#')[0])
return data
def read_file(hexdump, clip=False):
return MidiFile(file=io.BytesIO(parse_hexdump(hexdump)), clip=clip)
def test_no_tracks():
assert read_file("""
4d 54 68 64 # MThd
00 00 00 06 # Chunk size
00 01 # Type 1
00 00 # 0 tracks
00 78 # 120 ticks per beat
""").tracks == []
def test_single_message():
assert read_file(HEADER_ONE_TRACK + """
4d 54 72 6b # MTrk
00 00 00 04
20 90 40 40 # note_on
""").tracks[0] == [Message('note_on', note=64, velocity=64, time=32)]
def test_too_long_message():
with raises(IOError):
read_file(HEADER_ONE_TRACK + """
4d 54 72 6b # MTrk
00 00 00 04
00 ff 03 ff ff 7f # extremely long track name message
""")
def test_two_tracks():
mid = read_file("""
4d54 6864 0000 0006 0001 0002 0040 # Header
4d54 726b 0000 0008 00 90 40 10 40 80 40 10 # Track 0
4d54 726b 0000 0008 00 90 47 10 40 80 47 10 # Track 1
""")
assert len(mid.tracks) == 2
# TODO: add some more tests here.
def test_empty_file():
with raises(EOFError):
read_file("")
def test_eof_in_track():
with raises(EOFError):
read_file(HEADER_ONE_TRACK + """
4d 54 72 6b # MTrk
00 00 00 01 # Chunk size
# Oops, no data here.
""")
def test_invalid_data_byte_no_clipping():
# TODO: should this raise IOError?
with raises(IOError):
read_file(HEADER_ONE_TRACK + """
4d 54 72 6b # MTrk
00 00 00 04 # Chunk size
00 90 ff 40 # note_on note=255 velocity=64
""")
def test_invalid_data_byte_with_clipping_high():
midi_file = read_file(HEADER_ONE_TRACK + """
4d 54 72 6b # MTrk
00 00 00 04 # Chunk size
00 90 ff 40 # note_on note=255 velocity=64
""", clip=True)
assert midi_file.tracks[0][0].note == 127
def test_meta_messages():
# TODO: should this raise IOError?
mid = read_file(HEADER_ONE_TRACK + """
4d 54 72 6b # MTrk
00 00 00 0c # Chunk size
00 ff 03 04 54 65 73 74 # track_name name='Test'
00 ff 2f 00 # end_of_track
""")
track = mid.tracks[0]
assert track[0] == MetaMessage('track_name', name='Test')
assert track[1] == MetaMessage('end_of_track')
def test_meta_message_bad_key_sig_throws_key_signature_error_sharps():
with raises(KeySignatureError):
read_file(HEADER_ONE_TRACK + """
4d 54 72 6b # MTrk
00 00 00 09 # Chunk size
00 ff 59 02 08 # Key Signature with 8 sharps
00 ff 2f 00 # end_of_track
""")
def test_meta_message_bad_key_sig_throws_key_signature_error_flats():
with raises(KeySignatureError):
read_file(HEADER_ONE_TRACK + """
4d 54 72 6b # MTrk
00 00 00 09 # Chunk size
00 ff 59 02 f8 # Key Signature with 8 flats
00 ff 2f 00 # end_of_track
""")
def test_meta_messages_with_length_0():
"""sequence_number and midi_port with no data bytes should be accepted.
In rare cases these messages have length 0 and thus no data
bytes. (See issues 42 and 93.) It is unclear why these messages
are missing their data. It could be cased by a bug in the software
that created the files.
So far this has been fixed by adding a test to each of these two
meta message classes. If the problem appears with other message
types it may be worth finding a more general solution.
"""
mid = read_file(HEADER_ONE_TRACK + """
4d 54 72 6b # MTrk
00 00 00 17
00 ff 00 00 # sequence_number with no data bytes (should be 2).
00 ff 21 00 # midi_port with no data bytes (should be 1).
00 ff 00 02 00 01 # sequence_number with correct number of data bytes (2).
00 ff 21 01 01 # midi_port with correct number of data bytes (1).
00 ff 2f 00
""")
assert mid.tracks[0] == [
MetaMessage('sequence_number', number=0),
MetaMessage('midi_port', port=0),
MetaMessage('sequence_number', number=1),
MetaMessage('midi_port', port=1),
MetaMessage('end_of_track'),
]
mido-1.2.9/tests/midifiles/test_tracks.py 0000664 0001750 0001750 00000000743 13355500373 020712 0 ustar olemb olemb 0000000 0000000 from mido.midifiles.meta import MetaMessage
from mido.midifiles.tracks import MidiTrack
def test_track_slice():
track = MidiTrack()
# Slice should return MidiTrack object.
assert isinstance(track[::], MidiTrack)
def test_track_name():
name1 = MetaMessage('track_name', name='name1')
name2 = MetaMessage('track_name', name='name2')
# The track should use the first name it finds.
track = MidiTrack([name1, name2])
assert track.name == name1.name
mido-1.2.9/tests/midifiles/test_meta.py 0000664 0001750 0001750 00000002561 13355500373 020351 0 ustar olemb olemb 0000000 0000000 import pytest
from mido.midifiles.meta import MetaMessage, MetaSpec_key_signature, KeySignatureError
def test_copy_invalid_argument():
with pytest.raises(ValueError):
MetaMessage('track_name').copy(a=1)
def test_copy_cant_override_type():
with pytest.raises(ValueError):
MetaMessage('track_name').copy(type='end_of_track')
class TestKeySignature:
@pytest.mark.parametrize('bad_key_sig', [[8, 0], [8, 1], [0, 2],
[9, 1], [255 - 7, 0]])
def test_bad_key_sig_throws_key_signature_error(self, bad_key_sig):
with pytest.raises(KeySignatureError):
MetaSpec_key_signature().decode(MetaMessage('key_signature'),
bad_key_sig)
@pytest.mark.parametrize('input_bytes,expect_sig', [([0, 0], 'C'),
([0, 1], 'Am'),
([255 - 6, 0], 'Cb'),
([255 - 6, 1], 'Abm'),
([7, 1], 'A#m')
])
def test_key_signature(self, input_bytes, expect_sig):
msg = MetaMessage('key_signature')
MetaSpec_key_signature().decode(msg, input_bytes)
assert msg.key == expect_sig
mido-1.2.9/tests/midifiles/test_units.py 0000664 0001750 0001750 00000001350 13355500373 020560 0 ustar olemb olemb 0000000 0000000 from mido.midifiles.units import tempo2bpm, bpm2tempo, tick2second, second2tick
def test_tempo2bpm_bpm2tempo():
for bpm, tempo in [
(20, 3000000),
(60, 1000000),
(120, 500000),
(240, 250000),
]:
assert bpm == tempo2bpm(tempo)
assert tempo == bpm2tempo(bpm)
# TODO: these tests could be improved with better test values such as
# edge cases.
def test_tick2second():
assert tick2second(1, ticks_per_beat=100, tempo=500000) == 0.005
assert tick2second(2, ticks_per_beat=100, tempo=100000) == 0.002
def test_second2tick():
assert second2tick(0.005, ticks_per_beat=100, tempo=500000) == 1
assert second2tick(0.002, ticks_per_beat=100, tempo=100000) == 2
mido-1.2.9/tests/test_parser.py 0000664 0001750 0001750 00000006424 13355500373 016754 0 ustar olemb olemb 0000000 0000000 from __future__ import print_function
import random
from pytest import raises
from mido.messages import Message, specs
from mido.parser import Parser, parse, parse_all
def test_parse():
"""Parse a note_on msg and compare it to one created with Message()."""
parsed = parse(b'\x90\x4c\x20')
other = Message('note_on', channel=0, note=0x4c, velocity=0x20)
assert parsed == other
def test_parse_stray_data():
"""The parser should ignore stray data bytes."""
assert parse_all(b'\x20\x30') == []
def test_parse_stray_status_bytes():
"""The parser should ignore stray status bytes."""
assert parse_all(b'\x90\x90\xf0') == []
def test_encode_and_parse():
"""Encode a message and then parse it.
Should return the same message.
"""
note_on = Message('note_on')
assert note_on == parse(note_on.bytes())
def test_feed_byte():
"""Put various things into feed_byte()."""
parser = Parser()
parser.feed_byte(0)
parser.feed_byte(255)
with raises(TypeError):
parser.feed_byte([1, 2, 3])
with raises(ValueError):
parser.feed_byte(-1)
with raises(ValueError):
parser.feed_byte(256)
def test_feed():
"""Put various things into feed()."""
parser = Parser()
parser.feed([])
parser.feed([1, 2, 3])
# TODO: add more valid types.
with raises(TypeError):
parser.feed(1)
with raises(TypeError):
parser.feed(None)
with raises(TypeError):
parser.feed()
def test_parse_random_bytes():
"""Parser should not crash when parsing random data."""
randrange = random.Random('a_random_seed').randrange
parser = Parser()
for _ in range(10000):
byte = randrange(256)
parser.feed_byte(byte)
def test_parse_channel():
"""Parser should not discard the channel in channel messages."""
assert parse([0x90, 0x00, 0x00]).channel == 0
assert parse([0x92, 0x00, 0x00]).channel == 2
def test_one_byte_message():
"""Messages that are one byte long should not wait for data bytes."""
messages = parse_all([0xf6]) # Tune request.
assert len(messages) == 1
assert messages[0].type == 'tune_request'
def test_undefined_messages():
"""The parser should ignore undefined status bytes and sysex_end."""
messages = parse_all([0xf4, 0xf5, 0xf7, 0xf9, 0xfd])
assert messages == []
def test_realtime_inside_sysex():
"""Realtime message inside sysex should be delivered first."""
messages = parse_all([0xf0, 0, 0xfb, 0, 0xf7])
assert len(messages) == 2
assert messages[0].type == 'continue'
assert messages[1].type == 'sysex'
def test_undefined_realtime_inside_sysex():
"""Undefined realtime message inside sysex should ignored."""
messages = parse_all([0xf0, 0, 0xf9, 0xfd, 0, 0xf7])
assert len(messages) == 1
assert messages[0].type == 'sysex'
def test_encode_and_parse_all():
"""Encode and then parse all message types.
This checks mostly for errors in the parser.
"""
parser = Parser()
for type_ in sorted(specs.SPEC_BY_TYPE.keys()):
msg = Message(type_)
parser.feed(msg.bytes())
parser.get_message() == msg
assert parser.get_message() is None
def test_parser_ascii_text():
assert parse_all(b'7 bit ASCII should not produce any messages') == []
mido-1.2.9/tests/backends/ 0000775 0001750 0001750 00000000000 13355643235 015620 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/tests/backends/test_backend.py 0000664 0001750 0001750 00000000410 13355500373 020606 0 ustar olemb olemb 0000000 0000000 from mido.backends.backend import Backend
def test_split_api():
backend = Backend('test')
assert backend.name == 'test'
assert backend.api is None
backend = Backend('test/ALSA')
assert backend.name == 'test'
assert backend.api == 'ALSA'
mido-1.2.9/tests/backends/test_rtmidi.py 0000664 0001750 0001750 00000001372 13355500373 020517 0 ustar olemb olemb 0000000 0000000 from mido.backends.rtmidi_utils import expand_alsa_port_name
def test_expand_alsa_port_name():
port_names = sorted(['A:port 128:0',
'B:port 129:0',
'B:port 129:0',
'Z:port 130:0'])
def expand(name):
return expand_alsa_port_name(port_names, name)
# Should return first matching port.
assert expand('port') == 'A:port 128:0'
assert expand('A:port') == 'A:port 128:0'
assert expand('B:port') == 'B:port 129:0'
# Full name should also work.
assert expand('A:port 128:0') == 'A:port 128:0'
# If the port is not found the original name should be returned
# for the caller to deal with.
assert expand('invalid name') == 'invalid name'
mido-1.2.9/tests/test_syx.py 0000664 0001750 0001750 00000002423 13355500373 016276 0 ustar olemb olemb 0000000 0000000 from pytest import raises
from mido.messages import Message
from mido.syx import read_syx_file, write_syx_file
def test_read(tmpdir):
path = tmpdir.join("test.syx").strpath
msg = Message('sysex', data=(1, 2, 3))
with open(path, 'wb') as outfile:
outfile.write(msg.bin())
assert read_syx_file(path) == [msg]
with open(path, 'wt') as outfile:
outfile.write(msg.hex())
assert read_syx_file(path) == [msg]
with open(path, 'wt') as outfile:
outfile.write('NOT HEX')
with raises(ValueError):
read_syx_file(path)
def test_handle_any_whitespace(tmpdir):
path = tmpdir.join("test.syx").strpath
with open(path, 'wt') as outfile:
outfile.write('F0 01 02 \t F7\n F0 03 04 F7\n')
assert read_syx_file(path) == [Message('sysex', data=[1, 2]),
Message('sysex', data=[3, 4])]
def test_write(tmpdir):
# p = tmpdir.mkdir("sub").join("hello.txt")
path = tmpdir.join("test.syx").strpath
msg = Message('sysex', data=(1, 2, 3))
write_syx_file(path, [msg])
with open(path, 'rb') as infile:
assert infile.read() == msg.bin()
write_syx_file(path, [msg], plaintext=True)
with open(path, 'rt') as infile:
assert infile.read().strip() == msg.hex()
mido-1.2.9/tests/messages/ 0000775 0001750 0001750 00000000000 13355643235 015655 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/tests/messages/test_messages.py 0000664 0001750 0001750 00000006346 13355500373 021101 0 ustar olemb olemb 0000000 0000000 from pytest import raises
from mido.messages.specs import MIN_PITCHWHEEL, MAX_PITCHWHEEL, MIN_SONGPOS, MAX_SONGPOS
from mido.messages.messages import Message, SysexData
def test_msg_time_equality():
# Since 1.1.18 time is included in comparison.
assert Message('clock', time=0) == Message('clock', time=0)
assert Message('clock', time=0) != Message('clock', time=1)
def test_set_type():
"""Can't change the type of a message."""
with raises(AttributeError):
Message('note_on').type = 'note_off'
def test_encode_pitchwheel():
assert 'E0 00 00' == Message('pitchwheel', pitch=MIN_PITCHWHEEL).hex()
assert 'E0 00 40' == Message('pitchwheel', pitch=0).hex()
assert 'E0 7F 7F' == Message('pitchwheel', pitch=MAX_PITCHWHEEL).hex()
def test_decode_pitchwheel():
assert Message.from_hex('E0 00 00').pitch == MIN_PITCHWHEEL
assert Message.from_hex('E0 00 40').pitch == 0
assert Message.from_hex('E0 7F 7F').pitch == MAX_PITCHWHEEL
def test_encode_songpos():
assert 'F2 00 00' == Message('songpos', pos=MIN_SONGPOS).hex()
assert 'F2 7F 7F' == Message('songpos', pos=MAX_SONGPOS).hex()
def test_decode_songpos():
assert Message.from_hex('F2 00 00').pos == MIN_SONGPOS
assert Message.from_hex('F2 7F 7F').pos == MAX_SONGPOS
def test_sysex_data_is_sysexdata_object():
assert isinstance(Message.from_hex('F0 00 F7').data, SysexData)
def test_sysex_data_accepts_different_types():
assert Message('sysex', data=(0, 1, 2)).data == (0, 1, 2)
assert Message('sysex', data=[0, 1, 2]).data == (0, 1, 2)
assert Message('sysex', data=range(3)).data == (0, 1, 2)
assert Message('sysex', data=bytearray([0, 1, 2])).data == (0, 1, 2)
assert Message('sysex', data=b'\x00\x01\x02').data == (0, 1, 2)
def test_copy():
assert Message('start').copy(time=1) == Message('start', time=1)
def test_init_invalid_argument():
with raises(ValueError):
Message('note_on', zzzzzzzzzzzz=2)
with raises(ValueError):
# note_on doesn't take program.
Message('note_on', program=2)
def test_copy_invalid_argument():
with raises(ValueError):
Message('note_on').copy(zzzzzzzzzzzz=2)
with raises(ValueError):
# note_on doesn't take program.
Message('note_on').copy(program=2)
def test_copy_cant_change_type():
with raises(ValueError):
Message('start').copy(type='stop')
def test_copy_can_have_same_type():
Message('start').copy(type='start')
def test_copy_handles_data_generator():
msg1 = Message('sysex')
msg2 = msg1.copy(data=(i for i in range(3)))
assert msg2.data == (0, 1, 2)
assert isinstance(msg2.data, SysexData)
def test_compare_with_nonmessage():
with raises(TypeError):
Message('clock') == 'not a message'
def test_from_dict_default_values():
msg = Message('note_on', channel=0, note=0, time=0)
data = {'type': 'note_on'}
assert Message.from_dict(data) == msg
def test_dict_sysex_data():
msg = Message('sysex', data=(1, 2, 3))
data = msg.dict()
assert data == {'type': 'sysex', 'data': [1, 2, 3], 'time': 0}
assert isinstance(data['data'], list)
def test_from_hex_sysex_data_type():
msg = Message.from_hex('F0 01 02 03 F7')
assert isinstance(msg.data, SysexData)
mido-1.2.9/tests/messages/test_encode.py 0000664 0001750 0001750 00000001255 13355500373 020521 0 ustar olemb olemb 0000000 0000000 from mido.messages.specs import SPEC_BY_STATUS
from mido.messages.encode import encode_message
from mido.messages.decode import decode_message
def test_encode_decode_all():
"""Encode and then decode all messages on all channels.
Each data byte is different so that the test will fail if the
bytes are swapped during encoding or decoding.
"""
data_bytes = [1, 2, 3]
for status_byte, spec in SPEC_BY_STATUS.items():
if status_byte == 0xf0:
msg_bytes = [0xf0] + data_bytes + [0xf7]
else:
msg_bytes = [status_byte] + data_bytes[:spec['length'] - 1]
assert encode_message(decode_message(msg_bytes)) == msg_bytes
mido-1.2.9/tests/messages/test_checks.py 0000664 0001750 0001750 00000001057 13355500373 020524 0 ustar olemb olemb 0000000 0000000 from pytest import raises
from mido.messages.checks import check_time
from mido.py2 import PY2
def test_check_time():
check_time(1)
check_time(1.5)
if PY2:
# long should be allowed. (It doesn't exist in Python3,
# so there's no need to check for it here.)
check_time(long('9829389283L')) # noqa: F821
with raises(TypeError):
check_time(None)
with raises(TypeError):
check_time('abc')
with raises(TypeError):
check_time(None)
with raises(TypeError):
check_time('abc')
mido-1.2.9/tests/messages/test_decode.py 0000664 0001750 0001750 00000001763 13355500373 020513 0 ustar olemb olemb 0000000 0000000 from pytest import raises
from mido.messages.decode import decode_message
def sysex(data):
"""Make sysex data."""
return [0xf0] + list(data) + [0xf7]
def test_sysex():
data = b'\xf0\x01\x02\x03\xf7'
msg = {'type': 'sysex', 'data': (1, 2, 3), 'time': 0}
assert decode_message(data) == msg
def test_channel():
assert decode_message(b'\x91\x00\x00')['channel'] == 1
def test_sysex_end():
with raises(ValueError):
decode_message(b'\xf0\x00\x12')
def test_zero_bytes():
with raises(ValueError):
decode_message(b'')
def test_too_few_bytes():
with raises(ValueError):
decode_message(b'\x90')
def test_too_many_bytes():
with raises(ValueError):
decode_message(b'\x90\x00\x00\x00')
def test_invalid_status():
with raises(ValueError):
decode_message(b'\x00')
def test_sysex_without_stop_byte():
with raises(ValueError):
decode_message([0xf0])
with raises(ValueError):
decode_message([0xf0, 0])
mido-1.2.9/tests/messages/test_strings.py 0000664 0001750 0001750 00000001362 13355500373 020754 0 ustar olemb olemb 0000000 0000000 from pytest import raises
from mido.messages import Message
def test_decode_sysex():
assert Message.from_str('sysex data=(1,2,3)').data == (1, 2, 3)
def test_decode_invalid_sysex_with_spaces():
with raises(ValueError):
Message.from_str('sysex data=(1, 2, 3)')
def test_encode_sysex():
assert str(Message('sysex', data=())) == 'sysex data=() time=0'
# This should not have an extra comma.
assert str(Message('sysex', data=(1,))) == 'sysex data=(1) time=0'
assert str(Message('sysex', data=(1, 2, 3))) == 'sysex data=(1,2,3) time=0'
def test_encode_long_int():
# Make sure Python 2 doesn't stick an 'L' at the end of a long
# integer.
assert 'L' not in str(Message('clock', time=1231421984983298432948))
mido-1.2.9/tests/test_frozen.py 0000664 0001750 0001750 00000001652 13355500373 016761 0 ustar olemb olemb 0000000 0000000 from mido.messages import Message
from mido.frozen import (is_frozen, freeze_message, thaw_message,
FrozenMessage, FrozenMetaMessage,
FrozenUnknownMetaMessage)
def test_hashability():
"""Test that messages are hashable."""
hash(FrozenMessage('note_on'))
# List is converted to tuple.
hash(FrozenMessage('sysex', data=[1, 2, 3]))
hash(FrozenMetaMessage('track_name', name='Some track'))
hash(FrozenUnknownMetaMessage(123, [1, 2, 3]))
def test_freeze_and_thaw():
"""Test that messages are hashable."""
assert not is_frozen(thaw_message(freeze_message(Message('note_on'))))
def test_thawed_message_is_copy():
frozen_msg = FrozenMessage('note_on')
thawed_msg = Message('note_on')
assert thaw_message(frozen_msg) == thawed_msg
def test_is_frozen():
assert is_frozen(FrozenMessage('note_on'))
assert not is_frozen(Message('note_on'))
mido-1.2.9/tests/test_ports.py 0000664 0001750 0001750 00000004170 13355500373 016623 0 ustar olemb olemb 0000000 0000000 import pytest
from mido.messages import Message
from mido.ports import BaseIOPort
class TestIOPort:
class Port(BaseIOPort):
def _open(self):
self.close_called = False
def _send(self, message):
self._messages.append(message)
def _close(self):
self.close_called = True
@pytest.fixture
def port(self):
with self.Port('Name') as p:
yield p
def test_basic(self, port):
assert port.name == 'Name'
assert not port.closed
assert port._messages is port._parser.messages
with pytest.raises(TypeError):
port.send('not a message')
def test_recv_non_blocking(self, port):
message = Message('note_on')
port.send(message)
port.poll()
assert port.poll() is None
def test_send_message(self, port):
message = Message('note_on')
port.send(message)
port.send(message)
def test_port_close(self, port):
port.close()
assert port.close_called
port.close_called = False
port.close()
assert port.closed
assert not port.close_called
def test_mido_port_non_blocking_recv(self, port):
assert port.receive(block=False) is None
def test_close_inside_iteration():
# This type of port can close when it runs out of messages.
# (And example of this is socket ports.)
#
# Iteration should then stop after all messages in the
# internal queue have been received.
message = Message('note_on')
class Port(BaseIOPort):
def __init__(self, messages):
BaseIOPort.__init__(self)
# Simulate some messages that arrived
# earlier.
self._messages.extend(messages)
self.closed = False
def _receive(self, block=True):
# Oops, the other end hung up.
if self._messages:
return self._messages.popleft()
else:
self.close()
return None
message = Message('note_on')
with Port([message, message]) as port:
assert len(list(port)) == 2
mido-1.2.9/tests/test_tokenizer.py 0000664 0001750 0001750 00000002755 13355500373 017475 0 ustar olemb olemb 0000000 0000000 from mido.tokenizer import Tokenizer
def tokenize(midi_bytes):
return list(Tokenizer(midi_bytes))
def test_channel_message():
assert tokenize([0x90, 1, 2]) == [[0x90, 1, 2]]
def test_sysex():
assert tokenize([0xf0, 1, 2, 3, 0xf7]) == [[0xf0, 1, 2, 3, 0xf7]]
def test_empty_sysex():
assert tokenize([0xf0, 0xf7]) == [[0xf0, 0xf7]]
def test_realtime():
assert tokenize([0xf8]) == [[0xf8]]
def test_double_realtime():
assert tokenize([0xf8, 0xf8]) == [[0xf8], [0xf8]]
def test_realtime_inside_message():
"""Realtime message inside message should reset the parser."""
assert tokenize([0x90, 1, 0xf8, 2]) == [[0xf8]]
def test_realtime_inside_sysex():
"""Realtime messages are allowed inside sysex.
The sysex messages should be delivered first.
This is the only case where a message is allowed inside another message.
"""
assert tokenize([0xf0, 1, 0xf8, 2, 0xf7]) == [[0xf8], [0xf0, 1, 2, 0xf7]]
assert tokenize([0xf0, 0xf8, 0xf7]) == [[0xf8], [0xf0, 0xf7]]
def test_message_inside_sysex():
"""Non-realtime message inside sysex should reset the parser."""
assert tokenize([0xf0, 0x90, 1, 2, 0xf7]) == [[0x90, 1, 2]]
def test_sysex_inside_sysex():
"""Sysex inside sysex should reset the parser."""
assert tokenize([0xf0, 1, 0xf0, 2, 0xf7]) == [[0xf0, 2, 0xf7]]
def test_stray_data_bytes():
"""Data bytes outside messages should be ignored."""
assert tokenize([0, 1, 0x90, 2, 3, 4, 5, 0xf8, 6]) == [[0x90, 2, 3], [0xf8]]
mido-1.2.9/tests/test_sockets.py 0000664 0001750 0001750 00000002444 13355500373 017131 0 ustar olemb olemb 0000000 0000000 import pytest
from mido.sockets import parse_address
class TestParseAddress:
@pytest.mark.parametrize('input_str, expected',
[(':8080', ('', 8080)),
('localhost:8080', ('localhost', 8080))
])
def test_parse_address_normal(self, input_str, expected):
assert parse_address(input_str) == expected
def test_too_many_colons_raises_value_error(self):
with pytest.raises(ValueError):
parse_address(':to_many_colons:8080')
def test_only_hostname_raises_value_error(self):
with pytest.raises(ValueError):
parse_address('only_hostname')
def test_empty_string_raises_value_error(self):
with pytest.raises(ValueError):
parse_address('')
def test_only_colon_raises_value_error(self):
with pytest.raises(ValueError):
parse_address(':')
def test_non_number_port_raises_value_error(self):
with pytest.raises(ValueError):
parse_address(':shoe')
def test_port_zero_raises_value_error(self):
with pytest.raises(ValueError):
parse_address(':0')
def test_out_of_range_port_raises_value_error(self):
with pytest.raises(ValueError):
parse_address(':65536')
mido-1.2.9/tox.ini 0000664 0001750 0001750 00000000331 13355500373 014207 0 ustar olemb olemb 0000000 0000000 [tox]
envlist = py27, py36
[testenv]
commands = pip install -q -e .[dev]
py.test . -rs -q
check-manifest -v
sphinx-build docs {envtmpdir} -q -E
flake8 mido --ignore=F401
mido-1.2.9/setup.py 0000775 0001750 0001750 00000003413 13355500373 014415 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
import os
import sys
here = os.path.abspath(os.path.dirname(__file__))
def get_about():
about = {}
path = os.path.join(here, 'mido', '__about__.py')
with open(path, 'rt') as aboutfile:
exec(aboutfile.read(), about)
return about
about = get_about()
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
if sys.argv[-1] == "publish":
os.system("python setup.py sdist upload")
sys.exit()
elif sys.argv[-1] == "docs":
os.system("sphinx-build docs docs/_build")
sys.exit()
setup(
name='mido',
version=about['__version__'],
description='MIDI Objects for Python',
long_description=open('README.rst', 'rt').read(),
author=about['__author__'],
author_email=about['__author_email__'],
url=about['__url__'],
license=about['__license__'],
package_data={'': ['LICENSE']},
package_dir={'mido': 'mido'},
packages=['mido', 'mido.backends'],
scripts=['bin/mido-play',
'bin/mido-ports',
'bin/mido-serve',
'bin/mido-connect'],
include_package_data=True,
install_requires=[],
extras_require={
'dev': ['check-manifest>=0.35',
'flake8>=3.4.1',
'pytest>=3.2.2',
'sphinx>=1.6.3',
'tox>=2.8.2'
],
'ports': ['python-rtmidi>=1.1.0']
},
zip_safe=False,
classifiers=(
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Natural Language :: English',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.2',
),
)
mido-1.2.9/docs/ 0000775 0001750 0001750 00000000000 13355643235 013634 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/docs/_static/ 0000775 0001750 0001750 00000000000 13355643235 015262 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/docs/_static/PLACEHOLDER 0000664 0001750 0001750 00000000070 13167442216 016721 0 ustar olemb olemb 0000000 0000000 This file is needed for git to include this directory.
mido-1.2.9/docs/implementing_ports.rst 0000664 0001750 0001750 00000014605 13337034031 020277 0 ustar olemb olemb 0000000 0000000 Writing a New Port
==================
The Mido port API allows you to write new ports to do practically
anything.
A new port type can be defined by subclassing one of the base classes
and overriding one or more methods. Here's an example::
from mido.ports import BaseOutput
class PrintPort(BaseOutput):
def _send(message):
print(message)
>>> port = PrintPort()
>>> port.send(msg)
note_on channel=0 note=0 velocity=64 time=0
``_send()`` will be called by ``send()``, and is responsible for
actually sending the message somewhere (or in this case print it out).
Overridable Methods
-------------------
There are four overridable methods (all of them default to doing
nothing)::
``_open(self, **kwargs)``
Should do whatever is necessary to initialize the port (for
example opening a MIDI device.)
Called by ``__init__()``. The ``name`` attribute is already
set when ``_open()`` is called, but you will get the rest of
the keyword arguments.
If your port takes a different set of arguments or has other
special needs, you can override ``__init__()`` instead.
``_close(self)``
Should clean up whatever resources the port has allocated (such as
closing a MIDI device).
Called by ``close()`` if the port is not already closed.
``_send(self, message)``
(Output ports only.)
Should send the message (or do whatever else that makes sense).
Called by ``send()`` if the port is open and the message is a Mido
message. (You don't need any type checking here.)
Raise IOError if something goes wrong.
``_receive(self, block=True)``
(Input ports only.)
Should return a message if there is one available.
If ``block=True`` it should block until a message is available and
then return it.
If ``block=False`` it should return a message or ``None`` if there
is no message yet. If you return ``None`` the enclosing
``pending()`` method will check ``self._messages`` and return one
from there.
.. note:: ``Prior to 1.2.0 ``_receive()`` would put messages in
``self._messages`` (usually via the parser) and rely on
``receive()`` to return them to the user.
Since this was not thread safe the API was changed in
1.2.0 to allow the ``_receive()`` to return a
message. The old behavior is still supported, so old
code will work as before.
Raise IOError if something goes wrong.
Each method corresponds to the public method of the same name, and
will be called by that method. The outer method will take care of many
things, so the inner method only needs to do the very minimum. The
outer method also provides the doc string, so you don't have to worry
about that.
The base classes are ``BaseInput``, ``BaseOutput`` and ``BaseIOPort``
(which is a subclass of the other two.)
Locking
-------
The calls to ``_receive()`` and ``_send()`` will are protected by a
lock, ``left.lock``. As a result all send and receive will be thread
safe.
.. note:: If your ``_receive()`` function actually blocks instead of
letting the parent class handle it ``poll()`` will not
work. The two functions are protected by the same lock, so
when ``receive()`` blocks it will also block other threads
calling ``poll()``. In this case you need to implement your
own locking.
If you want to implement your own thread safety you can set the
``_locking`` attribute in your class::
class MyInput(ports.BaseInput):
_locking = False
...
An example of this is ``mido.backends.rtmidi`` where the callback is
used to feed an internal queue that ``receive()`` reads from.
Examples
--------
An full example of a device port for the imaginary MIDI library
``fjopp``::
import fjopp
from mido.ports import BaseIOPort
# This defines an I/O port.
class FjoppPort(BaseIOPort):
def _open(self, **kwargs):
self._device = fjopp.open_device(self.name)
def _close(self):
self._device.close()
def _send(self, message):
self.device.write(message.bytes())
def _receive(self, block=True):
while True:
data = self.device.read()
if data:
self._parser.feed(data)
else:
return
If ``fjopp`` supports blocking read, you can do this to actually block
on the device instead of letting ``receive()`` and friends poll and
wait for you::
def _receive(self, block=True):
if block:
# Actually block on the device.
# (``read_blocking()`` will always return some data.)
while not ``self._messages``:
data = self._device.read_blocking()
self._parser.feed(data)
else:
# Non-blocking read like above.
while True:
data = self.device.read()
if data:
self._parser.feed(data)
This can be used for any kind of port that wants to block on a pipe,
an socket or another input source. Note that Mido will still use
polling and waiting when receiving from multiple ports (for example in
a ``MultiPort``).
If you want separate input and output classes, but the ``_open()`` and
``_close()`` methods have a lot in common, you can implement this
using a mix-in.
Sometimes it's useful to know inside the methods whether the port
supports input or output. The way to do this is to check for the
methods ```send()`` and ``receive()``, for example::
def _open(self, **kwargs):
if hasattr(self, 'send'):
# This is an output port.
if hasattr(self, 'receive'):
# This is an input port.
if hasattr(self, 'send') and hasattr(self, 'receive'):
# This is an I/O port.
Attributes
----------
A port has some attributes that can be useful inside your methods.
``name``
The name of the port. The value is device specific and does not
have to be unique. It can have any value, but must be a string or
``None``.
This is set by ``__init__()``.
``closed``
True if the port is closed. You don't have to worry about this
inside your methods.
``_messages``
This is a ``collections.deque`` of messages that have been read
and are ready to be received. This is a shortcut to
``_parser.messages``.
``_device_type`` (Optional.)
If this attribute exists, it's a string which will be used in
``__repr__()``. If it doesn't exist, the class name will be used
instead.
mido-1.2.9/docs/ports.rst 0000664 0001750 0001750 00000016273 13355500373 015541 0 ustar olemb olemb 0000000 0000000 Ports
=====
A Mido port is an object that can send or receive messages (or both).
You can open a port by calling one of the open methods, for example::
>>> inport = mido.open_input('SH-201')
>>> outport = mido.open_output('Integra-7')
Now you can receive messages on the input port and send messages on
the output port::
>>> msg = inport.receive()
>>> outport.send(msg)
The message is copied by ``send()``, so you can safely modify your
original message without causing breakage in other parts of the
system.
In this case, the ports are device ports, and are connected to some
sort of (physical or virtual) MIDI device, but a port can be
anything. For example, you can use a ``MultiPort`` receive messages
from multiple ports as if they were one::
from mido.ports import MultiPort
...
multi = MultiPort([inport1, inport2, inport3])
for msg in multi:
print(msg)
This will receive messages from all ports and print them out. Another
example is a socket port, which is a wrapper around a TCP/IP socket.
No matter how the port is implemented internally or what it does, it
will look and behave like any other Mido port, so all kinds of ports
can be used interchangeably.
.. note:: Sending and receiving messages is thread safe. Opening and
closing ports and listing port names are not.
Common Things
-------------
How to open a port depends on the port type. Device ports (PortMidi,
RtMidi and others defined in backends) are opened with the open
functions, for example::
port = mido.open_output()
Input and I/O ports (which support both input and output) are opened
with ``open_input()`` and ``open_ioport()`` respectively. If you call
these without a port name like above, you will get the (system
specific) default port. You can override this by setting the
``MIDO_DEFAULT_OUTPUT`` etc. environment variables.
To get a list of available ports, you can do::
>>> mido.get_output_names()
['SH-201', 'Integra-7']
and then::
>>> port = mido.open_output('Integra-7')
There are corresponding function for input and I/O ports.
To learn how to open other kinds of ports, see the documentation for
the port type in question.
The port name is available in ``port.name``.
To close a port, call::
port.close()
or use the ``with`` statement to have the port closed automatically::
with mido.open_input() as port:
for message in port:
do_something_with(message)
You can check if the port is closed with::
if port.closed:
print("Yup, it's closed.")
If the port is already closed, calling ``close()`` will simply do nothing.
Output Ports
------------
Output ports basically have only one method::
outport.send(message)
This will send the message immediately. (Well, the port can choose to
do whatever it wants with the message, but at least it's sent.)
There are also a couple of utility methods::
outport.reset()
This will send "all notes off" and "reset all controllers" on every
channel. This is used to reset everything to the default state, for
example after playing back a song or messing around with controllers.
If you pass ``autoreset=True`` to the constructor, ``reset()`` will be
called when the port closes::
with mido.open_output('Integra-7') as outport:
for msg in inport:
outport.send(msg)
# reset() is called here
outport.close() # or here
Sometimes notes hang because a ``note_off`` has not been sent. To
(abruptly) stop all sounding notes, you can call::
outport.panic()
This will not reset controllers. Unlike ``reset()``, the notes will
not be turned off gracefully, but will stop immediately with no regard
to decay time.
Input Ports
-----------
To iterate over incoming messages:::
for msg in port:
print(msg)
This will iterate over messages as they arrive on the port until the
port closes. (So far only socket ports actually close by
themselves. This happens if the other end disconnects.)
You can also do non-blocking iteration::
for msg in port.iter_pending():
print(msg)
This will iterate over all messages that have already arrived. It is
typically used in main loops where you want to do something else while
you wait for messages::
while True:
for msg in port.iter_pending():
print(msg)
do_other_stuff()
In an event based system like a GUI where you don't write the main
loop you can install a handler that's called periodically. Here's an
example for GTK::
def callback(self):
for msg in self.inport:
print(msg)
gobject.timeout_add_seconds(timeout, callback)
To get a bit more control you can receive messages one at a time::
msg = port.receive()
This will block until a message arrives. To get a message only if one
is available, you can use `poll()`::
msg = port.poll()
This will return ``None`` if no message is available.
.. note:: There used to be a ``pending()`` method which returned the
number of pending messages. It was removed in 1.2.0 for
three reasons:
* with ``poll()`` and ``iter_pending()`` it is no longer
necessary
* it was unreliable when multithreading and for some ports
it doesn't even make sense
* it made the internal method API confusing. `_send()` sends
a message so `_receive()` should receive a message.
Callbacks
---------
Instead of reading from the port you can install a callback function
which will be called for every message that arrives.
Here's a simple callback function::
def print_message(message):
print(message)
To install the callback you can either pass it when you create the
port or later by setting the ``callback`` attribute::
port = mido.open_input(callback=print_message)
port.callback = print_message
...
port.callback = another_function
.. note::
Since the callback runs in a different thread you may need to use
locks or other synchronization mechanisms to keep your main program and
the callback from stepping on each other's toes.
Calling ``receive()``, ``__iter__()``, or ``iter_pending()`` on a port
with a callback will raise an exception::
ValueError: a callback is set for this port
To clear the callback::
port.callback = None
This will return the port to normal.
Port API
--------
Common Methods and Attributes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``close()``
Close the port. If the port is already closed this will simply do
nothing.
``name``
Name of the port or None.
``closed``
True if the port is closed.
Output Port Methods
^^^^^^^^^^^^^^^^^^^
``send(message)``
Send a message.
``reset()``
Sends "all notes off" and "reset all controllers on all channels.
``panic()``
Sends "all sounds off" on all channels. This will abruptly end all
sounding notes.
Input Port Methods
^^^^^^^^^^^^^^^^^^
``receive(block=True)``
Receive a message. This will block until it returns a message. If
``block=True`` is passed it will instead return ``None`` if there is
no message.
``poll()``
Returns a message, or ``None`` if there are no pending messages.
``iter_pending()``
Iterates through pending messages.
``__iter__()``
Iterates through messages as they arrive on the port until the port
closes.
mido-1.2.9/docs/backends/ 0000775 0001750 0001750 00000000000 13355643235 015406 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/docs/backends/amidi.rst 0000664 0001750 0001750 00000001766 13337034031 017221 0 ustar olemb olemb 0000000 0000000 amidi (Experimental)
--------------------
Name: ``mido.backends.amidi``
Features:
* Linux only.
* very basic implementation.
* no callbacks
* can only access physical ports. (Devices that are plugged in.)
* high overhead when sending since it runs the ``amidi`` command for
each messages.
* known bug: is one behind when receiving messages. See below.
The ``amidi`` command (a part of ALSA) is used for I/O::
* ``amidi -l`` to list messages (in ``get_input_names()`` etc.)
* ``amidi -d -p DEVICE`` to receive messages. ``amidi`` prints these
out one on each line as hex bytes. Unfortunately it puts the newline
at the beginning of the line which flushes the buffer before the
message instead of after. This causes problems with non-blocking
receiption using ``select.poll()`` which means messages are received
one behind. This needs to be looked into.
* ``amidi --send-hex MESSAGE_IN_HEX -p DEVICE`` to send
messages. Since this is called for every messages the overhead is
very high.
mido-1.2.9/docs/backends/rtmidi.rst 0000664 0001750 0001750 00000007063 13337034031 017422 0 ustar olemb olemb 0000000 0000000 RtMidi (Default, Recommended)
-----------------------------
Name: ``mido.backends.rtmidi``
The RtMidi backend is a thin wrapper around `python-rtmidi
`_
Features:
* callbacks
* true blocking ``receive()`` in Python 3 (using a callback and a
queue)
* virtual ports
* ports can be opened multiple times, each will receive a copy of each message
* client name can be specified when opening a virtual port
* sends but doesn't receive active sensing
* port list is always up to date
* all methods but ``close()`` are thread safe
Port Names (Linux/ALSA
^^^^^^^^^^^^^^^^^^^^^^
When you're using Linux/ALSA the port names include client name and
ALSA client and port numbers, for example:
.. code-block:: python
>>> mido.get_output_names()
['TiMidity:TiMidity port 0 128:0']
The ALSA client and port numbers ("128:0" in this case) can change
from session to session, making it hard to hard code port names or use
them in config files.
To get around this the RtMidi backend allows you to leave out the the
port number of port number and client names. These lines will all open
the port above:
.. code-block:: python
mido.open_output('TiMidity port 0')
.. code-block:: python
mido.open_output('TiMidity:TiMidity port 0')
.. code-block:: python
mido.open_output('TiMidity:TiMidity port 0 128:0')
There is currently no way to list ports without port number or client
name. This can be added in a future version of there is demand for it
and a suitable API is found.
Virtual Ports
^^^^^^^^^^^^^
RtMidi is the only backend that can create virtual ports:
.. code-block:: python
>>> port = mido.open_input('New Port', virtual=True)
>>> port
Other applications can now connect to this port. (One oddity is that,
at least in Linux, RtMidi can't see its own virtual ports, while
PortMidi can see them.)
Client Name
^^^^^^^^^^^
You can specify a client name for the port: (New in 1.2.0.)
.. code-block:: python
>>> port = mido.open_input('New Port', client_name='My Client')
This requires python-rtmidi >= 1.0rc1. If ``client_name`` is passed
the port will be a virtal port.
.. note:: Unfortunately, at least with ALSA, opening two ports with
the same ``client_name`` creates two clients with the same
name instead of one client with two ports.
There are a couple of problems with port names in Linux. First, RtMidi
can't see some software ports such as ``amSynth MIDI IN``. PortMidi
uses the same ALSA sequencer API, so this is problem in RtMidi.
Second, in some versions of RtMidi ports are named inconsistently. For
example, the input port 'Midi Through 14:0' has a corresponding output
named 'Midi Through:0'. Unless this was intended, it is a bug in
RtMidi's ALSA implementation.
Choosing API
^^^^^^^^^^^^
The RtMidi library can be compiled with support for more than one
API. You can select API by adding it after the module name, either in
the environment variable::
$ export MIDO_BACKEND=mido.backends.rtmidi/LINUX_ALSA
$ export MIDO_BACKEND=mido.backends.rtmidi/UNIX_JACK
or in one of these::
>>> mido.set_backend('mido.backends.rtmidi/LINUX_ALSA')
>>> mido.backend
>>> mido.Backend('mido.backends.rtmidi/UNIX_JACK')
This allows you to, for example, use both ALSA and JACK ports in the
same program.
To get a list of available APIs::
>>> mido.backend.module.get_api_names()
['LINUX_ALSA', 'UNIX_JACK']
mido-1.2.9/docs/backends/rtmidi_python.rst 0000664 0001750 0001750 00000000761 13337034031 021021 0 ustar olemb olemb 0000000 0000000 rtmidi_python
-------------
Name: ``mido.backends.rtmidi_python``
Installing
^^^^^^^^^^
::
pip install rtmidi-python
Features
^^^^^^^^
* uses the ``rtmidi_python`` package rather than ``python_rtmidi``
* supports callbacks
* limited support for virtual ports (no client name)
* no true blocking
* sends but doesn't receive active sensing
Since the API of ``rtmidi_python`` and ``python_rtmidi`` are almost
identical it would make sense to refactor so they share most of the
code.
mido-1.2.9/docs/backends/pygame.rst 0000664 0001750 0001750 00000000337 13337034031 017411 0 ustar olemb olemb 0000000 0000000 Pygame
------
Name: ``mido.backends.pygame``
The Pygame backend uses ``pygame.midi`` for I/O.
Doesn't receive ``active_sensing``.
Callbacks are currently not implemented.
Pygame.midi is implemented on top of PortMidi.
mido-1.2.9/docs/backends/index.rst 0000664 0001750 0001750 00000004266 13337034031 017243 0 ustar olemb olemb 0000000 0000000 Backends
========
.. toctree::
:maxdepth: 1
rtmidi
portmidi
pygame
rtmidi_python
amidi
Choosing a Backend
------------------
Mido comes with five backends:
* :doc:`RtMidi ` is the recommended backends. It has all the
features of the other ones and more and is usually easier to
install.
* :doc:`PortMidi ` was the default backend up until 1.2. It
uses the ``portmidi`` shared library and can be difficult to install
on some systems.
* :doc:`Pygame ` uses the ``pygame.midi``.
* :doc:`rtmidi-python ` uses the ``rtmidi_python`` package, an
alternative wrapper for PortMidi. It is currently very basic but
easier to install on some Windows systems.
* :doc:`Amidi ` is an experimental backend for Linux/ALSA
that uses the command ``amidi`` to send and receive messages.
If you want to use another than the RtMidi you can override this with
the ``MIDO_BACKEND`` environment variable, for example::
$ MIDO_BACKEND=mido.backends.portmidi ./program.py
Alternatively, you can set the backend from within your program::
>>> mido.set_backend('mido.backends.portmidi')
>>> mido.backend
This will override the environment variable.
If you want to use more than one backend at a time, you can do::
rtmidi = mido.Backend('mido.backends.rtmidi')
portmidi = mido.Backend('mido.backends.portmidi')
input = rtmidi.open_input()
output = portmidi.open_output()
for message in input:
output.send(message)
The backend will not be loaded until you call one of the ``open_`` or
``get_`` methods. You can pass ``load=True`` to have it loaded right
away.
If you pass ``use_environ=True`` the module will use the environment
variables ``MIDO_DEFAULT_INPUT`` etc. for default ports.
Environment Variables
---------------------
You can override the backend's choice of default ports with these
three environment variables::
MIDO_DEFAULT_INPUT
MIDO_DEFAULT_OUTPUT
MIDO_DEFAULT_IOPORT
For example::
$ MIDO_DEFAULT_INPUT='SH-201' python program.py
or::
$ export MIDO_DEFAULT_OUTPUT='Integra-7'
$ python program1.py
$ python program2.py
mido-1.2.9/docs/backends/portmidi.rst 0000664 0001750 0001750 00000001630 13337034031 017753 0 ustar olemb olemb 0000000 0000000 PortMidi
--------
Name: ``mido.backends.portmidi``
Installing
^^^^^^^^^^
The PortMidi backend requires the ``portmidi`` shared library.
`Ubuntu `_::
apt install libportmidi-dev
`Homebrew `_::
brew install portmidi
`MacPorts `_::
port install portmidi
The backend will look for::
portmidi.so (Linux)
portmidi.dll (Windows)
portmidi.dynlib (macOS)
Features
^^^^^^^^
Can send but doesn't receive ``active_sensing`` messages.
PortMidi has no callback mechanism, so callbacks are implemented in
Python with threads. Each port with a callback has a dedicated thread
doing blocking reads from the device.
Due to limitations in PortMidi the port list will not be up-to-date if
there are any ports open. (The refresh is implemented by
re-initalizing PortMidi which would break any open ports.)
mido-1.2.9/docs/conf.py 0000664 0001750 0001750 00000017305 13337033714 015134 0 ustar olemb olemb 0000000 0000000 # -*- coding: utf-8 -*-
#
# Mido documentation build configuration file, created by
# sphinx-quickstart on Wed Jun 26 16:58:08 2013.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..'))
import mido
from mido import __version__
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.ifconfig']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Mido'
copyright = u'Ole Martin Bjørndalen'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = __version__
# The full version, including alpha/beta/rc tags.
release = __version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'Midodoc'
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Mido.tex', u'Mido Documentation',
u'Ole Martin Bjørndalen', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'mido', u'Mido Documentation',
[u'Ole Martin Bjørndalen'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Mido', u'Mido Documentation',
u'Ole Martin Bjørndalen', 'Mido', 'MIDI Objects for Python',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
mido-1.2.9/docs/acknowledgements.rst 0000664 0001750 0001750 00000000357 13355500373 017720 0 ustar olemb olemb 0000000 0000000 Acknowledgments
===============
Thanks to /u/tialpoy/ on Reddit for extensive code review and helpful
suggestions.
Thanks to everyone who has sent bug reports and patches.
The PortMidi wrapper is based on portmidizero by Grant Yoshida.
mido-1.2.9/docs/messages.rst 0000664 0001750 0001750 00000007776 13337034031 016202 0 ustar olemb olemb 0000000 0000000 Messages
========
A Mido message is a Python object with methods and attributes. The
attributes will vary depending on message type.
To create a new message::
>>> mido.Message('note_on')
You can pass attributes as keyword arguments::
>>> mido.Message('note_on', note=100, velocity=3, time=6.2)
All attributes will default to 0. The exceptions are ``velocity``,
which defaults to 64 (middle velocity) and ``data`` which defaults to
``()``.
You can set and get attributes as you would expect::
>>> msg = mido.Message('note_on')
>>> msg.note
0
The ``type`` attribute can be used to determine message type::
>>> msg.type
'note_on'
Attributes are also settable but it's always better to use
``msg.copy()``::
>>> msg.copy(note=99, time=100.0)
.. note:: Mido always makes a copy of messages instead of modifying
them so if you do the same you have immutable messages in
practice. (Third party libraries may not follow the same
rule.)
.. note:: :doc:`/frozen_messages` are a variant of messages that are
hashable and can be used as dictionary keys. They are also
safe from tampering by third party libraries. You can freely
convert between the two and use frozen messages wherever
normal messages are allowed.
Mido supports all message types defined by the MIDI standard. For a
full list of messages and their attributes, see :doc:`/message_types`.
Converting To Bytes
-------------------
You can convert a message to MIDI bytes with one of these methods:
>>> msg = mido.Message('note_on')
>>> msg
>>> msg.bytes()
[144, 0, 64]
>>> msg.bin()
bytearray(b'\x90\x00@')
>>> msg.hex()
'90 00 40'
Converting From Bytes
---------------------
You can turn bytes back into messages with the :doc:`/parser `.
You can also create a message from bytes using class methods (new in
1.2):
.. code-block:: python
msg1 = mido.Message.from_bytes([0x90, 0x40, 0x60])
msg2 = mido.Message.from_hex('90, 40 60')
The bytes must contain exactly one complete message. If not
``ValueError`` is raised.
The Time Attribute
------------------
Each message has a ``time`` attribute, which can be set to any value
of type ``int`` or ``float`` (and in Python 2 also ``long``). What you
do with this value is entirely up to you.
Some parts of Mido use the attribute for special purposes. In MIDI
file tracks, it is used as delta time (in ticks).
.. note:: Before 1.1.18 the ``time`` attribute was not included in
comparisons. If you want the old behavior the easies way is
``msg1.bytes()`` == ``msg2.bytes()``.
To sort messages on time you can do::
messages.sort(key=lambda message: message.time)
or::
import operator
messages.sort(key=operator.attrgetter('time'))
System Exclusive Messages
-------------------------
System Exclusive (SysEx) messages are used to send device specific
data. The ``data`` attribute is a tuple of data bytes which serves as
the payload of the message::
>>> msg = Message('sysex', data=[1, 2, 3])
>>> msg
>>> msg.hex()
'F0 01 02 03 F7'
You can also extend the existing data::
>>> msg = Message('sysex', data=[1, 2, 3])
>>> msg.data += [4, 5]
>>> msg.data += [6, 7, 8]
>>> msg
Any sequence of integers is allowed, and type and range checking is
applied to each data byte. These are all valid::
(65, 66, 67)
[65, 66, 67]
(i + 65 for i in range(3))
(ord(c) for c in 'ABC')
bytearray(b'ABC')
b'ABC' # Python 3 only.
For example::
>>> msg = Message('sysex', data=bytearray(b'ABC'))
>>> msg.data += bytearray(b'DEF')
>>> msg
mido-1.2.9/docs/roadmap.rst 0000664 0001750 0001750 00000013746 13355500373 016017 0 ustar olemb olemb 0000000 0000000 Roadmap
=======
This will be developed into a proper roadmap but for now it's more of
a list of ideas.
Near Future
-----------
* create a place for general discussion:
Now exists: https://groups.google.com/forum/#!forum/mido-community
* a `PEP `_ like process for new
features and major changes?
Various Improvements to MIDI File Parsing
-----------------------------------------
* add ``mido.exceptions.MidiParseError``.
* add better error handling to MIDI file parser as discussed in `issue
#63 `_.
* support RIFF MIDI files (`issue #43
`_)
* support MIDI files that end in empty meta message
(`issue #42 `_)
Better Support for Concurrency and Multithreading
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Mido was not originally designed for multithreading. Locks were added
to the port base classes as an attempt to get around this but it is a
crude solution that has created numerous problems and headaches.
The RtMido backend has abandoned locking in favor of using RtMidi's
user callback to feed a queue. If you write your port so that
``send()``, ``receive()`` and ``poll()`` are thread safe the rest of
the rest of the API will be as well.
For ports that do actual I/O (MIDI devices, sockets, files, pipes
etc.) it is always best for the port itself to ensure thread
safety. It's less clear what to do for utility ports like
``MultiPort``.
Mido is currently not very good at multiplexing input. You can use
``MultiPort`` and ``multi_receive()``, but since it can't actually
block on more than one port it uses poll and wait. This uses more
resources and adds latency.
The alternative is to use callbacks, but only a few backends support
(and some like PortMidi fake them with a thread that polls and waits,
taking you back to square one). Programming with callbacks also forces
you to deal with multithreading (since the callback runs in a
different thread) and to break your program flow up into callback
handlers. This is not always desirable.
In Go the solution would be to use channels instead of ports. Each
port would then have its own channel with a goroutine reading from the
device and feeding the channel, and the language would take care of
the multiplexing. I am not sure how one would achieve something like
this in Python.
Making Messages Immutable
^^^^^^^^^^^^^^^^^^^^^^^^^
See: https://github.com/olemb/mido/issues/36
The current workaround is frozen messages (``mido.freeze``).
In any case, the documentation should be updated to encourage copying
over mutation.
Native Backends (ALSA, JACK, CoreMIDI, Windows MIDI)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
See https://github.com/olemb/mido-native-backends
No Default Backend?
^^^^^^^^^^^^^^^^^^^
Currently one backend is chosen as the default. Perhaps it would be
better to require the user to specify the backend with
``$MIDO_BACKEND`` or ``mido.set_backend()``.
New API for Creating New Port Types
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The current system uses multiple inheritance making the code very hard
to follow and reason about:
* too much magic and too much you need to keep in your head
* attributes like ``self.name`` and ``self.closed`` appear in your
name space and you have to dig around in the base classes to see
where they come from
* there is a ``self._parser`` in every port even if you don't need it
* ``self._parser`` is not thread safe
* ``BaseInput.receive()`` needs to account for all the numerous
current and historical behaviors of ``self._receive()``.
* blocking and nonblocking receive can not be protected by the same
lock (since a call to ``receive()`` would then block a call to
``poll()``), which means ports that due true blocking need to do
their own locking.
* if you want to do our own locking you need to remember to set
``_locking=False``. This will replace the lock with a dummy lock,
which while doing nothing still adds a bit of overhead.
A good principle is for any part of the code to know as little as
possible about the rest of the code. For example a backend port should
only need to worry about:
* opening and closing the device
* reading and writing data (blocking and nonblocking)
It should not have to worry about things like ``autoreset``,
``closed=True/False`` and iteration. Also, as long as its ``send()``
and ``receive()/poll()`` methods are thread safe the rest of the API
will be as well.
Some alternatives to subclassing:
* embedding: write a basic port and wrap it in a ``FancyPort`` which
provides the rest of the API
* mixins: write a basic port and use mixins (``PortMethods``,
``InputMethods``, ``OutputMethods``) to import the rest of the API.
Maybe
-----
* add a way to convert between MIDI file types as suggested in `issue
#92 `_.
* RtMidi backend: allow user to list ports without client name and
ALSA port numbers.
* Add native backends? See https://github.com/olemb/mido-native-backends
* Currently all backends ignore ``active_sensing`` messages because
they create a lot of noise and are usually not very useful. Should
this be changed (perhaps as an option)?
Filtering can be turned off with:
* rtmidi: ``self._rt.ignore_types(False, False, False)``
* portmidi: ``pm.lib.Pa_SetFilter(self._stream, 0)``
* rtmidi_python: ``self._rt.ignore_types(False, False, False)``
* pygame: (is there a way to configure this?)
* amidi: (not sure if this receives ``active_sensing`` already)
* Refactor ``rtmidi`` and ``rtmidi_python`` backends to avoid code
duplication. This would give ``rtmidi_python`` all of the features
of ``rtmidi`` (as long as they are supported in the package).
* Add more fine grained error types, for example ``PortNotFound``
instead of just ``IOError``. (This should be a subclass so old code
still works.) One problem here is that new code that uses
``PortNotFound`` would not work with backends that raise ``IOError``.
mido-1.2.9/docs/socket_ports.rst 0000664 0001750 0001750 00000007421 13337034031 017075 0 ustar olemb olemb 0000000 0000000 Socket Ports - MIDI over TCP/IP
===============================
About Socket Ports
------------------
Socket ports allow you to send MIDI messages over a computer
network.
The protocol is standard MIDI bytes over a TCP stream.
Caveats
-------
The data is sent over an unencrypted channel. Also, the default server
allows connections from any host and also accepts arbitrary sysex
messages, which could allow anyone to for example overwrite patches on
your synths (or worse). Use only on trusted networks.
If you need more security, you can build a custom server with a white
list of clients that are allowed to connect.
If timing is critical, latency and jitter (especially on wireless
networks) may make socket ports unusable.
Sending Messages to a Server
----------------------------
First, let's import some things::
from mido.sockets import PortServer, connect
After that, a simple server is only two lines::
for message in PortServer('localhost', 8080):
print(message)
You can then connect to the server and send it messages::
output = connect('localhost', 8080):
output.send(message)
Each end of the connection behaves like a normal Mido I/O port, with
all the usual methods.
The host may be a DNS host name or IP address (as a string). It may
also be '', in which case connections are accepted on any ip address
on the computer.
Turning Things on their Head
----------------------------
If you want the server to send messages the client, you can instead
do::
server = PortServer('localhost', 8080):
while True:
server.send(message)
...
and then on the client side::
for message in connect('localhost', 8080):
print(message)
The client will now print any message that the server sends. Each
message that the server sends will be received by all connected
clients.
Under the Hood
--------------
The examples above use the server and client ports as normal I/O
ports. This makes it easy to write simple servers, but you don't have
any control connections and the way messages are sent and received.
To get more control, you can ignore all the other methods of the
``PortServer`` object and use only ``accept()``. Here's a simple
server implemented this way::
with PortServer('localhost', 8080) as server:
while True:
client = server.accept()
for message in client:
print(message)
``accept()`` waits for a client to connect, and returns a SocketPort
object which is connected to the SocketPort object returned by
``connect()`` at the other end.
The server above has one weakness: it allows only one connection at a
time. You can get around this by using ``accept(block=False)``. This
will return a SocketPort if there is a connection waiting and None if
there is connection yet.
Using this, you can write the server any way you like, for example::
with PortServer('localhost', 8080) as server:
clients = []
while True:
# Handle connections.
client = server.accept(block=False)
if client:
print('Connection from {}'.format(client.name))
clients.append(client)
for i, client in reversed(enumerate(clients)):
if client.closed:
print('{} disconnected'.format(client.name))
del clients[i]
# Receive messages.
for client in clients:
for message in client.iter_pending()
print('Received {} from {}'.format(message, client))
# Do other things
...
Possible Future Additions
-------------------------
Optional HTTP-style headers could be added. As long as these are 7-bit
ASCII, they will be counted as data bytes and ignored by clients or
servers who don't expect them.
mido-1.2.9/docs/about_midi.rst 0000664 0001750 0001750 00000005622 13337034031 016473 0 ustar olemb olemb 0000000 0000000 About MIDI
==========
A Short Introduction To MIDI
----------------------------
MIDI is a simple binary protocol for communicating with synthesizers
and other electronic music equipment.
It was developed in 1981 by Dave Smith and Chet Wood of Sequential
Systems. MIDI was quickly embraced by all the major synth
manufacturers and led to developments such as microcomputer
sequencers, and with them the electronic home studio. Although many
attempts have been made to replace it, it is still the industry
standard.
MIDI was designed for the 8-bit micro controllers found in synthesizers
at the beginning of the 80's. As such, it is a very minimal
byte-oriented protocol. The message for turning a note on is only
three bytes long (here shown in hexadecimal)::
92 3C 64
This message consists of::
92 -- 9 == message type note on
2 == channel 2
3C -- note 60 (middle C)
64 -- velocity (how hard the note is hit)
The first byte is called a status byte. It has the upper bit set,
which is how you can tell it apart from the following data
bytes. Data bytes are thus only 7 bits (0..127).
Each message type has a given number of data bytes, the exception
being the System Exclusive message which has a start and a stop byte
and any number of data bytes in-between these two::
F0 ... F7
Messages can be divided into four groups:
* Channel messages. These are used to turn notes on and off, to change
patches, and change controllers (pitch bend, modulation wheel, pedal
and many others). There are 16 channels, and the channel number is
encoded in the lower 4 bits of the status byte. Each synth can
choose which channel (or channels) it responds to. This can typically
be configured.
* System common messages.
* System real time messages, the include start, stop, continue, song
position (for playback of songs) and reset.
* System Exclusive messages (often called Sysex messages). These are
used for sending and receiving device specific such as patch data.
Some Examples of Messages
-------------------------
::
# Turn on middle C on channel 2:
92 3C 64
# Turn it back off:
82 3C 64
# Change to program (sound) number 4 on channel 2:
C2 04
# Continue (Starts a song that has been paused):
FB
# Sysex data request for the Roland SH-201 synthesizer:
F0 41 10 00 00 16 11 20 00 00 00 00 00 00 21 3F F7
Further Reading
---------------
* `An Introduction to MIDI `_
* `MIDI Basics `_ (by Yamaha)
* `Wikipedia's page on MIDI `_
* `MIDI Manufacturers Association `_
* `A full table of MIDI messages `_
* `Essentials of the MIDI protocol `_
mido-1.2.9/docs/syx.rst 0000664 0001750 0001750 00000001540 13355500373 015204 0 ustar olemb olemb 0000000 0000000 SYX Files
=========
SYX files are used to store SysEx messages, usually for patch
data.
Reading and Writing
-------------------
To read a SYX file::
messages = mido.read_syx_file('patch.syx')
To write a SYX file::
mido.write_syx_file('patch.syx', messages)
Non-sysex messages will be ignored.
Plain Text Format
-----------------
Mido also supports plain text SYX files. These are read in exactly the
same way::
messages = mido.read_syx_file('patch.txt')
``read_syx_file()`` determines which format the file is by looking at
the first byte. It Raises ValueError if file is plain text and byte
is not a 2-digit hex number.
To write plain text::
mido.write_syx_file('patch.txt', messages, plaintext=True)
This will write the messages as hex encoded bytes with one message per
line::
F0 00 01 5D 02 00 F7
F0 00 01 5D 03 00 F7
mido-1.2.9/docs/changes.rst 0000664 0001750 0001750 00000054323 13355642162 016003 0 ustar olemb olemb 0000000 0000000 Changes
=======
(See :doc:`roadmap` for future plans.)
Release History
---------------
1.2.9 (2018-10-05)
^^^^^^^^^^^^^^^^^^
* rewrote ``Parser`` class around a MIDI tokenizer. Should lead to
slight speedup and much cleaner code.
* bugfix: `data` attribute was missing for `UnknownMetaMessage`
objects. This caused `AttributeError` when the messages were printed
or saved to a file. Also, the documentation incorrectly listed the
attribute as `_data` instead of `data`. (Reported by Groowy.)
* bugfix: UnknownMetaMessage encoding was broken causing crashes when
saving a file with unknown meta messages. (Reported by exeex, issue
#159.)
* bugfix: inputs and outputs were switched around when opening named
ports with PortMidi backend. (Reproduced by Predrag Radovic, issue
#108, fix by Juan Antonio Aldea, pull request #109.)
* bugfix: time signature meta messages had wrong default value of
2/4. The default value is now 4/4. (Fix by Sebastian Böck, pull
request #104.)
* bugfix: ``msg.copy()`` didn't handle generators for sysex
data. ``msg.copy(data=(i for i in range(3)))`` would give
``data=()`` instead of ``data=(0,1,2)``.
(The code should be refactored so this is handled by the same
function everywhere, such as in ``__init__()``, in ``copy()`` and in
``parser.feed()``.)
* bugfix: ``MultiPort._receive()`` ignored the ``block``
parameter. (Fix by Tom Swirly, pull request #135.)
* bugfix: sequencer number meta message was incorrectly limited to
range 0..255 instead of 0..65335. (Reported by muranyia, issue
#144.)
* now using Tox for testing. (Implemented by Chris Apple, pull request
#123.)
* Travis integration up by Carl Thomé and Chris Apple.
1.2.8 (2017-06-30)
^^^^^^^^^^^^^^^^^^
* bugfix: nonblocking receive was broken for RtMidi IO
ports. (Reported by Chris Apple, issue #99.)
* bugfix: ``IOPort.poll()`` would block if another thread was waiting
for ``receive()``. Fixed the problem by removing the lock, which
was never needed in the first place as the embedded input port does
its own locking.
1.2.7 (2017-05-31)
^^^^^^^^^^^^^^^^^^
* added max length when reading message from a MIDI file. This
prevents Python from running out of memory when reading a corrupt
file. Instead it will now raise an ``IOError`` with a descriptive
error message. (Implemented by Curtis Hawthorne, pull request #95.)
* removed dependency on ``python-rtmidi`` from tests. (Reported by
Josue Ortega, issue #96.)
1.2.6 (2017-05-04)
^^^^^^^^^^^^^^^^^^
* bugfix: Sending sysex with Pygame in Python 3 failed with
``"TypeError: array() argument 1 must be a unicode character, not
byte"``. (Reported by Harry Williamson.)
* now handles ``sequence_number`` and ``midi_port`` messages with 0
data bytes. These are incorrect but can occur in rare cases. See
``mido/midifiles/test_midifiles.py`` for more. (Reported by Gilthans
(issue #42) and hyst329 (issue #93)).
1.2.5 (2017-04-28)
^^^^^^^^^^^^^^^^^^
* bugfix: RtMidi backend ignored ``api`` argument. (Fix by Tom Feist,
pull request #91.)
1.2.4 (2017-03-19)
^^^^^^^^^^^^^^^^^^
* fixed outdated python-rtmidi install instructions. (Reported by
Christopher Arndt, issue #87.)
1.2.3 (2017-03-14)
^^^^^^^^^^^^^^^^^^
* typo and incorrect links in docs fixed by Michael (miketwo) (pull requests
#84 and #85).
1.2.2 (2017-03-14)
^^^^^^^^^^^^^^^^^^
* bugfix: sysex data was broken in string format encoding and decoding.
The data was encoded with spaces ('data=(1, 2, 3)') instead of as one word
('data=(1,2,3)').
* added some tests for string format.
* bugfix: ``BaseOutput.send()`` raised string instead of ``ValueError``.
1.2.1 (2017-03-10)
^^^^^^^^^^^^^^^^^^
* bugfix: IO port never received anything when used with RtMidi
backend. (Reported by dagargo, issue #83.)
This was caused by a very old bug introduced in 1.0.3. IOPort
mistakenly called the inner method ``self.input._receive()`` instead
of ``self.input.receive()``. This happens to work for ports that
override ``_receive()`` but not for the new RtMidi backend which
overrides ``receive()``. (The default implementation of
``_receive()`` just drops the message on the floor.)
* bugfix: PortMidi backend was broken due to missing import
(``ctypes.byref``). (Introduced in 1.2.0.)
1.2.0 (2017-03-07)
^^^^^^^^^^^^^^^^^^^
New implementation of messages and parser:
* completely reimplemented messages. The code is now much simpler,
clearer and easier to work with.
* new contructors ``Message.from_bytes()``, ``Message.from_hex()``,
``Message.from_str()``.
* new message attributes ``is_meta`` and ``is_realtime``.
Frozen (immutable) messages:
* added ``FrozenMessage`` and ``FrozenMetaMessage``. These are
immutable versions of ``Message`` and ``MetaMessage`` that are
hashable and thus can be used as dictionary keys. These are
available in ``mido.frozen``. (Requested by Jasper Lyons, issue
#36.)
RtMidi is now the default backend:
* switched default backend from PortMidi to RtMidi. RtMidi is easier
to install on most systems and better in every way.
If you want to stick to PortMidi you can either set the environment
variable ``$MIDO_BACKEND=mido.backends.portmidi`` or call
``mido.set_backend('mido.backends.portmidi')`` in your program.
* refactored the RtMidi backend to have a single ``Port`` class
instead of inheriting from base ports. It was getting hard to keep
track of it all. The code is now a lot easier to reason about.
* you can now pass ``client_name`` when opening RtMidi ports:
``open_output('Test', client_name='My Client')``. When
``client_name`` is passed the port will automatically be a virtual
port.
* with ``LINUX_ALSA`` you can now omit client name and ALSA
client/port number when opening ports, allowing you to do
``mido.open_output('TiMidity port 0')`` instead of
``mido.open_output('TiMidity:TiMidity port 0 128:0')``. (See RtMidi
backend docs for more.)
Changes to the port API:
* ports now have ``is_input`` and ``is_output`` attributes.
* new functions ``tick2second()`` and ``second2tick()``. (By Carl
Thomé, pull request #71.)
* added ``_locking`` attribute to ``BasePort``. You can set this to
``False`` in a subclass to do your own locking.
* ``_receive()`` is now allowed to return a messages. This makes the
API more consistent and makes it easier to implement thread safe
ports.
* ``pending()`` is gone. This had to be done to allow for the new
``_receive()`` behavior.
* improved MIDI file documentation. (Written by Carl Thomé.)
Other changes:
* bugfix: if a port inherited from both ``BaseInput`` and
``BaseOutput`` this would cause ``BasePort.__init__()`` to be called
twice, which means ``self._open()`` was also called twice. As a
workaround ``BasePort.__init__()`` will check if ``self.closed``
exists.
* added ``mido.version_info``.
* ``mido.set_backend()`` can now be called with ``load=True``.
* added ``multi_send()``.
* ``MIN_PITCHWHEEL``, ``MAX_PITCHWHEEL``, ``MIN_SONGPOS`` and
``MAX_SONGPOS`` are now available in the top level module (for
example ``mido.MIN_PITCHWHEEL``).
* added experimental new backend ``mido.backends.amidi``. This uses
the ALSA ``amidi`` command to send and receive messages, which makes
it very inefficient but possibly useful for sysex transfer.
* added new backend ``mido.backends.rtmidi_python`` (previously
available in the examples folder.) This uses the ``rtmidi-python``
package instead of ``python-rtmidi``. For now it lacks some of
features of the ``rtmidi`` backend, but can still be useful on
systems where ``python-rtmidi`` is not available. (Requested by
netchose, issue #55.)
1.1.24 (2017-02-16)
^^^^^^^^^^^^^^^^^^^
* bugfix: PortMidi backend was broken on macOS due to a typo. (Fix by
Sylvain Le Groux, pull request #81.)
1.1.23 (2017-01-31)
^^^^^^^^^^^^^^^^^^^
* bugfix: ``read_syx_file()`` didn't handle '\n' in text format file
causing it to crash. (Reported by Paul Forgey, issue #80.)
1.1.22 (2017-01-27)
^^^^^^^^^^^^^^^^^^^
* the bugfix in 1.1.20 broke blocking receive() for RtMidi. Reverting
the changes. This will need some more investigation.
1.1.21 (2017-01-26)
^^^^^^^^^^^^^^^^^^^
* bugfix: MidiFile save was broken in 1.1.20 due to a missing import.
1.1.20 (2017-01-26)
^^^^^^^^^^^^^^^^^^^
* bugfix: close() would sometimes hang for RtMidi input ports. (The
bug was introduced in 1.1.18 when the backend was rewritten to
support true blocking.)
* Numpy numbers can now be used for all message attributes. (Based on
implementation by Henry Mao, pull request #78.)
The code checks against numbers.Integral and numbers.Real (for the
time attribute) so values can be any subclass of these.
1.1.19 (2017-01-25)
^^^^^^^^^^^^^^^^^^^
* Pygame backend can now receive sysex messages. (Fix by Box of Stops.)
* bugfix: ``libportmidi.dylib`` was not found when using
MacPorts. (Fix by yam655, issue #77.)
* bugfix: ``SocketPort.__init()`` was not calling
``IOPort.__init__()`` which means it didn't get a
``self._lock``. (Fixed by K Lars Lohn, pull request #72. Also
reported by John J. Foerch, issue #79.)
* fixed typo in intro example (README and index.rst). Fix by Antonio
Ospite (pull request #70), James McDermott (pull request #73) and
Zdravko Bozakov (pull request #74).
* fixed typo in virtual ports example (Zdravko Bozakov, pull request #75.)
1.1.18 (2016-10-22)
^^^^^^^^^^^^^^^^^^^
* ``time`` is included in message comparison. ``msg1 == msg2`` will
now give the same result as ``str(msg1) == str(msg2)`` and
``repr(msg1)`` == ``repr(msg2)``.
This means you can now compare tracks wihout any trickery, for
example: ``mid1.tracks == mid2.tracks``.
If you need to leave out time the easiest was is ``msg1.bytes() ==
msg2.bytes()``.
This may in rare cases break code.
* bugfix: ``end_of_track`` messages in MIDI files were not handled correctly.
(Reported by Colin Raffel, issue #62).
* bugfix: ``merge_tracks()`` dropped messages after the first
``end_of_track`` message. The new implementation removes all
``end_of_track`` messages and adds one at the end, making sure to
adjust the delta times of the remaining messages.
* refactored MIDI file code.
* ``mido-play`` now has a new option ``-m / --print-messages`` which
prints messages as they are played back.
* renamed ``parser._parsed_messages`` to
``parser.messages``. ``BaseInput`` and ``SocketPort`` use it so it
should be public.
* ``Parser()`` now takes an option argument ``data`` which is passed
to ``feed()``.
1.1.17 (2016-10-06)
^^^^^^^^^^^^^^^^^^^
* RtMidi now supports true blocking ``receive()`` in Python 3. This
should result in better performance and lower latency. (Thanks to
Adam Roberts for helping research queue behavior. See issue #49 for
more.)
* bugfix: ``MidiTrack.copy()`` (Python 3 only) returned ``list``.
* fixed example ``queue_port.py`` which broke when locks where added.
1.1.16 (2016-09-27)
^^^^^^^^^^^^^^^^^^^
* bugfix: ``MidiTrack`` crashed instead of returning a message on
``track[index]``. Fix by Colin Raffel (pull request #61).
* added ``__add__()`` and ``__mul__()`` to ``MidiTrack`` so ``+`` and
``*`` will return tracks instead of lists.
* added ``poll()`` method to input ports as a shortcut for
``receive(block=False)``.
* added example ``rtmidi_python_backend.py``, a backend for the
rtmidi-python package (which is different from the python-rtmidi
backend that Mido currently uses.) This may at some point be added
to the package but for now it's in the examples folder. (Requested
by netchose, issue #55.)
* removed custom ``_import_module()``. Its only function was to make
import errors more informative by showing the full module path, such
as ``ImportError: mido.backends.rtmidi`` instead of just ``ImportError:
rtmidi``. Unfortunately it ended up masking import errors in the
backend module, causing confusion.
It turns ``importlib.import_module()`` can be called with the full
path, and on Python 3 it will also display the full path in the
``ImportError`` message.
1.1.15 (2016-08-24)
^^^^^^^^^^^^^^^^^^^
* Sending and receiving messages is now thread safe. (Initial
implementation by Adam Roberts.)
* Bugfix: ``PortServer`` called ``__init__`` from the wrong
class. (Fix by Nathan Hurst.)
* Changes to ``MidiTrack``:
* ``MidiTrack()`` now takes a as a parameter an iterable of
messages. Examples:
.. code-block:: python
MidiTrack(messages)
MidiTrack(port.iter_pending())
MidiTrack(msg for msg in some_generator)
* Slicing a ``MidiTrack`` returns a ``MidiTrack``. (It used to
return a ``list``.) Example:
.. code-block:: python
track[1:10]
* Added the ability to use file objects as well as filenames when reading,
writing and saving MIDI files. This allows you to create a MIDI file
dynamically, possibly *not* using mido, save it to an io.BytesIO, and
then play that in-memory file, without having to create an intermediate
external file. Of course the memory file (and/or the MidiFile) can still
be saved to an external file.
(Implemented by Brian O'Neill.)
* PortMidi backend now uses pm.lib.Pm_GetHostErrorText() to get host
error messages instead of just the generic "PortMidi: \`Host error\'".
(Implemented by Tom Manderson.)
Thanks to Richard Vogl and Tim Cook for reporting errors in the docs.
1.1.14 (2015-06-09)
^^^^^^^^^^^^^^^^^^^
* bugfix: merge_tracks() concatenated the tracks instead of merging
them. This caused tracks to be played back one by one. (Issue #28,
reported by Charles Gillingham.)
* added support for running status when writing MIDI files.
(Implemented by John Benediktsson.)
* rewrote the callback system in response to issues #23 and #25.
* there was no way to set a callback function if the port was opened
without one. (Issue#25, reported by Nils Werner.)
Callbacks can now be set and cleared at any time by either passing
one to ``open_input()`` or updating the ``callback`` attribute.
This causes some slight changes to the behavior of the port when
using callbacks. Previously if you opened the port with a callback
and then set ``port.callback = None`` the callback thread would keep
running but drop any incoming messages. If you do the same now the
callback thread will stop and the port will return normal
non-callback behavior. If you want the callback thread to drop
messages you can set ``port.callback = lambda message: None``.
Also, ``receive()`` no longer checks ``self.callback``. This was
inconsistent as it was the only method to do so. It also allows
ports that don't support callbacks to omit the ``callback``
attribute.
* bugfix: closing a port would sometimes cause a segfault when using
callbacks. (Issue #24, reported by Francesco Ceruti.)
* bugfix: Pygame ports were broken due to a faulty check for ``virtual=True``.
* now raises ``ValueError`` instead of ``IOError`` if you pass
``virtual`` or ``callback`` while opening a port and the backend
doesn't support them. (An unsupported argument is not an IO error.)
* fixed some errors in backend documentation. (Pull request #23 by
velolala.)
* ``MultiPort`` now has a ``yield_port`` argument just like
``multi_receive()``.
1.1.13 (2015-02-07)
^^^^^^^^^^^^^^^^^^^
* the PortMidi backend will now return refresh the port list when you
ask for port names are open a new port, which means you will see
devices that you plug in after loading the backend. (Due to
limitations in PortMidi the list will only be refreshed if there are
no open ports.)
* bugfix: ``tempo2bpm()`` was broken and returned the wrong value for
anything but 500000 microseconds per beat (120 BPM). (Reported and
fixed by Jorge Herrera, issue #21)
* bugfix: ``merge_tracks()`` didn't work with empty list of tracks.
* added proper keyword arguments and doc strings to open functions.
1.1.12 (2014-12-02)
^^^^^^^^^^^^^^^^^^^
* raises IOError if you try to open a virtual port with PortMidi or
Pygame. (They are not supported by these backends.)
* added ``merge_tracks()``.
* removed undocumented method ``MidiFile.get_messages()``.
(Replaced by ``merge_tracks(mid.tracks)``.)
* bugfix: ``receive()`` checked ``self.callback`` which didn't exist
for all ports, causing an ``AttributeError``.
1.1.11 (2014-10-15)
^^^^^^^^^^^^^^^^^^^
* added ``bpm2tempo()`` and ``tempo2bpm()``.
* fixed error in documentation (patch by Michael Silver).
* added notes about channel numbers to documentation (reported by
ludwig404 / leonh, issue #18).
1.1.10 (2014-10-09)
^^^^^^^^^^^^^^^^^^^
* bugfix: MidiFile.length was computer incorrectly.
* bugfix: tempo changes caused timing problems in MIDI file playback.
(Reported by Michelle Thompson.)
* mido-ports now prints port names in single ticks.
* MidiFile.__iter__() now yields end_of_track. This means playback
will end there instead of at the preceding message.
1.1.9 (2014-10-06)
^^^^^^^^^^^^^^^^^^
* bugfix: _compute_tick_time() was not renamed to
_compute_seconds_per_tick() everywhere.
* bugfix: sleep time in play() was sometimes negative.
1.1.8 (2014-09-29)
^^^^^^^^^^^^^^^^^^
* bugfix: timing in MIDI playback was broken from 1.1.7 on. Current
time was subtracted before time stamps were converted from ticks to
seconds, leading to absurdly large delta times. (Reported by Michelle
Thompson.)
* bugfix: ``read_syx_file()`` didn't handle empty file.
1.1.7 (2014-08-12)
^^^^^^^^^^^^^^^^^^
* some classes and functions have been moved to more accessible locations::
from mido import MidiFile, MidiTrack, MetaMessage
from mido.midifiles import MetaSpec, add_meta_spec
* you can now iterate over a MIDI file. This will generate all MIDI
messages in playback order. The ``time`` attribute of each message
is the number of seconds since the last message or the start of the
file. (Based on suggestion by trushkin in issue #16.)
* added get_sleep_time() to complement set_sleep_time().
* the Backend object no longer looks for the backend module exists on
startup, but will instead just import the module when you call one
of the ``open_*()`` or ``get_*()`` functions. This test didn't work
when the library was packaged in a zip file or executable.
This means that Mido can now be installed as Python egg and frozen
with tools like PyInstaller and py2exe. See "Freezing Mido Programs"
for more on this.
(Issue #17 reported by edauenhauer and issue #14 reported by
netchose.)
* switched to pytest for unit tests.
1.1.6 (2014-06-21)
^^^^^^^^^^^^^^^^^^
* bugfix: package didn't work with easy_install.
(Issue #14, reported by netchose.)
* bugfix: 100% memory consumption when calling blocking receive()
on a PortMidi input. (Issue #15, reported by Francesco Ceruti.)
* added wheel support: http://pythonwheels.com/
1.1.5 (2014-04-18)
^^^^^^^^^^^^^^^^^^
* removed the 'mode' attribute from key_signature messages. Minor keys
now have an 'm' appended, for example 'Cm'.
* bugfix: sysex was broken in MIDI files.
* bugfix: didn't handle MIDI files without track headers.
* bugfix: MIDI files didn't handle channel prefix > 15
* bugfix: MIDI files didn't handle SMPTE offset with frames > 29
1.1.4 (2014-10-04)
^^^^^^^^^^^^^^^^^^
* bugfix: files with key signatures Cb, Db and Gb failed due to faulty
error handling.
* bugfix: when reading some MIDI files Mido crashed with the message
"ValueError: attribute must be in range 0..255". The reason was that
Meta messages set running status, which caused the next statusless
message to be falsely interpreted as a meta message. (Reported by
Domino Marama).
* fixed a typo in MidiFile._read_track(). Sysex continuation should
work now.
* rewrote tests to make them more readable.
1.1.3 (2013-10-14)
^^^^^^^^^^^^^^^^^^
* messages are now copied on send. This allows the sender to modify the
message and send it to another port while the two ports receive their
own personal copies that they can modify without any side effects.
1.1.2 (2013-10-05)
^^^^^^^^^^^^^^^^^^
* bugfix: non-ASCII character caused trouble with installation when LC_ALL=C.
(Reported by Gene De Lisa)
* bugfix: used old exception handling syntax in rtmidi backend which
broke in 3.3
* fixed broken link in
1.1.1 (2013-10-04)
^^^^^^^^^^^^^^^^^^
* bugfix: mido.backends package was not included in distribution.
1.1.0 (2013-10-01)
^^^^^^^^^^^^^^^^^^
* added support for selectable backends (with MIDO_BACKEND) and
included python-rtmidi and pygame backends in the official library
(as mido.backend.rtmidi and mido.backend.pygame).
* added full support for MIDI files (read, write playback)
* added MIDI over TCP/IP (socket ports)
* added utility programs mido-play, mido-ports, mido-serve and mido-forward.
* added support for SMPTE time code quarter frames.
* port constructors and ``open_*()`` functions can now take keyword
arguments.
* output ports now have reset() and panic() methods.
* new environment variables MIDO_DEFAULT_INPUT, MIDO_DEFAULT_OUTPUT
and MIDO_DEFAULT_IOPORT. If these are set, the open_*() functions
will use them instead of the backend's default ports.
* added new meta ports MultiPort and EchoPort.
* added new examples and updated the old ones.
* format_as_string() now takes an include_time argument (defaults to True)
so you can leave out the time attribute.
* sleep time inside sockets can now be changed.
* Message() no longer accepts a status byte as its first argument. (This was
only meant to be used internally.)
* added callbacks for input ports (PortMidi and python-rtmidi)
* PortMidi and pygame input ports now actually block on the device
instead of polling and waiting.
* removed commas from repr() format of Message and MetaMessage to make
them more consistent with other classes.
1.0.4 (2013-08-15)
^^^^^^^^^^^^^^^^^^
* rewrote parser
1.0.3 (2013-07-12)
^^^^^^^^^^^^^^^^^^
* bugfix: __exit__() didn't close port.
* changed repr format of message to start with "message".
* removed support for undefined messages. (0xf4, 0xf5, 0xf7, 0xf9 and 0xfd.)
* default value of velocity is now 64 (0x40).
(This is the recommended default for devices that don't support velocity.)
1.0.2 (2013-07-31)
^^^^^^^^^^^^^^^^^^
* fixed some errors in the documentation.
1.0.1 (2013-07-31)
^^^^^^^^^^^^^^^^^^
* multi_receive() and multi_iter_pending() had wrong implementation.
They were supposed to yield only messages by default.
1.0.0 (2013-07-20)
^^^^^^^^^^^^^^^^^^
Initial release.
Basic functionality: messages, ports and parser.
mido-1.2.9/docs/license.rst 0000664 0001750 0001750 00000002133 13337033714 016002 0 ustar olemb olemb 0000000 0000000 License
=======
The MIT License (MIT)
Copyright (c) 2013-infinity Ole Martin Bjørndalen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
mido-1.2.9/docs/parsing.rst 0000664 0001750 0001750 00000004040 13337034031 016013 0 ustar olemb olemb 0000000 0000000 Parsing MIDI Bytes
==================
MIDI is a binary protocol. Each each message is encoded as a status byte
followed by up to three data bytes. (Sysex messages can have any number of
data bytes and use a stop byte instead.)
.. note:: To parse a single message you can use the class methods
``mido.Message.from_bytes()`` and
``mido.Message.from_hex()`` (new in 1.2).
Mido comes with a parser that turns MIDI bytes into messages. You can create a parser object, or call one of the utility functions::
>>> mido.parse([0x92, 0x10, 0x20])
>>> mido.parse_all([0x92, 0x10, 0x20, 0x82, 0x10, 0x20])
[,
]
These functions are just shortcuts for the full ``Parser`` class. This
is the parser used inside input ports to parse incoming messages.
Here are a few examples of how it can be used::
>>> p = mido.Parser()
>>> p.feed([0x90, 0x10, 0x20])
>>> p.pending()
1
>>> p.get_message()
>>> p.feed_byte(0x90)
>>> p.feed_byte(0x10)
>>> p.feed_byte(0x20)
>>> p.feed([0x80, 0x10, 0x20])
``feed()`` accepts any iterable that generates integers in 0..255. The
parser will skip and stray status bytes or data bytes, so you can
safely feed it random data and see what comes out the other end.
``get_message()`` will return ``None`` if there are no messages ready
to be gotten.
You can also fetch parsed messages out of the parser by iterating over
it::
>>> p.feed([0x92, 0x10, 0x20, 0x82, 0x10, 0x20])
>>> for message in p:
... print(message)
note_on channel=2 note=16 velocity=32 time=0
note_off channel=2 note=16 velocity=32 time=0
The messages are available in `p.messages` (a `collections.deque`).
For the full table of MIDI binary encoding, see:
``_
mido-1.2.9/docs/message_types.rst 0000664 0001750 0001750 00000004713 13337034031 017227 0 ustar olemb olemb 0000000 0000000 Message Types
=============
Supported Messages
------------------
============== ==============================
Name Keyword Arguments / Attributes
============== ==============================
note_off channel note velocity
note_on channel note velocity
polytouch channel note value
control_change channel control value
program_change channel program
aftertouch channel value
pitchwheel channel pitch
sysex data
quarter_frame frame_type frame_value
songpos pos
song_select song
tune_request
clock
start
continue
stop
active_sensing
reset
============== ==============================
``quarter_frame`` is used for SMPTE time codes. See:
http://www.electronics.dit.ie/staff/tscarff/Music_technology/midi/MTC.htm
Parameter Types
---------------
=========== ====================== ================
Name Valid Range Default Value
=========== ====================== ================
channel 0..15 0
frame_type 0..7 0
frame_value 0..15 0
control 0..127 0
note 0..127 0
program 0..127 0
song 0..127 0
value 0..127 0
velocity 0..127 64
data (0..127, 0..127, ...) () (empty tuple)
pitch -8192..8191 0
pos 0..16383 0
time any integer or float 0
=========== ====================== ================
.. note::
Mido numbers channels 0 to 15 instead of 1 to 16. This makes them
easier to work with in Python but you may want to add and subtract
1 when communicating with the user.
``velocity`` is how fast the note was struck or released. It defaults
to 64 so that if you don't set it, you will still get a reasonable
value. (64 is the recommended default for devices that don't support
it attack or release velocity.)
The ``time`` parameter is not included in the encoded message, and is
(currently) not used by Mido in any way. You can use it for whatever
purpose you wish.
The ``data`` parameter accepts any iterable that generates numbers in
0..127. This includes::
mido.Message('sysex', data=[1, 2, 3])
mido.Message('sysex', data=range(10))
mido.Message('sysex', data=(i for i in range(10) if i % 2 == 0))
For details about the binary encoding of a MIDI message, see:
http://www.midi.org/techspecs/midimessages.php
mido-1.2.9/docs/frozen_messages.rst 0000664 0001750 0001750 00000002550 13337034031 017546 0 ustar olemb olemb 0000000 0000000 Frozen Messages
---------------
(New in 1.2.)
Since Mido messages are mutable (can change) they can not be hashed or
put in dictionaries. This makes it hard to use them for things like
Markov chains.
In these situations you can use frozen messages:
.. code-block:: python
from mido.frozen import FrozenMessage
msg = FrozenMessage('note_on')
d = {msg: 'interesting'}
Frozen messages are used and behave in exactly the same way as normal
messages with one exception: attributes are not settable.
There are also variants for meta messages (``FrozenMetaMessage`` and
``FrozenUnknownMetaMessage``).
You can freeze and thaw messages with:
.. code-block:: python
from mido.frozen import freeze_message, thaw_message
frozen = freeze_message(msg)
thawed = thaw_message(frozen)
``thaw_message()`` will always return a copy. Passing a frozen message
to ``freeze_message()`` will return the original message.
Both functions return ``None`` if you pass ``None`` which is handy for
things like:
.. code-block:: python
msg = freeze_message(port.receive())
# Python 3 only:
for msg in map(freeze_message, port):
...
# Python 2 and 3:
for msg in (freeze_message(msg) for msg in port):
...
To check if a message is frozen:
.. code-block:: python
from mido.frozen import is_frozen
if is_frozen(msg):
...
mido-1.2.9/docs/images/ 0000775 0001750 0001750 00000000000 13355643235 015101 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/docs/images/midi_time.svg 0000664 0001750 0001750 00000047547 13337034031 017567 0 ustar olemb olemb 0000000 0000000
mido-1.2.9/docs/string_encoding.rst 0000664 0001750 0001750 00000006230 13337034031 017527 0 ustar olemb olemb 0000000 0000000 String Encoding
===============
Mido messages can be serialized to a text format, which can be used to
safely store messages in text files, send them across sockets or embed
them in JSON, among other things.
To encode a message, simply call ``str()`` on it::
>>> cc = control_change(channel=9, control=1, value=122, time=60)
>>> str(cc)
'control_change channel=9 control=1 value=122 time=60'
To convert the other way (new method in 1.2)::
>>> mido.Message.from_str('control_change control=1 value=122')
Alternatively, you can call the ``format_as_string`` function directly:
>>> mido.format_as_string(cc)
'control_change channel=9 control=1 value=122 time=60'
If you don't need the time attribute or you want to store it elsewhere, you
can pass ``include_time=False``::
>>> mido.format_as_string(cc)
'control_change channel=9 control=1 value=122'
(This option is also available in ``mido.Message.from_str()``.)
Format
------
The format is simple::
MESSAGE_TYPE [PARAMETER=VALUE ...]
These are the same as the arguments to ``mido.Message()``. The order
of parameters doesn't matter, but each one can only appear once.
Only these character will ever occur in a string encoded Mido message::
[a-z][0-9][ =_.+()]
or written out::
'abcdefghijklmnopqrstuvwxyz0123456789 =_.+()'
This means the message can be embedded in most text formats without
any form of escaping.
Parsing
-------
To parse a message, you can use ``mido.parse_string()``::
>>> parse_string('control_change control=1 value=122 time=0.5')
Parameters that are left out are set to their default
values. ``ValueError`` is raised if the message could not be
parsed. Extra whitespace is ignored::
>>> parse_string(' control_change control=1 value=122')
To parse messages from a stream, you can use
``mido.messages.parse_string_stream()``::
for (message, error) in parse_string_stream(open('some_music.text')):
if error:
print(error)
else:
do_something_with(message)
This will return every valid message in the stream. If a message could
not be parsed, ``message`` will be ``None`` and ``error`` will be an error
message describing what went wrong, as well as the line number where
the error occurred.
The argument to ``parse_string_stream()`` can be any object that
generates strings when iterated over, such as a file or a list.
``parse_string_stream()`` will ignore blank lines and comments (which
start with a # and go to the end of the line). An example of valid
input::
# A very short song with an embedded sysex message.
note_on channel=9 note=60 velocity=120 time=0
# Send some data
sysex data=(1,2,3) time=0.5
pitchwheel pitch=4000 # bend the not a little time=0.7
note_off channel=9 note=60 velocity=60 time=1.0
Examples
--------
And example of messages embedded in JSON::
{'messages': [
'0.0 note_on channel=9 note=60 velocity=120',
'0.5 sysex data=(1,2,3)',
...
])
mido-1.2.9/docs/contributing.rst 0000664 0001750 0001750 00000004356 13355500373 017100 0 ustar olemb olemb 0000000 0000000 Contributing
============
Questions
---------
If you have questions about contributing code or suggestions
for how to make contributing easier, please write to
https://groups.google.com/forum/#!forum/mido-community.
Installing for developers
-------------------------
To install the dev dependencies, you can run the command::
pip install -e .[dev]
This will install all needed dependencies for testing and documentation.
Testing
-------
`pytest `_ is used for unit testing. The tests
are found in `mido/test_*.py`.
Tests can be run using the command::
py.test
Before submission, it is required that the tox tests run and pass. Run the tox tests using::
tox
It is required to test on at least 2.7 and 3.5 before submission. Any other passes are nice to have
You can also set up a commit hook::
echo "tox" >.git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
This will run tests when you commit and cancel the commit if any tests
fail.
Testing MIDI file support
-------------------------
Test Files
^^^^^^^^^^
The `Lakh MIDI Dataset `_ is
a great resource for testing the MIDI file parser.
Publishing (Release Checklist)
------------------------------
I am currently the only one with access to publishing on PyPI and
readthedocs. This will hopefully change in the future.
Bump Version
^^^^^^^^^^^^
X.Y.Z is the version, for example 1.1.18 or 1.2.0.
* update version and date in `docs/changes.rst`
* update version in `mido/__about__.py`
* `git commit -a -c "Bumped version to X.Y.Z."`
Publish on PyPI
^^^^^^^^^^^^^^^
I like to do this before I push to GitHub. This way if the package
fails to upload I can roll back and fix it before I push my changes.
::
rm -rf dist/*
python setup.py bdist_wheel --universal
python setup.py sdist
twine upload twine upload dist/*
Push to GitHub
^^^^^^^^^^^^^^
::
git tag X.Y.Z
git push
git push --tags
Update the stable branch (if this is a stable release):
::
git checkout stable
git pull . master
git push
git checkout master
Update Read the Docs
^^^^^^^^^^^^^^^^^^^^
Log into readthedocs.org and build the latest documentation. This is
set up to use the stable branch.
mido-1.2.9/docs/midi_files.rst 0000664 0001750 0001750 00000015575 13337034031 016473 0 ustar olemb olemb 0000000 0000000 MIDI Files
==========
MidiFile objects can be used to read, write and play back MIDI
files.
Opening a File
--------------
You can open a file with::
from mido import MidiFile
mid = MidiFile('song.mid')
.. note:: Sysex dumps such as patch data are often stored in SYX files
rather than MIDI files. If you get "MThd not found. Probably not a
MIDI file" try ``mido.read_syx_file()``. (See :doc:`syx` for more.)
The ``tracks`` attribute is a list of tracks. Each track is a list of
messages and meta messages, with the ``time`` attribute of each
messages set to its delta time (in ticks). (See Tempo and Beat
Resolution below for more on delta times.)
To print out all messages in the file, you can do::
for i, track in enumerate(mid.tracks):
print('Track {}: {}'.format(i, track.name))
for msg in track:
print(msg)
The entire file is read into memory. Thus you can freely modify tracks
and messages, and save the file back by calling the ``save()``
method. (More on this below.)
Iterating Over Messages
-----------------------
Iterating over a ``MidiFile`` object will generate all MIDI messages
in the file in playback order. The ``time`` attribute of each message
is the number of seconds since the last message or the start of the
file.
Meta messages will also be included. If you want to filter them out,
you can do::
if msg.is_meta:
...
This makes it easy to play back a MIDI file on a port::
for msg in MidiFile('song.mid'):
time.sleep(msg.time)
if not msg.is_meta:
port.send(msg)
This is so useful that there's a method for it::
for msg in MidiFile('song.mid').play():
port.send(msg)
This does the sleeping and filtering for you. If you pass
``meta_messages=True`` you will also get meta messages. These can not
be sent on ports, which is why they are off by default.
Creating a New File
-------------------
You can create a new file by calling MidiFile without the ``filename``
argument. The file can then be saved by calling the ``save()`` method::
from mido import Message, MidiFile, MidiTrack
mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)
track.append(Message('program_change', program=12, time=0))
track.append(Message('note_on', note=64, velocity=64, time=32))
track.append(Message('note_off', note=64, velocity=127, time=32))
mid.save('new_song.mid')
The ``MidiTrack`` class is a subclass of list, so you can use all the
usual methods.
All messages must be tagged with delta time (in ticks). (A delta time
is how long to wait before the next message.)
If there is no 'end_of_track' message at the end of a track, one will
be written anyway.
A complete example can be found in ``examples/midifiles/``.
The ``save`` method takes either a filename (``str``) or, using the ``file``
keyword parameter, a file object such as an in-memory binary file (an
``io.BytesIO``). If you pass a file object, ``save`` does not close it.
Similarly, the ``MidiFile`` constructor can take either a filename, or
a file object by using the ``file`` keyword parameter. if you pass a file
object to ``MidiFile`` as a context manager, the file is not closed when
the context manager exits. Examples can be found in ``test_midifiles2.py``.
File Types
----------
There are three types of MIDI files:
* type 0 (single track): all messages are saved in one track
* type 1 (synchronous): all tracks start at the same time
* type 2 (asynchronous): each track is independent of the others
When creating a new file, you can select type by passing the ``type``
keyword argument, or by setting the ``type`` attribute::
mid = MidiFile(type=2)
mid.type = 1
Type 0 files must have exactly one track. A ``ValueError`` is raised
if you attempt to save a file with no tracks or with more than one
track.
Playback Length
---------------
You can get the total playback time in seconds by accessing the
``length`` property::
mid.length
This is only supported for type 0 and 1 files. Accessing ``length`` on
a type 2 file will raise ``ValueError``, since it is impossible to
compute the playback time of an asynchronous file.
Meta Messages
-------------
Meta messages behave like normal messages and can be created in the
usual way, for example::
>>> from mido import MetaMessage
>>> MetaMessage('key_signature', key='C#', mode='major')
You can tell meta messages apart from normal messages with::
if msg.is_meta:
...
or if you know the message type you can use the ``type`` attribute::
if msg.type == 'key_signature':
...
elif msg.type == 'note_on':
...
Meta messages can not be sent on ports.
For a list of supported meta messages and their attributes, and also
how to implement new meta messages, see :doc:`meta_message_types`.
About the Time Attribute
------------------------
The ``time`` attribute is used in several different ways:
* inside a track, it is delta time in ticks. This must be an integer.
* in messages yielded from ``play()``, it is delta time in seconds
(time elapsed since the last yielded message)
* (only important to implementers) inside certain methods it is
used for absolute time in ticks or seconds
Tempo and Beat Resolution
-------------------------
.. image:: images/midi_time.svg
Timing in MIDI files is centered around ticks and beats. A beat is the same as
a quarter note. Beats are divided into ticks, the smallest unit of time in
MIDI.
Each message in a MIDI file has a delta time, which tells how many ticks have
passed since the last message. The length of a tick is defined in ticks per
beat. This value is stored as ``ticks_per_beat`` in MidiFile objects and
remains fixed throughout the song.
MIDI Tempo vs. BPM
^^^^^^^^^^^^^^^^^^
Unlike music, tempo in MIDI is not given as beats per minute, but rather in
microseconds per beat.
The default tempo is 500000 microseconds per beat, which is 120 beats per
minute. The meta message 'set_tempo' can be used to change tempo during a song.
You can use :py:func:`bpm2tempo` and :py:func:`tempo2bpm` to convert to and
from beats per minute. Note that :py:func:`tempo2bpm` may return a floating
point number.
Converting Between Ticks and Seconds
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To convert from MIDI time to absolute time in seconds, the number of beats per
minute (BPM) and ticks per beat (often called pulses per quarter note or PPQ,
for short) have to be decided upon.
You can use :py:func:`tick2second` and :py:func:`second2tick` to convert to
and from seconds and ticks. Note that integer rounding of the result might be
necessary because MIDI files require ticks to be integers.
If you have a lot of rounding errors you should increase the time resolution
with more ticks per beat, by setting MidiFile.ticks_per_beat to a large number.
Typical values range from 96 to 480 but some use even more ticks per beat.
mido-1.2.9/docs/authors.rst 0000664 0001750 0001750 00000000112 13337033714 016040 0 ustar olemb olemb 0000000 0000000 Authors
=======
Ole Martin Bjørndalen (lead programmer)
Rapolas Binkys
mido-1.2.9/docs/meta_message_types.rst 0000664 0001750 0001750 00000021563 13355500373 020246 0 ustar olemb olemb 0000000 0000000 Meta Message Types
==================
Supported Messages
------------------
sequence_number (0x00)
^^^^^^^^^^^^^^^^^^^^^^
=============== ============ ========
Attribute Values Default
=============== ============ ========
number 0..65535 0
=============== ============ ========
Sequence number in type 0 and 1 MIDI files;
pattern number in type 2 MIDI files.
text (0x01)
^^^^^^^^^^^
============== ============== ========
Attribute Values Default
============== ============== ========
text string ''
============== ============== ========
General "Text" Meta Message. Can be used for any text based data.
copyright (0x02)
^^^^^^^^^^^^^^^^
============== ============== ========
Attribute Values Default
============== ============== ========
text string ''
============== ============== ========
Provides information about a MIDI file's copyright.
track_name (0x03)
^^^^^^^^^^^^^^^^^
============== ============== ========
Attribute Values Default
============== ============== ========
name string ''
============== ============== ========
Stores a MIDI track's name.
instrument_name (0x04)
^^^^^^^^^^^^^^^^^^^^^^
============== ============== ========
Attribute Values Default
============== ============== ========
name string ''
============== ============== ========
Stores an instrument's name.
lyrics (0x05)
^^^^^^^^^^^^^
============== ============== ========
Attribute Values Default
============== ============== ========
text string ''
============== ============== ========
Stores the lyrics of a song. Typically one syllable per Meta Message.
marker (0x06)
^^^^^^^^^^^^^
============== ============== ========
Attribute Values Default
============== ============== ========
text string ''
============== ============== ========
Marks a point of interest in a MIDI file.
Can be used as the marker for the beginning of a verse, solo, etc.
cue_marker (0x07)
^^^^^^^^^^^^^^^^^
============== ============== ========
Attribute Values Default
============== ============== ========
text string ''
============== ============== ========
Marks a cue. IE: 'Cue performer 1', etc
device_name (0x09)
^^^^^^^^^^^^^^^^^^
============== ============== ========
Attribute Values Default
============== ============== ========
name string ''
============== ============== ========
Gives the name of the device.
channel_prefix (0x20)
^^^^^^^^^^^^^^^^^^^^^
============== ============== ========
Attribute Values Default
============== ============== ========
channel 0..255 0
============== ============== ========
Gives the prefix for the channel on which events are played.
midi_port (0x21)
^^^^^^^^^^^^^^^^
============== ============== ========
Attribute Values Default
============== ============== ========
port 0..255 0
============== ============== ========
Gives the MIDI Port on which events are played.
end_of_track (0x2f)
^^^^^^^^^^^^^^^^^^^
============== ============== ========
Attribute Values Default
============== ============== ========
n/a n/a n/a
============== ============== ========
An empty Meta Message that marks the end of a track.
set_tempo (0x51)
^^^^^^^^^^^^^^^^
============== ============== ========
Attribute Values Default
============== ============== ========
tempo 0..16777215 500000
============== ============== ========
Tempo is in microseconds per beat (quarter note). You can use
:py:func:`bpm2tempo` and :py:func:`tempo2bpm` to convert to and from
beats per minute. Note that :py:func:`tempo2bpm` may return a floating
point number.
smpte_offset (0x54)
^^^^^^^^^^^^^^^^^^^
============== ================= ========
Attribute Values Default
============== ================= ========
frame_rate 24, 25, 29.97, 30 24
hours 0..255 0
minutes 0..59 0
seconds 0..59 0
frames 0..255 0
sub_frames 0..99 0
============== ================= ========
time_signature (0x58)
^^^^^^^^^^^^^^^^^^^^^
============================ =============== ========
Attribute Values Default
============================ =============== ========
numerator 0..255 4
denominator 1..2**255 4
clocks_per_click 0..255 24
notated_32nd_notes_per_beat 0..255 8
============================ =============== ========
Time signature of:
4/4 : MetaMessage('time_signature', numerator=4, denominator=4)
3/8 : MetaMessage('time_signature', numerator=3, denominator=8)
.. note:: From 1.2.9 time signature message have the correct default
value of 4/4. In earlier versions the default value was 2/4
due to a typo in the code.
key_signature (0x59)
^^^^^^^^^^^^^^^^^^^^
========= ================== ========
Attribute Values Default
========= ================== ========
key 'C', 'F#m', ... 'C'
========= ================== ========
Valid values: A A#m Ab Abm Am B Bb Bbm Bm C C# C#m Cb Cm D D#m Db Dm E
Eb Ebm Em F F# F#m Fm G G#m Gb Gm
Note: the mode attribute was removed in 1.1.5. Instead, an 'm' is
appended to minor keys.
sequencer_specific (0x7f)
^^^^^^^^^^^^^^^^^^^^^^^^^
============== ============== ========
Attribute Values Default
============== ============== ========
data [..] []
============== ============== ========
An unprocessed sequencer specific message containing raw data.
Unknown Meta Messages
---------------------
Unknown meta messages will be returned as ``UnknownMetaMessage``
objects, with ``type`` set to ``unknown_meta``. The messages are saved
back to the file exactly as they came out.
Code that depends on ``UnknownMetaMessage`` may break if the message
in question is ever implemented, so it's best to only use these to
learn about the format of the new message and then implement it as
described below.
``UnknownMetaMessage`` have two attributes::
``type_byte`` - a byte which uniquely identifies this message type
``data`` - the message data as a list of bytes
These are also visible in the ``repr()`` string::
Implementing New Meta Messages
------------------------------
If you come across a meta message which is not implemented, or you
want to use a custom meta message, you can add it by writing a new
meta message spec::
from mido.midifiles import MetaSpec, add_meta_spec
class MetaSpec_light_color(MetaSpec):
type_byte = 0xf0
attributes = ['r', 'g', 'b']
defaults = [0, 0, 0]
def decode(self, message, data):
# Interpret the data bytes and assign them to attributes.
(message.r, message.g, message.b) = data
def encode(self, message):
# Encode attributes to data bytes and
# return them as a list of ints.
return [message.r, message.g, message.b]
def check(self, name, value):
# (Optional)
# This is called when the user assigns
# to an attribute. You can use this for
# type and value checking. (Name checking
# is already done.
#
# If this method is left out, no type and
# value checking will be done.
if not isinstance(value, int):
raise TypeError('{} must be an integer'.format(name))
if not 0 <= value <= 255:
raise TypeError('{} must be in range 0..255'.format(name))
Then you can add your new message type with::
add_meta_spec(MetaSpec_light_color)
and create messages in the usual way::
>>> from mido import MetaMessage
>>> MetaMessage('light_color', r=120, g=60, b=10)
and the new message type will now work when reading and writing MIDI
files.
Some additional functions are available::
encode_string(unicode_string)
decode_string(byte_list)
These convert between a Unicode string and a list of bytes using the
current character set in the file.
If your message contains only one string with the attribute name
``text`` or ``name``, you can subclass from one of the existing
messages with these attributes, for example::
class MetaSpec_copyright(MetaSpec_text):
type_byte = 0x02
class MetaSpec_instrument_name(MetaSpec_track_name):
type_byte = 0x04
This allows you to skip everything but ``type_byte``, since the rest
is inherited.
See the existing MetaSpec classes for further examples.
mido-1.2.9/docs/intro.rst 0000664 0001750 0001750 00000012003 13337034031 015501 0 ustar olemb olemb 0000000 0000000 Introduction (Basic Concepts)
=============================
Mido is all about messages and ports.
Messages
--------
Mido allows you to work with MIDI messages as Python objects. To
create a new message::
>>> from mido import Message
>>> msg = Message('note_on', note=60)
>>> msg
.. note::
Mido numbers channels 0 to 15 instead of 1 to 16. This makes them
easier to work with in Python but you may want to add and subtract
1 when communicating with the user.
A list of all supported message types and their parameters can be
found in :doc:`message_types`.
The values can now be accessed as attributes::
>>> msg.type
'note_on'
>>> msg.note
60
>>> msg.velocity
64
Attributes are also settable but this should be avoided. It's better
to use ``msg.copy()``::
>>> msg.copy(note=100, velocity=127)
>> import mido
>>> mido.Message('note_on', channel=2092389483249829834)
Traceback (most recent call last):
File "", line 1, in
File "/home/olemb/src/mido/mido/messages/messages.py", line 89, in __init__
check_msgdict(msgdict)
File "/home/olemb/src/mido/mido/messages/checks.py", line 100, in check_msgdict
check_value(name, value)
File "/home/olemb/src/mido/mido/messages/checks.py", line 87, in check_value
_CHECKS[name](value)
File "/home/olemb/src/mido/mido/messages/checks.py", line 17, in check_channel
raise ValueError('channel must be in range 0..15')
ValueError: channel must be in range 0..15
This means that the message object is always a valid MIDI message.
Ports
-----
To create an output port and send a message::
>>> outport = mido.open_output()
>>> outport.send(msg)
To create an input port and receive a message::
>>> inport = mido.open_input()
>>> msg = inport.receive()
.. note::
Multiple threads can safely send and receive notes on the same
port.
This will give you the default output and input ports. If you want to
open a specific port, you will need its name. To get a list of all
available input ports::
>>> mido.get_input_names()
['Midi Through Port-0', 'SH-201', 'Integra-7']
>>> inport = mido.open_input('SH-201')
All Mido ports can be used with the ``with`` statement, which will
close the port for you::
with mido.open_input('SH-201') as inport:
...
To iterate through all incoming messages::
for msg in inport:
...
You can also receive and iterate over messages in a non-blocking
way.
For more about ports, see :doc:`ports`.
All Ports are Ports
-------------------
The input and output ports used above are device ports, which
communicate with a (physical or virtual) MIDI device.
Other port types include:
* ``MultiPort``, which wraps around a set of ports and allow you to send to all of them or receive from all of them as if they were one.
* ``SocketPort``, which communicates with another port over a TCP/IP (network) connection.
* ``IOPort``, which wraps around an input and an output port and allows you to send and receive messages as if the two were the same port.
Ports of all types look and behave the same way, so they can be used
interchangeably.
It's easy to write new port types. See :doc:`implementing_ports`.
Virtual Ports
-------------
Virtual ports allow you to create new ports that other applications
can connect to::
with mido.open_input('New Port', virtual=True) as inport:
for message in inport:
print(message)
The port should now appear to other applications as "New Port".
Unfortunately virtual ports are not supported by PortMidi and Pygame
so this only works with RtMidi.
Parsing MIDI Bytes
------------------
Mido comes with a parser that allows you to turn bytes into
messages. You can create a new parser::
>>> p = mido.Parser()
>>> p.feed([0x90, 0x40])
>>> p.feed_byte(0x60)
You can then fetch messages out of the parser::
>>> p.pending()
1
>>> for message in p:
... print(message)
...
note_on channel=0 note=64 velocity=96 time=0
For more on parsers and parsing see :doc:`parsing`.
You can also create a message from bytes using class methods (new in
1.2):
.. code-block:: python
msg1 = mido.Message.from_bytes([0x90, 0x40, 0x60])
msg2 = mido.Message.from_hex('90, 40 60')
The bytes must contain exactly one complete message. If not
``ValueError`` is raised.
Backends
--------
Mido comes with backends for RtMidi and PortMidi and Pygame. The
default is RtMidi. You can select another backend or even use multiple
backends at the same time. For more on this, see :doc:`backends/index`.
mido-1.2.9/docs/freezing.rst 0000664 0001750 0001750 00000002166 13337034031 016170 0 ustar olemb olemb 0000000 0000000 Freezing to EXE File
====================
PyInstaller
-----------
When you build an executable with PyInstaller and run it you may get
import errors like this one::
ImportError: No module named mido.backends.portmidi
The reason is that Mido uses ``import_module()`` to import the backend
modules, while PyInstaller looks for ``import`` statements.
The easiest fix is to import the module at the top of the program::
import mido
import mido.backends.portmidi # The backend you want to use.
print(mido.get_input_names())
and then run ``pyinstaller`` like usual::
$ pyinstaller --onefile midotest.py
$ ./dist/midotest
[u'Midi Through Port-0']
If you don't want to change the program, you can instead declare the
backend module as a `hidden import
`_.
bbFreeze, py2exe, cx_Freeze, py2app, etc.
-----------------------------------------
I suspect the same is true for these, but I have not had a chance to
try it out yet.
Adding the explicit ``import`` statement should always work, though,
since Mido backends are just normal Python modules.
mido-1.2.9/docs/bin.rst 0000664 0001750 0001750 00000002525 13337033714 015135 0 ustar olemb olemb 0000000 0000000 Included Programs
=================
These are installed with Mido.
mido-play
---------
Plays back one or more MIDI files::
$ mido-play song1.mid song2.mid
mido-ports
----------
Lists available input and output ports and shows environment variables
and the current backend module.
mido-serve
----------
Serves one or more ports over the network, for example::
$ mido-serve :9080 'Integra-7'
You can now connect to this port with ``mido-forward`` (or use
``mido.sockets.connect()`` and send messages to it. The messages will
be forwarded to every port you listed (in this case 'Integra-7').
mido-connect
------------
Forwards all messages that arrive on one or more ports to a server.
For example, to use the SH-201 keyboard connected to this computer to
play sounds on the Integra-7 on a computer named ``mac.local`` (which
runs the server as above), you can do::
$ mido-connect mac.local:9080 'SH-201'
Note that you may experience latency and jitter, so this may not be
very useful for live playing or for playing back songs.
There is also no security built in, so you should only use this on a
trusted network. (Anyone can connect and send anything, including
harmful sysex messages.)
``mido-serve`` and ``mido-connect`` are only included as fun programs
to play with, but may in the future be expanded into something more
usable.
mido-1.2.9/docs/index.rst 0000664 0001750 0001750 00000003440 13355500373 015471 0 ustar olemb olemb 0000000 0000000 .. Mido documentation master file, created by
sphinx-quickstart on Wed Jun 26 16:58:08 2013.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Mido - MIDI Objects for Python
==============================
Version |version|
Mido is a library for working with MIDI messages and ports. It's
designed to be as straight forward and Pythonic as possible:
.. code-block:: python
>>> import mido
>>> msg = mido.Message('note_on', note=60)
>>> msg.type
'note_on'
>>> msg.note
60
>>> msg.bytes()
[144, 60, 64]
>>> msg.copy(channel=2)
.. code-block:: python
port = mido.open_output('Port Name')
port.send(msg)
.. code-block:: python
with mido.open_input() as inport:
for msg in inport:
print(msg)
.. code-block:: python
mid = mido.MidiFile('song.mid')
for msg in mid.play():
port.send(msg)
Mido is short for MIDI objects.
Source code
-----------
https://github.com/olemb/mido/
About This Document
-------------------
This document is available at https://mido.readthedocs.io/
To build locally::
python setup.py docs
This requires Sphinx. The resulting files can be found in
``docs/_build/``.
Contents
--------
.. toctree::
:maxdepth: 2
changes
roadmap
installing
backends/index
contributing
intro
messages
frozen_messages
ports
midi_files
syx
parsing
string_encoding
socket_ports
bin
implementing_ports
implementing_backends
freezing
about_midi
message_types
meta_message_types
lib
resources
license
authors
acknowledgements
Indices and tables
------------------
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
mido-1.2.9/docs/implementing_backends.rst 0000664 0001750 0001750 00000002304 13337033714 020702 0 ustar olemb olemb 0000000 0000000 Writing a New Backend
=====================
A backend is a Python module with one or more of these::
Input -- an input port class
Output -- an output port class
IOPort -- an I/O port class
get_devices() -- returns a list of devices
Once written, the backend can be used by setting the environment
variable ``MIDO_BACKEND`` or by calling ``mido.set_backend()``. In
both cases, the path of the module is used.
``Input``
And input class for ``open_input()``. This is only required if the
backend supports input.
``Output``
And output class for ``open_output()``. This is only required if the
backend supports output.
``IOPort``
An I/O port class for ``open_ioport()``. If this is not found,
``open_ioport()`` will return ``mido.ports.IOPort(Input(),
Output())``.
``get_devices(**kwargs)``
Returns a list of devices, where each device is dictionary with at
least these three values::
{
'name': 'Some MIDI Input Port',
'is_input': True,
'is_output': False,
}
These are used to build return values for ``get_input_names()`` etc..
This function will also be available to the user directly.
For examples, see ``mido/backends/``.
mido-1.2.9/docs/resources.rst 0000664 0001750 0001750 00000003174 13337033714 016400 0 ustar olemb olemb 0000000 0000000 Resources
=========
* `MIDI Manufacturers Association `_ (midi.org)
* `Table of MIDI Messages `_
(midi.org)
* `Tech Specs & Info `_ (midi.org)
* `MIDI `_ (Wikipedia)
* `Essentials of the MIDI Protocol
`_
(Craig Stuart Sapp, CCRMA)
* `Outline of the Standard MIDI File Structure
`_ (Craig Stuart Sapp,
CCRMA)
* `Active Sense
`_
(About the active sensing message.)
* `Active Sensing `_
(Sweetwater)
* `MIDI Technical/Programming Docs `_
(Jeff Glatt)
* `Standard MIDI Files `_
(cnx.org)
* `MIDI File Parsing
`_ (Course
assignment in `Music 253 `_ at
Stanford University)
* `MIDI File Format `_
(The Sonic Spot)
* `Delta time and running status `_
(mic at recordingblogs.com)
* `MIDI meta messages
`_ (recordingblog.com)
* `Meta Message
`_
(Sound On Sound)
mido-1.2.9/docs/lib.rst 0000664 0001750 0001750 00000004451 13337034031 015124 0 ustar olemb olemb 0000000 0000000 .. _api:
Library Reference
=================
Messages
--------
.. module:: mido
.. autoclass:: Message
:members:
:inherited-members:
:undoc-members:
Ports
-----
.. autofunction:: open_input
.. autofunction:: open_output
.. autofunction:: open_ioport
.. autofunction:: get_input_names
.. autofunction:: get_output_names
.. autofunction:: get_ioport_names
Backends
--------
.. autofunction:: set_backend
.. autoclass:: Backend
:members:
:inherited-members:
:undoc-members:
Parsing
-------
.. autofunction:: parse
.. autofunction:: parse_all
.. autoclass:: Parser
:members:
:inherited-members:
:undoc-members:
MIDI Files
-----------
.. autoclass:: MidiFile
:members:
:inherited-members:
:undoc-members:
.. autoclass:: MidiTrack
:members:
:inherited-members:
:undoc-members:
.. autoclass:: MetaMessage
:members:
:inherited-members:
:undoc-members:
.. autofunction:: tick2second
.. autofunction:: second2tick
.. autofunction:: bpm2tempo
.. autofunction:: tempo2bpm
.. autofunction:: merge_tracks
SYX Files
---------
.. autofunction:: read_syx_file
.. autofunction:: write_syx_file
Port Classes and Functions
--------------------------
.. module:: mido.ports
.. autoclass:: BaseInput
:members:
:inherited-members:
:undoc-members:
.. autoclass:: BaseOutput
:members:
:inherited-members:
:undoc-members:
.. autoclass:: IOPort
:members:
:inherited-members:
:undoc-members:
.. autoclass:: MultiPort
:members:
:inherited-members:
:undoc-members:
.. autofunction:: multi_receive
.. autofunction:: multi_iter_pending
.. autofunction:: multi_send
.. autofunction:: sleep
.. autofunction:: set_sleep_time
.. autofunction:: get_sleep_time
.. autofunction:: panic_messages
.. autofunction:: reset_messages
Socket Ports
------------
.. module:: mido.sockets
.. autoclass:: PortServer
:members:
:inherited-members:
:undoc-members:
.. autoclass:: SocketPort
:members:
:inherited-members:
:undoc-members:
.. autofunction:: parse_address
Frozen Messages
---------------
.. module:: mido.frozen
.. autofunction:: freeze_message
.. autofunction:: thaw_message
.. autofunction:: is_frozen
.. autoclass:: Frozen
.. autoclass:: FrozenMessage
.. autoclass:: FrozenMetaMessage
.. autoclass:: FrozenUnknownMetaMessage
mido-1.2.9/docs/make.bat 0000664 0001750 0001750 00000011744 13167442216 015245 0 ustar olemb olemb 0000000 0000000 @ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
set I18NSPHINXOPTS=%SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^` where ^ is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. singlehtml to make a single large HTML file
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. devhelp to make HTML files and a Devhelp project
echo. epub to make an epub
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. text to make text files
echo. man to make manual pages
echo. texinfo to make Texinfo files
echo. gettext to make PO message catalogs
echo. changes to make an overview over all changed/added/deprecated items
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Mido.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Mido.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
if errorlevel 1 exit /b 1
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "text" (
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The text files are in %BUILDDIR%/text.
goto end
)
if "%1" == "man" (
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The manual pages are in %BUILDDIR%/man.
goto end
)
if "%1" == "texinfo" (
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
goto end
)
if "%1" == "gettext" (
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
if errorlevel 1 exit /b 1
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
if errorlevel 1 exit /b 1
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
if errorlevel 1 exit /b 1
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
:end
mido-1.2.9/docs/installing.rst 0000664 0001750 0001750 00000001363 13337034031 016521 0 ustar olemb olemb 0000000 0000000 Installing Mido
===============
Requirements
------------
Mido targets Python 2.7 and 3.2. It is developed and tested in Ubuntu
and Mac OS X but should also work in Windows.
There are no external dependencies unless you want to use the port
backends, which are loaded on demand.
Mido comes with backends for `RtMidi (python-rtmidi)
`_ , `PortMidi
`_ and `Pygame
`_. See :doc:`backends/index` for
help choosing a backend.
Installing
----------
To install::
pip install mido
If you want to use ports::
pip install python-rtmidi
See :doc:`backends/index` for installation instructions for other
backends.
mido-1.2.9/docs/Makefile 0000664 0001750 0001750 00000012664 13167442216 015302 0 ustar olemb olemb 0000000 0000000 # Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make ' where is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Mido.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Mido.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/Mido"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Mido"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
mido-1.2.9/MANIFEST.in 0000664 0001750 0001750 00000000706 13355500373 014440 0 ustar olemb olemb 0000000 0000000 include README.rst LICENSE tox.ini
recursive-include tests *.py
recursive-include examples *.py
recursive-include extras *.py
include docs/_static/PLACEHOLDER
recursive-include docs *.bat
recursive-include docs *.py
recursive-include docs *.rst
recursive-include docs Makefile
recursive-include examples *.py
recursive-include examples *.sh
recursive-include extras *.rst
recursive-include mido *.py
recursive-include docs/images *.svg
prune docs/_build
mido-1.2.9/LICENSE 0000664 0001750 0001750 00000002104 13167442216 013703 0 ustar olemb olemb 0000000 0000000 The MIT License
Copyright (c) 2013-infinity Ole Martin Bjørndalen
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
mido-1.2.9/PKG-INFO 0000664 0001750 0001750 00000011333 13355643235 014002 0 ustar olemb olemb 0000000 0000000 Metadata-Version: 2.1
Name: mido
Version: 1.2.9
Summary: MIDI Objects for Python
Home-page: https://mido.readthedocs.io/
Author: Ole Martin Bjorndalen
Author-email: ombdalen@gmail.com
License: MIT
Description: Mido - MIDI Objects for Python
==============================
.. image:: https://travis-ci.org/olemb/mido.svg?branch=master
:target: https://travis-ci.org/olemb/mido
Mido is a library for working with MIDI messages and ports. It's
designed to be as straight forward and Pythonic as possible:
.. code-block:: python
>>> import mido
>>> msg = mido.Message('note_on', note=60)
>>> msg.type
'note_on'
>>> msg.note
60
>>> msg.bytes()
[144, 60, 64]
>>> msg.copy(channel=2)
.. code-block:: python
port = mido.open_output('Port Name')
port.send(msg)
.. code-block:: python
with mido.open_input() as inport:
for msg in inport:
print(msg)
.. code-block:: python
mid = mido.MidiFile('song.mid')
for msg in mid.play():
port.send(msg)
Full documentation at https://mido.readthedocs.io/
Main Features
-------------
* works in Python 2 and 3.
* convenient message objects.
* supports RtMidi, PortMidi and Pygame. New backends are easy to
write.
* full support for all 18 messages defined by the MIDI standard.
* standard port API allows all kinds of input and output ports to be
used interchangeably. New port types can be written by subclassing
and overriding a few methods.
* includes a reusable MIDI parser.
* full support for MIDI files (read, write, create and play) with
complete access to every message in the file, including all common
meta messages.
* can read and write SYX files (binary and plain text).
* implements (somewhat experimental) MIDI over TCP/IP with socket
ports. This allows for example wireless MIDI between two
computers.
* includes programs for playing MIDI files, listing ports and
serving and forwarding ports over a network.
Status
------
1.2 is the third stable release.
Requirements
------------
Mido targets Python 2.7 and 3.2.
Installing
----------
::
pip install mido
If you want to use ports::
pip install python-rtmidi
See ``docs/backends/`` for other backends.
Source Code
-----------
https://github.com/olemb/mido/
License
-------
Mido is released under the terms of the `MIT license
`_.
Questions and suggestions
-------------------------
Please ask questions about Mido at
https://groups.google.com/forum/#!forum/mido-community.
This mailing list was created to give both the user community a place to ask
and hopefully also answer questions and for the developers a space to discuss
Mido development. The success of the mailing list will depend on the community
effort to also answer questions.
Looking for maintainers
-----------------------
This project is looking for somebody to take over the maintenance since the
original author @olemb is busy with other projects. We look for somebody or a
group of people who care about the code and would like to steer this project in
future by discussing proposals, reviewing pull requests, and looking over
issues. Please write to mido-community@googlegroups.com if you would like to
help out with maintenance.
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3.2
Provides-Extra: dev
Provides-Extra: ports
mido-1.2.9/README.rst 0000664 0001750 0001750 00000006076 13355500373 014377 0 ustar olemb olemb 0000000 0000000 Mido - MIDI Objects for Python
==============================
.. image:: https://travis-ci.org/olemb/mido.svg?branch=master
:target: https://travis-ci.org/olemb/mido
Mido is a library for working with MIDI messages and ports. It's
designed to be as straight forward and Pythonic as possible:
.. code-block:: python
>>> import mido
>>> msg = mido.Message('note_on', note=60)
>>> msg.type
'note_on'
>>> msg.note
60
>>> msg.bytes()
[144, 60, 64]
>>> msg.copy(channel=2)
.. code-block:: python
port = mido.open_output('Port Name')
port.send(msg)
.. code-block:: python
with mido.open_input() as inport:
for msg in inport:
print(msg)
.. code-block:: python
mid = mido.MidiFile('song.mid')
for msg in mid.play():
port.send(msg)
Full documentation at https://mido.readthedocs.io/
Main Features
-------------
* works in Python 2 and 3.
* convenient message objects.
* supports RtMidi, PortMidi and Pygame. New backends are easy to
write.
* full support for all 18 messages defined by the MIDI standard.
* standard port API allows all kinds of input and output ports to be
used interchangeably. New port types can be written by subclassing
and overriding a few methods.
* includes a reusable MIDI parser.
* full support for MIDI files (read, write, create and play) with
complete access to every message in the file, including all common
meta messages.
* can read and write SYX files (binary and plain text).
* implements (somewhat experimental) MIDI over TCP/IP with socket
ports. This allows for example wireless MIDI between two
computers.
* includes programs for playing MIDI files, listing ports and
serving and forwarding ports over a network.
Status
------
1.2 is the third stable release.
Requirements
------------
Mido targets Python 2.7 and 3.2.
Installing
----------
::
pip install mido
If you want to use ports::
pip install python-rtmidi
See ``docs/backends/`` for other backends.
Source Code
-----------
https://github.com/olemb/mido/
License
-------
Mido is released under the terms of the `MIT license
`_.
Questions and suggestions
-------------------------
Please ask questions about Mido at
https://groups.google.com/forum/#!forum/mido-community.
This mailing list was created to give both the user community a place to ask
and hopefully also answer questions and for the developers a space to discuss
Mido development. The success of the mailing list will depend on the community
effort to also answer questions.
Looking for maintainers
-----------------------
This project is looking for somebody to take over the maintenance since the
original author @olemb is busy with other projects. We look for somebody or a
group of people who care about the code and would like to steer this project in
future by discussing proposals, reviewing pull requests, and looking over
issues. Please write to mido-community@googlegroups.com if you would like to
help out with maintenance.
mido-1.2.9/setup.cfg 0000664 0001750 0001750 00000000200 13355643235 014515 0 ustar olemb olemb 0000000 0000000 [wheel]
universal = 1
[easy_install]
[tool:pytest]
norecursedirs = build dist examples
[egg_info]
tag_build =
tag_date = 0
mido-1.2.9/mido.egg-info/ 0000775 0001750 0001750 00000000000 13355643235 015326 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/mido.egg-info/top_level.txt 0000664 0001750 0001750 00000000005 13355643235 020053 0 ustar olemb olemb 0000000 0000000 mido
mido-1.2.9/mido.egg-info/SOURCES.txt 0000664 0001750 0001750 00000005546 13355643235 017224 0 ustar olemb olemb 0000000 0000000 LICENSE
MANIFEST.in
README.rst
setup.cfg
setup.py
tox.ini
bin/mido-connect
bin/mido-play
bin/mido-ports
bin/mido-serve
docs/Makefile
docs/about_midi.rst
docs/acknowledgements.rst
docs/authors.rst
docs/bin.rst
docs/changes.rst
docs/conf.py
docs/contributing.rst
docs/freezing.rst
docs/frozen_messages.rst
docs/implementing_backends.rst
docs/implementing_ports.rst
docs/index.rst
docs/installing.rst
docs/intro.rst
docs/lib.rst
docs/license.rst
docs/make.bat
docs/message_types.rst
docs/messages.rst
docs/meta_message_types.rst
docs/midi_files.rst
docs/parsing.rst
docs/ports.rst
docs/resources.rst
docs/roadmap.rst
docs/socket_ports.rst
docs/string_encoding.rst
docs/syx.rst
docs/_static/PLACEHOLDER
docs/backends/amidi.rst
docs/backends/index.rst
docs/backends/portmidi.rst
docs/backends/pygame.rst
docs/backends/rtmidi.rst
docs/backends/rtmidi_python.rst
docs/images/midi_time.svg
examples/using_rtmidi_directly.py
examples/backends/printer.py
examples/backends/rtm.py
examples/backends/use_printer.py
examples/midifiles/create_midi_file.py
examples/midifiles/midifile_to_json.py
examples/midifiles/play_midi_file.py
examples/midifiles/print_midi_file.py
examples/midifiles/test.sh
examples/ports/input_filter.py
examples/ports/list_ports.py
examples/ports/multi_receive.py
examples/ports/nonblocking_receive.py
examples/ports/queue_port.py
examples/ports/receive.py
examples/ports/send.py
examples/sockets/forward_ports.py
examples/sockets/serve_ports.py
examples/sockets/simple_client.py
examples/sockets/simple_server.py
extras/README.rst
extras/hid_joystick.py
mido/__about__.py
mido/__init__.py
mido/frozen.py
mido/parser.py
mido/ports.py
mido/py2.py
mido/sockets.py
mido/syx.py
mido/tokenizer.py
mido/version.py
mido.egg-info/PKG-INFO
mido.egg-info/SOURCES.txt
mido.egg-info/dependency_links.txt
mido.egg-info/not-zip-safe
mido.egg-info/requires.txt
mido.egg-info/top_level.txt
mido/backends/__init__.py
mido/backends/_parser_queue.py
mido/backends/amidi.py
mido/backends/backend.py
mido/backends/portmidi.py
mido/backends/portmidi_init.py
mido/backends/pygame.py
mido/backends/rtmidi.py
mido/backends/rtmidi_python.py
mido/backends/rtmidi_utils.py
mido/messages/__init__.py
mido/messages/checks.py
mido/messages/decode.py
mido/messages/encode.py
mido/messages/messages.py
mido/messages/specs.py
mido/messages/strings.py
mido/midifiles/__init__.py
mido/midifiles/meta.py
mido/midifiles/midifiles.py
mido/midifiles/tracks.py
mido/midifiles/units.py
tests/test_frozen.py
tests/test_parser.py
tests/test_ports.py
tests/test_sockets.py
tests/test_syx.py
tests/test_tokenizer.py
tests/backends/test_backend.py
tests/backends/test_rtmidi.py
tests/messages/test_checks.py
tests/messages/test_decode.py
tests/messages/test_encode.py
tests/messages/test_messages.py
tests/messages/test_strings.py
tests/midifiles/test_meta.py
tests/midifiles/test_midifiles.py
tests/midifiles/test_tracks.py
tests/midifiles/test_units.py mido-1.2.9/mido.egg-info/dependency_links.txt 0000664 0001750 0001750 00000000001 13355643235 021374 0 ustar olemb olemb 0000000 0000000
mido-1.2.9/mido.egg-info/PKG-INFO 0000664 0001750 0001750 00000011333 13355643235 016424 0 ustar olemb olemb 0000000 0000000 Metadata-Version: 2.1
Name: mido
Version: 1.2.9
Summary: MIDI Objects for Python
Home-page: https://mido.readthedocs.io/
Author: Ole Martin Bjorndalen
Author-email: ombdalen@gmail.com
License: MIT
Description: Mido - MIDI Objects for Python
==============================
.. image:: https://travis-ci.org/olemb/mido.svg?branch=master
:target: https://travis-ci.org/olemb/mido
Mido is a library for working with MIDI messages and ports. It's
designed to be as straight forward and Pythonic as possible:
.. code-block:: python
>>> import mido
>>> msg = mido.Message('note_on', note=60)
>>> msg.type
'note_on'
>>> msg.note
60
>>> msg.bytes()
[144, 60, 64]
>>> msg.copy(channel=2)
.. code-block:: python
port = mido.open_output('Port Name')
port.send(msg)
.. code-block:: python
with mido.open_input() as inport:
for msg in inport:
print(msg)
.. code-block:: python
mid = mido.MidiFile('song.mid')
for msg in mid.play():
port.send(msg)
Full documentation at https://mido.readthedocs.io/
Main Features
-------------
* works in Python 2 and 3.
* convenient message objects.
* supports RtMidi, PortMidi and Pygame. New backends are easy to
write.
* full support for all 18 messages defined by the MIDI standard.
* standard port API allows all kinds of input and output ports to be
used interchangeably. New port types can be written by subclassing
and overriding a few methods.
* includes a reusable MIDI parser.
* full support for MIDI files (read, write, create and play) with
complete access to every message in the file, including all common
meta messages.
* can read and write SYX files (binary and plain text).
* implements (somewhat experimental) MIDI over TCP/IP with socket
ports. This allows for example wireless MIDI between two
computers.
* includes programs for playing MIDI files, listing ports and
serving and forwarding ports over a network.
Status
------
1.2 is the third stable release.
Requirements
------------
Mido targets Python 2.7 and 3.2.
Installing
----------
::
pip install mido
If you want to use ports::
pip install python-rtmidi
See ``docs/backends/`` for other backends.
Source Code
-----------
https://github.com/olemb/mido/
License
-------
Mido is released under the terms of the `MIT license
`_.
Questions and suggestions
-------------------------
Please ask questions about Mido at
https://groups.google.com/forum/#!forum/mido-community.
This mailing list was created to give both the user community a place to ask
and hopefully also answer questions and for the developers a space to discuss
Mido development. The success of the mailing list will depend on the community
effort to also answer questions.
Looking for maintainers
-----------------------
This project is looking for somebody to take over the maintenance since the
original author @olemb is busy with other projects. We look for somebody or a
group of people who care about the code and would like to steer this project in
future by discussing proposals, reviewing pull requests, and looking over
issues. Please write to mido-community@googlegroups.com if you would like to
help out with maintenance.
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3.2
Provides-Extra: dev
Provides-Extra: ports
mido-1.2.9/mido.egg-info/not-zip-safe 0000664 0001750 0001750 00000000001 13227134323 017543 0 ustar olemb olemb 0000000 0000000
mido-1.2.9/mido.egg-info/requires.txt 0000664 0001750 0001750 00000000157 13355643235 017731 0 ustar olemb olemb 0000000 0000000
[dev]
check-manifest>=0.35
flake8>=3.4.1
pytest>=3.2.2
sphinx>=1.6.3
tox>=2.8.2
[ports]
python-rtmidi>=1.1.0
mido-1.2.9/bin/ 0000775 0001750 0001750 00000000000 13355643235 013454 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/bin/mido-connect 0000775 0001750 0001750 00000001531 13167442216 015756 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
"""
Forward all messages from one or more ports to server.
"""
import argparse
import mido
def parse_args():
parser = argparse.ArgumentParser(description=__doc__)
arg = parser.add_argument
arg('address',
metavar='ADDRESS',
help='host:port to connect to')
arg('ports',
metavar='PORT',
nargs='+',
help='input ports to listen to')
return parser.parse_args()
args = parse_args()
try:
hostname, port = mido.sockets.parse_address(args.address)
ports = [mido.open_input(name) for name in args.ports]
with mido.sockets.connect(hostname, port) as server_port:
print('Connected.')
for message in mido.ports.multi_receive(ports):
print('Sending {}'.format(message))
server_port.send(message)
except KeyboardInterrupt:
pass
mido-1.2.9/bin/mido-play 0000775 0001750 0001750 00000004347 13337034031 015271 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
"""
Play MIDI file on output port.
Example:
mido-play some_file.mid
Todo:
- add option for printing messages
"""
from __future__ import print_function, division
import sys
import argparse
import mido
from mido import MidiFile, Message, tempo2bpm
def parse_args():
parser = argparse.ArgumentParser(description=__doc__)
arg = parser.add_argument
arg('-o', '--output-port',
help='Mido port to send output to')
arg('-m', '--print-messages',
dest='print_messages',
action='store_true',
default=False,
help='Print messages as they are played back')
arg('-q', '--quiet',
dest='quiet',
action='store_true',
default=False,
help='print nothing')
arg('files',
metavar='FILE',
nargs='+',
help='MIDI file to play')
return parser.parse_args()
def play_file(output, filename, print_messages):
midi_file = MidiFile(filename)
print('Playing {}.'.format(midi_file.filename))
length = midi_file.length
print('Song length: {} minutes, {} seconds.'.format(
int(length / 60),
int(length % 60)))
print('Tracks:')
for i, track in enumerate(midi_file.tracks):
print(' {:2d}: {!r}'.format(i, track.name.strip()))
for message in midi_file.play(meta_messages=True):
if print_messages:
sys.stdout.write(repr(message) + '\n')
sys.stdout.flush()
if isinstance(message, Message):
output.send(message)
elif message.type == 'set_tempo':
print('Tempo changed to {:.1f} BPM.'.format(
tempo2bpm(message.tempo)))
print()
def main():
try:
with mido.open_output(args.output_port) as output:
print('Using output {!r}.'.format(output.name))
output.reset()
try:
for filename in args.files:
play_file(output, filename, args.print_messages)
finally:
print()
output.reset()
except KeyboardInterrupt:
pass
args = parse_args()
if args.quiet:
def print(*args):
pass
main()
mido-1.2.9/bin/mido-ports 0000775 0001750 0001750 00000001376 13337034031 015472 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
"""
List available PortMidi ports.
"""
from __future__ import print_function
import os
import sys
import mido
def print_ports(heading, port_names):
print(heading)
for name in port_names:
print(" '{}'".format(name))
print()
print()
print_ports('Available input Ports:', mido.get_input_names())
print_ports('Available output Ports:', mido.get_output_names())
for name in ['MIDO_DEAFULT_INPUT',
'MIDO_DEFAULT_OUTPUT',
'MIDO_DEFAULT_IOPORT',
'MIDO_BACKEND']:
try:
value = os.environ[name]
print('{}={!r}'.format(name, value))
except LookupError:
print('{} not set.'.format(name))
print()
print('Using backend {}.'.format(mido.backend.name))
print()
mido-1.2.9/bin/mido-serve 0000775 0001750 0001750 00000001624 13167442216 015454 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
"""
Serve one or more output ports. Every message received on any of the
connected sockets will be sent to every output port.
"""
import argparse
import mido
from mido import sockets
from mido.ports import MultiPort
def parse_args():
parser = argparse.ArgumentParser(description=__doc__)
arg = parser.add_argument
arg('address',
metavar='ADDRESS',
help='host:port to serve on')
arg('ports',
metavar='PORT',
nargs='+',
help='output port to serve')
return parser.parse_args()
args = parse_args()
try:
out = MultiPort([mido.open_output(name) for name in args.ports])
(hostname, port) = sockets.parse_address(args.address)
with sockets.PortServer(hostname, port) as server:
for message in server:
print('Received {}'.format(message))
out.send(message)
except KeyboardInterrupt:
pass
mido-1.2.9/extras/ 0000775 0001750 0001750 00000000000 13355643235 014212 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/extras/hid_joystick.py 0000664 0001750 0001750 00000015275 13337034031 017245 0 ustar olemb olemb 0000000 0000000 """Read from /dev/input/js0 and return as dictionaries.
If you have pygame it is easier and more portable to do something
like::
import pygame.joystick
from pygame.event import event_name
pygame.init()
pygame.joystick.init()
js = pygame.joystick.Joystick(0)
js.init()
while True:
for event in pygame.event.get():
if event.axis == 0:
print(event)
Init:
8 = init?
Time stamp |
(ms since boot) |
--+--+--+-- | -- Button number
f0 fb 37 09 00 00 81 00
f0 fb 37 09 00 00 81 01
f0 fb 37 09 00 00 81 02
f0 fb 37 09 00 00 81 03
f0 fb 37 09 00 00 81 04
f0 fb 37 09 00 00 81 05
f0 fb 37 09 00 00 81 06
f0 fb 37 09 00 00 81 07
f0 fb 37 09 00 00 81 08
f0 fb 37 09 00 00 81 09
f0 fb 37 09 00 00 81 0a
f0 fb 37 09 00 00 81 0b
f0 fb 37 09 00 00 82 00
f0 fb 37 09 00 00 82 01
f0 fb 37 09 00 00 82 02
f0 fb 37 09 00 00 82 03
f0 fb 37 09 00 00 82 04
f0 fb 37 09 00 00 82 05
--+-- |
| 1 = button, 2 =
|
value (little endian unsigned)
button down
|
98 f0 2f 09 01 00 01 00 1 down
08 fa 2f 09 00 00 01 00 1 up
2c 6a 31 09 01 00 01 01 2 down
04 73 31 09 00 00 01 01 2 up
48 bf 32 09 01 00 01 02 3 down
f8 c4 32 09 00 00 01 02 3 up
Logitech PS2-style gamepad:
axis 0 == left stick -left / right (left is negative)
axis 1 == left stick -up / down (up is negative)
axis 2 == right stick -left / right
axis 3 == right stick -up / down
axis 4 == plus stick -left / right (when mode is off), values min/0/max
axis 5 == plus stick -up / down (when mode is off, values min/0/max
The + stick has two modes. When the mode light is off, it sends axis
4/5. When mode is on, it sends axis 0/1. The values are -32767, 0, and 32767.
Other axis have values from -32767 to 32767 as well.
"""
import struct
import select
JS_EVENT_BUTTON = 0x1
JS_EVENT_AXIS = 0x2
JS_EVENT_INIT = 0x80
def read_event(device):
data = device.read(8)
event = {}
(event['time'],
event['value'],
event['type'],
event['number']) = struct.unpack('IhBB', data)
event['init'] = bool(event['type'] & JS_EVENT_INIT)
event['type'] &= 0x7f # Strip away the flag bits (JS_EVENT_INIT etc.)
if event['type'] != JS_EVENT_BUTTON:
event['normalized_value'] = \
float(event['value']) / 0x7fff # Normalize to -1..1
event['type'] = {1: 'button', 2: 'axis'}[event['type']]
return event
def read_events(device_name):
with open(device_name, 'rb') as device:
while True:
yield read_event(device)
def panic(port):
"""
Send "All Notes Off" and "Reset All Controllers" on
all channels.
"""
for channel in range(16):
for control in [121, 123]:
message = mido.Message('control_change',
channel=channel,
control=control, value=0)
print(message)
port.send(message)
class Monophonic(object):
# Todo: this assumes everything is on channel 0!
def __init__(self, output, channel=0):
self.output = output
self.notes = set()
self.current_note = None
self.channel = channel
def send(self, message):
if message.type not in ['note_on', 'note_off'] or \
message.channel != self.channel:
self.output.send(message)
return
if message.type == 'note_on':
self.notes.add(message.note)
elif message.type == 'note_off':
if message.note in self.notes:
self.notes.remove(message.note)
print(self.notes)
try:
note = min(self.notes)
except ValueError:
note = None
if note == self.current_note:
return # Same note as before, no change.
if self.current_note is not None:
off = mido.Message('note_off',
note=self.current_note,
velocity=message.velocity)
print(off)
self.output.send(off)
self.current_note = None
if note is not None:
on = mido.Message('note_on',
note=note,
velocity=message.velocity)
print(on)
self.output.send(on)
self.current_note = note
def play_scale(dev, out):
# out = Monophonic(out, channel=0)
# Major scale.
scale = [0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19]
# program = 16 # Organ
program = 74
out.send(mido.Message('program_change', program=program))
while True:
event = read_event(dev)
if event['init']:
continue # Skip init events.
if event['type'] == 'button':
# Convert to D-major scale starting at middle D.
note = 62 + 12 + scale[event['number']]
if event['value']:
type_ = 'note_on'
else:
type_ = 'note_off'
message = mido.Message(type_, note=note, velocity=64)
out.send(message)
# elif event['type'] == 'axis':
# if event['number'] == 0:
# pitch_scale = mido.messages.MAX_PITCHWHEEL
# pitch = int(event['normalized_value'] * pitch_scale)
# out.send(mido.Message('pitchwheel', pitch=pitch))
def play_drums(dev, out):
# http://www.midi.org/techspecs/gm1sound.php
note_mapping = {
2: 35, # Acoustic Bass Drum
6: 38, # Acoustic Snare
1: 41, # Low Floor Tom
4: 47, # Low Mid Tom
3: 50, # High Tom
8: 51, # Ride Cymbal
5: 42, # Closed Hi Hat
7: 46, # Open Hi Hat
9: 52, # Chinese Cymbal
10: 55, # Splash Cymbal
}
while True:
event = read_event(dev)
if event['init']:
continue
if event['type'] == 'button':
print(event)
button = event['number'] + 1 # Number buttons starting with 1.
if button not in note_mapping:
continue
if event['value']:
type_ = 'note_on'
else:
type_ = 'note_off'
note = note_mapping[button]
message = mido.Message(type_, channel=9, note=note, velocity=64)
print(message)
out.send(message)
if __name__ == '__main__':
import sys
import mido
with open('/dev/input/js0') as dev:
with mido.open_output('SD-20 Part A') as out:
try:
# play_drums(dev, out)
play_scale(dev, out)
finally:
panic(out)
mido-1.2.9/extras/README.rst 0000664 0001750 0001750 00000000427 13337034031 015670 0 ustar olemb olemb 0000000 0000000 Disclaimer
===========
These are experimental versions of functionality that may be rolled
into Mido some time in the future. Until the, proceed at your own
risk. (Where risk means the danger the code will crash or that the API
will change once you had just gotten used to it.)
mido-1.2.9/examples/ 0000775 0001750 0001750 00000000000 13355643235 014522 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/examples/midifiles/ 0000775 0001750 0001750 00000000000 13355643235 016467 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/examples/midifiles/midifile_to_json.py 0000664 0001750 0001750 00000000533 13355500373 022352 0 ustar olemb olemb 0000000 0000000 import sys
import json
import mido
def midifile_to_dict(mid):
tracks = []
for track in mid.tracks:
tracks.append([vars(msg).copy() for msg in track])
return {
'ticks_per_beat': mid.ticks_per_beat,
'tracks': tracks,
}
mid = mido.MidiFile(sys.argv[1])
print(json.dumps(midifile_to_dict(mid), indent=2))
mido-1.2.9/examples/midifiles/test.sh 0000775 0001750 0001750 00000000207 13167442216 020001 0 ustar olemb olemb 0000000 0000000 #!/bin/bash
function play {
# mido-play test.mid
pmidi -p 20:0 -d 0 test.mid
}
./create_midi_file.py && xxd test.mid && play
mido-1.2.9/examples/midifiles/print_midi_file.py 0000775 0001750 0001750 00000000704 13337034031 022166 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
"""
Open a MIDI file and print every message in every track.
Support for MIDI files is still experimental.
"""
import sys
from mido import MidiFile
if __name__ == '__main__':
filename = sys.argv[1]
midi_file = MidiFile(filename)
for i, track in enumerate(midi_file.tracks):
sys.stdout.write('=== Track {}\n'.format(i))
for message in track:
sys.stdout.write(' {!r}\n'.format(message))
mido-1.2.9/examples/midifiles/create_midi_file.py 0000775 0001750 0001750 00000001057 13355500373 022306 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
"""
Create a new MIDI file with some random notes.
The file is saved to test.mid.
"""
import random
from mido import Message, MidiFile, MidiTrack
notes = [64, 64+7, 64+12]
outfile = MidiFile()
track = MidiTrack()
outfile.tracks.append(track)
track.append(Message('program_change', program=12))
delta = 16
for i in range(4):
note = random.choice(notes)
track.append(Message('note_on', note=note, velocity=100, time=delta))
track.append(Message('note_off', note=note, velocity=100, time=delta))
outfile.save('test.mid')
mido-1.2.9/examples/midifiles/play_midi_file.py 0000775 0001750 0001750 00000001010 13355500373 021775 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
"""
Play MIDI file on output port.
Run with (for example):
./play_midi_file.py 'SH-201 MIDI 1' 'test.mid'
"""
import sys
import mido
from mido import MidiFile
filename = sys.argv[1]
if len(sys.argv) == 3:
portname = sys.argv[2]
else:
portname = None
with mido.open_output(portname) as output:
try:
for message in MidiFile(filename).play():
print(message)
output.send(message)
except KeyboardInterrupt:
print()
output.reset()
mido-1.2.9/examples/backends/ 0000775 0001750 0001750 00000000000 13355643235 016274 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/examples/backends/use_printer.py 0000664 0001750 0001750 00000001031 13355500373 021173 0 ustar olemb olemb 0000000 0000000 """
Try out the new printer port backend.
It also works with MIDO_BACKEND, so you can do:
$ MIDO_BACKEND=printer python
>>> import mido
>>> mido.get_output_names()
['The Printer Port']
"""
import mido
mido.set_backend('printer')
print('Available outputs: {!r}'.format(mido.get_output_names()))
with mido.open_output() as port:
print('Using {}.'.format(port))
port.send(mido.Message('program_change', program=10))
for i in [1, 2, 3]:
port.send(mido.Message('control_change', control=1, value=i))
mido-1.2.9/examples/backends/rtm.py 0000664 0001750 0001750 00000005761 13355500373 017454 0 ustar olemb olemb 0000000 0000000 """
Experimental backend for rtmidi-python:
http://github.com/superquadratic/rtmidi-python
- Doesn't work with Python 3.3:
File "rtmidi_python.pyx", line 61, in rtmidi_python.MidiIn.__cinit__
(rtmidi_ python.cpp:1214)
TypeError: expected bytes, str found
- Virtual ports don't show up, so other programs can't connect to them.
- There is no way to select API.
Other than that, it works exactly like the included python-rtmidi
backend.
"""
from __future__ import absolute_import
import rtmidi_python as rtmidi
from mido.ports import BaseInput, BaseOutput
def get_devices():
devices = []
input_names = set(rtmidi.MidiIn().ports)
output_names = set(rtmidi.MidiOut().ports)
for name in sorted(input_names | output_names):
devices.append({
'name': name,
'is_input': name in input_names,
'is_output': name in output_names,
})
return devices
class PortCommon(object):
def _open(self, virtual=False, callback=None):
opening_input = hasattr(self, 'receive')
if opening_input:
self._rt = rtmidi.MidiIn()
self._rt.ignore_types(False, False, False)
if callback:
def callback_wrapper(message, delta_time):
self._parser.feed(message)
for message in self._parser:
callback(message)
self._rt.callback = self._callback = callback_wrapper
self._has_callback = True
else:
self._has_callback = False
else:
self._rt = rtmidi.MidiOut()
# Turn of ignore of sysex, time and active_sensing.
ports = self._rt.ports
if virtual:
if self.name is None:
raise IOError('virtual port must have a name')
self._rt.open_virtual_port(self.name)
else:
if self.name is None:
# Todo: this could fail if list is empty.
# In RtMidi, the default port is the first port.
try:
self.name = ports[0]
except IndexError:
raise IOError('no ports available')
try:
port_id = ports.index(self.name)
except ValueError:
raise IOError('unknown port {!r}'.format(self.name))
self._rt.open_port(port_id)
self._device_type = 'rtmidi_python'
def _close(self):
self._rt.close_port()
class Input(PortCommon, BaseInput):
# Todo: sysex messages do not arrive here.
def _receive(self, block=True):
if self._has_callback:
raise IOError('a callback is currently set for this port')
while True:
(message, delta_time) = self._rt.get_message()
if message is None:
break
else:
self._parser.feed(message)
class Output(PortCommon, BaseOutput):
def _send(self, message):
self._rt.send_message(message.bytes())
mido-1.2.9/examples/backends/printer.py 0000664 0001750 0001750 00000001100 13355500373 020314 0 ustar olemb olemb 0000000 0000000 """
A simple custom backend with an output port type which prints messages
to stdout.
"""
from mido.ports import BaseOutput
def get_devices():
return [{'name': 'The Print Port',
'is_input': False,
'is_output': True}]
class Output(BaseOutput):
def _open(self, **kwargs):
device = get_devices()[0]
if self.name is None:
self.name = device['name']
elif self.name != device['name']:
raise ValueError('unknown port {!r}'.format(self.name))
def _send(self, message):
print(message)
mido-1.2.9/examples/using_rtmidi_directly.py 0000664 0001750 0001750 00000001242 13355500373 021462 0 ustar olemb olemb 0000000 0000000 """
First example from here modified to use Mido messages:
http://pypi.python.org/pypi/python-rtmidi/
"""
import time
import mido
import rtmidi
midiout = rtmidi.MidiOut()
available_ports = midiout.get_ports()
if available_ports:
midiout.open_port(0)
else:
midiout.open_virtual_port("My virtual output")
# Original example:
# note_on = [0x99, 60, 112] # channel 10, middle C, velocity 112
# note_off = [0x89, 60, 0]
note_on = mido.Message('note_on', channel=9, note=60, velocity=112).bytes()
note_off = mido.Message('note_off', channel=9, note=60, velocity=0).bytes()
midiout.send_message(note_on)
time.sleep(0.5)
midiout.send_message(note_off)
del midiout
mido-1.2.9/examples/sockets/ 0000775 0001750 0001750 00000000000 13355643235 016175 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/examples/sockets/forward_ports.py 0000775 0001750 0001750 00000000764 13167442216 021451 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
"""
Forward all messages from one or more ports to server.
Example:
python forward_ports.py localhost:8080 'Keyboard MIDI 1'
"""
import sys
import mido
host, port = mido.sockets.parse_address(sys.argv[1])
ports = [mido.open_input(name) for name in sys.argv[2:]]
with mido.sockets.connect(host, port) as server_port:
print('Connected.')
for message in mido.ports.multi_receive(ports):
print('Sending {}'.format(message))
server_port.send(message)
mido-1.2.9/examples/sockets/simple_client.py 0000775 0001750 0001750 00000001604 13167442216 021377 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
"""
Simple client that sends program_change messages to server at timed
intervals.
Example:
python simple_client.py localhost:8080
"""
import sys
import time
import random
import mido
if sys.argv[1:]:
address = sys.argv[1]
else:
address = 'localhost:9080'
host, port = mido.sockets.parse_address(address)
notes = [60, 67, 72, 79, 84, 79, 72, 67, 60]
on = mido.Message('note_on', velocity=100)
off = mido.Message('note_off', velocity=100)
base = random.randrange(12)
print('Connecting to {}'.format(address))
with mido.sockets.connect(host, port) as server_port:
try:
message = mido.Message('program_change')
for note in notes:
on.note = off.note = base + note
server_port.send(on)
time.sleep(0.05)
server_port.send(off)
time.sleep(0.1)
finally:
server_port.reset()
mido-1.2.9/examples/sockets/simple_server.py 0000775 0001750 0001750 00000000770 13355500373 021430 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
"""
Simple server that just prints every message it receives.
python simple_server.py localhost:8080
"""
import sys
from mido import sockets
if sys.argv[1:]:
address = sys.argv[1]
else:
address = 'localhost:9080'
try:
(hostname, portno) = sockets.parse_address(address)
print('Serving on {}'.format(address))
with sockets.PortServer(hostname, portno) as server:
for message in server:
print(message)
except KeyboardInterrupt:
pass
mido-1.2.9/examples/sockets/serve_ports.py 0000775 0001750 0001750 00000001466 13167442216 021131 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
"""
Serve one or more output ports.
Every message received on any of the connected sockets will be sent to
every output port.
Example:
python serve_ports.py :8080 'SH-201' 'SD-20 Part A'
This simply iterates through all incoming messages. More advanced and
flexible servers can be written by calling the ``accept()`` and
``accept(block=False) methods directly. See PortServer.__init__() for
an example.
"""
import sys
import mido
from mido import sockets
from mido.ports import MultiPort
# Todo: do this with a argument parser.
out = MultiPort([mido.open_output(name) for name in sys.argv[2:]])
(host, port) = sockets.parse_address(sys.argv[1])
with sockets.PortServer(host, port) as server:
for message in server:
print('Received {}'.format(message))
out.send(message)
mido-1.2.9/examples/ports/ 0000775 0001750 0001750 00000000000 13355643235 015671 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/examples/ports/input_filter.py 0000775 0001750 0001750 00000001537 13355500373 020753 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
"""
Receive messages on the input port and print messages with a specific
channel (defaults to 0).
Usage:
python example_input_filter.py portname [CHANNEL] [...]
"""
from __future__ import print_function
import sys
import mido
def accept_notes(port):
"""Only let note_on and note_off messages through."""
for message in port:
if message.type in ('note_on', 'note_off'):
yield message
if len(sys.argv) > 1:
portname = sys.argv[1]
else:
portname = None # Use default port
try:
with mido.open_input(portname) as port:
print('Using {}'.format(port))
print("Ignoring everything but 'note_on' and 'note_off'.")
print('Waiting for notes...')
for message in accept_notes(port):
print('Received {}'.format(message))
except KeyboardInterrupt:
pass
mido-1.2.9/examples/ports/send.py 0000775 0001750 0001750 00000001506 13337034031 017165 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
"""
Send random notes to the output port.
"""
from __future__ import print_function
import sys
import time
import random
import mido
from mido import Message
if len(sys.argv) > 1:
portname = sys.argv[1]
else:
portname = None # Use default port
# A pentatonic scale
notes = [60, 62, 64, 67, 69, 72]
try:
with mido.open_output(portname, autoreset=True) as port:
print('Using {}'.format(port))
while True:
note = random.choice(notes)
on = Message('note_on', note=note)
print('Sending {}'.format(on))
port.send(on)
time.sleep(0.05)
off = Message('note_off', note=note)
print('Sending {}'.format(off))
port.send(off)
time.sleep(0.1)
except KeyboardInterrupt:
pass
print()
mido-1.2.9/examples/ports/queue_port.py 0000664 0001750 0001750 00000001521 13355500373 020425 0 ustar olemb olemb 0000000 0000000 """A port interface around a queue.
This allows you to create internal ports to send messages between threads.
"""
import mido
from mido import ports
try:
import queue
except ImportError:
# Python 2.
import Queue as queue
class QueuePort(ports.BaseIOPort):
# We don't need locking since the queue takes care of that.
_locking = False
def _open(self):
self.queue = queue.Queue()
def _send(self, msg):
self.queue.put(msg)
def _receive(self, block=True):
try:
return self.queue.get(block=block)
except queue.Empty:
return None
def _device_type(self):
return 'Queue'
def main():
msg = mido.Message('clock')
port = QueuePort()
print(port.poll())
port.send(msg)
print(port.receive())
if __name__ == '__main__':
main()
mido-1.2.9/examples/ports/multi_receive.py 0000775 0001750 0001750 00000000654 13167442216 021104 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
"""
Receive messages from multiple ports.
"""
import mido
from mido.ports import multi_receive
# Open all available inputs.
ports = [mido.open_input(name) for name in mido.get_input_names()]
for port in ports:
print('Using {}'.format(port))
print('Waiting for messages...')
try:
for message in multi_receive(ports):
print('Received {}'.format(message))
except KeyboardInterrupt:
pass
mido-1.2.9/examples/ports/receive.py 0000775 0001750 0001750 00000001006 13337034031 017651 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
"""
Receive messages from the input port and print them out.
"""
from __future__ import print_function
import sys
import mido
if len(sys.argv) > 1:
portname = sys.argv[1]
else:
portname = None # Use default port
try:
with mido.open_input(portname) as port:
print('Using {}'.format(port))
print('Waiting for messages...')
for message in port:
print('Received {}'.format(message))
sys.stdout.flush()
except KeyboardInterrupt:
pass
mido-1.2.9/examples/ports/nonblocking_receive.py 0000775 0001750 0001750 00000001105 13355500373 022243 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
"""
Example of non-blocking reception from input port.
"""
from __future__ import print_function
import sys
import time
import mido
if len(sys.argv) > 1:
portname = sys.argv[1]
else:
portname = None # Use default port
try:
with mido.open_input(portname) as port:
print('Using {}'.format(port))
while True:
for message in port.iter_pending():
print('Received {}'.format(message))
print('Doing something else for a while...')
time.sleep(0.5)
except KeyboardInterrupt:
pass
mido-1.2.9/examples/ports/list_ports.py 0000775 0001750 0001750 00000000615 13355500373 020445 0 ustar olemb olemb 0000000 0000000 #!/usr/bin/env python
"""
List available ports.
This is a simple version of mido-ports.
"""
from __future__ import print_function
import mido
def print_ports(heading, port_names):
print(heading)
for name in port_names:
print(" '{}'".format(name))
print()
print()
print_ports('Input Ports:', mido.get_input_names())
print_ports('Output Ports:', mido.get_output_names())
mido-1.2.9/mido/ 0000775 0001750 0001750 00000000000 13355643235 013634 5 ustar olemb olemb 0000000 0000000 mido-1.2.9/mido/frozen.py 0000664 0001750 0001750 00000004450 13355500373 015507 0 ustar olemb olemb 0000000 0000000 from .messages import Message
from .midifiles import MetaMessage, UnknownMetaMessage
class Frozen(object):
def __repr__(self):
text = super(Frozen, self).__repr__()
return '