I've been thinking about this one for ages and finally got around to trying a prototype... The idea is to use a small micro-controller (ATTiny85, Uno etc) to send the appropriate MIDI sequence to my Whammy 5 pedal for playing Muse's "Map of the Problematique" -
see, told you it was one-trick

The code seems to work just fine, but I discovered that the Program Change ID for Octave up and Octave down seem to be incorrect in the Digitech manual. Manual states PC=2 is oct down, and PC=8 for oct up. In my tests and using and confirmed on an external midi watcher app (Midi Wrench) it needs PC=1 and PC=7 respectively. Has anyone else found this?
Here's my code which can be easily tested if you have an Uno, Midi Shield and a Whammy 5. There's a bit of redundant stuff in there, and no promises on compactness or best practice (I'm a Mechanical Engineer)
/*
------------
aM U S Ed
------------
"Map of the Problematique" Sequencer for Digitech Whammy5
using ARDUINO UNO to send required MIDI Program Changes and Control Changes
EasilyAmused, April 2017
Hardware:
Arduino Uno
Sparkfun Midi Shield
Digitech Whammy 5
Midi cable
TO DO:
Use a toggle switch to choose between external and internal midi clock sync
- use a digital pin to detect setting, put checks in the code to activate
This ^^ is somewhat moot given this is only for one song, and the song
played at 125 bpm... Would be purest to forego this external sync entirely!?
*/
#include <elapsedMillis.h>
elapsedMillis timeElapsed;
const int CHANNEL = 1; // MIDI Channel to output to. Set Whammy to listen on this, or OMNI...
const int CONTNUM = 11; // Expression pedal code on Whammy5 is 11
const int running = 1;
const int stopped = 0;
const int REDLED = 7; // Using MIDI Shield for testing
const int SOURCE = 4;
const int STOMP = 2; // use interrupt on this pin (start/stop switch)
boolean EXT_CLOCK = false; // External midi clock ("true" if using an external clock)
unsigned int interval = 120; // 120ms interval should give 1/4 notes at 125 bpm. Do the math...
//60L * 1000 * 1000 / BPM / 4 (in microsec)
byte data; // for holding incoming MIDI data byte (TBD)
byte midi_start = 0xfa;
byte midi_stop = 0xfc;
byte midi_clock = 0xf8;
byte midi_continue = 0xfb;
int midi_state = running; // running/stopped
int tock = 0; // start of a 16th note
int tick_counter = 0; // Midi clock is defined by 6 pulses (ticks) per 16th note
int prog[16]={8,8,2,2,2,2,8,8,2,2,2,2,8,8,2,2}; // PC 2 = octave down, PC 8 = octave up
int pitch[16]={127,0,127,0,127,0,127,0,127,0,127,0,127,0,127,127}; // expression pedal position
int i = 0; //index for arrays
void setup()
{
pinMode(STOMP, INPUT_PULLUP);
attachInterrupt(2, stomp_handler, CHANGE);
pinMode(SOURCE, INPUT);
pinMode(REDLED, OUTPUT);
digitalWrite(REDLED,HIGH); // sparkfun midi shield HIGH ==> LED is OFF
Serial.begin(31250);
}
void loop() {
/*
if (Serial.available() > 0) { // FOR EXTERNAL MIDI CLOCK - TBD
data = Serial.read(); // FOR EXTERNAL MIDI CLOCK - TBD
}
*/
while(true) { // FOR INTERNAL MIDI CLOCK ONLY TEST
data = midi_clock; // FOR INTERNAL MIDI CLOCK ONLY TEST
if (data == midi_clock) {
if (++tick_counter > 5) {
tock = 1;
tick_counter = 0;
}
}
if (data == midi_start) {
midi_state = running;
i = 0; // reset to beginning of 16 step sequence (bar)
tick_counter = 5;
}
if (data == midi_continue) {
midi_state = running;
tick_counter = 5;
}
if (data == midi_stop) {
midi_state = stopped;
}
if ( (tock == 1) && (midi_state == running) ) {
while(timeElapsed < interval) {
// twiddle thumbs until timing is right
}
if (!EXT_CLOCK) {
syncClock();
}
progChange(CHANNEL, prog[i]-1);
controlChange(CHANNEL, CONTNUM, pitch[i]);
if (++i > 15) {
i=0;
digitalWrite(REDLED,LOW);
delay(20);
digitalWrite(REDLED,HIGH);
} // loop over the array (sequence length = 16 x 16th notes)
tock = 0;
timeElapsed = 0;
}
if(digitalRead(SOURCE)) {
EXT_CLOCK=true;
digitalWrite(0,HIGH);
}
else {
EXT_CLOCK=false;
digitalWrite(0,LOW);
}
} // end of if loop for midi reading
} // end of main loop()
void progChange(int newChannel, int prognum) {
Serial.write( 0xC0 | (newChannel-1) );
Serial.write(prognum);
}
void controlChange(int newChannel, int controlNumber, int controlValue) {
Serial.write( 0xB0 | (newChannel-1) );
Serial.write(controlNumber);
Serial.write(controlValue);
}
void syncClock() {
Serial.write(midi_clock);
}
void stomp_handler() // toggles the midi_state given a stomp switch
{
static unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = millis();
// If interrupts come faster than 200ms, assume it's a bounce and ignore
if (interrupt_time - last_interrupt_time > 200)
{
if (midi_state == running) {
midi_state = stopped;
}
else {
midi_state = running;
i = 0;
tick_counter = 5;
}
}
last_interrupt_time = interrupt_time;
}