diff --git a/examples/Gravity/Gravity.ino b/examples/Gravity/Gravity.ino index 58069c0..755d8ea 100644 --- a/examples/Gravity/Gravity.ino +++ b/examples/Gravity/Gravity.ino @@ -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); diff --git a/examples/Gravity/channel.h b/examples/Gravity/channel.h index d9a0028..55a87ac 100644 --- a/examples/Gravity/channel.h +++ b/examples/Gravity/channel.h @@ -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;