Skip to content

Instantly share code, notes, and snippets.

@jedgarpark
Created May 29, 2024 20:56
Show Gist options
  • Save jedgarpark/bb5c707bd9cd202ca158cbd21596393b to your computer and use it in GitHub Desktop.
Save jedgarpark/bb5c707bd9cd202ca158cbd21596393b to your computer and use it in GitHub Desktop.
# SPDX-FileCopyrightText: Copyright (c) 2023 Scott Shawcroft for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2024 Tod Kurt
# SPDX-FileCopyrightText: Copyright (c) 2024 John Park
# SPDX-License-Identifier: Unlicense
#
# Forward MIDI from device on USB Host port to computer via USB MIDI
# and 5-pin serial MIDI via MIDI Feather Wing or similar MIDI output.
# Suppots hot-plug of the device on the USB Host port.
#
# adapted from https://github.com/adafruit/Adafruit_CircuitPython_USB_Host_MIDI/blob/main/examples/usb_host_midi_simpletest.py
# needs "circup install adafruit_midi adafruit_usb_host_descriptors"
# and hand-copy library from https://github.com/todbot/Adafruit_CircuitPython_USB_Host_MIDI
# USB MIDI on ESP32-S2 needs boot.py enablement
# https://github.com/todbot/circuitpython-tricks?tab=readme-ov-file#enable-usb-midi-in-bootpy-for-esp32-s2-and-stm32f4
# to do:
# -use tft buttons to remap cc numbers
# -buttons to transpose key?
# -buttons to change MIDI output channel
# DONE: button D0 for MIDI panic message
# -
import time
# import asyncio
import board
import busio
import usb
import usb_midi
import max3421e
import adafruit_usb_host_midi
# import terminalio
import winterbloom_smolmidi as smolmidi
# from adafruit_display_text import label
# import displayio
# import vectorio
import digitalio
import keypad
from cedargrove_midi_tools import note_to_name
# display = board.DISPLAY
# BORDER = 20
# FONTSCALE = 2
# LINE_HEIGHT = 24
# R1 = 200# 0#255
# G1 = 0 # 170 #191
# B1 = 160 # 0
# FIRST_COLOR = ((R1, G1, B1))
# FIRST_COLOR_DIM = ((R1//3, G1//3, B1//3)) # 0x000900
#
# R2 = 221# 0#255
# G2 = 221 # 170 #191
# B2 = 0 # 0
# SECOND_COLOR = ((R2, G2, B2))
# SECOND_COLOR_DIM =((R2//2, G2//2, B2//2))
# TEXT_COLOR0 = SECOND_COLOR_DIM
# TEXT_COLOR = FIRST_COLOR # 0x00FF00
#
# screen = displayio.Group()
# display.root_group = screen
#
#
# # shapes
# palette1 = displayio.Palette(1)
# palette1[0] = FIRST_COLOR_DIM
# palette2 = displayio.Palette(1)
# palette2[0] = SECOND_COLOR
#
# line1 = vectorio.Rectangle(pixel_shader=palette1, width=display.width, height=2, x=0, y=LINE_HEIGHT+12)
# line2 = vectorio.Rectangle(pixel_shader=palette1, width=display.width, height=2, x=0, y=(LINE_HEIGHT*2)+12)
# line3 = vectorio.Rectangle(pixel_shader=palette1, width=display.width, height=2, x=0, y=(LINE_HEIGHT*3)+12)
# line4 = vectorio.Rectangle(pixel_shader=palette1, width=display.width, height=2, x=0, y=(LINE_HEIGHT*4)+12)
# line5 = vectorio.Rectangle(pixel_shader=palette1, width=display.width, height=2, x=0, y=(LINE_HEIGHT*5)+12)
# screen.append(line1)
# screen.append(line2)
# screen.append(line3)
# screen.append(line4)
# screen.append(line5)
#
# vert_line1 = vectorio.Rectangle(pixel_shader=palette1, width=2, height=display.height-12, x=0, y=LINE_HEIGHT+12)
# screen.append(vert_line1)
# vert_line2 = vectorio.Rectangle(pixel_shader=palette1, width=2, height=display.height-12, x=16, y=LINE_HEIGHT+12)
# screen.append(vert_line2)
# vert_line3 = vectorio.Rectangle(pixel_shader=palette1, width=2, height=display.height-12, x=216, y=LINE_HEIGHT+12)
# screen.append(vert_line3)
# vert_line4 = vectorio.Rectangle(pixel_shader=palette1, width=2, height=display.height-12, x=238, y=LINE_HEIGHT+12)
# screen.append(vert_line4)
#
# #indicators
# circ1=vectorio.Circle(pixel_shader=palette2, radius=4, x=8, y=LINE_HEIGHT*2)
# screen.append(circ1)
# # device label
# text = "MIDI device"
# device_label = label.Label(terminalio.FONT, text=text, color=TEXT_COLOR0)
# device_width = device_label.bounding_box[2] * FONTSCALE
# device_group = displayio.Group(scale=FONTSCALE,x=2,y=12,)
# device_group.append(device_label) # Subgroup for text scaling
# screen.append(device_group)
#
# # channel label
# text = "ch. __ > __"
# channel_label = label.Label(terminalio.FONT, text=text, color=TEXT_COLOR)
# channel_width = channel_label.bounding_box[2] * FONTSCALE
# channel_group = displayio.Group(scale=FONTSCALE,x=20,y=LINE_HEIGHT*2,)
# channel_group.append(channel_label) # Subgroup for text scaling
# screen.append(channel_group)
#
#
# # note label
# text = "-- --"
# note_label = label.Label(terminalio.FONT, text=text, color=TEXT_COLOR)
# note_width = note_label.bounding_box[2] * FONTSCALE
# note_group = displayio.Group(scale=FONTSCALE,x=20,y=LINE_HEIGHT*3,)
# note_group.append(note_label) # Subgroup for text scaling
# screen.append(note_group)
#
# # channel pressure label
# text = " ---"
# pressure_label = label.Label(terminalio.FONT, text=text, color=TEXT_COLOR)
# pressure_width = pressure_label.bounding_box[2] * FONTSCALE
# pressure_group = displayio.Group(scale=FONTSCALE,x=108,y=LINE_HEIGHT*3,)
# pressure_group.append(pressure_label) # Subgroup for text scaling
# screen.append(pressure_group)
#
# # cc label
# text = "cc: -- > -- --"
# cc_label = label.Label(terminalio.FONT, text=text, color=TEXT_COLOR)
# cc_width = cc_label.bounding_box[2] * FONTSCALE
# cc_group = displayio.Group(scale=FONTSCALE,x=20,y=LINE_HEIGHT*4,)
# cc_group.append(cc_label) # Subgroup for text scaling
# screen.append(cc_group)
#
# # message label
# text = "pitch bend 64"
# pb_label = label.Label(terminalio.FONT, text=text, color=TEXT_COLOR)
# pb_width = cc_label.bounding_box[2] * FONTSCALE
# pb_group = displayio.Group(scale=FONTSCALE,x=20,y=LINE_HEIGHT*5,)
# pb_group.append(pb_label) # Subgroup for text scaling
# screen.append(pb_group)
spi = board.SPI()
cs = board.D10
irq = board.D9
host_chip = max3421e.Max3421E(spi, chip_select=cs, irq=irq)
# # You must wire up the needed resistors and jack yourself, or use the MIDI Feather wing
uart = busio.UART(rx=board.RX, tx=board.TX, baudrate=31250, timeout=0.001)
midi_usb_out = usb_midi.ports[1]
remapped_ch = 0 # non-human version, e.g. 0-15
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT
led_state = False
button0 = keypad.Keys((board.D0,), value_when_pressed=False, pull=True)
# BUTTON_PINS = (board.D1, board.D2)
# buttons = keypad.Keys(BUTTON_PINS, value_when_pressed=True, pull=True)
def send_midi_panic():
msg = smolmidi.Message()
msg.type = smolmidi.NOTE_OFF
msg.channel = remapped_ch
msg.data = bytearray(2)
for notenum in range(128):
msg.data[0] = notenum
msg.data[1] = 0
msg_bytes = bytes(msg)
midi_usb_out.write(msg_bytes)
uart.write(msg_bytes)
time.sleep(0.001)
def look_for_midi_usb_device():
"""Look for MIDI device on USB Host port, returns None if no device"""
midi_usb_device = None
for device in usb.core.find(find_all=True):
try:
midi_usb_device = adafruit_usb_host_midi.MIDI(device, timeout=0.001)
print("Found vid/pid %04x/%04x" % (device.idVendor, device.idProduct),
device.manufacturer, device.product)
# device_label.text=(str(device.product))
time.sleep(2)
except ValueError:
print("bad device:", device)
# device_label.text=("bad device:"+(str(device.product)))
return midi_usb_device
while True:
midi_usb_device = look_for_midi_usb_device()
if midi_usb_device:
print("Found MIDI device: ", midi_usb_device)
midi_device = smolmidi.MidiIn(midi_usb_device)
try:
while True:
event0 = button0.events.get()
if event0:
button_number = event0.key_number
print("button", button_number)
if event0.pressed:
if button_number == 0:
send_midi_panic()
print("midi panic")
# event = buttons.events.get()
# if event:
# button_number = event.key_number
# print("button", button_number)
# if event.pressed:
# if button_number == 0:
# remapped_ch = (remapped_ch + 1) % 15
# print("out channel:", str(remapped_ch+1)) # make it human
# circ1.y=LINE_HEIGHT*2
msg = midi_device.receive()
if msg:
led.value=True
led_state=True
if msg.channel is not None:
latest_channel = msg.channel
# channel_label.text=("ch. " + str(msg.channel+1) + " > " + str(remapped_ch+1))
msg_type_hex = (hex(msg.type))
if msg.type == smolmidi.NOTE_ON:
print("ch", msg.channel+1, ">", str(remapped_ch+1), msg_type_hex, "NOTE_ON", msg.data[0], msg.data[1])
# note_str=( note_to_name(msg.data[0])+ " " + str(msg.data[1]) )
# note_label.text=(note_str)
# circ1.y=LINE_HEIGHT*3
# msg.data[0] = msg.data[0]+12 # bump up an ocatave
elif msg.type == smolmidi.NOTE_OFF:
print(msg_type_hex, "NOTE_OFF", msg.data[0], msg.data[1])
# note_str=( note_to_name(msg.data[0])+ " __")
# note_label.text=(note_str)
# circ1.y=LINE_HEIGHT*3
# msg.data[0] = msg.data[0]+12 # bump up an ocatave
elif msg.type == smolmidi.CC:
in_cc_num = msg.data[0]
if msg.data[0]==1:
msg.data[0]=23
print(msg_type_hex, "CC", in_cc_num, " > ", msg.data[0], msg.data[1])
# cc_str=( "cc: " + str(in_cc_num) + " > " + str(msg.data[0])+ " " + str(msg.data[1]) )
# cc_label.text=(cc_str)
# circ1.y=LINE_HEIGHT*4
elif msg.type == smolmidi.PITCH_BEND:
print(msg_type_hex, "PITCH_BEND", msg.data[1])
# pb_str=( "pitch bend " + str(msg.data[1]) )
# pb_label.text=(pb_str)
# circ1.y=LINE_HEIGHT*5
elif msg.type == smolmidi.CHANNEL_PRESSURE:
print(msg_type_hex, "CHANNEL_PRESSURE", msg.data[0])
# pressure_str=str(msg.data[0])
# pressure_label.text=(" " + pressure_str)
# circ1.y=LINE_HEIGHT*3
msg.channel = remapped_ch
msg_bytes=bytes(msg) # wrap the message back up for sending
print("msg:", ["%2x" % v for v in msg_bytes])
midi_usb_out.write(msg_bytes)
uart.write(msg_bytes)
if led_state:
led.value=False
led_state=False
except usb.core.USBError:
print("device unplug? looking again for device")
# device_label.text="looking for device"
else:
time.sleep(0.5)
print("looking for device")
# device_label.text="looking for device"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment