This is my first post, so please forgive any oversights!
I wanted to hook my Yamaha DTXplorer drum kit to our Guitar Hero drum kit's midi input, and play the songs in the game using some "real drums".
More expensive drum kits can assign midi note numbers to the drum pads and cymbals, but the relatively inexpensive DTXplorer's brain requires some external remapping of the (standard) drum midi notes to those expected by the (Guitar Hero or Rock Band) video game. I'd been using my laptop plus the $10 eDrum MIDI Mapper software to do this, but it was time to put the laptop back to work doing it's "regular" job.
Thanks to some forum topics on how to interface to Midi (see MIDI under Communications in the Reference) and the sketch included below, I was able to get my Arduino to "echo" my e-drum hits over to the game controller's midi input. Strangely, though, I was limited to only about two drum hits per second. This was going to be totally impractical, not to mention that it was impossible for me to believe the AtMega368 couldn't process more than one note per 500 milliseconds! Turns out there is a poorly-documented catch in the Midi specs.
The sketch I've included is (as far as I can tell) different from most any applications I've been able to find using Google, in that it can handle the Midi Note-On mode. Basically, the midi note-on mode gets enabled with the first Note-On command, and although the Midi Note-On message is three bytes (command, note #, velocity) in length, subsequent notes may be sent without the command (the first of the three bytes). At least, that is, until some other command comes along which will terminate the Note-On mode. (The exception to this are the Midi Real-Time commands.)
Anyway, if anyone is interested I can put some detailed construction info up here on how I did it all "on the cheap". ($5 for Midi cables, $15 for Guitar Hero wireless drum brain, $8 for the enclosure and battery wires, $2 for resistors, $5 for a breadboard, and the cost of the Arduino itself). By simply adding a switch to Arduino you can easily change the mapping to best suit which game you're using at the moment (Guitar Hero or Rock Band) and preferred drum pad layout.
// Midi Drum Note Re-mapping utility
// All commands and data are assumed to be on channel 10.
// Global Variables:
byte note = 0; // The MIDI note value received and to be played
byte velocity = 0; // Midi note velocity
byte stat = 0; // Midi command/status byte
int ledPin = 13; // LED connected to digital pin 13
boolean noteMode = false; // Keep track of whether we have multiple notes going or not.
void setup()
{
// Set MIDI baud rate:
Serial.begin(31250); // bit rate for Midi = (31250)
// Initialize the digital pin as an output:
pinMode(ledPin, OUTPUT);
}
void loop()
{
digitalWrite(ledPin, LOW); // set the LED on
noteMode = false;
stat = getCmd(); // wait for actual Midi Status byte
switch (stat) // Now we'll take action based on the midi command.
{
case 248: // if we received a Timing Clock, echo it
Serial.print(stat, BYTE);
break;
case 254: // if we received an "Active Sense", echo it and turn LED On
digitalWrite(ledPin, HIGH);
Serial.print(stat, BYTE);
break;
case 153: // if we received a midi Note ON, turn on LED and enter the "note on" mode.
noteMode = true; // from now on just echo note & velocity received until we exit "Note ON" mode
// when we receive some (non-data or Real Time) command.
digitalWrite(ledPin, HIGH);
do {
note = getData();
velocity = getData();
switch (note) // re-map note as necessary (this map is for Rock Band)
{
case 31: // rx snare
note = 38; // red
break;
case 33: // rx kick
note = 36; // purple
break;
case 34: // rx snare open rim
note = 38; // red
break;
case 37: // rx snare close rim
note = 38; // red
break;
case 42: // rx hi hat close
note = 46; // yellow
break;
case 46: // rx hi hat open
note = 46; // yellow
break;
case 47: // rx mid tom
note = 48; // blue
break;
case 48: // rx hi tom
note = 46; // yellow
break;
case 43: // rx low tom
note = 45; // green
break;
case 49: // rx crash
note = 46; // yellow
break;
case 51: // rx ride
note = 49; // orange
break;
case 52: // rx ride edge
note = 49; // orange
break;
case 57: // rx crash edge
note = 46; // yellow
break;
default: // if no re-map matches, leave note as-is.
break;
} // end note remapping switch
noteOn(stat, note, velocity);
note = 0;
velocity = 0;
}
while (noteMode = true);
stat = 0;
break;
case 240: // received System Exclusive GM System ON (includes four bytes data + EOX)
stat = getCmd(); // wait for EOX command (value = 247)
break;
case 247: // received EOX; no need to do anything
break;
case 250: // received Start
break;
case 251: // received Continue
break;
case 252: // received Stop
break;
case 255: // received System Reset
break;
case 185: // received Note OFF cmd: (grab two bytes data)
note = getData();
velocity = getData();
noteOn(stat, note, velocity);
note = 0;
velocity = 0;
stat = 0;
break;
default: // if we have no command match, discard it for this version.
break;
stat = 0;
} // end command switch
// digitalWrite(ledPin, LOW); // set the LED off
} // end loop
//////////////////////////////////////////////////////////////////////////////////////////////////
// verify that an expected incoming data byte is < 127;
// if not, assume a System Realtime msg and get the next byte
byte getData() {
byte value;
value = 255; // Begin assuming "non-data" value received to ensure that only
// an incoming value <= 127 breaks out of the loop.
do
{
if (Serial.available() > 0)
{
value = Serial.read();
if (value > 127) {noteMode = false;}
}
}
while (value > 127); // waiting for data byte only (reject any commands)
return value;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Wait for and verify that an expected incoming command byte is > or = 128
byte getCmd() {
byte value;
value = 0; // ensure that only an incoming value > 127 breaks out of the loop.
do
{
if (Serial.available() > 0)
{
value = Serial.read();
}
}
while (value < 128); // waiting for Command byte
noteMode = false; // received command byte, so by default we're out of Note ON mode.
return value;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// plays a three byte MIDI note.
void noteOn(byte midistat, byte midi1, byte midi2)
{
Serial.print(midistat, BYTE); // send midi note ON/OFF command, channel 10
Serial.print(midi1, BYTE); // send out note #
Serial.print(midi2, BYTE); // send out velocity
}