circuitpython-tmidi 1.0.5__tar.gz → 3.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-1.0.5 → circuitpython_tmidi-3.0.0}/.github/workflows/release_gh.yml +1 -4
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/.gitignore +4 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/.pre-commit-config.yaml +3 -3
- circuitpython_tmidi-3.0.0/LATENCY_RESULTS.md +77 -0
- {circuitpython_tmidi-1.0.5/circuitpython_tmidi.egg-info → circuitpython_tmidi-3.0.0}/PKG-INFO +101 -24
- circuitpython_tmidi-3.0.0/README.rst +194 -0
- circuitpython_tmidi-3.0.0/REUSE.toml +6 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0/circuitpython_tmidi.egg-info}/PKG-INFO +101 -24
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/circuitpython_tmidi.egg-info/SOURCES.txt +26 -2
- circuitpython_tmidi-3.0.0/docs/examples.rst +47 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/docs/index.rst +1 -4
- circuitpython_tmidi-3.0.0/examples/tmidi_adafruit_midi_comparison.py +160 -0
- circuitpython_tmidi-3.0.0/examples/tmidi_latency_device.py +348 -0
- circuitpython_tmidi-3.0.0/examples/tmidi_latency_host.py +110 -0
- circuitpython_tmidi-3.0.0/examples/tmidi_simple_arpeggiator.py +69 -0
- circuitpython_tmidi-3.0.0/examples/tmidi_simple_poll.py +44 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/examples/tmidi_simple_receiver.py +7 -3
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/examples/tmidi_simpletest.py +2 -2
- circuitpython_tmidi-3.0.0/examples/tmidi_sysex.py +17 -0
- circuitpython_tmidi-3.0.0/examples/tmidi_test_device.py +168 -0
- circuitpython_tmidi-3.0.0/examples/tmidi_test_host.py +428 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/pyproject.toml +14 -7
- circuitpython_tmidi-3.0.0/src/Makefile +19 -0
- circuitpython_tmidi-3.0.0/src/tmidi_turbo.py +131 -0
- circuitpython_tmidi-3.0.0/tests/__init__.py +2 -0
- circuitpython_tmidi-3.0.0/tests/conftest.py +8 -0
- circuitpython_tmidi-3.0.0/tests/data/rk006_capture.txt +518 -0
- circuitpython_tmidi-3.0.0/tests/test_kernel_model.py +70 -0
- circuitpython_tmidi-3.0.0/tests/test_tmidi_callbacks.py +196 -0
- circuitpython_tmidi-3.0.0/tests/test_tmidi_capture.py +65 -0
- circuitpython_tmidi-3.0.0/tests/test_tmidi_messages.py +77 -0
- circuitpython_tmidi-3.0.0/tests/test_tmidi_one.py +154 -0
- circuitpython_tmidi-3.0.0/tests/test_tmidi_sysex.py +132 -0
- circuitpython_tmidi-3.0.0/tmidi/__init__.py +643 -0
- circuitpython_tmidi-3.0.0/tmidi/tmidi_kernel.armv6m.mpy +0 -0
- circuitpython_tmidi-3.0.0/tmidi/tmidi_kernel.armv7emsp.mpy +0 -0
- circuitpython_tmidi-3.0.0/tmidi/tmidi_kernel.rv32imc.mpy +0 -0
- circuitpython_tmidi-3.0.0/tmidi/tmidi_kernel.xtensawin.mpy +0 -0
- circuitpython_tmidi-1.0.5/README.rst +0 -116
- circuitpython_tmidi-1.0.5/docs/examples.rst +0 -18
- circuitpython_tmidi-1.0.5/tests/test_tmidi_one.py +0 -64
- circuitpython_tmidi-1.0.5/tmidi.py +0 -365
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/.github/workflows/build.yml +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/.github/workflows/failure-help-text.yml +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/.github/workflows/release_pypi.yml +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/.pylintrc +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/.readthedocs.yaml +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/CODE_OF_CONDUCT.md +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/LICENSE +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/LICENSES/CC-BY-4.0.txt +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/LICENSES/MIT.txt +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/LICENSES/Unlicense.txt +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/README.rst.license +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/circuitpython_tmidi.egg-info/dependency_links.txt +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/circuitpython_tmidi.egg-info/requires.txt +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/circuitpython_tmidi.egg-info/top_level.txt +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/docs/_static/favicon.ico +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/docs/_static/favicon.ico.license +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/docs/api.rst +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/docs/api.rst.license +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/docs/conf.py +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/docs/examples.rst.license +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/docs/index.rst.license +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/docs/requirements.txt +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/examples/tmidi_midiforwarder.py +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/examples/tmidi_multiple_receiver.py +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/optional_requirements.txt +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/requirements.txt +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/setup.cfg +0 -0
- {circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/tests/test_one.py +0 -0
|
@@ -17,7 +17,4 @@ jobs:
|
|
|
17
17
|
with:
|
|
18
18
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
19
19
|
upload-url: ${{ github.event.release.upload_url }}
|
|
20
|
-
|
|
21
|
-
# argument along with the prefix (or full name) of the package folder
|
|
22
|
-
# so the MPY bundles are built correctly:s
|
|
23
|
-
# package-prefix: tmidi
|
|
20
|
+
package-prefix: tmidi
|
|
@@ -4,18 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
repos:
|
|
6
6
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
7
|
-
rev:
|
|
7
|
+
rev: v6.0.0
|
|
8
8
|
hooks:
|
|
9
9
|
- id: check-yaml
|
|
10
10
|
- id: end-of-file-fixer
|
|
11
11
|
- id: trailing-whitespace
|
|
12
12
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
13
|
-
rev: v0.
|
|
13
|
+
rev: v0.15.12
|
|
14
14
|
hooks:
|
|
15
15
|
- id: ruff-format
|
|
16
16
|
- id: ruff
|
|
17
17
|
args: ["--fix"]
|
|
18
18
|
- repo: https://github.com/fsfe/reuse-tool
|
|
19
|
-
rev:
|
|
19
|
+
rev: v6.2.0
|
|
20
20
|
hooks:
|
|
21
21
|
- id: reuse
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# tmidi latency results
|
|
2
|
+
|
|
3
|
+
Time from a MIDI message on the wire to CircuitPython code acting on it,
|
|
4
|
+
from `examples/tmidi_latency_device.py` (and `tmidi_latency_host.py` for
|
|
5
|
+
USB).
|
|
6
|
+
|
|
7
|
+
- Raspberry Pi Pico (RP2040) @ 125 MHz, CircuitPython 11.0.0-alpha.2
|
|
8
|
+
- tmidi with the pure Python parser
|
|
9
|
+
- Cells are median (p90) in ms
|
|
10
|
+
- "idle": the main loop only reads MIDI
|
|
11
|
+
- "busy": the main loop also does 5 ms of other work per pass
|
|
12
|
+
- Clock jitter: how far handled clocks land from their 20.8 ms spacing
|
|
13
|
+
(120 BPM)
|
|
14
|
+
|
|
15
|
+
## UART
|
|
16
|
+
|
|
17
|
+
Absolute, via a GP20 -> GP21 loopback. A 3-byte message takes 0.96 ms on
|
|
18
|
+
the wire, which is included. The Pico also does the sending here, so CC
|
|
19
|
+
storm losses are pessimistic.
|
|
20
|
+
|
|
21
|
+
Idle loop:
|
|
22
|
+
|
|
23
|
+
| Scenario | tmidi receive() | tmidi poll() | adafruit_midi |
|
|
24
|
+
|-----------------|------------------|--------------|------------------|
|
|
25
|
+
| note | 2.05 (2.17) | 1.89 (1.95) | 3.84 (4.49) |
|
|
26
|
+
| chord, 8 notes | 4.95 (5.39) | 1.85 (2.20) | 16.19 (18.17) |
|
|
27
|
+
| clock | 1.68 (1.80) | 1.43 (1.56) | 3.57 (4.21) |
|
|
28
|
+
| clock jitter | 0.14 (0.48) | 0.13 (0.39) | 0.62 (2.60) |
|
|
29
|
+
| CC storm | 68.5, 1403 lost | 1.88 (2.14) | 139, 1982 lost |
|
|
30
|
+
|
|
31
|
+
Busy loop:
|
|
32
|
+
|
|
33
|
+
| Scenario | tmidi receive() | tmidi poll() | adafruit_midi |
|
|
34
|
+
|-----------------|------------------|--------------|------------------|
|
|
35
|
+
| note | 5.52 (10.71) | 4.03 (6.44) | 71.72 (80.99) |
|
|
36
|
+
| chord, 8 notes | 23.72 (40.16) | 5.07 (7.20) | 70.67 (94.29) |
|
|
37
|
+
| clock | 6.80 (14.65) | 3.97 (6.62) | 57.71 (91.13) |
|
|
38
|
+
| clock jitter | 3.55 (8.90) | 2.40 (5.01) | 8.59 (14.15) |
|
|
39
|
+
| CC storm | 288, 2563 lost | 5.79 (7.37) | 285, 2702 lost |
|
|
40
|
+
|
|
41
|
+
CC storm: 3000 CCs back to back at wire speed.
|
|
42
|
+
|
|
43
|
+
## USB
|
|
44
|
+
|
|
45
|
+
Relative: the Mac and the Pico share no clock, so these are the delay over
|
|
46
|
+
the fastest message seen in each run. No USB run lost messages; "behind"
|
|
47
|
+
means it had not caught up with the storm when the run ended.
|
|
48
|
+
|
|
49
|
+
Idle loop:
|
|
50
|
+
|
|
51
|
+
| Scenario | tmidi receive() | tmidi poll() | adafruit_midi |
|
|
52
|
+
|-----------------|------------------|--------------|------------------|
|
|
53
|
+
| note | 0.15 (0.22) | 0.14 (0.21) | 0.46 (0.77) |
|
|
54
|
+
| chord, 8 notes | 2.94 (5.13) | 2.31 (4.20) | 6.99 (12.31) |
|
|
55
|
+
| clock | 0.11 (0.17) | 0.11 (0.18) | 0.34 (0.55) |
|
|
56
|
+
| clock jitter | 0.05 (0.13) | 0.04 (0.13) | 0.14 (0.42) |
|
|
57
|
+
| CC storm | 8.20 (14.25) | 0.92 (1.07) | behind |
|
|
58
|
+
|
|
59
|
+
Busy loop:
|
|
60
|
+
|
|
61
|
+
| Scenario | tmidi receive() | tmidi poll() | adafruit_midi |
|
|
62
|
+
|-----------------|------------------|--------------|------------------|
|
|
63
|
+
| note | 4.32 (7.31) | 2.80 (5.12) | 17.03 (33.75) |
|
|
64
|
+
| chord, 8 notes | 23.65 (43.10) | 4.21 (6.99) | 27.35 (50.38) |
|
|
65
|
+
| clock | 4.59 (10.62) | 2.55 (4.61) | 7.52 (21.69) |
|
|
66
|
+
| clock jitter | 3.37 (5.17) | 2.27 (3.74) | 5.27 (12.32) |
|
|
67
|
+
| CC storm | behind | 2.97 (6.37) | behind |
|
|
68
|
+
|
|
69
|
+
## Takeaways
|
|
70
|
+
|
|
71
|
+
- Single notes on an idle loop: all three are within about 2 ms.
|
|
72
|
+
- receive() (tmidi or adafruit_midi) handles one message per pass, so
|
|
73
|
+
chords, clock and storms back up behind a busy loop. poll() handles
|
|
74
|
+
everything waiting each pass and stays close to idle-loop numbers.
|
|
75
|
+
- For steady MIDI clock, use poll() and keep the main loop's passes short.
|
|
76
|
+
- The viper parser made idle-loop medians 0.1-0.7 ms faster and
|
|
77
|
+
otherwise changed little.
|
{circuitpython_tmidi-1.0.5/circuitpython_tmidi.egg-info → circuitpython_tmidi-3.0.0}/PKG-INFO
RENAMED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: circuitpython-tmidi
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: MIDI library for CircuitPython
|
|
5
5
|
Author-email: Tod Kurt <tod@todbot.com>
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/todbot/CircuitPython_TMIDI
|
|
8
8
|
Keywords: adafruit,blinka,circuitpython,micropython,tmidi,midi
|
|
9
9
|
Classifier: Intended Audience :: Developers
|
|
10
10
|
Classifier: Topic :: Software Development :: Libraries
|
|
11
11
|
Classifier: Topic :: Software Development :: Embedded Systems
|
|
12
12
|
Classifier: Topic :: System :: Hardware
|
|
13
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
14
13
|
Classifier: Programming Language :: Python :: 3
|
|
15
14
|
Description-Content-Type: text/x-rst
|
|
16
15
|
License-File: LICENSE
|
|
@@ -37,6 +36,100 @@ Introduction
|
|
|
37
36
|
|
|
38
37
|
MIDI library for CircuitPython
|
|
39
38
|
|
|
39
|
+
TMIDI is a MIDI library for CircuitPython / MicroPython. It attempts to
|
|
40
|
+
be a provide a simpler API and perform faster, especially for MIDI parsing,
|
|
41
|
+
compared to ``adafruit_midi`` (`adafruit_midi github <https://github.com/adafruit/Adafruit_CircuitPython_MIDI/>`_)
|
|
42
|
+
TMIDI is derived from ``winterbloom_smolmidi``
|
|
43
|
+
(`smolmidi github <https://github.com/wntrblm/Winterbloom_SmolMIDI>`_)
|
|
44
|
+
by Thea Flowers for Winterbloom.
|
|
45
|
+
|
|
46
|
+
Like ``adafruit_midi``, TMIDI works on CircuitPython's ``usb_midi`` and ``busio.UART``,
|
|
47
|
+
or any stream-like object that supports ``.readinto()`` and ``.write()``
|
|
48
|
+
|
|
49
|
+
Usage Example
|
|
50
|
+
=============
|
|
51
|
+
|
|
52
|
+
Sending MIDI over USB. Channels are 0-indexed, so ``channel=0`` is
|
|
53
|
+
MIDI channel 1:
|
|
54
|
+
|
|
55
|
+
.. code-block:: python
|
|
56
|
+
|
|
57
|
+
import time
|
|
58
|
+
import usb_midi
|
|
59
|
+
import tmidi
|
|
60
|
+
|
|
61
|
+
midi = tmidi.MIDI(midi_out=usb_midi.ports[1])
|
|
62
|
+
|
|
63
|
+
while True:
|
|
64
|
+
midi.send(tmidi.Message(tmidi.NOTE_ON, 60, 100, channel=0))
|
|
65
|
+
time.sleep(0.5)
|
|
66
|
+
midi.send(tmidi.Message(tmidi.NOTE_OFF, 60, 0, channel=0))
|
|
67
|
+
time.sleep(0.5)
|
|
68
|
+
|
|
69
|
+
Receiving MIDI over USB. ``receive()`` returns ``None`` when nothing
|
|
70
|
+
has arrived:
|
|
71
|
+
|
|
72
|
+
.. code-block:: python
|
|
73
|
+
|
|
74
|
+
import usb_midi
|
|
75
|
+
import tmidi
|
|
76
|
+
|
|
77
|
+
midi = tmidi.MIDI(midi_in=usb_midi.ports[0])
|
|
78
|
+
|
|
79
|
+
while True:
|
|
80
|
+
if msg := midi.receive():
|
|
81
|
+
if msg.type == tmidi.NOTE_ON:
|
|
82
|
+
print("note on:", msg.note, msg.velocity, msg.channel)
|
|
83
|
+
else:
|
|
84
|
+
print(msg)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
Callbacks
|
|
88
|
+
=========
|
|
89
|
+
|
|
90
|
+
Instead of ``receive()``, register functions for the message types you want
|
|
91
|
+
and call ``poll()``. No ``Message`` objects are allocated.
|
|
92
|
+
|
|
93
|
+
.. code-block:: python
|
|
94
|
+
|
|
95
|
+
def note_on(channel, note, velocity):
|
|
96
|
+
print("note on:", channel, note, velocity)
|
|
97
|
+
|
|
98
|
+
midi.on(tmidi.NOTE_ON, note_on)
|
|
99
|
+
while True:
|
|
100
|
+
midi.poll()
|
|
101
|
+
|
|
102
|
+
See ``examples/tmidi_simple_poll.py`` for a full example.
|
|
103
|
+
|
|
104
|
+
Turbo parser
|
|
105
|
+
============
|
|
106
|
+
|
|
107
|
+
TMIDI runs entirely in Python, but uses a faster ``@micropython.viper``
|
|
108
|
+
parser when your firmware can load native code. The parsers ship inside the
|
|
109
|
+
``tmidi/`` library folder as ``tmidi_kernel.<arch>.mpy``, and CircuitPython
|
|
110
|
+
picks the one for your board. ``tmidi.ENGINE`` says which parser is in use.
|
|
111
|
+
|
|
112
|
+
TMIDI 3.0 is a folder, ``lib/tmidi/``, instead of a single ``lib/tmidi.mpy``.
|
|
113
|
+
Delete an old ``lib/tmidi.mpy`` when upgrading.
|
|
114
|
+
|
|
115
|
+
====================== =====================================
|
|
116
|
+
``tmidi_kernel`` file Boards
|
|
117
|
+
====================== =====================================
|
|
118
|
+
``armv6m`` RP2040
|
|
119
|
+
``armv7emsp`` SAMD51, nRF52840, RP2350, STM32F4
|
|
120
|
+
``xtensawin`` ESP32-S2, ESP32-S3
|
|
121
|
+
``rv32imc`` ESP32-C3, C5, C6
|
|
122
|
+
====================== =====================================
|
|
123
|
+
|
|
124
|
+
To rebuild them from ``src/tmidi_turbo.py``, use an ``mpy-cross`` from the
|
|
125
|
+
CircuitPython version they will run on. The files land in ``tmidi/``:
|
|
126
|
+
|
|
127
|
+
.. code-block:: shell
|
|
128
|
+
|
|
129
|
+
cd src
|
|
130
|
+
make MPY_CROSS=path/to/circuitpython/mpy-cross/build/mpy-cross
|
|
131
|
+
|
|
132
|
+
|
|
40
133
|
|
|
41
134
|
Dependencies
|
|
42
135
|
=============
|
|
@@ -99,27 +192,6 @@ Or the following command to update an existing version:
|
|
|
99
192
|
|
|
100
193
|
circup update
|
|
101
194
|
|
|
102
|
-
Usage Example
|
|
103
|
-
=============
|
|
104
|
-
|
|
105
|
-
.. code-block:: python
|
|
106
|
-
|
|
107
|
-
import usb_midi
|
|
108
|
-
import tmidi
|
|
109
|
-
|
|
110
|
-
midi_usb = tmidi.MIDI(midi_in=usb_midi.ports[0], midi_out=usb_midi.ports[1])
|
|
111
|
-
|
|
112
|
-
def send_midi_panic(channel=1):
|
|
113
|
-
msg = tmidi.Message(tmidi.NOTE_OFF, channel-1) # channels are 0-indexed
|
|
114
|
-
for notenum in range(128):
|
|
115
|
-
msg.note = notenum
|
|
116
|
-
msg.velocity = 0
|
|
117
|
-
midi_usb.send(msg)
|
|
118
|
-
time.sleep(0.01)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
send_midi_panic()
|
|
122
|
-
|
|
123
195
|
|
|
124
196
|
Documentation
|
|
125
197
|
=============
|
|
@@ -128,6 +200,11 @@ API documentation for this library can be found on `Read the Docs <https://circu
|
|
|
128
200
|
For information on building library documentation, please check out
|
|
129
201
|
`this guide <https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/sharing-our-docs-on-readthedocs#sphinx-5-1>`_.
|
|
130
202
|
|
|
203
|
+
Testing
|
|
204
|
+
=======
|
|
205
|
+
|
|
206
|
+
Install ``pytest`` with ``pip3 install pytest circuitpython-mocks --upgrade`` and run ``pytest -v``
|
|
207
|
+
|
|
131
208
|
Contributing
|
|
132
209
|
============
|
|
133
210
|
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
Introduction
|
|
2
|
+
============
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
.. image:: https://readthedocs.org/projects/circuitpython-tmidi/badge/?version=latest
|
|
6
|
+
:target: https://circuitpython-tmidi.readthedocs.io/
|
|
7
|
+
:alt: Documentation Status
|
|
8
|
+
.. image:: https://img.shields.io/discord/327254708534116352.svg
|
|
9
|
+
:target: https://adafru.it/discord
|
|
10
|
+
:alt: Discord
|
|
11
|
+
.. image:: https://github.com/todbot/CircuitPython_TMIDI/workflows/Build%20CI/badge.svg
|
|
12
|
+
:target: https://github.com/todbot/CircuitPython_TMIDI/actions
|
|
13
|
+
:alt: Build Status
|
|
14
|
+
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
|
15
|
+
:target: https://github.com/psf/black
|
|
16
|
+
:alt: Code Style: Black
|
|
17
|
+
|
|
18
|
+
MIDI library for CircuitPython
|
|
19
|
+
|
|
20
|
+
TMIDI is a MIDI library for CircuitPython / MicroPython. It attempts to
|
|
21
|
+
be a provide a simpler API and perform faster, especially for MIDI parsing,
|
|
22
|
+
compared to ``adafruit_midi`` (`adafruit_midi github <https://github.com/adafruit/Adafruit_CircuitPython_MIDI/>`_)
|
|
23
|
+
TMIDI is derived from ``winterbloom_smolmidi``
|
|
24
|
+
(`smolmidi github <https://github.com/wntrblm/Winterbloom_SmolMIDI>`_)
|
|
25
|
+
by Thea Flowers for Winterbloom.
|
|
26
|
+
|
|
27
|
+
Like ``adafruit_midi``, TMIDI works on CircuitPython's ``usb_midi`` and ``busio.UART``,
|
|
28
|
+
or any stream-like object that supports ``.readinto()`` and ``.write()``
|
|
29
|
+
|
|
30
|
+
Usage Example
|
|
31
|
+
=============
|
|
32
|
+
|
|
33
|
+
Sending MIDI over USB. Channels are 0-indexed, so ``channel=0`` is
|
|
34
|
+
MIDI channel 1:
|
|
35
|
+
|
|
36
|
+
.. code-block:: python
|
|
37
|
+
|
|
38
|
+
import time
|
|
39
|
+
import usb_midi
|
|
40
|
+
import tmidi
|
|
41
|
+
|
|
42
|
+
midi = tmidi.MIDI(midi_out=usb_midi.ports[1])
|
|
43
|
+
|
|
44
|
+
while True:
|
|
45
|
+
midi.send(tmidi.Message(tmidi.NOTE_ON, 60, 100, channel=0))
|
|
46
|
+
time.sleep(0.5)
|
|
47
|
+
midi.send(tmidi.Message(tmidi.NOTE_OFF, 60, 0, channel=0))
|
|
48
|
+
time.sleep(0.5)
|
|
49
|
+
|
|
50
|
+
Receiving MIDI over USB. ``receive()`` returns ``None`` when nothing
|
|
51
|
+
has arrived:
|
|
52
|
+
|
|
53
|
+
.. code-block:: python
|
|
54
|
+
|
|
55
|
+
import usb_midi
|
|
56
|
+
import tmidi
|
|
57
|
+
|
|
58
|
+
midi = tmidi.MIDI(midi_in=usb_midi.ports[0])
|
|
59
|
+
|
|
60
|
+
while True:
|
|
61
|
+
if msg := midi.receive():
|
|
62
|
+
if msg.type == tmidi.NOTE_ON:
|
|
63
|
+
print("note on:", msg.note, msg.velocity, msg.channel)
|
|
64
|
+
else:
|
|
65
|
+
print(msg)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
Callbacks
|
|
69
|
+
=========
|
|
70
|
+
|
|
71
|
+
Instead of ``receive()``, register functions for the message types you want
|
|
72
|
+
and call ``poll()``. No ``Message`` objects are allocated.
|
|
73
|
+
|
|
74
|
+
.. code-block:: python
|
|
75
|
+
|
|
76
|
+
def note_on(channel, note, velocity):
|
|
77
|
+
print("note on:", channel, note, velocity)
|
|
78
|
+
|
|
79
|
+
midi.on(tmidi.NOTE_ON, note_on)
|
|
80
|
+
while True:
|
|
81
|
+
midi.poll()
|
|
82
|
+
|
|
83
|
+
See ``examples/tmidi_simple_poll.py`` for a full example.
|
|
84
|
+
|
|
85
|
+
Turbo parser
|
|
86
|
+
============
|
|
87
|
+
|
|
88
|
+
TMIDI runs entirely in Python, but uses a faster ``@micropython.viper``
|
|
89
|
+
parser when your firmware can load native code. The parsers ship inside the
|
|
90
|
+
``tmidi/`` library folder as ``tmidi_kernel.<arch>.mpy``, and CircuitPython
|
|
91
|
+
picks the one for your board. ``tmidi.ENGINE`` says which parser is in use.
|
|
92
|
+
|
|
93
|
+
TMIDI 3.0 is a folder, ``lib/tmidi/``, instead of a single ``lib/tmidi.mpy``.
|
|
94
|
+
Delete an old ``lib/tmidi.mpy`` when upgrading.
|
|
95
|
+
|
|
96
|
+
====================== =====================================
|
|
97
|
+
``tmidi_kernel`` file Boards
|
|
98
|
+
====================== =====================================
|
|
99
|
+
``armv6m`` RP2040
|
|
100
|
+
``armv7emsp`` SAMD51, nRF52840, RP2350, STM32F4
|
|
101
|
+
``xtensawin`` ESP32-S2, ESP32-S3
|
|
102
|
+
``rv32imc`` ESP32-C3, C5, C6
|
|
103
|
+
====================== =====================================
|
|
104
|
+
|
|
105
|
+
To rebuild them from ``src/tmidi_turbo.py``, use an ``mpy-cross`` from the
|
|
106
|
+
CircuitPython version they will run on. The files land in ``tmidi/``:
|
|
107
|
+
|
|
108
|
+
.. code-block:: shell
|
|
109
|
+
|
|
110
|
+
cd src
|
|
111
|
+
make MPY_CROSS=path/to/circuitpython/mpy-cross/build/mpy-cross
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
Dependencies
|
|
116
|
+
=============
|
|
117
|
+
This library depends on:
|
|
118
|
+
|
|
119
|
+
* `Adafruit CircuitPython <https://github.com/adafruit/circuitpython>`_
|
|
120
|
+
|
|
121
|
+
Please ensure all dependencies are available on the CircuitPython filesystem.
|
|
122
|
+
This is easily achieved by downloading
|
|
123
|
+
`the Adafruit library and driver bundle <https://circuitpython.org/libraries>`_
|
|
124
|
+
or individual libraries can be installed using
|
|
125
|
+
`circup <https://github.com/adafruit/circup>`_.
|
|
126
|
+
|
|
127
|
+
Installing from PyPI
|
|
128
|
+
=====================
|
|
129
|
+
|
|
130
|
+
On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from
|
|
131
|
+
PyPI <https://pypi.org/project/circuitpython-tmidi/>`_.
|
|
132
|
+
To install for current user:
|
|
133
|
+
|
|
134
|
+
.. code-block:: shell
|
|
135
|
+
|
|
136
|
+
pip3 install circuitpython-tmidi
|
|
137
|
+
|
|
138
|
+
To install system-wide (this may be required in some cases):
|
|
139
|
+
|
|
140
|
+
.. code-block:: shell
|
|
141
|
+
|
|
142
|
+
sudo pip3 install circuitpython-tmidi
|
|
143
|
+
|
|
144
|
+
To install in a virtual environment in your current project:
|
|
145
|
+
|
|
146
|
+
.. code-block:: shell
|
|
147
|
+
|
|
148
|
+
mkdir project-name && cd project-name
|
|
149
|
+
python3 -m venv .venv
|
|
150
|
+
source .env/bin/activate
|
|
151
|
+
pip3 install circuitpython-tmidi
|
|
152
|
+
|
|
153
|
+
Installing to a Connected CircuitPython Device with Circup
|
|
154
|
+
==========================================================
|
|
155
|
+
|
|
156
|
+
Make sure that you have ``circup`` installed in your Python environment.
|
|
157
|
+
Install it with the following command if necessary:
|
|
158
|
+
|
|
159
|
+
.. code-block:: shell
|
|
160
|
+
|
|
161
|
+
pip3 install circup
|
|
162
|
+
|
|
163
|
+
With ``circup`` installed and your CircuitPython device connected use the
|
|
164
|
+
following command to install:
|
|
165
|
+
|
|
166
|
+
.. code-block:: shell
|
|
167
|
+
|
|
168
|
+
circup install tmidi
|
|
169
|
+
|
|
170
|
+
Or the following command to update an existing version:
|
|
171
|
+
|
|
172
|
+
.. code-block:: shell
|
|
173
|
+
|
|
174
|
+
circup update
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
Documentation
|
|
178
|
+
=============
|
|
179
|
+
API documentation for this library can be found on `Read the Docs <https://circuitpython-tmidi.readthedocs.io/>`_.
|
|
180
|
+
|
|
181
|
+
For information on building library documentation, please check out
|
|
182
|
+
`this guide <https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/sharing-our-docs-on-readthedocs#sphinx-5-1>`_.
|
|
183
|
+
|
|
184
|
+
Testing
|
|
185
|
+
=======
|
|
186
|
+
|
|
187
|
+
Install ``pytest`` with ``pip3 install pytest circuitpython-mocks --upgrade`` and run ``pytest -v``
|
|
188
|
+
|
|
189
|
+
Contributing
|
|
190
|
+
============
|
|
191
|
+
|
|
192
|
+
Contributions are welcome! Please read our `Code of Conduct
|
|
193
|
+
<https://github.com/todbot/CircuitPython_TMIDI/blob/HEAD/CODE_OF_CONDUCT.md>`_
|
|
194
|
+
before contributing to help this project stay welcoming.
|
{circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0/circuitpython_tmidi.egg-info}/PKG-INFO
RENAMED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: circuitpython-tmidi
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: MIDI library for CircuitPython
|
|
5
5
|
Author-email: Tod Kurt <tod@todbot.com>
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/todbot/CircuitPython_TMIDI
|
|
8
8
|
Keywords: adafruit,blinka,circuitpython,micropython,tmidi,midi
|
|
9
9
|
Classifier: Intended Audience :: Developers
|
|
10
10
|
Classifier: Topic :: Software Development :: Libraries
|
|
11
11
|
Classifier: Topic :: Software Development :: Embedded Systems
|
|
12
12
|
Classifier: Topic :: System :: Hardware
|
|
13
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
14
13
|
Classifier: Programming Language :: Python :: 3
|
|
15
14
|
Description-Content-Type: text/x-rst
|
|
16
15
|
License-File: LICENSE
|
|
@@ -37,6 +36,100 @@ Introduction
|
|
|
37
36
|
|
|
38
37
|
MIDI library for CircuitPython
|
|
39
38
|
|
|
39
|
+
TMIDI is a MIDI library for CircuitPython / MicroPython. It attempts to
|
|
40
|
+
be a provide a simpler API and perform faster, especially for MIDI parsing,
|
|
41
|
+
compared to ``adafruit_midi`` (`adafruit_midi github <https://github.com/adafruit/Adafruit_CircuitPython_MIDI/>`_)
|
|
42
|
+
TMIDI is derived from ``winterbloom_smolmidi``
|
|
43
|
+
(`smolmidi github <https://github.com/wntrblm/Winterbloom_SmolMIDI>`_)
|
|
44
|
+
by Thea Flowers for Winterbloom.
|
|
45
|
+
|
|
46
|
+
Like ``adafruit_midi``, TMIDI works on CircuitPython's ``usb_midi`` and ``busio.UART``,
|
|
47
|
+
or any stream-like object that supports ``.readinto()`` and ``.write()``
|
|
48
|
+
|
|
49
|
+
Usage Example
|
|
50
|
+
=============
|
|
51
|
+
|
|
52
|
+
Sending MIDI over USB. Channels are 0-indexed, so ``channel=0`` is
|
|
53
|
+
MIDI channel 1:
|
|
54
|
+
|
|
55
|
+
.. code-block:: python
|
|
56
|
+
|
|
57
|
+
import time
|
|
58
|
+
import usb_midi
|
|
59
|
+
import tmidi
|
|
60
|
+
|
|
61
|
+
midi = tmidi.MIDI(midi_out=usb_midi.ports[1])
|
|
62
|
+
|
|
63
|
+
while True:
|
|
64
|
+
midi.send(tmidi.Message(tmidi.NOTE_ON, 60, 100, channel=0))
|
|
65
|
+
time.sleep(0.5)
|
|
66
|
+
midi.send(tmidi.Message(tmidi.NOTE_OFF, 60, 0, channel=0))
|
|
67
|
+
time.sleep(0.5)
|
|
68
|
+
|
|
69
|
+
Receiving MIDI over USB. ``receive()`` returns ``None`` when nothing
|
|
70
|
+
has arrived:
|
|
71
|
+
|
|
72
|
+
.. code-block:: python
|
|
73
|
+
|
|
74
|
+
import usb_midi
|
|
75
|
+
import tmidi
|
|
76
|
+
|
|
77
|
+
midi = tmidi.MIDI(midi_in=usb_midi.ports[0])
|
|
78
|
+
|
|
79
|
+
while True:
|
|
80
|
+
if msg := midi.receive():
|
|
81
|
+
if msg.type == tmidi.NOTE_ON:
|
|
82
|
+
print("note on:", msg.note, msg.velocity, msg.channel)
|
|
83
|
+
else:
|
|
84
|
+
print(msg)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
Callbacks
|
|
88
|
+
=========
|
|
89
|
+
|
|
90
|
+
Instead of ``receive()``, register functions for the message types you want
|
|
91
|
+
and call ``poll()``. No ``Message`` objects are allocated.
|
|
92
|
+
|
|
93
|
+
.. code-block:: python
|
|
94
|
+
|
|
95
|
+
def note_on(channel, note, velocity):
|
|
96
|
+
print("note on:", channel, note, velocity)
|
|
97
|
+
|
|
98
|
+
midi.on(tmidi.NOTE_ON, note_on)
|
|
99
|
+
while True:
|
|
100
|
+
midi.poll()
|
|
101
|
+
|
|
102
|
+
See ``examples/tmidi_simple_poll.py`` for a full example.
|
|
103
|
+
|
|
104
|
+
Turbo parser
|
|
105
|
+
============
|
|
106
|
+
|
|
107
|
+
TMIDI runs entirely in Python, but uses a faster ``@micropython.viper``
|
|
108
|
+
parser when your firmware can load native code. The parsers ship inside the
|
|
109
|
+
``tmidi/`` library folder as ``tmidi_kernel.<arch>.mpy``, and CircuitPython
|
|
110
|
+
picks the one for your board. ``tmidi.ENGINE`` says which parser is in use.
|
|
111
|
+
|
|
112
|
+
TMIDI 3.0 is a folder, ``lib/tmidi/``, instead of a single ``lib/tmidi.mpy``.
|
|
113
|
+
Delete an old ``lib/tmidi.mpy`` when upgrading.
|
|
114
|
+
|
|
115
|
+
====================== =====================================
|
|
116
|
+
``tmidi_kernel`` file Boards
|
|
117
|
+
====================== =====================================
|
|
118
|
+
``armv6m`` RP2040
|
|
119
|
+
``armv7emsp`` SAMD51, nRF52840, RP2350, STM32F4
|
|
120
|
+
``xtensawin`` ESP32-S2, ESP32-S3
|
|
121
|
+
``rv32imc`` ESP32-C3, C5, C6
|
|
122
|
+
====================== =====================================
|
|
123
|
+
|
|
124
|
+
To rebuild them from ``src/tmidi_turbo.py``, use an ``mpy-cross`` from the
|
|
125
|
+
CircuitPython version they will run on. The files land in ``tmidi/``:
|
|
126
|
+
|
|
127
|
+
.. code-block:: shell
|
|
128
|
+
|
|
129
|
+
cd src
|
|
130
|
+
make MPY_CROSS=path/to/circuitpython/mpy-cross/build/mpy-cross
|
|
131
|
+
|
|
132
|
+
|
|
40
133
|
|
|
41
134
|
Dependencies
|
|
42
135
|
=============
|
|
@@ -99,27 +192,6 @@ Or the following command to update an existing version:
|
|
|
99
192
|
|
|
100
193
|
circup update
|
|
101
194
|
|
|
102
|
-
Usage Example
|
|
103
|
-
=============
|
|
104
|
-
|
|
105
|
-
.. code-block:: python
|
|
106
|
-
|
|
107
|
-
import usb_midi
|
|
108
|
-
import tmidi
|
|
109
|
-
|
|
110
|
-
midi_usb = tmidi.MIDI(midi_in=usb_midi.ports[0], midi_out=usb_midi.ports[1])
|
|
111
|
-
|
|
112
|
-
def send_midi_panic(channel=1):
|
|
113
|
-
msg = tmidi.Message(tmidi.NOTE_OFF, channel-1) # channels are 0-indexed
|
|
114
|
-
for notenum in range(128):
|
|
115
|
-
msg.note = notenum
|
|
116
|
-
msg.velocity = 0
|
|
117
|
-
midi_usb.send(msg)
|
|
118
|
-
time.sleep(0.01)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
send_midi_panic()
|
|
122
|
-
|
|
123
195
|
|
|
124
196
|
Documentation
|
|
125
197
|
=============
|
|
@@ -128,6 +200,11 @@ API documentation for this library can be found on `Read the Docs <https://circu
|
|
|
128
200
|
For information on building library documentation, please check out
|
|
129
201
|
`this guide <https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/sharing-our-docs-on-readthedocs#sphinx-5-1>`_.
|
|
130
202
|
|
|
203
|
+
Testing
|
|
204
|
+
=======
|
|
205
|
+
|
|
206
|
+
Install ``pytest`` with ``pip3 install pytest circuitpython-mocks --upgrade`` and run ``pytest -v``
|
|
207
|
+
|
|
131
208
|
Contributing
|
|
132
209
|
============
|
|
133
210
|
|
{circuitpython_tmidi-1.0.5 → circuitpython_tmidi-3.0.0}/circuitpython_tmidi.egg-info/SOURCES.txt
RENAMED
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
.pylintrc
|
|
4
4
|
.readthedocs.yaml
|
|
5
5
|
CODE_OF_CONDUCT.md
|
|
6
|
+
LATENCY_RESULTS.md
|
|
6
7
|
LICENSE
|
|
7
8
|
README.rst
|
|
8
9
|
README.rst.license
|
|
10
|
+
REUSE.toml
|
|
9
11
|
optional_requirements.txt
|
|
10
12
|
pyproject.toml
|
|
11
13
|
requirements.txt
|
|
12
|
-
tmidi.py
|
|
13
14
|
.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md
|
|
14
15
|
.github/workflows/build.yml
|
|
15
16
|
.github/workflows/failure-help-text.yml
|
|
@@ -33,9 +34,32 @@ docs/index.rst.license
|
|
|
33
34
|
docs/requirements.txt
|
|
34
35
|
docs/_static/favicon.ico
|
|
35
36
|
docs/_static/favicon.ico.license
|
|
37
|
+
examples/tmidi_adafruit_midi_comparison.py
|
|
38
|
+
examples/tmidi_latency_device.py
|
|
39
|
+
examples/tmidi_latency_host.py
|
|
36
40
|
examples/tmidi_midiforwarder.py
|
|
37
41
|
examples/tmidi_multiple_receiver.py
|
|
42
|
+
examples/tmidi_simple_arpeggiator.py
|
|
43
|
+
examples/tmidi_simple_poll.py
|
|
38
44
|
examples/tmidi_simple_receiver.py
|
|
39
45
|
examples/tmidi_simpletest.py
|
|
46
|
+
examples/tmidi_sysex.py
|
|
47
|
+
examples/tmidi_test_device.py
|
|
48
|
+
examples/tmidi_test_host.py
|
|
49
|
+
src/Makefile
|
|
50
|
+
src/tmidi_turbo.py
|
|
51
|
+
tests/__init__.py
|
|
52
|
+
tests/conftest.py
|
|
53
|
+
tests/test_kernel_model.py
|
|
40
54
|
tests/test_one.py
|
|
41
|
-
tests/
|
|
55
|
+
tests/test_tmidi_callbacks.py
|
|
56
|
+
tests/test_tmidi_capture.py
|
|
57
|
+
tests/test_tmidi_messages.py
|
|
58
|
+
tests/test_tmidi_one.py
|
|
59
|
+
tests/test_tmidi_sysex.py
|
|
60
|
+
tests/data/rk006_capture.txt
|
|
61
|
+
tmidi/__init__.py
|
|
62
|
+
tmidi/tmidi_kernel.armv6m.mpy
|
|
63
|
+
tmidi/tmidi_kernel.armv7emsp.mpy
|
|
64
|
+
tmidi/tmidi_kernel.rv32imc.mpy
|
|
65
|
+
tmidi/tmidi_kernel.xtensawin.mpy
|