master_clock/src/Gate.cpp

388 lines
8.5 KiB
C++

// Gate.cpp
#include "Gate.h"
#include "Mod.h"
#include "Settings.h"
#include "globals.h"
#include "hardware/pwm.h"
#include <cstdlib>
#include <cstring>
#include <math.h>
Gate::Gate(uint8_t pin, uint8_t idx, uint8_t slotIdx1, uint8_t slotIdx2) : Output(pin, idx, slotIdx1, slotIdx2) {
this->pin = pin;
this->idx = idx;
this->slotIdx1 = slotIdx1;
this->slotIdx2 = slotIdx2;
state = 0;
editing = 0;
modifierSelectionIndex = 8;
divideMode = 0; // 1 divison | 0 multiplication
modifier = 0; // divide mode modifier (4x, /32, etc)
dur = 0; // how long pulse is on
width = 50; // pulse width
len = 0; // max len a pulse can be on, as determined by width
p = 100; // probability of a pulse
level = 100;
modDest1 = (idx + 1) % 8;
modDest2 = (idx + 2) % 8;
}
void Gate::pack(OutputConfig &cfg) {
cfg.type = TYPE_GATE;
GateSettings* s = (GateSettings*)cfg.data;
s->modifierSelectionIndex = this->modifierSelectionIndex;
s->divideMode = this->divideMode;
s->modifier = this->modifier;
s->width = this->width;
s->p = this->p;
s->level = this->level;
s->shape = (uint8_t)this->shape;
}
void Gate::unpack(const OutputConfig &cfg) {
if (cfg.type != TYPE_GATE) return;
GateSettings s;
memcpy(&s, cfg.data, sizeof(GateSettings));
this->modifierSelectionIndex = s.modifierSelectionIndex;
this->divideMode = s.divideMode;
this->modifier = s.modifier;
this->width = s.width;
this->p = s.p;
this->level = s.level;
this->shape = (WaveShape)s.shape;
setDiv(this->modifierSelectionIndex);
setWidth(this->width);
}
void Gate::setupPatches() {
matrix.patch(this->slotIdx1, this->modDest1, this->idx, DEST_LEVEL, 100, false);
matrix.patch(this->slotIdx2, this->modDest2, this->idx, DEST_LEVEL, 100, false);
}
void Gate::setLen(uint32_t currentPeriod) {
len = (uint32_t)((double)currentPeriod * (width / 100.0) / 1000.0);
}
void Gate::setDiv(uint8_t modifier_selecton_index) {
switch (modifier_selecton_index) {
case 0: // x32
tickInterval = 3;
isEnabled = true;
divideMode = 0;
modifier = 32;
break;
case 1: // x16
tickInterval = 6;
isEnabled = true;
divideMode = 0;
modifier = 16;
break;
case 2: // x12
tickInterval = 8;
isEnabled = true;
divideMode = 0;
modifier = 12;
break;
case 3: // x8
tickInterval = 12;
isEnabled = true;
divideMode = 0;
modifier = 8;
break;
case 4: // x6
tickInterval = 16;
isEnabled = true;
divideMode = 0;
modifier = 6;
break;
case 5: // x4
tickInterval = 24;
isEnabled = true;
divideMode = 0;
modifier = 4;
break;
case 6: // x3
tickInterval = 32;
isEnabled = true;
divideMode = 0;
modifier = 3;
break;
case 7: // x2
tickInterval = 48;
isEnabled = true;
divideMode = 0;
modifier = 2;
break;
case 8: // 0 (OFF)
tickInterval = 0;
isEnabled = false;
divideMode = 0;
modifier = 0;
break;
case 9: // /1
tickInterval = 96;
isEnabled = true;
divideMode = 1;
modifier = 1;
break;
case 10: // /2
tickInterval = 192;
isEnabled = true;
divideMode = 1;
modifier = 2;
break;
case 11: // /3
tickInterval = 288;
isEnabled = true;
divideMode = 1;
modifier = 3;
break;
case 12: // /4
tickInterval = 384;
isEnabled = true;
divideMode = 1;
modifier = 4;
break;
case 13: // /6
tickInterval = 576;
isEnabled = true;
divideMode = 1;
modifier = 4;
break;
case 14: // /8
tickInterval = 768;
isEnabled = true;
divideMode = 1;
modifier = 8;
break;
case 15: // /16
tickInterval = 1536;
isEnabled = true;
divideMode = 1;
modifier = 16;
break;
case 16: // /32
tickInterval = 3072;
isEnabled = true;
divideMode = 1;
modifier = 16;
break;
default:
tickInterval = 96;
isEnabled = true;
divideMode = 1;
}
this->triggerCount = 0;
this->lastTriggerTick = 0xFFFFFFFF;
setWidth(this->width);
// this is called in width, check if needed still?
calculatePulseWidth();
};
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;
double us_per_tick = 625000.0 / (double)BPM;
calculatePulseWidth();
this->pulseDurationUs =
(uint32_t)(us_per_tick * (double)this->pulseWidthTicks);
if (this->pulseDurationUs < 100)
this->pulseDurationUs = 100;
}
void Gate::calculatePulseWidth() {
if (tickInterval == 0 || !isEnabled) {
pulseWidthTicks = 0;
return;
}
this->pulseWidthTicks =
(uint32_t)((float)this->tickInterval * (this->width / 100.0f));
if (this->width > 0 && this->pulseWidthTicks == 0) {
this->pulseWidthTicks = 1;
}
}
void Gate::turnOn() {
if (!isEnabled || tickInterval == 0)
return;
if (MASTER_TICK % tickInterval == 0) {
if (MASTER_TICK != lastTriggerTick) {
lastTriggerTick = MASTER_TICK;
float baseP = (float)this->p;
float effectiveP = baseP + (this->pMod * 100.0f);
if (effectiveP > 100.0f) effectiveP = 100.0f;
if (effectiveP < 0.0f) effectiveP = 0.0f;
if ((rand() % 100) + 1 > (uint8_t)effectiveP) {
scheduledTick = 0xFFFFFFFF;
return;
}
// swing
triggerCount++;
uint32_t swingDelayTicks =
(uint32_t)((float)tickInterval * ((float)swing - 50.0f) / 100.0f);
if (triggerCount % 2 == 0) {
scheduledTick = MASTER_TICK + swingDelayTicks;
} else {
scheduledTick = MASTER_TICK;
}
}
}
if (MASTER_TICK >= scheduledTick && !state) {
state = 1;
startTick = MASTER_TICK;
startTimeUs = time_us_64();
scheduledTick = 0xFFFFFFFF;
currentRandomVal = (float)rand() / (float)RAND_MAX;
}
}
void Gate::update() {
if (!state && !sticky) {
lastOutVal = 0.0f;
return;
}
uint64_t now = time_us_64();
uint32_t elapsedUs = (uint32_t)(now - startTimeUs);
if (elapsedUs >= pulseDurationUs) {
state = 0;
if (width < 100) {
scheduledTick = 0xFFFFFFFF;
lastTriggerTick = 0xFFFFFFFF;
}
if (!sticky)
writeAnalog(0);
return;
}
float phase = (float)elapsedUs / (float)pulseDurationUs;
float outVal = 0;
switch (shape) {
case SINE:
outVal = (sinf(phase * 2.0f * 3.14159f) * 0.5f) + 0.5f;
break;
case HALFSINE: // AKA HUMP
outVal = sinf(phase * 3.14159f);
break;
case TRIANGLE:
outVal = (phase < 0.5f) ? (phase * 2.0f) : (2.0f - (phase * 2.0f));
break;
case SAW:
outVal = 1.0f - phase;
break;
case RAMP:
outVal = phase;
break;
case EXP:
outVal = expf(-5.0f * phase);
break;
case REXP:
outVal = expf(5.0f * (phase - 1.0f));
break;
case LOG:
outVal = 1.0f - expf(-5.0f * phase);
break;
case SQUARE:
outVal = 1.0f;
break;
case BOUNCE:
outVal = fabsf(sinf(phase * 3.14159f * 2.0f));
break;
case SIGMO:
outVal = phase * phase * (3.0f - 2.0f * phase);
break;
case WOBBLE:
outVal = expf(-3.0f * phase) * cosf(phase * 3.14159f * 4.0f);
if (outVal < 0)
outVal = 0;
break;
case STEPDW:
outVal = 1.0f - (floorf(phase * 4.0f) / 3.0f);
break;
case STEPUP:
outVal = floorf(phase * 4.0f) / 3.0f;
break;
case SH:
outVal = currentRandomVal;
break;
}
this->lastOutVal = outVal;
// handle width mod
float effectiveWidth = (float)width + (widthMod * 100.0f);
if (effectiveWidth > 100.0f) effectiveWidth = 100.0f;
if (effectiveWidth < 1.0f) effectiveWidth = 1.0f;
double us_per_tick = 625000.0 / (double)BPM;
uint32_t modulatedTicks = (uint32_t)((float)this->tickInterval * (effectiveWidth / 100.0f));
if (modulatedTicks < 1) modulatedTicks = 1;
this->pulseDurationUs = (uint32_t)(us_per_tick * (double)modulatedTicks);
float baseLevel = (float)this->level / 100.0f;
float normalizedMod = this->levelMod;
if (normalizedMod > 1.0f || normalizedMod < -1.0f) {
normalizedMod /= 100.0f;
}
float finalLevel = baseLevel + normalizedMod;
if (finalLevel > 1.0f) finalLevel = 1.0f;
if (finalLevel < 0.0f) finalLevel = 0.0f;
writeAnalog((uint16_t)(outVal * 1023.0f * finalLevel));}
void Gate::writeAnalog(uint16_t val) { pwm_set_gpio_level(pin, val); }
void Gate::turnOff() { writeAnalog(0); }