Where does the loud ramp come from (BPF)

I set the lower bound of the range for the cutoff freq of the BPF to 300 (way remote to be near 0, which is dangerous). However I experienced a very loud ramp accidentally in this code. Can any one spot where this could come from?

WARNING: this code might generate a VERY LOUD ramp in the BPF!

(
{
	var cutoff = LFNoise2.kr(1);
	var saw = Saw.ar(
		freq: 10,
		mul: LFPulse.kr(
			LFNoise1.kr(1).range(1, 10),
			width: 0.1
		)
	);
	var bpf = BPF.ar(saw, cutoff.range(300, 10000).poll, rq:0.01, mul:10);
	Pan2.ar(bpf, SinOsc.kr(1));
}.play
)

At first glance, rq:0.01, mul:10 is the likely culprit, but I’m not able to reproduce the issue you’re describing.

I usually check for these kind of issues by adding Peak.ar().poll at certain points in the code until I find where the issue is:

(
{
	var cutoff = LFNoise2.kr(1);
	var saw = Saw.ar(
		freq: 10,
		mul: LFPulse.kr(
			LFNoise1.kr(1).range(1, 10),
			width: 0.1
		)
	);
	var bpf = BPF.ar(saw, cutoff.range(300, 10000), rq:0.01, mul:10);
	// Peak.ar(bpf).poll;
	Peak.ar(Pan2.ar(bpf, SinOsc.kr(1))).poll;
	Silent.ar(1);
}.play
)

In this case, all the values I got were consistently under 0.2. My next guess is that LFNoise2 output a value outside of what .range expects as an input.

You could just make the last line Pan2.ar(bpf, SinOsc.kr(1)).tanh; or (...).clip(-1,1); but that just prevents the issue by distorting the signal. It doesn’t tell you where it’s coming from.

Instead of using LFNoise2, you could use LFNoise1 and then cutoff.exprange(300, 10000) but I’m not even sure that’s the issue. It’s just the only other explanation I can come up with.

1 Like

Very nice trick for monitoring peak values. When you say you put a Peak at certain points, should you always care that the output of Peak doesn’t bother the data your Synth actually needed at that point? How is it that in your code the Pan2.ar can be replaced without problem with a Peak.ar?

Your question is related to order of execution.

This basically says that you can add a line that monitors a variable at a certain point in a function before you reassign its value to check what it might be at that point.

For example:

(
{
var sine = SinOsc.ar(440);
Peak.ar(sine).poll();
sine;
}.play;
)

Functions always output the last item that’s executed. In this case, it’s whatever is assigned to the local variable sine. Peak.ar(sine) doesn’t affect the internal state of the variable sine, it just reads values from it.

On the other hand, if you run this, you’ll see that it just outputs a value that ramps up very quickly to 1. It’s so fast you won’t actually be able to see it ramp up but you will be able to tell from the first poll value:

(
{
var sine = SinOsc.ar(440);
sine = Peak.ar(sine).poll(); //reassign the value of variable sine to Peak.ar(sine)
sine;
}.scope(1, s.options.numOutputBusChannels+1); //you'll see it but it won't whack your speakers with DC offset
)

That means you can do something like this:

(
{
	var sig = SinOsc.ar(440) * 1000;
	Peak.ar(sig).poll(label:"before");
	sig = sig.tanh;
	Peak.ar(sig).poll(label:"after");
	sig;
}.play
)

That’ll give you the peak value of the very loud signal that sig is initialized to:

var sig = SinOsc.ar(440) * 1000;
Peak.ar(sig).poll(label:"before");

and the value of sig after reassignment:

sig = sig.tanh;
Peak.ar(sig).poll(label:"after");

but the output value of the function is soft clipped to ±1 because of the reassignment.

1 Like

If I’m not mistaken, the first work on the “sc-140” CD (sc-140 | SuperCollider) leverages this kind of “loud ramp” in BPF to achieve percussive effects. It was a mystery to me how to reproduce it reliably:

{LocalOut.ar(a=CombN.ar(BPF.ar(LocalIn.ar(2)*7.5+Saw.ar([32,33],0.2),2**LFNoise0.kr(4/3,4)*300,0.1).distort,2,2,40));a}.play