From 635c42de41a94d741f7c8e9184c8e1525a2da9de Mon Sep 17 00:00:00 2001 From: midilab Date: Tue, 22 Mar 2022 19:31:20 -0300 Subject: [PATCH 1/7] initial engine rewrited for avr and arm platforms. missing the sync part --- src/uClock.cpp | 148 +++++++++++++++++++++++-------------------------- src/uClock.h | 6 +- 2 files changed, 74 insertions(+), 80 deletions(-) diff --git a/src/uClock.cpp b/src/uClock.cpp index f7093e9..db7714b 100755 --- a/src/uClock.cpp +++ b/src/uClock.cpp @@ -1,8 +1,7 @@ /*! * @file uClock.cpp * Project BPM clock generator for Arduino - * @brief A Library to implement BPM clock tick calls using hardware timer1 interruption. Tested on ATmega168/328, ATmega16u4/32u4 and ATmega2560. - * Derived work from mididuino MidiClock class. (c) 2008 - 2011 - Manuel Odendahl - wesen@ruinwesen.com + * @brief A Library to implement BPM clock tick calls using hardware timer interruption. Tested on ATmega168/328, ATmega16u4/32u4 and ATmega2560. * @version 0.10.6 * @author Romulo Silva * @date 13/03/2022 @@ -28,16 +27,6 @@ */ #include "uClock.h" -// pickup a avr timer to make use. -// pickup only one! -// try to avoid timer0, only use it if you know what you are doing. -// 0 = delay(), millis() e micros() -// 1 = Servo.h library(any other?) -// 2 = tone() -//#define AVR_TIMER_0 -#define AVR_TIMER_1 -//#define AVR_TIMER_2 - // // Timer setup for work clock // @@ -46,68 +35,37 @@ IntervalTimer _uclockTimer; void uclockISR(); void uclockInitTimer() { - _uclockTimer.begin(uclockISR, 16); + ATOMIC( + + // begin at 120bpm (20833us) + _uclockTimer.begin(uclockISR, 20833); + // Set the interrupt priority level, controlling which other interrupts // this timer is allowed to interrupt. Lower numbers are higher priority, // with 0 the highest and 255 the lowest. Most other interrupts default to 128. // As a general guideline, interrupt routines that run longer should be given // lower priority (higher numerical values). _uclockTimer.priority(0); + ) } #else void uclockInitTimer() { -#if defined(AVR_TIMER_0) - ATOMIC( - // Timer0 init - TCCR0A = 0; - TCCR0B = 0; - TCNT0 = 0; - // set compare match register for 62500 Hz increments - // = 16000000 / (1 * 62500) - 1 (must be <256) - OCR0A = 255; - // turn on CTC mode - TCCR0B |= (1 << WGM02); - // Set CS02, CS01 and CS00 bits for 1 prescaler - TCCR0B |= (0 << CS02) | (0 << CS01) | (1 << CS00); - // enable timer compare interrupt - TIMSK0 |= (1 << OCIE0A); - ) -#endif -#if defined(AVR_TIMER_1) ATOMIC( // Timer1 init - TCCR1A = 0; - TCCR1B = 0; - TCNT1 = 0; - // set compare match register for 62500 Hz increments - // = 16000000 / (1 * 62500) - 1 (must be <65536) - OCR1A = 255; + // begin at 120bpm (48.0007680122882 Hz) + TCCR1A = 0; // set entire TCCR1A register to 0 + TCCR1B = 0; // same for TCCR1B + TCNT1 = 0; // initialize counter value to 0 + // set compare match register for 48.0007680122882 Hz increments + OCR1A = 41665; // = 16000000 / (8 * 48.0007680122882) - 1 (must be <65536) // turn on CTC mode TCCR1B |= (1 << WGM12); - // Set CS12, CS11 and CS10 bits for 1 prescaler - TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10); + // Set CS12, CS11 and CS10 bits for 8 prescaler + TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10); // enable timer compare interrupt - TIMSK1 |= (1 << OCIE1A); + TIMSK1 |= (1 << OCIE1A); ) -#endif -#if defined(AVR_TIMER_2) - ATOMIC( - // Timer2 init - TCCR2A = 0; - TCCR2B = 0; - TCNT2 = 0; - // set compare match register for 62500 Hz increments - // = 16000000 / (1 * 62500) - 1 (must be <256) - OCR2A = 255; - // turn on CTC mode - TCCR2B |= (1 << WGM22); - // Set CS22, CS21 and CS20 bits for 1 prescaler - TCCR2B |= (0 << CS22) | (0 << CS21) | (1 << CS20); - // enable timer compare interrupt - TIMSK2 |= (1 << OCIE2A); - ) -#endif } #endif @@ -195,6 +153,50 @@ void uClockClass::pause() } } +void uClockClass::setTimerTempo(float bpm) +{ + // 96 ppqn resolution + uint32_t tick_us_interval = (60000000 / 96 / bpm)*4; + +#if defined(TEENSYDUINO) && !defined(__AVR_ATmega32U4__) + ATOMIC( + _uclockTimer.update(tick_us_interval); + ) +#else + float tick_hertz_interval = 1/((float)tick_us_interval/1000000); + uint32_t ocr; + uint8_t tccr = 0; + + // 16bits avr timer setup + if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 1 )) < 65535) { + // Set CS12, CS11 and CS10 bits for 1 prescaler + tccr |= (0 << CS12) | (0 << CS11) | (1 << CS10); + } else if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 8 )) < 65535) { + // Set CS12, CS11 and CS10 bits for 8 prescaler + tccr |= (0 << CS12) | (1 << CS11) | (0 << CS10); + } else if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 64 )) < 65535) { + // Set CS12, CS11 and CS10 bits for 64 prescaler + tccr |= (0 << CS12) | (1 << CS11) | (1 << CS10); + } else if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 256 )) < 65535) { + // Set CS12, CS11 and CS10 bits for 256 prescaler + tccr |= (1 << CS12) | (0 << CS11) | (0 << CS10); + } else if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 1024 )) < 65535) { + // Set CS12, CS11 and CS10 bits for 1024 prescaler + tccr |= (1 << CS12) | (0 << CS11) | (1 << CS10); + } else { + // tempo not achiavable + return; + } + + ATOMIC( + TCCR1B = 0; + OCR1A = ocr-1; + TCCR1B |= (1 << WGM12); + TCCR1B |= tccr; + ) +#endif +} + void uClockClass::setTempo(float bpm) { if (mode == EXTERNAL_CLOCK) { @@ -205,13 +207,9 @@ void uClockClass::setTempo(float bpm) return; } - tempo = bpm; + setTimerTempo(bpm); - ATOMIC( - //interval = (freq_resolution / (tempo * 24 / 60)) - drift; - //interval = 62500 / (tempo * 24 / 60) - drift; - interval = (uint16_t)((156250.0 / tempo) - drift); - ) + tempo = bpm; } float uClockClass::getTempo() @@ -223,9 +221,9 @@ float uClockClass::getTempo() } if (acc != 0) { // get average interval, because MIDI sync world is a wild place... - //tempo = (((float)freq_resolution/24) * 60) / (float)(acc / EXT_INTERVAL_BUFFER_SIZE); + ////tempo = (float)((((float)FREQ_RESOLUTION/24) * 60) / (float)(acc / EXT_INTERVAL_BUFFER_SIZE)); // derivated one time calc value = ( freq_resolution / 24 ) * 60 - tempo = (float)(156250.0 / (acc / EXT_INTERVAL_BUFFER_SIZE)); + //tempo = (float)(156250.0 / (acc / EXT_INTERVAL_BUFFER_SIZE)); } } return tempo; @@ -350,7 +348,7 @@ void uClockClass::handleExternalClock() void uClockClass::handleTimerInt() { - if (counter == 0) { + //if (counter == 0) { // update internal clock base counter counter = interval; @@ -400,9 +398,9 @@ void uClockClass::handleTimerInt() mod6_counter = 0; } - } else { - counter--; - } + //} else { + // counter--; + //} } // elapsed time support @@ -457,21 +455,13 @@ volatile uint32_t _timer = 0; // // TIMER INTERRUPT HANDLER -// Clocked at: 62.5kHz/16usec +// // #if defined(TEENSYDUINO) && !defined(__AVR_ATmega32U4__) void uclockISR() #else -#if defined(AVR_TIMER_0) -ISR(TIMER0_COMPA_vect) -#endif -#if defined(AVR_TIMER_1) ISR(TIMER1_COMPA_vect) #endif -#if defined(AVR_TIMER_2) -ISR(TIMER2_COMPA_vect) -#endif -#endif { // global timer counter _timer = millis(); diff --git a/src/uClock.h b/src/uClock.h index 520b4dd..5cb9115 100755 --- a/src/uClock.h +++ b/src/uClock.h @@ -35,6 +35,8 @@ namespace umodular { namespace clock { +#define AVR_CLOCK_FREQ 16000000 + #define PHASE_FACTOR 16 #define EXT_INTERVAL_BUFFER_SIZE 24 @@ -52,6 +54,8 @@ class uClockClass { private: + void setTimerTempo(float bpm); + void (*onClock96PPQNCallback)(uint32_t * tick); void (*onClock32PPQNCallback)(uint32_t * tick); void (*onClock16PPQNCallback)(uint32_t * tick); @@ -83,6 +87,7 @@ class uClockClass { uint32_t ext_interval_acc; uint16_t ext_interval_idx; + public: enum { @@ -153,7 +158,6 @@ class uClockClass { uint8_t getNumberOfDays(uint32_t time); uint32_t getNowTimer(); uint32_t getPlayTime(); - }; } } // end namespace umodular::clock From 717489542601b7c3e8a05019c6762a38dfcf4b77 Mon Sep 17 00:00:00 2001 From: midilab Date: Thu, 24 Mar 2022 09:23:33 -0300 Subject: [PATCH 2/7] initial slave sync new implementation setup, need some tests and tunnings... --- src/uClock.cpp | 94 ++++++++++---------------------------------------- src/uClock.h | 21 +++-------- 2 files changed, 23 insertions(+), 92 deletions(-) diff --git a/src/uClock.cpp b/src/uClock.cpp index db7714b..62d1302 100755 --- a/src/uClock.cpp +++ b/src/uClock.cpp @@ -87,9 +87,6 @@ static inline uint16_t clock_diff(uint16_t old_clock, uint16_t new_clock) uClockClass::uClockClass() { - // drift is used to sligth calibrate with your slave clock - drift = 1; - slave_drift = 0; pll_x = 220; tempo = 120; start_timer = 0; @@ -97,7 +94,6 @@ uClockClass::uClockClass() sync_interval = 0; state = PAUSED; mode = INTERNAL_CLOCK; - ext_interval_acc = 0; resetCounters(); onClock96PPQNCallback = NULL; @@ -112,7 +108,6 @@ uClockClass::uClockClass() void uClockClass::init() { - // init work clock timer interrupt at 16 microseconds uclockInitTimer(); } @@ -156,14 +151,14 @@ void uClockClass::pause() void uClockClass::setTimerTempo(float bpm) { // 96 ppqn resolution - uint32_t tick_us_interval = (60000000 / 96 / bpm)*4; + tick_us_interval = (60000000 / 24 / bpm); + tick_hertz_interval = 1/((float)tick_us_interval/1000000); #if defined(TEENSYDUINO) && !defined(__AVR_ATmega32U4__) ATOMIC( _uclockTimer.update(tick_us_interval); ) #else - float tick_hertz_interval = 1/((float)tick_us_interval/1000000); uint32_t ocr; uint8_t tccr = 0; @@ -214,62 +209,9 @@ void uClockClass::setTempo(float bpm) float uClockClass::getTempo() { - if (mode == EXTERNAL_CLOCK) { - uint32_t acc = 0; - for (uint8_t i=0; i < EXT_INTERVAL_BUFFER_SIZE; i++) { - acc += ext_interval_buffer[i]; - } - if (acc != 0) { - // get average interval, because MIDI sync world is a wild place... - ////tempo = (float)((((float)FREQ_RESOLUTION/24) * 60) / (float)(acc / EXT_INTERVAL_BUFFER_SIZE)); - // derivated one time calc value = ( freq_resolution / 24 ) * 60 - //tempo = (float)(156250.0 / (acc / EXT_INTERVAL_BUFFER_SIZE)); - } - } return tempo; } -void uClockClass::setDrift(uint8_t value) -{ - ATOMIC(drift = value) - // force set tempo to update runtime interval - setTempo(tempo); -} - -void uClockClass::setSlaveDrift(uint8_t value) -{ - ATOMIC(slave_drift = value) -} - -uint8_t uClockClass::getDrift() -{ - return drift; -} - -// each interval is 16us -// this method is usefull for debug -uint16_t uClockClass::getInterval() -{ - return interval; -} - -// Main poolling tick call -uint8_t uClockClass::getTick(uint32_t * tick) -{ - ATOMIC( - uint32_t last_tick = internal_tick; - ) - if (*tick != last_tick) { - *tick = last_tick; - return 1; - } - if (last_tick - *tick > 1) { - *tick++; - return 1; - } - return 0; -} - void uClockClass::setMode(uint8_t tempo_mode) { mode = tempo_mode; @@ -292,14 +234,12 @@ void uClockClass::clockMe() void uClockClass::resetCounters() { counter = 0; - last_clock = 0; + external_clock = 0; internal_tick = 0; external_tick = 0; div32th_counter = 0; div16th_counter = 0; mod6_counter = 0; - inmod6_counter = 0; - ext_interval_idx = 0; } // TODO: Tap stuff @@ -316,8 +256,9 @@ void uClockClass::shuffle() void uClockClass::handleExternalClock() { - last_interval = clock_diff(last_clock, _clock); - last_clock = _clock; + uint32_t u_timer = micros(); + last_interval = clock_diff(external_clock, u_timer); + external_clock = u_timer; // slave tick me! external_tick++; @@ -331,12 +272,6 @@ void uClockClass::handleExternalClock() break; case STARTED: - // accumulate interval incomming ticks data for getTempo() smooth reads on slave mode - if(++ext_interval_idx >= EXT_INTERVAL_BUFFER_SIZE) { - ext_interval_idx = 0; - } - ext_interval_buffer[ext_interval_idx] = last_interval; - if (external_tick == 1) { interval = last_interval; } else { @@ -349,11 +284,9 @@ void uClockClass::handleExternalClock() void uClockClass::handleTimerInt() { //if (counter == 0) { - // update internal clock base counter + counter = interval; - // need a callback? - // please, use the polling method with getTick() instead... if (onClock96PPQNCallback) { onClock96PPQNCallback(&internal_tick); } @@ -381,7 +314,8 @@ void uClockClass::handleTimerInt() mod6_counter++; if (mode == EXTERNAL_CLOCK) { - sync_interval = clock_diff(last_clock, _clock); + uint32_t u_timer = micros(); + sync_interval = clock_diff(external_clock, u_timer); if ((internal_tick < external_tick) || (internal_tick > (external_tick + 1))) { internal_tick = external_tick; } @@ -392,6 +326,16 @@ void uClockClass::handleTimerInt() counter += phase_mult(counter - sync_interval); } } + + // update internal clock base counter + float diff = 1/((float)counter/1000000.0); + float bpm = (float)((float)(diff/24.0) * 60.0); + if (bpm != tempo) { + if (bpm > 1 && bpm < 300) { + tempo = bpm; + setTimerTempo(tempo); + } + } } if (mod6_counter == 6) { diff --git a/src/uClock.h b/src/uClock.h index 5cb9115..550fb0e 100755 --- a/src/uClock.h +++ b/src/uClock.h @@ -39,8 +39,6 @@ namespace umodular { namespace clock { #define PHASE_FACTOR 16 -#define EXT_INTERVAL_BUFFER_SIZE 24 - #define SECS_PER_MIN (60UL) #define SECS_PER_HOUR (3600UL) #define SECS_PER_DAY (SECS_PER_HOUR * 24L) @@ -64,9 +62,10 @@ class uClockClass { volatile uint32_t internal_tick; volatile uint32_t external_tick; - volatile uint16_t interval; - volatile uint16_t last_clock; - volatile uint8_t inmod6_counter; + volatile uint32_t interval; + volatile uint32_t external_clock; + uint32_t tick_us_interval; + float tick_hertz_interval; uint32_t div32th_counter; uint32_t div16th_counter; uint8_t mod6_counter; @@ -74,8 +73,6 @@ class uClockClass { uint16_t pll_x; uint32_t last_tick; - uint8_t drift; - uint8_t slave_drift; float tempo; uint32_t start_timer; uint8_t mode; @@ -83,11 +80,6 @@ class uClockClass { uint16_t last_interval; uint16_t sync_interval; - uint16_t ext_interval_buffer[EXT_INTERVAL_BUFFER_SIZE]; - uint32_t ext_interval_acc; - uint16_t ext_interval_idx; - - public: enum { @@ -136,11 +128,6 @@ class uClockClass { void pause(); void setTempo(float bpm); float getTempo(); - void setDrift(uint8_t value); - uint8_t getDrift(); - void setSlaveDrift(uint8_t value); - uint16_t getInterval(); - uint8_t getTick(uint32_t *_tick); // external timming control void setMode(uint8_t tempo_mode); From 59cd1c7e18f7ea0167b617a8deaa0913e1d5f427 Mon Sep 17 00:00:00 2001 From: midilab Date: Fri, 25 Mar 2022 11:08:04 -0300 Subject: [PATCH 3/7] tests and tunning for external sync and phase_mult system --- src/uClock.cpp | 158 +++++++++++++++++++++++++++++++------------------ src/uClock.h | 29 +++++++-- 2 files changed, 122 insertions(+), 65 deletions(-) diff --git a/src/uClock.cpp b/src/uClock.cpp index 62d1302..55aaada 100755 --- a/src/uClock.cpp +++ b/src/uClock.cpp @@ -76,12 +76,12 @@ static inline uint32_t phase_mult(uint32_t val) return (val * PHASE_FACTOR) >> 8; } -static inline uint16_t clock_diff(uint16_t old_clock, uint16_t new_clock) +static inline uint16_t clock_diff(uint32_t old_clock, uint32_t new_clock) { if (new_clock >= old_clock) { return new_clock - old_clock; } else { - return new_clock + (65535 - old_clock); + return new_clock + (4294967295 - old_clock); } } @@ -207,8 +207,23 @@ void uClockClass::setTempo(float bpm) tempo = bpm; } +float inline uClockClass::freqToBpm(uint32_t freq) +{ + float usecs = 1/((float)freq/1000000.0); + return (float)((float)(usecs/24.0) * 60.0); +} + float uClockClass::getTempo() { + if (mode == EXTERNAL_CLOCK) { + uint32_t acc = 0; + for (uint8_t i=0; i < EXT_INTERVAL_BUFFER_SIZE; i++) { + acc += ext_interval_buffer[i]; + } + if (acc != 0) { + return freqToBpm(acc / EXT_INTERVAL_BUFFER_SIZE); + } + } return tempo; } @@ -239,7 +254,11 @@ void uClockClass::resetCounters() external_tick = 0; div32th_counter = 0; div16th_counter = 0; - mod6_counter = 0; + mod6_counter = 0; + indiv32th_counter = 0; + indiv16th_counter = 0; + inmod6_counter = 0; + ext_interval_idx = 0; } // TODO: Tap stuff @@ -256,12 +275,6 @@ void uClockClass::shuffle() void uClockClass::handleExternalClock() { - uint32_t u_timer = micros(); - last_interval = clock_diff(external_clock, u_timer); - external_clock = u_timer; - - // slave tick me! - external_tick++; switch (state) { case PAUSED: @@ -272,6 +285,34 @@ void uClockClass::handleExternalClock() break; case STARTED: + + uint32_t u_timer = micros(); + last_interval = clock_diff(external_clock, u_timer); + external_clock = u_timer; + + if (inmod6_counter == 0) { + indiv16th_counter++; + indiv32th_counter++; + } + + if (inmod6_counter == 3) { + indiv32th_counter++; + } + + // slave tick me! + external_tick++; + inmod6_counter++; + + if (inmod6_counter == 6) { + inmod6_counter = 0; + } + + // accumulate interval incomming ticks data for getTempo() smooth reads on slave mode + if(++ext_interval_idx >= EXT_INTERVAL_BUFFER_SIZE) { + ext_interval_idx = 0; + } + ext_interval_buffer[ext_interval_idx] = last_interval; + if (external_tick == 1) { interval = last_interval; } else { @@ -283,68 +324,67 @@ void uClockClass::handleExternalClock() void uClockClass::handleTimerInt() { - //if (counter == 0) { + counter = interval; + + if ((internal_tick < external_tick) || (internal_tick > (external_tick + 1))) { + internal_tick = external_tick; + div32th_counter = indiv32th_counter; + div16th_counter = indiv16th_counter; + mod6_counter = inmod6_counter; + } - counter = interval; + if (onClock96PPQNCallback) { + onClock96PPQNCallback(&internal_tick); + } - if (onClock96PPQNCallback) { - onClock96PPQNCallback(&internal_tick); + if (mod6_counter == 0) { + if (onClock32PPQNCallback) { + onClock32PPQNCallback(&div32th_counter); } - - if (mod6_counter == 0) { - if (onClock32PPQNCallback) { - onClock32PPQNCallback(&div32th_counter); - } - if (onClock16PPQNCallback) { - onClock16PPQNCallback(&div16th_counter); - } - div16th_counter++; - div32th_counter++; + if (onClock16PPQNCallback) { + onClock16PPQNCallback(&div16th_counter); } + div16th_counter++; + div32th_counter++; + } - if (mod6_counter == 3) { - if (onClock32PPQNCallback) { - onClock32PPQNCallback(&div32th_counter); - } - div32th_counter++; + if (mod6_counter == 3) { + if (onClock32PPQNCallback) { + onClock32PPQNCallback(&div32th_counter); } + div32th_counter++; + } - // tick me! - internal_tick++; - mod6_counter++; + // tick me! + internal_tick++; + mod6_counter++; - if (mode == EXTERNAL_CLOCK) { - uint32_t u_timer = micros(); - sync_interval = clock_diff(external_clock, u_timer); - if ((internal_tick < external_tick) || (internal_tick > (external_tick + 1))) { - internal_tick = external_tick; - } - if (internal_tick <= external_tick) { - counter -= phase_mult(sync_interval); - } else { - if (counter > sync_interval) { - counter += phase_mult(counter - sync_interval); - } - } + if (mod6_counter == 6) { + mod6_counter = 0; + } - // update internal clock base counter - float diff = 1/((float)counter/1000000.0); - float bpm = (float)((float)(diff/24.0) * 60.0); - if (bpm != tempo) { - if (bpm > 1 && bpm < 300) { - tempo = bpm; - setTimerTempo(tempo); - } + if (mode == EXTERNAL_CLOCK) { + uint32_t u_timer = micros(); + sync_interval = clock_diff(external_clock, u_timer); + + if (internal_tick <= external_tick) { + counter -= phase_mult(sync_interval); + } else { + if (counter > sync_interval) { + counter += phase_mult(counter - sync_interval); } } - - if (mod6_counter == 6) { - mod6_counter = 0; - } - //} else { - // counter--; - //} + // update internal clock timer frequency + float bpm = freqToBpm(counter); + if (bpm != tempo) { + if (bpm >= MIN_BPM && bpm <= MAX_BPM) { + tempo = bpm; + setTimerTempo(tempo); + } + } + } + } // elapsed time support diff --git a/src/uClock.h b/src/uClock.h index 550fb0e..ea0109c 100755 --- a/src/uClock.h +++ b/src/uClock.h @@ -39,6 +39,8 @@ namespace umodular { namespace clock { #define PHASE_FACTOR 16 +#define EXT_INTERVAL_BUFFER_SIZE 24 + #define SECS_PER_MIN (60UL) #define SECS_PER_HOUR (3600UL) #define SECS_PER_DAY (SECS_PER_HOUR * 24L) @@ -53,6 +55,7 @@ class uClockClass { private: void setTimerTempo(float bpm); + float inline freqToBpm(uint32_t freq); void (*onClock96PPQNCallback)(uint32_t * tick); void (*onClock32PPQNCallback)(uint32_t * tick); @@ -60,16 +63,27 @@ class uClockClass { void (*onClockStartCallback)(); void (*onClockStopCallback)(); - volatile uint32_t internal_tick; - volatile uint32_t external_tick; + volatile uint32_t interval; volatile uint32_t external_clock; uint32_t tick_us_interval; float tick_hertz_interval; - uint32_t div32th_counter; - uint32_t div16th_counter; - uint8_t mod6_counter; - uint16_t counter; + + volatile uint32_t internal_tick; + volatile uint32_t external_tick; + + // tick external and internal control + volatile uint32_t indiv32th_counter; + volatile uint32_t indiv16th_counter; + volatile uint8_t inmod6_counter; + + volatile uint32_t div96th_counter; + volatile uint32_t div32th_counter; + volatile uint32_t div16th_counter; + volatile uint8_t mod6_counter; + + + uint32_t counter; uint16_t pll_x; uint32_t last_tick; @@ -80,6 +94,9 @@ class uClockClass { uint16_t last_interval; uint16_t sync_interval; + uint16_t ext_interval_buffer[EXT_INTERVAL_BUFFER_SIZE]; + uint16_t ext_interval_idx; + public: enum { From 16d0ea98042d13505a17a495d84ce10bc9b73c48 Mon Sep 17 00:00:00 2001 From: midilab Date: Fri, 25 Mar 2022 12:49:12 -0300 Subject: [PATCH 4/7] tests and tunnings --- src/uClock.cpp | 55 +++++++++++++++++++++++++------------------------- src/uClock.h | 1 - 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/uClock.cpp b/src/uClock.cpp index 55aaada..b73f9ed 100755 --- a/src/uClock.cpp +++ b/src/uClock.cpp @@ -324,13 +324,34 @@ void uClockClass::handleExternalClock() void uClockClass::handleTimerInt() { - counter = interval; + if (mode == EXTERNAL_CLOCK) { + if ((internal_tick < external_tick) || (internal_tick > (external_tick + 1))) { + internal_tick = external_tick; + div32th_counter = indiv32th_counter; + div16th_counter = indiv16th_counter; + mod6_counter = inmod6_counter; + } + + counter = interval; + uint32_t u_timer = micros(); + sync_interval = clock_diff(external_clock, u_timer); + + if (internal_tick <= external_tick) { + counter -= phase_mult(sync_interval); + } else { + if (counter > sync_interval) { + counter += phase_mult(counter - sync_interval); + } + } - if ((internal_tick < external_tick) || (internal_tick > (external_tick + 1))) { - internal_tick = external_tick; - div32th_counter = indiv32th_counter; - div16th_counter = indiv16th_counter; - mod6_counter = inmod6_counter; + // update internal clock timer frequency + float bpm = freqToBpm(counter); + if (bpm != tempo) { + if (bpm >= MIN_BPM && bpm <= MAX_BPM) { + tempo = bpm; + setTimerTempo(tempo); + } + } } if (onClock96PPQNCallback) { @@ -362,28 +383,6 @@ void uClockClass::handleTimerInt() if (mod6_counter == 6) { mod6_counter = 0; } - - if (mode == EXTERNAL_CLOCK) { - uint32_t u_timer = micros(); - sync_interval = clock_diff(external_clock, u_timer); - - if (internal_tick <= external_tick) { - counter -= phase_mult(sync_interval); - } else { - if (counter > sync_interval) { - counter += phase_mult(counter - sync_interval); - } - } - - // update internal clock timer frequency - float bpm = freqToBpm(counter); - if (bpm != tempo) { - if (bpm >= MIN_BPM && bpm <= MAX_BPM) { - tempo = bpm; - setTimerTempo(tempo); - } - } - } } diff --git a/src/uClock.h b/src/uClock.h index ea0109c..d69b455 100755 --- a/src/uClock.h +++ b/src/uClock.h @@ -63,7 +63,6 @@ class uClockClass { void (*onClockStartCallback)(); void (*onClockStopCallback)(); - volatile uint32_t interval; volatile uint32_t external_clock; uint32_t tick_us_interval; From 2a2430573a6e415b4ea7e4618cdd347367ab7ff5 Mon Sep 17 00:00:00 2001 From: midilab Date: Wed, 30 Mar 2022 07:31:45 -0300 Subject: [PATCH 5/7] first review and qa tests --- src/uClock.cpp | 14 +++++++------- src/uClock.h | 38 ++++++++++++++++++-------------------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/uClock.cpp b/src/uClock.cpp index b73f9ed..62640df 100755 --- a/src/uClock.cpp +++ b/src/uClock.cpp @@ -76,7 +76,7 @@ static inline uint32_t phase_mult(uint32_t val) return (val * PHASE_FACTOR) >> 8; } -static inline uint16_t clock_diff(uint32_t old_clock, uint32_t new_clock) +static inline uint32_t clock_diff(uint32_t old_clock, uint32_t new_clock) { if (new_clock >= old_clock) { return new_clock - old_clock; @@ -87,7 +87,6 @@ static inline uint16_t clock_diff(uint32_t old_clock, uint32_t new_clock) uClockClass::uClockClass() { - pll_x = 220; tempo = 120; start_timer = 0; last_interval = 0; @@ -248,7 +247,6 @@ void uClockClass::clockMe() void uClockClass::resetCounters() { - counter = 0; external_clock = 0; internal_tick = 0; external_tick = 0; @@ -287,6 +285,9 @@ void uClockClass::handleExternalClock() case STARTED: uint32_t u_timer = micros(); + if(external_tick == 0) { + external_clock = u_timer; + } last_interval = clock_diff(external_clock, u_timer); external_clock = u_timer; @@ -316,7 +317,7 @@ void uClockClass::handleExternalClock() if (external_tick == 1) { interval = last_interval; } else { - interval = (((uint32_t)interval * (uint32_t)pll_x) + (uint32_t)(256 - pll_x) * (uint32_t)last_interval) >> 8; + interval = (((uint32_t)interval * (uint32_t)PLL_X) + (uint32_t)(256 - PLL_X) * (uint32_t)last_interval) >> 8; } break; } @@ -325,6 +326,7 @@ void uClockClass::handleExternalClock() void uClockClass::handleTimerInt() { if (mode == EXTERNAL_CLOCK) { + // sync tick position with external tick clock if ((internal_tick < external_tick) || (internal_tick > (external_tick + 1))) { internal_tick = external_tick; div32th_counter = indiv32th_counter; @@ -332,7 +334,7 @@ void uClockClass::handleTimerInt() mod6_counter = inmod6_counter; } - counter = interval; + uint32_t counter = interval; uint32_t u_timer = micros(); sync_interval = clock_diff(external_clock, u_timer); @@ -433,7 +435,6 @@ uint32_t uClockClass::getPlayTime() umodular::clock::uClockClass uClock; -volatile uint16_t _clock = 0; volatile uint32_t _timer = 0; // @@ -450,7 +451,6 @@ ISR(TIMER1_COMPA_vect) _timer = millis(); if (uClock.state == uClock.STARTED) { - _clock++; uClock.handleTimerInt(); } } diff --git a/src/uClock.h b/src/uClock.h index d69b455..7f1a093 100755 --- a/src/uClock.h +++ b/src/uClock.h @@ -39,6 +39,11 @@ namespace umodular { namespace clock { #define PHASE_FACTOR 16 +#define PLL_X 220 + +// for smooth slave tempo calculate display you should raise this value +// in between 64 to 128. +// note: this doesn't impact on sync time, only display time getTempo() #define EXT_INTERVAL_BUFFER_SIZE 24 #define SECS_PER_MIN (60UL) @@ -63,37 +68,30 @@ class uClockClass { void (*onClockStartCallback)(); void (*onClockStopCallback)(); - volatile uint32_t interval; - volatile uint32_t external_clock; - uint32_t tick_us_interval; - float tick_hertz_interval; - + // internal clock control volatile uint32_t internal_tick; - volatile uint32_t external_tick; - - // tick external and internal control - volatile uint32_t indiv32th_counter; - volatile uint32_t indiv16th_counter; - volatile uint8_t inmod6_counter; - - volatile uint32_t div96th_counter; volatile uint32_t div32th_counter; volatile uint32_t div16th_counter; volatile uint8_t mod6_counter; + // external clock control + volatile uint32_t external_clock; + volatile uint32_t external_tick; + volatile uint32_t indiv32th_counter; + volatile uint32_t indiv16th_counter; + volatile uint8_t inmod6_counter; + volatile uint32_t interval; + volatile uint32_t last_interval; + uint32_t sync_interval; - uint32_t counter; - uint16_t pll_x; + uint32_t tick_us_interval; + float tick_hertz_interval; - uint32_t last_tick; float tempo; uint32_t start_timer; uint8_t mode; - uint16_t last_interval; - uint16_t sync_interval; - - uint16_t ext_interval_buffer[EXT_INTERVAL_BUFFER_SIZE]; + volatile uint32_t ext_interval_buffer[EXT_INTERVAL_BUFFER_SIZE]; uint16_t ext_interval_idx; public: From f6d36e76aae124c739490bbc1c2d8aae2835bd9e Mon Sep 17 00:00:00 2001 From: midilab Date: Wed, 30 Mar 2022 07:43:59 -0300 Subject: [PATCH 6/7] initialize external_clock at slave first start state --- src/uClock.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/uClock.cpp b/src/uClock.cpp index 62640df..a6a11bf 100755 --- a/src/uClock.cpp +++ b/src/uClock.cpp @@ -280,14 +280,12 @@ void uClockClass::handleExternalClock() case STARTING: state = STARTED; + external_clock = micros(); break; case STARTED: uint32_t u_timer = micros(); - if(external_tick == 0) { - external_clock = u_timer; - } last_interval = clock_diff(external_clock, u_timer); external_clock = u_timer; From 31e632ccda34b5e8827c765164d0478e2de41aa6 Mon Sep 17 00:00:00 2001 From: midilab Date: Fri, 1 Apr 2022 13:00:39 -0300 Subject: [PATCH 7/7] rewrite clock engine for more horse power process between clock calls --- README.md | 15 --------------- .../LeonardoUsbSlaveMidiClockMonitor.ino | 2 -- .../TeensyUsbMasterMidiClock.ino | 2 -- .../TeensyUsbSlaveMidiClock.ino | 2 -- .../TeensyUsbSlaveMidiClockMonitor.ino | 7 ------- src/uClock.cpp | 6 +++--- src/uClock.h | 10 +++++----- 7 files changed, 8 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index f0f4cc1..76b3d2c 100755 --- a/README.md +++ b/README.md @@ -97,8 +97,6 @@ void onClockStop() { } void setup() { - // drift for USB Teensy - uClock.setDrift(1); // Inits the clock uClock.init(); // Set the callback function for the clock output to send MIDI Sync message. @@ -317,16 +315,3 @@ void loop() //processYourPots(); } ``` - -## Troubleshooting - -If you slave host are not showing correct of close bpm on sync, please try to use the drift variable to adjust. It normaly goes from value 4(good for clock over USB) to 11(good for common MIDI interfaces running at 31250 speed). The default value is 11. - -To use MIDI via USB on Leonardo boards please start setting the drift to 4: -uClock.setDrift(4); - -For teensy boards and USB Midi try: -uClock.setDrift(1); - -Please use uClock.setDrift() before uClock.init(); - diff --git a/examples/LeonardoUsbSlaveMidiClockMonitor/LeonardoUsbSlaveMidiClockMonitor.ino b/examples/LeonardoUsbSlaveMidiClockMonitor/LeonardoUsbSlaveMidiClockMonitor.ino index 18a1013..1c0d0b4 100644 --- a/examples/LeonardoUsbSlaveMidiClockMonitor/LeonardoUsbSlaveMidiClockMonitor.ino +++ b/examples/LeonardoUsbSlaveMidiClockMonitor/LeonardoUsbSlaveMidiClockMonitor.ino @@ -103,8 +103,6 @@ void setup() { // // uClock Setup // - // Drift for arudino leonardo over USB as MIDI HID - uClock.setSlaveDrift(10); uClock.init(); uClock.setClock96PPQNOutput(ClockOut96PPQN); // For MIDI Sync Start and Stop diff --git a/examples/TeensyUsbMasterMidiClock/TeensyUsbMasterMidiClock.ino b/examples/TeensyUsbMasterMidiClock/TeensyUsbMasterMidiClock.ino index 436a8dd..3c27364 100644 --- a/examples/TeensyUsbMasterMidiClock/TeensyUsbMasterMidiClock.ino +++ b/examples/TeensyUsbMasterMidiClock/TeensyUsbMasterMidiClock.ino @@ -55,8 +55,6 @@ void setup() { pinMode(LED_BUILTIN, OUTPUT); // Setup our clock system - // drift for USB Teensy - uClock.setDrift(1); // Inits the clock uClock.init(); // Set the callback function for the clock output to send MIDI Sync message. diff --git a/examples/TeensyUsbSlaveMidiClock/TeensyUsbSlaveMidiClock.ino b/examples/TeensyUsbSlaveMidiClock/TeensyUsbSlaveMidiClock.ino index 1fde0b8..a8aa6f0 100644 --- a/examples/TeensyUsbSlaveMidiClock/TeensyUsbSlaveMidiClock.ino +++ b/examples/TeensyUsbSlaveMidiClock/TeensyUsbSlaveMidiClock.ino @@ -76,8 +76,6 @@ void setup() { usbMIDI.setHandleStop(onExternalStop); // Setup our clock system - // drift for USB Teensy - uClock.setDrift(1); // Inits the clock uClock.init(); // Set the callback function for the clock output to send MIDI Sync message. diff --git a/examples/TeensyUsbSlaveMidiClockMonitor/TeensyUsbSlaveMidiClockMonitor.ino b/examples/TeensyUsbSlaveMidiClockMonitor/TeensyUsbSlaveMidiClockMonitor.ino index 533cb26..835b028 100644 --- a/examples/TeensyUsbSlaveMidiClockMonitor/TeensyUsbSlaveMidiClockMonitor.ino +++ b/examples/TeensyUsbSlaveMidiClockMonitor/TeensyUsbSlaveMidiClockMonitor.ino @@ -11,11 +11,6 @@ * - u8g2 * - uClock * - * This example make use of drift values (6, 1) - * respectively for internal and external drift reference. - * This example was tested on a macbook - * running ableton live 9 as master clock - * * This example code is in the public domain. */ @@ -111,8 +106,6 @@ void setup() { // uClock Setup // // Setup our clock system - // drift for USB Teensy - uClock.setDrift(1); uClock.init(); uClock.setClock96PPQNOutput(ClockOut96PPQN); // For MIDI Sync Start and Stop diff --git a/src/uClock.cpp b/src/uClock.cpp index a6a11bf..43cde97 100755 --- a/src/uClock.cpp +++ b/src/uClock.cpp @@ -1,10 +1,10 @@ /*! * @file uClock.cpp * Project BPM clock generator for Arduino - * @brief A Library to implement BPM clock tick calls using hardware timer interruption. Tested on ATmega168/328, ATmega16u4/32u4 and ATmega2560. - * @version 0.10.6 + * @brief A Library to implement BPM clock tick calls using hardware timer interruption. Tested on ATmega168/328, ATmega16u4/32u4 and ATmega2560 and Teensy LC. + * @version 1.0.0 * @author Romulo Silva - * @date 13/03/2022 + * @date 01/04/2022 * @license MIT - (c) 2022 - Romulo Silva - contact@midilab.co * * Permission is hereby granted, free of charge, to any person obtaining a diff --git a/src/uClock.h b/src/uClock.h index 7f1a093..6a3c18f 100755 --- a/src/uClock.h +++ b/src/uClock.h @@ -1,11 +1,10 @@ /*! - * @file uClock.cpp + * @file uClock.h * Project BPM clock generator for Arduino - * @brief A Library to implement BPM clock tick calls using hardware timer1 interruption. Tested on ATmega168/328, ATmega16u4/32u4 and ATmega2560. - * Derived work from mididuino MidiClock class. (c) 2008 - 2011 - Manuel Odendahl - wesen@ruinwesen.com - * @version 0.10.6 + * @brief A Library to implement BPM clock tick calls using hardware timer interruption. Tested on ATmega168/328, ATmega16u4/32u4 and ATmega2560 and Teensy LC. + * @version 1.0.0 * @author Romulo Silva - * @date 13/03/2022 + * @date 01/04/2022 * @license MIT - (c) 2022 - Romulo Silva - contact@midilab.co * * Permission is hereby granted, free of charge, to any person obtaining a @@ -44,6 +43,7 @@ namespace umodular { namespace clock { // for smooth slave tempo calculate display you should raise this value // in between 64 to 128. // note: this doesn't impact on sync time, only display time getTempo() +// if you dont want to use it, set it to 1 for memory save #define EXT_INTERVAL_BUFFER_SIZE 24 #define SECS_PER_MIN (60UL)