aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2023-03-28 14:42:48 +0000
committers-ol <s+removethis@s-ol.nu>2023-03-28 14:48:47 +0000
commite08aca612fd481d1a733003f54bc4c9bf47439cc (patch)
treede6e0e1df65a16ef0bb3a4d11d3aa706e8f954f6
parentadd 'simulator' board for testing on desktop (diff)
downloadfirmware-e08aca612fd481d1a733003f54bc4c9bf47439cc.tar.gz
firmware-e08aca612fd481d1a733003f54bc4c9bf47439cc.zip
simulator: sync matrix state via SysEx messages
-rw-r--r--config.py1
-rw-r--r--hex33board/__init__.py29
-rw-r--r--hex33board/base.py2
-rw-r--r--hex33board/boards/r3_d4cf583.py16
-rw-r--r--hex33board/boards/simulator.py65
-rw-r--r--hex33board/core.py12
-rw-r--r--hex33board/i2c.py2
7 files changed, 87 insertions, 40 deletions
diff --git a/config.py b/config.py
index 37f274c..72819c1 100644
--- a/config.py
+++ b/config.py
@@ -1,3 +1,4 @@
from hex33board.boards.r3_d4cf583 import *
dev_mode = False
+simulator = False
diff --git a/hex33board/__init__.py b/hex33board/__init__.py
index 06e2f42..6735988 100644
--- a/hex33board/__init__.py
+++ b/hex33board/__init__.py
@@ -1,9 +1,8 @@
from __future__ import annotations
import displayio
-from adafruit_midi.note_on import NoteOn
-from adafruit_midi.note_off import NoteOff
from adafruit_ticks import ticks_ms
+from adafruit_midi.system_exclusive import SystemExclusive
try:
import storage
@@ -50,22 +49,22 @@ class Keyboard:
def __init__(self, board):
self.board = board
- self.matrix = board.create_matrix()
+ self.matrix = board.create_matrix(self)
self.board.dev_mode = dev_mode
- self.pixels = board.create_pixels(auto_write=False)
+ self.pixels = board.create_pixels(self, auto_write=False)
displayio.release_displays()
- self.display = board.create_display(width=128, height=32, auto_refresh=False)
+ self.display = board.create_display(self, width=128, height=32, auto_refresh=False)
self.display.show(displayio.Group())
self.display.refresh()
- self.midi_usb = board.create_midi_usb()
+ self.midi_usb = board.create_midi_usb(self)
- self.midi_trs = board.create_midi_trs()
+ self.midi_trs = board.create_midi_trs(self)
self.i2c = None
- self.audio_out = board.create_audio_out()
+ self.audio_out = board.create_audio_out(self)
self.settings = Settings(
{
@@ -251,14 +250,9 @@ class Keyboard:
self._mode = mode
self._mode.enter()
- def broadcast(self, on: bool, note: Note, local: bool):
- Class = NoteOn if on else NoteOff
- midi = Class(note.pitch, self.velocity)
- self.midi_usb.send(midi)
- self.midi_trs.send(midi)
-
- if self.i2c and local:
- self.i2c.send(on, note)
+ def broadcast(self, msg: MIDIMessage):
+ self.midi_usb.send(msg)
+ self.midi_trs.send(msg)
def run(self):
while True:
@@ -266,6 +260,9 @@ class Keyboard:
self.mode.tick(ticks)
for (i, pressed) in self.matrix.scan_for_changes():
+ if self.board.simulator:
+ msg = SystemExclusive(b"\0s-", [i, pressed])
+ self.broadcast(msg)
mode = self.sticky_modes.pop(i, self.mode)
should_stick = mode.key_event(i, pressed)
diff --git a/hex33board/base.py b/hex33board/base.py
index e140329..18c7709 100644
--- a/hex33board/base.py
+++ b/hex33board/base.py
@@ -88,7 +88,7 @@ class BaseMode(Mode):
self.scale_label.text = self.keyboard.scale.format()
def tick(self, ticks_ms: int):
- for note in self.notes_expiring.values():
+ for note in list(self.notes_expiring.values()):
note.update_expiry(ticks_ms)
self.rgb.tick(ticks_ms)
diff --git a/hex33board/boards/r3_d4cf583.py b/hex33board/boards/r3_d4cf583.py
index b2a72db..078ad53 100644
--- a/hex33board/boards/r3_d4cf583.py
+++ b/hex33board/boards/r3_d4cf583.py
@@ -27,16 +27,16 @@ matrix_pins = (
)
-def create_matrix():
+def create_matrix(board):
return Matrix(*matrix_pins)
pixels_pin = board.GP17
-def create_pixels(**kwargs):
+def create_pixels(board, **kwargs):
return NeoPixel(pixels_pin, 48 + 4, **kwargs)
display_pins = { "sda": board.GP14, "scl": board.GP15 }
display_rotation = 180
-def create_display(**kwargs):
+def create_display(board, **kwargs):
i2c = I2C(**display_pins, frequency=1000000)
bus = I2CDisplay(i2c, device_address=0x3C)
return SSD1306(
@@ -44,22 +44,22 @@ def create_display(**kwargs):
)
midi_pins = { "tx": board.GP16, "rx": None }
-def create_midi_trs():
+def create_midi_trs(board):
return MIDI(None, UART(**midi_pins, baudrate=31250))
-def create_midi_usb():
+def create_midi_usb(board):
midi_in = next(p for p in usb_midi.ports if isinstance(p, usb_midi.PortIn))
midi_out = next(p for p in usb_midi.ports if isinstance(p, usb_midi.PortOut))
return MIDI(midi_in, midi_out)
audio_pins = { "left_channel": board.GP18, "right_channel": board.GP19 }
-def create_audio_out():
+def create_audio_out(board):
return PWMAudioOut(**audio_pins)
sda = board.GP20
scl = board.GP21
-def create_i2c(**kwargs):
+def create_i2c(board, **kwargs):
return I2C(sda=sda, scl=scl, **kwargs)
-def create_i2ctarget(**kwargs):
+def create_i2ctarget(board, **kwargs):
from i2ctarget import I2CTarget
return I2CTarget(sda=sda, scl=scl, **kwargs)
diff --git a/hex33board/boards/simulator.py b/hex33board/boards/simulator.py
index 32f83a7..234acbb 100644
--- a/hex33board/boards/simulator.py
+++ b/hex33board/boards/simulator.py
@@ -1,34 +1,75 @@
import displayio
import rtmidi
from blinka_displayio_pygamedisplay import PyGameDisplay
-from adafruit_midi import MIDI
+from adafruit_midi import MIDI, MIDIMessage
+from adafruit_midi.system_exclusive import SystemExclusive
+import adafruit_midi.note_on
+import adafruit_midi.note_off
rtmidi_api = rtmidi.API_UNSPECIFIED
if rtmidi.API_UNIX_JACK in rtmidi.get_compiled_api():
rtmidi_api = rtmidi.API_UNIX_JACK
-def create_midi_usb():
+class MIDI:
+ def __init__(self, inp, out):
+ self.inp = inp
+ self.out = out
+ self.in_channel = tuple(range(16))
+ self.out_channel = out
+
+ def send(self, message, channel=None):
+ if not self.out:
+ return
+
+ if channel is None:
+ channel = self.out_channel
+ if isinstance(message, MIDIMessage):
+ message.channel = channel
+
+ self.out.send_message(bytes(message))
+
+ def receive(self):
+ if not self.inp:
+ return
+
+ msg = self.inp.get_message()
+ if msg:
+ return MIDIMessage.from_message_bytes(msg[0], self.in_channel)[0]
+
+def create_midi_usb(board):
inp = rtmidi.MidiIn(rtmidi_api, "0x33.boad sim")
out = rtmidi.MidiOut(rtmidi_api, "0x33.boad sim")
inp.open_virtual_port("USB in")
out.open_virtual_port("USB out")
+ inp.ignore_types(sysex=False)
return MIDI(inp, out)
-def create_midi_trs():
+def create_midi_trs(board):
out = rtmidi.MidiOut(rtmidi_api, "0x33.boad sim")
out.open_virtual_port("TRS out")
return MIDI(None, out)
-def create_matrix():
+def create_matrix(board):
class DummyMatrix:
+ def __init__(self, board):
+ self.board = board
+
def scan_for_changes(self):
- if False:
- yield 0, True
+ if not self.board.midi_usb:
+ return
+
+ msg = self.board.midi_usb.receive()
+ while msg:
+ if isinstance(msg, SystemExclusive) and msg.manufacturer_id == b'\0s-':
+ key, state = msg.data
+ yield key, state
+
+ msg = self.board.midi_usb.receive()
- return DummyMatrix()
+ return DummyMatrix(board)
display_rotation = 0
-def create_display(**kwargs):
+def create_display(board, **kwargs):
class ScaledDisplay:
def __init__(self, width, height, scale=1, **args):
self.display = PyGameDisplay(width=width*scale, height=height*scale, **args)
@@ -45,7 +86,7 @@ def create_display(**kwargs):
return ScaledDisplay(scale=4, **kwargs)
-def create_pixels(**kwargs):
+def create_pixels(board, **kwargs):
class DummyNeopixels:
def __init__(self):
self.brightness = 1
@@ -61,11 +102,11 @@ def create_pixels(**kwargs):
return DummyNeopixels()
-def create_audio_out():
+def create_audio_out(board):
pass
-def create_i2c(**kwargs):
+def create_i2c(board, **kwargs):
pass
-def create_i2ctarget(**kwargs):
+def create_i2ctarget(board, **kwargs):
pass
diff --git a/hex33board/core.py b/hex33board/core.py
index 922dea8..730d0c4 100644
--- a/hex33board/core.py
+++ b/hex33board/core.py
@@ -4,6 +4,8 @@ import displayio
from micropython import const
from colorsys import hsv_to_rgb
from adafruit_ticks import ticks_ms, ticks_diff
+from adafruit_midi.note_on import NoteOn
+from adafruit_midi.note_off import NoteOff
def clamp_norm(v):
return max(0, min(v, 1))
@@ -28,7 +30,10 @@ class Note:
self.base.notes[self.pitch] = self
self.base.notes_dirty = True
- self.keyboard.broadcast(True, self, local)
+ msg = NoteOn(self.pitch, self.keyboard.velocity)
+ self.keyboard.broadcast(msg)
+ if local and self.keyboard.i2c:
+ self.keyboard.i2c.send(self, True)
if self.pitch in self.base.notes_expiring:
del self.base.notes_expiring[self.pitch]
@@ -39,7 +44,10 @@ class Note:
del self.base.notes[self.pitch]
self.base.notes_dirty = True
- self.keyboard.broadcast(False, self, local)
+ msg = NoteOff(self.pitch, self.keyboard.velocity)
+ self.keyboard.broadcast(msg)
+ if local and self.keyboard.i2c:
+ self.keyboard.i2c.send(self, False)
self.end_tick = ticks_ms()
diff --git a/hex33board/i2c.py b/hex33board/i2c.py
index 32ed17a..cc31c45 100644
--- a/hex33board/i2c.py
+++ b/hex33board/i2c.py
@@ -27,7 +27,7 @@ class I2CModule:
elif pitch in self.base.notes:
self.base.notes[pitch].off(local=False)
- def send(self, on, note):
+ def send(self, note, on):
if on:
self.queue.append(note.pitch | NOTE_ON_BIT)
else: