I’ve just encountered a strange case. I thought it might be better to discuss it here before logging a bug.
I haven’t been able to reduce it down any further than this. The problem occurs with:
- Multiple SinOscs;
- Through independent shapers with a specific transfer function;
- Where two out of every four channels are delayed by a small amount;
- And tanh distorted;
- Then one of the non-delayed channels is RMS’ed.
The RMS sometimes reports as a small negative number. This should be impossible as all of the input values are real, and there is no real number whose square is < 0 – so the sum of the squares should be guaranteed positive. But it isn’t.
-> localhost
distAmp: -0.00290428
-> localhost
distAmp: -0.00285521
Unfortunately, if I remove any of the above elements, then the issue is no longer reproduced. 1 or 2 SinOscs, no problem; 3 or 4, problem. I couldn’t reproduce the issue with a simple linear transfer function. I couldn’t reproduce the issue with fewer than two delays (if numOscs = 2, there’s one DelayC, but I didn’t see the problem in that case – even though none of the delayed channels is going into the running sum!).
It’s moderately sporadic. With numOscs = 3, it posts a negative number about half the time, or more. With numOscs = 4, it occurs almost every time.
RunningSum’s source does something fancy to try to counteract floating-point rounding error. I’m guessing that I accidentally discovered a signal where the algorithm doesn’t work. But I just don’t see it.
hjh
(
s.waitForBoot {
var out = Array.new,
theta = 0.0,
n = 512,
baseRate = 2pi / n,
endRate = 12;
SynthDef(\distBass, { |out, freq = 440, gate = 1, t_gate = 1, freqlag = 0.1, freqRand = 1.008, amp = 0.1,
preamp = 0.1, bufnum = 0, preTanhAmp = 20,
atk = 0.01, dcy = 0.1, sus = 0.6, rel = 0.1,
delay = 0.012, distMix = 0.5, hpfreq = 260|
// HERE: numOscs < 3 does not reproduce the issue
var numOscs = 3;
var lowFRand = freqRand.reciprocal,
fRand = Array.fill(numOscs, { 1 /* TExpRand.kr(lowFRand, freqRand, t_gate) */ }),
sig = SinOsc.ar(Lag.kr(freq * fRand, freqlag)),
eg = EnvGen.kr(Env.adsr(atk, dcy, sus, rel), gate, doneAction: 2),
distAmp,
distorted = Shaper.ar(bufnum, sig * preamp);
// decorrelate
distorted.do { |chan, i|
if(#[1, 2].includes(i % 4)) {
distorted[i] = DelayC.ar(chan, 0.1, SinOsc.kr(Rand(0.1, 0.2), Rand(0, pi), delay * 0.5, delay));
};
};
distorted = Mix((distorted * (preamp * preTanhAmp)).tanh.clump(2));
distAmp = RunningSum.ar(distorted[0].squared, 441).sqrt;
Poll.ar(Trig1.ar(distAmp < 0, 1), distAmp, "distAmp");
}).add;
if(~shapebuf.isNil) {
while { theta < 6pi } {
out = out.add(sin(theta));
theta = theta + (baseRate * theta.linexp(0, 6pi, 1, endRate));
};
out = (out.drop(1).reverse).neg ++ out;
out = out.resamp1(2048).as(Signal);
~shapebuf = Buffer.sendCollection(s, out.asWavetable);
};
s.sync;
(instrument: \distBass, bufnum: ~shapebuf, midinote: 24,
ffreq: 261.4, fDecay: 0.17, fegAmt: 5, preamp: 0.26, rqDecay: 1,
out: 1000, sustain: 1
).play;
};
)