Misaligned triggers at specific sample rates

Hi all,

The following code examples produce the following plots on my computer (macOS 14.3, SC 3.13.0). I would expect plots 3 and 4 to be identical, but it looks like the triggers in the first two signals are misaligned. It happens with some number combinations but not others, at the audio rate and control rate. For me, this issue occurs when the server is running at 48000 samples/sec, but not at 44100. Wondering if anyone can reproduce or explain.

Eli

(
{ // audio rate
	[
		Impulse.ar(1200),
		Impulse.ar(800),
		Impulse.ar(1200) * Impulse.ar(800),
		Impulse.ar(400),
	]
}.plot(bounds: Rect(200, 50, 1300, 800))
)

(
{ // control rate
	[
		Impulse.kr(40),
		Impulse.kr(60),
		Impulse.kr(40) * Impulse.kr(60),
		Impulse.kr(20)
	]
}.plot(0.15, bounds: Rect(200, 50, 1300, 800))
)

I think this is due to single sample and rounding issues concerning the sample rate, because if we take whole integers of the sample rate (respectively block size), the effect is not observable.

(
{ // audio rate
	var foo = Impulse.ar(SampleRate.ir/4);
	var bar = Impulse.ar(SampleRate.ir/6);
	[
		foo,
		bar,
		foo * bar,
		Impulse.ar(SampleRate.ir/12),
	]
}.plot(bounds: Rect(200, 50, 1300, 800))
)

and

(
{ // control rate
	var foo = Impulse.kr(s.options.blockSize/4);
	var bar = Impulse.kr(s.options.blockSize/6);
	[
		foo,
		bar,
		foo * bar,
		Impulse.kr(s.options.blockSize/12),
	]
}.plot(1.0, bounds: Rect(200, 50, 1300, 800))
)

If we turn the trigger into a gate which is open for two samples, the code work as expected as well

(
{ // audio rate
	var foo = Impulse.ar(1200);
	var bar = Impulse.ar(800);
	[
		foo,
		bar,
		Trig.ar(foo, (SampleRate.ir/2).reciprocal) * Trig.ar(bar, (SampleRate.ir/2).reciprocal),
		Impulse.ar(400),
	]
}.plot(bounds: Rect(200, 50, 1300, 800))
)

However, 48000 is a multiple of 1200 and 800, so I am not quite sure where this problem comes from - maybe it is due to numerical instability of floats and some DSP code decides whether the scheduled sample is due by checking if the diff is >=0.0. If we get <0.0 due to numerical instability, a sample may be triggered too early or too late. Since Impulse relies on a phasor internally, this could be the cause of the problem here as summation of floats is prone to inaccuracy.

Hope this helps a bit, I am also not expert in these rounding issues :confused:

edit: if you look closely you can see that the spikes of Impulse.ar(1200) and Impulse.ar(800) are also not alinging in the plot.

Thanks, floating point instability definitely seems like a reasonable guess to me, and using Trig to extend each impulse by an extra sample is a fine workaround. Still though, surprising behavior…