Skip to content

Connect a Raspberry Pi Pico between a USB MIDI device and a USB Host to modify the USB MIDI traffic

License

Notifications You must be signed in to change notification settings

rppicomidi/pico-usb-midi-filter

Repository files navigation

pico-usb-midi-filter

Description

This project uses the Raspberry Pi Pico built-in USB port and an additional USB port created from the PIOs to make a MIDI data filter. You plug the PC into the Raspberry Pi Pico's MicroUSB port. You plug your MIDI keyboard or other MIDI device into the added USB Host port. The added USB Host port enumerates your MIDI device and then initializes the Pico's USB Device port so it looks to your PC just the same as the downstream USB device. Because the Pico now sits between your PC and your MIDI device, it can maniputlate the MIDI data stream to filter it as required.

This program demostrates translating some Mackie Control protocol button messages from the Arturia Keylab Essential 88 to other Mackie Control protocol button messages so that they work correctly with the Cubase DAW, and it demostrates Mackie Control fader pickup (because it seems Arturia Keylab Essential only supports pickup with HUI. See the FAQ). However, the code is structured so that it may be modified to perform just about any other filtering. That is, this code should serve as a reasonable starting point for other more sophisticated processing such as

  • Removing MIDI clock or Activie Sensing messages
  • Transposing notes
  • Re-scaling note velocity
  • Inserting controller messages or program change messages
  • Changing MIDI channel for notes in a range (to perform keyboard split, for example).
  • etc.

This project requires correctly soldering a USB host port connector to a Raspberry Pi Pico board. The circuit described here has no current limiting, ESD protection, or other safeguards. Measure voltages carefully and please don't hook this to your expensive computer and MIDI equipment until you have done some testing with at least some basic test equipment.

Also, any brand name stuff I mention in this README file is just documentation of what I did. This is not an advertisement. I don't get paid to do this.

This project would not have been possible without the code of the tinyusb project and the Pico-PIO-USB project. Thank you to the developers for publishing the source on github.

If you find issues with this document or with the code, please report them on this project's github page.

Hardware

This project uses the built-in USB port as the USB downstream MIDI device port (connects to a PC's USB host port or an upstream USB hub port). It also uses two GPIO pins, both PIOs and one of the ARM cores of the Pico's RP2040 chip to create an upstream USB MIDI host port (connects to your MIDI keyboard or other MIDI device). The Pico board and the MIDI device get power from the PC host port or upstream USB hub. Because this project uses the on-board USB for MIDI, it can't use it for stdio debug prints. This project uses UART0 on pins GP16 (Pico Board pin 21) and GP17 (Pico Board pin 22) for the stdio debug UART. If your project needs the debug UART pins for something else, please disable the stdio UART in the Cmake file.

Wiring the USB Host Port

I used a Sparkfun CAB-10177 with the green and white wires (D+ and D- signals) swapped on the 4-pin female header connector. I soldered a 4-pin male header to pins 1-3 of the Pico board and left one of the pins hanging off the edge of the board. I soldered a wire from that pin hanging off the edge of the board to Pin 40 of the Pico board (VBus). I then plugged the 4-pin female header connector so the black wire (ground) connects to Pin 3 of the Pico board, the red wire connects to pin hanging off the edge of the Pico board, the green wire connects to pin 1 of the Pico board, and the white wire connects to pin 2 of the Pico board. If you want to add series termination resistors to D+ and D-, resistors between 22 and 33 ohms are probably close. I didn't bother and it seemed good enough for my testing.

Here is a photo of the bottom view wiring.

Here is a photo of the top view wiring.

Wiring for a Pico Probe

I like to use a Pico Probe (see Appendix A of the Getting Started with Raspberry Pi Pico guide) for code development in a Microsoft Visual Studio Code IDE. I soldered in a 3-pin male header on the 3 DEBUG pins at the bottom edge of the Pico board. I also soldered a 2-pin male header to pins 21 and 22 (GPIO pins GP16 and GP17) for the debug UART. I wired the GND, SWCLK and SWDIO as shown in the Appendix. However, because I used Pico board pins 1 and 2 for the USB host, I had to wire the debug UART to different pins. Connect the Pico A UART 1 Rx on Pico A pin 7 to Pico B UART0 Tx signal on Pico B Pin 21 (not 1). Connect the Pico A UART Tx on Pico A pin 6 and the Pico B UART0 Rx signal to target Pico board pin 22 (not 2).

Here is a photo of my complete setup with the Pico Probe attached. The Pico Probe board is the lower Pico board.

This is not commercial quality hardware

The USB host port hardware created from the PIO is not compliant with the USB specification, but it seems to work OK in my experimental setup. The specification deviations that I know about are:

  • The D+ and D- pins use the on-chip pull-down resistors that can be between 50k and 80k instead of the 15k pull-down resistors the USB spec recommends.
  • The D+ and D- pins are wired directly to the I/O pins and don't use termination resistors to match the 50 ohm impedance the USB spec recommends. You can try to do better by adding in-line resistors as described above.
  • The USB port receiver is not differential. The software that decodes the USB signaling only uses the D+ signal to decode the NRZI signal.
  • I wired in no current limiting for the USB host port Vbus supply. It relies on the current limiting from the upstream PC's USB host that supplies power to the Pico board.

Someone who has an oscilloscope capable of looking at the USB eye pattern should recommend correct resistor values to fix termination issues given you can't easily place resistors close to the pins on the RP2040 chip on an off-the-shelf Raspberry Pi Pico board.

Software

This project relies on the Raspberry Pi Pico SDK, which includes the TinyUSB library. At the time of this writing, the Pico SDK version is 1.5.1, and includes TinyUSB 0.15.0. This project requires a version of TinyUSB from 15-Aug-2023 or later or else the USB Host port driver won't install. Also, you will need to apply a patch to the TinyUSB library to enable the TinyUSB's Device driver to properly clone the control endpoint length field in the USB descriptor. Finally, because TinyUSB dropped git submodule support in its newer versions, you will need to run a Python 3 script to install the code from the Pico-PIO-USB project.

All instructions below assume that you are using a Linux command line environment or similar.

Set up your development environment

Before you start trying to build this code, please make sure you have a working development environment. See the Getting Started with Raspberry Pi Pico document for instructions. You will need the git command line utility plus other development tools. You will also need to have Python 3 installed and in your PATH.

Install the project software

Set your current directory to some project directory ${PROJECTS} (e.g. ${HOME}/projects/pico) and then clone the pico-usb-midi-filter project:

cd ${PROJECTS}
git clone https://github.com/rppicomidi/pico-usb-midi-filter.git

Updating and patching the TinyUSB library

Previous versions of the pico-usb-midi-filter project instruct you to use a fork of TinyUSB, which can be confusing and hurts compatibility with other projects. If you installed the TinyUSB fork previously, please restore the original TinyUSB code before attempting to follow the instructions below.

The Pico SDK uses the main repository for TinyUSB as a git submodule. The instructions have been tested for the case of installing the Pico SDK in the directory ${PICO_SDK_PATH}, which for me is set to ${HOME}/projects/pico/pico-sdk.

  1. If you have not already done so, follow the instructions for installing the Raspberry Pi Pico SDK in Chapter 2 of the Getting started with Raspberry Pi Pico document.

  2. Set the working directory to the tinyusb library and get the latest TinyUSB code

cd ${PICO_SDK_PATH}/lib/
git submodule update tinyusb
cd tinyusb
git checkout master
git pull
  1. Install the Pico-PIO-USB library (stay in the ${PICO_SDK_PATH}/lib/tinyusb directory)
python3 tools/get_deps.py rp2040
  1. Patch TinyUSB (stay in the ${PICO_SDK_PATH}/lib/tinyusb directory)
git checkout -b ep0sz-fn
git apply ${PROJECTS}/pico-usb-midi-filter/tinyusb_patches/0001-Allow-defining-CFG_TUD_ENDPOINT0_SIZE-as-a-function.patch
git add -A
git commit -m 'Allow defining CFG_TUD_ENDPOINT0_SIZE as a function'

Note: If you need to update TinyUSB in the future, follow these steps from the ${PICO_SDK_PATH}/lib/tinyusb directory to undo the patch:

git checkout master
git branch -D ep0sz-fn
git pull

Then update TinyUSB and repeat step 4 above to apply the patch again.

Building from the command line and loading the Pico software using the file system interface

The command line build process is pretty common for most Pico projects. I have the pico-sdk installed in $HOME/projects/pico/pico-sdk.

export PICO_SDK_PATH=$HOME/projects/pico/pico-sdk/
cd [the parent directory where you cloned this project]/pico-usb-midi-filter
mkdir build
cd build
cmake ..
make

This process should generate the file pico-usb-midi-filter\build\midi_app.uf2. Connect a USB cable to your PC. Do not connect it to your Pico board yet. Hold the BOOTSEL button on your Pico board and plug the cable to your Pico board's microUSB connector. The Pico should automount on your computer. Use the PC file manager to drag and drop the ``pico-usb-midi-filter.uf2` file to the Pico "file system". The Pico should automatically unmount when the file copy is complete.

Building and debugging using VS Code and the Pico Probe

These instructions apply for Ubuntu Linux. Before you use VS Code with this project the first time, open a terminal window and type the following commands:

cd [the parent directory where you cloned this project]/
mkdir build
cd build
touch compile_commands.json

This project already includes a hidden .vscode directory that does most of the heavy lifting for setting up the project for VS Code. I connect the Pico Probe's microUSB to the host PC via an external hub and and then I connect target Pico board's microUSB directly to the PC. For reasons I can't explain, debug does not seem to work correctly otherwise.

I have the pico-sdk installed in $HOME/projects/pico/pico-sdk. I set up the environment using this script from the command line (I am using Ubuntu Linux that uses gnome-terminal):

#!/bin/sh
export PICO_SDK_PATH=$HOME/projects/pico/pico-sdk/
gnome-terminal -- openocd -f interface/picoprobe.cfg -f target/rp2040.cfg
gnome-terminal -- minicom -D /dev/ttyACM0 -b 115200

The first line after the shebang sets PICO_SDK_PATH so that the build works correctly. The first gnome-terminal command opens a new terminal window and runs the openocd server that talks to the Pico Probe. The second gnome-terminal command opens a minicom serial port console window.

Finally, from the original terminal where I ran this script I start VS Code using this command.

code

When VS Code launches, File->Open Folder... and choose the directory where you installed the pico-usb-midi-filter project. You will have to choose the arm-none-eabi-gcc compiler. Now you should be able to build and run this code as described in Chapter 7 of the Getting started with Raspberry Pi Pico document.

Testing

Before messing with a DAW, plug your MIDI device to the Pico's added USB host port and, from the Linux command line, type

amidi -l

You should see something like this if you have the Arturia KeyLab Essential plugged to the Pico's added USB Host port:

Dir Device    Name
IO  hw:1,0,0  Arturia KeyLab Essential 88 MID
IO  hw:1,0,1  Arturia KeyLab Essential 88 DAW

You will need to use the appriate Mac or PC Tools to run low level tests using those computers as hosts.

This program should just pass MIDI data transparently between the PC and whatever other MIDI device you have connected. If you plug an Arturia Keylab Essential keyboard to the Pico's added USB host port, and set the Controller Map to DAW mode, you can see the MIDI filter in action.

If you have an Arturia Keylab Essential, make sure you set the keyboard Map Setup to DAW mode and you use Arturia Midi Control Center software to set the Controller Map DAW Mode to Mackie Control and the DAW Fader mode to Jump. The Mackie Control commands will come out of virtual MIDI cable 1, which is called something like MIDIIN2 and MIDIOUT2 in Windows and DAW in Linux or Mac.

If you want to test it with Cubase, follow the instructions for setting up Cubase to work with Mackie control here. With this code, the Save, Undo and Punch buttons work as the button labels suggest. The Metro button functions as the marker add button. And the faders will have soft pickup instead of jumping the first time you move them.

Creating your own MIDI filter

I created the filter I needed for my project. However, you may need your own filter. The API for your filter is in midi_filter.h. Create a new filter source file (e.g., mykeyboard_split_filter.c) and make sure you implement all functions in the midi_filter.h. Remove keylab_essential_mc_filter.c from CMakeLists.txt and replace it with the name of your filter source file. Rebuild the project, and it all should just work.

About

Connect a Raspberry Pi Pico between a USB MIDI device and a USB Host to modify the USB MIDI traffic

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published