circuitpython-tmidi 2.0.0__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-2.0.0 → circuitpython_tmidi-3.0.0}/.github/workflows/release_gh.yml +1 -4
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/.gitignore +4 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/.pre-commit-config.yaml +3 -3
- circuitpython_tmidi-3.0.0/LATENCY_RESULTS.md +77 -0
- {circuitpython_tmidi-2.0.0/circuitpython_tmidi.egg-info → circuitpython_tmidi-3.0.0}/PKG-INFO +97 -27
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/README.rst +95 -24
- circuitpython_tmidi-3.0.0/REUSE.toml +6 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0/circuitpython_tmidi.egg-info}/PKG-INFO +97 -27
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/circuitpython_tmidi.egg-info/SOURCES.txt +23 -2
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/docs/examples.rst +10 -0
- {circuitpython_tmidi-2.0.0 → 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-3.0.0/examples/tmidi_simple_receiver.py +21 -0
- 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-2.0.0 → 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/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-2.0.0 → circuitpython_tmidi-3.0.0}/tests/test_tmidi_messages.py +32 -1
- 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-2.0.0/examples/tmidi_simple_arpeggiator.py +0 -58
- circuitpython_tmidi-2.0.0/examples/tmidi_simple_receiver.py +0 -31
- circuitpython_tmidi-2.0.0/tests/test_tmidi_one.py +0 -64
- circuitpython_tmidi-2.0.0/tmidi.py +0 -423
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/.github/workflows/build.yml +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/.github/workflows/failure-help-text.yml +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/.github/workflows/release_pypi.yml +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/.pylintrc +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/.readthedocs.yaml +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/CODE_OF_CONDUCT.md +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/LICENSE +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/LICENSES/CC-BY-4.0.txt +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/LICENSES/MIT.txt +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/LICENSES/Unlicense.txt +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/README.rst.license +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/circuitpython_tmidi.egg-info/dependency_links.txt +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/circuitpython_tmidi.egg-info/requires.txt +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/circuitpython_tmidi.egg-info/top_level.txt +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/docs/_static/favicon.ico +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/docs/_static/favicon.ico.license +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/docs/api.rst +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/docs/api.rst.license +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/docs/conf.py +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/docs/examples.rst.license +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/docs/index.rst.license +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/docs/requirements.txt +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/examples/tmidi_midiforwarder.py +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/examples/tmidi_multiple_receiver.py +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/examples/tmidi_simpletest.py +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/optional_requirements.txt +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/requirements.txt +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/setup.cfg +0 -0
- {circuitpython_tmidi-2.0.0 → circuitpython_tmidi-3.0.0}/tests/__init__.py +0 -0
- {circuitpython_tmidi-2.0.0 → 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-2.0.0/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,28 +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(midi_channel=1):
|
|
113
|
-
ch = midi_channel-1
|
|
114
|
-
msg = tmidi.Message(tmidi.NOTE_OFF, channel=ch) # channels are 0-indexed
|
|
115
|
-
for notenum in range(128):
|
|
116
|
-
msg.note = notenum
|
|
117
|
-
msg.velocity = 0
|
|
118
|
-
midi_usb.send(msg)
|
|
119
|
-
time.sleep(0.01)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
send_midi_panic()
|
|
123
|
-
|
|
124
195
|
|
|
125
196
|
Documentation
|
|
126
197
|
=============
|
|
@@ -132,8 +203,7 @@ For information on building library documentation, please check out
|
|
|
132
203
|
Testing
|
|
133
204
|
=======
|
|
134
205
|
|
|
135
|
-
Install ``pytest`` with ``pip3 install pytest --upgrade`` and run ``pytest -v``
|
|
136
|
-
To build docs:
|
|
206
|
+
Install ``pytest`` with ``pip3 install pytest circuitpython-mocks --upgrade`` and run ``pytest -v``
|
|
137
207
|
|
|
138
208
|
Contributing
|
|
139
209
|
============
|
|
@@ -17,6 +17,100 @@ Introduction
|
|
|
17
17
|
|
|
18
18
|
MIDI library for CircuitPython
|
|
19
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
|
+
|
|
20
114
|
|
|
21
115
|
Dependencies
|
|
22
116
|
=============
|
|
@@ -79,28 +173,6 @@ Or the following command to update an existing version:
|
|
|
79
173
|
|
|
80
174
|
circup update
|
|
81
175
|
|
|
82
|
-
Usage Example
|
|
83
|
-
=============
|
|
84
|
-
|
|
85
|
-
.. code-block:: python
|
|
86
|
-
|
|
87
|
-
import usb_midi
|
|
88
|
-
import tmidi
|
|
89
|
-
|
|
90
|
-
midi_usb = tmidi.MIDI(midi_in=usb_midi.ports[0], midi_out=usb_midi.ports[1])
|
|
91
|
-
|
|
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
|
|
95
|
-
for notenum in range(128):
|
|
96
|
-
msg.note = notenum
|
|
97
|
-
msg.velocity = 0
|
|
98
|
-
midi_usb.send(msg)
|
|
99
|
-
time.sleep(0.01)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
send_midi_panic()
|
|
103
|
-
|
|
104
176
|
|
|
105
177
|
Documentation
|
|
106
178
|
=============
|
|
@@ -112,8 +184,7 @@ For information on building library documentation, please check out
|
|
|
112
184
|
Testing
|
|
113
185
|
=======
|
|
114
186
|
|
|
115
|
-
Install ``pytest`` with ``pip3 install pytest --upgrade`` and run ``pytest -v``
|
|
116
|
-
To build docs:
|
|
187
|
+
Install ``pytest`` with ``pip3 install pytest circuitpython-mocks --upgrade`` and run ``pytest -v``
|
|
117
188
|
|
|
118
189
|
Contributing
|
|
119
190
|
============
|
{circuitpython_tmidi-2.0.0 → 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,28 +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(midi_channel=1):
|
|
113
|
-
ch = midi_channel-1
|
|
114
|
-
msg = tmidi.Message(tmidi.NOTE_OFF, channel=ch) # channels are 0-indexed
|
|
115
|
-
for notenum in range(128):
|
|
116
|
-
msg.note = notenum
|
|
117
|
-
msg.velocity = 0
|
|
118
|
-
midi_usb.send(msg)
|
|
119
|
-
time.sleep(0.01)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
send_midi_panic()
|
|
123
|
-
|
|
124
195
|
|
|
125
196
|
Documentation
|
|
126
197
|
=============
|
|
@@ -132,8 +203,7 @@ For information on building library documentation, please check out
|
|
|
132
203
|
Testing
|
|
133
204
|
=======
|
|
134
205
|
|
|
135
|
-
Install ``pytest`` with ``pip3 install pytest --upgrade`` and run ``pytest -v``
|
|
136
|
-
To build docs:
|
|
206
|
+
Install ``pytest`` with ``pip3 install pytest circuitpython-mocks --upgrade`` and run ``pytest -v``
|
|
137
207
|
|
|
138
208
|
Contributing
|
|
139
209
|
============
|
{circuitpython_tmidi-2.0.0 → 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,12 +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
|
|
38
42
|
examples/tmidi_simple_arpeggiator.py
|
|
43
|
+
examples/tmidi_simple_poll.py
|
|
39
44
|
examples/tmidi_simple_receiver.py
|
|
40
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
|
|
41
51
|
tests/__init__.py
|
|
52
|
+
tests/conftest.py
|
|
53
|
+
tests/test_kernel_model.py
|
|
42
54
|
tests/test_one.py
|
|
55
|
+
tests/test_tmidi_callbacks.py
|
|
56
|
+
tests/test_tmidi_capture.py
|
|
43
57
|
tests/test_tmidi_messages.py
|
|
44
|
-
tests/test_tmidi_one.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
|
|
@@ -18,6 +18,16 @@ Show to to receive MIDI messages.
|
|
|
18
18
|
:linenos:
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
Simple poll
|
|
22
|
+
-----------
|
|
23
|
+
|
|
24
|
+
Show how to receive MIDI messages with callbacks and ``poll()``.
|
|
25
|
+
|
|
26
|
+
.. literalinclude:: ../examples/tmidi_simple_poll.py
|
|
27
|
+
:caption: examples/tmidi_simple_poll.py
|
|
28
|
+
:linenos:
|
|
29
|
+
|
|
30
|
+
|
|
21
31
|
Multiple receivers
|
|
22
32
|
------------------
|
|
23
33
|
|
|
@@ -36,10 +36,7 @@ Table of Contents
|
|
|
36
36
|
Download Library Bundle <https://circuitpython.org/libraries>
|
|
37
37
|
CircuitPython Reference Documentation <https://docs.circuitpython.org>
|
|
38
38
|
CircuitPython Support Forum <https://forums.adafruit.com/viewforum.php?f=60>
|
|
39
|
-
|
|
40
|
-
Adafruit Learning System <https://learn.adafruit.com>
|
|
41
|
-
Adafruit Blog <https://blog.adafruit.com>
|
|
42
|
-
Adafruit Store <https://www.adafruit.com>
|
|
39
|
+
|
|
43
40
|
|
|
44
41
|
Indices and tables
|
|
45
42
|
==================
|