Skip to content

Instantly share code, notes, and snippets.

@todbot
Last active May 2, 2024 01:35
Show Gist options
  • Save todbot/67c0fdbc65c98f48d71d2f2722d8127f to your computer and use it in GitHub Desktop.
Save todbot/67c0fdbc65c98f48d71d2f2722d8127f 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-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/adafruit/Adafruit_CircuitPython_USB_Host_MIDI
import time
import board
import busio
import usb
import usb_midi
import adafruit_usb_host_midi
import adafruit_midi
# note, must have all these or adafruit_midi crashes when forwarding messages
# pylint: disable=unused-import
from adafruit_midi.channel_pressure import ChannelPressure
from adafruit_midi.control_change import ControlChange
from adafruit_midi.midi_message import MIDIUnknownEvent
from adafruit_midi.note_off import NoteOff
from adafruit_midi.note_on import NoteOn
from adafruit_midi.pitch_bend import PitchBend
from adafruit_midi.polyphonic_key_pressure import PolyphonicKeyPressure
from adafruit_midi.program_change import ProgramChange
from adafruit_midi.start import Start
from adafruit_midi.stop import Stop
from adafruit_midi.system_exclusive import SystemExclusive
from adafruit_midi.timing_clock import TimingClock
# This setup is to use TX pin on Feather RP2040 with USB Type A Host as MIDI out
# 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_uart = adafruit_midi.MIDI(midi_out=uart, midi_in=uart)
midi_usb = adafruit_midi.MIDI(midi_out=usb_midi.ports[1])
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)
print("Found vid/pid %04x/%04x" % (device.idVendor, device.idProduct),
device.manufacturer, device.product)
except ValueError:
print("bad device:", device)
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 = adafruit_midi.MIDI(midi_in=midi_usb_device)
try:
while True:
msg = midi_device.receive() # receive MIDI from device plugged into host port
if msg:
print("midi msg:",msg)
midi_uart.send(msg) # forward to MIDI devices on 5-pin jack
midi_usb.send(msg) # forward to computer via USB MIDI
except usb.core.USBError:
print("device unplug? looking again for device")
else:
time.sleep(0.2)
print("looking for device")
@todbot
Copy link
Author

todbot commented May 1, 2024

The above version has a problem where it seems to "miss" incoming MIDI messages and then gets off-by-one, sending Note Offs for Note Ons and vice-versa.

Here's a version using SmolMIDI. Requires my fork of adafruit_usb_host_midi. It exhibits the problem less.

# SPDX-FileCopyrightText: Copyright (c) 2023 Scott Shawcroft for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2024 Tod Kurt
#
# 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


import time
import board
import busio
import usb
import usb_midi
import adafruit_usb_host_midi

import winterbloom_smolmidi as smolmidi

# This setup is to use TX pin on Feather RP2040 with USB Type A Host as MIDI out
# 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]

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)
            print("Found vid/pid %04x/%04x" % (device.idVendor, device.idProduct),
                  device.manufacturer, device.product)
        except ValueError:
            print("bad device:", device)
    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:
                msg = midi_device.receive()
                if msg:
                    msg_bytes = bytes(msg)
                    print("msg:", ["%2x" % v for v in msg_bytes])
                    midi_usb_out.write(msg_bytes)
                    uart.write(msg_bytes)
        
        except usb.core.USBError:
            print("device unplug? looking again for device")
    else:
        time.sleep(0.5)
        print("looking for device")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment