Skip to content

Commit

Permalink
Allow programmer to work over usb MIDI
Browse files Browse the repository at this point in the history
  • Loading branch information
tstirrat committed Oct 9, 2024
1 parent bcf8121 commit 0b8f75a
Show file tree
Hide file tree
Showing 10 changed files with 667 additions and 1,125 deletions.
105 changes: 71 additions & 34 deletions Arduinoboy/Arduinoboy.ino
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@
#define MEM_MIDIOUT_BIT_DELAY 61
#define MEM_MIDIOUT_BYTE_DELAY 63

// #define PRO_MICRO // define this for Pro Micro custom Arduino (not detectable from regular defines)

/***************************************************************************
* User Settings
***************************************************************************/
Expand Down Expand Up @@ -140,6 +142,8 @@ byte defaultMemoryMap[MEM_MAX] = {
};
byte memory[MEM_MAX];

#include <MIDI.h>

/***************************************************************************
* Lets Assign our Arduino Pins .....
***************************************************************************/
Expand All @@ -153,9 +157,8 @@ byte memory[MEM_MAX];
* Be sure to compile
***************************************************************************/
#if defined (__MK20DX256__) || defined (__MK20DX128__) || defined (__MKL26Z64__)
#define USE_TEENSY 1
#define USE_USB 1
#include <MIDI.h>
#define HAS_USB_MIDI
#include <USB-MIDI.h>

#if defined (__MKL26Z64__)
#define GB_SET(bit_cl,bit_out,bit_in) GPIOB_PDOR = ((bit_in<<3) | (bit_out<<1) | bit_cl)
Expand All @@ -173,12 +176,35 @@ int pinButtonMode = 2; //toggle button for selecting the mode

HardwareSerial *serial = &Serial1;

/***************************************************************************
* Pro Micro (ATmega32U4, with different pin config)
***************************************************************************/
#elif defined (PRO_MICRO)
#define HAS_USB_MIDI
#include <USB-MIDI.h>

#define GB_SET(bit_cl, bit_out, bit_in) PORTF = (PINF & B00011111) | ((bit_cl<<7) | ((bit_out)<<6) | ((bit_in)<<5))
// ^ The reason for not using digitalWrite is to allign clock and data pins for the GB shift reg.
// Pin distribution comes from official Arduino Leonardo documentation

int pinGBClock = A0; // Analog In 0 - clock out to gameboy
int pinGBSerialOut = A1; // Analog In 1 - serial data to gameboy
int pinGBSerialIn = A2; // Analog In 2 - serial data from gameboy
int pinMidiInputPower = 4; // power pin for midi input opto-isolator
int pinStatusLed = 10; // Status LED
int pinLeds[] = {9,8,7,6,5,10}; // LED Pins
int pinButtonMode = 3; //toggle button for selecting the mode

HardwareSerial *serial = &Serial1;



/***************************************************************************
* Arduino Leonardo/Yún/Micro (ATmega32U4)
***************************************************************************/
#elif defined (__AVR_ATmega32U4__)
#define USE_LEONARDO
#include <MIDIUSB.h>
#define HAS_USB_MIDI
#include <USB-MIDI.h>

#define GB_SET(bit_cl, bit_out, bit_in) PORTF = (PINF & B00011111) | ((bit_cl<<7) | ((bit_out)<<6) | ((bit_in)<<5))
// ^ The reason for not using digitalWrite is to allign clock and data pins for the GB shift reg.
Expand All @@ -200,9 +226,8 @@ HardwareSerial *serial = &Serial1;
***************************************************************************/
#elif defined (__SAM3X8E__)
#define USE_DUE

#define USE_LEONARDO
#include <MIDIUSB.h>
#define HAS_USB_MIDI
#include <USB-MIDI.h>
#include <digitalWriteFast.h>


Expand Down Expand Up @@ -267,12 +292,12 @@ boolean sysexProgrammingMode = 0;
boolean sysexProgrammingWaiting = 0;
boolean sysexProgrammingConnected = 0;

unsigned long sysexProgrammerWaitTime = 2000; //2 seconds
unsigned long sysexProgrammerCallTime = 1000; //1 second
const unsigned long PROGRAMMER_MAX_WAIT_TIME = 2000; //2 seconds
const unsigned long PROGRAMMER_MAX_CALL_TIME = 1000; //1 second
unsigned long sysexProgrammerLastResponse = 0;
unsigned long sysexProgrammerLastSent = 0;

byte sysexManufacturerId = 0x69; //har har harrrrr :)
const byte SYSEX_MFG_ID = 0x69; //har har harrrrr :)
int sysexPosition;
byte sysexData[128];
byte longestSysexMessage = 128;
Expand Down Expand Up @@ -421,6 +446,24 @@ uint8_t mapQueueWaitUsb = 5; //5ms - Needs to be longer because message packet i
***************************************************************************/
#define GB_MIDI_DELAY 500 //Microseconds to delay the sending of a byte to gb

/***************************************************************************
* MIDI Setup (Serial and USB)
***************************************************************************/
USING_NAMESPACE_MIDI;

#ifdef HAS_USB_MIDI
typedef USBMIDI_NAMESPACE::usbMidiTransport __umt;
typedef MIDI_NAMESPACE::MidiInterface<__umt> __ss;
__umt usbMIDI_t(0 /** cable no */);
__ss uMIDI((__umt&)usbMIDI_t);
#endif

#if defined(ARDUINO_SAM_DUE) || defined(USBCON) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__)
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, sMIDI);
#else
MIDI_CREATE_INSTANCE(HardwareSerial, Serial, sMIDI);
#endif

void setup() {
/*
Init Memory
Expand All @@ -437,25 +480,22 @@ void setup() {
pinMode(pinGBSerialIn,INPUT);
pinMode(pinGBSerialOut,OUTPUT);

/*
Set MIDI Serial Rate
*/

#ifdef USE_USB
serial->begin(31250); //31250
#else
if(usbMode == true) {
serial->begin(38400);
} else {
pinMode(pinMidiInputPower,OUTPUT);
digitalWrite(pinMidiInputPower,HIGH); // turn on the optoisolator
#ifdef USE_LEONARDO
Serial1.begin(31250); //31250
#else
Serial.begin(31250); //31250
#endif
}
#endif
/*
Set MIDI Serial Rate
*/
// if(usbMode == true) {
// serial->begin(38400);
// } else {
pinMode(pinMidiInputPower,OUTPUT);
digitalWrite(pinMidiInputPower,HIGH); // turn on the optoisolator
sMIDI.begin(MIDI_CHANNEL_OMNI);

// Enable serial MIDI in to serial MIDI out "thru mode"
if (!usbMode) sMIDI.turnThruOn(Thru::Full);

#ifdef HAS_USB_MIDI
uMIDI.begin(MIDI_CHANNEL_OMNI);
#endif

/*
Set Pin States
Expand Down Expand Up @@ -489,10 +529,7 @@ void setup() {
#endif
lastMode = memory[MEM_MODE];

/*
usbMidi sysex support
*/
usbMidiInit();
initProgrammerSysexHandlers();

startupSequence();

Expand Down
162 changes: 42 additions & 120 deletions Arduinoboy/Mode_LSDJ_Keyboard.ino
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ void modeLSDJKeyboardSetup()
digitalWrite(pinStatusLed,LOW);
pinMode(pinGBClock,OUTPUT);
digitalWrite(pinGBClock,HIGH);
#ifdef USE_TEENSY
usbMIDI.setHandleRealTimeSystem(NULL);
#endif
blinkMaxCount=1000;

/* The stuff below makes sure the code is in the same state as LSDJ on reset / restart, mode switched, etc. */
Expand All @@ -38,75 +35,50 @@ void modeLSDJKeyboardSetup()

void modeLSDJKeyboard()
{
while(1){ //Loop foreverrrr
modeLSDJKeyboardMidiReceive();
if (serial->available()) { //If MIDI is sending
incomingMidiByte = serial->read(); //Get the byte sent from MIDI
if(!checkForProgrammerSysex(incomingMidiByte) && !usbMode) serial->write(incomingMidiByte);//Echo the Byte to MIDI Output
while (1) {
modeLSDJKeyboardUsbMidiReceive();
modeLSDJKeyboardSerialMidiReceive();

updateStatusLed(); // Update our status blinker
setMode(); // Check if mode button was depressed
}
}

/***************************************************************************
* Midi to LSDJ Keyboard Handling *
***************************************************************************/
//If the byte is a Status Message
if(incomingMidiByte & 0x80) {
/* Status message Information (# = midi channel 0 to F [1-16] )
0x8# = Note Off
0x9# = Note On
0xA# = AfterTouch (ie, key pressure)
0xB# = Control Change
0xC# = Program (patch) change
0xD# = Channel Pressure
0xE# = Pitch Wheel
0xF0 - 0xF7 = System Common Messages
0xF8 - 0xFF = System Realtime Messages
*/
void modeLSDJKeyboardUsbMidiReceive()
{
#ifdef HAS_USB_MIDI
while(uMIDI.read(memory[MEM_KEYBD_CH]+1)) {

//Weee hello world bitwise and. ... make the second hex digit zero so we can have a simple case statement
// - the second digit is usually the midi channel 0 to F (1-16) unless its a 0xF0 message...
switch (incomingMidiByte & 0xF0) {
case 0x90:
//Note-On Status Message (Note: we have to treat this carefully because note status isnt sent on every note-on, damn it)
//There are 3 bytes total we need: Channel, Note, and velocity, these wil be assigned to a array until we have the velocity,
//at that point we can then call our note out function to LSDJ
midiNoteOnMode = true; //Set our stupid "Note on mode" on
midiData[0] = incomingMidiByte; //Assign the byte to the first position of a data array. (this is the midi channel)
midiData[1] = false; //Force the second position to false (this will hold the note number)
break;
case 0xC0:
//Program change message
midiProgramChange = true; //Set our silly "Program Change mode" ... we need to get the next byte later
midiNoteOnMode = false; //Turn Note-on mode off
midiData[0] = incomingMidiByte - 48;//Set the number to a "note on" message so we can use the same "channel" variable as note on messages
break;
case 0xF0:
//Do nothing, these dont interfear with our note-on mode
break;
default:
//Turn Note-On mode off
midiNoteOnMode = false;
break;
}
} else if(midiNoteOnMode) {
//It wasnt a status bit, so lets assume it was a note message if the last status message was note-on.
if(!midiData[1]) {
//If we dont have a note number, we assume this byte is the note number, get it...
midiData[1] = incomingMidiByte;
} else {
//We have our note and channel, so call our note function...
byte type = uMIDI.getType();
byte data1 = uMIDI.getData1();
byte data2 = uMIDI.getData2();

playLSDJNote(midiData[0], midiData[1], incomingMidiByte);
midiData[1] = false; //Set the note to false, forcing to capture the next note
}
} else if (midiProgramChange) {
changeLSDJInstrument(midiData[0], incomingMidiByte);
midiProgramChange = false;
midiData[0] = false;
}
handleLSDJKeyboardMessage(type, data1, data2);
}
#endif
}

void modeLSDJKeyboardSerialMidiReceive() {
if (!sMIDI.read(memory[MEM_KEYBD_CH])) return;

byte type = sMIDI.getType();
byte data1 = sMIDI.getData1();
byte data2 = sMIDI.getData2();

handleLSDJKeyboardMessage(type, data1, data2);
}

updateStatusLed(); // Update our status blinker
setMode(); // Check if mode button was depressed
void handleLSDJKeyboardMessage(byte type, byte data1, byte data2) {
switch(type) {
case midi::NoteOff:
playLSDJNote(midi::NoteOff + memory[MEM_KEYBD_CH], data1, 0);
break;
case midi::NoteOn:
playLSDJNote(midi::NoteOn + memory[MEM_KEYBD_CH], data1, data2);
break;
case midi::ProgramChange:
changeLSDJInstrument(midi::ProgramChange + memory[MEM_KEYBD_CH], data1);
break;
}
}

Expand All @@ -118,10 +90,10 @@ void changeLSDJInstrument(byte channel,byte message)
{
keyboardCurrentIns = message; //set the current instrument number

if(channel == (0x90+memory[MEM_KEYBD_CH]) && keyboardCurrentIns != keyboardLastIns) {
if(channel == (midi::NoteOn+memory[MEM_KEYBD_CH]) && keyboardCurrentIns != keyboardLastIns) {
//if its on our midi channel and the instrument isnt the same as our currrent
if(!memory[MEM_KEYBD_COMPAT_MODE]) {
sendKeyboardByteToGameboy(0x80 | message); // <- this is suppose to work but doesn't :/
sendKeyboardByteToGameboy(midi::NoteOff | message); // <- this is suppose to work but doesn't :/
} else {
//We will find out which is greater, the current instrument or the last instrument. then
//cycle up or down to that instrument
Expand All @@ -147,7 +119,7 @@ void changeLSDJInstrument(byte channel,byte message)
*/
void playLSDJNote(byte channel,byte note, byte velocity)
{
if(channel == (0x90+memory[MEM_KEYBD_CH])
if(channel == (midi::NoteOn+memory[MEM_KEYBD_CH])
&& velocity > 0x00) { //If midi channel = ours and the velocity is greater then 0
if(note >= keyboardNoteStart) {
keyboardNoteOffset = 0;
Expand Down Expand Up @@ -233,54 +205,4 @@ void sendKeyboardByteToGameboy(byte send_byte)
delayMicroseconds(50);
GB_SET(1,1,0);
delayMicroseconds(4000);
}

void modeLSDJKeyboardMidiReceive()
{
#ifdef USE_TEENSY

while(usbMIDI.read(memory[MEM_KEYBD_CH]+1)) {
switch(usbMIDI.getType()) {
case 0x80: // note off
playLSDJNote(0x90+memory[MEM_KEYBD_CH], usbMIDI.getData1(), 0);
break;
case 0x90: // note on
playLSDJNote(0x90+memory[MEM_KEYBD_CH], usbMIDI.getData1(), usbMIDI.getData2());
break;
case 0xC0: // PG
changeLSDJInstrument(0xC0+memory[MEM_KEYBD_CH], usbMIDI.getData1());
break;
/*
case 3: // CC
break;
case 5: // AT
break;
case 6: // PB
break;
*/
}
}
#endif
#ifdef USE_LEONARDO

midiEventPacket_t rx;
do {
rx = MidiUSB.read();
switch (rx.header)
{
case 0x08: // note off
playLSDJNote(0x90+memory[MEM_KEYBD_CH], rx.byte2, 0);
statusLedOn();
break;
case 0x09: // note on
playLSDJNote(0x90+memory[MEM_KEYBD_CH], rx.byte2, rx.byte3);
statusLedOn();
break;
case 0x0C: // PG
changeLSDJInstrument(0xC0+memory[MEM_KEYBD_CH], rx.byte2);
statusLedOn();
break;
}
} while (rx.header != 0);
#endif
}
}
Loading

0 comments on commit 0b8f75a

Please sign in to comment.