Line 6 DL4 Dotted Eighth Tap Tempo Mod

Hey guys,

So almost a year ago now, my brother Brian and I decided to make a dotted eighth tap tempo feature available on a Line 6 DL4 that I use on my guitar rig. We used an Arduino platform to prototype the project. I have been successfully using the mod for about 8 months now. Check out these videos for more info:

Here’s the full source code for a standard Arduino or a standalone ATMega328 chip:

[EDIT]
I noticed there was a bug in the original code I posted. I went through and parsed a lot of stuff down in my original source code but I apparently made some errors. This code is the original source code and it is fully functional! 🙂

/*
 DL4 Dotted Eighth Tap Tempo
 Written By:
 Joe Schoolcraft
 Brian Schoolcraft
 
 Written for Brian Wampler of Wampler Pedals
 
 Hardware Details:
 Pin 10 : Tempo Out Signal - connected to base of NPN
 Pin 13 : Momentary switch to ground
 
*/

//Pin Definition

#define TEMPO_OUT 10

#define TEMPO_BUTTON 13

//Constant inputs
#define DEBOUNCE_DELAY 40     // length of debounce "wait period"
#define HOLD_DELAY 750
#define RESET_TIME 2002       // Time it takes for the current timer and tap count to reset if only one tap is put in
#define PULSES 4
#define OUTPUT_TIME 40

// Variables that will change
byte outputCount = 0;
byte tapCount = 0;
byte intervalCount = 0;
byte reading  = HIGH;         // temporary button state storage (used in debounce routine)
byte tapState = HIGH;         // current tap button state
byte lastTapState = HIGH;     // the last tap button state 
byte lastButtonState = LOW;   // the previous reading from the input pin
byte pulseCount = 4;          // Number of pulses sent to pedal
byte divisor;

int alpha;
int currentTimer[3];                 // array of most recent tap counts
int bpm;

float tempoScale = 0.75;             // Tempo scale factor (dotted eighth, etc)
float interval;
 
unsigned long timeoutTime = 0;       // this is when the timer will trigger next 
unsigned long indicatorTimeout;      // sets the turn off time for the LED
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long lastTap;               // when the last tap happened 

void setup()
{
  pinMode( TEMPO_BUTTON, INPUT );    // tap button - press it to set the tempo 
  pinMode( TEMPO_OUT, OUTPUT );      // Tempo output to pedal

//  Serial.begin(115200);
}

void loop()
{
  reading = digitalRead(TEMPO_BUTTON);
  tapState = debounce(reading, lastButtonState, &lastDebounceTime, DEBOUNCE_DELAY, HOLD_DELAY);
  lastButtonState = reading;  // save the state for comparison on the next loop

  // read the state of the switch into a local variable:
  if( tapState == LOW && tapState != lastTapState )  {  // button just changed from HIGH to LOW
    if (tapCount != 0) {                                // We've already recorded a tap in this set
    
      tapCount++;
      
      for(int n = 2; n > 0; n--){
        currentTimer[n] = currentTimer[n-1];
      }
      
      currentTimer[0] = millis() - lastTap;  // calculate the tap interval
      lastTap = millis();                    // Store the tap time for the next loop
      pulseCount = 0;                        // reset pulse count to zero
      outputCount = 0;                       // reset pulse count to zero
      intervalCount = tapCount - 1;
      alpha = currentTimer[0] + currentTimer[1] + currentTimer[2];
      divisor = constrain(intervalCount,0,3);
      interval = alpha/divisor;
      bpm = 1/(interval/60000);
      interval = (60000/bpm) * tempoScale;
    }
    else {                 // We haven't seen a tap since the last output to the pedal
      lastTap = millis();  // Store the tap time for the next loop
      tapCount++;
    }  
  }

  lastTapState = tapState; // keep track of the state for the next loop

  // check for timer timeout 
  if (millis() >= timeoutTime && pulseCount < PULSES)  // it's time to send the output pulse
  {
    pulseCount++; // keep track of how many pulses we've sent
    outputCount++;

    indicatorTimeout = millis() + OUTPUT_TIME;  // set the time for the output to turn back off 

    timeoutTime = millis() + (interval); // set the time for the next output pulse

    // clear all the taps we've recorded
  }

  // display the button state on LED 2 

  // send the output pulse to the LED and the transistor

  if( millis() < indicatorTimeout ) {
    digitalWrite(TEMPO_OUT, HIGH );
  }
  else {
    digitalWrite(TEMPO_OUT, LOW );
  }

  if (outputCount > PULSES-1 && currentTimer[0] != 0 | millis() - lastTap > RESET_TIME){
    clearTaps();
  }    
//         Serial.print(tapCount);
//               Serial.print('\t');
//         Serial.print(bpm);
//                Serial.print('\t');
//         Serial.print(interval);
//                Serial.print('\t');
//         Serial.print(millis());
//                Serial.print('\t');
//         Serial.print(pulseCount);
//                Serial.print('\t');
//         Serial.print(outputCount);
//                Serial.print('\t');
//         Serial.print(currentTimer[1]);
//                Serial.print('\t');
//         Serial.println(tempoScale);
}

//Clears the currentTimer array
void clearTaps(){
  for(int t = 0; t < 3; t++){
      currentTimer[t] = 0;
      if(currentTimer[t + 1] == 0){
        break;
      }
    }
    tapCount = 0;
}

//Debounces any footswitch
byte debounce(byte _reading, byte _lastButtonState, unsigned long *_lastDebounceTime, int _holdDelay, byte dbounceTime)
{
  byte state = HIGH;
  if (_reading != _lastButtonState) {  // button state just changed
    *_lastDebounceTime = millis();   // reset the debouncing timer
  }
  if ((millis() - *_lastDebounceTime) >= _holdDelay && state == HIGH) {
    // whatever the reading is at, it's been there for longer
    // than the hold delay, so take it as the actual current state for use in the rest of the script
    state = _reading;
  }
  else{
    state = HIGH;
  }
  if ((millis() - *_lastDebounceTime) >= dbounceTime && state == LOW){
    state = _reading;
    clearTaps();
  }
  return state;
}
Advertisements