|
| 1 | +# SPDX-FileCopyrightText: 2026 Liz Clark for Adafruit Industries |
| 2 | +# SPDX-License-Identifier: MIT |
| 3 | + |
| 4 | +"""Bass Synth MIDI Stomp Box""" |
| 5 | + |
| 6 | +import board |
| 7 | +import busio |
| 8 | +import adafruit_midi |
| 9 | +import keypad |
| 10 | +from digitalio import DigitalInOut, Direction |
| 11 | +# pylint: disable=unused-import |
| 12 | +from adafruit_midi.control_change import ControlChange |
| 13 | +from adafruit_midi.pitch_bend import PitchBend |
| 14 | +from adafruit_midi.note_off import NoteOff |
| 15 | +from adafruit_midi.note_on import NoteOn |
| 16 | +from adafruit_midi.program_change import ProgramChange |
| 17 | +from adafruit_ticks import ticks_ms, ticks_add, ticks_diff |
| 18 | + |
| 19 | +# status LED |
| 20 | +led = DigitalInOut(board.GP18) |
| 21 | +led.direction = Direction.OUTPUT |
| 22 | +led.value = True |
| 23 | + |
| 24 | +# UART MIDI |
| 25 | +uart = busio.UART(board.GP0, board.GP1, baudrate=31250) |
| 26 | +# midi channel setup |
| 27 | +midi_out_channel = 1 |
| 28 | +# midi setup - UART out on GP0 |
| 29 | +midi = adafruit_midi.MIDI( |
| 30 | + midi_out=uart, |
| 31 | + out_channel=(midi_out_channel - 1), |
| 32 | +) |
| 33 | + |
| 34 | +# foot switches as keypad object |
| 35 | +KEY_PINS = ( |
| 36 | + board.GP2, |
| 37 | + board.GP3, |
| 38 | + board.GP4, |
| 39 | + board.GP5, |
| 40 | + board.GP6, |
| 41 | + board.GP7, |
| 42 | + board.GP8, |
| 43 | + board.GP9, |
| 44 | + board.GP10, |
| 45 | + board.GP11, |
| 46 | + board.GP12, |
| 47 | + board.GP13, |
| 48 | + board.GP14, |
| 49 | + board.GP15, |
| 50 | +) |
| 51 | +notes = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59] |
| 52 | +keys = keypad.Keys(KEY_PINS, value_when_pressed=False) |
| 53 | + |
| 54 | +# variables & states |
| 55 | +MIN_NOTE = 24 # lowest starting octave |
| 56 | +MAX_NOTE = 84 # highest starting octave |
| 57 | +channel_num = midi.out_channel # track channel |
| 58 | +pressed_channel = False # did we try to change the MIDI channel |
| 59 | +blink_count = 0 # number of times the LED has blinked |
| 60 | +clock = ticks_ms() # time keeping |
| 61 | +blink_timer = 500 # blink interval (0.5 seconds) |
| 62 | + |
| 63 | +while True: |
| 64 | + event = keys.events.get() |
| 65 | + if event: |
| 66 | + if event.pressed: |
| 67 | + if event.key_number == 12: |
| 68 | + # change MIDI channel |
| 69 | + channel_num = (channel_num + 1) % 16 |
| 70 | + midi.out_channel = channel_num |
| 71 | + print(channel_num + 1) |
| 72 | + pressed_channel = True |
| 73 | + led.value = False |
| 74 | + blink_count = 0 |
| 75 | + clock = ticks_ms() |
| 76 | + elif event.key_number == 13: |
| 77 | + # checks if transposing the first note (C) by 1 octave would exceed the MAX_NOTE |
| 78 | + # if it does, wraps the array down to start at MIN_NOTE |
| 79 | + # otherwise, transposes all notes up by one octave |
| 80 | + notes = [MIN_NOTE + (n - notes[0]) if notes[0] + 12 > MAX_NOTE |
| 81 | + else n + 12 for n in notes] |
| 82 | + else: |
| 83 | + # otherwise send noteOn message |
| 84 | + midi.send(NoteOn(notes[event.key_number], 120)) |
| 85 | + if event.released: |
| 86 | + if event.key_number < 12: |
| 87 | + midi.send(NoteOff(notes[event.key_number], 120)) |
| 88 | + if pressed_channel: |
| 89 | + # blink LED to show what MIDI channel we're on |
| 90 | + if ticks_diff(ticks_ms(), clock) > blink_timer: |
| 91 | + if not led.value: |
| 92 | + led.value = True |
| 93 | + blink_count += 1 |
| 94 | + else: |
| 95 | + led.value = False |
| 96 | + clock = ticks_add(clock, blink_timer) |
| 97 | + # reset after blinking |
| 98 | + if blink_count == (channel_num + 1): |
| 99 | + pressed_channel = False |
| 100 | + blink_count = 0 |
| 101 | + led.value = True |
0 commit comments