diff options
| author | s-ol <s+removethis@s-ol.nu> | 2023-03-28 14:42:48 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2023-03-28 14:48:47 +0000 |
| commit | e08aca612fd481d1a733003f54bc4c9bf47439cc (patch) | |
| tree | de6e0e1df65a16ef0bb3a4d11d3aa706e8f954f6 | |
| parent | add 'simulator' board for testing on desktop (diff) | |
| download | firmware-e08aca612fd481d1a733003f54bc4c9bf47439cc.tar.gz firmware-e08aca612fd481d1a733003f54bc4c9bf47439cc.zip | |
simulator: sync matrix state via SysEx messages
| -rw-r--r-- | config.py | 1 | ||||
| -rw-r--r-- | hex33board/__init__.py | 29 | ||||
| -rw-r--r-- | hex33board/base.py | 2 | ||||
| -rw-r--r-- | hex33board/boards/r3_d4cf583.py | 16 | ||||
| -rw-r--r-- | hex33board/boards/simulator.py | 65 | ||||
| -rw-r--r-- | hex33board/core.py | 12 | ||||
| -rw-r--r-- | hex33board/i2c.py | 2 |
7 files changed, 87 insertions, 40 deletions
@@ -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: |
