Code: Select all
phase += delta;
while (phase >= next || sync) {
overlap = phase - next;
fraction = (phase > next ? overlap / delta : 0.0);
// syncing, sync happened before next step
if (sync && sync_fraction > fraction) {
set_phase(sync_fraction, destination_phase);
} else {
set_phase(fraction, next < 1.0 ? next : 0.0);
}
}
Note that regardless of whether sync happens before the next step or after, you must always loop to handle both events or aliasing and error will result.
This is called "event handling with sub-sample precision."
If you want morphing conditions (variable pulse-width, morphing waveforms, ...) the code is a lot more complex in order to handle things as efficiently as possible. Still though you can generate any waveform with any set of conditions from one implementation. It isn't necessary to have multiple implementations one for each of pulse, ramp or other waveforms.
This is because ultimately any first-order changes in delta or other conditions (pulse-width modulation) are simply more of the same sort of events that you need to handle with sub-sample precision.