bpm_detect

BPM detector with smoothing
Author: Are Leistad
License: BSD
Github: drj/seq/bpm_detect.axo

Inlets

frac32 Time base input

Outlets

int32.positive BPM integer

int32.positive BPM tenths

int32.positive Samples

Attributes

combo clocksource

combo device

combo smooth

Declaration
enum {
  CLOCK_SOURCE_external = 0,
  CLOCK_SOURCE_midi = 1,
  CLOCK_SOURCE_midi_omni = 2
} CLOCK_SOURCE;

uint32_t sync_count;
uint32_t ktimer;
uint32_t last_ktime;
int32_t bpm_int;
int32_t bpm_tenths;
int32_t samples;
int32_t old_inlet_in;

__attribute__((always_inline)) __STATIC_INLINE int
midi_device_test(midi_device_t dev, uint8_t port, int selected_dev,
                 uint8_t selected_port) {
  if ((selected_dev == MIDI_DEVICE_OMNI) ||
      (selected_dev == dev && selected_port == port))
    return 1;
  return 0;
}

#if attr_smooth > 0
#define FIFO_EXP attr_smooth
#define FIFO_LEN (1 << FIFO_EXP)
#define FIFO_MASK (FIFO_LEN - 1)
typedef struct _AveragerContext {
  int32_t fifo[FIFO_LEN];
  int32_t fifo_i;
  int32_t acc;
} AveragerContext;
AveragerContext avg;
__attribute__((always_inline)) __STATIC_INLINE int32_t
moving_average(AveragerContext *avg, int32_t new_value) {
  avg->fifo_i = (avg->fifo_i + 1) & FIFO_MASK;
  avg->acc -= avg->fifo[avg->fifo_i];
  avg->acc += new_value;
  avg->fifo[avg->fifo_i] = new_value;
  return avg->acc >> FIFO_EXP;
}
#endif

void on_clock(void) {
  if (sync_count < 1) {
    sync_count++;
  } else {
#if attr_smooth > 0
    int32_t kperiod = moving_average(&avg, ktimer - last_ktime);
#else
    int32_t kperiod = ktimer - last_ktime;
#endif
    last_ktime = ktimer;
    if (kperiod < 1) {
      kperiod = 1;
    }
    float bpm_float = 180000.0f / kperiod;
    bpm_int = bpm_float;
    bpm_tenths = (bpm_float - bpm_int) * 10;
    samples = kperiod * 16;
  }
  last_ktime = ktimer;
}
Init
#if attr_smooth > 0
for (avg.fifo_i = 0; avg.fifo_i < FIFO_LEN; avg.fifo_i++) {
  avg.fifo[avg.fifo_i] = 0;
}
avg.fifo_i = 0;
avg.acc = 0;
#endif
sync_count = 0;
ktimer = 0;
last_ktime = 0;
bpm_int = 0;
bpm_tenths = 0;
samples = 0;
old_inlet_in = 0;
Control Rate
if (attr_clocksource == CLOCK_SOURCE_external && inlet_in > 0 &&
    old_inlet_in <= 0) {
  on_clock();
}

old_inlet_in = inlet_in;
outlet_bpm = bpm_int;
outlet_dec = bpm_tenths;
outlet_samples = samples;
ktimer++;
Midi Handler
if (attr_clocksource == CLOCK_SOURCE_midi_omni ||
    (attr_clocksource == CLOCK_SOURCE_midi &&
     midi_device_test(dev, port, attr_device) == 1)) {
  if (status == MIDI_TIMING_CLOCK) {
    on_clock();
  }
}

Privacy

© 2024 Zrna Research