diff --git a/boot_out.txt b/boot_out.txt index 1b756ac..6581951 100644 --- a/boot_out.txt +++ b/boot_out.txt @@ -1 +1,2 @@ -Adafruit CircuitPython 6.2.0-rc.0 on 2021-04-01; Raspberry Pi Pico with rp2040 +Adafruit CircuitPython 7.0.0 on 2021-09-20; Raspberry Pi Pico with rp2040 +Board ID:raspberry_pi_pico diff --git a/code.py b/code.py index ab18ebf..17f06d9 100644 --- a/code.py +++ b/code.py @@ -1,9 +1,7 @@ -import time +from adafruit_midi.midi_message import MIDIMessage import board import busio -import adafruit_ssd1306 - from adafruit_bus_device.i2c_device import I2CDevice import adafruit_dotstar @@ -11,8 +9,8 @@ import adafruit_midi from adafruit_midi.control_change import ControlChange -from adafruit_midi.note_off import NoteOff -from adafruit_midi.note_on import NoteOn + +from ulab import numpy as math from digitalio import DigitalInOut, Direction, Pull @@ -24,175 +22,243 @@ p.direction = Direction.OUTPUT p.value = 1 -num_butts = 0x10 - -lights = adafruit_dotstar.DotStar( - board.GP18, board.GP19, num_butts, brightness=0.1, auto_write=True -) - i2c = busio.I2C(board.GP5, board.GP4) keypad = I2CDevice(i2c, 0x20) midi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=0) -oled_height = 32 -oled_width = 128 -# oled = adafruit_ssd1306.SSD1306_I2C(oled_width, oled_height, i2c) -# oled.fill(0) -# oled.show() - -class LFO_STATE: - off = 0 - slow = 1 - fast = 2 +class Butts: + length = 0x10 + held = [False] * length + pressed = [False] * length + def read(self): + with keypad: + keypad.write(bytes([0x0])) + result = bytearray(2) + keypad.readinto(result) + b = result[0] | result[1] << 8 -num_lfos = 8 + for i in range(0x0, Butts.length): + p = not (1 << i) & b + if p: + if self.held[i]: + self.pressed[i] = False + else: + self.pressed[i] = True + self.held[i] = True + else: + self.pressed[i] = False + self.held[i] = False + return self.pressed -def read_butts(): - pressed = [0] * 16 - with keypad: - keypad.write(bytes([0x0])) - result = bytearray(2) - keypad.readinto(result) - b = result[0] | result[1] << 8 +lights = adafruit_dotstar.DotStar(board.GP18, + board.GP19, + Butts.length, + brightness=1, + auto_write=True) - for i in range(0x0, len(pressed)): - if not (1 << i) & b: - pressed[i] = 1 - else: - pressed[i] = 0 - return pressed +butts = Butts() -class Lfo: - OFF = "off" - SLOW = "slow" - FAST = "fast" - state = OFF +class Control: idx = 0 - value = 0.0 - direction = +1 - held = False - incs = {OFF: 0, SLOW: 0.01, FAST: 0.1} - start = 0.0 - end = 1.0 def __init__(self, idx): self.idx = idx - def draw(self): - line_height = oled_height // num_lfos - # TODO consider removing gap - # oled.fill_rect( - # 0, self.idx * line_height, int(self.value * oled_width), line_height - 1, 1 - # ) + def light(self, color): + lights[self.idx] = color - def pressed(self, butts): - return butts[self.idx] - def step(self): - inc = self.incs[self.state] - self.value += inc * self.direction - if self.value <= self.start: - self.value = self.start - self.direction = 1 - elif self.value >= self.end: - self.value = self.end - self.direction = -1 - color_offset = lfo.int(128) - if lfo.off: - self.light((0, 0, 0)) - elif lfo.slow: - self.light((255, color_offset, 0)) - elif lfo.fast: - self.light((0, color_offset, 255)) +class Lfo(Control): + length = 0x8 + idx = 0 + direction = +1 + tick = 1 + start = 0x0 + end = 0xA + speed = 0x5 + on = False - def light(self, color): - lights[self.idx] = color + def __init__(self, idx): + self.idx = idx - def int(self, mul): - return int(self.value * mul) + def step(self): + self.tick += 1 - def send(self): - midi.send(ControlChange(self.idx, self.int(127))) + def update_lights(self): + if self.tick % 3: + return + i = lfo.int(255) + redness = (math.sin(2**math.pi / self.tick) + 1) * 64 + if lfo.on: + self.light((redness, i // (self.speed + 1), i)) + else: + self.light((0, 0, 0)) @property - def off(self): - return self.state == Lfo.OFF + def sin(self): + speed = 2**(12 - self.speed) + period = (2 * math.pi) / speed + amp = (self.start - self.end) / 10 + offset = self.start / 10 + sin = math.sin(period * self.tick) + return (amp * sin) + offset @property - def slow(self): - return self.state == Lfo.SLOW + def value(self): + return (self.sin + 1) / 2 - @property - def fast(self): - return self.state == Lfo.FAST + def int(self, mul): + return int(self.value * mul) - def press(self): - if self.held: - return - elif self.off: - self.state = Lfo.SLOW - elif self.slow: - self.state = Lfo.FAST - elif self.fast: - self.state = Lfo.OFF - self.held = True + def send(self): + midi.send(ControlChange(self.idx, self.int(127))) - def release(self): - self.held = False + def press(self, pressed_buttons): + if pressed_buttons[self.idx]: + self.on = not self.on + self.light((0, 0, 0)) bpm_mode = False bpm = 128 -lfos = [Lfo(0)] * num_lfos - -for idx in range(num_lfos): - lfos[idx] = Lfo(idx) +lfos = [Lfo(x) for x in range(Lfo.length)] class Mode: NORMAL = "normal" TEACHING = "teaching" - SET_BPM = "bpm" + SET_SPEED_CHOOSE_LFO = "speed lfo" + SET_SPEED = "speed set" + SET_LIMIT_CHOOSE_LFO = "limit lfo" SET_LIMIT_START = "limit start" SET_LIMIT_END = "limit end" SET_CHANNEL = "channel" current = NORMAL + @property + def normal(self): + return self.current == Mode.NORMAL + + @property + def teaching(self): + return self.current == Mode.TEACHING + + def become_normal(self): + self.current = Mode.NORMAL + mode = Mode() -while True: - # todo use bpm - time.sleep(0.1) - # oled.fill(0) - # todo set bpm mode (press B then type a number then press F to select. press B again to cancel) - # todo set channel mode (press C, type number to set midi channel, then F to select. press B to cancel) - # in bpm and channel mode: numbers should be white, B should be red and F should be green - # todo teaching mode (press F, disables LFO, press a button to send the CC for that lfo once) - # todo set limits mode (press A, press a control, press a number and then select start (0-A) and end (0-A)) +# for the type +setting_lfo = Lfo(-1) - if mode.current == mode.TEACHING: - continue +pmode = None - butts = read_butts() +while True: + # todo set channel mode (press C, type number to set midi channel, then F to select. press B to cancel) + # todo set cc number mode (press C, then C, then select an lfo, type number to set cc, then F to select. press B to cancel) + butts.read() + if pmode != mode.current: + for idx in range(Butts.length): + lights[idx] = (0, 0, 0) + if mode.current == mode.NORMAL: + lights[0xA] = (10, 20, 40) + lights[0xB] = (50, 30, 10) + lights[0xF] = (50, 20, 20) + pmode = mode.current for lfo in lfos: - lfo.step() - - if not lfo.off: + if lfo.on and not mode.teaching: + lfo.step() lfo.send() - lfo.draw() - - if lfo.pressed(butts): - lfo.press() - else: - lfo.release() + if mode.normal: + lfo.press(butts.pressed) + lfo.update_lights() + + # todo deal with repetition + if mode.teaching: + lights[0xF] = (255, 0, 0) + for idx in range(Lfo.length): + lights[idx] = (250, 250, 255) + if butts.pressed[idx]: + lfos[idx].send() + mode.become_normal() + + if butts.pressed[0xF]: + mode.become_normal() + + elif mode.current == mode.SET_SPEED_CHOOSE_LFO: + lights[0xF] = (255, 0, 0) + for idx in range(Lfo.length): + lights[idx] = (255, 255, 240) + if butts.pressed[idx]: + setting_lfo = lfos[idx] + mode.become(mode.SET_SPEED) + if butts.pressed[0xF]: + mode.become_normal() + + elif mode.current == mode.SET_SPEED: + lights[0xF] = (255, 0, 0) + for idx in range(0xB): + if idx == setting_lfo.speed: + lights[idx] = (40, 240, 40) + else: + lights[idx] = (40, 140, 250) + if butts.pressed[idx]: + setting_lfo.speed = idx + mode.become_normal() + + if butts.pressed[0xF]: + mode.become_normal() + + elif mode.current == mode.SET_LIMIT_CHOOSE_LFO: + lights[0xF] = (255, 0, 0) + for idx in range(Lfo.length): + lights[idx] = (250, 250, 255) + if butts.pressed[idx]: + setting_lfo = lfos[idx] + mode.become(mode.SET_LIMIT_START) + if butts.pressed[0xF]: + mode.become_normal() + + elif mode.current == mode.SET_LIMIT_START: + lights[0xF] = (255, 0, 0) + for idx in range(0xB): + if idx == setting_lfo.start: + lights[idx] = (40, 240, 40) + else: + lights[idx] = (255, 200, 30) + if butts.pressed[idx]: + setting_lfo.start = idx + mode.become(mode.SET_LIMIT_END) + if butts.pressed[0xF]: + mode.become_normal() + + elif mode.current == mode.SET_LIMIT_END: + lights[0xF] = (255, 0, 0) + for idx in range(setting_lfo.start, 0xB): + if idx == setting_lfo.end: + lights[idx] = (40, 240, 40) + else: + lights[idx] = (40, 230, 180) + if butts.pressed[idx]: + setting_lfo.end = idx + mode.become_normal() + if butts.pressed[0xF]: + mode.become_normal() + + elif mode.normal: + if butts.pressed[0xA]: + mode.current = mode.SET_LIMIT_CHOOSE_LFO + if butts.pressed[0xF]: + mode.current = mode.TEACHING + if butts.pressed[0xB]: + mode.current = mode.SET_SPEED_CHOOSE_LFO - # oled.show() lights.show() diff --git a/font5x8.bin b/font5x8.bin deleted file mode 100644 index 9a0563b..0000000 Binary files a/font5x8.bin and /dev/null differ diff --git a/lib/adafruit_bus_device/i2c_device.mpy b/lib/adafruit_bus_device/i2c_device.mpy index b8fff50..e5b81a2 100644 Binary files a/lib/adafruit_bus_device/i2c_device.mpy and b/lib/adafruit_bus_device/i2c_device.mpy differ diff --git a/lib/adafruit_bus_device/spi_device.mpy b/lib/adafruit_bus_device/spi_device.mpy index e7e77ee..3fbcb59 100644 Binary files a/lib/adafruit_bus_device/spi_device.mpy and b/lib/adafruit_bus_device/spi_device.mpy differ diff --git a/lib/adafruit_dotstar.mpy b/lib/adafruit_dotstar.mpy index 696e977..7ded493 100644 Binary files a/lib/adafruit_dotstar.mpy and b/lib/adafruit_dotstar.mpy differ diff --git a/lib/adafruit_framebuf.mpy b/lib/adafruit_framebuf.mpy deleted file mode 100644 index c21eff8..0000000 Binary files a/lib/adafruit_framebuf.mpy and /dev/null differ diff --git a/lib/adafruit_midi/__init__.mpy b/lib/adafruit_midi/__init__.mpy index 141f9b2..5714439 100644 Binary files a/lib/adafruit_midi/__init__.mpy and b/lib/adafruit_midi/__init__.mpy differ diff --git a/lib/adafruit_midi/channel_pressure.mpy b/lib/adafruit_midi/channel_pressure.mpy index 65bf297..622833d 100644 Binary files a/lib/adafruit_midi/channel_pressure.mpy and b/lib/adafruit_midi/channel_pressure.mpy differ diff --git a/lib/adafruit_midi/control_change.mpy b/lib/adafruit_midi/control_change.mpy index bbd7d1e..97b01fb 100644 Binary files a/lib/adafruit_midi/control_change.mpy and b/lib/adafruit_midi/control_change.mpy differ diff --git a/lib/adafruit_midi/control_change_values.mpy b/lib/adafruit_midi/control_change_values.mpy index a65081c..5d45b61 100644 Binary files a/lib/adafruit_midi/control_change_values.mpy and b/lib/adafruit_midi/control_change_values.mpy differ diff --git a/lib/adafruit_midi/midi_continue.mpy b/lib/adafruit_midi/midi_continue.mpy index f283ae3..4d8ffc2 100644 Binary files a/lib/adafruit_midi/midi_continue.mpy and b/lib/adafruit_midi/midi_continue.mpy differ diff --git a/lib/adafruit_midi/midi_message.mpy b/lib/adafruit_midi/midi_message.mpy index 2317df8..fc4c805 100644 Binary files a/lib/adafruit_midi/midi_message.mpy and b/lib/adafruit_midi/midi_message.mpy differ diff --git a/lib/adafruit_midi/mtc_quarter_frame.mpy b/lib/adafruit_midi/mtc_quarter_frame.mpy index 17504cd..021639b 100644 Binary files a/lib/adafruit_midi/mtc_quarter_frame.mpy and b/lib/adafruit_midi/mtc_quarter_frame.mpy differ diff --git a/lib/adafruit_midi/note_off.mpy b/lib/adafruit_midi/note_off.mpy index 1d8669c..4d5176b 100644 Binary files a/lib/adafruit_midi/note_off.mpy and b/lib/adafruit_midi/note_off.mpy differ diff --git a/lib/adafruit_midi/note_on.mpy b/lib/adafruit_midi/note_on.mpy index 7d5a5ee..2a26f71 100644 Binary files a/lib/adafruit_midi/note_on.mpy and b/lib/adafruit_midi/note_on.mpy differ diff --git a/lib/adafruit_midi/pitch_bend.mpy b/lib/adafruit_midi/pitch_bend.mpy index 4d651da..f6c8762 100644 Binary files a/lib/adafruit_midi/pitch_bend.mpy and b/lib/adafruit_midi/pitch_bend.mpy differ diff --git a/lib/adafruit_midi/polyphonic_key_pressure.mpy b/lib/adafruit_midi/polyphonic_key_pressure.mpy index ca4b6fb..edd7e84 100644 Binary files a/lib/adafruit_midi/polyphonic_key_pressure.mpy and b/lib/adafruit_midi/polyphonic_key_pressure.mpy differ diff --git a/lib/adafruit_midi/program_change.mpy b/lib/adafruit_midi/program_change.mpy index b4a0c99..40b3585 100644 Binary files a/lib/adafruit_midi/program_change.mpy and b/lib/adafruit_midi/program_change.mpy differ diff --git a/lib/adafruit_midi/start.mpy b/lib/adafruit_midi/start.mpy index 289b357..46cc88f 100644 Binary files a/lib/adafruit_midi/start.mpy and b/lib/adafruit_midi/start.mpy differ diff --git a/lib/adafruit_midi/stop.mpy b/lib/adafruit_midi/stop.mpy index 1c6e458..102bb25 100644 Binary files a/lib/adafruit_midi/stop.mpy and b/lib/adafruit_midi/stop.mpy differ diff --git a/lib/adafruit_midi/system_exclusive.mpy b/lib/adafruit_midi/system_exclusive.mpy index 049950d..2fa2444 100644 Binary files a/lib/adafruit_midi/system_exclusive.mpy and b/lib/adafruit_midi/system_exclusive.mpy differ diff --git a/lib/adafruit_midi/timing_clock.mpy b/lib/adafruit_midi/timing_clock.mpy index 3b31223..01f94a8 100644 Binary files a/lib/adafruit_midi/timing_clock.mpy and b/lib/adafruit_midi/timing_clock.mpy differ diff --git a/lib/adafruit_ssd1306.mpy b/lib/adafruit_ssd1306.mpy deleted file mode 100644 index 46e53b3..0000000 Binary files a/lib/adafruit_ssd1306.mpy and /dev/null differ diff --git a/lib/font5x8.bin b/lib/font5x8.bin deleted file mode 100644 index 9a0563b..0000000 Binary files a/lib/font5x8.bin and /dev/null differ diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 0000000..5a78d48 --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,7 @@ +{ + "stubPath": "./stubs", + "include": [ + "./code.py", + "./lib/*" + ] +}