frac32buffer Delay time bias
frac32buffer 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
bool32.rising Clock input
frac32 Delay time, fraction of the referenced delay writer's time
frac32buffer Delay output
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 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();
}
old_24ppq = inlet_24ppq;
ktimer++;
uint32_t tmp_time;
int32_t time_mod;
uint32_t tmp_d;
uint32_t tmp_d_limited;
uint32_t tmp_di;
uint32_t tmp_w1;
uint32_t tmp_w2;
int32_t tmp_a1;
int32_t tmp_a2;
int32_t tmp_r;
if (clock_source != CLOCK_SOURCE_internal) {
tmp_time = sync_time;
} else {
tmp_time = param_time;
}
time_mod = ___SMMUL((tmp_time + inlet_time) << 3, inlet_tmod << 2);
tmp_d = __USAT(tmp_time + inlet_time + time_mod, 27);
outlet_time = tmp_d;
// Must limit the length!
tmp_d_limited = tmp_d >> (27 - attr_delayname.LENGTHPOW);
if (tmp_d_limited > attr_delayname.LENGTHMASK - BUFSIZE) {
tmp_d_limited = attr_delayname.LENGTHMASK - BUFSIZE;
tmp_d = 0;
}
tmp_di = attr_delayname.writepos - tmp_d_limited - BUFSIZE + buffer_index - 1;
tmp_w1 = (tmp_d << (attr_delayname.LENGTHPOW + 3)) & 0x3FFFFFFF;
tmp_w2 = (1 << 30) - tmp_w1;
tmp_a1 = attr_delayname.array[tmp_di & attr_delayname.LENGTHMASK] << 16;
tmp_a2 = attr_delayname.array[(tmp_di + 1) & attr_delayname.LENGTHMASK] << 16;
tmp_r = ___SMMUL(tmp_a1, tmp_w1);
tmp_r = ___SMMLA(tmp_a2, tmp_w2, tmp_r);
outlet_out = tmp_r;
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();
}
}