from __future__ import annotations import displayio from adafruit_ticks import ticks_ms from adafruit_midi.note_on import NoteOn from adafruit_midi.note_off import NoteOff from adafruit_display_text import label from .util import FONT_10, led_map from .i2c import i2c_encode led_map = led_map[:] led_map.extend([1, 0, None, None, 2, 3]) try: import audiomixer import synthio except: synthio = None class Keyboard: matrix: Matrix | None pixels: NeoPixel | None display: Display | None midi_usb: MIDI | None midi_trs: MIDI | None audio_out: PWMAudioOut | None def __init__(self, board): try: self.matrix = board.create_matrix(self) except Exception as e: print("Error bringing up matrix:", e) self.matrix = None try: self.pixels = board.create_pixels(self, auto_write=False) except Exception as e: print("Error bringing up pixels:", e) self.pixels = None try: displayio.release_displays() self.display = board.create_display( self, width=128, height=32, auto_refresh=False ) self.group = displayio.Group() self.label = label.Label( FONT_10, text="Hello World", color=0xFFFFFF, anchor_point=(0, 0), anchored_position=(0, 0), ) self.group.append(self.label) self.display.root_group = self.group self.display.refresh() except Exception as e: print("Error bringing up display:", e) self.display = None try: self.midi_usb = board.create_midi_usb(self) except Exception as e: print("Error bringing up midi_usb:", e) self.midi_usb = None try: self.midi_trs = board.create_midi_trs(self) self.midi_trs.out_channel = 0 except Exception as e: print("Error bringing up midi_trs:", e) self.midi_trs = None try: self.i2c = board.create_i2c(board, frequency=1000000, timeout=1000) self.i2c.try_lock() except Exception as e: print("Error bringing up i2c:", e) self.i2c = None try: self.audio_out = board.create_audio_out(self) self.synth = synthio.Synthesizer(sample_rate=41100) self.mixer = audiomixer.Mixer( sample_rate=41100, buffer_size=5120, channel_count=1 ) self.filter = self.synth.low_pass_filter(2000, 0.8) self.audio_out.play(self.mixer) self.mixer.voice[0].play(self.synth) self.mixer.voice[0].level = (30 * 30) / 10000 except Exception as e: print("Error bringing up synth:", e) self.synth = None self.cycle = 0 self.pressed = {} for i, l in enumerate(led_map): self.pressed[i] = 0 self.notes = {} def broadcast(self, msg: MIDIMessage): if self.midi_usb: self.midi_usb.send(msg) if self.midi_trs: self.midi_trs.send(msg) def tick(self): self.cycle = (self.cycle + 1) % 1000 if self.cycle < 300: base = 0x660000 elif self.cycle < 600: base = 0x006600 elif self.cycle < 900: base = 0x000066 else: base = 0x666666 for i, pressed in self.matrix.scan_for_changes(): pitch = 36 + i if pressed: self.broadcast(NoteOn(pitch, 127)) self.pressed[i] |= 0xFF0000 else: self.broadcast(NoteOff(pitch, 127)) self.pressed[i] &= ~0xFF0000 if self.i2c: msg = i2c_encode(32 + i, pressed) try: self.i2c.writeto(0x33, bytes([msg])) except OSError: pass if self.synth: if pressed: note = synthio.Note(synthio.midi_to_hz(pitch), filter=self.filter) self.notes[i] = note self.synth.press(note) elif note := self.notes.pop(i, None): self.synth.release(note) if self.pixels: for i, l in enumerate(led_map): if l is None: continue self.pixels[l] = self.pressed[i] | base self.pixels.show() if self.display: self.display.refresh() def run(self): while True: self.tick()