Apologies if that was an irritating thing to say… I had just noticed that the question sat around for a few days without any response and I was thinking about why.

Long post ahead…

I think so. … except: BBandStop will always fully suppress the center frequency, but you want a gain factor. MidEQ may be what you want instead.

Frequencies will definitely be exponentially distributed in that case (good).

I like to test components in isolation. If you’re not certain that the amplitudes are correct, you could take the function out by itself, look at the numbers, try some plots.

Now, when I look more closely at your inputs to the functions, I see a couple of odd things.

```
cycleFreq = floor(SampleRate.ir, cycleRate);
freqIndex = mod(notchNum, cycleFreq);
```

`floor`

is a unary op – there is no second argument. So cycleFreq will only be the sample rate. (Maybe you meant `trunc`

but the purpose of this is still not clear to me.) Then notchNum will always be far, far below even the trunc result. Unless you have tens of thousands of frequencies, this `mod`

it isn’t going to do anything.

So drop those. Just pass integer indices into the functions.

`var notches = (1..notchNums);`

and `notches.do { |notch, notchNum| { ... }`

– in the loop, you will get 1 and 0, then 2 and 1, then 3 and 2, etc… so you don’t need the array at all. You could just do `notchNums.do`

and add 1 where needed… but then, it’s often better to do calculations based on 0 rather than 1. So `notch-1`

and `freqIndex-1`

are just working around the “start with 1” anyway… simplify further, get rid of those. Then “notch” and “freqIndex” are the same, so, collapse to one.

Then, after eliminating the spurious distinction between `notch`

and `freqIndex`

, the `exp`

in the frequency function becomes…

```
exp = (cycleFreq * notch + notch) / cycleFreq;
= ((cycleFreq + 1) * notch) / cycleFreq
= notch * ((cycleFreq + 1) / cycleFreq)
= notch * (1 + (1 / cycleFreq))
```

`cycleFreq`

is very high, so the fraction will be very small. This means, practically, the exponent is almost exactly equal just to `notch`

– so, there’s really not much point. So I would get rid of that too.

Having simplified, then:

```
~getCenterFreqs = { |notch, f0|
f0 * (2 ** notch);
};
```

But now you don’t have any control over the width of the band. That is, the function parameters don’t satisfy the need (and it’s harder to recognize this fact, because of extra parameters being passed in, and a lot of noise in the formulas, which reduces out once you look at it closely).

So, OK, trying a new approach:

```
(
~getCenterFreqs = { |notch, notchNums, f0, fn|
notch.linexp(0, notchNums-1, f0, fn)
};
)
n = (0..9);
o = ~getCenterFreqs.(n, 10, 20, 12000);
o.doAdjacentPairs { |a, b| (b/a).postln }; "" // all the same, good
```

The amps can be simplified similarly:

```
(
~getCenterAmps = { |notch, notchNums, mindB, maxdB|
var numerator, theta, loudness, loudnessCurve, centerGain;
theta = 2 * pi * notch / notchNums;
loudness = ( maxdB - mindB ) * ( 1 - cos(theta) ) / 2;
loudnessCurve = mindB + loudness;
centerGain = 10 ** (loudnessCurve / 20);
centerGain;
};
)
p = ~getCenterAmps.(n, 10, -3, -20);
p.plot;
```

This looks a bit asymmetrical, but, the barber pole will involve adding a fractional offset, so I think it will be OK.

Then…

```
(
// different version: MidEQ takes db, not amp! So return dB
~getCenterAmps = { |notch, notchNums, mindB, maxdB|
var numerator, theta, loudness;
theta = 2 * pi * notch / notchNums;
loudness = ( maxdB - mindB ) * ( 1 - cos(theta) ) / 2;
mindB + loudness;
};
a = SynthDef(\barberpole, {
var cycleRate = 0.1;
var mindB = -3;
var maxdB = -20;
var f0 = 20, fn = 12000;
var notchNums = 10;
var sig;
var sr = SampleRate.ir;
var rq = MouseX.kr(1, 80, 0).reciprocal;
var freqs = (0..notchNums - 1).linexp(0, notchNums-1, f0, fn);
//sig = Saw.ar(100);
sig = PinkNoise.ar(1);
freqs.do { |freq, i|
var amp = ~getCenterAmps.(i, notchNums, mindB, maxdB);
sig = MidEQ.ar(sig, freq, rq, amp);
};
sig = Pan2.ar(sig, \pan.kr(0), \amp.kr(0.25));
Out.ar(\out.kr(0), sig);
}).play;
)
```

The barberpole should be an LFO. I’d test with sines first (because it’s hard to hear a frequency that has been notched out). Actually there’s a mistake in the amp handling here, so this probably doesn’t sound like a real Shepard tone.

```
(
a = SynthDef(\barberpole, {
var cycleRate = 0.1;
var mindB = -3;
var maxdB = -20;
var f0 = 20, fn = 12000;
var notchNums = 10;
var sig;
var sr = SampleRate.ir;
var rq = MouseX.kr(1, 80, 0).reciprocal;
var cycle = LFSaw.ar(cycleRate, mul: 0.5, add: 0.5);
var freqs = (cycle + (0..notchNums - 1)).linexp(0, notchNums, f0, fn);
//sig = Saw.ar(100);
// sig = PinkNoise.ar(1);
sig = 0;
freqs.do { |freq, i|
var amp = ~getCenterAmps.(i + cycle, notchNums, mindB, maxdB);
sig = sig + SinOsc.ar(freq, 0, amp.ampdb)
};
sig = Pan2.ar(sig, \pan.kr(0), \amp.kr(0.25));
Out.ar(\out.kr(0), sig);
}).play;
)
```

Then put the notches back in:

```
(
a = SynthDef(\barberpole, {
var cycleRate = 0.1;
var mindB = -3;
var maxdB = -20;
var f0 = 20, fn = 12000;
var notchNums = 10;
var sig;
var sr = SampleRate.ir;
var rq = MouseX.kr(1, 80, 0).reciprocal;
var cycle = LFSaw.ar(cycleRate, mul: 0.5, add: 0.5);
var freqs = (cycle + (0..notchNums - 1)).linexp(0, notchNums, f0, fn);
sig = PinkNoise.ar(1);
freqs.do { |freq, i|
var amp = ~getCenterAmps.(i + cycle, notchNums, mindB, maxdB);
sig = MidEQ.ar(sig, freq, rq, amp);
};
sig = Pan2.ar(sig, \pan.kr(0), \amp.kr(0.25));
Out.ar(\out.kr(0), sig);
}).play;
)
```

hjh