mirror of https://github.com/midilab/uClock
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
7.1 KiB
227 lines
7.1 KiB
/*!
|
|
* @file uClock.h
|
|
* Project BPM clock generator for Arduino
|
|
* @brief A Library to implement BPM clock tick calls using hardware interruption. Supported and tested on AVR boards(ATmega168/328, ATmega16u4/32u4 and ATmega2560) and ARM boards(RPI2040, Teensy, Seedstudio XIAO M0 and ESP32)
|
|
* @version 2.2.0
|
|
* @author Romulo Silva
|
|
* @date 10/06/2017
|
|
* @license MIT - (c) 2024 - Romulo Silva - contact@midilab.co
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* 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
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#ifndef __U_CLOCK_H__
|
|
#define __U_CLOCK_H__
|
|
|
|
#include <Arduino.h>
|
|
#include <inttypes.h>
|
|
|
|
namespace umodular { namespace clock {
|
|
|
|
// for extended steps in memory style and make use of 96ppqn for record propurse we can
|
|
// keep array[step] memory layout and add new information about note possition to be check for the entire ppqn pulse
|
|
// example: for a whole 24 pulses we only check array[step].offset that can vary from 0 to 24(ppqn/4)
|
|
// time/tick notation and representation notes:
|
|
// one quarter note == 4 steps in 16th notes step sequencer style
|
|
// PPQN / 4 = pulses in between steps(from step sequencer perspective, a quarter note have 4 steps)
|
|
// 24 PPQN (6 pulses per step)
|
|
// 48 PPQN (12 pulses per step)
|
|
// 96 PPQN (24 pulses per step)
|
|
|
|
// min: -(ppqn/4)-1 step, max: (ppqn/4)-1 steps
|
|
// adjust the size of you template if more than 16 shuffle step info needed
|
|
#define MAX_SHUFFLE_TEMPLATE_SIZE 16
|
|
typedef struct {
|
|
bool active = false;
|
|
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()
|
|
// if you dont want to use it, set it to 1 for memory save
|
|
#define EXT_INTERVAL_BUFFER_SIZE 24
|
|
|
|
#define MIN_BPM 1
|
|
#define MAX_BPM 300
|
|
|
|
#define PHASE_FACTOR 16
|
|
#define PLL_X 220
|
|
|
|
#define SECS_PER_MIN (60UL)
|
|
#define SECS_PER_HOUR (3600UL)
|
|
#define SECS_PER_DAY (SECS_PER_HOUR * 24L)
|
|
|
|
class uClockClass {
|
|
|
|
public:
|
|
enum SyncMode {
|
|
INTERNAL_CLOCK = 0,
|
|
EXTERNAL_CLOCK
|
|
};
|
|
|
|
enum ClockState {
|
|
PAUSED = 0,
|
|
STARTING,
|
|
STARTED
|
|
};
|
|
|
|
enum PPQNResolution {
|
|
PPQN_24 = 24,
|
|
PPQN_48 = 48,
|
|
PPQN_96 = 96,
|
|
PPQN_384 = 384,
|
|
PPQN_480 = 480,
|
|
PPQN_960 = 960
|
|
};
|
|
|
|
ClockState state;
|
|
|
|
uClockClass();
|
|
|
|
void setOnPPQN(void (*callback)(uint32_t tick)) {
|
|
onPPQNCallback = callback;
|
|
}
|
|
|
|
void setOnStep(void (*callback)(uint32_t step)) {
|
|
onStepCallback = callback;
|
|
}
|
|
|
|
void setOnSync24(void (*callback)(uint32_t tick)) {
|
|
onSync24Callback = callback;
|
|
}
|
|
|
|
void setOnClockStart(void (*callback)()) {
|
|
onClockStartCallback = callback;
|
|
}
|
|
|
|
void setOnClockStop(void (*callback)()) {
|
|
onClockStopCallback = callback;
|
|
}
|
|
|
|
void setOnClockContinue(void (*callback)()) {
|
|
onClockStartCallback = callback;
|
|
}
|
|
|
|
void setOnClockPause(void (*callback)()) {
|
|
onClockPauseCallback = callback;
|
|
}
|
|
|
|
void init();
|
|
void setPPQN(PPQNResolution resolution);
|
|
|
|
void handleTimerInt();
|
|
void handleExternalClock();
|
|
void resetCounters();
|
|
|
|
// external class control
|
|
void start();
|
|
void stop();
|
|
void pause();
|
|
void continue_playing();
|
|
void setTempo(float bpm);
|
|
float getTempo();
|
|
|
|
// for software timer implementation(fallback for no board support)
|
|
void run();
|
|
|
|
// external timming control
|
|
void setMode(SyncMode tempo_mode);
|
|
SyncMode 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();
|
|
|
|
uint32_t bpmToMicroSeconds(float bpm);
|
|
|
|
private:
|
|
float inline freqToBpm(uint32_t freq);
|
|
|
|
// shuffle
|
|
bool inline processShuffle();
|
|
|
|
void (*onPPQNCallback)(uint32_t tick);
|
|
void (*onStepCallback)(uint32_t step);
|
|
void (*onSync24Callback)(uint32_t tick);
|
|
void (*onClockStartCallback)();
|
|
void (*onClockStopCallback)();
|
|
void (*onClockContinueCallback)();
|
|
void (*onClockPauseCallback)();
|
|
|
|
// internal clock control
|
|
// uint16_t ppqn;
|
|
PPQNResolution ppqn = PPQN_96;
|
|
uint32_t tick;
|
|
uint32_t int_clock_tick;
|
|
uint8_t mod24_counter;
|
|
uint8_t mod24_ref;
|
|
uint8_t mod_step_counter;
|
|
uint8_t mod_step_ref;
|
|
uint32_t step_counter; // should we go uint16_t?
|
|
|
|
// external clock control
|
|
volatile uint32_t ext_clock_us;
|
|
volatile uint32_t ext_clock_tick;
|
|
volatile uint32_t ext_interval;
|
|
uint32_t last_interval;
|
|
uint32_t sync_interval;
|
|
|
|
float tempo;
|
|
uint32_t start_timer;
|
|
SyncMode mode;
|
|
|
|
volatile uint32_t ext_interval_buffer[EXT_INTERVAL_BUFFER_SIZE];
|
|
uint16_t ext_interval_idx;
|
|
|
|
// shuffle implementation
|
|
volatile SHUFFLE_TEMPLATE shuffle;
|
|
int8_t last_shff = 0;
|
|
bool shuffle_shoot_ctrl = true;
|
|
volatile int8_t shuffle_length_ctrl = 0;
|
|
};
|
|
|
|
} } // end namespace umodular::clock
|
|
|
|
extern umodular::clock::uClockClass uClock;
|
|
|
|
extern "C" {
|
|
extern volatile uint32_t _millis;
|
|
}
|
|
|
|
#endif /* __U_CLOCK_H__ */
|
|
|
|
|