diff --git a/include/Gate.h b/include/Gate.h index 7b3aba0..58814af 100644 --- a/include/Gate.h +++ b/include/Gate.h @@ -3,16 +3,14 @@ #define Gate_h #include -#include class Gate { private: bool state; - int16_t cycle; uint32_t dur; uint32_t len; - uint16_t div; + uint32_t lastTriggerTick = 0xFFFFFFFF; public: Gate(uint8_t pin); @@ -21,6 +19,8 @@ class Gate { int8_t modifierSelectionIndex; uint8_t divideMode; uint16_t modifier; + uint16_t tickInterval; + bool isEnabled; uint8_t width; uint8_t p; @@ -29,9 +29,6 @@ class Gate { void setLen(uint32_t currentPeriod); void setDiv(uint8_t modifier_selection_index); void setWidth(uint16_t newWidth); - void setP(uint16_t prob); - - bool getState(); }; #endif diff --git a/include/globals.h b/include/globals.h index a81d5ed..478c82d 100644 --- a/include/globals.h +++ b/include/globals.h @@ -6,6 +6,8 @@ #include #include + +// PINS static constexpr uint8_t OUT_1_PIN = 0; static constexpr uint8_t OUT_2_PIN = 2; static constexpr uint8_t OUT_3_PIN = 4; @@ -22,12 +24,12 @@ static constexpr uint8_t ENCODER_CLK_PIN = 20; static constexpr uint8_t ENCODER_DT_PIN = 21; static constexpr uint8_t ENCODER_SW_PIN = 22; +// TIME BASED extern volatile uint8_t BPM; - static constexpr uint32_t MINUTE_US = 60000000; static constexpr uint8_t PPQN = 96; +extern volatile uint32_t MASTER_TICK; -static uint32_t MASTER_TICK = 0; // Modifiers in UI order static std::array MODIFIERS = {8, 4, 2, 0, 1, 2, 3, 4, 8, 16}; @@ -36,11 +38,15 @@ static std::array MOD_TYPES = {0, 0, 0, 0, 1, 1, 1, 1, 1, 1}; // Modifier string static std::array MODIFIER_STRINGS = {"x8", "x4", "x2", "x0", "/1", "/2", "/3", "/4", "/8", "/16"}; + inline uint32_t millis() { return to_ms_since_boot(get_absolute_time()); } + inline uint32_t micros() { return to_us_since_boot(get_absolute_time()); } + + #endif // GLOBALS_H diff --git a/src/DisplayHandler.cpp b/src/DisplayHandler.cpp index b2d0926..64c22fa 100644 --- a/src/DisplayHandler.cpp +++ b/src/DisplayHandler.cpp @@ -10,8 +10,6 @@ #include "pico-ssd1306/shapeRenderer/ShapeRenderer.h" #include "pico-ssd1306/textRenderer/TextRenderer.h" -//TODO: -// the pulses are not even... they occur independently of eachother. pico_ssd1306::SSD1306* display = nullptr; extern void update_BPM(bool up); diff --git a/src/Gate.cpp b/src/Gate.cpp index 3dd0ec9..c69bbcf 100644 --- a/src/Gate.cpp +++ b/src/Gate.cpp @@ -1,20 +1,18 @@ // Gate.cpp -#include "pico/stdlib.h" #include "Gate.h" #include "globals.h" -#include #include + Gate::Gate(uint8_t pin) { this->pin = pin; state = 0; - modifierSelectionIndex = 3; editing = 0; + + modifierSelectionIndex = 3; divideMode = 0; // 1 divison | 0 multiplication modifier = 0; // divide mode modifier (4x, /32, etc) - div = 0; // cycles needed before a pulse based on divide mode and modifier - cycle = 0; // how many cycles have passed since last pulse dur = 0; // how long pulse is on width = 50; // pulse width @@ -23,73 +21,134 @@ Gate::Gate(uint8_t pin) { p = 100; // probability of a pulse } -bool Gate::getState() { - return state; -} void Gate::setLen(uint32_t currentPeriod) { len = (uint32_t)((double)currentPeriod * (width / 100.0) / 1000.0); } + void Gate::setDiv(uint8_t modifier_selecton_index) { - printf("HOW ABOUT HERE\n"); - - uint8_t modifier = MODIFIERS[modifier_selecton_index]; - uint8_t mod_type = MOD_TYPES[modifier_selecton_index]; - if (mod_type == 1) { - div = PPQN * modifier; - } else { - div = PPQN / modifier; - } - divideMode = mod_type; - this->modifier = modifier; - - if (this->modifier == 0) { - turnOff(); + switch(modifier_selecton_index) { + case 0: // x8 (32nd triplets) + tickInterval = 8; // 96 / 12 hits per beat + isEnabled = true; + divideMode = 0; + modifier = 8; + break; + case 1: // x4 (16th triplets) + tickInterval = 16; // 96 / 6 hits per beat + isEnabled = true; + divideMode = 0; + modifier = 4; + break; + case 2: // x2 (8th triplets) + tickInterval = 32; // 96 / 3 hits per beat + isEnabled = true; + divideMode = 0; + modifier = 2; + break; + case 3: // 0 (OFF) + tickInterval = 0; + isEnabled = false; + divideMode = 0; + modifier = 0; + break; + case 4: // /1 (Quarter Notes - The Pulse) + tickInterval = 96; // 96 / 1 hit per beat + isEnabled = true; + divideMode = 1; + modifier = 1; + break; + case 5: // /2 (8th Notes) + tickInterval = 192; // 96 * 2 (1 hit every 2 beats) + isEnabled = true; + divideMode = 1; + modifier = 2; + break; + case 6: // /3 (Dotted Quarter or specialized) + tickInterval = 288; // 96 * 3 (1 hit every 3 beats) + isEnabled = true; + divideMode = 1; + modifier = 3; + break; + case 7: // /4 (Whole Notes) + tickInterval = 384; // 96 * 4 (1 hit every 4 beats) + isEnabled = true; + divideMode = 1; + modifier = 4; + break; + case 8: // /8 (2 Bar phrasing) + tickInterval = 768; // 96 * 8 (1 hit every 8 beats) + isEnabled = true; + divideMode = 1; + modifier = 8; + break; + case 9: // /16 (4 Bar phrasing) + tickInterval = 1536; // 96 * 16 (1 hit every 16 beats) + isEnabled = true; + divideMode = 1; + modifier = 16; + break; + default: + tickInterval = 96; + isEnabled = true; + divideMode = 1; } + // TODO: check if this is actually needed setWidth(this->width); }; -void Gate::setWidth(uint16_t newWidth) { - width = newWidth; - if (divideMode == 1) { - len = (uint32_t)((double)(MINUTE_US / BPM) * (width / 100.0) / 1000.0); - } else { - len = (uint32_t)((double)(MINUTE_US / BPM / modifier) * (width / 100.0) / 1000.0); - } -}; -void Gate::setP(uint16_t prob) { - this->p = prob; +void Gate::setWidth(uint16_t newWidth) { + this->width = newWidth; + + double ms_per_tick = (60000.0 / (double)BPM) / 96.0; + + double ms_between_triggers = ms_per_tick * (double)this->tickInterval; + + this->len = (uint32_t)(ms_between_triggers * (this->width / 100.0)); + + uint32_t max_allowed_len = (ms_between_triggers > 5) ? (uint32_t)ms_between_triggers - 2 : 1; + + if (this->len > max_allowed_len) { + this->len = max_allowed_len; + } + + if (this->len < 1) this->len = 1; } + void Gate::turnOn() { - cycle += 1; - uint8_t pRes = 1; + if (!isEnabled || tickInterval == 0) return; - if (cycle == div) { - if (p < 100) { - uint32_t r = (rand() % 100) + 1; - if (r > p) { - pRes = 0; - } - } + if (MASTER_TICK % tickInterval == 0) { + if (MASTER_TICK != lastTriggerTick) { + lastTriggerTick = MASTER_TICK; - if (pRes == 1) { - state = 1; - gpio_put(pin, state); - dur = millis(); - } - cycle = 0; - }; + uint8_t pRes = 1; + if (p < 100) { + if ((rand() % 100) + 1 > p) pRes = 0; + } + + if (pRes == 1) { + state = 1; + gpio_put(pin, 1); + dur = millis(); + } + } + } + else { + lastTriggerTick = 0xFFFFFFFF; + } } + void Gate::turnOff() { - if (state == 1 && millis() - dur >= len) { - state = 0; - gpio_put(pin, state); - dur = 0; - }; + if (state == 1) { + if (millis() - dur >= len) { + state = 0; + gpio_put(pin, 0); + } + } } - diff --git a/src/main.cpp b/src/main.cpp index 6d0bc56..13a925c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,14 +11,16 @@ #include "DisplayHandler.h" #include "EncoderHandler.h" -struct repeating_timer bpm_timer = {0}; +// Time based operations +struct repeating_timer bpm_timer = {0}; volatile uint8_t BPM = 60; volatile uint8_t PLAY = 1; volatile uint32_t period_us = 0; +volatile uint32_t MASTER_TICK; -volatile bool beatToggle = false; +// Initialize Outputs Gate out1(OUT_1_PIN); Gate out2(OUT_2_PIN); Gate out3(OUT_3_PIN); @@ -30,13 +32,15 @@ Gate out8(OUT_8_PIN); static Gate* outputs[] = {&out1, &out2, &out3, &out4, &out5, &out6, &out7, &out8}; + +// Initialize Handlers static DisplayHandler display_handler(outputs); -static EncoderHandler encoder_handler(&display_handler); +static EncoderHandler encoder_handler(&display_handler); + bool timer_callback(struct repeating_timer *t) { if (PLAY == 1) { - beatToggle = true; MASTER_TICK += 1; } return true; @@ -86,58 +90,32 @@ void setup_outs() { } // manual setup - out1.setDiv(1); - // out1.setWidth(50); - // - // out2.setDiv(4, 0); - // out2.setWidth(50); - // - // out3.setDiv(2, 0); - // out3.setWidth(50); - // - // out4.setDiv(1); - // out4.setWidth(50); - // - // out5.setDiv(2); - // out5.setWidth(50); - // - // out6.setDiv(4); - // out6.setWidth(50); - // - // out7.setDiv(8); - // out7.setWidth(50); - // - // out8.setDiv(16); - // out8.setWidth(50); + // out1.setDiv(5); + // out1.setWidth(80); } void handle_outs() { - if (beatToggle) { - for (Gate* g: outputs) { - if (g->modifier != 0) { - g->turnOn(); - } - } - - beatToggle = false; - } - for (Gate* g: outputs) { - if (g->modifier != 0) { - g->turnOff(); - } - } + for (Gate* g: outputs) { + g->turnOn(); + g->turnOff(); + } } int main() { + // initialize stdio_init_all(); + + // Seed random srand(rosc_hw->randombit); + // Initialize display and multicore display_handler.setup(); multicore_launch_core1(core1_entry); multicore_fifo_push_blocking(1); + // Initialize Encoder encoder_handler.setup(); setup_outs();