Fix oscillator/synth phase wraparound drift
Replace conditional-subtraction phase wrapping with a single stable wrap:
- OscillatorNode: `(phase + freq_mod/sr).rem_euclid(1.0)` — also wraps correctly
when FM drives freq_mod negative (the old `if >= 1.0` wouldn't).
- SynthVoice: `(phase + frequency/sr).fract()`.
Repeated `if phase >= 1.0 { phase -= 1.0 }` accumulates f32 rounding error over
long-held notes, drifting the timbre.
This commit is contained in:
parent
4f58edf436
commit
77eb2c2d33
|
|
@ -164,11 +164,11 @@ impl AudioNode for OscillatorNode {
|
||||||
output[frame * 2] = sample; // Left
|
output[frame * 2] = sample; // Left
|
||||||
output[frame * 2 + 1] = sample; // Right
|
output[frame * 2 + 1] = sample; // Right
|
||||||
|
|
||||||
// Update phase once per frame
|
// Update phase once per frame. `rem_euclid(1.0)` gives a numerically stable
|
||||||
self.phase += freq_mod / sample_rate_f32;
|
// wraparound into [0, 1): repeated conditional subtraction accumulates f32
|
||||||
if self.phase >= 1.0 {
|
// rounding error over long-held notes (timbre drift), and FM can drive
|
||||||
self.phase -= 1.0;
|
// `freq_mod` negative, which a one-sided `if >= 1.0` wouldn't wrap at all.
|
||||||
}
|
self.phase = (self.phase + freq_mod / sample_rate_f32).rem_euclid(1.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,11 +113,10 @@ impl SynthVoice {
|
||||||
// Simple sine wave
|
// Simple sine wave
|
||||||
let sample = (self.phase * 2.0 * PI).sin() * (self.velocity as f32 / 127.0) * 0.3;
|
let sample = (self.phase * 2.0 * PI).sin() * (self.velocity as f32 / 127.0) * 0.3;
|
||||||
|
|
||||||
// Update phase
|
// Update phase. Use `.fract()` for a numerically stable wraparound: repeated
|
||||||
self.phase += self.frequency / sample_rate;
|
// conditional subtraction accumulates f32 rounding error over long-held notes,
|
||||||
if self.phase >= 1.0 {
|
// which drifts the timbre.
|
||||||
self.phase -= 1.0;
|
self.phase = (self.phase + self.frequency / sample_rate).fract();
|
||||||
}
|
|
||||||
|
|
||||||
self.age += 1;
|
self.age += 1;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue