fixing width

This commit is contained in:
Dominic DiTaranto 2026-03-16 22:40:01 -04:00
parent 787f084b74
commit ed79c8e3b9

View file

@ -294,59 +294,58 @@ void Gate::update() {
// 1. EXIT EARLY IF OFF // 1. EXIT EARLY IF OFF
if (!state && !sticky) { if (!state && !sticky) {
lastOutVal = 0.0f; lastOutVal = 0.0f;
// Make sure we actually write 0 if we aren't sticky
writeAnalog(0); writeAnalog(0);
return; return;
} }
// 2. LIVE WIDTH MODULATION // 2. LIVE WIDTH MODULATION
// We calculate the 'stopTick' every frame.
// IMPORTANT: Cap at 98% to ensure the gate has a "low" period before next beat.
float effectiveWidth = (float)width + (widthMod * 100.0f); float effectiveWidth = (float)width + (widthMod * 100.0f);
if (effectiveWidth > 98.0f) effectiveWidth = 98.0f; if (effectiveWidth > 100.0f) effectiveWidth = 100.0f;
if (effectiveWidth < 1.0f) effectiveWidth = 1.0f; if (effectiveWidth < 0.0f) effectiveWidth = 0.0f;
uint32_t modulatedTicks = (uint32_t)((float)this->tickInterval * (effectiveWidth / 100.0f)); uint32_t modulatedTicks = (uint32_t)((float)this->tickInterval * (effectiveWidth / 100.0f));
if (modulatedTicks < 1) modulatedTicks = 1;
// This is our "Target" end point
this->stopTick = startTick + modulatedTicks; this->stopTick = startTick + modulatedTicks;
// 3. THE HARD SYNC (THE FIX) // 3. THE HARD SYNC
// If the Master Tick reached stopTick, kill the gate. // Only kill the gate if width is strictly less than 100%
if (MASTER_TICK >= stopTick) { if (effectiveWidth < 100.0f) {
state = 0; if (MASTER_TICK >= stopTick) {
// Don't reset lastTriggerTick here, otherwise turnOn() might re-fire state = 0;
// on the same tick that we just finished. if (!sticky) {
if (!sticky) { lastOutVal = 0.0f;
lastOutVal = 0.0f; writeAnalog(0);
writeAnalog(0); }
return;
} }
return;
} }
// 4. HYBRID SMOOTHNESS MATH // 4. HYBRID SMOOTHNESS MATH
uint64_t now = time_us_64(); uint64_t now = time_us_64();
// Ensure we don't underflow if now < last_clk_us (jitter)
uint64_t usSinceLastTick = (now > last_clk_us) ? (now - last_clk_us) : 0; uint64_t usSinceLastTick = (now > last_clk_us) ? (now - last_clk_us) : 0;
double current_BPM_for_math = (double)filteredBPM; double current_BPM_for_math = (double)filteredBPM;
if (current_BPM_for_math < 1.0) current_BPM_for_math = 1.0; if (current_BPM_for_math < 1.0) current_BPM_for_math = 1.0;
double us_per_tick = 60000000.0 / (current_BPM_for_math * (double)PPQN); double us_per_tick = 60000000.0 / (current_BPM_for_math * (double)PPQN);
float subTick = (float)usSinceLastTick / (float)us_per_tick; float subTick = (float)usSinceLastTick / (float)us_per_tick;
if (subTick > 0.98f) subTick = 0.98f; if (subTick > 0.99f) subTick = 0.99f;
// Calculate phase (0.0 to 1.0) // --- THE SINE WAVE FIX ---
// If width is 100%, we calculate phase based on the WHOLE interval.
// If width < 100%, we calculate phase based on the PULSE duration.
float elapsedTicks = (float)(MASTER_TICK - startTick) + subTick; float elapsedTicks = (float)(MASTER_TICK - startTick) + subTick;
float totalDurationTicks = (float)(stopTick - startTick); float totalDurationTicks = (effectiveWidth >= 100.0f) ? (float)tickInterval : (float)(stopTick - startTick);
// Safety check for division by zero
if (totalDurationTicks < 1.0f) totalDurationTicks = 1.0f; if (totalDurationTicks < 1.0f) totalDurationTicks = 1.0f;
float phase = elapsedTicks / totalDurationTicks; float phase = elapsedTicks / totalDurationTicks;
if (phase > 1.0f) phase = 1.0f;
// Keep phase looping if we are at 100% width (so LFOs/Sines keep moving)
if (effectiveWidth >= 100.0f) {
while (phase >= 1.0f) phase -= 1.0f;
} else {
if (phase > 1.0f) phase = 1.0f;
}
if (phase < 0.0f) phase = 0.0f; if (phase < 0.0f) phase = 0.0f;
// 5. WAVEFORM GENERATION // 5. WAVEFORM GENERATION
@ -360,7 +359,7 @@ void Gate::update() {
case EXP: outVal = expf(-5.0f * phase); break; case EXP: outVal = expf(-5.0f * phase); break;
case REXP: outVal = expf(5.0f * (phase - 1.0f)); break; case REXP: outVal = expf(5.0f * (phase - 1.0f)); break;
case LOG: outVal = 1.0f - expf(-5.0f * phase); break; case LOG: outVal = 1.0f - expf(-5.0f * phase); break;
case SQUARE: outVal = 1.0f; break; // Square is simple ON case SQUARE: outVal = 1.0f; break;
case BOUNCE: outVal = fabsf(sinf(phase * 3.14159265f * 2.0f)); break; case BOUNCE: outVal = fabsf(sinf(phase * 3.14159265f * 2.0f)); break;
case SIGMO: outVal = phase * phase * (3.0f - 2.0f * phase); break; case SIGMO: outVal = phase * phase * (3.0f - 2.0f * phase); break;
case WOBBLE: outVal = expf(-3.0f * phase) * cosf(phase * 3.14159265f * 4.0f); case WOBBLE: outVal = expf(-3.0f * phase) * cosf(phase * 3.14159265f * 4.0f);
@ -378,7 +377,6 @@ void Gate::update() {
if (finalLevel > 1.0f) finalLevel = 1.0f; if (finalLevel > 1.0f) finalLevel = 1.0f;
if (finalLevel < 0.0f) finalLevel = 0.0f; if (finalLevel < 0.0f) finalLevel = 0.0f;
// Use (uint16_t) cast to ensure the PWM driver gets a clean integer
writeAnalog((uint16_t)(outVal * 1023.0f * finalLevel)); writeAnalog((uint16_t)(outVal * 1023.0f * finalLevel));
} }