124 lines
3.1 KiB
Python
124 lines
3.1 KiB
Python
#!/usr/bin/env python3.11
|
|
import itertools
|
|
import sys
|
|
import time
|
|
import rtmidi
|
|
from typing import Tuple
|
|
|
|
|
|
G_STATUS_NAMES = {
|
|
0x80: "NOTE_OFF",
|
|
0x90: "NOTE_ON",
|
|
0xa0: "KEY_PRESSR",
|
|
0xb0: "CTRL_CHANGE",
|
|
0xc0: "PROG_CHANGE",
|
|
0xd0: "CHAN_PRESSR",
|
|
0xe0: "PITCH_BEND",
|
|
0xf0: "XXX",
|
|
}
|
|
|
|
|
|
def note_on(chan: int, note: int, velocity: int) -> Tuple[int, int, int]:
|
|
assert 0 < chan <= 16, chan
|
|
assert 0 <= note <= 0xFF, note
|
|
assert 0 <= velocity <= 0xFF, velocity
|
|
msg_byte = 0x90 + (chan - 1)
|
|
return msg_byte, note, velocity
|
|
|
|
|
|
def note_off(chan: int, note: int) -> Tuple[int, int, int]:
|
|
assert 0 < chan <= 16, chan
|
|
assert 0 <= note <= 0xFF, note
|
|
msg_byte = 0x80 + (chan - 1)
|
|
return msg_byte, note, 0
|
|
|
|
|
|
def get_status_name(status: int) -> str:
|
|
return G_STATUS_NAMES.get(status & 0xF0) or f'<? {status:02x} ?>'
|
|
|
|
|
|
def test_midi_out() -> int:
|
|
midi_out = rtmidi.MidiOut()
|
|
ports: list[str] = midi_out.get_ports()
|
|
print('Ports:')
|
|
for i, port_name in enumerate(ports):
|
|
print(f'{i:2d} {port_name}')
|
|
|
|
# nanokey_index: int = ([i for i, port_name in enumerate(ports) if 'nanoKEY' in port_name] or [-1])[0]
|
|
# if nanokey_index < 0:
|
|
# print(f'nanoKEY2 port -> NOT FOUND [{ports}]', file=sys.stderr)
|
|
# return 1
|
|
#
|
|
# print(f'nanoKEY2 port -> {nanokey_index} ({ports[nanokey_index]})')
|
|
# midi_out.open_port(nanokey_index)
|
|
midi_out.open_port(0)
|
|
with midi_out:
|
|
midi_out.send_message(note_on(1, 60, 112))
|
|
time.sleep(0.5)
|
|
midi_out.send_message(note_off(1, 60))
|
|
time.sleep(0.1)
|
|
|
|
return 0
|
|
|
|
|
|
def handle_midi_event(msg, midi_out):
|
|
midi_msg, delay = msg
|
|
status, b1, b2 = midi_msg
|
|
b_hi = b1 + (b2 << 8)
|
|
status_name = get_status_name(status)
|
|
# if status_name == "PITCH_BEND":
|
|
print(f'[d:{delay:2.2f}] {status_name:11s} [ch {1 + (status & 0x0f)}] -> {b1:02x}[{b1:3d}] {b2:02x}[{b2:3d}] hi: {b_hi}')
|
|
|
|
midi_out.send_message([status, b1, 127])
|
|
|
|
|
|
def set_all(midi_out, value):
|
|
for ch, btn_id in itertools.product([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 16], range(128)):
|
|
midi_out.send_message(note_on(ch, btn_id, value))
|
|
|
|
|
|
def test_midi_in() -> int:
|
|
midi_in = rtmidi.MidiIn()
|
|
ports: list[str] = midi_in.get_ports()
|
|
print('Ports:')
|
|
for i, port_name in enumerate(ports):
|
|
print(f'{i:2d} {port_name}')
|
|
|
|
midi_out = rtmidi.MidiOut()
|
|
|
|
port = 1
|
|
midi_in.set_callback(handle_midi_event, midi_out)
|
|
midi_in.open_port(port)
|
|
midi_out.open_port(port)
|
|
try:
|
|
set_all(midi_out, 127)
|
|
time.sleep(0.5)
|
|
set_all(midi_out, 0)
|
|
|
|
i = 0
|
|
levels = list(range(3))
|
|
while True:
|
|
if i >= len(levels):
|
|
i = -len(levels) + 1
|
|
|
|
midi_out.send_message(note_on(3, 2, levels[abs(i)]))
|
|
|
|
i += 1
|
|
time.sleep(0.5)
|
|
except KeyboardInterrupt:
|
|
print('CTRL+C TRIGGERED')
|
|
finally:
|
|
set_all(midi_out, 0)
|
|
midi_in.cancel_callback()
|
|
midi_in.close_port()
|
|
|
|
return 0
|
|
|
|
|
|
def main() -> int:
|
|
return test_midi_in()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
raise SystemExit(main())
|