-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathSerialMIDI.py
72 lines (58 loc) · 2.18 KB
/
SerialMIDI.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
"""
Simple script to test MIDI over Serial. Simply reads the position and touch
status, and always responds with the current position, so the setpoint is the
same as the position set by the user.
To use:
- In Motor-Controller/main.cpp, set the `WITH_MIDI` macro to `1`.
- Set `Config::serial_control` to `false`.
- Upload Motor-Controller to an Arduino Uno/Nano.
- Use `pip` to install the `pySerial` package, e.g. using
`python3 -m pip install pySerial`.
- Change the `SER_PORT` constant below to the port of your Arduino.
- Run this script, e.g. using
`python3 Python/SerialMIDI.py`.
- Move the fader and see the messages be printed and the setpoint updated.
"""
SER_PORT = "/dev/ttyUSB0"
from serial import Serial
class MIDIParser:
"""Very basic, only supports note on/off and pitch bend"""
NAMES = {0x80: "NOTE_OFF", 0x90: "NOTE_ON", 0xE0: "PITCH_BEND"}
def __init__(self):
self.type, self.channel, self.data1, self.data2 = 0, 0, 0, 0
self.third_byte = False
def __call__(self, b: int) -> bool:
if b & 0x80:
self.type = b & 0xF0
self.channel = (b & 0x0F) + 1
self.third_byte = False
elif self.third_byte:
self.data2 = b
self.third_byte = False
return self.type in self.NAMES
else:
self.data1 = b
self.third_byte = True
return False
def handle_midi(ser: Serial, parser: MIDIParser):
name = parser.NAMES[parser.type]
if name == "PITCH_BEND":
value = parser.data1 | (parser.data2 << 7)
print(f"{name}: {parser.channel}, " f"{value}")
# Echo back to change the setpoint
msg = [parser.type | (parser.channel - 1), parser.data1, parser.data2]
ser.write(bytes(msg))
else:
print(f"{name}: {parser.channel}, " f"{parser.data1}, {parser.data2}")
def read_midi(ser: Serial, parser: MIDIParser):
data: bytes = ser.read()
if not data:
return
for b in data:
if parser(b):
handle_midi(ser, parser)
with Serial(SER_PORT, 1_000_000, timeout=0.5) as ser:
ser.reset_input_buffer()
parser = MIDIParser()
while True:
read_midi(ser, parser)