Mapping the Problematique - a one-trick MIDI pedal

Started by easilyamused, April 18, 2017, 10:15:01 PM

Previous topic - Next topic

easilyamused

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;
}




Ice-9

Sounds like they have numbered the programs from 1-128 but as midi starts at 0 it should be 0-127, so I would guess all program change numbers are 1 out. Could this be the answer ?
www.stanleyfx.co.uk

Sanity: doing the same thing over and over again and expecting the same result. Mick Taylor

Please at least have 1 forum post before sending me a PM demanding something.

easilyamused

Quote from: Ice-9 on April 19, 2017, 04:55:04 AM
Sounds like they have numbered the programs from 1-128 but as midi starts at 0 it should be 0-127, so I would guess all program change numbers are 1 out. Could this be the answer ?
Yes that sounds plausible. It works, so maybe I'll go with that! Thanks!


Sent from my iPhone using Tapatalk

ElectricDruid

That'd be exactly the same as the MIDI channels, which are "labelled" 1-16, but in practice are actually 0-15.

MIDI Channel 1 being 0000 is such second nature to me now that I don't even notice how odd that is.

Tom

easilyamused

Quote from: ElectricDruid on April 19, 2017, 06:24:04 PM
That'd be exactly the same as the MIDI channels, which are "labelled" 1-16, but in practice are actually 0-15.

MIDI Channel 1 being 0000 is such second nature to me now that I don't even notice how odd that is.

Tom
Thanks Tom! I knew someone around here would have the definitive answer.


Sent from my iPhone using Tapatalk