Skip to content

Commit d95851d

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 1391978 commit d95851d

2 files changed

Lines changed: 226 additions & 1 deletion

File tree

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

src/audio/selector/tune/sof_selector_blobs.m

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
% Export configuration blobs for Selector
1+
% sof_selector_blobs - export configuration blobs for Selector
2+
%
3+
% This script is run without arguments. It exports a number of
4+
% configuration blobs for selector/micsel component. The first
5+
% category of blobs are for upmix and downmix between mono to
6+
% 7.1 channel audio formats.
7+
%
8+
% The second category is for duplicating stereo to four channels
9+
% for 2-way speaker crossover filter.
10+
%
211

312
% SPDX-License-Identifier: BSD-3-Clause
413
%
@@ -97,6 +106,39 @@ function sof_selector_blobs()
97106
write_blob(sel, "upmix_stereo_to_51");
98107
write_blob(sel, "upmix_stereo_to_71");
99108

109+
% Stereo to L,L,R,R
110+
sel.coeffs = [ 1 0 0 0 0 0 0 0 ; ...
111+
1 0 0 0 0 0 0 0 ; ...
112+
0 1 0 0 0 0 0 0 ; ...
113+
0 1 0 0 0 0 0 0 ; ...
114+
0 0 0 0 0 0 0 0 ; ...
115+
0 0 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+
write_blob(sel, "xover_selector_lr_to_llrr");
119+
120+
% Stereo to R,R,L,L
121+
sel.coeffs = [ 0 1 0 0 0 0 0 0 ; ...
122+
0 1 0 0 0 0 0 0 ; ...
123+
1 0 0 0 0 0 0 0 ; ...
124+
1 0 0 0 0 0 0 0 ; ...
125+
0 0 0 0 0 0 0 0 ; ...
126+
0 0 0 0 0 0 0 0 ; ...
127+
0 0 0 0 0 0 0 0 ; ...
128+
0 0 0 0 0 0 0 0 ];
129+
write_blob(sel, "xover_selector_lr_to_rrll");
130+
131+
% Stereo to L,R,L,R
132+
sel.coeffs = [ 1 0 0 0 0 0 0 0 ; ...
133+
0 1 0 0 0 0 0 0 ; ...
134+
1 0 0 0 0 0 0 0 ; ...
135+
0 1 0 0 0 0 0 0 ; ...
136+
0 0 0 0 0 0 0 0 ; ...
137+
0 0 0 0 0 0 0 0 ; ...
138+
0 0 0 0 0 0 0 0 ; ...
139+
0 0 0 0 0 0 0 0 ];
140+
write_blob(sel, "xover_selector_lr_to_lrlr");
141+
100142
sof_selector_paths(false);
101143
end
102144

0 commit comments

Comments
 (0)