Last active
August 20, 2023 12:58
-
-
Save YuuichiAkagawa/c6cb587ebd6dde3228df4cf52f2ed1a9 to your computer and use it in GitHub Desktop.
UHS2-MIDI: Digital Synth PRA32-U USB MIDI bridge
This file contains 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
/* | |
******************************************************************************* | |
UHS2-MIDI Digital Synth PRA32-U bridge | |
Copyright (C) 2023 Yuuichi Akagawa | |
******************************************************************************* | |
*/ | |
#include <UHS2-MIDI.h> | |
#include <usbhub.h> | |
USB Usb; | |
USBHub Hub1(&Usb); | |
#define NUMBER_OF_DEVICES 2 | |
UHS2MIDI_CREATE_INSTANCE(&Usb, 0, Midi1); | |
UHS2MIDI_CREATE_INSTANCE(&Usb, 0, Midi2); | |
MIDI_NAMESPACE::MidiInterface<UHS2MIDI_NAMESPACE::uhs2MidiTransport> *Midi[] {&Midi1, &Midi2}; | |
#define 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() { | |
Midi1.begin(MIDI_CHANNEL_OMNI); | |
Midi2.begin(MIDI_CHANNEL_OMNI); | |
//USB attach/detach handler | |
Midi1.getTransport()->attachOnInit(onInit1); | |
Midi2.getTransport()->attachOnInit(onInit2); | |
Midi1.getTransport()->attachOnRelease(onDetach1); | |
Midi2.getTransport()->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 ) { | |
Midi[ixRpiPico]->sendControlChange(120, 0, PRA32U_MIDI_CH); | |
Midi[ixRpiPico]->sendControlChange(123, 0, PRA32U_MIDI_CH); | |
Midi[ixRpiPico]->sendControlChange(121, 0, PRA32U_MIDI_CH); | |
isPanic = false; | |
} | |
//preset change | |
if ( isSelPreset ) { | |
currPreset++; | |
if ( currPreset > NUMBER_OF_PRESET) { | |
currPreset = 1; | |
} | |
sendPreset(ixRpiPico, currPreset); | |
isSelPreset = false; | |
} | |
//MIDI Controller | |
if ( ixmidiCtrl != 0xff ) { | |
do { | |
Midi[ixmidiCtrl]->read(); | |
} while ( Midi[ixmidiCtrl]->getTransport()->available() > 0); | |
} | |
} | |
void sendPreset(uint8_t idx, uint8_t noPreset) | |
{ | |
uint8_t params = sizeof(presetControllers) / PRESET_ELEMENTS; | |
for (uint8_t i = 0; i < params; i++) { | |
Midi[idx]->sendControlChange(presetControllers[i][0], presetControllers[i][noPreset], PRA32U_MIDI_CH); | |
} | |
} | |
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]->getTransport()->idVendor(); | |
uint16_t pid = Midi[idx]->getTransport()->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; | |
#if defined(UHS2_MIDI_VERSION) && (UHS2_MIDI_VERSION >= 10001) | |
//is SQ-64? | |
if( vid == 0x0944 && pid == 0x0146 ){ | |
Midi[idx]->getTransport()->setCableNumber(1); | |
} else { | |
Midi[idx]->getTransport()->setCableNumber(0); | |
} | |
#endif | |
//register MIDI message handler | |
Midi[idx]->setHandleNoteOn(handleNoteOn); | |
Midi[idx]->setHandleNoteOff(handleNoteOff); | |
Midi[idx]->setHandleControlChange(handleControlChange); | |
Midi[idx]->setHandleProgramChange(handleProgramChange); | |
Midi[idx]->setHandlePitchBend(handlePitchBend); | |
} | |
void detachMidi(uint8_t idx) | |
{ | |
if ( idx == ixmidiCtrl ) { | |
ixmidiCtrl = 0xff; | |
//unregister MIDI message handler | |
Midi[idx]->setHandleNoteOn(nullptr); | |
Midi[idx]->setHandleNoteOff(nullptr); | |
Midi[idx]->setHandleControlChange(nullptr); | |
Midi[idx]->setHandleProgramChange(nullptr); | |
Midi[idx]->setHandlePitchBend(nullptr); | |
return; | |
} | |
if ( idx == ixRpiPico ) { | |
ixRpiPico = 0xff; | |
return; | |
} | |
} | |
//MIDI message handler | |
void handleNoteOn(byte inChannel, byte inNumber, byte inVelocity) | |
{ | |
Midi[ixRpiPico]->sendNoteOn(inNumber, inVelocity, PRA32U_MIDI_CH); | |
} | |
void handleNoteOff(byte inChannel, byte inNumber, byte inVelocity) | |
{ | |
Midi[ixRpiPico]->sendNoteOff(inNumber, inVelocity, PRA32U_MIDI_CH); | |
} | |
void handleControlChange(byte inChannel, byte inControlNumber, byte inControlValue) | |
{ | |
Midi[ixRpiPico]->sendControlChange(inControlNumber, inControlValue, PRA32U_MIDI_CH); | |
} | |
void handleProgramChange(byte inChannel, byte inProgramNumber) | |
{ | |
Midi[ixRpiPico]->sendProgramChange(inProgramNumber, PRA32U_MIDI_CH); | |
} | |
void handlePitchBend(byte inChannel, int bend) | |
{ | |
Midi[ixRpiPico]->sendPitchBend(bend, PRA32U_MIDI_CH); | |
} | |
//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