bool32.rising Clock input
frac32 Delay time bias
frac32 Delay time modulation
int32 Override clocksource attribute, 0,1,2,3,4 = attr,int,ext,midi,midi-omni
int32 Override clockmul parameter, values above 0 overrides clockmul
int32 Override clockdiv parameter, values above 0 overrides clockdiv
frac32buffer Delay output
frac32 Delay time, fraction of the referenced delay writer's time
int32 clockmul
int32 clockdiv
frac32.u.map Internal delay time
objref delayname
combo clocksource
combo device
combo smooth
//#define DEBUGF LogTextMessage
#define DEBUGF(...) \
{}
enum {
CLOCK_SOURCE_internal = 0,
CLOCK_SOURCE_external = 1,
CLOCK_SOURCE_midi = 2,
CLOCK_SOURCE_midi_omni = 3
} CLOCK_SOURCE;
int32_t start_sequence;
int32_t clock_source;
int32_t clock_multiplier;
int32_t clock_divider;
uint32_t sync_count; // At least N clocks before deriving synced time
uint32_t sync_time; // Synced time expressed in fractional delay length
uint32_t ktimer; // 3kHz timer
uint32_t last_ktime;
float delay_length_cpl; // cache result of 1.0 / lenght of delay line
uint32_t old_24ppq;
__attribute__((always_inline)) __STATIC_INLINE float Q27ToF(int32_t op1) {
float fop1 = *(float *)(&op1);
__ASM volatile("VCVT.F32.S32 %0, %0, 27" : "+w"(fop1));
return (fop1);
}
__attribute__((always_inline)) __STATIC_INLINE int32_t FToQ27(float fop1) {
__ASM volatile("VCVT.S32.F32 %0, %0, 27" : "+w"(fop1));
int32_t r = *(int32_t *)(&fop1);
return (r);
}
__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) {
float period;
int32_t kperiod;
#if attr_smooth > 0
kperiod = moving_average(&avg, ktimer - last_ktime);
#else
kperiod = ktimer - last_ktime;
#endif
last_ktime = ktimer;
period = kperiod * 16 * clock_divider;
period = period / clock_multiplier;
if (period < 1.0f) {
period = 1.0f;
}
if (sync_count < 1) {
sync_count++;
} else {
sync_time = FToQ27(period * delay_length_cpl);
}
}
#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
start_sequence = 0;
clock_source = CLOCK_SOURCE_internal;
clock_multiplier = 1;
clock_divider = 12;
sync_count = 0;
sync_time = 0;
ktimer = 0;
last_ktime = 0;
delay_length_cpl = 1.0f / attr_delayname.LENGTH;
old_24ppq = 0;
// Use preset time until synced
if (start_sequence == 0 && clock_source != CLOCK_SOURCE_internal) {
sync_time = param_time;
start_sequence++;
}
#ifdef USE_SYNC_HOLDOFF
// If too long between clocks (likely absence of clock), require two successive
// clocks to resync. Use max delay time as the minimum clock interval (using 5
// seconds for now)?
if (ktimer - last_ktime > (5 * 3000)) {
sync_count = 0;
}
#endif
// Live overrides for the clock source and clock divider and multiplier
// parameters. Allow clock selection without restarting the patch is good :)
// (The int32 spinbox is not CC assignable...(bug?))
if (inlet_clkoverride > 0) {
clock_source = inlet_clkoverride - 1;
if (clock_source > CLOCK_SOURCE_midi_omni) {
clock_source = CLOCK_SOURCE_midi_omni;
} else if (clock_source < CLOCK_SOURCE_internal) {
clock_source = CLOCK_SOURCE_internal;
}
} else {
clock_source = attr_clocksource;
}
if (inlet_divoverride > 0) {
clock_divider = inlet_divoverride;
if (clock_divider < 1) {
clock_divider = 1;
}
} else {
clock_divider = param_clockdiv;
}
if (inlet_muloverride > 0) {
clock_multiplier = inlet_muloverride;
if (clock_multiplier < 1) {
clock_multiplier = 1;
}
} else {
clock_multiplier = param_clockmul;
}
if (clock_source == CLOCK_SOURCE_external && inlet_24ppq && !old_24ppq) {
on_clock();
}
int32_t tmp_time;
if (clock_source != CLOCK_SOURCE_internal) {
tmp_time = sync_time;
} else {
tmp_time = param_time;
}
// tmod is scaled to cover the delay range
int32_t time_mod = ___SMMUL((tmp_time + inlet_time) << 3, inlet_tmod << 2);
int32_t total_time = __USAT(tmp_time + inlet_time + time_mod, 27);
uint32_t length = total_time >> (27 - attr_delayname.LENGTHPOW);
if (length > attr_delayname.LENGTHMASK - BUFSIZE) {
length = attr_delayname.LENGTHMASK - BUFSIZE;
}
uint32_t delay = attr_delayname.writepos - length - BUFSIZE;
outlet_time = total_time;
old_24ppq = inlet_24ppq;
ktimer++;
outlet_out = attr_delayname.array[(delay++) & attr_delayname.LENGTHMASK];
if (clock_source == CLOCK_SOURCE_midi_omni ||
(clock_source == CLOCK_SOURCE_midi &&
midi_device_test(dev, port, attr_device) == 1)) {
if (status == MIDI_TIMING_CLOCK) {
on_clock();
}
}