Tristan Rowley 23 hours ago committed by GitHub
commit 069e5039ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 8
      examples/AVRUartSlaveMidiClockMonitor/AVRUartSlaveMidiClockMonitor.ino
  2. 8
      examples/LeonardoUsbSlaveMidiClockMonitor/LeonardoUsbSlaveMidiClockMonitor.ino
  3. 8
      examples/TeensyUsbSlaveMidiClockMonitor/TeensyUsbSlaveMidiClockMonitor.ino
  4. 491
      src/uClock.cpp
  5. 18
      src/uClock.h

@ -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 {

@ -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 {

@ -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 {

@ -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();
}

@ -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;

Loading…
Cancel
Save