diff --git a/examples/AVRUartSlaveMidiClockMonitor/AVRUartSlaveMidiClockMonitor.ino b/examples/AVRUartSlaveMidiClockMonitor/AVRUartSlaveMidiClockMonitor.ino index c7c3cae..12e7b7c 100644 --- a/examples/AVRUartSlaveMidiClockMonitor/AVRUartSlaveMidiClockMonitor.ino +++ b/examples/AVRUartSlaveMidiClockMonitor/AVRUartSlaveMidiClockMonitor.ino @@ -144,16 +144,16 @@ void loop() { u8x8->drawUTF8(8 + 4, 7, " "); } } - if (clock_state != uClock.state) { - clock_state = uClock.state; + if (clock_state != uClock.clock_state) { + clock_state = uClock.clock_state; if (clock_state >= 1) { u8x8->drawUTF8(0, 7, "Playing"); } else { u8x8->drawUTF8(0, 7, "Stopped "); } } - if (clock_mode != uClock.getMode()) { - clock_mode = uClock.getMode(); + if (clock_mode != uClock.getClockMode()) { + clock_mode = uClock.getClockMode(); if (clock_mode == uClock.EXTERNAL_CLOCK) { u8x8->drawUTF8(10, 0, "Slave "); } else { diff --git a/examples/LeonardoUsbSlaveMidiClockMonitor/LeonardoUsbSlaveMidiClockMonitor.ino b/examples/LeonardoUsbSlaveMidiClockMonitor/LeonardoUsbSlaveMidiClockMonitor.ino index a7ac66e..f681444 100644 --- a/examples/LeonardoUsbSlaveMidiClockMonitor/LeonardoUsbSlaveMidiClockMonitor.ino +++ b/examples/LeonardoUsbSlaveMidiClockMonitor/LeonardoUsbSlaveMidiClockMonitor.ino @@ -135,16 +135,16 @@ void loop() { u8x8->drawUTF8(8+4, 7, " "); } } - if (clock_state != uClock.state) { - clock_state = uClock.state; + if (clock_state != uClock.clock_state) { + clock_state = uClock.clock_state; if (clock_state >= 1) { u8x8->drawUTF8(0, 7, "Playing"); } else { u8x8->drawUTF8(0, 7, "Stopped"); } } - if (clock_mode != uClock.getMode()) { - clock_mode = uClock.getMode(); + if (clock_mode != uClock.getClockMode()) { + clock_mode = uClock.getClockMode(); if (clock_mode == uClock.EXTERNAL_CLOCK) { u8x8->drawUTF8(10, 0, "Slave "); } else { diff --git a/examples/TeensyUsbSlaveMidiClockMonitor/TeensyUsbSlaveMidiClockMonitor.ino b/examples/TeensyUsbSlaveMidiClockMonitor/TeensyUsbSlaveMidiClockMonitor.ino index e6dc0c0..0c7e791 100644 --- a/examples/TeensyUsbSlaveMidiClockMonitor/TeensyUsbSlaveMidiClockMonitor.ino +++ b/examples/TeensyUsbSlaveMidiClockMonitor/TeensyUsbSlaveMidiClockMonitor.ino @@ -144,16 +144,16 @@ void loop() { u8x8->drawUTF8(8+4, 7, " "); } } - if (clock_state != uClock.state) { - clock_state = uClock.state; + if (clock_state != uClock.clock_state) { + clock_state = uClock.clock_state; if (clock_state >= 1) { u8x8->drawUTF8(0, 7, "Playing"); } else { u8x8->drawUTF8(0, 7, "Stopped"); } } - if (clock_mode != uClock.getMode()) { - clock_mode = uClock.getMode(); + if (clock_mode != uClock.getClockMode()) { + clock_mode = uClock.getClockMode(); if (clock_mode == uClock.EXTERNAL_CLOCK) { u8x8->drawUTF8(10, 0, "Slave "); } else { diff --git a/src/uClock.cpp b/src/uClock.cpp index 7923b9d..309bcae 100755 --- a/src/uClock.cpp +++ b/src/uClock.cpp @@ -81,7 +81,6 @@ #include "platforms/software.h" #endif - // // Platform specific timer setup/control // @@ -136,10 +135,17 @@ uClockClass::uClockClass() onStepCallback = nullptr; onClockStartCallback = nullptr; onClockStopCallback = nullptr; + onClockPauseCallback = nullptr; + onClockContinueCallback = nullptr; // initialize reference data calculateReferencedata(); } +uClockClass::~uClockClass() +{ + delete[] ext_interval_buffer; +} + void uClockClass::init() { if (ext_interval_buffer == nullptr) @@ -150,6 +156,246 @@ void uClockClass::init() setTempo(tempo); } +void uClockClass::handleInternalClock() +{ + // 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 && clock_state == STARTED) { + // 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::handleExternalClock() +{ + switch (clock_state) { + case SYNCING: + // set clock_mode as started, and goes on to calculate the first ext_interval + clock_state = STARTED; + // no break here, just go on to calculate our first ext_interval + + case STARTED: + uint32_t now_clock_us = micros(); + last_interval = clock_diff(ext_clock_us, now_clock_us); + ext_clock_us = now_clock_us; + + // 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; + + // external clock tick me! + ext_clock_tick++; + + // calculate sync 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; + + case PAUSED: + break; + + case STARTING: + clock_state = SYNCING; + ext_clock_us = micros(); + break; + } +} + +void uClockClass::clockMe() +{ + ATOMIC(handleExternalClock()) +} + +void uClockClass::start() +{ + resetCounters(); + start_timer = millis(); + + if (onClockStartCallback) + onClockStartCallback(); + + if (clock_mode == INTERNAL_CLOCK) { + ATOMIC(clock_state = STARTED) + } else { + ATOMIC(clock_state = STARTING) + } +} + +void uClockClass::stop() +{ + ATOMIC(clock_state = PAUSED) + resetCounters(); + start_timer = 0; + if (onClockStopCallback) + onClockStopCallback(); +} + +void uClockClass::pause() +{ + if (clock_state == PAUSED) { + if (clock_mode == INTERNAL_CLOCK) { + ATOMIC(clock_state = STARTED) + } else if (clock_mode == EXTERNAL_CLOCK) { + ATOMIC(clock_state = STARTING) + } + if (onClockContinueCallback) + onClockContinueCallback(); + } else { + ATOMIC(clock_state = PAUSED) + if (onClockPauseCallback) + onClockPauseCallback(); + } +} + +void uClockClass::setClockMode(ClockMode tempo_mode) +{ + ATOMIC(clock_mode = tempo_mode) + // trying to set external clock while playing? force sync ext_interval + if (tempo_mode == EXTERNAL_CLOCK && clock_state == STARTED) + ATOMIC(clock_state = STARTING) +} + +uClockClass::ClockMode uClockClass::getClockMode() +{ + return clock_mode; +} + uint32_t uClockClass::bpmToMicroSeconds(float bpm) { return (60000000.0f / (float)output_ppqn / bpm); @@ -188,43 +434,6 @@ void uClockClass::setInputPPQN(PPQNResolution resolution) ) } -void uClockClass::start() -{ - resetCounters(); - start_timer = millis(); - - if (onClockStartCallback) { - onClockStartCallback(); - } - - if (clock_mode == INTERNAL_CLOCK) { - clock_state = STARTED; - } else { - clock_state = STARTING; - } -} - -void uClockClass::stop() -{ - clock_state = PAUSED; - start_timer = 0; - resetCounters(); - if (onClockStopCallback) { - onClockStopCallback(); - } -} - -void uClockClass::pause() -{ - if (clock_mode == INTERNAL_CLOCK) { - if (clock_state == PAUSED) { - start(); - } else { - stop(); - } - } -} - void uClockClass::setTempo(float bpm) { if (clock_mode == EXTERNAL_CLOCK) { @@ -235,9 +444,7 @@ void uClockClass::setTempo(float bpm) return; } - ATOMIC( - tempo = bpm - ) + ATOMIC(tempo = bpm) setTimerTempo(bpm); } @@ -280,25 +487,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() -{ - if (clock_mode == EXTERNAL_CLOCK) { - ATOMIC( - handleExternalClock() - ) - } -} - void uClockClass::setExtIntervalBuffer(uint8_t buffer_size) { if (ext_interval_buffer != nullptr) @@ -306,7 +494,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() @@ -432,182 +620,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 - 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; - } -} - // elapsed time support uint8_t uClockClass::getNumberOfSeconds(uint32_t time) { @@ -665,7 +677,6 @@ void uClockHandler() // global timer counter _millis = millis(); - if (uClock.clock_state == uClock.STARTED) { - uClock.handleTimerInt(); - } + if (uClock.clock_state == uClock.STARTED || uClock.clock_state == uClock.SYNCING) + uClock.handleInternalClock(); } diff --git a/src/uClock.h b/src/uClock.h index b4fafaf..e9d4f31 100755 --- a/src/uClock.h +++ b/src/uClock.h @@ -66,6 +66,7 @@ class uClockClass { enum ClockState { PAUSED = 0, STARTING, + SYNCING, STARTED }; @@ -86,6 +87,7 @@ class uClockClass { ClockState clock_state; uClockClass(); + ~uClockClass(); void setOnOutputPPQN(void (*callback)(uint32_t tick)) { onOutputPPQNCallback = callback; @@ -132,11 +134,19 @@ class uClockClass { onClockStopCallback = 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(); @@ -201,6 +211,8 @@ class uClockClass { void (*onSync48Callback)(uint32_t tick); void (*onClockStartCallback)(); void (*onClockStopCallback)(); + void (*onClockPauseCallback)(); + void (*onClockContinueCallback)(); // clock input/output control PPQNResolution output_ppqn = PPQN_96; @@ -242,9 +254,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;