Consistent clock behavior for ext cv and midi.
This commit is contained in:
58
clock.h
58
clock.h
@ -24,7 +24,10 @@
|
|||||||
#define MIDI_CONTINUE 0xFB
|
#define MIDI_CONTINUE 0xFB
|
||||||
|
|
||||||
const int DEFAULT_TEMPO = 120;
|
const int DEFAULT_TEMPO = 120;
|
||||||
static bool MIDI_ENABLED = false;
|
|
||||||
|
typedef void (*ExtCallback)(void);
|
||||||
|
static ExtCallback extUserCallback = nullptr;
|
||||||
|
static void serialEventNoop(uint8_t msg, uint8_t status) {}
|
||||||
|
|
||||||
enum Source {
|
enum Source {
|
||||||
SOURCE_INTERNAL,
|
SOURCE_INTERNAL,
|
||||||
@ -38,12 +41,12 @@ class Clock {
|
|||||||
public:
|
public:
|
||||||
void Init() {
|
void Init() {
|
||||||
NeoSerial.begin(31250);
|
NeoSerial.begin(31250);
|
||||||
NeoSerial.attachInterrupt(onSerialEvent);
|
|
||||||
|
|
||||||
// Static pin definition for pulse out.
|
// Static pin definition for pulse out.
|
||||||
pinMode(PULSE_OUT_PIN, OUTPUT);
|
pinMode(PULSE_OUT_PIN, OUTPUT);
|
||||||
|
|
||||||
// Initialize the clock library
|
// Initialize the clock library
|
||||||
|
uClock.setExtIntervalBuffer(32);
|
||||||
uClock.init();
|
uClock.init();
|
||||||
uClock.setClockMode(uClock.INTERNAL_CLOCK);
|
uClock.setClockMode(uClock.INTERNAL_CLOCK);
|
||||||
uClock.setOutputPPQN(uClock.PPQN_96);
|
uClock.setOutputPPQN(uClock.PPQN_96);
|
||||||
@ -52,14 +55,15 @@ class Clock {
|
|||||||
// MIDI events.
|
// MIDI events.
|
||||||
uClock.setOnClockStart(sendMIDIStart);
|
uClock.setOnClockStart(sendMIDIStart);
|
||||||
uClock.setOnClockStop(sendMIDIStop);
|
uClock.setOnClockStop(sendMIDIStop);
|
||||||
uClock.setOnSync24(sendMidiClock);
|
uClock.setOnSync24(sendMIDIClock);
|
||||||
uClock.setOnSync48(sendPulseOut);
|
uClock.setOnSync48(sendPulseOut);
|
||||||
|
|
||||||
uClock.start();
|
uClock.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler for receiving clock trigger(PPQN_4 or PPQN_24).
|
// Handle external clock tick and call user callback when receiving clock trigger (PPQN_4, PPQN_24, or MIDI).
|
||||||
void AttachExtHandler(void (*callback)(void)) {
|
void AttachExtHandler(void (*callback)()) {
|
||||||
|
extUserCallback = callback;
|
||||||
attachInterrupt(digitalPinToInterrupt(EXT_PIN), callback, RISING);
|
attachInterrupt(digitalPinToInterrupt(EXT_PIN), callback, RISING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,8 +74,13 @@ class Clock {
|
|||||||
|
|
||||||
// Set the source of the clock mode.
|
// Set the source of the clock mode.
|
||||||
void SetSource(Source source) {
|
void SetSource(Source source) {
|
||||||
|
bool was_playing = !IsPaused();
|
||||||
uClock.stop();
|
uClock.stop();
|
||||||
MIDI_ENABLED = false;
|
// If source is currently MIDI, disable the serial interrupt handler.
|
||||||
|
if (source_ == SOURCE_EXTERNAL_MIDI) {
|
||||||
|
NeoSerial.attachInterrupt(serialEventNoop);
|
||||||
|
}
|
||||||
|
source_ = source;
|
||||||
switch (source) {
|
switch (source) {
|
||||||
case SOURCE_INTERNAL:
|
case SOURCE_INTERNAL:
|
||||||
uClock.setClockMode(uClock.INTERNAL_CLOCK);
|
uClock.setClockMode(uClock.INTERNAL_CLOCK);
|
||||||
@ -87,52 +96,64 @@ class Clock {
|
|||||||
case SOURCE_EXTERNAL_MIDI:
|
case SOURCE_EXTERNAL_MIDI:
|
||||||
uClock.setClockMode(uClock.EXTERNAL_CLOCK);
|
uClock.setClockMode(uClock.EXTERNAL_CLOCK);
|
||||||
uClock.setInputPPQN(uClock.PPQN_24);
|
uClock.setInputPPQN(uClock.PPQN_24);
|
||||||
MIDI_ENABLED = true;
|
NeoSerial.attachInterrupt(onSerialEvent);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
uClock.start();
|
if (was_playing) {
|
||||||
|
uClock.start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return true if the current selected source is externl (PPQN_4, PPQN_24, or MIDI).
|
||||||
bool ExternalSource() {
|
bool ExternalSource() {
|
||||||
return uClock.getClockMode() == uClock.EXTERNAL_CLOCK;
|
return uClock.getClockMode() == uClock.EXTERNAL_CLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return true if the current selected source is the internal master clock.
|
||||||
bool InternalSource() {
|
bool InternalSource() {
|
||||||
return uClock.getClockMode() == uClock.INTERNAL_CLOCK;
|
return uClock.getClockMode() == uClock.INTERNAL_CLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the current BPM tempo.
|
||||||
int Tempo() {
|
int Tempo() {
|
||||||
return uClock.getTempo();
|
return uClock.getTempo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the clock tempo to a int between 1 and 400.
|
||||||
void SetTempo(int tempo) {
|
void SetTempo(int tempo) {
|
||||||
return uClock.setTempo(tempo);
|
return uClock.setTempo(tempo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Record an external clock tick received to process external/internal syncronization.
|
||||||
void Tick() {
|
void Tick() {
|
||||||
uClock.clockMe();
|
uClock.clockMe();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pause() {
|
// Start the internal clock.
|
||||||
uClock.pause();
|
void Start() {
|
||||||
}
|
|
||||||
|
|
||||||
void Reset() {
|
|
||||||
uClock.start();
|
uClock.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop internal clock clock.
|
||||||
|
void Stop() {
|
||||||
|
uClock.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the clock is not running.
|
||||||
bool IsPaused() {
|
bool IsPaused() {
|
||||||
return uClock.clock_state == uClock.PAUSED;
|
return uClock.clock_state == uClock.PAUSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Source source_ = SOURCE_INTERNAL;
|
||||||
|
|
||||||
static void onSerialEvent(uint8_t msg, uint8_t status) {
|
static void onSerialEvent(uint8_t msg, uint8_t status) {
|
||||||
// if (!MIDI_ENABLED) {
|
// Note: uClock start and stop will echo to MIDI.
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case MIDI_CLOCK:
|
case MIDI_CLOCK:
|
||||||
uClock.clockMe();
|
if (extUserCallback) {
|
||||||
|
extUserCallback();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case MIDI_STOP:
|
case MIDI_STOP:
|
||||||
uClock.stop();
|
uClock.stop();
|
||||||
@ -141,7 +162,6 @@ class Clock {
|
|||||||
case MIDI_CONTINUE:
|
case MIDI_CONTINUE:
|
||||||
uClock.start();
|
uClock.start();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +173,7 @@ class Clock {
|
|||||||
NeoSerial.write(MIDI_STOP);
|
NeoSerial.write(MIDI_STOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sendMidiClock(uint32_t tick) {
|
static void sendMIDIClock(uint32_t tick) {
|
||||||
NeoSerial.write(MIDI_CLOCK);
|
NeoSerial.write(MIDI_CLOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
*
|
*
|
||||||
* BTN1: Play/pause the internal clock.
|
* BTN1: Play/pause the internal clock.
|
||||||
*
|
*
|
||||||
* BTN2: Reset all clocks.
|
* BTN2: Stop all clocks.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -68,8 +68,8 @@ void setup() {
|
|||||||
gravity.Init();
|
gravity.Init();
|
||||||
|
|
||||||
// Clock handlers.
|
// Clock handlers.
|
||||||
gravity.clock.AttachExtHandler(ExtClock);
|
gravity.clock.AttachIntHandler(HandleIntClockTick);
|
||||||
gravity.clock.AttachIntHandler(IntClock);
|
gravity.clock.AttachExtHandler(HandleExtClockTick);
|
||||||
|
|
||||||
// Encoder rotate and press handlers.
|
// Encoder rotate and press handlers.
|
||||||
gravity.encoder.AttachPressHandler(HandleEncoderPressed);
|
gravity.encoder.AttachPressHandler(HandleEncoderPressed);
|
||||||
@ -95,16 +95,9 @@ void loop() {
|
|||||||
// Firmware handlers.
|
// Firmware handlers.
|
||||||
//
|
//
|
||||||
|
|
||||||
void ExtClock() {
|
void HandleIntClockTick(uint32_t tick) {
|
||||||
if (gravity.clock.ExternalSource()) {
|
|
||||||
gravity.clock.Tick();
|
|
||||||
app.refresh_screen = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void IntClock(uint32_t tick) {
|
|
||||||
for (int i = 0; i < OUTPUT_COUNT; i++) {
|
for (int i = 0; i < OUTPUT_COUNT; i++) {
|
||||||
const auto& channel = app.channel[i];
|
auto& channel = app.channel[i];
|
||||||
auto& output = gravity.outputs[i];
|
auto& output = gravity.outputs[i];
|
||||||
|
|
||||||
const uint32_t mod_pulses = clock_mod_pulses[channel.clock_mod_index];
|
const uint32_t mod_pulses = clock_mod_pulses[channel.clock_mod_index];
|
||||||
@ -126,18 +119,27 @@ void IntClock(uint32_t tick) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandlePlayPressed() {
|
void HandleExtClockTick() {
|
||||||
gravity.clock.Pause();
|
// Ignore tick if not using external source.
|
||||||
if (gravity.clock.IsPaused()) {
|
if (!gravity.clock.ExternalSource()) {
|
||||||
for (int i = 0; i < OUTPUT_COUNT; i++) {
|
return;
|
||||||
gravity.outputs[i].Low();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
gravity.clock.Tick();
|
||||||
|
app.refresh_screen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandlePlayPressed() {
|
||||||
|
gravity.clock.IsPaused()
|
||||||
|
? gravity.clock.Start()
|
||||||
|
: gravity.clock.Stop();
|
||||||
|
ResetOutputs();
|
||||||
app.refresh_screen = true;
|
app.refresh_screen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleShiftPressed() {
|
void HandleShiftPressed() {
|
||||||
gravity.clock.Reset();
|
gravity.clock.Stop();
|
||||||
|
ResetOutputs();
|
||||||
|
app.refresh_screen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleEncoderPressed() {
|
void HandleEncoderPressed() {
|
||||||
@ -228,6 +230,12 @@ Channel& GetSelectedChannel() {
|
|||||||
return app.channel[app.selected_channel - 1];
|
return app.channel[app.selected_channel - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResetOutputs() {
|
||||||
|
for (int i = 0; i < OUTPUT_COUNT; i++) {
|
||||||
|
gravity.outputs[i].Low();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// UI Display functions.
|
// UI Display functions.
|
||||||
//
|
//
|
||||||
|
|||||||
16
gravity.cpp
16
gravity.cpp
@ -12,17 +12,17 @@
|
|||||||
#include "gravity.h"
|
#include "gravity.h"
|
||||||
|
|
||||||
void Gravity::Init() {
|
void Gravity::Init() {
|
||||||
InitClock();
|
initClock();
|
||||||
InitInputs();
|
initInputs();
|
||||||
InitOutputs();
|
initOutputs();
|
||||||
InitDisplay();
|
initDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gravity::InitClock() {
|
void Gravity::initClock() {
|
||||||
clock.Init();
|
clock.Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gravity::InitInputs() {
|
void Gravity::initInputs() {
|
||||||
shift_button.Init(SHIFT_BTN_PIN);
|
shift_button.Init(SHIFT_BTN_PIN);
|
||||||
play_button.Init(PLAY_BTN_PIN);
|
play_button.Init(PLAY_BTN_PIN);
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ void Gravity::InitInputs() {
|
|||||||
PCMSK1 |= B00001000;
|
PCMSK1 |= B00001000;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gravity::InitOutputs() {
|
void Gravity::initOutputs() {
|
||||||
// Initialize each of the outputs with it's GPIO pins and probability.
|
// Initialize each of the outputs with it's GPIO pins and probability.
|
||||||
outputs[0].Init(OUT_CH1);
|
outputs[0].Init(OUT_CH1);
|
||||||
outputs[1].Init(OUT_CH2);
|
outputs[1].Init(OUT_CH2);
|
||||||
@ -49,7 +49,7 @@ void Gravity::InitOutputs() {
|
|||||||
outputs[4].Init(OUT_CH5);
|
outputs[4].Init(OUT_CH5);
|
||||||
outputs[5].Init(OUT_CH6);
|
outputs[5].Init(OUT_CH6);
|
||||||
}
|
}
|
||||||
void Gravity::InitDisplay() {
|
void Gravity::initDisplay() {
|
||||||
// OLED Display configuration.
|
// OLED Display configuration.
|
||||||
display.begin();
|
display.begin();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,10 +37,10 @@ class Gravity {
|
|||||||
AnalogInput cv2;
|
AnalogInput cv2;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void InitClock();
|
void initClock();
|
||||||
void InitDisplay();
|
void initDisplay();
|
||||||
void InitInputs();
|
void initInputs();
|
||||||
void InitOutputs();
|
void initOutputs();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Gravity gravity;
|
extern Gravity gravity;
|
||||||
|
|||||||
Reference in New Issue
Block a user