update-encoder-logic #2
5 changed files with 143 additions and 105 deletions
|
|
@ -3,16 +3,14 @@
|
|||
#define Gate_h
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
#include <array>
|
||||
#include <string>
|
||||
|
||||
|
||||
// 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<uint8_t, 10> MODIFIERS = {8, 4, 2, 0, 1, 2, 3, 4, 8, 16};
|
||||
|
|
@ -36,11 +38,15 @@ static std::array<uint8_t, 10> MOD_TYPES = {0, 0, 0, 0, 1, 1, 1, 1, 1, 1};
|
|||
// Modifier string
|
||||
static std::array<std::string, 10> 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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
167
src/Gate.cpp
167
src/Gate.cpp
|
|
@ -1,20 +1,18 @@
|
|||
// Gate.cpp
|
||||
#include "pico/stdlib.h"
|
||||
#include "Gate.h"
|
||||
#include "globals.h"
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
58
src/main.cpp
58
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);
|
||||
|
||||
|
||||
|
||||
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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue