added setExtIntervalBuffer(uint8_t buffer_size) for getTempo() average calculus setup. should be called before uClock.init();

pull/55/head
midilab 3 weeks ago
parent 0c20a494eb
commit 3ea0983dd6
  1. 103
      src/uClock.cpp
  2. 37
      src/uClock.h

@ -23,7 +23,7 @@
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE. * DEALINGS IN THE SOFTWARE.
*/ */
#include "uClock.h" #include "uClock.h"
@ -90,23 +90,23 @@
// header of this file // header of this file
void uclockInitTimer() void uclockInitTimer()
{ {
// begin at 120bpm // begin at 120bpm
initTimer(uClock.bpmToMicroSeconds(120.00)); initTimer(uClock.bpmToMicroSeconds(120.00));
} }
void setTimerTempo(float bpm) void setTimerTempo(float bpm)
{ {
setTimer(uClock.bpmToMicroSeconds(bpm)); setTimer(uClock.bpmToMicroSeconds(bpm));
} }
namespace umodular { namespace clock { namespace umodular { namespace clock {
static inline uint32_t phase_mult(uint32_t val) static inline uint32_t phase_mult(uint32_t val)
{ {
return (val * PHASE_FACTOR) >> 8; return (val * PHASE_FACTOR) >> 8;
} }
static inline uint32_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) { if (new_clock >= old_clock) {
return new_clock - old_clock; return new_clock - old_clock;
@ -140,21 +140,24 @@ uClockClass::uClockClass()
calculateReferencedata(); calculateReferencedata();
} }
void uClockClass::init() void uClockClass::init()
{ {
if (ext_interval_buffer == nullptr)
setExtIntervalBuffer(1);
uclockInitTimer(); uclockInitTimer();
// first interval calculus // first interval calculus
setTempo(tempo); setTempo(tempo);
} }
uint32_t uClockClass::bpmToMicroSeconds(float bpm) uint32_t uClockClass::bpmToMicroSeconds(float bpm)
{ {
return (60000000.0f / (float)output_ppqn / bpm); return (60000000.0f / (float)output_ppqn / bpm);
} }
void uClockClass::calculateReferencedata() void uClockClass::calculateReferencedata()
{ {
mod_clock_ref = output_ppqn / input_ppqn; mod_clock_ref = output_ppqn / input_ppqn;
mod_sync1_ref = output_ppqn / PPQN_1; mod_sync1_ref = output_ppqn / PPQN_1;
mod_sync2_ref = output_ppqn / PPQN_2; mod_sync2_ref = output_ppqn / PPQN_2;
mod_sync4_ref = output_ppqn / PPQN_4; mod_sync4_ref = output_ppqn / PPQN_4;
@ -185,20 +188,20 @@ void uClockClass::setInputPPQN(PPQNResolution resolution)
) )
} }
void uClockClass::start() void uClockClass::start()
{ {
resetCounters(); resetCounters();
start_timer = millis(); start_timer = millis();
if (onClockStartCallback) { if (onClockStartCallback) {
onClockStartCallback(); onClockStartCallback();
} }
if (clock_mode == INTERNAL_CLOCK) { if (clock_mode == INTERNAL_CLOCK) {
clock_state = STARTED; clock_state = STARTED;
} else { } else {
clock_state = STARTING; clock_state = STARTING;
} }
} }
void uClockClass::stop() void uClockClass::stop()
@ -211,7 +214,7 @@ void uClockClass::stop()
} }
} }
void uClockClass::pause() void uClockClass::pause()
{ {
if (clock_mode == INTERNAL_CLOCK) { if (clock_mode == INTERNAL_CLOCK) {
if (clock_state == PAUSED) { if (clock_state == PAUSED) {
@ -222,12 +225,12 @@ void uClockClass::pause()
} }
} }
void uClockClass::setTempo(float bpm) void uClockClass::setTempo(float bpm)
{ {
if (clock_mode == EXTERNAL_CLOCK) { if (clock_mode == EXTERNAL_CLOCK) {
return; return;
} }
if (bpm < MIN_BPM || bpm > MAX_BPM) { if (bpm < MIN_BPM || bpm > MAX_BPM) {
return; return;
} }
@ -239,19 +242,19 @@ void uClockClass::setTempo(float bpm)
setTimerTempo(bpm); setTimerTempo(bpm);
} }
float uClockClass::getTempo() float uClockClass::getTempo()
{ {
if (clock_mode == EXTERNAL_CLOCK) { if (clock_mode == EXTERNAL_CLOCK) {
uint32_t acc = 0; uint32_t acc = 0;
// wait the buffer to get full // wait the buffer to get full
if (ext_interval_buffer[EXT_INTERVAL_BUFFER_SIZE-1] == 0) { if (ext_interval_buffer[ext_interval_buffer_size-1] == 0) {
return tempo; return tempo;
} }
for (uint8_t i=0; i < EXT_INTERVAL_BUFFER_SIZE; i++) { for (uint8_t i=0; i < ext_interval_buffer_size; i++) {
acc += ext_interval_buffer[i]; acc += ext_interval_buffer[i];
} }
if (acc != 0) { if (acc != 0) {
return freqToBpm(acc / EXT_INTERVAL_BUFFER_SIZE); return freqToBpm(acc / ext_interval_buffer_size);
} }
} }
return tempo; return tempo;
@ -272,17 +275,17 @@ float inline uClockClass::freqToBpm(uint32_t freq)
return (float)((float)(usecs/(float)input_ppqn) * 60.0); return (float)((float)(usecs/(float)input_ppqn) * 60.0);
} }
void uClockClass::setClockMode(ClockMode tempo_mode) void uClockClass::setClockMode(ClockMode tempo_mode)
{ {
clock_mode = tempo_mode; clock_mode = tempo_mode;
} }
uClockClass::ClockMode uClockClass::getClockMode() uClockClass::ClockMode uClockClass::getClockMode()
{ {
return clock_mode; return clock_mode;
} }
void uClockClass::clockMe() void uClockClass::clockMe()
{ {
if (clock_mode == EXTERNAL_CLOCK) { if (clock_mode == EXTERNAL_CLOCK) {
ATOMIC( ATOMIC(
@ -291,7 +294,17 @@ void uClockClass::clockMe()
} }
} }
void uClockClass::resetCounters() void uClockClass::setExtIntervalBuffer(uint8_t buffer_size)
{
if (ext_interval_buffer != nullptr)
return;
// alloc once and forever policy
ext_interval_buffer_size = buffer_size;
ext_interval_buffer = (uint32_t*) malloc( sizeof(uint32_t) * ext_interval_buffer_size );
}
void uClockClass::resetCounters()
{ {
tick = 0; tick = 0;
int_clock_tick = 0; int_clock_tick = 0;
@ -316,16 +329,16 @@ void uClockClass::resetCounters()
sync24_tick = 0; sync24_tick = 0;
mod_sync48_counter = 0; mod_sync48_counter = 0;
sync48_tick = 0; sync48_tick = 0;
for (uint8_t i=0; i < EXT_INTERVAL_BUFFER_SIZE; i++) { for (uint8_t i=0; i < ext_interval_buffer_size; i++) {
ext_interval_buffer[i] = 0; ext_interval_buffer[i] = 0;
} }
} }
void uClockClass::tap() void uClockClass::tap()
{ {
// we can make use of mod_sync1_ref for tap // we can make use of mod_sync1_ref for tap
//uint8_t mod_tap_ref = output_ppqn / PPQN_1; //uint8_t mod_tap_ref = output_ppqn / PPQN_1;
// we only set tap if ClockMode is INTERNAL_CLOCK // we only set tap if ClockMode is INTERNAL_CLOCK
} }
@ -381,7 +394,7 @@ bool inline uClockClass::processShuffle()
int8_t shff = shuffle.step[step_counter%shuffle.size]; int8_t shff = shuffle.step[step_counter%shuffle.size];
if (shuffle_shoot_ctrl == false && mod_step_counter == 0) if (shuffle_shoot_ctrl == false && mod_step_counter == 0)
shuffle_shoot_ctrl = true; shuffle_shoot_ctrl = true;
//if (mod_step_counter == mod_step_ref-1) //if (mod_step_counter == mod_step_ref-1)
@ -389,11 +402,11 @@ bool inline uClockClass::processShuffle()
mod_shuffle = mod_step_counter - shff; mod_shuffle = mod_step_counter - shff;
// any late shuffle? we should skip next mod_step_counter == 0 // any late shuffle? we should skip next mod_step_counter == 0
if (last_shff < 0 && mod_step_counter != 1) if (last_shff < 0 && mod_step_counter != 1)
return false; return false;
} else if (shff < 0) { } else if (shff < 0) {
mod_shuffle = mod_step_counter - (mod_step_ref + shff); mod_shuffle = mod_step_counter - (mod_step_ref + shff);
//if (last_shff < 0 && mod_step_counter != 1) //if (last_shff < 0 && mod_step_counter != 1)
// return false; // return false;
shuffle_shoot_ctrl = true; shuffle_shoot_ctrl = true;
} }
@ -414,7 +427,7 @@ bool inline uClockClass::processShuffle()
return false; return false;
} }
void uClockClass::handleExternalClock() void uClockClass::handleExternalClock()
{ {
switch (clock_state) { switch (clock_state) {
case PAUSED: case PAUSED:
@ -434,7 +447,7 @@ void uClockClass::handleExternalClock()
ext_clock_tick++; ext_clock_tick++;
// accumulate interval incomming ticks data for getTempo() smooth reads on slave clock_mode // accumulate interval incomming ticks data for getTempo() smooth reads on slave clock_mode
if(++ext_interval_idx >= EXT_INTERVAL_BUFFER_SIZE) { if(++ext_interval_idx >= ext_interval_buffer_size) {
ext_interval_idx = 0; ext_interval_idx = 0;
} }
ext_interval_buffer[ext_interval_idx] = last_interval; ext_interval_buffer[ext_interval_idx] = last_interval;
@ -448,7 +461,7 @@ void uClockClass::handleExternalClock()
} }
} }
void uClockClass::handleTimerInt() void uClockClass::handleTimerInt()
{ {
// track main input clock counter // track main input clock counter
if (mod_clock_counter == mod_clock_ref) if (mod_clock_counter == mod_clock_ref)
@ -515,7 +528,7 @@ void uClockClass::handleTimerInt()
} }
++mod_sync2_counter; ++mod_sync2_counter;
} }
// Sync4 callback // Sync4 callback
if (onSync4Callback) { if (onSync4Callback) {
if (mod_sync4_counter == mod_sync4_ref) if (mod_sync4_counter == mod_sync4_ref)
@ -583,7 +596,7 @@ void uClockClass::handleTimerInt()
if (mod_step_counter == mod_step_ref) if (mod_step_counter == mod_step_ref)
mod_step_counter = 0; mod_step_counter = 0;
// processShufle make use of mod_step_counter == 0 logic too // processShufle make use of mod_step_counter == 0 logic too
if (processShuffle()) { if (processShuffle()) {
onStepCallback(step_counter); onStepCallback(step_counter);
// going forward to the next step call // going forward to the next step call
++step_counter; ++step_counter;
@ -605,7 +618,7 @@ uint8_t uClockClass::getNumberOfMinutes(uint32_t time)
{ {
if ( time == 0 ) { if ( time == 0 ) {
return time; return time;
} }
return (((_millis - time) / 1000) / SECS_PER_MIN) % SECS_PER_MIN; return (((_millis - time) / 1000) / SECS_PER_MIN) % SECS_PER_MIN;
} }
@ -613,7 +626,7 @@ uint8_t uClockClass::getNumberOfHours(uint32_t time)
{ {
if ( time == 0 ) { if ( time == 0 ) {
return time; return time;
} }
return (((_millis - time) / 1000) % SECS_PER_DAY) / SECS_PER_HOUR; return (((_millis - time) / 1000) % SECS_PER_DAY) / SECS_PER_HOUR;
} }
@ -621,7 +634,7 @@ uint8_t uClockClass::getNumberOfDays(uint32_t time)
{ {
if ( time == 0 ) { if ( time == 0 ) {
return time; return time;
} }
return ((_millis - time) / 1000) / SECS_PER_DAY; return ((_millis - time) / 1000) / SECS_PER_DAY;
} }
@ -629,12 +642,12 @@ uint32_t uClockClass::getNowTimer()
{ {
return _millis; return _millis;
} }
uint32_t uClockClass::getPlayTime() uint32_t uClockClass::getPlayTime()
{ {
return start_timer; return start_timer;
} }
} } // end namespace umodular::clock } } // end namespace umodular::clock
umodular::clock::uClockClass uClock; umodular::clock::uClockClass uClock;
@ -642,13 +655,13 @@ umodular::clock::uClockClass uClock;
volatile uint32_t _millis = 0; volatile uint32_t _millis = 0;
// //
// TIMER HANDLER // TIMER HANDLER
// //
void uClockHandler() 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.handleTimerInt();
} }

@ -23,7 +23,7 @@
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE. * DEALINGS IN THE SOFTWARE.
*/ */
#ifndef __U_CLOCK_H__ #ifndef __U_CLOCK_H__
@ -36,7 +36,7 @@ namespace umodular { namespace clock {
// Shuffle templates are specific for each PPQN output resolution // Shuffle templates are specific for each PPQN output resolution
// min: -(output_ppqn/4)-1 ticks // min: -(output_ppqn/4)-1 ticks
// max: (output_ppqn/4)-1 ticks // max: (output_ppqn/4)-1 ticks
// adjust the size of you template if more than 16 shuffle step info needed // adjust the size of you template if more than 16 shuffle step info needed
#define MAX_SHUFFLE_TEMPLATE_SIZE 16 #define MAX_SHUFFLE_TEMPLATE_SIZE 16
typedef struct { typedef struct {
@ -45,12 +45,6 @@ typedef struct {
int8_t step[MAX_SHUFFLE_TEMPLATE_SIZE] = {0}; int8_t step[MAX_SHUFFLE_TEMPLATE_SIZE] = {0};
} SHUFFLE_TEMPLATE; } SHUFFLE_TEMPLATE;
// 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 128
#define MIN_BPM 1 #define MIN_BPM 1
#define MAX_BPM 400 #define MAX_BPM 400
@ -90,7 +84,7 @@ class uClockClass {
}; };
ClockState clock_state; ClockState clock_state;
uClockClass(); uClockClass();
void setOnOutputPPQN(void (*callback)(uint32_t tick)) { void setOnOutputPPQN(void (*callback)(uint32_t tick)) {
@ -105,19 +99,19 @@ class uClockClass {
void setOnSync1(void (*callback)(uint32_t tick)) { void setOnSync1(void (*callback)(uint32_t tick)) {
onSync1Callback = callback; onSync1Callback = callback;
} }
void setOnSync2(void (*callback)(uint32_t tick)) { void setOnSync2(void (*callback)(uint32_t tick)) {
onSync2Callback = callback; onSync2Callback = callback;
} }
void setOnSync4(void (*callback)(uint32_t tick)) { void setOnSync4(void (*callback)(uint32_t tick)) {
onSync4Callback = callback; onSync4Callback = callback;
} }
void setOnSync8(void (*callback)(uint32_t tick)) { void setOnSync8(void (*callback)(uint32_t tick)) {
onSync8Callback = callback; onSync8Callback = callback;
} }
void setOnSync12(void (*callback)(uint32_t tick)) { void setOnSync12(void (*callback)(uint32_t tick)) {
onSync12Callback = callback; onSync12Callback = callback;
} }
@ -125,7 +119,7 @@ class uClockClass {
void setOnSync24(void (*callback)(uint32_t tick)) { void setOnSync24(void (*callback)(uint32_t tick)) {
onSync24Callback = callback; onSync24Callback = callback;
} }
void setOnSync48(void (*callback)(uint32_t tick)) { void setOnSync48(void (*callback)(uint32_t tick)) {
onSync48Callback = callback; onSync48Callback = callback;
} }
@ -145,7 +139,7 @@ class uClockClass {
void handleTimerInt(); void handleTimerInt();
void handleExternalClock(); void handleExternalClock();
void resetCounters(); void resetCounters();
// external class control // external class control
void start(); void start();
void stop(); void stop();
@ -160,6 +154,11 @@ class uClockClass {
void setClockMode(ClockMode tempo_mode); void setClockMode(ClockMode tempo_mode);
ClockMode getClockMode(); ClockMode getClockMode();
void clockMe(); void clockMe();
// for smooth slave tempo calculate display you should raise the
// buffer_size of ext_interval_buffer in between 64 to 128. 254 max size.
// note: this doesn't impact on sync time, only display time getTempo()
// if you dont want to use it, it is default set it to 1 for memory save
void setExtIntervalBuffer(uint8_t buffer_size);
// shuffle // shuffle
void setShuffle(bool active); void setShuffle(bool active);
@ -169,10 +168,10 @@ class uClockClass {
void setShuffleTemplate(int8_t * shuff, uint8_t size); void setShuffleTemplate(int8_t * shuff, uint8_t size);
// use this to know how many positive or negative ticks to add to current note length // use this to know how many positive or negative ticks to add to current note length
int8_t getShuffleLength(); int8_t getShuffleLength();
// todo! // todo!
void tap(); void tap();
// elapsed time support // elapsed time support
uint8_t getNumberOfSeconds(uint32_t time); uint8_t getNumberOfSeconds(uint32_t time);
uint8_t getNumberOfMinutes(uint32_t time); uint8_t getNumberOfMinutes(uint32_t time);
@ -246,7 +245,8 @@ class uClockClass {
uint32_t start_timer; uint32_t start_timer;
ClockMode clock_mode; ClockMode clock_mode;
volatile uint32_t ext_interval_buffer[EXT_INTERVAL_BUFFER_SIZE]; volatile uint32_t * ext_interval_buffer = nullptr;
uint8_t ext_interval_buffer_size;
uint16_t ext_interval_idx; uint16_t ext_interval_idx;
// shuffle implementation // shuffle implementation
@ -265,4 +265,3 @@ extern "C" {
} }
#endif /* __U_CLOCK_H__ */ #endif /* __U_CLOCK_H__ */

Loading…
Cancel
Save