Skip to content

Commit 8ffea5c

Browse files
committed
Audio: EQIIR/Selector: Add scripts to produce crossover blobs
This patch adds to script sof_selector_blobs.m export of stereo stream upmix to duplicated channels. The duplicated channels are then filtered with desired crossover characteristics. The added script for IIR EQ sof_example_lr4.m exports 4th order Linkwitz-Riley files for crossover low-pass and high-pass. The added example should be quite typical for laptop PCs. With larger speakers the frequency could be lower, with smaller higher. Also the crossover order could be changed to lower and higher. The 4th order should be quite typical. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
1 parent c48dcae commit 8ffea5c

File tree

2 files changed

+176
-0
lines changed

2 files changed

+176
-0
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
%% Design 4th order Linkwitz–Riley filter bank
2+
3+
% SPDX-License-Identifier: BSD-3-Clause
4+
%
5+
% Copyright (c) 2025, Intel Corporation.
6+
%
7+
% Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
8+
9+
function sof_example_lr4()
10+
11+
%% Common definitions
12+
fs = 48e3;
13+
fc = 2e3;
14+
sof_tools = '../../../../tools';
15+
tpath = fullfile(sof_tools, 'topology/topology2/include/components/eqiir');
16+
cpath = fullfile(sof_tools, 'ctl/ipc4/eq_iir');
17+
18+
sof_eq_paths(1);
19+
20+
%% --------------------------------------------------
21+
%% Example: Band-split 2ch to 4ch low and high bands
22+
%% --------------------------------------------------
23+
design_name = sprintf('xover_lr4_%dhz_lhlh_%dkhz', fc, round(fs/1000));
24+
blob_fn = fullfile(cpath, [design_name '.bin']);
25+
alsa_fn = fullfile(cpath, [design_name '.txt']);
26+
tplg_fn = fullfile(tpath, [design_name '.conf']);
27+
comment = 'LR4 filter bank coefficients';
28+
howto = 'cd src/audio/eq_iir/tune; octave sof_example_lr4.m';
29+
30+
% Design low-pass and high-pass filters
31+
eq_lo = lo_band_iir(fs, fc);
32+
eq_hi = hi_band_iir(fs, fc);
33+
34+
% Quantize and pack filter coefficients plus shifts etc.
35+
bq_lo = sof_eq_iir_blob_quant(eq_lo.p_z, eq_lo.p_p, eq_lo.p_k);
36+
bq_hi = sof_eq_iir_blob_quant(eq_hi.p_z, eq_hi.p_p, eq_hi.p_k);
37+
38+
% Build blob
39+
channels_in_config = 4; % Setup max 4 channels EQ
40+
assign_response = [0 1 0 1]; % Order: lo, hi, lo, hi
41+
num_responses = 2; % Two responses: lo, hi
42+
bm = sof_eq_iir_blob_merge(channels_in_config, ...
43+
num_responses, ...
44+
assign_response, ...
45+
[bq_lo bq_hi]);
46+
47+
% Pack and write file
48+
sof_eq_pack_export(bm, blob_fn, alsa_fn, tplg_fn, comment, howto)
49+
50+
%% --------------------------------------------------
51+
%% Example: Same but filters order is lo, lo, hi, hi
52+
%% --------------------------------------------------
53+
54+
design_name = sprintf('xover_lr4_%dhz_llhh_%dkhz', fc, round(fs/1000));
55+
blob_fn = fullfile(cpath, [design_name '.bin']);
56+
alsa_fn = fullfile(cpath, [design_name '.txt']);
57+
tplg_fn = fullfile(tpath, [design_name '.conf']);
58+
59+
assign_response = [0 0 1 1]; % Order: lo, lo, hi, hi
60+
num_responses = 2; % Two responses: lo, hi
61+
bm = sof_eq_iir_blob_merge(channels_in_config, ...
62+
num_responses, ...
63+
assign_response, ...
64+
[bq_lo bq_hi]);
65+
66+
% Pack and write file
67+
sof_eq_pack_export(bm, blob_fn, alsa_fn, tplg_fn, comment, howto)
68+
69+
%% ------------------------------------
70+
%% Done.
71+
%% ------------------------------------
72+
73+
sof_eq_paths(0);
74+
end
75+
76+
%% -------------------
77+
%% EQ design functions
78+
%% -------------------
79+
80+
function eq = lo_band_iir(fs, fc)
81+
82+
83+
%% Get defaults for equalizer design
84+
eq = sof_eq_defaults();
85+
eq.fs = fs;
86+
eq.enable_iir = 1;
87+
eq.iir_norm_type = 'peak';
88+
eq.iir_norm_offs_db = 0;
89+
90+
% Parametric EQs are PEQ_HP1, PEQ_HP2, PEQ_LP1, PEQ_LP2, PEQ_LS1,
91+
% PEQ_LS2, PEQ_HS1, PEQ_HS2 = 8, PEQ_PN2, PEQ_LP4, and PEQ_HP4.
92+
%
93+
% Parametric EQs take as second argument the cutoff frequency in Hz
94+
% and as second argument a dB value (can use 0 for LP2). The
95+
% Third argument is a Q-value (can use 0 for LP2).
96+
97+
% Two 2nd order butterworth low-pass filters for 4th order Linkwitz–Riley
98+
eq.peq = [ ...
99+
eq.PEQ_LP2 fc 0 0 ; ...
100+
eq.PEQ_LP2 fc 0 0 ; ...
101+
];
102+
103+
%% Design EQ
104+
eq = sof_eq_compute(eq);
105+
106+
%% Plot
107+
sof_eq_plot(eq);
108+
109+
end
110+
111+
function eq = hi_band_iir(fs, fc)
112+
113+
114+
%% Get defaults for equalizer design
115+
eq = sof_eq_defaults();
116+
eq.fs = fs;
117+
eq.enable_iir = 1;
118+
eq.iir_norm_type = 'peak';
119+
eq.iir_norm_offs_db = 0;
120+
121+
% Two 2nd order high-pass filters for 4th order Linkwitz–Riley
122+
eq.peq = [ ...
123+
eq.PEQ_HP2 fc 0 0 ; ...
124+
eq.PEQ_HP2 fc 0 0 ; ...
125+
];
126+
127+
%% Design EQ
128+
eq = sof_eq_compute(eq);
129+
130+
%% Plot
131+
sof_eq_plot(eq);
132+
133+
end
134+
135+
136+
137+
% Pack and write file common function for all exports
138+
function sof_eq_pack_export(bm, bin_fn, ascii_fn, tplg_fn, note, howto)
139+
140+
bp = sof_eq_iir_blob_pack(bm, 4); % IPC4
141+
142+
if ~isempty(bin_fn)
143+
sof_ucm_blob_write(bin_fn, bp);
144+
end
145+
146+
if ~isempty(ascii_fn)
147+
sof_alsactl_write(ascii_fn, bp);
148+
end
149+
150+
if ~isempty(tplg_fn)
151+
sof_tplg2_write(tplg_fn, bp, 'IIR', note, howto);
152+
end
153+
154+
end

src/audio/selector/tune/sof_selector_blobs.m

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,28 @@ function sof_selector_blobs()
9797
write_blob(sel, "upmix_stereo_to_51");
9898
write_blob(sel, "upmix_stereo_to_71");
9999

100+
% Stereo to L,L,R,R
101+
sel.coeffs = [ 1 0 0 0 0 0 0 0 ; ...
102+
1 0 0 0 0 0 0 0 ; ...
103+
0 1 0 0 0 0 0 0 ; ...
104+
0 1 0 0 0 0 0 0 ; ...
105+
0 0 0 0 0 0 0 0 ; ...
106+
0 0 0 0 0 0 0 0 ; ...
107+
0 0 0 0 0 0 0 0 ; ...
108+
0 0 0 0 0 0 0 0 ];
109+
write_blob(sel, "xover_selector_lr_to_llrr");
110+
111+
% Stereo to L,R,L,R
112+
sel.coeffs = [ 1 0 0 0 0 0 0 0 ; ...
113+
0 1 0 0 0 0 0 0 ; ...
114+
1 0 0 0 0 0 0 0 ; ...
115+
0 1 0 0 0 0 0 0 ; ...
116+
0 0 0 0 0 0 0 0 ; ...
117+
0 0 0 0 0 0 0 0 ; ...
118+
0 0 0 0 0 0 0 0 ; ...
119+
0 0 0 0 0 0 0 0 ];
120+
write_blob(sel, "xover_selector_lr_to_lrlr");
121+
100122
sof_selector_paths(false);
101123
end
102124

0 commit comments

Comments
 (0)