first commit Hexpad code

This commit is contained in:
John Park 2023-03-17 21:55:02 -07:00
parent 30dd7f806c
commit 9bca118f59

236
Hexpad/code.py Normal file
View file

@ -0,0 +1,236 @@
# SPDX-FileCopyrightText: 2023 John Park for Adafruit
#
# SPDX-License-Identifier: MIT
# Hexboard seven key modal note/chord pad for MIDI instruments
# Runs on QT Py RP2040
# (other QT Pys should work, but the BOOT button is handy for initiating configuration)
import time
import board
from digitalio import DigitalInOut, Pull
import keypad
import neopixel
import rainbowio
import usb_midi
import adafruit_midi
from adafruit_midi.note_on import NoteOn
from adafruit_midi.note_off import NoteOff
button = DigitalInOut(board.BUTTON)
button.pull = Pull.UP
num_switches = 7
leds = neopixel.NeoPixel(board.A0, num_switches, brightness=0.7)
leds.fill(rainbowio.colorwheel(5))
leds.show()
# root_picked = False
note = 0
root = 0 # defaults to a C
# lists of modal intervals (relative to root). Customize these if you want other scales/keys
major = (0, 2, 4, 5, 7, 9, 11)
minor = (0, 2, 3, 5, 7, 8, 10)
dorian = (0, 2, 3, 5, 7, 9, 10)
phrygian = (0, 1, 3, 5, 7, 8, 10)
lydian = (0, 2, 4, 6, 7, 9, 11)
mixolydian = (0, 2, 4, 5, 7, 9, 10)
locrian = (0, 1, 3, 5, 6, 8, 10)
modes = []
modes.append(major)
modes.append(minor)
modes.append(dorian)
modes.append(phrygian)
modes.append(lydian)
modes.append(mixolydian)
modes.append(locrian)
octv = 4
mode = 0 # default to major scale
play_chords = True # default to play chords
pre_notes = modes[mode] # initial mapping
keymap = (4, 3, 5, 0, 2, 6, 1) # physical to logical key mapping
# Key chart | logical |Interval chart example
# 6 1 | 6 7 | 9 11
# 5 0 2 | 3 4 5 | 4 5 7
# 4 3 | 0 1 | 0 2
# MIDI Setup
midi_usb_channel = 1 # change this to your desired MIDI out channel, 1-16
midi_usb = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=midi_usb_channel-1)
# Keyswitch setup
keyswitch_pins = (board.A3, board.A2, board.SDA, board.SCL, board.TX, board.RX, board.A1)
keyswitches = keypad.Keys(keyswitch_pins, value_when_pressed=False, pull=True)
def pick_mode():
print("Choose mode...")
mode_picked = False
# pylint: disable=global-statement
global mode
while not mode_picked:
# pylint: disable=redefined-outer-name
keyswitch = keyswitches.events.get() # check for key events
if keyswitch:
if keyswitch.pressed:
mode = keymap.index(keyswitch.key_number) # bottom left key is 0/major
print("Mode is:", mode)
if keyswitch.released:
mode_picked = True
leds.fill(rainbowio.colorwheel(8))
leds.show()
pick_octave()
def pick_octave():
print("Choose octave...")
octave_picked = False
# pylint: disable=global-statement
global octv
while not octave_picked:
if button.value is False: # pressed
launch_config()
time.sleep(0.1)
# pylint: disable=redefined-outer-name
keyswitch = keyswitches.events.get() # check for key events
if keyswitch:
if keyswitch.pressed:
octv = keymap.index(keyswitch.key_number) # get remapped position, lower left is 0
print("Octave is:", octv)
if keyswitch.released:
octave_picked = True
leds.fill(rainbowio.colorwheel(16))
pick_root()
def pick_root():# user selects key in which to play
print("Choose root note...")
root_picked = False
# pylint: disable=global-statement
global root
while not root_picked:
if button.value is False: # pressed
launch_config()
time.sleep(0.1)
# pylint: disable=redefined-outer-name
keyswitch = keyswitches.events.get() # check for key events
if keyswitch:
if keyswitch.pressed:
root = keymap.index(keyswitch.key_number) # get remapped position, lower left is 0
print("ksw:", keyswitch.key_number, "keymap index:", root)
note = pre_notes[root]
print("note:", note)
midi_usb.send(NoteOn(note + (12*octv), 120))
root_notes.clear()
# pylint: disable=redefined-outer-name
for mode_interval in range(num_switches):
root_notes.append(modes[mode][mode_interval] + note)
print("root note intervals:", root_notes)
if keyswitch.released:
note = pre_notes[root]
midi_usb.send(NoteOff(note + (12*octv), 0))
root_picked = True
leds.fill(0x0)
leds[3] = rainbowio.colorwheel(12)
leds[4] = rainbowio.colorwheel(5)
leds.show()
pick_chords()
def pick_chords():
print("Choose chords vs. single notes...")
chords_picked = False
# pylint: disable=global-statement
global play_chords
while not chords_picked:
if button.value is False: # pressed
launch_config()
time.sleep(0.1)
# pylint: disable=redefined-outer-name
keyswitch = keyswitches.events.get() # check for key events
if keyswitch:
if keyswitch.pressed:
if keyswitch.key_number == 4:
play_chords = True
print("Chords are on")
chords_picked = True
playback_led_colors()
if keyswitch.key_number == 3:
play_chords = False
print("Chords are off")
chords_picked = True
playback_led_colors()
# create the interval list based on root key and mode that's been picked in variable
root_notes = []
for mode_interval in range(num_switches):
root_notes.append(modes[mode][mode_interval] + note)
print("---Hexpad---")
print("\nRoot note intervals:", root_notes)
key_colors = (18, 10, 18, 26, 26, 18, 10)
def playback_led_colors():
for i in range(num_switches):
leds[i]=(rainbowio.colorwheel(key_colors[i]))
leds.show()
time.sleep(0.1)
playback_led_colors()
# MIDI Note Message Functions
def send_note_on(note_num):
if play_chords is True:
note_num = root_notes[note_num] + (12*octv)
midi_usb.send(NoteOn(note_num, 120))
midi_usb.send(NoteOn(note_num + modes[mode][2], 80))
midi_usb.send(NoteOn(note_num + modes[mode][4], 60))
midi_usb.send(NoteOn(note_num+12, 80))
else:
note_num = root_notes[note_num] + (12*octv)
midi_usb.send(NoteOn(note_num, 120))
def send_note_off(note_num):
if play_chords is True:
note_num = root_notes[note_num] + (12*octv)
midi_usb.send(NoteOff(note_num, 0))
midi_usb.send(NoteOff(note_num + modes[mode][2], 0))
midi_usb.send(NoteOff(note_num + modes[mode][4], 0))
midi_usb.send(NoteOff(note_num+12, 0))
else:
note_num = root_notes[note_num] + (12*octv)
midi_usb.send(NoteOff(note, 0))
def send_midi_panic():
for x in range(128):
midi_usb.send(NoteOff(x, 0))
def launch_config():
print("-launching config-")
send_midi_panic()
leds.fill(rainbowio.colorwheel(5))
leds.show()
pick_mode()
send_midi_panic() # turn off any stuck notes at startup
while True:
keyswitch = keyswitches.events.get() # check for key events
if keyswitch:
keyswitch_number=keyswitch.key_number
if keyswitch.pressed:
note_picked = keymap.index(keyswitch.key_number)
send_note_on(note_picked)
leds[keyswitch_number]=(rainbowio.colorwheel(10))
leds.show()
if keyswitch.released:
note_picked = keymap.index(keyswitch.key_number)
send_note_off(note_picked)
leds[keyswitch_number]=(rainbowio.colorwheel(key_colors[keyswitch_number]))
leds.show()
if button.value is False: # pressed
launch_config()
time.sleep(0.1)