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/c6cb587ebd6dde3228df4cf52f2ed1a9 to your computer and use it in GitHub Desktop.
Save YuuichiAkagawa/c6cb587ebd6dde3228df4cf52f2ed1a9 to your computer and use it in GitHub Desktop.
UHS2-MIDI: Digital Synth PRA32-U USB MIDI bridge
/*
*******************************************************************************
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