Hi,
I’d go server-side and use PV ugens. For splitting with filters you can use the BandSplitter Quark. But you can also split with FFT. I’m following a FFT band EQ example I’ve suggested here:
https://www.listarc.bham.ac.uk/lists/sc-users/msg66848.html
( https://www.listarc.bham.ac.uk/lists/sc-users-old/msg66848.html )
The new class is very similar, but, in contrast to PV_BinPeakEQ, it only returns the signal below a threshold from the regarded band. It uses the same trick with Impulse.ar, which is described in the old thread.
PV_BinMagBelow : PV_ChainUGen {
*new { |buffer, loBin, hiBin, threshold|
^this.multiNew(\control, buffer, loBin, hiBin, threshold)
}
*new1 { |rate, buffer, loBin, hiBin, threshold|
var bufSize, wipe_1, wipe_2, chain_band, chain_lo, chain_hi, chain_mul, mul, size;
bufSize = buffer.fftSize;
size = buffer.asArray.size;
chain_band = size.collect { LocalBuf(bufSize) };
chain_lo = size.collect { LocalBuf(bufSize) };
chain_hi = size.collect { LocalBuf(bufSize) };
mul = Impulse.ar(SampleRate.ir * 2 / bufSize);
chain_mul = FFT(LocalBuf(bufSize), mul);
chain_band = PV_Copy(buffer, chain_band);
chain_lo = PV_Copy(buffer, chain_lo);
chain_hi = PV_Copy(buffer, chain_hi);
wipe_1 = loBin * 2 / bufSize;
chain_band = PV_BrickWall(chain_band, wipe_1);
chain_lo = PV_BrickWall(chain_lo, wipe_1 - 1);
wipe_2 = hiBin * 2 / bufSize;
chain_band = PV_BrickWall(chain_band, wipe_2 - 1);
chain_hi = PV_BrickWall(chain_hi, wipe_2);
chain_band = PV_MagBelow(chain_band, threshold);
^chain_band[0]
}
}
Then, after recompile
(
SynthDef(\magBelow_5_band, { |out = 0, in, amp = 1, mix = 1|
// use NamedControl syntax here, more convenient with larger array args
var loFreqs = \loFreqs.kr([0, 1000, 2000, 5000, 10000]);
var hiFreqs = \hiFreqs.kr([1000, 2000, 5000, 10000, 20000]);
var thrs = \thrs.kr(100 ! 5);
var pans = \pans.kr(0 ! 5);
var amps = \amps.kr(1 ! 5);
var inSig = In.ar(in, 1);
var loBins, hiBins, binRange;
var bufSize = 1024;
var chain = FFT(LocalBuf(bufSize), inSig), multiChain, multi, stereo;
var chains = { LocalBuf(bufSize) } ! 5;
binRange = s.sampleRate / bufSize;
loBins = (loFreqs / binRange).round;
hiBins = (hiFreqs / binRange).round;
chains = chains.collect { |x| PV_Copy(chain, x) };
multiChain = 5.collect { |i| PV_BinMagBelow(chains[i], loBins[i], hiBins[i], thrs[i]) };
multi = IFFT(multiChain);
stereo = Mix(multi.collect { |x, i| Pan2.ar(x, pans[i], amps[i]) });
XOut.ar(out, mix, stereo * amp * EnvGate());
}).add;
)
// start fx silently
y = Synth(\magBelow_5_band)
// start mono source, mix of harmonic sound + noise
x = { Saw.ar(50, 0.1) + WhiteNoise.ar(0.03) }.play
s.freqscope
// for the threshold argument you'd have to find out a scaling
y.set(\thrs, 1!5);
// different thresholds
y.set(\thrs, [5, 0, 0.2, 10, 0]);
// weight the remaining bands
y.set(\amps, ([0.2, 0, 5, 0.1, 1]) );
// pan
y.set(\pans, ([-1, 0, 1, 0, 0]) );
// general amp
y.set(\amp, 0.5);
x.release
y.release
Thinking about it, it would be worth generalizing such band-relative operations.
BTW I’m not sure if you get much benefit with 32 bands (though, easy to try, just exchange the number 5 and the arrays of that size - avoid empty bands in the low). But it might make sense having a look at the Bark scale ( https://en.wikipedia.org/wiki/Bark_scale ). Last term a student of mine had the idea to FFT-split according to this scale and distribute to the 24 speakers of our concert room. Due to the cancelling of all concerts the project had to be done in a binaural format, but this worked quite well.
Regards
Daniel