Skip to content

Instantly share code, notes, and snippets.

@YuuichiAkagawa
Last active August 20, 2023 12:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save YuuichiAkagawa/cdf48c4313ca82222337ad9fda5fc369 to your computer and use it in GitHub Desktop.
Save YuuichiAkagawa/cdf48c4313ca82222337ad9fda5fc369 to your computer and use it in GitHub Desktop.
USBH_MIDI: Digital Synth PRA32-U USB MIDI bridge
/*
*******************************************************************************
USBH_MIDI Digital Synth PRA32-U bridge
Copyright (C) 2023 Yuuichi Akagawa
*******************************************************************************
*/
#include <usbh_midi.h>
#include <usbhub.h>
USB Usb;
USBHub Hub1(&Usb);
USBH_MIDI Midi1(&Usb);
USBH_MIDI Midi2(&Usb);
//Midi instance array
USBH_MIDI *Midi[] {&Midi1, &Midi2};
#define PRA32U_MIDI_CH 1
#define PRA32U_MIDI_CH_USB (PRA32U_MIDI_CH - 1)
#define NUMBER_OF_PRESET 8
#define PRESET_ELEMENTS (NUMBER_OF_PRESET + 1)
uint8_t currPreset = 1;
//Preset table v0.3.1 (from https://raw.githubusercontent.com/risgk/digital-synth-pra32-u/main/pra32-u-ctrl.html)
uint8_t presetControllers [][PRESET_ELEMENTS] = {
//APP_NAME_VERSION = "PRA32-U CTRL v0.3.1";
{ 24, 96 , 96 , 96 , 96 , 96 , 96 , 96 , 0 },
{102, 0 , 0 , 0 , 0 , 0 , 0 , 127, 0 },
{103, 64 , 64 , 64 , 64 , 64 , 0 , 127, 0 },
{ 26, 127, 64 , 64 , 64 , 127, 64 , 64 , 64 },
{ 55, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
{ 20, 71 , 64 , 64 , 64 , 64 , 64 , 64 , 64 },
{ 21, 64 , 70 , 70 , 70 , 70 , 70 , 70 , 64 },
{ 25, 64 , 32 , 32 , 32 , 32 , 32 , 32 , 0 },
{ 16, 115, 115, 91 , 115, 67 , 115, 67 , 127},
{ 17, 64 , 64 , 32 , 32 , 64 , 64 , 32 , 0 },
{ 18, 64 , 64 , 100, 16 , 112, 64 , 76 , 64 },
{ 86, 127, 127, 127, 127, 127, 127, 127, 127},
{ 23, 0 , 64 , 48 , 96 , 0 , 0 , 0 , 0 },
{ 19, 0 , 0 , 80 , 96 , 96 , 0 , 104, 0 },
{ 27, 127, 127, 0 , 0 , 0 , 127, 0 , 127},
{ 28, 0 , 64 , 0 , 0 , 96 , 0 , 0 , 0 },
{104, 64 , 64 , 70 , 64 , 64 , 64 , 96 , 64 },
{105, 0 , 0 , 64 , 0 , 0 , 0 , 127, 0 },
{ 87, 127, 0 , 0 , 0 , 64 , 127, 0 , 0 },
{ 22, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
{ 14, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
{ 80, 80 , 80 , 80 , 80 , 80 , 80 , 80 , 80 },
{ 81, 0 , 0 , 0 , 0 , 0 , 64 , 0 , 0 },
{ 15, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
{ 82, 118, 118, 118, 118, 64 , 127, 64 , 64 },
{ 9, 0 , 0 , 0 , 0 , 0 , 127, 0 , 0 },
{ 83, 64 , 64 , 64 , 64 , 88 , 64 , 88 , 64 },
{110, 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 },
{ 56, 0 , 64 , 0 , 0 , 0 , 0 , 0 , 0 },
{ 57, 0 , 0 , 0 , 0 , 96 , 0 , 104, 0 },
{ 58, 127, 127, 127, 127, 0 , 127, 0 , 127},
{ 59, 0 , 64 , 0 , 0 , 96 , 0 , 0 , 0 },
{ 63, 127, 127, 127, 127, 127, 127, 127, 0 },
{ 61, 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 },
{ 60, 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 },
{ 62, 64 , 64 , 64 , 64 , 64 , 64 , 64 , 64 },
{ 85, 12 , 12 , 12 , 12 , 12 , 12 , 12 , 12 },
{111, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
{ 31, 0 , 0 , 0 , 0 , 127, 0 , 0 , 0 },
{ 89, 0 , 0 , 0 , 0 , 127, 0 , 0 , 0 },
{109, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
};
//device index
uint8_t ixRpiPico = 0xff;
uint8_t ixmidiCtrl = 0xff;
//interrupt flags
bool isPanic = false;
bool isSelPreset = false;
void setup() {
//attache onInit handler
Midi1.attachOnInit(onInit1);
Midi2.attachOnInit(onInit2);
Midi1.attachOnRelease(onDetach1);
Midi2.attachOnRelease(onDetach2);
if (Usb.Init() == -1) {
while (1); //halt
}//if (Usb.Init() == -1...
delay( 200 );
//Preset Select
pinMode(3, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(3), selectPreset, LOW);
//All Reset
pinMode(2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), panic, LOW);
}
void loop() {
Usb.Task();
//Adafruit Pico(PRA32-U)
if ( ixRpiPico == 0xff ) {
//Subsequent processes are skipped until the device is online.
return;
}
//panic
if ( isPanic ) {
sendControlChange(ixRpiPico, 120, 0);
sendControlChange(ixRpiPico, 123, 0);
sendControlChange(ixRpiPico, 121, 0);
isPanic = false;
}
//preset change
if ( isSelPreset ) {
currPreset++;
if ( currPreset > NUMBER_OF_PRESET) {
currPreset = 1;
}
sendPreset(ixRpiPico, currPreset);
isSelPreset = false;
}
//MIDI Controller
if ( ixmidiCtrl != 0xff ) {
uint8_t midiMsg[ 3 ];
uint8_t size;
//Read all MIDI messages
do {
if ( (size = Midi[ixmidiCtrl]->RecvData(midiMsg)) > 0 ) {
//Send to Adafruit Pico(PRA32-U)
if ( (midiMsg[0] & 0xf0) < 0xf0 ) { //Ignore system messages
Midi[ixRpiPico]->SendData(midiMsg);
}
}
} while (size > 0);
}
}
void sendControlChange(uint8_t idx, uint8_t noCc, uint8_t valCc)
{
uint8_t buf[3];
buf[0] = 0xb0 | PRA32U_MIDI_CH_USB;
buf[1] = noCc;
buf[2] = valCc;
Midi[idx]->SendData(buf);
}
void sendPreset(uint8_t idx, uint8_t noPreset)
{
uint8_t params = sizeof(presetControllers) / PRESET_ELEMENTS;
for (uint8_t i = 0; i < params; i++) {
sendControlChange(idx, presetControllers[i][0], presetControllers[i][noPreset]);
}
}
void onInit1()
{
setupmidi(0);
}
void onInit2()
{
setupmidi(1);
}
void onDetach1()
{
detachMidi(0);
}
void onDetach2()
{
detachMidi(1);
}
void setupmidi(uint8_t idx)
{
uint16_t vid = Midi[idx]->idVendor();
uint16_t pid = Midi[idx]->idProduct();
//is Adafruit Pico(PRA32-U)?
if ( vid == 0x239a && pid == 0xcafe ) {
ixRpiPico = idx;
// select preset
sendPreset(idx, currPreset);
return;
}
//is any MIDI Controller ?
ixmidiCtrl = idx;
}
void detachMidi(uint8_t idx)
{
if ( idx == ixmidiCtrl ) {
ixmidiCtrl = 0xff;
return;
}
if ( idx == ixRpiPico ) {
ixRpiPico = 0xff;
return;
}
}
//push button interrupt handler
void selectPreset()
{
isSelPreset = true;
}
//panic
void panic()
{
isPanic = true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment