Can a simple IIR biquad filter be used as an audio de-emphasis filter for broadcast FM radio? If so, how does one translate the filter requirements (50 uS time constant in the U.S.?) into parameters for one of the common (e.g. RBJ's) biquad cookbook algorithms, given typical audio sample rates? Is the formula for a low shelving filter appropriate?

• it seems to me that the de-emphasis is a 1st-order LPF with the corner frequency at $\frac{1}{2 \pi \, 75 \, \mu S}$. all of the shelves and filters in the cookbook are 2nd order. you can experiment and see if a shelf will match up well enough. Commented Oct 5, 2016 at 6:29
• Both answers below seem to meet the bill? Can one be selected or were you looking for something more? Commented Jan 13 at 17:02

You could also implement a 2nd order IIR filter which has response of 1st order filter by using some other TF but one used in RBJ's cookbook. Here's one way to do it using MZT (C++).

double Fs = 44100; // higher sample rate results better filter accuracy
double T1 = 0.000075; // time constant
double p1 = -exp(-1.0/(Fs*T1));
double z1 = 1 + z1;

a0 = 1.0;
a1 = p1;
a2 = 0;

b0 = 1.0;
b1 = z1;
b2 = 0;

% swap between a1, b1 to select pre- or de-emphasis


Coefficients from above calculations adds +13.63dB gain so you need to normalize before use.

Same as Octave code:

% PACKAGES

clear all;
clc;

fs = 44100;
samplingtime = 1/fs;

% analog prototype
A2 = [1];
B2 = [0.000075 1];
Ds = tf(B2, A2); % swap between A2 and B2 to select pre- or de-emphasis
Ds = Ds/dcgain(Ds);

% MZT
T1 = 0.000075; % 75us
z1 = -exp(-1.0/(fs*T1));
p1 = 1+z1;

a0 = 1.0;
a1 = p1;
a2 = 0;

b0 = 1.0;
b1 = z1;
b2 = 0;

% swap between a1, b1 to select pre- or de-emphasis

Bmzt = [b0 a1 b2]
Amzt = [a0 b1 a2]

DzMZT = tf(Amzt, Bmzt, samplingtime);
DzMZT = DzMZT/dcgain(DzMZT);

%% Plot
wmin = 2 * pi * 20.0; % 20Hz
wmax = 2 * pi * ((fs/2.0) - (fs/2 - 20000)); 20kHz

figure(1);
bode(Ds, 'b', DzMZT, 'c', {wmin, wmax});
legend('Analog prototype', 'MZT 2nd order','location', 'northwest');
grid on;


There are few other ways to calculate the coefficients as like what mentioned here.

If you don't want to code then here are (75us) IIR coefficients for most common Fs:

44100
den = [ 1 -0.2430506562509079 -0.3653116688328392 ]
num = [ 0.226236781592716 0.1524695626369773 0.01293133068655965 ]

48000
den = [ 1 -0.2625656192085276 -0.3736509425970024 ]
num = [ 0.2102915623850396 0.1415200473180778 0.01197182849135262 ]

88200
den = [ 1 -0.3702185951814965 -0.419953874650389 ]
num = [ 0.1217100727211314 0.08132561501764404 0.006791842429339071 ]

96000
den = [ 1 -0.3813262768034237 -0.424764645791489 ]
num = [ 0.1125121591800709 0.07513002079134688 0.006266897433669414 ]

176400
den = [ 1 -0.4405600251706213 -0.4505357896180226 ]
num = [ 0.06328932872397297 0.04212236025403318 0.003492496233349943 ]

192000
den = [ 1 -0.4464885169088165 -0.4531262255096845 ]
num = [ 0.05834721577148291 0.03882109525679095 0.003216946553225094 ]

352800
den = [ 1 -0.4775710488916642 -0.4667412351991018 ]
num = [ 0.03239176683609103 0.02151798489625147 0.001777964176891494 ]

384000
den = [ 1 -0.4806342452619782 -0.4680860610215655 ]
num = [ 0.02982988806358219 0.01981315821469544 0.00163664743817871 ]


Magnitude error less than 0.1 dB (20Hz-20kHz)

Taken from this excellent RDS tutorial, and original source:

De-emphasis filter, $$H(s) = \frac {1} {RC.s + 1}$$, implemented as IIR filter via a bilinear transform:

bz, az = sig.bilinear(1, [75e-6, 1], fs=sample_rate)
x = sig.lfilter(bz, az, x)


It works as expected on this sample of US FM broadcast:

• One thing that you should keep in mind is that the bilinear transform maps $z=-1$ (which is Nyquist) to $s=j\infty$. So your LPF is gonna take a dive as we get up to Nyquist. Now if your sample rate is something like 44.1 kHz, that dive at high frequencies might be something you don't want. Then it may well be that a shelf filter with something that levels off at high frequencies might be better. The lower corner of the shelf is the same (whatever corresponds to 75 microseconds). But there should be a zero near $z=-1$ to level it off a little. Commented Jun 16, 2023 at 5:37
• @robertbristow-johnson, I added some plots, incl. the frequency response. My understanding was the whole channel was pre-emphasized (-/+ 125 kHz), not the L+R or L-R portions. Therefore, the filter b/w is -/+ 125 kHz and the very low response regions are past -/+ 100 kHz. Correct me if I'm wrong (it seems I'm).
– mins
Commented Jun 16, 2023 at 15:26
• what's your sample rate? Commented Jun 16, 2023 at 16:45
• @robertbristow-johnson: I've only a little experience, I've two code sections, which are different. In the first one the sample rate is 250k, the signal is FM-demodulated, de-emphasized, then decimated by 6 to get 41.66k. In the second one, samples at 250k are FM-demodulated, decimated by 6 and de-emphasized with a filter calculated for 250k. I used the first example to produce the plots.
– mins
Commented Jun 16, 2023 at 17:24
• With such a high sample rate, this frequency-warping effect of the bilinear transform can be neglected. I forgot about this de-emphasis happening to the entire multiplex FM signal (with the L-R bumped up to 38 kHz and other subbands). I thought it was just being done to the audio. So my 44.1 kHz sample rate was not realistic. But you can see at ±125 kHz that your LPF filter response that was doing a nice, predictable -6 dB/octave, that it takes a dive at 125 kHz. It's not a problem for you, but it sometime is a problem for other audio applications with slower sample rate. Commented Jun 16, 2023 at 17:26