add additional external ppqn

This commit is contained in:
2026-02-21 09:44:50 -08:00
parent 6d38c6b36b
commit 763d58f411
3 changed files with 153 additions and 146 deletions

View File

@ -266,6 +266,12 @@ void DisplayMainPage() {
case Clock::SOURCE_EXTERNAL_PPQN_4: case Clock::SOURCE_EXTERNAL_PPQN_4:
subText = F("4 PPQN"); subText = F("4 PPQN");
break; break;
case Clock::SOURCE_EXTERNAL_PPQN_2:
subText = F("2 PPQN");
break;
case Clock::SOURCE_EXTERNAL_PPQN_1:
subText = F("1 PPQN");
break;
case Clock::SOURCE_EXTERNAL_MIDI: case Clock::SOURCE_EXTERNAL_MIDI:
subText = F("MIDI"); subText = F("MIDI");
break; break;

View File

@ -285,6 +285,12 @@ void DisplayMainPage() {
case Clock::SOURCE_EXTERNAL_PPQN_4: case Clock::SOURCE_EXTERNAL_PPQN_4:
subText = F("4 PPQN"); subText = F("4 PPQN");
break; break;
case Clock::SOURCE_EXTERNAL_PPQN_2:
subText = F("2 PPQN");
break;
case Clock::SOURCE_EXTERNAL_PPQN_1:
subText = F("1 PPQN");
break;
case Clock::SOURCE_EXTERNAL_MIDI: case Clock::SOURCE_EXTERNAL_MIDI:
subText = F("MIDI"); subText = F("MIDI");
break; break;

View File

@ -4,7 +4,7 @@
* @brief Wrapper Class for clock timing functions. * @brief Wrapper Class for clock timing functions.
* @version 0.1 * @version 0.1
* @date 2025-05-04 * @date 2025-05-04
* *
* @copyright MIT - (c) 2025 - Adam Wonak - adam.wonak@gmail.com * @copyright MIT - (c) 2025 - Adam Wonak - adam.wonak@gmail.com
* *
*/ */
@ -17,7 +17,8 @@
#include "peripherials.h" #include "peripherials.h"
#include "uClock/uClock.h" #include "uClock/uClock.h"
// MIDI clock, start, stop, and continue byte definitions - based on MIDI 1.0 Standards. // MIDI clock, start, stop, and continue byte definitions - based on MIDI 1.0
// Standards.
#define MIDI_CLOCK 0xF8 #define MIDI_CLOCK 0xF8
#define MIDI_START 0xFA #define MIDI_START 0xFA
#define MIDI_STOP 0xFC #define MIDI_STOP 0xFC
@ -28,164 +29,158 @@ static ExtCallback extUserCallback = nullptr;
static void serialEventNoop(uint8_t msg, uint8_t status) {} static void serialEventNoop(uint8_t msg, uint8_t status) {}
class Clock { class Clock {
public: public:
static constexpr int DEFAULT_TEMPO = 120; static constexpr int DEFAULT_TEMPO = 120;
enum Source { enum Source {
SOURCE_INTERNAL, SOURCE_INTERNAL,
SOURCE_EXTERNAL_PPQN_24, SOURCE_EXTERNAL_PPQN_24,
SOURCE_EXTERNAL_PPQN_4, SOURCE_EXTERNAL_PPQN_4,
SOURCE_EXTERNAL_MIDI, SOURCE_EXTERNAL_PPQN_2,
SOURCE_LAST, SOURCE_EXTERNAL_PPQN_1,
}; SOURCE_EXTERNAL_MIDI,
SOURCE_LAST,
};
enum Pulse { enum Pulse {
PULSE_NONE, PULSE_NONE,
PULSE_PPQN_1, PULSE_PPQN_1,
PULSE_PPQN_4, PULSE_PPQN_4,
PULSE_PPQN_24, PULSE_PPQN_24,
PULSE_LAST, PULSE_LAST,
}; };
void Init() { void Init() {
NeoSerial.begin(31250); NeoSerial.begin(31250);
// Initialize the clock library // Initialize the clock library
uClock.init(); uClock.init();
uClock.setClockMode(uClock.INTERNAL_CLOCK); uClock.setClockMode(uClock.INTERNAL_CLOCK);
uClock.setOutputPPQN(uClock.PPQN_96); uClock.setOutputPPQN(uClock.PPQN_96);
uClock.setTempo(DEFAULT_TEMPO); uClock.setTempo(DEFAULT_TEMPO);
// MIDI events. // MIDI events.
uClock.setOnClockStart(sendMIDIStart); uClock.setOnClockStart(sendMIDIStart);
uClock.setOnClockStop(sendMIDIStop); uClock.setOnClockStop(sendMIDIStop);
uClock.setOnSync24(sendMIDIClock); uClock.setOnSync24(sendMIDIClock);
uClock.start(); 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();
// If we are changing the source from MIDI, disable the serial interrupt
// handler.
if (source_ == SOURCE_EXTERNAL_MIDI) {
NeoSerial.attachInterrupt(serialEventNoop);
} }
source_ = source;
// Handle external clock tick and call user callback when receiving clock trigger (PPQN_4, PPQN_24, or MIDI). switch (source) {
void AttachExtHandler(void (*callback)()) { case SOURCE_INTERNAL:
extUserCallback = callback; uClock.setClockMode(uClock.INTERNAL_CLOCK);
attachInterrupt(digitalPinToInterrupt(EXT_PIN), callback, RISING); 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_2:
uClock.setClockMode(uClock.EXTERNAL_CLOCK);
uClock.setInputPPQN(uClock.PPQN_2);
break;
case SOURCE_EXTERNAL_PPQN_1:
uClock.setClockMode(uClock.EXTERNAL_CLOCK);
uClock.setInputPPQN(uClock.PPQN_1);
break;
case SOURCE_EXTERNAL_MIDI:
uClock.setClockMode(uClock.EXTERNAL_CLOCK);
uClock.setInputPPQN(uClock.PPQN_24);
NeoSerial.attachInterrupt(onSerialEvent);
break;
} }
if (was_playing) {
// Internal PPQN96 callback for all clock timer operations. uClock.start();
void AttachIntHandler(void (*callback)(uint32_t)) {
uClock.setOnOutputPPQN(callback);
} }
}
// Set the source of the clock mode. // Return true if the current selected source is externl (PPQN_4, PPQN_24, or
void SetSource(Source source) { // MIDI).
bool was_playing = !IsPaused(); bool ExternalSource() {
uClock.stop(); return uClock.getClockMode() == uClock.EXTERNAL_CLOCK;
// If we are changing the source from MIDI, disable the serial interrupt handler. }
if (source_ == SOURCE_EXTERNAL_MIDI) {
NeoSerial.attachInterrupt(serialEventNoop); // Return true if the current selected source is the internal master clock.
} bool InternalSource() {
source_ = source; return uClock.getClockMode() == uClock.INTERNAL_CLOCK;
switch (source) { }
case SOURCE_INTERNAL:
uClock.setClockMode(uClock.INTERNAL_CLOCK); // Returns the current BPM tempo.
break; int Tempo() { return uClock.getTempo(); }
case SOURCE_EXTERNAL_PPQN_24:
uClock.setClockMode(uClock.EXTERNAL_CLOCK); // Set the clock tempo to a int between 1 and 400.
uClock.setInputPPQN(uClock.PPQN_24); void SetTempo(int tempo) { return uClock.setTempo(tempo); }
break;
case SOURCE_EXTERNAL_PPQN_4: // Record an external clock tick received to process external/internal
uClock.setClockMode(uClock.EXTERNAL_CLOCK); // syncronization.
uClock.setInputPPQN(uClock.PPQN_4); void Tick() { uClock.clockMe(); }
break;
case SOURCE_EXTERNAL_MIDI: // Start the internal clock.
uClock.setClockMode(uClock.EXTERNAL_CLOCK); void Start() { uClock.start(); }
uClock.setInputPPQN(uClock.PPQN_24);
NeoSerial.attachInterrupt(onSerialEvent); // Stop internal clock clock.
break; void Stop() { uClock.stop(); }
}
if (was_playing) { // Reset all clock counters to 0.
uClock.start(); 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;
static void onSerialEvent(uint8_t msg, uint8_t status) {
// Note: uClock start and stop will echo to MIDI.
switch (msg) {
case MIDI_CLOCK:
if (extUserCallback) {
extUserCallback();
}
break;
case MIDI_STOP:
uClock.stop();
sendMIDIStop();
break;
case MIDI_START:
case MIDI_CONTINUE:
uClock.start();
sendMIDIStart();
break;
} }
}
// Return true if the current selected source is externl (PPQN_4, PPQN_24, or MIDI). static void sendMIDIStart() { NeoSerial.write(MIDI_START); }
bool ExternalSource() {
return uClock.getClockMode() == uClock.EXTERNAL_CLOCK;
}
// Return true if the current selected source is the internal master clock. static void sendMIDIStop() { NeoSerial.write(MIDI_STOP); }
bool InternalSource() {
return uClock.getClockMode() == uClock.INTERNAL_CLOCK;
}
// Returns the current BPM tempo. static void sendMIDIClock(uint32_t tick) { NeoSerial.write(MIDI_CLOCK); }
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;
static void onSerialEvent(uint8_t msg, uint8_t status) {
// Note: uClock start and stop will echo to MIDI.
switch (msg) {
case MIDI_CLOCK:
if (extUserCallback) {
extUserCallback();
}
break;
case MIDI_STOP:
uClock.stop();
sendMIDIStop();
break;
case MIDI_START:
case MIDI_CONTINUE:
uClock.start();
sendMIDIStart();
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);
}
}; };
#endif #endif