review pause+continue feature

pull/51/head
midilab 3 days ago
parent 283ef71744
commit aa943fb711
  1. 481
      src/uClock.cpp
  2. 18
      src/uClock.h

@ -140,6 +140,11 @@ uClockClass::uClockClass()
calculateReferencedata(); calculateReferencedata();
} }
uClockClass::~uClockClass()
{
delete[] ext_interval_buffer;
}
void uClockClass::init() void uClockClass::init()
{ {
if (ext_interval_buffer == nullptr) if (ext_interval_buffer == nullptr)
@ -150,42 +155,186 @@ void uClockClass::init()
setTempo(tempo); setTempo(tempo);
} }
uint32_t uClockClass::bpmToMicroSeconds(float bpm) void uClockClass::handleInternalClock()
{ {
return (60000000.0f / (float)output_ppqn / bpm); // track main input clock counter
} if (mod_clock_counter == mod_clock_ref)
mod_clock_counter = 0;
void uClockClass::calculateReferencedata() // process sync signals first please...
{ if (mod_clock_counter == 0) {
mod_clock_ref = output_ppqn / input_ppqn;
mod_sync1_ref = output_ppqn / PPQN_1; if (clock_mode == EXTERNAL_CLOCK) {
mod_sync2_ref = output_ppqn / PPQN_2; // sync tick position with external tick clock
mod_sync4_ref = output_ppqn / PPQN_4; if ((int_clock_tick < ext_clock_tick) || (int_clock_tick > (ext_clock_tick + 1))) {
mod_sync8_ref = output_ppqn / PPQN_8; int_clock_tick = ext_clock_tick;
mod_sync12_ref = output_ppqn / PPQN_12; tick = int_clock_tick * mod_clock_ref;
mod_sync24_ref = output_ppqn / PPQN_24; mod_clock_counter = tick % mod_clock_ref;
mod_sync48_ref = output_ppqn / PPQN_48; mod_step_counter = tick % mod_step_ref;
mod_step_ref = output_ppqn / 4; }
uint32_t counter = ext_interval;
uint32_t now_clock_us = micros();
sync_interval = clock_diff(ext_clock_us, now_clock_us);
if (int_clock_tick <= ext_clock_tick) {
counter -= phase_mult(sync_interval);
} else {
if (counter > sync_interval) {
counter += phase_mult(counter - sync_interval);
}
}
// update internal clock timer frequency
float bpm = constrainBpm(freqToBpm(counter));
if (bpm != tempo) {
tempo = bpm;
setTimerTempo(bpm);
}
}
// internal clock tick me!
++int_clock_tick;
}
++mod_clock_counter;
// ALL OUTPUT SYNC CALLBACKS
// Sync1 callback
if (onSync1Callback) {
if (mod_sync1_counter == mod_sync1_ref)
mod_sync1_counter = 0;
if (mod_sync1_counter == 0) {
onSync1Callback(sync1_tick);
++sync1_tick;
}
++mod_sync1_counter;
}
// Sync2 callback
if (onSync2Callback) {
if (mod_sync2_counter == mod_sync2_ref)
mod_sync2_counter = 0;
if (mod_sync2_counter == 0) {
onSync2Callback(sync2_tick);
++sync2_tick;
}
++mod_sync2_counter;
}
// Sync4 callback
if (onSync4Callback) {
if (mod_sync4_counter == mod_sync4_ref)
mod_sync4_counter = 0;
if (mod_sync4_counter == 0) {
onSync4Callback(sync4_tick);
++sync4_tick;
}
++mod_sync4_counter;
}
// Sync8 callback
if (onSync8Callback) {
if (mod_sync8_counter == mod_sync8_ref)
mod_sync8_counter = 0;
if (mod_sync8_counter == 0) {
onSync8Callback(sync8_tick);
++sync8_tick;
}
++mod_sync8_counter;
}
// Sync12 callback
if (onSync12Callback) {
if (mod_sync12_counter == mod_sync12_ref)
mod_sync12_counter = 0;
if (mod_sync12_counter == 0) {
onSync12Callback(sync12_tick);
++sync12_tick;
}
++mod_sync12_counter;
}
// Sync24 callback
if (onSync24Callback) {
if (mod_sync24_counter == mod_sync24_ref)
mod_sync24_counter = 0;
if (mod_sync24_counter == 0) {
onSync24Callback(sync24_tick);
++sync24_tick;
}
++mod_sync24_counter;
}
// Sync48 callback
if (onSync48Callback) {
if (mod_sync48_counter == mod_sync48_ref)
mod_sync48_counter = 0;
if (mod_sync48_counter == 0) {
onSync48Callback(sync48_tick);
++sync48_tick;
}
++mod_sync48_counter;
}
// main PPQNCallback
if (onOutputPPQNCallback) {
onOutputPPQNCallback(tick);
++tick;
}
// step callback to support 16th old school style sequencers
// with builtin shuffle for this callback only
if (onStepCallback) {
if (mod_step_counter == mod_step_ref)
mod_step_counter = 0;
// processShufle make use of mod_step_counter == 0 logic too
if (processShuffle()) {
onStepCallback(step_counter);
// going forward to the next step call
++step_counter;
}
++mod_step_counter;
}
} }
void uClockClass::setOutputPPQN(PPQNResolution resolution) void uClockClass::handleExternalClock()
{ {
// dont allow PPQN lower than PPQN_4 for output clock (to avoid problems with mod_step_ref) switch (clock_state) {
if (resolution < PPQN_4) case PAUSED:
return; break;
ATOMIC( case STARTING:
output_ppqn = resolution; clock_state = STARTED;
calculateReferencedata(); ext_clock_us = micros();
) break;
case STARTED:
uint32_t now_clock_us = micros();
last_interval = clock_diff(ext_clock_us, now_clock_us);
ext_clock_us = now_clock_us;
// external clock tick me!
ext_clock_tick++;
// accumulate interval incomming ticks data for getTempo() smooth reads on slave clock_mode
if(++ext_interval_idx >= ext_interval_buffer_size) {
ext_interval_idx = 0;
}
ext_interval_buffer[ext_interval_idx] = last_interval;
if (ext_clock_tick == 1) {
ext_interval = last_interval;
} else {
ext_interval = (((uint32_t)ext_interval * (uint32_t)PLL_X) + (uint32_t)(256 - PLL_X) * (uint32_t)last_interval) >> 8;
}
break;
}
} }
void uClockClass::setInputPPQN(PPQNResolution resolution) void uClockClass::clockMe()
{ {
ATOMIC( if (clock_mode == EXTERNAL_CLOCK)
input_ppqn = resolution; ATOMIC(handleExternalClock())
calculateReferencedata();
)
} }
void uClockClass::start() void uClockClass::start()
@ -198,43 +347,85 @@ void uClockClass::start()
} }
if (clock_mode == INTERNAL_CLOCK) { if (clock_mode == INTERNAL_CLOCK) {
clock_state = STARTED; ATOMIC(clock_state = STARTED)
} else { } else {
clock_state = STARTING; ATOMIC(clock_state = STARTING)
} }
} }
void uClockClass::stop() void uClockClass::stop()
{ {
clock_state = PAUSED; ATOMIC(clock_state = PAUSED)
start_timer = 0;
resetCounters(); resetCounters();
start_timer = 0;
if (onClockStopCallback) { if (onClockStopCallback) {
onClockStopCallback(); onClockStopCallback();
} }
} }
void uClockClass::continue_playing() { void uClockClass::pause()
// todo: if not paused, should we start anyway? {
if (clock_state == PAUSED) { if (clock_state == PAUSED) {
start_timer = millis();
if (clock_mode == INTERNAL_CLOCK) { if (clock_mode == INTERNAL_CLOCK) {
clock_state = STARTED; ATOMIC(clock_state = STARTED)
} else { } else if (clock_mode == EXTERNAL_CLOCK) {
clock_state = STARTING; ATOMIC(clock_state = STARTING)
}
if (onClockContinueCallback) {
onClockContinueCallback();
} }
} else {
ATOMIC(clock_state = PAUSED)
} }
} }
void uClockClass::pause() void uClockClass::setClockMode(ClockMode tempo_mode)
{ {
clock_state = PAUSED; if (tempo_mode == EXTERNAL_CLOCK && clock_state == STARTED) {
if (onClockPauseCallback) { // trying to set external clock while playing? force sync last clock tick
onClockPauseCallback(); ATOMIC(clock_state = STARTING)
} }
ATOMIC(clock_mode = tempo_mode)
}
uClockClass::ClockMode uClockClass::getClockMode()
{
return clock_mode;
}
uint32_t uClockClass::bpmToMicroSeconds(float bpm)
{
return (60000000.0f / (float)output_ppqn / bpm);
}
void uClockClass::calculateReferencedata()
{
mod_clock_ref = output_ppqn / input_ppqn;
mod_sync1_ref = output_ppqn / PPQN_1;
mod_sync2_ref = output_ppqn / PPQN_2;
mod_sync4_ref = output_ppqn / PPQN_4;
mod_sync8_ref = output_ppqn / PPQN_8;
mod_sync12_ref = output_ppqn / PPQN_12;
mod_sync24_ref = output_ppqn / PPQN_24;
mod_sync48_ref = output_ppqn / PPQN_48;
mod_step_ref = output_ppqn / 4;
}
void uClockClass::setOutputPPQN(PPQNResolution resolution)
{
// dont allow PPQN lower than PPQN_4 for output clock (to avoid problems with mod_step_ref)
if (resolution < PPQN_4)
return;
ATOMIC(
output_ppqn = resolution;
calculateReferencedata();
)
}
void uClockClass::setInputPPQN(PPQNResolution resolution)
{
ATOMIC(
input_ppqn = resolution;
calculateReferencedata();
)
} }
void uClockClass::setTempo(float bpm) void uClockClass::setTempo(float bpm)
@ -247,9 +438,7 @@ void uClockClass::setTempo(float bpm)
return; return;
} }
ATOMIC( ATOMIC(tempo = bpm)
tempo = bpm
)
setTimerTempo(bpm); setTimerTempo(bpm);
} }
@ -292,21 +481,6 @@ float inline uClockClass::constrainBpm(float bpm)
return (bpm < MIN_BPM) ? MIN_BPM : ( bpm > MAX_BPM ? MAX_BPM : bpm ); return (bpm < MIN_BPM) ? MIN_BPM : ( bpm > MAX_BPM ? MAX_BPM : bpm );
} }
void uClockClass::setClockMode(ClockMode tempo_mode)
{
clock_mode = tempo_mode;
}
uClockClass::ClockMode uClockClass::getClockMode()
{
return clock_mode;
}
void uClockClass::clockMe()
{
ATOMIC(handleExternalClock())
}
void uClockClass::setExtIntervalBuffer(uint8_t buffer_size) void uClockClass::setExtIntervalBuffer(uint8_t buffer_size)
{ {
if (ext_interval_buffer != nullptr) if (ext_interval_buffer != nullptr)
@ -314,7 +488,7 @@ void uClockClass::setExtIntervalBuffer(uint8_t buffer_size)
// alloc once and forever policy // alloc once and forever policy
ext_interval_buffer_size = buffer_size; ext_interval_buffer_size = buffer_size;
ext_interval_buffer = (uint32_t*) malloc( sizeof(uint32_t) * ext_interval_buffer_size ); ext_interval_buffer = new uint32_t[ext_interval_buffer_size];
} }
void uClockClass::resetCounters() void uClockClass::resetCounters()
@ -440,182 +614,6 @@ bool inline uClockClass::processShuffle()
return false; return false;
} }
void uClockClass::handleExternalClock()
{
switch (clock_state) {
case PAUSED:
break;
case STARTING:
clock_state = STARTED;
ext_clock_us = micros();
break;
case STARTED:
uint32_t now_clock_us = micros();
last_interval = clock_diff(ext_clock_us, now_clock_us);
ext_clock_us = now_clock_us;
// external clock tick me!
ext_clock_tick++;
// accumulate interval incomming ticks data for getTempo() smooth reads on slave clock_mode
if(++ext_interval_idx >= ext_interval_buffer_size) {
ext_interval_idx = 0;
}
ext_interval_buffer[ext_interval_idx] = last_interval;
if (ext_clock_tick == 1) {
ext_interval = last_interval;
} else {
ext_interval = (((uint32_t)ext_interval * (uint32_t)PLL_X) + (uint32_t)(256 - PLL_X) * (uint32_t)last_interval) >> 8;
}
break;
}
}
void uClockClass::handleTimerInt()
{
// track main input clock counter
if (mod_clock_counter == mod_clock_ref)
mod_clock_counter = 0;
// process sync signals first please...
if (mod_clock_counter == 0) {
if (clock_mode == EXTERNAL_CLOCK) {
// sync tick position with external tick clock
if ((int_clock_tick < ext_clock_tick) || (int_clock_tick > (ext_clock_tick + 1))) {
int_clock_tick = ext_clock_tick;
tick = int_clock_tick * mod_clock_ref;
mod_clock_counter = tick % mod_clock_ref;
mod_step_counter = tick % mod_step_ref;
}
uint32_t counter = ext_interval;
uint32_t now_clock_us = micros();
sync_interval = clock_diff(ext_clock_us, now_clock_us);
if (int_clock_tick <= ext_clock_tick) {
counter -= phase_mult(sync_interval);
} else {
if (counter > sync_interval) {
counter += phase_mult(counter - sync_interval);
}
}
// update internal clock timer frequency
if (ext_interval > 0) {
float bpm = constrainBpm(freqToBpm(counter));
tempo = bpm;
setTimerTempo(bpm);
}
}
// internal clock tick me!
++int_clock_tick;
}
++mod_clock_counter;
// ALL OUTPUT SYNC CALLBACKS
// Sync1 callback
if (onSync1Callback) {
if (mod_sync1_counter == mod_sync1_ref)
mod_sync1_counter = 0;
if (mod_sync1_counter == 0) {
onSync1Callback(sync1_tick);
++sync1_tick;
}
++mod_sync1_counter;
}
// Sync2 callback
if (onSync2Callback) {
if (mod_sync2_counter == mod_sync2_ref)
mod_sync2_counter = 0;
if (mod_sync2_counter == 0) {
onSync2Callback(sync2_tick);
++sync2_tick;
}
++mod_sync2_counter;
}
// Sync4 callback
if (onSync4Callback) {
if (mod_sync4_counter == mod_sync4_ref)
mod_sync4_counter = 0;
if (mod_sync4_counter == 0) {
onSync4Callback(sync4_tick);
++sync4_tick;
}
++mod_sync4_counter;
}
// Sync8 callback
if (onSync8Callback) {
if (mod_sync8_counter == mod_sync8_ref)
mod_sync8_counter = 0;
if (mod_sync8_counter == 0) {
onSync8Callback(sync8_tick);
++sync8_tick;
}
++mod_sync8_counter;
}
// Sync12 callback
if (onSync12Callback) {
if (mod_sync12_counter == mod_sync12_ref)
mod_sync12_counter = 0;
if (mod_sync12_counter == 0) {
onSync12Callback(sync12_tick);
++sync12_tick;
}
++mod_sync12_counter;
}
// Sync24 callback
if (onSync24Callback) {
if (mod_sync24_counter == mod_sync24_ref)
mod_sync24_counter = 0;
if (mod_sync24_counter == 0) {
onSync24Callback(sync24_tick);
++sync24_tick;
}
++mod_sync24_counter;
}
// Sync48 callback
if (onSync48Callback) {
if (mod_sync48_counter == mod_sync48_ref)
mod_sync48_counter = 0;
if (mod_sync48_counter == 0) {
onSync48Callback(sync48_tick);
++sync48_tick;
}
++mod_sync48_counter;
}
// main PPQNCallback
if (onOutputPPQNCallback) {
onOutputPPQNCallback(tick);
++tick;
}
// step callback to support 16th old school style sequencers
// with builtin shuffle for this callback only
if (onStepCallback) {
if (mod_step_counter == mod_step_ref)
mod_step_counter = 0;
// processShufle make use of mod_step_counter == 0 logic too
if (processShuffle()) {
onStepCallback(step_counter);
// going forward to the next step call
++step_counter;
}
++mod_step_counter;
}
}
// elapsed time support // elapsed time support
uint8_t uClockClass::getNumberOfSeconds(uint32_t time) uint8_t uClockClass::getNumberOfSeconds(uint32_t time)
{ {
@ -673,7 +671,6 @@ void uClockHandler()
// global timer counter // global timer counter
_millis = millis(); _millis = millis();
if (uClock.clock_state == uClock.STARTED) { if (uClock.clock_state == uClock.STARTED)
uClock.handleTimerInt(); uClock.handleInternalClock();
}
} }

@ -86,6 +86,7 @@ class uClockClass {
ClockState clock_state; ClockState clock_state;
uClockClass(); uClockClass();
~uClockClass();
void setOnOutputPPQN(void (*callback)(uint32_t tick)) { void setOnOutputPPQN(void (*callback)(uint32_t tick)) {
onOutputPPQNCallback = callback; onOutputPPQNCallback = callback;
@ -132,19 +133,19 @@ class uClockClass {
onClockStopCallback = callback; onClockStopCallback = callback;
} }
void setOnClockContinue(void (*callback)()) {
onClockStartCallback = callback;
}
void setOnClockPause(void (*callback)()) { void setOnClockPause(void (*callback)()) {
onClockPauseCallback = callback; onClockPauseCallback = callback;
} }
void setOnClockContinue(void (*callback)()) {
onClockContinueCallback = callback;
}
void init(); void init();
void setOutputPPQN(PPQNResolution resolution); void setOutputPPQN(PPQNResolution resolution);
void setInputPPQN(PPQNResolution resolution); void setInputPPQN(PPQNResolution resolution);
void handleTimerInt(); void handleInternalClock();
void handleExternalClock(); void handleExternalClock();
void resetCounters(); void resetCounters();
@ -152,7 +153,6 @@ class uClockClass {
void start(); void start();
void stop(); void stop();
void pause(); void pause();
void continue_playing();
void setTempo(float bpm); void setTempo(float bpm);
float getTempo(); float getTempo();
@ -210,8 +210,8 @@ class uClockClass {
void (*onSync48Callback)(uint32_t tick); void (*onSync48Callback)(uint32_t tick);
void (*onClockStartCallback)(); void (*onClockStartCallback)();
void (*onClockStopCallback)(); void (*onClockStopCallback)();
void (*onClockContinueCallback)();
void (*onClockPauseCallback)(); void (*onClockPauseCallback)();
void (*onClockContinueCallback)();
// clock input/output control // clock input/output control
PPQNResolution output_ppqn = PPQN_96; PPQNResolution output_ppqn = PPQN_96;
@ -253,9 +253,9 @@ class uClockClass {
uint32_t last_interval; uint32_t last_interval;
uint32_t sync_interval; uint32_t sync_interval;
float tempo; volatile float tempo;
volatile ClockMode clock_mode;
uint32_t start_timer; uint32_t start_timer;
ClockMode clock_mode;
volatile uint32_t * ext_interval_buffer = nullptr; volatile uint32_t * ext_interval_buffer = nullptr;
uint8_t ext_interval_buffer_size; uint8_t ext_interval_buffer_size;

Loading…
Cancel
Save