initial support for shuffle with early and late tick setup on 16PPQN calls

pull/32/head
midilab 1 year ago
parent a89d0e70b9
commit 6f0863f247
  1. 78
      src/uClock.cpp
  2. 223
      src/uClock.h

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

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

Loading…
Cancel
Save