There are a couple of intertwined issues here, which are hard to separate.
NaN in this case is coming from Sweep outputting 0 as its pre-sample, and then reciprocal
produces NaN (unlike 1/0
= inf).
The comments about safe initialization of filters are useful, but I think perhaps a bit tangential.
Now… why is Sweep’s pre-sample 0, when its first real output value is nonzero?
Checking the source code, I think it’s just a bit of sloppiness (which is perhaps inevitable, since JMc was just one person writing code for hundreds of UGens). Sweep’s documentation says “Starts a linear raise by rate/sec from zero” – I overlooked this at first. But the calculation functions increment first before outputting – so the zero will get swallowed. Pre-incrementing already makes the actual behavior deviate from the documented intention.
But then, in the constructor,
ZOUT0(0) = unit->mLevel = 0.f;
Huh…? But we already know from the next
functions that the output will be pre-incremented. So setting ZOUT(0) to 0 is… baffling? A mistake? Or should the next
functions instead read like LOOP1(inNumSamples, ZXP(out) = level; level += rate;);
? So that the initial 0 will be output as a normal sample.
Because if Sweep’s behavior were consistent with its documentation, then I would have known to write (Sweep.ar(0, SampleRate.ir) + 1).reciprocal
, and done, no problem.
Unfortunately, I suspect there are a lot of such cases that we haven’t even found yet. As far as I can see, there was never a clear decision about the meaning of the Ctor pre-sample. Some UGens (like Sweep) initialize to y[-1] and then output y[0] in the first cycle; other UGens initialize to y[0]… which basically reads like, coding shortcuts were taken under time pressure.
So a short summary, I guess, would be:
- FOS is fine.
- Sweep should not mislead users into thinking that the output will always be > 0.
- UGen initialization is a bit sloppy in places.
hjh