Add clock run/reset

This commit is contained in:
2026-02-21 10:19:09 -08:00
parent 763d58f411
commit bd08ac4352
12 changed files with 324 additions and 162 deletions

View File

@ -107,6 +107,28 @@ void loop() {
}
}
// Clock Run
if (app.cv_run == 1 || app.cv_run == 2) {
auto &cv = app.cv_run == 1 ? gravity.cv1 : gravity.cv2;
int val = cv.Read();
if (val > AnalogInput::GATE_THRESHOLD && gravity.clock.IsPaused()) {
gravity.clock.Start();
app.refresh_screen = true;
} else if (val < AnalogInput::GATE_THRESHOLD && !gravity.clock.IsPaused()) {
gravity.clock.Stop();
ResetOutputs();
app.refresh_screen = true;
}
}
// Clock Reset
if ((app.cv_reset == 1 &&
gravity.cv1.IsRisingEdge(AnalogInput::GATE_THRESHOLD)) ||
(app.cv_reset == 2 &&
gravity.cv2.IsRisingEdge(AnalogInput::GATE_THRESHOLD))) {
gravity.clock.Reset();
}
// Check for dirty state eligible to be saved.
stateManager.update(app);
@ -285,6 +307,14 @@ void editMainParameter(int val) {
gravity.clock.SetTempo(gravity.clock.Tempo() + val);
app.tempo = gravity.clock.Tempo();
break;
case PARAM_MAIN_RUN:
updateSelection(app.selected_sub_param, val, 3);
app.cv_run = app.selected_sub_param;
break;
case PARAM_MAIN_RESET:
updateSelection(app.selected_sub_param, val, 3);
app.cv_reset = app.selected_sub_param;
break;
case PARAM_MAIN_SOURCE: {
byte source = static_cast<int>(app.selected_source);
updateSelection(source, val, Clock::SOURCE_LAST);
@ -301,6 +331,7 @@ void editMainParameter(int val) {
}
break;
}
// These changes are applied upon encoder button press.
case PARAM_MAIN_ENCODER_DIR:
updateSelection(app.selected_sub_param, val, 2);
break;

View File

@ -18,24 +18,26 @@
// Global state for settings and app behavior.
struct AppState {
int tempo = Clock::DEFAULT_TEMPO;
Channel channel[Gravity::OUTPUT_COUNT];
byte selected_param = 0;
byte selected_sub_param = 0; // Temporary value for editing params.
byte selected_channel = 0; // 0=tempo, 1-6=output channel
byte selected_swing = 0;
byte selected_save_slot = 0; // The currently active save slot.
Clock::Source selected_source = Clock::SOURCE_INTERNAL;
Clock::Pulse selected_pulse = Clock::PULSE_PPQN_24;
bool editing_param = false;
bool encoder_reversed = false;
bool refresh_screen = true;
int tempo = Clock::DEFAULT_TEMPO;
Channel channel[Gravity::OUTPUT_COUNT];
byte selected_param = 0;
byte selected_sub_param = 0; // Temporary value for editing params.
byte selected_channel = 0; // 0=tempo, 1-6=output channel
byte selected_swing = 0;
byte selected_save_slot = 0; // The currently active save slot.
byte cv_run = 0;
byte cv_reset = 0;
Clock::Source selected_source = Clock::SOURCE_INTERNAL;
Clock::Pulse selected_pulse = Clock::PULSE_PPQN_24;
bool editing_param = false;
bool encoder_reversed = false;
bool refresh_screen = true;
};
extern AppState app;
static Channel& GetSelectedChannel() {
return app.channel[app.selected_channel - 1];
static Channel &GetSelectedChannel() {
return app.channel[app.selected_channel - 1];
}
#endif // APP_STATE_H
#endif // APP_STATE_H

View File

@ -128,6 +128,8 @@ constexpr uint8_t CHANNEL_BOX_HEIGHT = 14;
enum ParamsMainPage : uint8_t {
PARAM_MAIN_TEMPO,
PARAM_MAIN_SOURCE,
PARAM_MAIN_RUN,
PARAM_MAIN_RESET,
PARAM_MAIN_PULSE,
PARAM_MAIN_ENCODER_DIR,
PARAM_MAIN_SAVE_DATA,
@ -296,6 +298,34 @@ void DisplayMainPage() {
break;
}
break;
case PARAM_MAIN_RUN:
mainText = F("RUN");
switch (app.cv_run) {
case 0:
subText = F("NONE");
break;
case 1:
subText = F("CV 1");
break;
case 2:
subText = F("CV 2");
break;
}
break;
case PARAM_MAIN_RESET:
mainText = F("RST");
switch (app.cv_reset) {
case 0:
subText = F("NONE");
break;
case 1:
subText = F("CV 1");
break;
case 2:
subText = F("CV 2");
break;
}
break;
case PARAM_MAIN_PULSE:
mainText = F("OUT");
switch (app.selected_pulse) {
@ -358,8 +388,9 @@ void DisplayMainPage() {
// Draw Main Page menu items
String menu_items[PARAM_MAIN_LAST] = {
F("TEMPO"), F("SOURCE"), F("PULSE OUT"), F("ENCODER DIR"),
F("SAVE"), F("LOAD"), F("RESET"), F("ERASE")};
F("TEMPO"), F("SOURCE"), F("CLK RUN"),
F("CLK RESET"), F("PULSE OUT"), F("ENCODER DIR"),
F("SAVE"), F("LOAD"), F("ERASE")};
drawMenuItems(menu_items, PARAM_MAIN_LAST);
}

View File

@ -18,7 +18,7 @@
// Define the constants for the current firmware.
const char StateManager::SKETCH_NAME[] = "ALT GRAVITY";
const char StateManager::SEMANTIC_VERSION[] =
"V2.0.0BETA2"; // NOTE: This should match the version in the
"V2.0.0BETA4"; // NOTE: This should match the version in the
// library.properties file.
// Number of available save slots.
@ -94,6 +94,8 @@ void StateManager::reset(AppState &app) {
app.selected_channel = default_app.selected_channel;
app.selected_source = default_app.selected_source;
app.selected_pulse = default_app.selected_pulse;
app.cv_run = default_app.cv_run;
app.cv_reset = default_app.cv_reset;
for (int i = 0; i < Gravity::OUTPUT_COUNT; i++) {
app.channel[i].Init();
@ -148,6 +150,8 @@ void StateManager::_saveState(const AppState &app, byte slot_index) {
save_data.selected_channel = app.selected_channel;
save_data.selected_source = static_cast<byte>(app.selected_source);
save_data.selected_pulse = static_cast<byte>(app.selected_pulse);
save_data.cv_run = app.cv_run;
save_data.cv_reset = app.cv_reset;
// TODO: break this out into a separate function. Save State should be
// broken out into global / per-channel save methods. When saving via
@ -185,6 +189,8 @@ void StateManager::_loadState(AppState &app, byte slot_index) {
app.selected_channel = load_data.selected_channel;
app.selected_source = static_cast<Clock::Source>(load_data.selected_source);
app.selected_pulse = static_cast<Clock::Pulse>(load_data.selected_pulse);
app.cv_run = load_data.cv_run;
app.cv_reset = load_data.cv_reset;
for (int i = 0; i < Gravity::OUTPUT_COUNT; i++) {
auto &ch = app.channel[i];

View File

@ -73,6 +73,8 @@ public:
byte selected_channel;
byte selected_source;
byte selected_pulse;
byte cv_run;
byte cv_reset;
ChannelState channel_data[Gravity::OUTPUT_COUNT];
};