Parallel equalizer, working for boosts but not cuts I am trying to replicate the behaviour of a parallel equalizer.

The approach is to use a parallel sum of bandpass filters like so:

$Response = 1 + bandpass1 * (gain1 - 1) + bandpass2 * (gain2 - 1) + ...$

Boosts and cuts are separately grouped and run in series.

I have successfully got the model working for two boosts in parallel. But running two cuts in parallel produces an unexpected result.

The figure below illustrates the problem. The thick blue line illustrates two boosts in parallel (thin orange and red line show the individual boosts that are being combined). The thick purple line illustrates two cuts in parallel (thin green and light-blue line show individual cuts that have been combined). As can be seen the individual cuts are a mirror image of the individual boosts but the parallel sum doesn't work for cuts.



Does anyone know what I'm missing so that that the thick purple line is a mirror image of the think blue line?

Here as my matlab code:

% boost 1
f1 = 176;
g1 = db2gain(12);
q1 = 1;

% boost 2
f2 = 672;
g2 = db2gain(12);
q2 = 1;

% cut 1
f3 = 176;
q3 = 1;
g3 = db2gain(-12);

% cut 2
f4 = 672;
g4 = db2gain(-12);
q4 = 1;

% bandpasses for boosts
[b1, a1] = bandpass(f1, proportionalQ(q1, g1));
[b2, a2] = bandpass(f2, proportionalQ(q2, g2));

% bandpasses for cuts
[b3, a3] = bandpass(f3, proportionalQ(q3, g3));
[b4, a4] = bandpass(f4, proportionalQ(q4, g4));

f = logspace(log10(20), log10(20000), 10000);

r1 = response(b1, a1, f); % bandpass response for boost 1
r2 = response(b2, a2, f); % bandpass response for boost 2
r3 = response(b3, a3, f); % bandpass response for cut 1
r4 = response(b4, a4, f); % bandpass response for cut 2

% run the analog bandpass filters in parallel as two boosts (OK)...
p1 = 1 + r1 .* (g1 - 1) + r2 .* (g2 - 1);

% run the analog bandpass filters in parallel as two cuts (NOT OK)...
p2 = 1 + r3 .* (g3 - 1) + r4 .* (g4 - 1);

figure;

semilogx(f, response2db(p1), 'LineWidth', 2); % parallel boosts (OK)

hold on;

semilogx(f, response2db(1 + r1 .* (g1 - 1))); % individual boost plot 1
semilogx(f, response2db(1 + r2 .* (g2 - 1))); % individual boost plot 2

semilogx(f, response2db(p2), 'LineWidth', 2); % parallel cuts (NOT OK)

semilogx(f, response2db(1 + r3 .* (g3 - 1))); % individual cut plot 1
semilogx(f, response2db(1 + r4 .* (g4 - 1))); % individual cut plot 2

xlabel('Frequency');
ylabel('Magnitude (dB)');

axis([20, 8000, -16, 16]);
grid;

hold off;


bandpass.m

function [b, a] = bandpass(f, q)
    b = [0, f / q, 0];
    a = [f * f, f / q, 1];
end


response2db.m

function [db] = response2db(r)
   db = 20 .* log10(abs(r));
end


db2gain.m

function [g] = db2gain(db)
    g = 10 .^ (db ./ 20);
end


proportionalQ.m

function [q] = proportionalQ(q, g)
    q = q .* sqrt(g);
end


response.m

function [r] = response(b, a, f)
    s = 1i .* f;

    r = (b(1) + s .* b(2) + s .* s .* b(3)) ...
        ./ (a(1) + s .* a(2) + s .* s .* a(3));
end
Cuts can simply be derived from boosts like so: p2 = 1 / p1

This is intuitive as this is how you would invert a bandshelf.

However, I'm not sure it leads to an elegant equation as inverting the sum of filters and recovering a rational filter expression might be messy, so there's potential a better way.