/** * @file clock.h * @author Adam Wonak (https://github.com/awonak) * @brief Wrapper Class for clock timing functions. * @version 0.1 * @date 2025-05-04 * * @copyright Copyright (c) 2025 * */ #ifndef CLOCK_H #define CLOCK_H #include #include #include "peripherials.h" // MIDI clock, start, stop, and continue byte definitions - based on MIDI 1.0 Standards. #define MIDI_CLOCK 0xF8 #define MIDI_START 0xFA #define MIDI_STOP 0xFC #define MIDI_CONTINUE 0xFB const int DEFAULT_TEMPO = 120; static bool MIDI_ENABLED = false; enum Source { SOURCE_INTERNAL, SOURCE_EXTERNAL_PPQN_24, SOURCE_EXTERNAL_PPQN_4, SOURCE_EXTERNAL_MIDI, SOURCE_LAST, }; class Clock { public: void Init() { NeoSerial.begin(31250); NeoSerial.attachInterrupt(onSerialEvent); // Static pin definition for pulse out. pinMode(PULSE_OUT_PIN, OUTPUT); // Initialize the clock library uClock.init(); uClock.setClockMode(uClock.INTERNAL_CLOCK); uClock.setOutputPPQN(uClock.PPQN_96); uClock.setTempo(DEFAULT_TEMPO); // MIDI events. uClock.setOnClockStart(sendMIDIStart); uClock.setOnClockStop(sendMIDIStop); uClock.setOnSync24(sendMidiClock); uClock.setOnSync48(sendPulseOut); uClock.start(); } // Handler for receiving clock trigger(PPQN_4 or PPQN_24). void AttachExtHandler(void (*callback)(void)) { 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) { uClock.stop(); MIDI_ENABLED = false; 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_MIDI: uClock.setClockMode(uClock.EXTERNAL_CLOCK); uClock.setInputPPQN(uClock.PPQN_24); MIDI_ENABLED = true; break; } uClock.start(); } bool ExternalSource() { return uClock.getClockMode() == uClock.EXTERNAL_CLOCK; } bool InternalSource() { return uClock.getClockMode() == uClock.INTERNAL_CLOCK; } int Tempo() { return uClock.getTempo(); } void SetTempo(int tempo) { return uClock.setTempo(tempo); } void Tick() { uClock.clockMe(); } void Pause() { uClock.pause(); } void Reset() { uClock.start(); } bool IsPaused() { return uClock.clock_state == uClock.PAUSED; } private: static void onSerialEvent(uint8_t msg, uint8_t status) { // if (!MIDI_ENABLED) { // return; // } switch (msg) { case MIDI_CLOCK: uClock.clockMe(); break; case MIDI_STOP: uClock.stop(); break; case MIDI_START: case MIDI_CONTINUE: uClock.start(); break; } } static void sendMIDIStart() { NeoSerial.write(MIDI_START); } static void sendMIDIStop() { NeoSerial.write(MIDI_STOP); } static void sendMidiClock(uint32_t tick) { NeoSerial.write(MIDI_CLOCK); } static void sendPulseOut(uint32_t tick) { digitalWrite(PULSE_OUT_PIN, !digitalRead(PULSE_OUT_PIN)); } }; #endif