Add clock run/reset
This commit is contained in:
@ -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.
|
// Check for dirty state eligible to be saved.
|
||||||
stateManager.update(app);
|
stateManager.update(app);
|
||||||
|
|
||||||
@ -285,6 +307,14 @@ void editMainParameter(int val) {
|
|||||||
gravity.clock.SetTempo(gravity.clock.Tempo() + val);
|
gravity.clock.SetTempo(gravity.clock.Tempo() + val);
|
||||||
app.tempo = gravity.clock.Tempo();
|
app.tempo = gravity.clock.Tempo();
|
||||||
break;
|
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: {
|
case PARAM_MAIN_SOURCE: {
|
||||||
byte source = static_cast<int>(app.selected_source);
|
byte source = static_cast<int>(app.selected_source);
|
||||||
updateSelection(source, val, Clock::SOURCE_LAST);
|
updateSelection(source, val, Clock::SOURCE_LAST);
|
||||||
@ -301,6 +331,7 @@ void editMainParameter(int val) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// These changes are applied upon encoder button press.
|
||||||
case PARAM_MAIN_ENCODER_DIR:
|
case PARAM_MAIN_ENCODER_DIR:
|
||||||
updateSelection(app.selected_sub_param, val, 2);
|
updateSelection(app.selected_sub_param, val, 2);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -18,24 +18,26 @@
|
|||||||
|
|
||||||
// Global state for settings and app behavior.
|
// Global state for settings and app behavior.
|
||||||
struct AppState {
|
struct AppState {
|
||||||
int tempo = Clock::DEFAULT_TEMPO;
|
int tempo = Clock::DEFAULT_TEMPO;
|
||||||
Channel channel[Gravity::OUTPUT_COUNT];
|
Channel channel[Gravity::OUTPUT_COUNT];
|
||||||
byte selected_param = 0;
|
byte selected_param = 0;
|
||||||
byte selected_sub_param = 0; // Temporary value for editing params.
|
byte selected_sub_param = 0; // Temporary value for editing params.
|
||||||
byte selected_channel = 0; // 0=tempo, 1-6=output channel
|
byte selected_channel = 0; // 0=tempo, 1-6=output channel
|
||||||
byte selected_swing = 0;
|
byte selected_swing = 0;
|
||||||
byte selected_save_slot = 0; // The currently active save slot.
|
byte selected_save_slot = 0; // The currently active save slot.
|
||||||
Clock::Source selected_source = Clock::SOURCE_INTERNAL;
|
Clock::Source selected_source = Clock::SOURCE_INTERNAL;
|
||||||
Clock::Pulse selected_pulse = Clock::PULSE_PPQN_24;
|
Clock::Pulse selected_pulse = Clock::PULSE_PPQN_24;
|
||||||
bool editing_param = false;
|
byte cv_run = 0;
|
||||||
bool encoder_reversed = false;
|
byte cv_reset = 0;
|
||||||
bool refresh_screen = true;
|
bool editing_param = false;
|
||||||
|
bool encoder_reversed = false;
|
||||||
|
bool refresh_screen = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern AppState app;
|
extern AppState app;
|
||||||
|
|
||||||
static Channel& GetSelectedChannel() {
|
static Channel &GetSelectedChannel() {
|
||||||
return app.channel[app.selected_channel - 1];
|
return app.channel[app.selected_channel - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // APP_STATE_H
|
#endif // APP_STATE_H
|
||||||
@ -127,6 +127,8 @@ constexpr uint8_t CHANNEL_BOX_HEIGHT = 14;
|
|||||||
// Menu items for editing global parameters.
|
// Menu items for editing global parameters.
|
||||||
enum ParamsMainPage : uint8_t {
|
enum ParamsMainPage : uint8_t {
|
||||||
PARAM_MAIN_TEMPO,
|
PARAM_MAIN_TEMPO,
|
||||||
|
PARAM_MAIN_RUN,
|
||||||
|
PARAM_MAIN_RESET,
|
||||||
PARAM_MAIN_SOURCE,
|
PARAM_MAIN_SOURCE,
|
||||||
PARAM_MAIN_PULSE,
|
PARAM_MAIN_PULSE,
|
||||||
PARAM_MAIN_ENCODER_DIR,
|
PARAM_MAIN_ENCODER_DIR,
|
||||||
@ -253,6 +255,34 @@ void DisplayMainPage() {
|
|||||||
}
|
}
|
||||||
subText = F("BPM");
|
subText = F("BPM");
|
||||||
break;
|
break;
|
||||||
|
case PARAM_MAIN_RUN:
|
||||||
|
mainText = F("RUN");
|
||||||
|
switch (app.cv_run) {
|
||||||
|
case 0:
|
||||||
|
subText = F("NONE");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
subText = F("CV1 GATE");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
subText = F("CV2 GATE");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PARAM_MAIN_RESET:
|
||||||
|
mainText = F("RST");
|
||||||
|
switch (app.cv_reset) {
|
||||||
|
case 0:
|
||||||
|
subText = F("NONE");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
subText = F("CV1 TRIG");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
subText = F("CV2 TRIG");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case PARAM_MAIN_SOURCE:
|
case PARAM_MAIN_SOURCE:
|
||||||
mainText = F("EXT");
|
mainText = F("EXT");
|
||||||
switch (app.selected_source) {
|
switch (app.selected_source) {
|
||||||
@ -339,8 +369,9 @@ void DisplayMainPage() {
|
|||||||
|
|
||||||
// Draw Main Page menu items
|
// Draw Main Page menu items
|
||||||
String menu_items[PARAM_MAIN_LAST] = {
|
String menu_items[PARAM_MAIN_LAST] = {
|
||||||
F("TEMPO"), F("SOURCE"), F("PULSE OUT"), F("ENCODER DIR"),
|
F("TEMPO"), F("CLK RUN"), F("CLK RESET"), F("SOURCE"),
|
||||||
F("SAVE"), F("LOAD"), F("RESET"), F("ERASE")};
|
F("PULSE OUT"), F("ENCODER DIR"), F("SAVE"), F("LOAD"),
|
||||||
|
F("RESET"), F("ERASE")};
|
||||||
drawMenuItems(menu_items, PARAM_MAIN_LAST);
|
drawMenuItems(menu_items, PARAM_MAIN_LAST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
// Define the constants for the current firmware.
|
// Define the constants for the current firmware.
|
||||||
const char StateManager::SKETCH_NAME[] = "ALT EUCLIDEAN";
|
const char StateManager::SKETCH_NAME[] = "ALT EUCLIDEAN";
|
||||||
const char StateManager::SEMANTIC_VERSION[] =
|
const char StateManager::SEMANTIC_VERSION[] =
|
||||||
"V2.0.0BETA2"; // NOTE: This should match the version in the
|
"V2.0.0BETA3"; // NOTE: This should match the version in the
|
||||||
// library.properties file.
|
// library.properties file.
|
||||||
|
|
||||||
// Number of available save slots.
|
// Number of available save slots.
|
||||||
@ -94,6 +94,8 @@ void StateManager::reset(AppState &app) {
|
|||||||
app.selected_channel = default_app.selected_channel;
|
app.selected_channel = default_app.selected_channel;
|
||||||
app.selected_source = default_app.selected_source;
|
app.selected_source = default_app.selected_source;
|
||||||
app.selected_pulse = default_app.selected_pulse;
|
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++) {
|
for (int i = 0; i < Gravity::OUTPUT_COUNT; i++) {
|
||||||
app.channel[i].Init();
|
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_channel = app.selected_channel;
|
||||||
save_data.selected_source = static_cast<byte>(app.selected_source);
|
save_data.selected_source = static_cast<byte>(app.selected_source);
|
||||||
save_data.selected_pulse = static_cast<byte>(app.selected_pulse);
|
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
|
// TODO: break this out into a separate function. Save State should be
|
||||||
// broken out into global / per-channel save methods. When saving via
|
// broken out into global / per-channel save methods. When saving via
|
||||||
@ -184,6 +188,8 @@ void StateManager::_loadState(AppState &app, byte slot_index) {
|
|||||||
app.selected_channel = load_data.selected_channel;
|
app.selected_channel = load_data.selected_channel;
|
||||||
app.selected_source = static_cast<Clock::Source>(load_data.selected_source);
|
app.selected_source = static_cast<Clock::Source>(load_data.selected_source);
|
||||||
app.selected_pulse = static_cast<Clock::Pulse>(load_data.selected_pulse);
|
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++) {
|
for (int i = 0; i < Gravity::OUTPUT_COUNT; i++) {
|
||||||
auto &ch = app.channel[i];
|
auto &ch = app.channel[i];
|
||||||
|
|||||||
@ -72,6 +72,8 @@ public:
|
|||||||
byte selected_channel;
|
byte selected_channel;
|
||||||
byte selected_source;
|
byte selected_source;
|
||||||
byte selected_pulse;
|
byte selected_pulse;
|
||||||
|
byte cv_run;
|
||||||
|
byte cv_reset;
|
||||||
ChannelState channel_data[Gravity::OUTPUT_COUNT];
|
ChannelState channel_data[Gravity::OUTPUT_COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
// Check for dirty state eligible to be saved.
|
||||||
stateManager.update(app);
|
stateManager.update(app);
|
||||||
|
|
||||||
@ -285,6 +307,14 @@ void editMainParameter(int val) {
|
|||||||
gravity.clock.SetTempo(gravity.clock.Tempo() + val);
|
gravity.clock.SetTempo(gravity.clock.Tempo() + val);
|
||||||
app.tempo = gravity.clock.Tempo();
|
app.tempo = gravity.clock.Tempo();
|
||||||
break;
|
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: {
|
case PARAM_MAIN_SOURCE: {
|
||||||
byte source = static_cast<int>(app.selected_source);
|
byte source = static_cast<int>(app.selected_source);
|
||||||
updateSelection(source, val, Clock::SOURCE_LAST);
|
updateSelection(source, val, Clock::SOURCE_LAST);
|
||||||
@ -301,6 +331,7 @@ void editMainParameter(int val) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// These changes are applied upon encoder button press.
|
||||||
case PARAM_MAIN_ENCODER_DIR:
|
case PARAM_MAIN_ENCODER_DIR:
|
||||||
updateSelection(app.selected_sub_param, val, 2);
|
updateSelection(app.selected_sub_param, val, 2);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -18,24 +18,26 @@
|
|||||||
|
|
||||||
// Global state for settings and app behavior.
|
// Global state for settings and app behavior.
|
||||||
struct AppState {
|
struct AppState {
|
||||||
int tempo = Clock::DEFAULT_TEMPO;
|
int tempo = Clock::DEFAULT_TEMPO;
|
||||||
Channel channel[Gravity::OUTPUT_COUNT];
|
Channel channel[Gravity::OUTPUT_COUNT];
|
||||||
byte selected_param = 0;
|
byte selected_param = 0;
|
||||||
byte selected_sub_param = 0; // Temporary value for editing params.
|
byte selected_sub_param = 0; // Temporary value for editing params.
|
||||||
byte selected_channel = 0; // 0=tempo, 1-6=output channel
|
byte selected_channel = 0; // 0=tempo, 1-6=output channel
|
||||||
byte selected_swing = 0;
|
byte selected_swing = 0;
|
||||||
byte selected_save_slot = 0; // The currently active save slot.
|
byte selected_save_slot = 0; // The currently active save slot.
|
||||||
Clock::Source selected_source = Clock::SOURCE_INTERNAL;
|
byte cv_run = 0;
|
||||||
Clock::Pulse selected_pulse = Clock::PULSE_PPQN_24;
|
byte cv_reset = 0;
|
||||||
bool editing_param = false;
|
Clock::Source selected_source = Clock::SOURCE_INTERNAL;
|
||||||
bool encoder_reversed = false;
|
Clock::Pulse selected_pulse = Clock::PULSE_PPQN_24;
|
||||||
bool refresh_screen = true;
|
bool editing_param = false;
|
||||||
|
bool encoder_reversed = false;
|
||||||
|
bool refresh_screen = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern AppState app;
|
extern AppState app;
|
||||||
|
|
||||||
static Channel& GetSelectedChannel() {
|
static Channel &GetSelectedChannel() {
|
||||||
return app.channel[app.selected_channel - 1];
|
return app.channel[app.selected_channel - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // APP_STATE_H
|
#endif // APP_STATE_H
|
||||||
@ -128,6 +128,8 @@ constexpr uint8_t CHANNEL_BOX_HEIGHT = 14;
|
|||||||
enum ParamsMainPage : uint8_t {
|
enum ParamsMainPage : uint8_t {
|
||||||
PARAM_MAIN_TEMPO,
|
PARAM_MAIN_TEMPO,
|
||||||
PARAM_MAIN_SOURCE,
|
PARAM_MAIN_SOURCE,
|
||||||
|
PARAM_MAIN_RUN,
|
||||||
|
PARAM_MAIN_RESET,
|
||||||
PARAM_MAIN_PULSE,
|
PARAM_MAIN_PULSE,
|
||||||
PARAM_MAIN_ENCODER_DIR,
|
PARAM_MAIN_ENCODER_DIR,
|
||||||
PARAM_MAIN_SAVE_DATA,
|
PARAM_MAIN_SAVE_DATA,
|
||||||
@ -296,6 +298,34 @@ void DisplayMainPage() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
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:
|
case PARAM_MAIN_PULSE:
|
||||||
mainText = F("OUT");
|
mainText = F("OUT");
|
||||||
switch (app.selected_pulse) {
|
switch (app.selected_pulse) {
|
||||||
@ -358,8 +388,9 @@ void DisplayMainPage() {
|
|||||||
|
|
||||||
// Draw Main Page menu items
|
// Draw Main Page menu items
|
||||||
String menu_items[PARAM_MAIN_LAST] = {
|
String menu_items[PARAM_MAIN_LAST] = {
|
||||||
F("TEMPO"), F("SOURCE"), F("PULSE OUT"), F("ENCODER DIR"),
|
F("TEMPO"), F("SOURCE"), F("CLK RUN"),
|
||||||
F("SAVE"), F("LOAD"), F("RESET"), F("ERASE")};
|
F("CLK RESET"), F("PULSE OUT"), F("ENCODER DIR"),
|
||||||
|
F("SAVE"), F("LOAD"), F("ERASE")};
|
||||||
drawMenuItems(menu_items, PARAM_MAIN_LAST);
|
drawMenuItems(menu_items, PARAM_MAIN_LAST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
// Define the constants for the current firmware.
|
// Define the constants for the current firmware.
|
||||||
const char StateManager::SKETCH_NAME[] = "ALT GRAVITY";
|
const char StateManager::SKETCH_NAME[] = "ALT GRAVITY";
|
||||||
const char StateManager::SEMANTIC_VERSION[] =
|
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.
|
// library.properties file.
|
||||||
|
|
||||||
// Number of available save slots.
|
// Number of available save slots.
|
||||||
@ -94,6 +94,8 @@ void StateManager::reset(AppState &app) {
|
|||||||
app.selected_channel = default_app.selected_channel;
|
app.selected_channel = default_app.selected_channel;
|
||||||
app.selected_source = default_app.selected_source;
|
app.selected_source = default_app.selected_source;
|
||||||
app.selected_pulse = default_app.selected_pulse;
|
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++) {
|
for (int i = 0; i < Gravity::OUTPUT_COUNT; i++) {
|
||||||
app.channel[i].Init();
|
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_channel = app.selected_channel;
|
||||||
save_data.selected_source = static_cast<byte>(app.selected_source);
|
save_data.selected_source = static_cast<byte>(app.selected_source);
|
||||||
save_data.selected_pulse = static_cast<byte>(app.selected_pulse);
|
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
|
// TODO: break this out into a separate function. Save State should be
|
||||||
// broken out into global / per-channel save methods. When saving via
|
// 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_channel = load_data.selected_channel;
|
||||||
app.selected_source = static_cast<Clock::Source>(load_data.selected_source);
|
app.selected_source = static_cast<Clock::Source>(load_data.selected_source);
|
||||||
app.selected_pulse = static_cast<Clock::Pulse>(load_data.selected_pulse);
|
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++) {
|
for (int i = 0; i < Gravity::OUTPUT_COUNT; i++) {
|
||||||
auto &ch = app.channel[i];
|
auto &ch = app.channel[i];
|
||||||
|
|||||||
@ -73,6 +73,8 @@ public:
|
|||||||
byte selected_channel;
|
byte selected_channel;
|
||||||
byte selected_source;
|
byte selected_source;
|
||||||
byte selected_pulse;
|
byte selected_pulse;
|
||||||
|
byte cv_run;
|
||||||
|
byte cv_reset;
|
||||||
ChannelState channel_data[Gravity::OUTPUT_COUNT];
|
ChannelState channel_data[Gravity::OUTPUT_COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -11,78 +11,94 @@
|
|||||||
#ifndef ANALOG_INPUT_H
|
#ifndef ANALOG_INPUT_H
|
||||||
#define ANALOG_INPUT_H
|
#define ANALOG_INPUT_H
|
||||||
|
|
||||||
const int MAX_INPUT = (1 << 10) - 1; // Max 10 bit analog read resolution.
|
const int MAX_INPUT = (1 << 10) - 1; // Max 10 bit analog read resolution.
|
||||||
|
|
||||||
// estimated default calibration value
|
// estimated default calibration value
|
||||||
const int CALIBRATED_LOW = -566;
|
const int CALIBRATED_LOW = -566;
|
||||||
const int CALIBRATED_HIGH = 512;
|
const int CALIBRATED_HIGH = 512;
|
||||||
|
|
||||||
class AnalogInput {
|
class AnalogInput {
|
||||||
public:
|
public:
|
||||||
AnalogInput() {}
|
static const int GATE_THRESHOLD = 0;
|
||||||
~AnalogInput() {}
|
|
||||||
|
|
||||||
/**
|
AnalogInput() {}
|
||||||
* Initializes a analog input object.
|
~AnalogInput() {}
|
||||||
*
|
|
||||||
* @param pin gpio pin for the analog input.
|
|
||||||
*/
|
|
||||||
void Init(uint8_t pin) {
|
|
||||||
pinMode(pin, INPUT);
|
|
||||||
pin_ = pin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the value of the analog input and set instance state.
|
* Initializes a analog input object.
|
||||||
*
|
*
|
||||||
*/
|
* @param pin gpio pin for the analog input.
|
||||||
void Process() {
|
*/
|
||||||
old_read_ = read_;
|
void Init(uint8_t pin) {
|
||||||
int raw = analogRead(pin_);
|
pinMode(pin, INPUT);
|
||||||
read_ = map(raw, 0, MAX_INPUT, low_, high_);
|
pin_ = pin;
|
||||||
read_ = constrain(read_ - offset_, -512, 512);
|
}
|
||||||
if (inverted_) read_ = -read_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set calibration values.
|
/**
|
||||||
|
* Read the value of the analog input and set instance state.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void Process() {
|
||||||
|
old_read_ = read_;
|
||||||
|
int raw = analogRead(pin_);
|
||||||
|
read_ = map(raw, 0, MAX_INPUT, low_, high_);
|
||||||
|
read_ = constrain(read_ - offset_, -512, 512);
|
||||||
|
if (inverted_)
|
||||||
|
read_ = -read_;
|
||||||
|
}
|
||||||
|
|
||||||
void AdjustCalibrationLow(int amount) { low_ += amount; }
|
// Set calibration values.
|
||||||
|
|
||||||
void AdjustCalibrationHigh(int amount) { high_ += amount; }
|
void AdjustCalibrationLow(int amount) { low_ += amount; }
|
||||||
|
|
||||||
void SetOffset(float percent) { offset_ = -(percent)*512; }
|
void AdjustCalibrationHigh(int amount) { high_ += amount; }
|
||||||
|
|
||||||
void SetAttenuation(float percent) {
|
void SetOffset(float percent) { offset_ = -(percent) * 512; }
|
||||||
low_ = abs(percent) * CALIBRATED_LOW;
|
|
||||||
high_ = abs(percent) * CALIBRATED_HIGH;
|
|
||||||
inverted_ = percent < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
void SetAttenuation(float percent) {
|
||||||
* Get the current value of the analog input within a range of +/-512.
|
low_ = abs(percent) * CALIBRATED_LOW;
|
||||||
*
|
high_ = abs(percent) * CALIBRATED_HIGH;
|
||||||
* @return read value within a range of +/-512.
|
inverted_ = percent < 0;
|
||||||
*
|
}
|
||||||
*/
|
|
||||||
inline int16_t Read() { return read_; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the analog read value as voltage.
|
* Get the current value of the analog input within a range of +/-512.
|
||||||
*
|
*
|
||||||
* @return A float representing the voltage (-5.0 to +5.0).
|
* @return read value within a range of +/-512.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
inline float Voltage() { return ((read_ / 512.0) * 5.0); }
|
inline int16_t Read() { return read_; }
|
||||||
|
|
||||||
private:
|
/**
|
||||||
uint8_t pin_;
|
* Return the analog read value as voltage.
|
||||||
int16_t read_;
|
*
|
||||||
uint16_t old_read_;
|
* @return A float representing the voltage (-5.0 to +5.0).
|
||||||
// calibration values.
|
*
|
||||||
int offset_ = 0;
|
*/
|
||||||
int low_ = CALIBRATED_LOW;
|
inline float Voltage() { return ((read_ / 512.0) * 5.0); }
|
||||||
int high_ = CALIBRATED_HIGH;
|
|
||||||
bool inverted_ = false;
|
/**
|
||||||
|
* Checks for a rising edge transition across a threshold.
|
||||||
|
*
|
||||||
|
* @param threshold The value that the input must cross.
|
||||||
|
* @return True if the value just crossed the threshold from below, false
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
inline bool IsRisingEdge(int16_t threshold) const {
|
||||||
|
bool was_high = old_read_ > threshold;
|
||||||
|
bool is_high = read_ > threshold;
|
||||||
|
return is_high && !was_high;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t pin_;
|
||||||
|
int16_t read_;
|
||||||
|
uint16_t old_read_;
|
||||||
|
// calibration values.
|
||||||
|
int offset_ = 0;
|
||||||
|
int low_ = CALIBRATED_LOW;
|
||||||
|
int high_ = CALIBRATED_HIGH;
|
||||||
|
bool inverted_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
* @brief Class for interacting with trigger / gate outputs.
|
* @brief Class for interacting with trigger / gate outputs.
|
||||||
* @version 0.1
|
* @version 0.1
|
||||||
* @date 2025-04-17
|
* @date 2025-04-17
|
||||||
*
|
*
|
||||||
* @copyright MIT - (c) 2025 - Adam Wonak - adam.wonak@gmail.com
|
* @copyright MIT - (c) 2025 - Adam Wonak - adam.wonak@gmail.com
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -16,79 +16,81 @@
|
|||||||
const byte DEFAULT_TRIGGER_DURATION_MS = 5;
|
const byte DEFAULT_TRIGGER_DURATION_MS = 5;
|
||||||
|
|
||||||
class DigitalOutput {
|
class DigitalOutput {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Initializes an CV Output paired object.
|
* Initializes an CV Output paired object.
|
||||||
*
|
*
|
||||||
* @param cv_pin gpio pin for the cv output
|
* @param cv_pin gpio pin for the cv output
|
||||||
*/
|
*/
|
||||||
void Init(uint8_t cv_pin) {
|
void Init(uint8_t cv_pin) {
|
||||||
pinMode(cv_pin, OUTPUT); // Gate/Trigger Output
|
pinMode(cv_pin, OUTPUT); // Gate/Trigger Output
|
||||||
cv_pin_ = cv_pin;
|
cv_pin_ = cv_pin;
|
||||||
trigger_duration_ = DEFAULT_TRIGGER_DURATION_MS;
|
trigger_duration_ = DEFAULT_TRIGGER_DURATION_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the trigger duration in miliseconds.
|
||||||
|
*
|
||||||
|
* @param duration_ms trigger duration in miliseconds
|
||||||
|
*/
|
||||||
|
void SetTriggerDuration(uint8_t duration_ms) {
|
||||||
|
trigger_duration_ = duration_ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn the CV and LED on or off according to the input state.
|
||||||
|
*
|
||||||
|
* @param state Arduino digital HIGH or LOW values.
|
||||||
|
*/
|
||||||
|
inline void Update(uint8_t state) {
|
||||||
|
if (state == HIGH)
|
||||||
|
High(); // Rising
|
||||||
|
if (state == LOW)
|
||||||
|
Low(); // Falling
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the cv output HIGH to about 5v.
|
||||||
|
inline void High() { update(HIGH); }
|
||||||
|
|
||||||
|
// Sets the cv output LOW to 0v.
|
||||||
|
inline void Low() { update(LOW); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begin a Trigger period for this output.
|
||||||
|
*/
|
||||||
|
inline void Trigger() {
|
||||||
|
update(HIGH);
|
||||||
|
last_triggered_ = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a bool representing the on/off state of the output.
|
||||||
|
*/
|
||||||
|
inline void Process() {
|
||||||
|
// If trigger is HIGH and the trigger duration time has elapsed, set the
|
||||||
|
// output low.
|
||||||
|
if (on_ && (millis() - last_triggered_) >= trigger_duration_) {
|
||||||
|
update(LOW);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the trigger duration in miliseconds.
|
* Return a bool representing the on/off state of the output.
|
||||||
*
|
*
|
||||||
* @param duration_ms trigger duration in miliseconds
|
* @return true if current cv state is high, false if current cv state is low
|
||||||
*/
|
*/
|
||||||
void SetTriggerDuration(uint8_t duration_ms) {
|
inline bool On() { return on_; }
|
||||||
trigger_duration_ = duration_ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
private:
|
||||||
* Turn the CV and LED on or off according to the input state.
|
unsigned long last_triggered_;
|
||||||
*
|
uint8_t trigger_duration_;
|
||||||
* @param state Arduino digital HIGH or LOW values.
|
uint8_t cv_pin_;
|
||||||
*/
|
bool on_;
|
||||||
inline void Update(uint8_t state) {
|
|
||||||
if (state == HIGH) High(); // Rising
|
|
||||||
if (state == LOW) Low(); // Falling
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets the cv output HIGH to about 5v.
|
void update(uint8_t state) {
|
||||||
inline void High() { update(HIGH); }
|
digitalWrite(cv_pin_, state);
|
||||||
|
on_ = state == HIGH;
|
||||||
// Sets the cv output LOW to 0v.
|
}
|
||||||
inline void Low() { update(LOW); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Begin a Trigger period for this output.
|
|
||||||
*/
|
|
||||||
inline void Trigger() {
|
|
||||||
update(HIGH);
|
|
||||||
last_triggered_ = millis();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a bool representing the on/off state of the output.
|
|
||||||
*/
|
|
||||||
inline void Process() {
|
|
||||||
// If trigger is HIGH and the trigger duration time has elapsed, set the output low.
|
|
||||||
if (on_ && (millis() - last_triggered_) >= trigger_duration_) {
|
|
||||||
update(LOW);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a bool representing the on/off state of the output.
|
|
||||||
*
|
|
||||||
* @return true if current cv state is high, false if current cv state is low
|
|
||||||
*/
|
|
||||||
inline bool On() { return on_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
unsigned long last_triggered_;
|
|
||||||
uint8_t trigger_duration_;
|
|
||||||
uint8_t cv_pin_;
|
|
||||||
uint8_t led_pin_;
|
|
||||||
bool on_;
|
|
||||||
|
|
||||||
void update(uint8_t state) {
|
|
||||||
digitalWrite(cv_pin_, state);
|
|
||||||
on_ = state == HIGH;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user