update comments and minor fixes.

This commit is contained in:
2025-07-03 07:57:18 -07:00
parent 7ce8bb661d
commit db50132c28
2 changed files with 32 additions and 14 deletions

View File

@ -1,15 +1,33 @@
/**
* @file Gravity.ino
* @author Adam Wonak (https://github.com/awonak/)
* @brief Demo firmware for Sitka Instruments Gravity.
* @brief Alt firmware version of Gravity by Sitka Instruments.
* @version 0.1
* @date 2025-05-04
*
* @copyright Copyright (c) 2025
*
* This version of Gravity firmware is a full rewrite that leverages the
* libGravity hardware abstraction library. The goal of this project was to
* create an open source friendly version of the firmware that makes it easy
* for users/developers to modify and create their own original alt firmware
* implementations.
*
* The libGravity library represents wrappers around the
* hardware peripherials to make it easy to interact with and add behavior
* to them. The library tries not to make any assumptions about what the
* firmware can or should do.
*
* The Gravity firmware is a slightly different implementation of the original
* firmware. There are a few notable changes; the internal clock operates at
* 96 PPQN instead of the original 24 PPQN, which allows for more granular
* quantization of features like duty cycle (pulse width) or offset.
* Additionally, this firmware replaces the sequencer with a Euclidean Rhythm
* generator.
*
* ENCODER:
* Press to change between selecting a parameter and editing the parameter.
* Hold & Rotate to change current output channel pattern.
* Press: change between selecting a parameter and editing the parameter.
* Hold & Rotate: change current selected output channel.
*
* BTN1: Play/pause the internal clock.
*
@ -37,7 +55,7 @@ void setup() {
// Initialize the state manager. This will load settings from EEPROM
stateManager.initialize(app);
InitAppState(app);
InitGravity(app);
// Clock handlers.
gravity.clock.AttachIntHandler(HandleIntClockTick);
@ -107,8 +125,8 @@ void HandleIntClockTick(uint32_t tick) {
break;
}
const uint16_t pulse_high_ticks = clock_mod_pulses[clock_index];
const uint32_t pulse_low_ticks = tick + max((long)(pulse_high_ticks / 2), 1L);
const uint32_t pulse_high_ticks = CLOCK_MOD_PULSES[clock_index];
const uint32_t pulse_low_ticks = tick + max((pulse_high_ticks / 2), 1L);
if (tick % pulse_high_ticks == 0) {
gravity.pulse.High();
@ -162,7 +180,7 @@ void HandleEncoderPressed() {
if (app.selected_param == PARAM_MAIN_RESET_STATE) {
if (app.selected_sub_param == 0) { // Reset
stateManager.reset(app);
InitAppState(app);
InitGravity(app);
}
}
}
@ -282,7 +300,7 @@ void updateSelection(byte& param, int change, int maxValue) {
// App Helper functions.
//
void InitAppState(AppState& app) {
void InitGravity(AppState& app) {
gravity.clock.SetTempo(app.tempo);
gravity.clock.SetSource(app.selected_source);
gravity.encoder.SetReverseDirection(app.encoder_reversed);

View File

@ -19,11 +19,11 @@ enum CvDestination : uint8_t {
CV_DEST_LAST,
};
static const int MOD_CHOICE_SIZE = 21;
static const byte MOD_CHOICE_SIZE = 21;
// Negative for multiply, positive for divide.
static const int clock_mod[MOD_CHOICE_SIZE] PROGMEM = {-24, -12, -8, -6, -4, -3, -2, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 24, 32, 64, 128};
static const int CLOCK_MOD[MOD_CHOICE_SIZE] PROGMEM = {-24, -12, -8, -6, -4, -3, -2, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 24, 32, 64, 128};
// This represents the number of clock pulses for a 96 PPQN clock source that match the above div/mult mods.
static const int clock_mod_pulses[MOD_CHOICE_SIZE] PROGMEM = {4, 8, 12, 16, 24, 32, 48, 96, 192, 288, 384, 480, 576, 1152, 672, 768, 1536, 2304, 3072, 6144, 12288};
static const int CLOCK_MOD_PULSES[MOD_CHOICE_SIZE] PROGMEM = {4, 8, 12, 16, 24, 32, 48, 96, 192, 288, 384, 480, 576, 1152, 672, 768, 1536, 2304, 3072, 6144, 12288};
class Channel {
public:
@ -111,7 +111,7 @@ class Channel {
int getDutyCycle(bool withCvMod = false) const { return withCvMod ? cvmod_duty_cycle : base_duty_cycle; }
int getOffset(bool withCvMod = false) const { return withCvMod ? cvmod_offset : base_offset; }
int getSwing(bool withCvMod = false) const { return withCvMod ? cvmod_swing : base_swing; }
int getClockMod(bool withCvMod = false) const { return pgm_read_word_near(&clock_mod[getClockModIndex(withCvMod)]); }
int getClockMod(bool withCvMod = false) const { return pgm_read_word_near(&CLOCK_MOD[getClockModIndex(withCvMod)]); }
int getClockModIndex(bool withCvMod = false) const { return withCvMod ? cvmod_clock_mod_index : base_clock_mod_index; }
bool isCvModActive() const { return cv1_dest != CV_DEST_NONE || cv2_dest != CV_DEST_NONE; }
@ -126,7 +126,7 @@ class Channel {
*/
void processClockTick(uint32_t tick, DigitalOutput& output) {
// Calculate output duty cycle state using cv modded values to determine pulse counts.
const uint16_t mod_pulses = pgm_read_word_near(&clock_mod_pulses[cvmod_clock_mod_index]);
const uint16_t mod_pulses = pgm_read_word_near(&CLOCK_MOD_PULSES[cvmod_clock_mod_index]);
const uint16_t duty_pulses = max((long)((mod_pulses * (100L - cvmod_duty_cycle)) / 100L), 1L);
const uint16_t offset_pulses = (long)((mod_pulses * (100L - cvmod_offset)) / 100L);
@ -144,7 +144,7 @@ class Channel {
// Step check
if (current_tick_offset % mod_pulses == 0) {
bool hit = cvmod_probability >= random(0, 100);
// Euclidean rhythm check
// Euclidean rhythm hit check
switch (pattern.NextStep()) {
case Pattern::REST: // Rest when active or fall back to probability
hit = false;