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
Comments
Post a Comment