import time import board import displayio import terminalio import adafruit_aw9523 import busio import adafruit_ssd1327 import digitalio from adafruit_display_text import label from adafruit_display_shapes.circle import Circle from adafruit_display_shapes.rect import Rect import usb_midi import adafruit_midi from adafruit_midi.note_on import NoteOn from adafruit_midi.note_off import NoteOff from presets import * from pins import * # Turn on LED on Pico led = digitalio.DigitalInOut(board.LED) led.direction = digitalio.Direction.OUTPUT led.value = True # Conversion between midi notes and american notation NOTES = ["C-1", "C#-1", "D-1", "D#-1", "E-1", "F-1", "F#-1", "G-1", "G#-1", "A-1", "A#-1", "B-1", "C0", "C#0", "D0", "D#0", "E0", "F0", "F#0", "G0", "G#0", "A0", "A#0", "B0", "C1", "C#1", "D1", "D#1", "E1", "F1", "F#1", "G1", "G#1", "A1", "A#1", "B1", "C2", "C#2", "D2", "D#2", "E2", "F2", "F#2", "G2", "G#2", "A2", "A#2", "B2", "C3", "C#3", "D3", "D#3", "E3", "F3", "F#3", "G3", "G#3", "A3", "A#3", "B3", "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4", "C5", "C#5", "D5", "D#5", "E5", "F5", "F#5", "G5", "G#5", "A5", "A#5", "B5", "C6", "C#6", "D6", "D#6", "E6", "F6", "F#6", "G6", "G#6", "A6", "A#6", "B6", "C7", "C#7", "D7", "D#7", "E7", "F7", "F#7", "G7", "G#7", "A7", "A#7", "B7", "C8", "C#8", "D8", "D#8", "E8", "F8", "F#8", "G8", "G#8", "A8", "A#8", "B8", "C9", "C#9", "D9", "D#9", "E9", "F9", "F#9", "G9", "G#9", "A9", "A#9", "B9" ] displayio.release_displays() # i2c setup, higher frequency for display refresh i2c = busio.I2C(board.GP1, board.GP0, frequency=1000000) # i2c display setup display_bus = displayio.I2CDisplay(i2c, device_address=0x3d) # i2c AW9523 GPIO expander setup aw = adafruit_aw9523.AW9523(i2c) # MIDI setup as MIDI out device #midi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=0) uart = busio.UART(board.GP16, board.GP17, baudrate=31250) midi_channel = 12 midi = adafruit_midi.MIDI( midi_in=uart, midi_out=uart, in_channel=1, out_channel=midi_channel, debug=True ) # display dimensions WIDTH = 128 HEIGHT = 128 # display setup display = adafruit_ssd1327.SSD1327(display_bus, width=WIDTH, height=HEIGHT, brightness=1) # main display group, shows default GUI menu splash = displayio.Group() # group for circle icons circle_group = displayio.Group() # group for text labels on circles text_group = displayio.Group() # list of circle positions spots = ( (16, 16), (48, 16), (80, 16), (112, 16), (16, 48), (48, 48), (80, 48), (112, 48), (16, 80), (48, 80), (80, 80), (112, 80), (16, 112), (48, 112), (80, 112), (112, 112), ) # creating the circles & pulling in positions from spots for spot in spots: circle = Circle(x0=spot[0], y0=spot[1], r=14, fill=0x888888) # adding circles to their display group circle_group.append(circle) # square to show position on menu rect = Rect(0, 0, 33, 33, fill=None, outline=0x00FF00, stroke=3) splash.append(circle_group) splash.append(rect) # strings and positions for the MIDI note text labels texts = [ {'num': NOTES[midi_notes[0]], 'pos': (10, 16)}, {'num': NOTES[midi_notes[1]], 'pos': (42, 16)}, {'num': NOTES[midi_notes[2]], 'pos': (74, 16)}, {'num': NOTES[midi_notes[3]], 'pos': (106, 16)}, {'num': NOTES[midi_notes[4]], 'pos': (10, 48)}, {'num': NOTES[midi_notes[5]], 'pos': (42, 48)}, {'num': NOTES[midi_notes[6]], 'pos': (74, 48)}, {'num': NOTES[midi_notes[7]], 'pos': (106, 48)}, {'num': NOTES[midi_notes[8]], 'pos': (10, 80)}, {'num': NOTES[midi_notes[9]], 'pos': (42, 80)}, {'num': NOTES[midi_notes[10]], 'pos': (74, 80)}, {'num': NOTES[midi_notes[11]], 'pos': (106, 80)}, {'num': NOTES[midi_notes[12]], 'pos': (10, 112)}, {'num': NOTES[midi_notes[13]], 'pos': (42, 112)}, {'num': NOTES[midi_notes[14]], 'pos': (74, 112)}, {'num':NOTES[midi_notes[15]], 'pos': (106, 112)}, ] text_labels = [] for text in texts: text_area = label.Label(terminalio.FONT, text=text['num'], color=0xFFFFFF) text_area.x = text['pos'][0] text_area.y = text['pos'][1] text_labels.append(text_area) text_group.append(text_area) splash.append(text_group) # secondary display group, shows large circle when button is selected big_splash = displayio.Group() # large circle to fill display big_circle = Circle(x0=64, y0=64, r=62, fill=0x888888) big_splash.append(big_circle) # large text to fill circle big_text = label.Label(terminalio.FONT, text=' ', color=0xFFFFFF) big_text.x = 38 big_text.y = 60 big_text.scale = 4 big_splash.append(big_text) selection_splash = displayio.Group() selection_text = label.Label(terminalio.FONT, text=' ', color=0xFFFFFF) selection_text.x = 0 selection_text.y = 60 selection_text.scale = 2 selection_splash.append(selection_text) # array for LEDs on AW9523 leds = [] led_pins = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] # setup to create the AW9523 outputs for LEDs for led in led_pins: led_pin = aw.get_pin(led) led_pin.direction = digitalio.Direction.OUTPUT leds.append(led_pin) note_buttons = [] for pin in note_pins: note_pin = digitalio.DigitalInOut(pin) note_pin.direction = digitalio.Direction.INPUT note_pin.pull = digitalio.Pull.UP note_buttons.append(note_pin) # note states note0_pressed = False note1_pressed = False note2_pressed = False note3_pressed = False note4_pressed = False note5_pressed = False note6_pressed = False note7_pressed = False note8_pressed = False note9_pressed = False note10_pressed = False note11_pressed = False note12_pressed = False note13_pressed = False note14_pressed = False note15_pressed = False # array of note states note_states = [note0_pressed, note1_pressed, note2_pressed, note3_pressed, note4_pressed, note5_pressed, note6_pressed, note7_pressed, note8_pressed, note9_pressed, note10_pressed, note11_pressed, note12_pressed, note13_pressed, note14_pressed, note15_pressed] # array for 5-way switch joystick = [select, up, down, left, right] for joy in joystick: joy.direction = digitalio.Direction.INPUT joy.pull = digitalio.Pull.UP # states for 5-way switch select_state = None up_state = None down_state = None left_state = None right_state = None midi_state = None # coordinates for navigating main GUI select_x = [0, 32, 64, 96] select_y = [0, 32, 64, 96] # y coordinate for 5-way switch navigation y_pos = 0 # x coordinate for 5-way switch navigation x_pos = 0 sub_state = False # default midi number midi_num = 60 # default MIDI button button_num = 0 # default MIDI button position button_pos = 0 # check for blinking LED led_check = None # time.monotonic() device clock = time.monotonic() clock_select_long = time.monotonic() select_long_counter = 0 clock_button_repeat = time.monotonic_ns() button_repeat = 0 note_selection = 0 # coordinates for tracking location of 5-way switch up_scroll = 0 down_scroll = 0 left_scroll = 0 right_scroll = 0 switch_coordinates = [(0, 0), (1, 0), (2, 0), (3, 0), (0, 1), (1, 1), (2, 1), (3, 1), (0, 2), (1, 2), (2, 2), (3, 2), (0, 3), (1, 3), (2, 3), (3, 3)] # show main display GUI display.show(splash) pressed = False while True: # debouncing for 5-way switch positions if up.value and up_state == "pressed": up_state = None pressed = False if down.value and down_state == "pressed": down_state = None down_state = None pressed = False if left.value and left_state == "pressed": left_state = None pressed = False if right.value and right_state == "pressed": right_state = None pressed = False if select.value and select_state == "pressed": select_state = None # MIDI input for i in range(16): buttons = note_buttons[i] if not buttons.value and note_states[i] is False: # send the MIDI note and light up the LED leds[i].value = True midi.send(NoteOn(midi_notes[i], velocity_standard)) #midi.write(bytes([0x90], notes[i], velocity_standard)) note_states[i] = True # if the button is released... if buttons.value and note_states[i] is True: # stop sending the MIDI note and turn off the LED midi.send(NoteOff(midi_notes[i], velocity_standard)) leds[i].value = False note_states[i] = False # if we're on the main GUI page if not sub_state: # if you press up on the 5-way switch... if not up.value and up_state is None: up_state = "pressed" # track the switch's position up_scroll -= 1 if up_scroll < 0: up_scroll = 3 y_pos = up_scroll down_scroll = up_scroll # if you press down on the 5-way switch... if not down.value and down_state is None: down_state = "pressed" # track the switch's position down_scroll += 1 if down_scroll > 3: down_scroll = 0 y_pos = down_scroll up_scroll = down_scroll # if you press left on the 5-way switch... if not left.value and left_state is None: # print("scroll", down_scroll) left_state = "pressed" # track the switch's position left_scroll -= 1 if left_scroll < 0: left_scroll = 3 x_pos = left_scroll right_scroll = left_scroll # if you press right on the 5-way switch... if not right.value and right_state is None: # print("scroll", down_scroll) right_state = "pressed" # track the switch's position right_scroll += 1 if right_scroll > 3: right_scroll = 0 x_pos = right_scroll left_scroll = right_scroll # update square's position on the GUI rect.y = select_y[y_pos] rect.x = select_x[x_pos] # update the currently highlighted button on the GUI for coords in switch_coordinates: if x_pos == coords[0] and y_pos == coords[1]: button_pos = switch_coordinates.index(coords) # print(button_pos) button_num = midi_notes[button_pos] # if you press select on the 5-way switch... if not select.value and select_state is None: select_state = "pressed" # grab the selected button's MIDI note midi_num = button_num # change into the secondary GUI menu sub_state = True # if an arcade button is selected to change the MIDI note... if sub_state: # display the secondary GUI menu display.show(big_splash) # display the selected button's MIDI note big_text.text = NOTES[midi_num] # blink the selected button's LED without pausing the loop if (time.monotonic() > (clock + 1)) and led_check is None: leds[button_pos].value = True led_check = True clock = time.monotonic() if (time.monotonic() > (clock + 1)) and led_check is True: leds[button_pos].value = False led_check = None clock = time.monotonic() if (time.monotonic() > (clock_select_long + 1)): if not select.value: #print(select_long_counter) select_long_counter += 1 else: if select_long_counter != 0: select_long_counter = 0 clock_select_long = time.monotonic() # Change note if select pressed for 3 seconds if select_long_counter == time_before_change_notes: selection_text.text, note_selection, midi_notes = changes_notes(note_selection) display.show(selection_splash) # Leds show! for led in leds: led.value = True time.sleep(0.03) for led in leds: led.value = False time.sleep(0.03) # Reset oled interface select_long_counter = 0 sub_state = False # Reset text on OLED for i in range(16): text_labels[i].text = NOTES[midi_notes[i]] # show main GUI display display.show(splash) # turn off blinking LED leds[button_pos].value = False # blocks the MIDI number from being set above 128 if midi_num >= 128: midi_num = 128 # blocks the MIDI number from being set below 0 if midi_num <= 0: midi_num = 0 # if you press right on the 5-way switch... if not right.value and right_state is None: # increase the MIDI number midi_num += 1 right_state = "pressed" # if you press up on the 5-way switch... if not up.value and up_state is None: # increase the MIDI number midi_num += 1 up_state = "pressed" # if you press left on the 5-way switch... if not left.value and left_state is None: # decrease the MIDI number midi_num -= 1 left_state = "pressed" # if you press down on the 5-way switch... if not down.value and down_state is None: # decrease the MIDI number midi_num -= 1 down_state = "pressed" # update arcade button's MIDI note # allows you to check note while you're adjusting it midi_notes[button_pos] = midi_num # if you press select on the 5-way switch... if not select.value and select_state is None: print(midi_notes) select_state = "pressed" # change back to main menu mode sub_state = False # update new MIDI number text label text_labels[button_pos].text = NOTES[midi_num] # show main GUI display display.show(splash) # turn off blinking LED leds[button_pos].value = False