diff --git a/src/uClock.cpp b/src/uClock.cpp index 563c9b4..98f557d 100755 --- a/src/uClock.cpp +++ b/src/uClock.cpp @@ -140,6 +140,11 @@ uClockClass::uClockClass() calculateReferencedata(); } +uClockClass::~uClockClass() +{ + delete[] ext_interval_buffer; +} + void uClockClass::init() { if (ext_interval_buffer == nullptr) @@ -150,42 +155,186 @@ void uClockClass::init() 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() -{ - 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; + // 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 + 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) - if (resolution < PPQN_4) - return; + switch (clock_state) { + case PAUSED: + break; - ATOMIC( - output_ppqn = resolution; - calculateReferencedata(); - ) + 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::setInputPPQN(PPQNResolution resolution) +void uClockClass::clockMe() { - ATOMIC( - input_ppqn = resolution; - calculateReferencedata(); - ) + if (clock_mode == EXTERNAL_CLOCK) + ATOMIC(handleExternalClock()) } void uClockClass::start() @@ -198,43 +347,85 @@ void uClockClass::start() } if (clock_mode == INTERNAL_CLOCK) { - clock_state = STARTED; + ATOMIC(clock_state = STARTED) } else { - clock_state = STARTING; + ATOMIC(clock_state = STARTING) } } void uClockClass::stop() { - clock_state = PAUSED; - start_timer = 0; + ATOMIC(clock_state = PAUSED) resetCounters(); + start_timer = 0; if (onClockStopCallback) { onClockStopCallback(); } } -void uClockClass::continue_playing() { - // todo: if not paused, should we start anyway? +void uClockClass::pause() +{ if (clock_state == PAUSED) { - start_timer = millis(); if (clock_mode == INTERNAL_CLOCK) { - clock_state = STARTED; - } else { - clock_state = STARTING; - } - if (onClockContinueCallback) { - onClockContinueCallback(); + ATOMIC(clock_state = STARTED) + } else if (clock_mode == EXTERNAL_CLOCK) { + ATOMIC(clock_state = STARTING) } + } else { + ATOMIC(clock_state = PAUSED) } } -void uClockClass::pause() +void uClockClass::setClockMode(ClockMode tempo_mode) { - clock_state = PAUSED; - if (onClockPauseCallback) { - onClockPauseCallback(); + if (tempo_mode == EXTERNAL_CLOCK && clock_state == STARTED) { + // trying to set external clock while playing? force sync last clock tick + 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) @@ -247,9 +438,7 @@ void uClockClass::setTempo(float bpm) return; } - ATOMIC( - tempo = bpm - ) + ATOMIC(tempo = 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 ); } -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) { if (ext_interval_buffer != nullptr) @@ -314,7 +488,7 @@ void uClockClass::setExtIntervalBuffer(uint8_t buffer_size) // alloc once and forever policy 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() @@ -440,182 +614,6 @@ bool inline uClockClass::processShuffle() 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 uint8_t uClockClass::getNumberOfSeconds(uint32_t time) { @@ -673,7 +671,6 @@ void uClockHandler() // global timer counter _millis = millis(); - if (uClock.clock_state == uClock.STARTED) { - uClock.handleTimerInt(); - } + if (uClock.clock_state == uClock.STARTED) + uClock.handleInternalClock(); } diff --git a/src/uClock.h b/src/uClock.h index 69ead1a..aa933c7 100755 --- a/src/uClock.h +++ b/src/uClock.h @@ -86,6 +86,7 @@ class uClockClass { ClockState clock_state; uClockClass(); + ~uClockClass(); void setOnOutputPPQN(void (*callback)(uint32_t tick)) { onOutputPPQNCallback = callback; @@ -132,19 +133,19 @@ class uClockClass { onClockStopCallback = callback; } - void setOnClockContinue(void (*callback)()) { - onClockStartCallback = callback; - } - void setOnClockPause(void (*callback)()) { onClockPauseCallback = callback; } + void setOnClockContinue(void (*callback)()) { + onClockContinueCallback = callback; + } + void init(); void setOutputPPQN(PPQNResolution resolution); void setInputPPQN(PPQNResolution resolution); - void handleTimerInt(); + void handleInternalClock(); void handleExternalClock(); void resetCounters(); @@ -152,7 +153,6 @@ class uClockClass { void start(); void stop(); void pause(); - void continue_playing(); void setTempo(float bpm); float getTempo(); @@ -210,8 +210,8 @@ class uClockClass { void (*onSync48Callback)(uint32_t tick); void (*onClockStartCallback)(); void (*onClockStopCallback)(); - void (*onClockContinueCallback)(); void (*onClockPauseCallback)(); + void (*onClockContinueCallback)(); // clock input/output control PPQNResolution output_ppqn = PPQN_96; @@ -253,9 +253,9 @@ class uClockClass { uint32_t last_interval; uint32_t sync_interval; - float tempo; + volatile float tempo; + volatile ClockMode clock_mode; uint32_t start_timer; - ClockMode clock_mode; volatile uint32_t * ext_interval_buffer = nullptr; uint8_t ext_interval_buffer_size;