/** * @file clock.h * @author Adam Wonak (https://github.com/awonak) * @brief Wrapper Class for clock timing functions. * @version 2.0.0 * @date 2025-08-17 * * @copyright MIT - (c) 2025 - Adam Wonak - adam.wonak@gmail.com * */ #ifndef CLOCK_H #define CLOCK_H #include "peripherials.h" #include "uClock/uClock.h" typedef void (*ExtCallback)(void); static ExtCallback extUserCallback = nullptr; class Clock { public: static constexpr int DEFAULT_TEMPO = 120; enum Source { SOURCE_INTERNAL, SOURCE_EXTERNAL_PPQN_24, SOURCE_EXTERNAL_PPQN_4, SOURCE_EXTERNAL_PPQN_1, SOURCE_EXTERNAL_MIDI, SOURCE_LAST, }; enum Pulse { PULSE_NONE, PULSE_PPQN_24, PULSE_PPQN_4, PULSE_PPQN_1, PULSE_LAST, }; void Init() { // Initialize the clock library uClock.init(); uClock.setClockMode(uClock.INTERNAL_CLOCK); uClock.setOutputPPQN(uClock.PPQN_96); uClock.setTempo(DEFAULT_TEMPO); uClock.start(); } // Handle external clock tick and call user callback when receiving clock trigger (PPQN_4, PPQN_24, or MIDI). void AttachExtHandler(void (*callback)()) { extUserCallback = callback; attachInterrupt(digitalPinToInterrupt(EXT_PIN), callback, RISING); } // Internal PPQN96 callback for all clock timer operations. void AttachIntHandler(void (*callback)(uint32_t)) { uClock.setOnOutputPPQN(callback); } // Set the source of the clock mode. void SetSource(Source source) { bool was_playing = !IsPaused(); uClock.stop(); source_ = source; switch (source) { case SOURCE_INTERNAL: uClock.setClockMode(uClock.INTERNAL_CLOCK); break; case SOURCE_EXTERNAL_PPQN_24: uClock.setClockMode(uClock.EXTERNAL_CLOCK); uClock.setInputPPQN(uClock.PPQN_24); break; case SOURCE_EXTERNAL_PPQN_4: uClock.setClockMode(uClock.EXTERNAL_CLOCK); uClock.setInputPPQN(uClock.PPQN_4); break; case SOURCE_EXTERNAL_PPQN_1: uClock.setClockMode(uClock.EXTERNAL_CLOCK); uClock.setInputPPQN(uClock.PPQN_1); break; case SOURCE_EXTERNAL_MIDI: break; } if (was_playing) { uClock.start(); } } // Return true if the current selected source is externl (PPQN_4, PPQN_24, or MIDI). bool ExternalSource() { return uClock.getClockMode() == uClock.EXTERNAL_CLOCK; } // Return true if the current selected source is the internal master clock. bool InternalSource() { return uClock.getClockMode() == uClock.INTERNAL_CLOCK; } // Returns the current BPM tempo. int Tempo() { return uClock.getTempo(); } // Set the clock tempo to a int between 1 and 400. void SetTempo(int tempo) { return uClock.setTempo(tempo); } // Record an external clock tick received to process external/internal syncronization. void Tick() { uClock.clockMe(); } // Start the internal clock. void Start() { uClock.start(); } // Stop internal clock clock. void Stop() { uClock.stop(); } // Reset all clock counters to 0. void Reset() { uClock.resetCounters(); } // Returns true if the clock is not running. bool IsPaused() { return uClock.clock_state == uClock.PAUSED; } private: Source source_ = SOURCE_INTERNAL; }; #endif