feat: Add configurable pulse output feature with UI control and associated logic.

This commit is contained in:
2026-03-18 13:11:08 -07:00
parent e363c05823
commit fabc9b9b8d

View File

@ -37,7 +37,8 @@ enum GlobalParam {
PARAM_GLOBAL_BPM = 1,
PARAM_GLOBAL_CV1_DEST = 2,
PARAM_GLOBAL_CV2_DEST = 3,
PARAM_GLOBAL_LAST = 4
PARAM_GLOBAL_PULSE_OUT = 4,
PARAM_GLOBAL_LAST = 5
};
enum CvDest {
@ -56,6 +57,7 @@ GlobalParam current_global_param = PARAM_GLOBAL_CLK_SRC;
CvDest cv1_dest = CV_DEST_MAP_X;
CvDest cv2_dest = CV_DEST_CHAOS;
Clock::Source selected_source = Clock::SOURCE_INTERNAL;
Clock::Pulse selected_pulse = Clock::PULSE_PPQN_4;
// UI & Navigation
enum SelectedParam {
@ -163,6 +165,20 @@ void ProcessSequencerTick(uint32_t tick) {
}
}
// Expansion Pulse Out gate
if (selected_pulse != Clock::PULSE_NONE) {
int pulse_high_ticks = 96; // 1 PPQN
if (selected_pulse == Clock::PULSE_PPQN_4) pulse_high_ticks = 24;
else if (selected_pulse == Clock::PULSE_PPQN_24) pulse_high_ticks = 4;
int pulse_low_ticks = tick + max(pulse_high_ticks / 2, 1);
if (tick % pulse_high_ticks == 0) {
gravity.pulse.High();
} else if (pulse_low_ticks % pulse_high_ticks == 0) {
gravity.pulse.Low();
}
}
// Handle new 16th note step
if (tick % PULSES_PER_16TH == 0) {
@ -256,6 +272,7 @@ void HandleExtClockTick() {
for (int i = 0; i < 6; i++) {
gravity.outputs[i].Low();
}
gravity.pulse.Low();
gravity.clock.Reset();
current_step = 0;
break;
@ -270,12 +287,23 @@ void OnPlayPress() {
gravity.clock.Stop();
for (int i = 0; i < 6; i++)
gravity.outputs[i].Low();
gravity.pulse.Low();
} else {
gravity.clock.Start();
}
needs_redraw = true;
}
void OnShiftPress() {
for (int i = 0; i < 6; i++) {
gravity.outputs[i].Low();
}
gravity.pulse.Low();
gravity.clock.Reset();
current_step = 0;
needs_redraw = true;
}
void OnEncoderPress() {
editing_param = !editing_param;
needs_redraw = true;
@ -375,6 +403,15 @@ void OnEncoderRotate(int val) {
cv2_dest = (CvDest)dest;
break;
}
case PARAM_GLOBAL_PULSE_OUT: {
int pulse = (int)selected_pulse + val;
pulse = constrain(pulse, 0, Clock::PULSE_LAST - 1);
selected_pulse = (Clock::Pulse)pulse;
if (selected_pulse == Clock::PULSE_NONE) {
gravity.pulse.Low();
}
break;
}
default:
break;
}
@ -437,7 +474,8 @@ const char str_source[] PROGMEM = "SOURCE";
const char str_tempo[] PROGMEM = "TEMPO";
const char str_cv1dest[] PROGMEM = "CV1 DEST";
const char str_cv2dest[] PROGMEM = "CV2 DEST";
const char* const global_menu_items[] PROGMEM = {str_source, str_tempo, str_cv1dest, str_cv2dest};
const char str_pulse[] PROGMEM = "PULSE OUT";
const char* const global_menu_items[] PROGMEM = {str_source, str_tempo, str_cv1dest, str_cv2dest, str_pulse};
void drawMenuItems(const char* const menu_items[], int menu_size, int current_item) {
gravity.display.setFont(TEXT_FONT);
@ -588,6 +626,25 @@ void DisplayMainArea() {
strcpy_P(mainText, PSTR("CV2"));
strcpy(subText, GetCvDestName(cv2_dest));
break;
case PARAM_GLOBAL_PULSE_OUT:
switch (selected_pulse) {
case Clock::PULSE_NONE:
strcpy_P(mainText, PSTR("OFF"));
break;
case Clock::PULSE_PPQN_24:
strcpy_P(mainText, PSTR("24"));
break;
case Clock::PULSE_PPQN_4:
strcpy_P(mainText, PSTR("4"));
break;
case Clock::PULSE_PPQN_1:
strcpy_P(mainText, PSTR("1"));
break;
default:
break;
}
strcpy_P(subText, PSTR("PPQN"));
break;
default:
break;
}
@ -645,6 +702,7 @@ void setup() {
LoadState();
gravity.play_button.AttachPressHandler(OnPlayPress);
gravity.shift_button.AttachPressHandler(OnShiftPress);
gravity.encoder.AttachPressHandler(OnEncoderPress);
gravity.encoder.AttachRotateHandler(OnEncoderRotate);
gravity.encoder.AttachPressRotateHandler(OnEncoderPressRotate);