Last active
August 20, 2023 12:58
-
-
Save YuuichiAkagawa/cdf48c4313ca82222337ad9fda5fc369 to your computer and use it in GitHub Desktop.
USBH_MIDI: Digital Synth PRA32-U USB MIDI bridge
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
******************************************************************************* | |
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