Arduino MIDI Remapper - Part 2

 

Fig.1 - MIDI events captured using MIDI-OX

In my last post I got the Arduino working with the MIDI and SoftwareSerial libraries and sending MIDI messages to the B4000+ organ sound module. The next stage was to get it receiving MIDI messages from the XB5 organ and retransmitting them to the B4000+.

Using Callbacks

“A callback is a function that you wrote and that will be used by the library when a message arrives. You put your message handling code in this function, and tell the library to call it back when the corresponding message is received” (github.com/FortySevenEffects/arduino_midi_library/wiki/Using-Callbacks).
“Othmar52” has created ‘boilerplate’ code for an Arduino-based MIDI thru device which can be used as a template for custom MIDI event transformations. The code uses ‘callbacks’ to repeat MIDI event messages without changing any of the data. Using this code as a starting point I modified it to use the SoftwareSerial library to receive on pin 8 and transmit on pin 9, to make it compatible with the Sparkfun MIDI board. At least during the development phase I wanted to keep the UART Rx/Tx pins available for sending debugging messages to the console.
Unfortunately it did not work properly, i.e. the received messages were being corrupted. I believe this is due to timing/latency issues with the SoftwareSerial library.

The Code

After some research I came across AltSoftSerial, an alternative library that provides more accurate timing. I modified the code to use the AltSoftSerial library, and ‘hey presto’, it now works! The code is shown below.

#include <MIDI.h>
#include <AltSoftSerial.h>
AltSoftSerial midiSerial;
MIDI_CREATE_INSTANCE(AltSoftSerial, midiSerial, MIDI);
// MIDI RX/TX pins are 8/9

void setup() {
  Serial.begin(115200);
  Serial.println("Starting serial with debug monitor");
  Serial.println("MIDI RX/TX pins are 8/9");
  
  MIDI.begin(MIDI_CHANNEL_OMNI); // Launch MIDI, by default listening to all channels
  MIDI.turnThruOff(); // We have to avoid channel based events being sent through

  // set callbacks for all different event types
  MIDI.setHandleClock(handleMidiEventClock);
  MIDI.setHandleTick(handleMidiEventClock);
  MIDI.setHandleStop(handleMidiEventStop);
  MIDI.setHandleStart(handleMidiEventStart);
  MIDI.setHandleContinue(handleMidiEventContinue);
  MIDI.setHandleNoteOn(handleMidiEventNoteOn);
  MIDI.setHandleNoteOff(handleMidiEventNoteOff);
  MIDI.setHandleAfterTouchPoly(handleMidiEventAfterTouchPoly);
  MIDI.setHandleAfterTouchChannel(handleMidiEventAfterTouchChannel);
  MIDI.setHandleProgramChange(handleMidiEventProgramChange);
  MIDI.setHandleControlChange(handleMidiEventControlChange);
  MIDI.setHandlePitchBend(handleMidiEventPitchBend);
  MIDI.setHandleSystemExclusive(handleMidiEventSysEx);
  MIDI.setHandleTimeCodeQuarterFrame(handleMidiEventTimeCodeQuarterFrame);
  MIDI.setHandleSongPosition(handleMidiEventSongPosition);
  MIDI.setHandleSongSelect(handleMidiEventSongSelect);
  MIDI.setHandleTuneRequest(handleMidiEventTuneRequest);
  MIDI.setHandleSystemReset(handleMidiEventSystemReset);
}

void loop() {
  MIDI.read();
}


void handleMidiEventNoteOn(byte inChannel, byte inPitch, byte inVelocity) {
  MIDI.sendNoteOn(inPitch, inVelocity, inChannel);
}
void handleMidiEventNoteOff(byte inChannel, byte inPitch, byte inVelocity) {
  MIDI.sendNoteOff(inPitch, inVelocity, inChannel);
}

void handleMidiEventAfterTouchPoly(byte inChannel, byte inNote, byte inValue) {
  MIDI.sendAfterTouch(inNote, inValue, inChannel);
}

void handleMidiEventControlChange(byte inChannel, byte inControlNumber, byte inControlValue) {
  MIDI.sendControlChange(inControlNumber, inControlValue, inChannel);
}

void handleMidiEventProgramChange(byte inChannel, byte inProgramNumber) {
  MIDI.sendProgramChange(inProgramNumber, inChannel);
}

void handleMidiEventAfterTouchChannel(byte inChannel, byte inPressure) {
  MIDI.sendAfterTouch(inPressure, inChannel);
}

void handleMidiEventPitchBend(byte inChannel, int inPitchValue) {
  MIDI.sendPitchBend(inPitchValue, inChannel);
}

void handleMidiEventSysEx(byte* inData, unsigned inSize)
{
    MIDI.sendSysEx(inSize, inData, true);
}

void handleMidiEventTimeCodeQuarterFrame(byte inData)
{
    MIDI.sendTimeCodeQuarterFrame(inData);
}

void handleMidiEventSongPosition(unsigned inBeats)
{
    MIDI.sendSongPosition(inBeats);
}

void handleMidiEventSongSelect(byte inSongNumber)
{
    MIDI.sendSongSelect(inSongNumber);
}
void handleMidiEventTuneRequest()
{
    MIDI.sendTuneRequest();
}

void handleMidiEventSystemReset() {
  MIDI.sendSystemReset();
}

void handleMidiEventClock() {
  MIDI.sendClock();
}

void handleMidiEventStart() {
  MIDI.sendStart();
}

void handleMidiEventStop() {
  MIDI.sendStop();
}

void handleMidiEventContinue() {
  MIDI.sendContinue();
}


The next step will be to work on the code to change the CC and PC messages coming from the XB5 organ to control the functions in the B4000+ sound module.
 

Continued in Part 3.

References

https://github.com/FortySevenEffects/arduino_midi_library/wiki/Using-Callbacks
https://github.com/othmar52/mioxm-workaround

 

Disclaimer: This is my personal blog. Views expressed in my posts are my own and not of my employer. The information provided comes with no warranty. I cannot be held responsible for the content of external websites. Any practical work you undertake is done at your own risk. Please make health and safety your number one priority.

Comments