circuitpython-tmidi 1.0.4__tar.gz → 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- circuitpython_tmidi-2.0.0/.pre-commit-config.yaml +21 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/.readthedocs.yaml +3 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/PKG-INFO +10 -3
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/README.rst +9 -2
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/circuitpython_tmidi.egg-info/PKG-INFO +10 -3
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/circuitpython_tmidi.egg-info/SOURCES.txt +3 -0
- circuitpython_tmidi-2.0.0/docs/examples.rst +37 -0
- circuitpython_tmidi-2.0.0/examples/tmidi_simple_arpeggiator.py +58 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/examples/tmidi_simple_receiver.py +16 -2
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/examples/tmidi_simpletest.py +2 -2
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/pyproject.toml +1 -1
- circuitpython_tmidi-2.0.0/tests/__init__.py +2 -0
- circuitpython_tmidi-2.0.0/tests/test_tmidi_messages.py +46 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/tests/test_tmidi_one.py +1 -2
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/tmidi.py +79 -21
- circuitpython_tmidi-1.0.4/.pre-commit-config.yaml +0 -46
- circuitpython_tmidi-1.0.4/docs/examples.rst +0 -18
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/.github/workflows/build.yml +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/.github/workflows/failure-help-text.yml +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/.github/workflows/release_gh.yml +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/.github/workflows/release_pypi.yml +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/.gitignore +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/.pylintrc +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/CODE_OF_CONDUCT.md +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/LICENSE +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/LICENSES/CC-BY-4.0.txt +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/LICENSES/MIT.txt +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/LICENSES/Unlicense.txt +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/README.rst.license +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/circuitpython_tmidi.egg-info/dependency_links.txt +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/circuitpython_tmidi.egg-info/requires.txt +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/circuitpython_tmidi.egg-info/top_level.txt +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/docs/_static/favicon.ico +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/docs/_static/favicon.ico.license +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/docs/api.rst +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/docs/api.rst.license +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/docs/conf.py +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/docs/examples.rst.license +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/docs/index.rst +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/docs/index.rst.license +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/docs/requirements.txt +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/examples/tmidi_midiforwarder.py +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/examples/tmidi_multiple_receiver.py +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/optional_requirements.txt +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/requirements.txt +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/setup.cfg +0 -0
- {circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/tests/test_one.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Unlicense
|
|
4
|
+
|
|
5
|
+
repos:
|
|
6
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
7
|
+
rev: v4.5.0
|
|
8
|
+
hooks:
|
|
9
|
+
- id: check-yaml
|
|
10
|
+
- id: end-of-file-fixer
|
|
11
|
+
- id: trailing-whitespace
|
|
12
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
13
|
+
rev: v0.3.4
|
|
14
|
+
hooks:
|
|
15
|
+
- id: ruff-format
|
|
16
|
+
- id: ruff
|
|
17
|
+
args: ["--fix"]
|
|
18
|
+
- repo: https://github.com/fsfe/reuse-tool
|
|
19
|
+
rev: v3.0.1
|
|
20
|
+
hooks:
|
|
21
|
+
- id: reuse
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: circuitpython-tmidi
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: MIDI library for CircuitPython
|
|
5
5
|
Author-email: Tod Kurt <tod@todbot.com>
|
|
6
6
|
License: MIT
|
|
@@ -109,8 +109,9 @@ Usage Example
|
|
|
109
109
|
|
|
110
110
|
midi_usb = tmidi.MIDI(midi_in=usb_midi.ports[0], midi_out=usb_midi.ports[1])
|
|
111
111
|
|
|
112
|
-
def send_midi_panic(
|
|
113
|
-
|
|
112
|
+
def send_midi_panic(midi_channel=1):
|
|
113
|
+
ch = midi_channel-1
|
|
114
|
+
msg = tmidi.Message(tmidi.NOTE_OFF, channel=ch) # channels are 0-indexed
|
|
114
115
|
for notenum in range(128):
|
|
115
116
|
msg.note = notenum
|
|
116
117
|
msg.velocity = 0
|
|
@@ -128,6 +129,12 @@ API documentation for this library can be found on `Read the Docs <https://circu
|
|
|
128
129
|
For information on building library documentation, please check out
|
|
129
130
|
`this guide <https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/sharing-our-docs-on-readthedocs#sphinx-5-1>`_.
|
|
130
131
|
|
|
132
|
+
Testing
|
|
133
|
+
=======
|
|
134
|
+
|
|
135
|
+
Install ``pytest`` with ``pip3 install pytest --upgrade`` and run ``pytest -v``
|
|
136
|
+
To build docs:
|
|
137
|
+
|
|
131
138
|
Contributing
|
|
132
139
|
============
|
|
133
140
|
|
|
@@ -89,8 +89,9 @@ Usage Example
|
|
|
89
89
|
|
|
90
90
|
midi_usb = tmidi.MIDI(midi_in=usb_midi.ports[0], midi_out=usb_midi.ports[1])
|
|
91
91
|
|
|
92
|
-
def send_midi_panic(
|
|
93
|
-
|
|
92
|
+
def send_midi_panic(midi_channel=1):
|
|
93
|
+
ch = midi_channel-1
|
|
94
|
+
msg = tmidi.Message(tmidi.NOTE_OFF, channel=ch) # channels are 0-indexed
|
|
94
95
|
for notenum in range(128):
|
|
95
96
|
msg.note = notenum
|
|
96
97
|
msg.velocity = 0
|
|
@@ -108,6 +109,12 @@ API documentation for this library can be found on `Read the Docs <https://circu
|
|
|
108
109
|
For information on building library documentation, please check out
|
|
109
110
|
`this guide <https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/sharing-our-docs-on-readthedocs#sphinx-5-1>`_.
|
|
110
111
|
|
|
112
|
+
Testing
|
|
113
|
+
=======
|
|
114
|
+
|
|
115
|
+
Install ``pytest`` with ``pip3 install pytest --upgrade`` and run ``pytest -v``
|
|
116
|
+
To build docs:
|
|
117
|
+
|
|
111
118
|
Contributing
|
|
112
119
|
============
|
|
113
120
|
|
{circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/circuitpython_tmidi.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: circuitpython-tmidi
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: MIDI library for CircuitPython
|
|
5
5
|
Author-email: Tod Kurt <tod@todbot.com>
|
|
6
6
|
License: MIT
|
|
@@ -109,8 +109,9 @@ Usage Example
|
|
|
109
109
|
|
|
110
110
|
midi_usb = tmidi.MIDI(midi_in=usb_midi.ports[0], midi_out=usb_midi.ports[1])
|
|
111
111
|
|
|
112
|
-
def send_midi_panic(
|
|
113
|
-
|
|
112
|
+
def send_midi_panic(midi_channel=1):
|
|
113
|
+
ch = midi_channel-1
|
|
114
|
+
msg = tmidi.Message(tmidi.NOTE_OFF, channel=ch) # channels are 0-indexed
|
|
114
115
|
for notenum in range(128):
|
|
115
116
|
msg.note = notenum
|
|
116
117
|
msg.velocity = 0
|
|
@@ -128,6 +129,12 @@ API documentation for this library can be found on `Read the Docs <https://circu
|
|
|
128
129
|
For information on building library documentation, please check out
|
|
129
130
|
`this guide <https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/sharing-our-docs-on-readthedocs#sphinx-5-1>`_.
|
|
130
131
|
|
|
132
|
+
Testing
|
|
133
|
+
=======
|
|
134
|
+
|
|
135
|
+
Install ``pytest`` with ``pip3 install pytest --upgrade`` and run ``pytest -v``
|
|
136
|
+
To build docs:
|
|
137
|
+
|
|
131
138
|
Contributing
|
|
132
139
|
============
|
|
133
140
|
|
{circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/circuitpython_tmidi.egg-info/SOURCES.txt
RENAMED
|
@@ -35,7 +35,10 @@ docs/_static/favicon.ico
|
|
|
35
35
|
docs/_static/favicon.ico.license
|
|
36
36
|
examples/tmidi_midiforwarder.py
|
|
37
37
|
examples/tmidi_multiple_receiver.py
|
|
38
|
+
examples/tmidi_simple_arpeggiator.py
|
|
38
39
|
examples/tmidi_simple_receiver.py
|
|
39
40
|
examples/tmidi_simpletest.py
|
|
41
|
+
tests/__init__.py
|
|
40
42
|
tests/test_one.py
|
|
43
|
+
tests/test_tmidi_messages.py
|
|
41
44
|
tests/test_tmidi_one.py
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Simple test
|
|
2
|
+
------------
|
|
3
|
+
|
|
4
|
+
Show how to send MIDI messages.
|
|
5
|
+
|
|
6
|
+
.. literalinclude:: ../examples/tmidi_simpletest.py
|
|
7
|
+
:caption: examples/tmidi_simpletest.py
|
|
8
|
+
:linenos:
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
Simple receiver
|
|
12
|
+
---------------
|
|
13
|
+
|
|
14
|
+
Show to to receive MIDI messages.
|
|
15
|
+
|
|
16
|
+
.. literalinclude:: ../examples/tmidi_simple_receiver.py
|
|
17
|
+
:caption: examples/tmidi_simple_receiver.py
|
|
18
|
+
:linenos:
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
Multiple receivers
|
|
22
|
+
------------------
|
|
23
|
+
|
|
24
|
+
Show to to receive MIDI messages from multiple receivers.
|
|
25
|
+
|
|
26
|
+
.. literalinclude:: ../examples/tmidi_multiple_receiver.py
|
|
27
|
+
:caption: examples/tmidi_multiple_receiver.py
|
|
28
|
+
:linenos:
|
|
29
|
+
|
|
30
|
+
Simple arpeggiator
|
|
31
|
+
------------------
|
|
32
|
+
|
|
33
|
+
Implement a simple arpeggiator that receives MIDI and sends it
|
|
34
|
+
|
|
35
|
+
.. literalinclude:: ../examples/tmidi_simple_arpeggiator.py
|
|
36
|
+
:caption: examples/tmidi_simple_arpeggiator.py
|
|
37
|
+
:linenos:
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024 Tod Kurt
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
# This example shows both receiving and sending MIDI messages
|
|
6
|
+
# by implementing a simple arpeggiator.
|
|
7
|
+
# MIDI notes send to MIDI In are arpeggiated to MIDI Output.
|
|
8
|
+
|
|
9
|
+
import time
|
|
10
|
+
import usb_midi
|
|
11
|
+
import tmidi
|
|
12
|
+
|
|
13
|
+
midi = tmidi.MIDI(midi_in=usb_midi.ports[0], midi_out=usb_midi.ports[1])
|
|
14
|
+
# if serial midi
|
|
15
|
+
# uart = busio.UART(rx=board.RX, tx=board.TX, timeout=0.000)
|
|
16
|
+
# midi = tmidi.MIDI(midi_in=uart, midi_out=uart)
|
|
17
|
+
|
|
18
|
+
tempo = 120 # bpm
|
|
19
|
+
notes_per_beat = 2 # 1 = quarter-note, 2 = 8th, 4 = 16th
|
|
20
|
+
note_time = 60 / tempo / notes_per_beat
|
|
21
|
+
gate_percent = 0.5
|
|
22
|
+
|
|
23
|
+
pressed_notes = []
|
|
24
|
+
note_i = 0
|
|
25
|
+
last_note_time = 0
|
|
26
|
+
gate_time = 0
|
|
27
|
+
while True:
|
|
28
|
+
# handle midi input
|
|
29
|
+
if msg := midi.receive():
|
|
30
|
+
if msg.type == tmidi.NOTE_ON and msg.velocity > 0:
|
|
31
|
+
print("note on", msg)
|
|
32
|
+
pressed_notes.append(msg.note)
|
|
33
|
+
elif msg.type == tmidi.NOTE_OFF or (
|
|
34
|
+
msg.type == tmidi.NOTE_ON and msg.velocity == 0
|
|
35
|
+
):
|
|
36
|
+
if msg.note in pressed_notes:
|
|
37
|
+
midi.send(msg) # send the note off
|
|
38
|
+
pressed_notes.remove(msg.note)
|
|
39
|
+
note_i = 0
|
|
40
|
+
|
|
41
|
+
# do midi output
|
|
42
|
+
if len(pressed_notes) == 0:
|
|
43
|
+
continue
|
|
44
|
+
|
|
45
|
+
now = time.monotonic()
|
|
46
|
+
if now - last_note_time >= note_time:
|
|
47
|
+
last_note_time = now
|
|
48
|
+
note_on = tmidi.Message(tmidi.NOTE_ON, pressed_notes[note_i], 127)
|
|
49
|
+
print("arp note_on: ", note_on)
|
|
50
|
+
midi.send(note_on)
|
|
51
|
+
gate_time = note_time * gate_percent
|
|
52
|
+
|
|
53
|
+
if gate_time > 0 and now - last_note_time >= gate_time:
|
|
54
|
+
gate_time = 0
|
|
55
|
+
note_off = tmidi.Message(tmidi.NOTE_OFF, pressed_notes[note_i], 127)
|
|
56
|
+
print("arp note_off:", note_off)
|
|
57
|
+
midi.send(note_off)
|
|
58
|
+
note_i = (note_i + 1) % len(pressed_notes)
|
|
@@ -12,6 +12,20 @@ midi_usb = tmidi.MIDI(midi_in=usb_midi.ports[0], midi_out=usb_midi.ports[1])
|
|
|
12
12
|
while True:
|
|
13
13
|
if msg := midi_usb.receive():
|
|
14
14
|
if msg.type == tmidi.NOTE_ON and msg.velocity > 0:
|
|
15
|
-
print(
|
|
15
|
+
print(
|
|
16
|
+
"note on: note:",
|
|
17
|
+
msg.note,
|
|
18
|
+
"vel:",
|
|
19
|
+
msg.velocity,
|
|
20
|
+
"channel:",
|
|
21
|
+
msg.channel,
|
|
22
|
+
)
|
|
16
23
|
elif msg.type == tmidi.NOTE_OFF or msg.velocity == 0:
|
|
17
|
-
print(
|
|
24
|
+
print(
|
|
25
|
+
"note off: note:",
|
|
26
|
+
msg.note,
|
|
27
|
+
"vel:",
|
|
28
|
+
msg.velocity,
|
|
29
|
+
"channel:",
|
|
30
|
+
msg.channel,
|
|
31
|
+
)
|
|
@@ -18,12 +18,12 @@ while True:
|
|
|
18
18
|
notenum = random.randint(36, 72)
|
|
19
19
|
velocity = 127
|
|
20
20
|
|
|
21
|
-
msg_on = tmidi.Message(tmidi.NOTE_ON, midi_channel - 1
|
|
21
|
+
msg_on = tmidi.Message(tmidi.NOTE_ON, notenum, velocity, channel=midi_channel - 1)
|
|
22
22
|
print("sending note on msg:", msg_on)
|
|
23
23
|
midi_usb.send(msg_on)
|
|
24
24
|
time.sleep(0.1)
|
|
25
25
|
|
|
26
|
-
msg_off = tmidi.Message(tmidi.NOTE_OFF, midi_channel - 1
|
|
26
|
+
msg_off = tmidi.Message(tmidi.NOTE_OFF, notenum, 0, channel=midi_channel - 1)
|
|
27
27
|
print("sending note off msg:", msg_off)
|
|
28
28
|
midi_usb.send(msg_off)
|
|
29
29
|
time.sleep(0.2)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024 Tod Kurt
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import tmidi
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PortStub:
|
|
9
|
+
def __init__(self, data):
|
|
10
|
+
self.data = data
|
|
11
|
+
self.expected = None
|
|
12
|
+
|
|
13
|
+
def write(self, buf, nbytes):
|
|
14
|
+
assert self.expected == list(buf)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_message_note_on():
|
|
18
|
+
msg = tmidi.Message(tmidi.NOTE_ON, 64, 123)
|
|
19
|
+
assert str(msg) == "Message(NoteOn ch:0 64 123)"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_message_program_change():
|
|
23
|
+
msg = tmidi.Message(tmidi.PROGRAM_CHANGE, 120, channel=11)
|
|
24
|
+
assert str(msg) == "Message(ProgramChange ch:11 120)"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_message_note_on_send():
|
|
28
|
+
port = PortStub(iter([]))
|
|
29
|
+
midi_out = tmidi.MIDI(midi_out=port)
|
|
30
|
+
|
|
31
|
+
msg = tmidi.Message(tmidi.NOTE_ON, 64, 123)
|
|
32
|
+
port.expected = [0x90, 64, 123]
|
|
33
|
+
midi_out.send(msg)
|
|
34
|
+
|
|
35
|
+
msg = tmidi.Message(tmidi.NOTE_ON, 32, 111, channel=3)
|
|
36
|
+
port.expected = [0x93, 32, 111]
|
|
37
|
+
midi_out.send(msg)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_message_program_change_send():
|
|
41
|
+
port = PortStub(iter([]))
|
|
42
|
+
midi_out = tmidi.MIDI(midi_out=port)
|
|
43
|
+
|
|
44
|
+
msg = tmidi.Message(tmidi.PROGRAM_CHANGE, 33)
|
|
45
|
+
port.expected = [0xC0, 33]
|
|
46
|
+
midi_out.send(msg)
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
# SPDX-FileCopyrightText: Copyright (c) 2024 Tod Kurt
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
4
|
|
|
5
|
-
import pytest # pylint: disable=unused-import
|
|
6
5
|
|
|
7
6
|
import tmidi
|
|
8
7
|
|
|
@@ -33,7 +32,7 @@ class PortStub:
|
|
|
33
32
|
|
|
34
33
|
return bytes_read
|
|
35
34
|
|
|
36
|
-
def write(self, buf):
|
|
35
|
+
def write(self, buf, n):
|
|
37
36
|
pass
|
|
38
37
|
|
|
39
38
|
|
|
@@ -32,7 +32,7 @@ Implementation Notes
|
|
|
32
32
|
|
|
33
33
|
# imports
|
|
34
34
|
|
|
35
|
-
__version__ = "
|
|
35
|
+
__version__ = "2.0.0"
|
|
36
36
|
__repo__ = "https://github.com/todbot/CircuitPython_TMIDI.git"
|
|
37
37
|
|
|
38
38
|
# Portions of this library come from Winterbloom_SmolMIDI:
|
|
@@ -144,7 +144,6 @@ _MSG_TYPE_NAMES = {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
|
|
147
|
-
# pylint: disable=chained-comparison
|
|
148
147
|
def _is_channel_message(status_byte):
|
|
149
148
|
return status_byte >= NOTE_OFF and status_byte < SYSEX
|
|
150
149
|
|
|
@@ -174,25 +173,37 @@ class Message:
|
|
|
174
173
|
MIDI Message.
|
|
175
174
|
|
|
176
175
|
:param mtype: The type of message, e.g. tmidi.NOTE_ON.
|
|
177
|
-
:param channel: The MIDI channel for this message, if applicable (0-15)
|
|
178
176
|
:param data0: The first data byte for this message,
|
|
179
177
|
e.g. the note number as an ``int`` (0-127) for NOTE_ON messages.
|
|
180
178
|
:param data1: The second data byte for this message,
|
|
181
179
|
e.g. the velocity (0-127) for NOTE_ON messages.
|
|
180
|
+
:param channel: The MIDI channel for this message, if applicable (0-15)
|
|
181
|
+
|
|
182
|
+
Example of creating Messages:
|
|
183
|
+
|
|
184
|
+
.. code-block:: python
|
|
185
|
+
|
|
186
|
+
# create Note On middle-C message on ch 1 (0-indexed)
|
|
187
|
+
m = Message(tmidi.NOTE_ON, 60, 127, channel=0)
|
|
188
|
+
# create CC 74 with val 63 on ch 4
|
|
189
|
+
m = Message(tmidi.CC, 74, 63, channel=4-1)
|
|
190
|
+
# create a pitch bend full up on ch 1
|
|
191
|
+
m = Message(tmidi.PITCH_BEND, 8191)
|
|
192
|
+
|
|
193
|
+
Example of creating accessing Message attributes:
|
|
194
|
+
|
|
195
|
+
.. code-block:: python
|
|
196
|
+
|
|
197
|
+
if msg := midi.receive():
|
|
198
|
+
if msg.type == tmidi.NOTE_ON:
|
|
199
|
+
print("note on:", msg.note, msg.velocity)
|
|
200
|
+
elif msg.type == tmidi.NOTE_OFF:
|
|
201
|
+
print("note off:", msg.note, msg.velocity)
|
|
202
|
+
elif msg.type == tmidi.PROGRAM_CHANGE:
|
|
203
|
+
print("program change:", msg.value)
|
|
182
204
|
"""
|
|
183
205
|
|
|
184
|
-
def __init__(self, mtype=SYSTEM_RESET,
|
|
185
|
-
"""
|
|
186
|
-
Create a MIDI Message.
|
|
187
|
-
|
|
188
|
-
Example::
|
|
189
|
-
# create Note On middle-C message on ch1 (0-indexed)
|
|
190
|
-
m = Message(tmidi.NOTE_ON, 0, 60, 127)
|
|
191
|
-
# create CC 74 with val 63 on ch 4
|
|
192
|
-
m = Message(tmidi.CC, 3, 74, 63)
|
|
193
|
-
# create a pitch bend full up on ch 1
|
|
194
|
-
m = Message(tmidi.PITCH_BEND, 0, 8191)
|
|
195
|
-
"""
|
|
206
|
+
def __init__(self, mtype=SYSTEM_RESET, data0=0, data1=0, channel=0):
|
|
196
207
|
self.type = mtype
|
|
197
208
|
self.channel = channel
|
|
198
209
|
self.data0 = data0
|
|
@@ -204,16 +215,15 @@ class Message:
|
|
|
204
215
|
status_byte = self.type
|
|
205
216
|
if _is_channel_message(status_byte):
|
|
206
217
|
status_byte |= self.channel
|
|
207
|
-
if
|
|
218
|
+
if self.type in _LEN_2_MESSAGES:
|
|
208
219
|
return bytes([status_byte, self.data0, self.data1])
|
|
209
|
-
|
|
220
|
+
elif self.type in _LEN_1_MESSAGES:
|
|
210
221
|
return bytes([status_byte, self.data0])
|
|
211
222
|
return bytes([status_byte])
|
|
212
223
|
|
|
213
224
|
def __repr__(self):
|
|
214
225
|
return self.__str__()
|
|
215
226
|
|
|
216
|
-
# pylint: disable=consider-using-f-string
|
|
217
227
|
def __str__(self):
|
|
218
228
|
mtype = self.type
|
|
219
229
|
type_str = "Message(" + _MSG_TYPE_NAMES.get(mtype, "Unknown")
|
|
@@ -246,7 +256,7 @@ class Message:
|
|
|
246
256
|
|
|
247
257
|
@property
|
|
248
258
|
def pitch_bend(self):
|
|
249
|
-
"""Pitch bend value of message (only for PITCH_BEND msgs)"""
|
|
259
|
+
"""Pitch bend value of message (only valid for PITCH_BEND msgs)"""
|
|
250
260
|
return (self.data1 << 7 | self.data0) - 8192
|
|
251
261
|
|
|
252
262
|
@pitch_bend.setter
|
|
@@ -255,10 +265,19 @@ class Message:
|
|
|
255
265
|
self.data0 = pbval & 0x7F
|
|
256
266
|
self.data1 = pbval >> 7
|
|
257
267
|
|
|
268
|
+
@property
|
|
269
|
+
def value(self):
|
|
270
|
+
"""Message value, only valid for len1 messages (Program Change, etc)"""
|
|
271
|
+
return self.data0
|
|
272
|
+
|
|
273
|
+
@value.setter
|
|
274
|
+
def value(self, val):
|
|
275
|
+
self.data0 = val
|
|
276
|
+
|
|
258
277
|
|
|
259
278
|
class MIDI:
|
|
260
279
|
"""
|
|
261
|
-
MIDI Parser and sender
|
|
280
|
+
MIDI Parser, receiver and sender
|
|
262
281
|
``midi_in`` or ``midi_out`` *must* be set or both together.
|
|
263
282
|
|
|
264
283
|
:param midi_in: an object which implements ``read(length)``,
|
|
@@ -266,6 +285,46 @@ class MIDI:
|
|
|
266
285
|
:param midi_out: an object which implements ``write(buffer, length)``,
|
|
267
286
|
set to ``usb_midi.ports[1]`` for USB MIDI, default None.
|
|
268
287
|
:param bool enable_running_status: Allow running status messages to work, default False.
|
|
288
|
+
|
|
289
|
+
Example of sending MIDI over USB:
|
|
290
|
+
|
|
291
|
+
.. code-block:: python
|
|
292
|
+
|
|
293
|
+
import usb_midi
|
|
294
|
+
import tmidi
|
|
295
|
+
midi_usb = tmidi.MIDI(midi_out=usb_midi.ports[1])
|
|
296
|
+
msg_on = tmidi.Message(tmidi.NOTE_ON, notenum, velocity)
|
|
297
|
+
midi_usb.send(msg_on)
|
|
298
|
+
|
|
299
|
+
Example of sending MIDI over UART (TRS or 5-pin):
|
|
300
|
+
|
|
301
|
+
.. code-block:: python
|
|
302
|
+
|
|
303
|
+
import board
|
|
304
|
+
import busio
|
|
305
|
+
import tmidi
|
|
306
|
+
uart = busio.UART(tx=board.TX, rx=board.RX, timeout=0.001)
|
|
307
|
+
midi_uart = tmidi.MIDI(midi_out=uart)
|
|
308
|
+
msg_on = tmidi.Message(tmidi.NOTE_ON, notenum, velocity)
|
|
309
|
+
midi_uart.send(msg_on)
|
|
310
|
+
|
|
311
|
+
Example of receiving MIDI:
|
|
312
|
+
|
|
313
|
+
.. code-block:: python
|
|
314
|
+
|
|
315
|
+
import board
|
|
316
|
+
import busio
|
|
317
|
+
import usb_midi
|
|
318
|
+
import tmidi
|
|
319
|
+
uart = busio.UART(tx=board.TX, rx=board.RX, timeout=0.001)
|
|
320
|
+
midi_uart = tmidi.MIDI(midi_in=uart, midi_out=uart)
|
|
321
|
+
midi_usb = tmidi.MIDI(midi_in=usb_midi.ports[0], midi_out=usb_midi.ports[1])
|
|
322
|
+
|
|
323
|
+
while True:
|
|
324
|
+
if msg := midi_usb.receive():
|
|
325
|
+
print("usb midi:", msg)
|
|
326
|
+
if msg := midi_uart.receive():
|
|
327
|
+
print("uart midi:", msg)
|
|
269
328
|
"""
|
|
270
329
|
|
|
271
330
|
def __init__(self, midi_in=None, midi_out=None, enable_running_status=False):
|
|
@@ -341,7 +400,6 @@ class MIDI:
|
|
|
341
400
|
|
|
342
401
|
return message
|
|
343
402
|
|
|
344
|
-
# pylint: disable=unnecessary-dunder-call
|
|
345
403
|
def send(self, msg, channel=None):
|
|
346
404
|
"""Send a MIDI message.
|
|
347
405
|
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2020 Diego Elio Pettenò
|
|
2
|
-
#
|
|
3
|
-
# SPDX-License-Identifier: Unlicense
|
|
4
|
-
|
|
5
|
-
default_language_version:
|
|
6
|
-
# force all unspecified python hooks to run python3.11
|
|
7
|
-
python: python3.11
|
|
8
|
-
|
|
9
|
-
repos:
|
|
10
|
-
- repo: https://github.com/python/black
|
|
11
|
-
rev: 23.3.0
|
|
12
|
-
hooks:
|
|
13
|
-
- id: black
|
|
14
|
-
- repo: https://github.com/fsfe/reuse-tool
|
|
15
|
-
rev: v1.1.2
|
|
16
|
-
hooks:
|
|
17
|
-
- id: reuse
|
|
18
|
-
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
19
|
-
rev: v4.4.0
|
|
20
|
-
hooks:
|
|
21
|
-
- id: check-yaml
|
|
22
|
-
- id: end-of-file-fixer
|
|
23
|
-
- id: trailing-whitespace
|
|
24
|
-
- repo: https://github.com/pycqa/pylint
|
|
25
|
-
rev: v2.17.4
|
|
26
|
-
hooks:
|
|
27
|
-
- id: pylint
|
|
28
|
-
name: pylint (library code)
|
|
29
|
-
types: [python]
|
|
30
|
-
args:
|
|
31
|
-
- --disable=consider-using-f-string
|
|
32
|
-
exclude: "^(docs/|examples/|tests/|setup.py$)"
|
|
33
|
-
- id: pylint
|
|
34
|
-
name: pylint (example code)
|
|
35
|
-
description: Run pylint rules on "examples/*.py" files
|
|
36
|
-
types: [python]
|
|
37
|
-
files: "^examples/"
|
|
38
|
-
args:
|
|
39
|
-
- --disable=missing-docstring,invalid-name,consider-using-f-string,duplicate-code
|
|
40
|
-
- id: pylint
|
|
41
|
-
name: pylint (test code)
|
|
42
|
-
description: Run pylint rules on "tests/*.py" files
|
|
43
|
-
types: [python]
|
|
44
|
-
files: "^tests/"
|
|
45
|
-
args:
|
|
46
|
-
- --disable=missing-docstring,consider-using-f-string,duplicate-code
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
Simple test
|
|
2
|
-
------------
|
|
3
|
-
|
|
4
|
-
Show how to send MIDI messages.
|
|
5
|
-
|
|
6
|
-
.. literalinclude:: ../examples/tmidi_simpletest.py
|
|
7
|
-
:caption: examples/tmidi_simpletest.py
|
|
8
|
-
:linenos:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Simple receiver
|
|
12
|
-
---------------
|
|
13
|
-
|
|
14
|
-
Show to to receive MIDI messages.
|
|
15
|
-
|
|
16
|
-
.. literalinclude:: ../examples/tmidi_simple_receiver.py
|
|
17
|
-
:caption: examples/tmidi_simple_receiver.py
|
|
18
|
-
:linenos:
|
|
File without changes
|
|
File without changes
|
{circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/.github/workflows/failure-help-text.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/circuitpython_tmidi.egg-info/requires.txt
RENAMED
|
File without changes
|
{circuitpython_tmidi-1.0.4 → circuitpython_tmidi-2.0.0}/circuitpython_tmidi.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|