Tap tempo issue

Status
Not open for further replies.

Qcontinuum

New member
Good evening everyone.
I am working on a midi Step sequencer and I am implementing the code that I attach to follow in order to implement a "tap tempo" (pressing a button several times calculates a number of bpm that I use to space the steps).
The code is a found, I have eliminated parts that I didn't need (midi output and clock signal in voltage output).
My question is the following:
Everything works and is implemented with the Step sequencer code but has a small anomaly.
He reads at least 4 keystrokes before doing the calculation and updating the "bpm" variable only that if the pressures are very fast the calculation is performed very quickly, if they are slow the calculation is as if it were performed more slowly. however, everything updates "bpm" with its own tempo.
My need would be: if I press the button 4 times the sketch should update "bpm" in synch with the time I have beaten (example: 4 tap, he averages me milliseconds and updates me bpm to 80) but the time that it takes to update the variable I would like it to be the same as the bpm it detected.
So: TAP / x Millis TAP / x Millis TAP / x Millis TAP / x Millis ..... And within the bpm value I should return "Bpm is equivalent to a value" (which it does now but with its own time, forcing me to perform a resynch on the Step sequencer).


Code:
 #include <TimerOne.h>

/*
 * For all features: if you do not define the pin, the feature will be disabled!
 */

/*
 * FEATURE: TAP BPM INPUT
 */
#define TAP_PIN 32
#define TAP_PIN_POLARITY RISING

#define MINIMUM_TAPS 4
#define EXIT_MARGIN 150 // If no tap after 150% of last tap interval -> measure and set

/*
 * FEATURE: DIMMER BPM INPUT
 */
#define DIMMER_INPUT_PIN 35

#define DIMMER_CHANGE_MARGIN 10 // Big value to make sure this doesn't interfere. Tweak as needed.

#define BLINK_OUTPUT_PIN 31
#define BLINK_PIN_POLARITY 0  // 0 = POSITIVE, 255 - NEGATIVE
#define BLINK_TIME 4 // How long to keep LED lit in CLOCK counts (so range is [0,24])

/*
 * FEATURE: EEPROM BPM storage
 */
#define EEPROM_ADDRESS 0 // Where to save BPM
#ifdef EEPROM_ADDRESS
#include <EEPROM.h>
#endif


/*
 * GENERAL PARAMETERS
 */
#define CLOCKS_PER_BEAT 24
#define MINIMUM_BPM 100 // Used for debouncing
#define MAXIMUM_BPM 4000 // Used for debouncing

long intervalMicroSeconds;
int bpm;  // BPM in tenths of a BPM!!

boolean initialized = false;
long minimumTapInterval = 60L * 1000 * 1000 * 10 / MAXIMUM_BPM;
long maximumTapInterval = 60L * 1000 * 1000 * 10 / MINIMUM_BPM;

volatile long firstTapTime = 0;
volatile long lastTapTime = 0;
volatile long timesTapped = 0;

volatile int blinkCount = 0;

int lastDimmerValue = 0;

boolean playing = false;
long lastStartStopTime = 0;

void setup(){

 //Serial.begin(9600);
  //  Set MIDI baud rate:

  // Set pin modes
#ifdef BLINK_OUTPUT_PIN
  pinMode(BLINK_OUTPUT_PIN, OUTPUT);
#endif


#ifdef DIMMER_INPUT_PIN
  pinMode(DIMMER_INPUT_PIN, INPUT);
#endif

#ifdef EEPROM_ADDRESS
  // Get the saved BPM value from 2 stored bytes: MSB LSB
  bpm = EEPROM.read(EEPROM_ADDRESS) << 8;
  bpm += EEPROM.read(EEPROM_ADDRESS + 1);
  if (bpm < MINIMUM_BPM || bpm > MAXIMUM_BPM) {
    bpm = 1200;
  }
#endif

#ifdef TAP_PIN
  // Interrupt for catching tap events
  attachInterrupt(digitalPinToInterrupt(TAP_PIN), tapInput, TAP_PIN_POLARITY);
#endif

#ifdef DIMMER_INPUT_PIN
  // Initialize dimmer value
  lastDimmerValue = analogRead(DIMMER_INPUT_PIN);
#endif
}

void tapInput() {
  long now = micros();
  if (now - lastTapTime < minimumTapInterval) {
    return; // Debounce
  }

  if (timesTapped == 0) {
    firstTapTime = now;
  }

  timesTapped++;
  lastTapTime = now;
  Serial.println("Tap!");
}

void startOrStop() {
  if (!playing) {
    Serial.println("Start playing");

  } else {
    Serial.println("Stop playing");

  }
  playing = !playing;
}

void sendClockPulse() {
  // Write the timing clock byte


  blinkCount = (blinkCount + 1) % CLOCKS_PER_BEAT;
  if (blinkCount == 0) {
    // Turn led on
#ifdef BLINK_OUTPUT_PIN
    analogWrite(BLINK_OUTPUT_PIN, 255 - BLINK_PIN_POLARITY);
#endif

  } else {

#ifdef BLINK_OUTPUT_PIN
    if (blinkCount == BLINK_TIME) {
      // Turn led on
      analogWrite(BLINK_OUTPUT_PIN, 0 + BLINK_PIN_POLARITY);
    }
#endif
  }
}

void updateBpm(long now) {
  // Update the timer
  long interval = calculateIntervalMicroSecs(bpm);
  Timer1.setPeriod(interval);

#ifdef EEPROM_ADDRESS
  // Save the BPM in 2 bytes, MSB LSB
  EEPROM.write(EEPROM_ADDRESS, bpm / 256);
#endif

  Serial.print("Set BPM to: ");
  Serial.print(bpm / 10);

}

long calculateIntervalMicroSecs(int bpm) {
  // Take care about overflows!
  return 60L * 1000 * 1000 * 10 / bpm / CLOCKS_PER_BEAT;
}

void loop()
{
  long now = micros();

#ifdef TAP_PIN
  /*
   * Handle tapping of the tap tempo button
   */
  if (timesTapped > 0 && timesTapped < MINIMUM_TAPS && (now - lastTapTime) > maximumTapInterval) {
    // Single taps, not enough to calculate a BPM -> ignore!
    //    Serial.println("Ignoring lone taps!");
    timesTapped = 0;
  } else if (timesTapped >= MINIMUM_TAPS) {
    long avgTapInterval = (lastTapTime - firstTapTime) / (timesTapped - 1);
    if ((now - lastTapTime) > (avgTapInterval * EXIT_MARGIN / 100)) {
      bpm = 60L * 1000 * 1000 * 10 / avgTapInterval;
      updateBpm(now);
  
      // Update blinkCount to make sure LED blink matches tapped beat
      blinkCount = ((now - lastTapTime) * 24 / avgTapInterval) % CLOCKS_PER_BEAT;

      timesTapped = 0;
    }
  }
#endif

/*#ifdef DIMMER_INPUT_PIN
 
  int curDimValue = analogRead(DIMMER_INPUT_PIN);
  if (curDimValue > lastDimmerValue + DIMMER_CHANGE_MARGIN
      || curDimValue < lastDimmerValue - DIMMER_CHANGE_MARGIN) {
    // We've got movement!!
    bpm = map(curDimValue, 0, 1023, MINIMUM_BPM, MAXIMUM_BPM);

    updateBpm(now);
    lastDimmerValue = curDimValue;
  }
#endif*/


}

I'm working with teensy 3.5 and teensyduino.
I know it's a bit confusing explanation, if you have questions I'm here.
 
Status
Not open for further replies.
Back
Top