diff --git a/src/uClock.cpp b/src/uClock.cpp index 4243484..eca1b6b 100755 --- a/src/uClock.cpp +++ b/src/uClock.cpp @@ -238,10 +238,43 @@ void uClockClass::tap() // tap me } -// TODO: Shuffle stuff -void uClockClass::shuffle() +void uClockClass::setShuffle(bool active) { - // shuffle me + ATOMIC(shuffle.active = active) +} + +bool uClockClass::isShuffled() +{ + return shuffle.active; +} + +void uClockClass::setShuffleSize(uint8_t size) +{ + if (size > MAX_SHUFFLE_TEMPLATE_SIZE) + size = MAX_SHUFFLE_TEMPLATE_SIZE; + ATOMIC(shuffle.size = size) +} + +void uClockClass::setShuffleData(uint8_t step, int8_t tick) +{ + if (step >= MAX_SHUFFLE_TEMPLATE_SIZE) + return; + ATOMIC(shuffle.step[step] = tick) +} + +void uClockClass::setShuffleTemplate(int8_t * shuff, uint8_t size) +{ + if (size > MAX_SHUFFLE_TEMPLATE_SIZE) + size = MAX_SHUFFLE_TEMPLATE_SIZE; + setShuffleSize(size); + for (uint8_t i=0; i < size; i++) { + setShuffleData(i, shuff[i]); + } +} + +int8_t uClockClass::getShuffleLength() +{ + return shuffle_length_ctrl; } void uClockClass::handleExternalClock() @@ -331,18 +364,17 @@ void uClockClass::handleTimerInt() onClock96PPQNCallback(internal_tick); } - if (mod6_counter == 0) { - if (onClock32PPQNCallback) { - onClock32PPQNCallback(div32th_counter); - } + // 16PPQN call and shuffle processing if enabled + if (processShuffle() == 0) { if (onClock16PPQNCallback) { onClock16PPQNCallback(div16th_counter); } div16th_counter++; - div32th_counter++; + shuffle_shoot_ctrl = false; } - if (mod6_counter == 3) { + // 32PPQN call. does anyone uses it? + if (mod6_counter == 3 || mod6_counter == 6) { if (onClock32PPQNCallback) { onClock32PPQNCallback(div32th_counter); } @@ -356,7 +388,35 @@ void uClockClass::handleTimerInt() if (mod6_counter == 6) { mod6_counter = 0; } - +} + +uint8_t inline uClockClass::processShuffle() +{ + uint8_t mod6_shuffle_counter; + if (!shuffle.active) { + mod6_shuffle_counter = mod6_counter; + } else { + // apply shuffle template to step + int8_t shff = shuffle.step[div16th_counter%shuffle.size]; + // keep track of next note shuffle for current note lenght control + shuffle_length_ctrl = shuffle.step[(div16th_counter+1)%shuffle.size]; + // prepare the next mod6 quantize to be called + if (shff == 0) { + mod6_shuffle_counter = mod6_counter; + shuffle_length_ctrl += shff; + } else if (shff > 0) { + if (shuffle_shoot_ctrl == false && mod6_counter > shff) + shuffle_shoot_ctrl = true; + mod6_shuffle_counter = shuffle_shoot_ctrl ? mod6_counter - shff : 1; + shuffle_length_ctrl -= shff; + } else if (shff < 0) { + if (shuffle_shoot_ctrl == false && mod6_counter == 0) + shuffle_shoot_ctrl = true; + mod6_shuffle_counter = shff - mod6_counter == -6 ? shuffle_shoot_ctrl ? 0 : 1 : 1; + shuffle_length_ctrl += shff; + } + } + return mod6_shuffle_counter; } // elapsed time support diff --git a/src/uClock.h b/src/uClock.h index e02c66e..f4381fe 100755 --- a/src/uClock.h +++ b/src/uClock.h @@ -34,6 +34,16 @@ namespace umodular { namespace clock { +// min: 2 step, max: 16 steps +// adjust the size of you template if more than 16 needed +// step adjust goes min: -5, max: 5 +#define MAX_SHUFFLE_TEMPLATE_SIZE 16 +typedef struct { + bool active; + uint8_t size = MAX_SHUFFLE_TEMPLATE_SIZE; + int8_t step[MAX_SHUFFLE_TEMPLATE_SIZE] = {0}; +} 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() @@ -51,105 +61,120 @@ namespace umodular { namespace clock { #define SECS_PER_DAY (SECS_PER_HOUR * 24L) class uClockClass { +private: + + float inline freqToBpm(uint32_t freq); + + // shuffle + uint8_t inline processShuffle(); + + void (*onClock96PPQNCallback)(uint32_t tick); + void (*onClock32PPQNCallback)(uint32_t tick); + void (*onClock16PPQNCallback)(uint32_t tick); + void (*onClockStartCallback)(); + void (*onClockStopCallback)(); + + // internal clock control + uint32_t internal_tick; + uint32_t div32th_counter; + uint32_t div16th_counter; + 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; + uint32_t last_interval; + uint32_t sync_interval; + + float tempo; + uint32_t start_timer; + uint8_t mode; + + volatile uint32_t ext_interval_buffer[EXT_INTERVAL_BUFFER_SIZE]; + uint16_t ext_interval_idx; + + // shuffle implementation that applies to 16PPQN callback + volatile SHUFFLE_TEMPLATE shuffle; + bool shuffle_shoot_ctrl = true; + volatile int8_t shuffle_length_ctrl = 0; + +public: + + enum { + INTERNAL_CLOCK = 0, + EXTERNAL_CLOCK + }; + + enum { + PAUSED = 0, + STARTING, + STARTED + }; + + uint8_t state; + + uClockClass(); - private: - - float inline freqToBpm(uint32_t freq); - - void (*onClock96PPQNCallback)(uint32_t tick); - void (*onClock32PPQNCallback)(uint32_t tick); - void (*onClock16PPQNCallback)(uint32_t tick); - void (*onClockStartCallback)(); - void (*onClockStopCallback)(); - - // internal clock control - uint32_t internal_tick; - uint32_t div32th_counter; - uint32_t div16th_counter; - 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; - uint32_t last_interval; - uint32_t sync_interval; - - float tempo; - uint32_t start_timer; - uint8_t mode; + void setClock96PPQNOutput(void (*callback)(uint32_t tick)) { + onClock96PPQNCallback = callback; + } + + void setClock32PPQNOutput(void (*callback)(uint32_t tick)) { + onClock32PPQNCallback = callback; + } + + void setClock16PPQNOutput(void (*callback)(uint32_t tick)) { + onClock16PPQNCallback = callback; + } + + void setOnClockStartOutput(void (*callback)()) { + onClockStartCallback = callback; + } + + void setOnClockStopOutput(void (*callback)()) { + onClockStopCallback = callback; + } + + void init(); + void handleTimerInt(); + void handleExternalClock(); + void resetCounters(); + + // external class control + void start(); + void stop(); + void pause(); + void setTempo(float bpm); + float getTempo(); + + // external timming control + void setMode(uint8_t tempo_mode); + uint8_t getMode(); + void clockMe(); + + // shuffle + void setShuffle(bool active); + bool isShuffled(); + void setShuffleSize(uint8_t size); + void setShuffleData(uint8_t step, int8_t tick); + void setShuffleTemplate(int8_t * shuff, uint8_t size); + // use this to know how many positive or negative ticks to add to current note length + int8_t getShuffleLength(); + + // todo! + void tap(); - volatile uint32_t ext_interval_buffer[EXT_INTERVAL_BUFFER_SIZE]; - uint16_t ext_interval_idx; - - public: - - enum { - INTERNAL_CLOCK = 0, - EXTERNAL_CLOCK - }; - - enum { - PAUSED = 0, - STARTING, - STARTED - }; - - uint8_t state; - - uClockClass(); - - void setClock96PPQNOutput(void (*callback)(uint32_t tick)) { - onClock96PPQNCallback = callback; - } - - void setClock32PPQNOutput(void (*callback)(uint32_t tick)) { - onClock32PPQNCallback = callback; - } - - void setClock16PPQNOutput(void (*callback)(uint32_t tick)) { - onClock16PPQNCallback = callback; - } - - void setOnClockStartOutput(void (*callback)()) { - onClockStartCallback = callback; - } - - void setOnClockStopOutput(void (*callback)()) { - onClockStopCallback = callback; - } - - void init(); - void handleTimerInt(); - void handleExternalClock(); - void resetCounters(); - - // external class control - void start(); - void stop(); - void pause(); - void setTempo(float bpm); - float getTempo(); - - // external timming control - void setMode(uint8_t tempo_mode); - uint8_t getMode(); - void clockMe(); - - // todo! - void shuffle(); - void tap(); - - // elapsed time support - uint8_t getNumberOfSeconds(uint32_t time); - uint8_t getNumberOfMinutes(uint32_t time); - uint8_t getNumberOfHours(uint32_t time); - uint8_t getNumberOfDays(uint32_t time); - uint32_t getNowTimer(); - uint32_t getPlayTime(); + // elapsed time support + uint8_t getNumberOfSeconds(uint32_t time); + uint8_t getNumberOfMinutes(uint32_t time); + uint8_t getNumberOfHours(uint32_t time); + uint8_t getNumberOfDays(uint32_t time); + uint32_t getNowTimer(); + uint32_t getPlayTime(); }; } } // end namespace umodular::clock