@@ -29,6 +29,7 @@
#include "adc.h"
#include "util.h"
#include "ftdi.h"
#include "synced_clock.h"

// this
#include "conf_board.h"
@@ -70,7 +71,7 @@ const u16 SCALES[24][16] = {
};

typedef enum {
mTrig, mMap, mSeries
mTrig, mMap, mSeries, mClock
} edit_modes;

typedef enum {
@@ -89,6 +90,7 @@ typedef struct {
u16 cv_steps[2][16];
u16 cv_curves[2][16];
u8 cv_probs[2][16];
sc_config sc_conf;
} whale_pattern;

typedef struct {
@@ -97,6 +99,9 @@ typedef struct {
u8 series_start, series_end;
u8 tr_mute[4];
u8 cv_mute[2];
u8 direct_clock;
u8 keep_tempo;
u8 keep_div_mult;
} whale_set;

typedef const struct {
@@ -134,8 +139,11 @@ u16 clip;
u16 *param_dest;
u8 quantize_in;

u8 clock_phase;
u16 clock_time, clock_temp;
synced_clock sclock;
u8 clock_enabled;
volatile u8 clock_on = 0;

u8 series_step;

u16 adc[4];
@@ -159,6 +167,7 @@ static void refresh(void);
static void refresh_mono(void);
static void refresh_preset(void);
static void clock(u8 phase);
static void update_synced_clock(u8 is_series, u8 is_synced);

// start/stop monome polling/refresh timers
extern void timers_set_monome(void);
@@ -173,6 +182,7 @@ static void handler_KeyTimer(s32 data);
static void handler_Front(s32 data);
static void handler_ClockNormal(s32 data);
static void handler_ClockExt(s32 data);
static void synced_clock_callback(void);

static void ww_process_ii(uint8_t *data, uint8_t l);

@@ -202,6 +212,7 @@ void clock(u8 phase) {
pattern = next_pattern;
next_pos = w.wp[pattern].loop_start;
pattern_jump = 0;
update_synced_clock(false, true);
}
// for series mode and delayed pattern change
if(series_jump) {
@@ -243,6 +254,7 @@ void clock(u8 phase) {

series_jump = 0;
series_step = 0;
update_synced_clock(true, true);
}

pos = next_pos;
@@ -446,6 +458,12 @@ void clock(u8 phase) {
// print_dbg_ulong(pos);
}

void update_synced_clock(u8 is_series, u8 is_synced) {
if (is_series)
sc_load_config(&sclock, &w.wp[pattern].sc_conf, !w.keep_tempo, !w.keep_div_mult, is_synced);
else
sc_load_config(&sclock, &w.wp[pattern].sc_conf, true, true, is_synced);
}


////////////////////////////////////////////////////////////////////////////////
@@ -456,7 +474,9 @@ static softTimer_t keyTimer = { .next = NULL, .prev = NULL };
static softTimer_t adcTimer = { .next = NULL, .prev = NULL };
static softTimer_t monomePollTimer = { .next = NULL, .prev = NULL };
static softTimer_t monomeRefreshTimer = { .next = NULL, .prev = NULL };

static softTimer_t synced_clock_off_timer = { .next = NULL, .prev = NULL };
static softTimer_t synced_clock_off_timer2 = { .next = NULL, .prev = NULL };
static softTimer_t synced_clock_off_timer3 = { .next = NULL, .prev = NULL };


static void clockTimer_callback(void* o) {
@@ -465,11 +485,8 @@ static void clockTimer_callback(void* o) {
// e.data = 0;
// event_post(&e);
if(clock_external == 0) {
// print_dbg("\r\ntimer.");

clock_phase++;
if(clock_phase>1) clock_phase=0;
(*clock_pulse)(clock_phase);
sc_process_tap(&sclock, tcTicks);
sc_save_config(&sclock, &w.wp[pattern].sc_conf);
}
}

@@ -663,6 +680,9 @@ static void handler_KeyTimer(s32 data) {
w.wp[x].cv_steps[1][n1] = w.wp[pattern].cv_steps[1][n1];
w.wp[x].cv_curves[1][n1] = w.wp[pattern].cv_curves[1][n1];
w.wp[x].cv_probs[1][n1] = w.wp[pattern].cv_probs[1][n1];
w.wp[x].sc_conf.div = w.wp[pattern].sc_conf.div;
w.wp[x].sc_conf.mult = w.wp[pattern].sc_conf.mult;
w.wp[x].sc_conf.period = w.wp[pattern].sc_conf.period;
}

w.wp[x].cv_mode[0] = w.wp[pattern].cv_mode[0];
@@ -678,6 +698,7 @@ static void handler_KeyTimer(s32 data) {

pattern = x;
next_pattern = x;
update_synced_clock(false, false);
monomeFrameDirty++;

// print_dbg("\r\n saved pattern: ");
@@ -706,7 +727,39 @@ static void handler_ClockNormal(s32 data) {
}

static void handler_ClockExt(s32 data) {
clock(data);
if (data) {
sc_process_tap(&sclock, tcTicks);
sc_save_config(&sclock, &w.wp[pattern].sc_conf);
}
if (w.direct_clock) clock(data);
}

static void synced_clock_off_callback(void* o) {
timer_remove(&synced_clock_off_timer);
clock(0);
}

static void synced_clock_off_callback2(void* o) {
timer_remove(&synced_clock_off_timer2);
clock_on = 0;
if (edit_mode == mClock) (*re)();
}

static void synced_clock_off_callback3(void* o) {
timer_remove(&synced_clock_off_timer3);
if (w.direct_clock) clock(0);
}

static void synced_clock_callback(void) {
clock_on = 1;
if (edit_mode == mClock) (*re)();
timer_remove(&synced_clock_off_timer);
timer_add(&synced_clock_off_timer, 10, &synced_clock_off_callback, NULL);
timer_remove(&synced_clock_off_timer2);
timer_add(&synced_clock_off_timer2, 40, &synced_clock_off_callback2, NULL);

if (!clock_enabled || (w.direct_clock && clock_external)) return;
clock(1);
}


@@ -753,6 +806,7 @@ static void handler_MonomeGridKey(s32 data) {
else {
pattern = i1;
next_pattern = i1;
update_synced_clock(false, false);
}
}
}
@@ -865,6 +919,9 @@ static void handler_MonomeGridKey(s32 data) {
if(z == 0) {
param_accept = 0;
live_in = 0;
} else if (key_meta) {
if (edit_mode == mSeries) update_synced_clock(false, false);
edit_mode = mClock;
}
monomeFrameDirty++;
}
@@ -873,8 +930,10 @@ static void handler_MonomeGridKey(s32 data) {
w.wp[pattern].tr_mode ^= 1;
else if(key_meta)
w.tr_mute[x] ^= 1;
else
else {
if (edit_mode == mSeries) update_synced_clock(false, false);
edit_mode = mTrig;
}
edit_prob = 0;
param_accept = 0;
monomeFrameDirty++;
@@ -888,14 +947,17 @@ static void handler_MonomeGridKey(s32 data) {
w.wp[pattern].cv_mode[edit_cv_ch] ^= 1;
else if(key_meta)
w.cv_mute[edit_cv_ch] ^= 1;
else
else {
if (edit_mode == mSeries) update_synced_clock(false, false);
edit_mode = mMap;
}

monomeFrameDirty++;
}
else if(SIZE==8 && (x == 4 || x == 5) && z) {
param_accept = 0;
edit_cv_ch = x-4;
if (edit_mode == mSeries) update_synced_clock(false, false);
edit_mode = mMap;
edit_prob = 0;

@@ -910,8 +972,18 @@ static void handler_MonomeGridKey(s32 data) {
edit_mode = mSeries;
monomeFrameDirty++;
}
else if(x == LENGTH-1)
else if(x == LENGTH-1) {
key_meta = z;
}
else if(x == LENGTH-2 && z) {
if (edit_mode == mSeries) update_synced_clock(false, false);
edit_mode = mClock;
monomeFrameDirty++;
}
else if(x == LENGTH-3 && z) {
clock_enabled = !clock_enabled;
monomeFrameDirty++;
}
}


@@ -1227,6 +1299,43 @@ static void handler_MonomeGridKey(s32 data) {

monomeFrameDirty++;
}

// clock settings
else if(edit_mode == mClock) {
if (!z) return;

switch (y) {
case 3:
// reserved
break;
case 4:
// reserved
break;
case 5:
sc_update_div(&sclock, x + 1);
sc_save_config(&sclock, &w.wp[pattern].sc_conf);
break;
case 6:
sc_update_mult(&sclock, x + 1);
sc_save_config(&sclock, &w.wp[pattern].sc_conf);
break;
case 7:
if (x == 0) {
w.keep_tempo = !w.keep_tempo;
} else if (x == 1) {
w.keep_div_mult = !w.keep_div_mult;
} else if (x == LENGTH-2) {
sc_process_tap(&sclock, tcTicks);
sc_save_config(&sclock, &w.wp[pattern].sc_conf);
} else if (x == LENGTH-1) {
w.direct_clock = !w.direct_clock;
} else if (x == LENGTH) {
clock_enabled = !clock_enabled;
}
break;
}
monomeFrameDirty++;
}
}
}

@@ -1268,6 +1377,12 @@ static void refresh() {
// alt
monomeLedBuffer[LENGTH] = 4;
if(key_alt) monomeLedBuffer[LENGTH] = 11;

// clock settings
if(SIZE==16) {
monomeLedBuffer[LENGTH-2] = edit_mode == mClock ? 11 : 0;
monomeLedBuffer[LENGTH-3] = clock_enabled ? 11 : 4;
}

// show mutes or on steps
if(key_meta) {
@@ -1502,6 +1617,22 @@ static void refresh() {
}
}

// clock settings
else if(edit_mode == mClock) {
for(i1=0;i1<SIZE;i1++) {
monomeLedBuffer[i1+48] = 0;
monomeLedBuffer[i1+64] = 0;
monomeLedBuffer[i1+80] = i1 < sclock.conf.div ? 11 : 2;
monomeLedBuffer[i1+96] = i1 < sclock.conf.mult ? 11 : 2;
monomeLedBuffer[i1+112] = 0;
}
monomeLedBuffer[112] = w.keep_tempo ? 11 : 4;
monomeLedBuffer[113] = w.keep_div_mult ? 11 : 4;
monomeLedBuffer[110+LENGTH] = clock_on ? 11 : 4;
monomeLedBuffer[111+LENGTH] = w.direct_clock ? 11 : 4;
monomeLedBuffer[112+LENGTH] = clock_enabled ? 11 : 4;
}

monome_set_quadrant_flag(0);
monome_set_quadrant_flag(1);
}
@@ -1566,6 +1697,12 @@ static void refresh_mono() {
// alt
if(key_alt) monomeLedBuffer[LENGTH] = 11;

// clock settings
if(SIZE==16) {
monomeLedBuffer[LENGTH-2] = edit_mode == mClock ? 11 : 0;
monomeLedBuffer[LENGTH-3] = clock_enabled ? 11 : 4;
}

// show position
monomeLedBuffer[16+pos] = 15;

@@ -1728,6 +1865,22 @@ static void refresh_mono() {
}
}

// clock settings
else if(edit_mode == mClock) {
for(i1=0;i1<SIZE;i1++) {
monomeLedBuffer[i1+48] = 0;
monomeLedBuffer[i1+64] = 0;
monomeLedBuffer[i1+80] = i1 < sclock.conf.div ? 11 : 2;
monomeLedBuffer[i1+96] = i1 < sclock.conf.mult ? 11 : 2;
monomeLedBuffer[i1+112] = 0;
}
monomeLedBuffer[112] = w.keep_tempo ? 11 : 4;
monomeLedBuffer[113] = w.keep_div_mult ? 11 : 4;
monomeLedBuffer[110+LENGTH] = clock_on ? 11 : 4;
monomeLedBuffer[111+LENGTH] = w.direct_clock ? 11 : 4;
monomeLedBuffer[112+LENGTH] = clock_enabled ? 11 : 4;
}

monome_set_quadrant_flag(0);
monome_set_quadrant_flag(1);
}
@@ -1785,9 +1938,12 @@ static void ww_process_ii(uint8_t *data, uint8_t l) {
break;
next_pos = d;
cut_pos++;
timer_set(&clockTimer,clock_time);
clock_phase = 1;
(*clock_pulse)(clock_phase);
sc_process_tap(&sclock, tcTicks);
sc_save_config(&sclock, &w.wp[pattern].sc_conf);
if (w.direct_clock) {
timer_add(&synced_clock_off_timer3, 10, &synced_clock_off_callback3, NULL);
clock(1);
}
break;
case WW_START:
if(d<0 || d>15)
@@ -1827,6 +1983,7 @@ static void ww_process_ii(uint8_t *data, uint8_t l) {
break;
pattern = d;
next_pattern = d;
update_synced_clock(edit_mode == mSeries, false);
monomeFrameDirty++;
break;
case WW_QPATTERN:
@@ -1947,6 +2104,10 @@ void flash_read(void) {
w.wp[i1].cv_mode[0] = flashy.w[preset_select].wp[i1].cv_mode[0];
w.wp[i1].cv_mode[1] = flashy.w[preset_select].wp[i1].cv_mode[1];
w.wp[i1].tr_mode = flashy.w[preset_select].wp[i1].tr_mode;

w.wp[i1].sc_conf.div = flashy.w[preset_select].wp[i1].sc_conf.div;
w.wp[i1].sc_conf.mult = flashy.w[preset_select].wp[i1].sc_conf.mult;
w.wp[i1].sc_conf.period = flashy.w[preset_select].wp[i1].sc_conf.period;
}

w.series_start = flashy.w[preset_select].series_start;
@@ -1958,6 +2119,10 @@ void flash_read(void) {
w.tr_mute[3] = flashy.w[preset_select].tr_mute[3];
w.cv_mute[0] = flashy.w[preset_select].cv_mute[0];
w.cv_mute[1] = flashy.w[preset_select].cv_mute[1];

w.direct_clock = flashy.w[preset_select].direct_clock;
w.keep_tempo = flashy.w[preset_select].keep_tempo;
w.keep_div_mult = flashy.w[preset_select].keep_div_mult;

for(i1=0;i1<64;i1++)
w.series_list[i1] = flashy.w[preset_select].series_list[i1];
@@ -2034,6 +2199,10 @@ int main(void)
w.wp[i1].cv_mode[0] = 0;
w.wp[i1].cv_mode[1] = 0;
w.wp[i1].tr_mode = 0;

w.wp[i1].sc_conf.div = 1;
w.wp[i1].sc_conf.mult = 1;
w.wp[i1].sc_conf.period = 400;
}

w.series_start = 0;
@@ -2046,9 +2215,13 @@ int main(void)
w.cv_mute[0] = 1;
w.cv_mute[1] = 1;

w.direct_clock = false;
w.keep_tempo = false;
w.keep_div_mult = false;

for(i1=0;i1<64;i1++)
w.series_list[i1] = 1;

// save all presets, clear glyphs
for(i1=0;i1<8;i1++) {
flashc_memcpy((void *)&flashy.w[i1], &w, sizeof(w), true);
@@ -2080,6 +2253,10 @@ int main(void)
timer_add(&adcTimer,100,&adcTimer_callback, NULL);
clock_temp = 10000; // out of ADC range to force tempo

clock_enabled = true;
sc_init(&sclock, &w.wp[pattern].sc_conf, &synced_clock_callback);
monomeFrameDirty++;

// setup daisy chain for two dacs
// spi_selectChip(SPI,DAC_SPI);
// spi_write(SPI,0x80);
@@ -0,0 +1,136 @@
#include "synced_clock.h"
#include "compiler.h"

static void sc_heartbeat_callback(void* o);
static void sc_update_period(synced_clock* sc, u32 tap);
static void sc_recalculate_intervals(synced_clock* sc);
static void sc_adjust_position(synced_clock* sc, u32 position);
static u32 sc_get_position(synced_clock* sc);

static void sc_heartbeat_callback(void* o) {
synced_clock* sc = o;
if (++sc->int_index >= sc->conf.mult) sc->int_index = 0;
timer_remove(&sc->heartbeat);
timer_add(&sc->heartbeat, sc->intervals[sc->int_index], &sc_heartbeat_callback, (void *)sc);
(*(sc->callback))();
}

static void sc_update_period(synced_clock* sc, u32 tap) {
u32 min = (sc->conf.period * 3) >> 2;
u32 max = min + (sc->conf.period >> 1);
if (tap < min || tap > max) sc->ext_taps_index = sc->ext_taps_count = 0;

sc->ext_taps[sc->ext_taps_index] = tap;
if (++sc->ext_taps_index >= SC_AVERAGING_TAPS) sc->ext_taps_index = 0;
if (sc->ext_taps_count < SC_AVERAGING_TAPS) sc->ext_taps_count++;

u64 total = 0;
for (u8 i = 0; i < sc->ext_taps_count; i++) total += sc->ext_taps[i];
sc->conf.period = total / (u64)sc->ext_taps_count;
}

static void sc_recalculate_intervals(synced_clock* sc) {
u32 total = sc->conf.period * sc->conf.div;
u32 interval = (total << 4) / (u32)sc->conf.mult;
u32 acc = 0;
u32 carryover = 0;
for (u8 i = 0; i < sc->conf.mult; i++) {
sc->intervals[i] = (interval + carryover) >> 4;
carryover = (interval + carryover) & 15;
acc += sc->intervals[i];
}
sc->intervals[sc->conf.mult - 1] += total - acc;
}

static void sc_adjust_position(synced_clock* sc, u32 position) {
u32 remaining = position % (sc->conf.period * sc->conf.div);
u8 expected_index = 0;
while (remaining >= sc->intervals[expected_index] && expected_index < sc->conf.mult) {
remaining -= sc->intervals[expected_index];
expected_index++;
}

u32 max_delta = sc->intervals[0] >> 2;
if (expected_index == (sc->int_index + 1) % sc->conf.mult && sc->heartbeat.ticksRemain < max_delta) {
sc_heartbeat_callback((void *)sc);
} else {
sc->int_index = expected_index;
sc->heartbeat.ticksRemain = sc->intervals[sc->int_index] - remaining;
}
}

static u32 sc_get_position(synced_clock* sc) {
u32 position = 0;
for (u8 i = 0; i < sc->int_index; i++) position += sc->intervals[i];
position += sc->heartbeat.ticks - sc->heartbeat.ticksRemain;
return position;
}

void sc_process_tap(synced_clock* sc, u64 tick) {
u64 elapsed = tick - sc->last_tick;
sc->last_tick = tick;
if (elapsed > (u32)60000) {
sc->ext_index = 0;
return;
}

if (++sc->ext_index >= sc->conf.div) sc->ext_index = 0;
sc_update_period(sc, elapsed);
sc_recalculate_intervals(sc);
sc_adjust_position(sc, sc->conf.period * sc->ext_index);
}

void sc_update_div(synced_clock* sc, u8 div) {
u32 pos = sc_get_position(sc);
sc->conf.div = div ? div : 1;
sc_recalculate_intervals(sc);
sc_adjust_position(sc, pos);
}

void sc_update_mult(synced_clock* sc, u8 mult) {
u32 pos = sc_get_position(sc);
sc->conf.mult = mult ? mult : 1;
sc_recalculate_intervals(sc);
sc_adjust_position(sc, pos);
}

void sc_init(synced_clock* sc, sc_config* conf, sc_callback_t callback) {
sc->conf.div = conf->div ? conf->div : 1;
sc->conf.mult = conf->mult ? conf->mult : 1;
sc->conf.period = conf->period;
sc->callback = callback;

sc->ext_index = 0;
sc->int_index = 0;
sc_recalculate_intervals(sc);

sc->ext_taps_index = 0;
sc->ext_taps_count = 0;
sc->last_tick = 0;

sc->int_index = sc->conf.mult - 1;
sc_heartbeat_callback((void *)sc);
}

void sc_load_config(synced_clock* sc, sc_config* conf, u8 update_period, u8 update_div_mult, u8 synced) {
u32 pos = synced ? 0 : sc_get_position(sc);
if (update_div_mult) {
sc->conf.div = conf->div ? conf->div : 1;
sc->conf.mult = conf->mult ? conf->mult : 1;
if (sc->conf.mult > SC_MAXMULT) sc->conf.mult = SC_MAXMULT;
}
if (update_period) {
sc->conf.period = conf->period;
}
if (update_div_mult || update_period) {
sc->ext_index = sc->int_index = 0;
sc_recalculate_intervals(sc);
sc_adjust_position(sc, pos);
}
}

void sc_save_config(synced_clock* sc, sc_config* conf) {
conf->div = sc->conf.div;
conf->mult = sc->conf.mult;
conf->period = sc->conf.period;
}
@@ -0,0 +1,38 @@
#pragma once

#define SC_MAXMULT 16
#define SC_AVERAGING_TAPS 3

#include "types.h"
#include "timers.h"

typedef volatile struct {
u8 div;
u8 mult;
u32 period;
} sc_config;

typedef void (*sc_callback_t)(void);

typedef volatile struct {
sc_config conf;
sc_callback_t callback;

u32 intervals[SC_MAXMULT];
u8 ext_index;
u8 int_index;

u32 ext_taps[SC_AVERAGING_TAPS];
u8 ext_taps_index;
u8 ext_taps_count;
u64 last_tick;

softTimer_t heartbeat;
} synced_clock;

void sc_init(synced_clock* sc, sc_config* conf, sc_callback_t callback);
void sc_load_config(synced_clock* sc, sc_config* conf, u8 update_period, u8 update_div_mult, u8 synced);
void sc_save_config(synced_clock* sc, sc_config* conf);
void sc_process_tap(synced_clock* sc, u64 tick);
void sc_update_div(synced_clock* sc, u8 div);
void sc_update_mult(synced_clock* sc, u8 mult);