FIR Anti-Aliasing Filters for Oversampling/Resample

First of all, please forgive my direct and simple language and the fact that I’m oversimplifying things here, it’s not easy for me to write about technical stuff. If anyone disagrees with certain things, they’re welcome to correct me.

When I realized that a new Resample UGen would be included in the development of SC 3.15, I thought it would be nice if we also had good anti-aliasing filters. The example in the 3.15 documentation uses two LPFs as an example before downsampling. However, LPFs aren’t really suitable for this purpose. Other filters in SC are also unsuitable for this purpose because they cannot roll off sharply in the middle of the frequency band while maintaining a stopband. That’s what FIR filters are for.

The reason for this is that LPFs behave differently in the resample-graph at higher sampling rates. At a sampling rate of 48000 and a cutoff frequency of 21 kHz, a 2x LPF yields the following attenuation values (rounded):
15KHz: -0.1dB, 18kHz: -0.5dB, 20kHz: -2.3dB, 21kHz: -6dB,
22kHz: -16dB, 23kHz: -39dB, 23.9kHz -118dB

However, this does not apply at a sampling rate of 96000 or when using “Resample(2)” with a cutoff frequency of 21 kHz and 2x LPF (rounded):
15KHz: -1.4dB, 18kHz: -3.1dB, 20kHz: -5dB, 21kHz: -6dB,
22kHz: -7.3dB, 23kHz: -9dB, 24kHz -11dB, 30kHz: -22dB > very bad AA-effect here!

This means: If you use 2x an LPF with “Resample(2)” before downsampling to filter out frequencies (AA components) above the target nyquist frequency, you’ll end up with a very flat filter with an attenuation of only -10 dB at 24kHz, as well as a very poor frequency response in the passband, an area that an AA filter shouldn’t actually affect.

For this reason, I decided to create some FIR AA filters for the community in advance. These FIR kernels are loaded as buffers using the Convolution2 UGen, and it’s very important that you use the correct kernel depending on the resample rate you’re using.

For now, I have created three FIR filters, all of which have the same specifications:

  1. The target downsampling rate is always 48000.
  2. Passband up to 20.5 kHz, deviation less than 0.005 dB, which essentially means no impact on lower frequencies.
  3. Transitionband up to 24 kHz: 21 kHz: -0.4dB, 21.5 kHz: -2dB, 22 kHz: -6dB, 22.5 kHz: -13dB, 23 kHz: -25dB, 23.5 kHz: -44dB, 24 kHz: -95dB
  4. Rejection band: at least -95dB at and above 24 kHz

The size of the filter varies depending on the oversampling rate:

  • 96k: 128 Samples (Resample 2x)
  • 192k: 256 Samples (Resample 4x)
  • 384k: 512 Samples (Resample 8x)

Since Convolution2’s CPU usage depends on the frame size and must be a power of two, I also used a power of two to maximize the filter specifications while minimizing CPU usage.

If anyone wants a different filter specification, it’s easy to create another one. Since I don’t have the development version of SC 3.15, I can only test it by changing the sampling rate in scsynth. Could someone please test my filters with 3.15 and Resample UGen?

You can also test these kernels by setting the sampling rate of the scsynth server to 96000 or higher!

Here are the kernels, instructions for using them with 3.14.1, and an example that should work for 3.15.

// IF you don't use the Resample(2) UGen in 3.15-dev: set serveroption for sample rate to 96k
s.options.sampleRate = 96000;

// boot server
s.boot;

// post the servers actual sample rate
s.actualSampleRate.round(1).postln;

// load the FIR kernel for sample rate 96k / 48k with Resample(2)
(
~kernelFir48k_2x_95dB_128S = Buffer.loadCollection(s, [
	3.10735094911491e-05, 9.87601637951595e-06, -1.10737477267071e-04, -2.11815991000221e-04,
	-9.76883186992134e-05, 1.41279669797449e-04, 1.39206575249781e-04, -1.57955220091580e-04,
	-2.56626749949320e-04, 1.16778781660566e-04, 3.88510701097779e-04, -1.66845850300382e-05,
	-5.21693094574120e-04, -1.59792042395175e-04, 6.25775870931313e-04, 4.17846946742348e-04,
	-6.64916289573010e-04, -7.51202292541550e-04, 5.98997430351011e-04, 1.13802978771451e-03,
	-3.88777755734192e-04, -1.53906230018351e-03, 2.05560715942015e-06, 1.89748942715106e-03,
	5.79300972416281e-04, -2.14128997234175e-03, -1.35230882133037e-03, 2.18820215685515e-03,
	2.28671057711517e-03, -1.95324604628126e-03, -3.32063289300062e-03, 1.35837213021172e-03,
	4.35889104822858e-03, -3.43483218672593e-04, -5.27452605522543e-03, -1.12217600177471e-03,
	5.91379851894811e-03, 3.02940358657481e-03, -6.10439807785135e-03, -5.32030829203232e-03,
	5.66609305472552e-03, 7.88232869743182e-03, -4.42246088869304e-03, -1.05453716477607e-02,
	2.21168886094490e-03, 1.30815899590126e-02, 1.10639419349884e-03, -1.52059139806634e-02,
	-5.65127539382519e-03, 1.65728896168359e-02, 1.15378913380359e-02, -1.67599472520647e-02,
	-1.89227860838246e-02, 1.52134569134602e-02, 2.81177787108968e-02, -1.10920286575042e-02,
	-3.98866339561082e-02, 2.78452499627548e-03, 5.63754114108983e-02, 1.39042568242949e-02,
	-8.51893225082190e-02, -5.63410254947792e-02, 1.75944051284943e-01, 4.19926559238836e-01,
	4.19926559238836e-01, 1.75944051284943e-01, -5.63410254947792e-02, -8.51893225082190e-02,
	1.39042568242949e-02, 5.63754114108983e-02, 2.78452499627548e-03, -3.98866339561082e-02,
	-1.10920286575042e-02, 2.81177787108968e-02, 1.52134569134602e-02, -1.89227860838246e-02,
	-1.67599472520647e-02, 1.15378913380359e-02, 1.65728896168359e-02, -5.65127539382519e-03,
	-1.52059139806634e-02, 1.10639419349884e-03, 1.30815899590126e-02, 2.21168886094490e-03,
	-1.05453716477607e-02, -4.42246088869304e-03, 7.88232869743182e-03, 5.66609305472552e-03,
	-5.32030829203232e-03, -6.10439807785135e-03, 3.02940358657481e-03, 5.91379851894811e-03,
	-1.12217600177471e-03, -5.27452605522543e-03, -3.43483218672593e-04, 4.35889104822858e-03,
	1.35837213021172e-03, -3.32063289300062e-03, -1.95324604628126e-03, 2.28671057711517e-03,
	2.18820215685515e-03, -1.35230882133037e-03, -2.14128997234175e-03, 5.79300972416281e-04,
	1.89748942715106e-03, 2.05560715942015e-06, -1.53906230018351e-03, -3.88777755734192e-04,
	1.13802978771451e-03, 5.98997430351011e-04, -7.51202292541550e-04, -6.64916289573010e-04,
	4.17846946742348e-04, 6.25775870931313e-04, -1.59792042395175e-04, -5.21693094574120e-04,
	-1.66845850300382e-05, 3.88510701097779e-04, 1.16778781660566e-04, -2.56626749949320e-04,
	-1.57955220091580e-04, 1.39206575249781e-04, 1.41279669797449e-04, -9.76883186992134e-05,
	-2.11815991000221e-04, -1.10737477267071e-04, 9.87601637951595e-06, 3.10735094911491e-05
]);
)

// post kernel size
~kernelFir48k_2x_95dB_128S.numFrames;

// plot kernel if you like to see
~kernelFir48k_2x_95dB_128S.plot;

// using the kernel in a SynthDef without Resample, but with server sample rate 96k for testing
// adjust the size in Convolution2 according to the kernel size
(
SynthDef(\filter01, {
	arg out=0, amp=0.2, kernel=0;
	var sig;
	sig = WhiteNoise.ar(amp) ! 2;
	sig = Convolution2.ar(sig, kernel, 0, 128);
	Out.ar(out, sig);
}).add;
)

// using the kernel in a SynthDef with Resample:
(
SynthDef(\filter01, {
	arg out=0, amp=0.2, kernel=0;
	var sig;
	Resample(2);
	sig = WhiteNoise.ar(amp) ! 2;
	sig = Convolution2.ar(sig, kernel, 0, 128);
	Out.ar(out, sig);
}).add;
)

~filter01 = Synth(\filter01, [amp: 0.2, kernel: ~kernelFir48k_2x_95dB_128S]);
~filter01.free;



// FIR kernel for sample rate 192k / 48k with Resample(4)
~kernelFir48k_4x_95dB_256S = Buffer.loadCollection(s, [
	1.70723691932502e-05, 1.80792387492879e-05, 1.43231981011602e-05, -3.61170983610532e-06,
	-3.52737209750031e-05, -7.24633025066862e-05, -1.00642118305463e-04, -1.04500091123357e-04,
	-7.58226933524237e-05, -1.98865110612319e-05, 4.36281825886469e-05, 8.71951694851486e-05,
	8.77709108396947e-05, 3.95639566808192e-05, -3.95696764461494e-05, -1.12928904359150e-04,
	-1.40687088920571e-04, -1.00234509626169e-04, -1.09522869952055e-06, 1.14595833878083e-04,
	1.88904165587220e-04, 1.76194917166450e-04, 6.93686837728316e-05, -9.01514026687766e-05,
	-2.26694558123647e-04, -2.65122004093280e-04, -1.70716512686001e-04, 2.63668515290303e-05,
	2.37493700786023e-04, 3.53958641079463e-04, 3.01593692454309e-04, 8.47948375120163e-05,
	-2.04970572553852e-04, -4.25626212792693e-04, -4.52667010572400e-04, -2.47097824102674e-04,
	1.12958592361456e-04, 4.58468693350772e-04, 6.07024795941677e-04, 4.57129640976647e-04,
	5.14246853887339e-05, -4.28371069900572e-04, -7.40384034284019e-04, -7.02497680205414e-04,
	-2.94800637387069e-04, 3.11470680202063e-04, 8.22154472888615e-04, 9.60598088076277e-04,
	6.14449751322455e-04, -8.76992993612036e-05, -8.17742494277761e-04, -1.19848609083670e-03,
	-9.95677600571714e-04, -2.55233797083610e-04, 6.91999964230900e-04, 1.37406637055806e-03,
	1.41004110175467e-03, 7.17821810164684e-04, -4.13626025371020e-04, -1.43872602787745e-03,
	-1.81482595170527e-03, -1.28518677010350e-03, -3.98279507854403e-05, 1.34136395562689e-03,
	2.15406349546994e-03, 1.92444796842065e-03, 6.76826344583535e-04, -1.03358199499595e-03,
	-2.36120774355083e-03, -2.58345935026493e-03, -1.48713032824841e-03, 4.75633874044843e-04,
	2.36339371654037e-03, 3.19120488601552e-03, 2.43802394441601e-03, 3.57406914567850e-04,
	-2.08697664630256e-03, -3.65993189909953e-03, -3.47192353784879e-03, -1.46987527402549e-03,
	1.46382285599018e-03, 3.88881705061292e-03, 4.50559839390012e-03, 2.84073215724165e-03,
	-4.37624868839988e-04, -3.76861499157747e-03, -5.43090409233912e-03, -4.42023735348557e-03,
	-1.03066512423811e-03, 3.18629923791483e-03, 6.11642953486911e-03, 6.12826884710284e-03,
	2.95936244711559e-03, -2.02807980667308e-03, -6.40866347856084e-03, -7.85399395446830e-03,
	-5.34587179032258e-03, 1.78106401281226e-04, 6.12985595407493e-03, 9.45572617713220e-03,
	8.17173292202695e-03, 2.49213199371040e-03, -5.06669365724040e-03, -1.07580238201416e-02,
	-1.14164985541796e-02, -6.15454447503820e-03, 2.93613383234335e-03, 1.15386890871853e-02,
	1.50892031030841e-02, 1.11034027212976e-02, 7.08669968736540e-04, -1.14849690771299e-02,
	-1.93039370921887e-02, -1.79710628406303e-02, -6.75507928085145e-03, 1.00471285629608e-02,
	2.45003368843427e-02, 2.84374242862899e-02, 1.75235039662254e-02, -5.85054457664745e-03,
	-3.23421109995325e-02, -4.85469659623459e-02, -4.21070420453171e-02, -7.07528891932349e-03,
	5.27081256316807e-02, 1.23807431380313e-01, 1.87148066685396e-01, 2.24399189900108e-01,
	2.24399189900108e-01, 1.87148066685396e-01, 1.23807431380313e-01, 5.27081256316807e-02,
	-7.07528891932349e-03, -4.21070420453171e-02, -4.85469659623459e-02, -3.23421109995325e-02,
	-5.85054457664745e-03, 1.75235039662254e-02, 2.84374242862899e-02, 2.45003368843427e-02,
	1.00471285629608e-02, -6.75507928085145e-03, -1.79710628406303e-02, -1.93039370921887e-02,
	-1.14849690771299e-02, 7.08669968736540e-04, 1.11034027212976e-02, 1.50892031030841e-02,
	1.15386890871853e-02, 2.93613383234335e-03, -6.15454447503820e-03, -1.14164985541796e-02,
	-1.07580238201416e-02, -5.06669365724040e-03, 2.49213199371040e-03, 8.17173292202695e-03,
	9.45572617713220e-03, 6.12985595407493e-03, 1.78106401281226e-04, -5.34587179032258e-03,
	-7.85399395446830e-03, -6.40866347856084e-03, -2.02807980667308e-03, 2.95936244711559e-03,
	6.12826884710284e-03, 6.11642953486911e-03, 3.18629923791483e-03, -1.03066512423811e-03,
	-4.42023735348557e-03, -5.43090409233912e-03, -3.76861499157747e-03, -4.37624868839988e-04,
	2.84073215724165e-03, 4.50559839390012e-03, 3.88881705061292e-03, 1.46382285599018e-03,
	-1.46987527402549e-03, -3.47192353784879e-03, -3.65993189909953e-03, -2.08697664630256e-03,
	3.57406914567850e-04, 2.43802394441601e-03, 3.19120488601552e-03, 2.36339371654037e-03,
	4.75633874044843e-04, -1.48713032824841e-03, -2.58345935026493e-03, -2.36120774355083e-03,
	-1.03358199499595e-03, 6.76826344583535e-04, 1.92444796842065e-03, 2.15406349546994e-03,
	1.34136395562689e-03, -3.98279507854403e-05, -1.28518677010350e-03, -1.81482595170527e-03,
	-1.43872602787745e-03, -4.13626025371020e-04, 7.17821810164684e-04, 1.41004110175467e-03,
	1.37406637055806e-03, 6.91999964230900e-04, -2.55233797083610e-04, -9.95677600571714e-04,
	-1.19848609083670e-03, -8.17742494277761e-04, -8.76992993612036e-05, 6.14449751322455e-04,
	9.60598088076277e-04, 8.22154472888615e-04, 3.11470680202063e-04, -2.94800637387069e-04,
	-7.02497680205414e-04, -7.40384034284019e-04, -4.28371069900572e-04, 5.14246853887339e-05,
	4.57129640976647e-04, 6.07024795941677e-04, 4.58468693350772e-04, 1.12958592361456e-04,
	-2.47097824102674e-04, -4.52667010572400e-04, -4.25626212792693e-04, -2.04970572553852e-04,
	8.47948375120163e-05, 3.01593692454309e-04, 3.53958641079463e-04, 2.37493700786023e-04,
	2.63668515290303e-05, -1.70716512686001e-04, -2.65122004093280e-04, -2.26694558123647e-04,
	-9.01514026687766e-05, 6.93686837728316e-05, 1.76194917166450e-04, 1.88904165587220e-04,
	1.14595833878083e-04, -1.09522869952055e-06, -1.00234509626169e-04, -1.40687088920571e-04,
	-1.12928904359150e-04, -3.95696764461494e-05, 3.95639566808192e-05, 8.77709108396947e-05,
	8.71951694851486e-05, 4.36281825886469e-05, -1.98865110612319e-05, -7.58226933524237e-05,
	-1.04500091123357e-04, -1.00642118305463e-04, -7.24633025066862e-05, -3.52737209750031e-05,
	-3.61170983610532e-06, 1.43231981011602e-05, 1.80792387492879e-05, 1.70723691932502e-05
]);
)


// FIR kernel for sample rate 384k / 48k with Resample(8)
~kernelFir48k_8x_95dB_512S = Buffer.loadCollection(s, [
	1.23064223528150e-05, 8.56209390195851e-06, 1.02135118329274e-05, 1.08953123650380e-05,
	1.01681101766024e-05, 7.67319123495313e-06, 3.20221056363857e-06, -3.24140880445665e-06,
	-1.13998867506142e-05, -2.07467150023188e-05, -3.05072426041582e-05, -3.97168951888198e-05,
	-4.73146752026809e-05, -5.22641958813905e-05, -5.36894924209287e-05, -5.10090073723377e-05,
	-4.40491729387362e-05, -3.31194226681453e-05, -1.90334988749114e-05, -3.06745652476350e-06,
	1.31476913642627e-05, 2.77920265363555e-05, 3.90660206946632e-05, 4.54199808697347e-05,
	4.57752592204873e-05, 3.97068939072248e-05, 2.75593642429868e-05, 1.04732590597708e-05,
	-9.68956200627949e-06, -3.05222680475741e-05, -4.93370888467211e-05, -6.34902158761089e-05,
	-7.07299936219836e-05, -6.95242033733937e-05, -5.93208523216142e-05, -4.07021705585527e-05,
	-1.54016801658050e-05, 1.38294639916733e-05, 4.35019446787506e-05, 6.97884852709189e-05,
	8.90014238009475e-05, 9.80902938141359e-05, 9.50946528633114e-05, 7.94881525617226e-05,
	5.23593081015779e-05, 1.63920144701768e-05, -2.43671725190895e-05, -6.49403692384107e-05,
	-1.00005924622649e-04, -1.24575638661258e-04, -1.34678023441314e-04, -1.27956930864333e-04,
	-1.04099491676875e-04, -6.50238647254600e-05, -1.47844122448662e-05, 4.08132916336977e-05,
	9.48569000994849e-05, 1.40163567480821e-04, 1.70210036486759e-04, 1.80039159392104e-04,
	1.67020518332613e-04, 1.31353919933643e-04, 7.62313641532546e-05, 7.61310816929172e-06,
	-6.63779152108550e-05, -1.36387983001117e-04, -1.92988367957606e-04, -2.27915056011294e-04,
	-2.35233993550712e-04, -2.12271252017667e-04, -1.60170155032066e-04, -8.39775952950108e-05,
	7.78122360377424e-06, 1.04014839010630e-04, 1.92373157779393e-04, 2.60801147011568e-04,
	2.99147190392864e-04, 3.00615860572138e-04, 2.62862960774309e-04, 1.88566262108239e-04,
	8.53652952938496e-05, -3.48587560935866e-05, -1.57305904381623e-04, -2.66042309104148e-04,
	-3.46037135637394e-04, -3.85195606791837e-04, -3.76118502192900e-04, -3.17339489618236e-04,
	-2.13847114227487e-04, -7.67825424606883e-05, 7.76923933695118e-05, 2.30252422952224e-04,
	3.60784894169767e-04, 4.50998100905384e-04, 4.86929016206925e-04, 4.61013063206983e-04,
	3.73418651507729e-04, 2.32430904431204e-04, 5.37837800597749e-05, -1.41025917518185e-04,
	-3.27272763826035e-04, -4.80093570309401e-04, -5.77754896757122e-04, -6.04674051045085e-04,
	-5.53781653132284e-04, -4.27880108607079e-04, -2.39767913890716e-04, -1.10516039918826e-05,
	2.30262988891094e-04, 4.53142351577243e-04, 6.27461792054856e-04, 7.28014530565822e-04,
	7.38067420257889e-04, 6.51971733916228e-04, 4.76438659131244e-04, 2.30246935055086e-04,
	-5.76537644621427e-05, -3.51469739877918e-04, -6.12949183260898e-04, -8.06298316893486e-04,
	-9.03004037542826e-04, -8.85922507435768e-04, -7.52060249980685e-04, -5.13620392019486e-04,
	-1.97096059156508e-04, 1.59559242303208e-04, 5.11399190717482e-04, 8.12075905155750e-04,
	1.01986896950631e-03, 1.10337749287827e-03, 1.04611005260341e-03, 8.49318415837528e-04,
	5.32623234592640e-04, 1.32251103610899e-04, -3.02990870832489e-04, -7.17572877953480e-04,
	-1.05624209071139e-03, -1.27129290193592e-03, -1.32916262117095e-03, -1.21545519131043e-03,
	-9.37663537688116e-04, -5.25132643331073e-04, -2.61489953337219e-05, 4.97584554230059e-04,
	9.78474186052486e-04, 1.35165689064837e-03, 1.56363188756929e-03, 1.57976740735506e-03,
	1.38964832234498e-03, 1.00947025445154e-03, 4.81040592801201e-04, -1.32625584444954e-04,
	-7.54679604201727e-04, -1.30393963600945e-03, -1.70535905514360e-03, -1.90012871662101e-03,
	-1.85407153731210e-03, -1.56315615575711e-03, -1.05528589581611e-03, -3.87973168516064e-04,
	3.57966694093941e-04, 1.08804029426637e-03, 1.70589507674737e-03, 2.12587338194187e-03,
	2.28468607474694e-03, 2.15063900892430e-03, 1.72910169779229e-03, 1.06334608027443e-03,
	2.30453909230461e-04, -6.67375142805460e-04, -1.51516660292581e-03, -2.19970041361385e-03,
	-2.62441465448834e-03, -2.72274855886219e-03, -2.46811405258139e-03, -1.87905133074164e-03,
	-1.01869407661331e-03, 1.16223862350454e-05, 1.08401825027977e-03, 2.05968761678217e-03,
	2.80660269883178e-03, 3.21707825564810e-03, 3.22289637065353e-03, 2.80591413954352e-03,
	2.00257918404656e-03, 9.01506195677435e-04, -3.65863557930403e-04, -1.64059587836309e-03,
	-2.75584720081222e-03, -3.55832793882558e-03, -3.92893027350887e-03, -3.79979800410544e-03,
	-3.16546207643880e-03, -2.08633426667071e-03, -6.83770069842106e-04, 8.73028283968842e-04,
	2.38692770096187e-03, 3.65731554673382e-03, 4.50611510699795e-03, 4.80206012557443e-03,
	4.48000165756622e-03, 3.55251838560080e-03, 2.11197291132312e-03, 3.22305486874948e-04,
	-1.59885229360146e-03, -3.40588749165303e-03, -4.85579038244531e-03, -5.73990035896185e-03,
	-5.91277656998834e-03, -5.31433463878459e-03, -3.98206968650154e-03, -2.05130166788649e-03,
	2.57190349661865e-04, 2.66119999376720e-03, 4.85031339316586e-03, 6.52454658189637e-03,
	7.43375235991690e-03, 7.41281461426712e-03, 6.40792501531396e-03, 4.49011822527223e-03,
	1.85364533623177e-03, -1.20145920456553e-03, -4.30131736011803e-03, -7.04147321473133e-03,
	-9.03672142604589e-03, -9.97165957776210e-03, -9.64571208183105e-03, -8.00670311069112e-03,
	-5.16812358944877e-03, -1.40694740315113e-03, 2.85897188113519e-03, 7.11249017773458e-03,
	1.07939624480986e-02, 1.33674274957941e-02, 1.43889486747321e-02, 1.35692166540809e-02,
	1.08227550584050e-02, 6.29720320322504e-03, 3.78087246910442e-04, -6.33295987325505e-03,
	-1.30656774262758e-02, -1.89505851429658e-02, -2.31039387299606e-02, -2.47198489078467e-02,
	-2.31600342170360e-02, -1.80315423721047e-02, -9.24369488508198e-03, 2.96258774950900e-03,
	1.80172223862361e-02, 3.50560821733500e-02, 5.29863190361067e-02, 7.05761985318517e-02,
	8.65610844569474e-02, 9.97553778609020e-02, 1.09159430856289e-01, 1.14050816719867e-01,
	1.14050816719867e-01, 1.09159430856289e-01, 9.97553778609020e-02, 8.65610844569474e-02,
	7.05761985318517e-02, 5.29863190361067e-02, 3.50560821733500e-02, 1.80172223862361e-02,
	2.96258774950900e-03, -9.24369488508198e-03, -1.80315423721047e-02, -2.31600342170360e-02,
	-2.47198489078467e-02, -2.31039387299606e-02, -1.89505851429658e-02, -1.30656774262758e-02,
	-6.33295987325505e-03, 3.78087246910442e-04, 6.29720320322504e-03, 1.08227550584050e-02,
	1.35692166540809e-02, 1.43889486747321e-02, 1.33674274957941e-02, 1.07939624480986e-02,
	7.11249017773458e-03, 2.85897188113519e-03, -1.40694740315113e-03, -5.16812358944877e-03,
	-8.00670311069112e-03, -9.64571208183105e-03, -9.97165957776210e-03, -9.03672142604589e-03,
	-7.04147321473133e-03, -4.30131736011803e-03, -1.20145920456553e-03, 1.85364533623177e-03,
	4.49011822527223e-03, 6.40792501531396e-03, 7.41281461426712e-03, 7.43375235991690e-03,
	6.52454658189637e-03, 4.85031339316586e-03, 2.66119999376720e-03, 2.57190349661865e-04,
	-2.05130166788649e-03, -3.98206968650154e-03, -5.31433463878459e-03, -5.91277656998834e-03,
	-5.73990035896185e-03, -4.85579038244531e-03, -3.40588749165303e-03, -1.59885229360146e-03,
	3.22305486874948e-04, 2.11197291132312e-03, 3.55251838560080e-03, 4.48000165756622e-03,
	4.80206012557443e-03, 4.50611510699795e-03, 3.65731554673382e-03, 2.38692770096187e-03,
	8.73028283968842e-04, -6.83770069842106e-04, -2.08633426667071e-03, -3.16546207643880e-03,
	-3.79979800410544e-03, -3.92893027350887e-03, -3.55832793882558e-03, -2.75584720081222e-03,
	-1.64059587836309e-03, -3.65863557930403e-04, 9.01506195677435e-04, 2.00257918404656e-03,
	2.80591413954352e-03, 3.22289637065353e-03, 3.21707825564810e-03, 2.80660269883178e-03,
	2.05968761678217e-03, 1.08401825027977e-03, 1.16223862350454e-05, -1.01869407661331e-03,
	-1.87905133074164e-03, -2.46811405258139e-03, -2.72274855886219e-03, -2.62441465448834e-03,
	-2.19970041361385e-03, -1.51516660292581e-03, -6.67375142805460e-04, 2.30453909230461e-04,
	1.06334608027443e-03, 1.72910169779229e-03, 2.15063900892430e-03, 2.28468607474694e-03,
	2.12587338194187e-03, 1.70589507674737e-03, 1.08804029426637e-03, 3.57966694093941e-04,
	-3.87973168516064e-04, -1.05528589581611e-03, -1.56315615575711e-03, -1.85407153731210e-03,
	-1.90012871662101e-03, -1.70535905514360e-03, -1.30393963600945e-03, -7.54679604201727e-04,
	-1.32625584444954e-04, 4.81040592801201e-04, 1.00947025445154e-03, 1.38964832234498e-03,
	1.57976740735506e-03, 1.56363188756929e-03, 1.35165689064837e-03, 9.78474186052486e-04,
	4.97584554230059e-04, -2.61489953337219e-05, -5.25132643331073e-04, -9.37663537688116e-04,
	-1.21545519131043e-03, -1.32916262117095e-03, -1.27129290193592e-03, -1.05624209071139e-03,
	-7.17572877953480e-04, -3.02990870832489e-04, 1.32251103610899e-04, 5.32623234592640e-04,
	8.49318415837528e-04, 1.04611005260341e-03, 1.10337749287827e-03, 1.01986896950631e-03,
	8.12075905155750e-04, 5.11399190717482e-04, 1.59559242303208e-04, -1.97096059156508e-04,
	-5.13620392019486e-04, -7.52060249980685e-04, -8.85922507435768e-04, -9.03004037542826e-04,
	-8.06298316893486e-04, -6.12949183260898e-04, -3.51469739877918e-04, -5.76537644621427e-05,
	2.30246935055086e-04, 4.76438659131244e-04, 6.51971733916228e-04, 7.38067420257889e-04,
	7.28014530565822e-04, 6.27461792054856e-04, 4.53142351577243e-04, 2.30262988891094e-04,
	-1.10516039918826e-05, -2.39767913890716e-04, -4.27880108607079e-04, -5.53781653132284e-04,
	-6.04674051045085e-04, -5.77754896757122e-04, -4.80093570309401e-04, -3.27272763826035e-04,
	-1.41025917518185e-04, 5.37837800597749e-05, 2.32430904431204e-04, 3.73418651507729e-04,
	4.61013063206983e-04, 4.86929016206925e-04, 4.50998100905384e-04, 3.60784894169767e-04,
	2.30252422952224e-04, 7.76923933695118e-05, -7.67825424606883e-05, -2.13847114227487e-04,
	-3.17339489618236e-04, -3.76118502192900e-04, -3.85195606791837e-04, -3.46037135637394e-04,
	-2.66042309104148e-04, -1.57305904381623e-04, -3.48587560935866e-05, 8.53652952938496e-05,
	1.88566262108239e-04, 2.62862960774309e-04, 3.00615860572138e-04, 2.99147190392864e-04,
	2.60801147011568e-04, 1.92373157779393e-04, 1.04014839010630e-04, 7.78122360377424e-06,
	-8.39775952950108e-05, -1.60170155032066e-04, -2.12271252017667e-04, -2.35233993550712e-04,
	-2.27915056011294e-04, -1.92988367957606e-04, -1.36387983001117e-04, -6.63779152108550e-05,
	7.61310816929172e-06, 7.62313641532546e-05, 1.31353919933643e-04, 1.67020518332613e-04,
	1.80039159392104e-04, 1.70210036486759e-04, 1.40163567480821e-04, 9.48569000994849e-05,
	4.08132916336977e-05, -1.47844122448662e-05, -6.50238647254600e-05, -1.04099491676875e-04,
	-1.27956930864333e-04, -1.34678023441314e-04, -1.24575638661258e-04, -1.00005924622649e-04,
	-6.49403692384107e-05, -2.43671725190895e-05, 1.63920144701768e-05, 5.23593081015779e-05,
	7.94881525617226e-05, 9.50946528633114e-05, 9.80902938141359e-05, 8.90014238009475e-05,
	6.97884852709189e-05, 4.35019446787506e-05, 1.38294639916733e-05, -1.54016801658050e-05,
	-4.07021705585527e-05, -5.93208523216142e-05, -6.95242033733937e-05, -7.07299936219836e-05,
	-6.34902158761089e-05, -4.93370888467211e-05, -3.05222680475741e-05, -9.68956200627949e-06,
	1.04732590597708e-05, 2.75593642429868e-05, 3.97068939072248e-05, 4.57752592204873e-05,
	4.54199808697347e-05, 3.90660206946632e-05, 2.77920265363555e-05, 1.31476913642627e-05,
	-3.06745652476350e-06, -1.90334988749114e-05, -3.31194226681453e-05, -4.40491729387362e-05,
	-5.10090073723377e-05, -5.36894924209287e-05, -5.22641958813905e-05, -4.73146752026809e-05,
	-3.97168951888198e-05, -3.05072426041582e-05, -2.07467150023188e-05, -1.13998867506142e-05,
	-3.24140880445665e-06, 3.20221056363857e-06, 7.67319123495313e-06, 1.01681101766024e-05,
	1.08953123650380e-05, 1.02135118329274e-05, 8.56209390195851e-06, 1.23064223528150e-05
]);
)

And here you can see a graph showing how two LPFs behave at a 96k sample rate with a cutoff of 21kHz, followed by my filter, and then my three FIR filter plots.





3 Likes

Hi, yes, we definitely need high-quality anti-aliasing filters!

Ideally, we would have AAIn and AAOut UGens that automatically do the filtering by choosing the appropriate kernel depending on the upsampling factor. (The kernels could be hardcoded and live in static memory.)


Strictly speaking, if you put a signal through an upsampled Synth, e.g. Distortion with anti-aliasing, you should also filter the input. Ideally, the input would be upsampled with zero-padding and then filtered. However, if the user forgets about the filtering, this can lead to unexpected results, especially with low-frequency modulation signals. That’s why I chose zero-order-hold as the upsampling method, which – although not perfect – also works without filtering. (The zero-order-hold acts like a crude low pass filter.)

I would imagine AAIn to automatically zero-pad the input before filtering, whereas AAOut would only apply the filter.

1 Like

I just remembered that there was someone else interested in antialiasing filters: Synth reblocking and upsampling by Spacechild1 · Pull Request #6702 · supercollider/supercollider · GitHub

Maybe @sadguitarius wants to join the conversation :slight_smile:

1 Like

I’m here! Thanks for looping me in @Spacechild1 and thanks also to @VactrolDrifter for your work on this. I’ve done a few deep dives on this stuff and currently have been using a polyphase IIR resampling implementation that gives fairly similar results to the one used in the MSP poly~ object (I’ve implemented my filter DSP in some unreleased MSP externals and it’s quite CPU efficient). My latest project involves grabbing some bits of this incredible repo to design coefficients that allow for approximately linear phase IIR resampling filters. Those coefficients would drop into the DSP code I already have. I’m getting closer to having something I actually like here, but I’ve also been using an LLM to port over some of Jenssen’s original Octave code to Python so I can generate coefficients without the whole Octave on a virtual machine thing, which is a great use for an LLM but requires a lot of babysitting.

All of that aside, I do think that a lot of this boils down to making some concrete decisions about
a) the type(s) of filter desired (FIR, IIR, what topology)
b) requirements on passband ripple, stopband attenuation, group delay etc.
c) what ratios of oversampling should be supported and at what base sample rates
d) how to balance CPU efficiency (for multiple instances, e.g. only resampling a nonlinear part of a single synth voice) vs quality (for a monolithic resampling process)

These are all kind of arbitrary decisions and I’m not aware of other discussions about this. But maybe here’s a good place to start! I’ve been an absolute scatterbrain lately but would love to help on this and it would give me good incentive to finish up some of this code I’ve been sitting on for a while.

I don’t want to derail the discussion around these FIR filters either, so if you want to move this to another topic, that totally makes sense to me.

3 Likes

@sadguitarius Thanks for chiming in!

For the built-in AAIn and AAOut UGens I would personally favor CPU efficiency and flexibility over quality. Ideally, they should have no significant impact on the total CPU usage.

It would be interesting to compare your IIR filters with @VactrolDrifter’s FIR filters in terms of frequency response and CPU usage.

2 Likes

Sounds good, I’ll prioritize getting something usable together to test out.

And agreed, I personally think efficiency is more important than a filter that is super accurate but has a ton of coefficients. Polyphase structures can really help to achieve a good balance of both though.

Also there were some good discussions about resampling filters scattered around on the Max/MSP Discord.

I should add latency to that list of priorities! In particular the balance of minimal latency vs phase-linearity for parallel processing, and I do imagine people will want both of those things available for different applications.

2 Likes

Of course, other filters should also be considered for SC build in UGen, polyphase filters or nearly linear IIR filters are also good choices! It is important to consider not only the frequency response but also the time domain response! A filter that applies different delays to frequencies across the entire passband can cause undesirable side effects in the passband even before attenuation sets in, in the higher frequencies.

Personally, I would prefer standard AAIn/AAOut UGens that allow you to select the filter type via arguments to ensure maximum flexibility. Ultimately, I personally would always prioritize quality over a bit more CPU performance.

For example, when I run 40 light synths at a 96 kHz sample rate, they use about 2% of my cpu power at peak times with scsynth. When I add my FIRs, that rises to 4%, meaning 40 of my FIRs use 2% of my CPU power! And then there are the people who use supernova and want the highest quality and don’t care about cpu anymore, that’s what this server is for, after all. With supernova i can run 5 times more default synths (tested) for the same cpu peak stable.
It would be a shame to limit users who actually have more than enough cpu power. And the UGens are supposed to offer everyone more convenience?

By the way, if you wish i could also build other filters and compare it in the same plot if you want. But only FIR’s or IIR’s like ellipticals, butterworth, chebys…

How many channels per Synth? What’s your CPU?

Question: isn’t Convolution2 (which internally uses FFT) overkill for such small kernels? I would try to benchmark against a time-domain implementation.

Side note: if AAIn and AAOut followed the API of the I/O UGens, we could pass more than one input into the UGen. This would allow us to calculate several filters in parallel using SIMD instructions! FIR filters with time-domain convolution could also be optimized in this way.

Personally, I would prefer standard AAIn/AAOut UGens that allow you to select the filter type via arguments to ensure maximum flexibility. Ultimately, I personally would always prioritize quality over a bit more CPU performance.

That would be the optimal solution I guess – at least from a user’s perspective :slight_smile:

I ran the test again, as following.

// scsynth with 96000 sample rate!

// base synth cpu usage test
(
SynthDef(\base, {
	var sig = WhiteNoise.ar(1 ! 2);
	Out.ar(0, sig * 0);
}).add;
)

200.do { Synth(\base) };
s.freeAll;

// FIR filter synth cpu usage test
(
SynthDef(\fir, {
	var sig = WhiteNoise.ar(1 ! 2);
	sig = Convolution2.ar(sig, ~kernelFir48k_2x_95dB_128S, 0, 128);
	Out.ar(0, sig * 0);
}).add;
)

200.do { Synth(\fir) };
s.freeAll;


// scsynth with 192000 sample rate!

// base synth cpu usage test
(
SynthDef(\base, {
	var sig = WhiteNoise.ar(1 ! 2);
	Out.ar(0, sig * 0);
}).add;
)

100.do { Synth(\base) };
s.freeAll;

// FIR filter synth cpu usage test
(
SynthDef(\fir, {
	var sig = WhiteNoise.ar(1 ! 2);
	sig = Convolution2.ar(sig, ~kernelFir48k_4x_95dB_256S, 0, 256);
	Out.ar(0, sig * 0);
}).add;
)

100.do { Synth(\fir) };
s.freeAll;

In scsynth with 96000 sample rate and 200 synths:
base synth peak: 9 - 11%, average: 9%
fir synth peak: 34 - 38%, average: 33%
36% - 10% = 26%, 26% / 200 = 0.13% cpu usage for a two channel 128T FIR.

In scsynth with 192000 sample rate and 100 synths:
// base synth peak: 9 - 12%, average: 9%
// fir synth peak: 34 - 41%, average: 33%
// 37% - 10% = 27%, 27% / 100 = 0.27% cpu usage for a two channel 256T FIR.

My CPU: AMD Ryzen 9 5900X 12-Core 24 Thread 4.95Ghz
My OS: openSUSE Tumbleweed with pipewire-jack

Thanks! You have quite a beefy machine :slight_smile:

Here are the results with my AMD Ryzen 7 PRO 5850U 8-Core 16 Thread 1.9–4.4 GHz on a Windows develop snapshot. The Server sample rate is always 48 kHz, instead the upsampling is done in the Synth with Resample.

Synth SR kernel size peak CPU FIR overhead CPU per Synth
200 x \base 2x 48 kHz 10% 0.05%
200 x \fir 2x 48 kHz 128 59% 49% 0.245%
200 x \base 4x 48 kHz 16.5% 0.05%
200 x \fir 4x 48 kHz 256 130% 113.5% 0.5675%
200 x \base 8x 48 kHz 28% 0.05%
200 x \fir 8x 48 kHz 512 270% 242% 1.21%

Seems like the filter takes almost twice as much CPU on my (weaker) system. 1.21% for a two-channel AA filter for 8x upsampling is quite heavy. If you also need to filter the input signal, you have to multiply that by two. It’s probably a decent trade-off considering the filter’s quality, but we definitely also need a cheaper option.

Note that if the FFT size is larger than the processing block size, the FFT causes a delay of FFT size - block size samples. You can avoid this delay by raising the block size accordingly. For example, with a Server block size of 64 and an upsample factor of 2, you would have to set the Synth block size to 128 to match the 128-point kernel. Currently, this doesn’t work; scsynth and Supernova complain if you try to set a Synth block size that is larger than the Server block size. I want to lift this restriction to some extent: it should be possible to set a larger block size as long as block size / resample factor <= Server block size.

PS: I’ve tried Convolution3 (time-domain convolution), but even with a 128 kernel it is prohibitively more expensive.