Skip to content

Commit 6f3bc03

Browse files
singalsukv2019i
authored andcommitted
Audio: Sound Dose: Add Octave script to export A-weight filter
This patch adds script sof_sound_dose_time_domain_filters.m that exports IIR and FIR coefficients to approximate A-weight function. The current choice is IIR only for lower MCPS load. The script sof_sound_dose_blobs.m creates a few control blobs to test the sound_dose component. A simple script sof_sound_dose_ref.m to compute dBFS and MEL for a wav file is added to compare with firmware reported values. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> (cherry picked from commit f908a2f)
1 parent 1db74bb commit 6f3bc03

4 files changed

Lines changed: 335 additions & 1 deletion

File tree

src/audio/eq_iir/tune/sof_export_c_eq_uint32t.m

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
function sof_export_c_eq_uint32t(fn, blob8, vn, justeq)
1+
function sof_export_c_eq_uint32t(fn, blob8, vn, justeq, howto)
22

33
% sof_export_c_eq_uint32t(fn, blob8, vn)
44
%
@@ -8,6 +8,7 @@ function sof_export_c_eq_uint32t(fn, blob8, vn, justeq)
88
% blob8 - blob data from EQ design
99
% vn - variable name
1010
% justeq - export just the EQ wihtout ABI headers for direct use in FW
11+
% howto - add a comment how the header file was created
1112

1213
% SPDX-License-Identifier: BSD-3-Clause
1314
%
@@ -26,6 +27,12 @@ function sof_export_c_eq_uint32t(fn, blob8, vn, justeq)
2627
fprintf(fh, ' * Copyright(c) %s Intel Corporation.\n', year);
2728
fprintf(fh, ' */\n');
2829
fprintf(fh, '\n');
30+
31+
if nargin == 5
32+
fprintf(fh, '/* Created %s with command:\n', datestr(now, 'yyyy-mm-dd'));
33+
fprintf(fh, ' * %s\n */\n\n', howto);
34+
end
35+
2936
fprintf(fh, '#include <stdint.h>\n\n');
3037

3138
% Drop 8 bytes TLV header
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
% Export configuration blobs for Sound Dose
2+
3+
% SPDX-License-Identifier: BSD-3-Clause
4+
%
5+
% Copyright (c) 2025, Intel Corporation.
6+
7+
function sof_sound_dose_blobs()
8+
9+
% Common definitions
10+
sof_tools = '../../../../tools';
11+
sof_tplg = fullfile(sof_tools, 'topology');
12+
fn.tpath = fullfile(sof_tplg, 'topology2/include/components/sound_dose');
13+
str_comp = "sound_dose_config";
14+
str_comment = "Exported with script sof_sound_dose_blobs.m";
15+
str_howto = "cd src/audio/sound_dose/tune; octave sof_sound_dose_blobs.m";
16+
bytes_control_param_id = 202;
17+
18+
% Set the parameters here
19+
sof_tools = '../../../../tools';
20+
sof_tplg = fullfile(sof_tools, 'topology');
21+
sof_tplg_sound_dose = fullfile(sof_tplg, 'topology2/include/components/sound_dose');
22+
sof_ctl_sound_dose = fullfile(sof_tools, 'ctl/ipc4/sound_dose');
23+
24+
sof_sound_dose_paths(true);
25+
26+
blob8 = sof_sound_dose_build_blob([10000 0], 0);
27+
tplg2_fn = sprintf("%s/setup_sens_100db.conf", sof_tplg_sound_dose);
28+
sof_tplg2_write(tplg2_fn, blob8, str_comp, str_comment, str_howto);
29+
sof_alsactl_write([sof_ctl_sound_dose "/setup_sens_100db.txt"], blob8);
30+
31+
blob8 = sof_sound_dose_build_blob([0 0], 0);
32+
tplg2_fn = sprintf("%s/setup_sens_0db.conf", sof_tplg_sound_dose);
33+
sof_tplg2_write(tplg2_fn, blob8, str_comp, str_comment, str_howto);
34+
sof_alsactl_write([sof_ctl_sound_dose "/setup_sens_0db.txt"], blob8);
35+
36+
blob8 = sof_sound_dose_build_blob([0 0], 1);
37+
tplg2_fn = sprintf("%s/setup_vol_0db.conf", sof_tplg_sound_dose);
38+
sof_tplg2_write(tplg2_fn, blob8, str_comp, str_comment, str_howto);
39+
sof_alsactl_write([sof_ctl_sound_dose "/setup_vol_0db.txt"], blob8);
40+
41+
blob8 = sof_sound_dose_build_blob([-1000 0], 1);
42+
tplg2_fn = sprintf("%s/setup_vol_-10db.conf", sof_tplg_sound_dose);
43+
sof_tplg2_write(tplg2_fn, blob8, str_comp, str_comment, str_howto);
44+
sof_alsactl_write([sof_ctl_sound_dose "/setup_vol_-10db.txt"], blob8);
45+
46+
blob8 = sof_sound_dose_build_blob([-1000 0], 2);
47+
tplg2_fn = sprintf("%s/setup_gain_-10db.conf", sof_tplg_sound_dose);
48+
sof_tplg2_write(tplg2_fn, blob8, str_comp, str_comment, str_howto);
49+
sof_alsactl_write([sof_ctl_sound_dose "/setup_gain_-10db.txt"], blob8);
50+
51+
blob8 = sof_sound_dose_build_blob([0 0], 2);
52+
tplg2_fn = sprintf("%s/setup_gain_0db.conf", sof_tplg_sound_dose);
53+
sof_tplg2_write(tplg2_fn, blob8, str_comp, str_comment, str_howto);
54+
sof_alsactl_write([sof_ctl_sound_dose "/setup_gain_0db.txt"], blob8);
55+
56+
blob8 = sof_sound_dose_build_blob([], bytes_control_param_id);
57+
tplg2_fn = sprintf("%s/setup_data_init.conf", sof_tplg_sound_dose);
58+
sof_tplg2_write(tplg2_fn, blob8, str_comp, str_comment, str_howto);
59+
60+
sof_sound_dose_paths(false);
61+
end
62+
63+
function sof_sound_dose_paths(enable)
64+
65+
common = '../../../../tools/tune/common';
66+
if enable
67+
addpath(common);
68+
else
69+
rmpath(common);
70+
end
71+
end
72+
73+
function blob8 = sof_sound_dose_build_blob(param_values, blob_param_id)
74+
75+
blob_type = 0;
76+
data_length = length(param_values);
77+
data_size = 2 * data_length;
78+
ipc_ver = 4;
79+
[abi_bytes, abi_size] = sof_get_abi(data_size, ipc_ver, blob_type, blob_param_id);
80+
blob_size = data_size + abi_size;
81+
blob8 = uint8(zeros(1, blob_size));
82+
blob8(1:abi_size) = abi_bytes;
83+
j = abi_size + 1;
84+
for i = 1:data_length
85+
blob8(j:j+1) = short2byte(int16(param_values(i)));
86+
j=j+2;
87+
end
88+
end
89+
90+
function bytes = short2byte(word)
91+
sh = [0 -8];
92+
bytes = uint8(zeros(1,2));
93+
bytes(1) = bitand(bitshift(word, sh(1)), 255);
94+
bytes(2) = bitand(bitshift(word, sh(2)), 255);
95+
end
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
% Compute MEL every 1s
2+
tc = 1.0;
3+
4+
% Load sound clip
5+
sound_clip = "/usr/share/sounds/alsa/Front_Right.wav";
6+
[x1, fs] = audioread(sound_clip);
7+
if size(x1, 2) == 1
8+
x1 = repmat(x1, 1, 2);
9+
end
10+
sx = size(x1);
11+
num_frames = sx(1);
12+
num_channels = sx(2);
13+
14+
% load A-weight filters
15+
load sof_sound_dose_time_domain_filters.mat
16+
17+
% Filter with IIR, FIR
18+
x2 = filter(b_iir, a_iir, x1);
19+
x3 = filter(b_fir, 1, x2);
20+
21+
%figure
22+
%plot(x1)
23+
24+
%figure
25+
%plot(x3)
26+
27+
% compute RMS level in 1s chunks
28+
np = 1;
29+
nc = tc * fs;
30+
num_chunks = floor(num_frames/nc);
31+
l_dbfs_all = zeros(num_chunks, num_channels);
32+
mel_all = zeros(num_chunks, 1);
33+
34+
dbfs_offs = 3.01;
35+
dbfs_offs_weight_filters = 3;
36+
i1 = 1;
37+
for n = 1:num_chunks
38+
sum = 0;
39+
i2 = i1 + nc - 1;
40+
for ch = 1:num_channels
41+
y = x3(i1:i2, ch);
42+
sum = sum + mean(y.^2);
43+
l_dbfs = 20*log10(sqrt(mean(y.^2))) + dbfs_offs + dbfs_offs_weight_filters;
44+
l_dbfs_all(np, ch) = l_dbfs;
45+
end
46+
mel = 10*log10(sum) + dbfs_offs_weight_filters;
47+
if num_channels > 1
48+
mel = mel - 1.5 * num_channels;
49+
end
50+
mel_all(np) = mel;
51+
i1 = i1 + nc;
52+
np = np + 1;
53+
end
54+
55+
figure(1)
56+
plot(l_dbfs_all)
57+
grid on
58+
xlabel('Time (s)');
59+
ylabel('Level (dBSFS)');
60+
61+
figure(2)
62+
plot(mel_all)
63+
grid on
64+
xlabel('Time (s)');
65+
ylabel('MEL (dB)');
66+
67+
mel_all
68+
69+
mel_all_rnd = round(mel_all)
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
% Export time domain IIR and FIR filter to apply A-weight for sound dose.
2+
%
3+
% Usage:
4+
% sof_sound_dose_time_domain_filters()
5+
6+
% SPDX-License-Identifier: BSD-3-Clause
7+
%
8+
% Copyright (c) 2025, Intel Corporation.
9+
10+
function sof_sound_dose_time_domain_filters(fs)
11+
12+
if exist('OCTAVE_VERSION', 'builtin')
13+
pkg load signal;
14+
end
15+
16+
sof_sound_dose_time_domain_filters_for_rate(48e3);
17+
sof_sound_dose_time_domain_filters_for_rate(44.1e3);
18+
19+
end
20+
21+
function sof_sound_dose_time_domain_filters_for_rate(fs)
22+
23+
sof_sound_dose = '../../sound_dose';
24+
sof_ctl = '../../../../tools/ctl';
25+
sound_dose_paths(true);
26+
27+
28+
fs_str = sprintf('%dk', round(fs/1000));
29+
fir_str = sprintf('sound_dose_fir_%s', fs_str);
30+
iir_str = sprintf('sound_dose_iir_%s', fs_str);
31+
prm.fir_coef_fn = fullfile(sof_sound_dose, [fir_str '.h']);
32+
prm.iir_coef_fn = fullfile(sof_sound_dose, [iir_str '.h']);
33+
prm.fir_blob_fn = fullfile(sof_ctl, ['ipc4/eq_fir/' fir_str '.txt']);
34+
prm.iir_blob_fn = fullfile(sof_ctl, ['ipc4/eq_iir/' iir_str '.txt']);
35+
prm.fir_blob_vn = fir_str;
36+
prm.iir_blob_vn = iir_str;
37+
prm.ipc_ver = 4;
38+
eq = sound_dose_filters(fs);
39+
export_filters(eq, prm);
40+
41+
sound_dose_paths(false);
42+
end
43+
44+
function export_filters(eq, prm)
45+
46+
b_fir = 1;
47+
b_iir = 1;
48+
a_iir = 1;
49+
howto = 'cd src/audio/sound_dose/tune; octave --quiet --no-window-system sof_sound_dose_time_domain_filters.m';
50+
51+
% Export FIR
52+
if eq.enable_fir
53+
bq = sof_eq_fir_blob_quant(eq.b_fir);
54+
channels_in_config = 2; % Setup max 2 channels EQ
55+
assign_response = [0 0]; % Same response for L and R
56+
num_responses = 1; % One response
57+
bm = sof_eq_fir_blob_merge(channels_in_config, ...
58+
num_responses, ...
59+
assign_response, ...
60+
bq);
61+
bp = sof_eq_fir_blob_pack(bm, prm.ipc_ver);
62+
sof_export_c_eq_uint32t(prm.fir_coef_fn, bp, prm.fir_blob_vn, 0, howto);
63+
sof_alsactl_write(prm.fir_blob_fn, bp);
64+
b_fir = eq.b_fir;
65+
end
66+
67+
% Export IIR
68+
if eq.enable_iir
69+
coefq = sof_eq_iir_blob_quant(eq.p_z, eq.p_p, eq.p_k);
70+
channels_in_config = 2; % Setup max 2 channels EQ
71+
assign_response = [0 0]; % Same response for L and R
72+
num_responses = 1; % One response
73+
coefm = sof_eq_iir_blob_merge(channels_in_config, ...
74+
num_responses, ...
75+
assign_response, ...
76+
coefq);
77+
coefp = sof_eq_iir_blob_pack(coefm, prm.ipc_ver);
78+
sof_export_c_eq_uint32t(prm.iir_coef_fn, coefp, prm.iir_blob_vn, 0, howto);
79+
sof_alsactl_write(prm.iir_blob_fn, coefp);
80+
[b_iir, a_iir] = zp2tf( eq.p_z, eq.p_p, eq.p_k);
81+
end
82+
83+
% Export mat file
84+
save sof_sound_dose_time_domain_filters.mat b_fir b_iir a_iir;
85+
86+
end
87+
88+
function eq = sound_dose_filters(fs)
89+
90+
np = 200;
91+
f_hi = 0.95 * fs/2;
92+
fv = logspace(log10(10), log10(f_hi), np);
93+
ref_a_weight = func_a_weight_db(fv);
94+
95+
eq = sof_eq_defaults();
96+
eq.fs = fs;
97+
eq.enable_fir = 0;
98+
eq.enable_iir = 1;
99+
eq.iir_norm_type = '1k'; % At 1 kHz -3 dB
100+
eq.iir_norm_offs_db = -3;
101+
eq.fir_norm_type = '1k'; % At 1 kHz 0 dB, total -3 dB gain to avoid clip
102+
eq.fir_norm_offs_db = 0;
103+
eq.p_fmin = 10;
104+
eq.p_fmax = 30e3;
105+
106+
eq.fir_minph = 1;
107+
eq.fir_beta = 4;
108+
eq.fir_length = 31;
109+
eq.fir_autoband = 0;
110+
eq.fmin_fir = 300;
111+
eq.fmax_fir = f_hi;
112+
113+
eq.raw_f = fv;
114+
eq.raw_m_db = zeros(1, length(fv));
115+
eq.target_f = fv;
116+
eq.target_m_db = ref_a_weight;
117+
118+
eq.peq = [ ...
119+
eq.PEQ_HP1 12 0 0; ...
120+
eq.PEQ_HP1 20 0 0; ...
121+
eq.PEQ_HP2 280 0 0; ...
122+
eq.PEQ_PN2 245 -5.45 0.5; ...
123+
eq.PEQ_HS1 13000 -3 0; ...
124+
eq.PEQ_HS1 14000 -3 0; ...
125+
];
126+
127+
eq = sof_eq_compute(eq);
128+
sof_eq_plot(eq, 1);
129+
figure(3);
130+
axis([10 20e3 -60 10]);
131+
132+
133+
end
134+
135+
% See https://en.wikipedia.org/wiki/A-weighting
136+
% IEC 61672-1:2013 Electroacoustics - Sound level meters,
137+
% Part 1: Specifications. IEC. 2013.
138+
139+
function a_weight = func_a_weight_db(fv)
140+
a_weight = 20*log10(func_ra(fv)) - 20*log10(func_ra(1000));
141+
end
142+
143+
function y = func_ra(f)
144+
f2 = f.^2;
145+
f4 = f.^4;
146+
c1 = 12194^2;
147+
c2 = 20.6^2;
148+
c3 = 107.7^2;
149+
c4 = 737.9^2;
150+
c5 = 12194^2;
151+
y = c1 * f4 ./ ((f2 + c2).*sqrt((f2 + c3).*(f2 + c4).*(f2 + c5)));
152+
end
153+
154+
function sound_dose_paths(enable)
155+
switch enable
156+
case true
157+
addpath('../../eq_iir/tune');
158+
addpath('../../../../tools/tune/common');
159+
case false
160+
rmpath('../../eq_iir/tune');
161+
rmpath('../../../../tools/tune/common');
162+
end
163+
end

0 commit comments

Comments
 (0)