Skip to content

Commit

Permalink
Tools: Tune: dcblock: Create set of cut-off frequencies
Browse files Browse the repository at this point in the history
This patch modifies script example_dblock.m to create in
addition to existing default coefficients blob with parameter
R=0.98 a set of blobs those provide (-3 dB) cut-off frequencies
20, 30, 40, 50, 100, 200 Hz for 16 kHz and 48 kHz for various
usages. Human understandable parameters for blobs helps to select
the configuration for the needed dcblock usage.

The parameter value R for given frequencies is calculated
with iterative function dcblock_rval_calculate(). A closed
form equation might be possible to derive from the transfer
function. If such is found this function can be replaced with
quicker equation -- or with more advanced faster converging
iteration.

The topology blob export functions are also modified to add
comment line for exact build command.

Signed-off-by: Seppo Ingalsuo <[email protected]>
  • Loading branch information
singalsu authored and lgirdwood committed Oct 16, 2023
1 parent 64fcebb commit d3b7e54
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 11 deletions.
8 changes: 7 additions & 1 deletion tools/tune/common/tplg2_write.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function tplg2_write(fn, blob8, component, comment)
function tplg2_write(fn, blob8, component, comment, howto)

% SPDX-License-Identifier: BSD-3-Clause
%
Expand All @@ -9,6 +9,9 @@ function tplg2_write(fn, blob8, component, comment)
if nargin < 4
comment = 'Exported Control Bytes';
end
if nargin < 5
howto = [];
end

%% Check that blob length is multiple of 32 bits
n_blob = length(blob8);
Expand All @@ -23,6 +26,9 @@ function tplg2_write(fn, blob8, component, comment)
fh = fopen(fn, 'w');
nl = 8;
fprintf(fh, '# %s %s\n', comment, date());
if ~isempty(howto)
fprintf(fh, '# %s\n', howto);
end
fprintf(fh, 'Object.Base.data.\"%s\" {\n', component);
fprintf(fh, '\tbytes \"\n');
for i = 1:nl:n_blob
Expand Down
9 changes: 8 additions & 1 deletion tools/tune/common/tplg_write.m
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
function tplg_write(fn, blob8, name, comment)
function tplg_write(fn, blob8, name, comment, howto)

if nargin < 4
comment = 'Exported Control Bytes';
end
if nargin < 5
howto = [];
end

%% Pad blob length to multiple of four bytes
n_orig = length(blob8);
Expand All @@ -14,6 +17,9 @@ function tplg_write(fn, blob8, name, comment)
fh = fopen(fn, 'w');
nl = 8;
fprintf(fh, '# %s %s\n', comment, date());
if ~isempty(howto)
fprintf(fh, '# %s\n', howto);
end
fprintf(fh, 'CONTROLBYTES_PRIV(%s_priv,\n', name);
fprintf(fh, '` bytes "');
for i = 1:nl:n_new
Expand All @@ -33,5 +39,6 @@ function tplg_write(fn, blob8, name, comment)
end
fprintf(fh, ')\n');
fclose(fh);
fprintf('Blob size %d was written to file %s\n', n_new, fn);

end
108 changes: 99 additions & 9 deletions tools/tune/dcblock/example_dcblock.m
Original file line number Diff line number Diff line change
@@ -1,28 +1,69 @@
function example_dcblock()

% Default blob, about 150 Hz cut-off @ 48 kHz
prm.fc = [];
prm.fs = [];
prm.R_coeffs = [0.98, 0.98, 0.98, 0.98, 0.98, 0.98, 0.98, 0.98];
prm.id = "default";
dcblock_blob_calculate(prm)

% Generate a set configuration blobs for 16 and 48 kHz rate
% The selected high-pass cut-off frequencies are in range 20 - 200 Hz:
% 20, 30, 40, 50, 80, 100, and 200 Hz
%
% Select for applications one that is best tradeoff between DC level
% settle time and the lowest frequency that should pass without
% much attenuation. Cut-off frequency by definition is the point where
% the attenuation is 3.01 dB.

prm.R_coeffs = [];
for fs = [16e3 48e3]
for fc = [20 30 40 50 80 100 200]
prm.id = sprintf("%dhz_%dkhz", round(fc), round(fs / 1000));
prm.fs = fs;
prm.fc = fc;
dcblock_blob_calculate(prm)
end
end

end

function dcblock_blob_calculate(prm)

% Set the parameters here
tplg1_fn = "../../topology/topology1/m4/dcblock_coef_default.m4"; % Control Bytes File
tplg2_fn = "../../topology/topology2/include/components/dcblock/default.conf";
tplg1_fn = sprintf("../../topology/topology1/m4/dcblock_coef_%s.m4", prm.id); % Control Bytes File
tplg2_fn = sprintf("../../topology/topology2/include/components/dcblock/%s.conf", prm.id);
% Use those files with sof-ctl to update the component's configuration
blob3_fn = "../../ctl/ipc3/dcblock_coef.blob"; % Blob binary file
alsa3_fn = "../../ctl/ipc3/dcblock_coef.txt"; % ALSA CSV format file
blob4_fn = "../../ctl/ipc4/dcblock_coef.blob"; % Blob binary file
alsa4_fn = "../../ctl/ipc4/dcblock_coef.txt"; % ALSA CSV format file
blob3_fn = sprintf("../../ctl/ipc3/dcblock/coef_%s.blob", prm.id); % Blob binary file
alsa3_fn = sprintf("../../ctl/ipc3/dcblock/coef_%s.txt", prm.id); % ALSA CSV format file
blob4_fn = sprintf("../../ctl/ipc4/dcblock/coef_%s.blob", prm.id); % Blob binary file
alsa4_fn = sprintf("../../ctl/ipc4/dcblock/coef_%s.txt", prm.id); % ALSA CSV format file

endian = "little";
R_coeffs = [0.98, 0.98, 0.98, 0.98, 0.98, 0.98, 0.98, 0.98];

if isempty(prm.fc)
R_coeffs = prm.R_coeffs;
else
channels = 8;
R = dcblock_rval_calculate(prm.fs, prm.fc);
R_coeffs = R * ones(1, channels);
end

addpath ./../common

blob8 = dcblock_build_blob(R_coeffs, endian);
blob8_ipc4 = dcblock_build_blob(R_coeffs, endian, 4);

% Generate output files
tplg_write(tplg1_fn, blob8, "DCBLOCK");
tplg_write(tplg1_fn, blob8, "DCBLOCK", ...
"Exported with script example_dcblock.m", ...
"cd tools/tune/dcblock; octave example_dcblock.m");
blob_write(blob3_fn, blob8);
alsactl_write(alsa3_fn, blob8);

tplg2_write(tplg2_fn, blob8_ipc4, "dcblock_config", "Exported with script example_dcblock.m");
tplg2_write(tplg2_fn, blob8_ipc4, "dcblock_config", ...
"Exported with script example_dcblock.m" , ...
"cd tools/tune/dcblock; octave example_dcblock.m");
blob_write(blob4_fn, blob8_ipc4);
alsactl_write(alsa4_fn, blob8_ipc4);

Expand All @@ -36,3 +77,52 @@ function example_dcblock()
rmpath ./../common

end

% Finds with iterative search parameter R for given cutoff frequency
function R = dcblock_rval_calculate(fs, fc_target)

if (fc_target / fs < 10 / 48e3 || fc_target / fs > 1000 / 48e3)
error("Illegal fc_target");
end

h_target = 1 / sqrt(2); % -3.01 dB
R = 0.5;
R_step = 0.005;
sign = 1;
w = 2 * pi * fc_target / fs;
j = sqrt(-1);
z = exp(j * w);

% Iteration 0
h = (1 - z^-1) / (1 - R * z^-1);
err_prev = (h_target - abs(h))^2;
R = R + sign * R_step;

% Do more iterations
for n = 1 : 200
h = (1 - z^-1) / (1 - R * z^-1);
err = (h_target - abs(h))^2;
if (err > err_prev)
sign = -sign;
R_step = R_step / 2;
end
R = R + sign * R_step;
err_prev = err;
end

% Sane result?
if R < eps || R > 1 - eps
error("Calculate of R iteration failed");
end

% Sane high-pass function?
f = [1 fc_target fs/2];
b = [ 1 -1 ]; a = [ 1 -R ];
h = freqz(b, a, f, fs);
h_db = 20*log10(abs(h));
err_hfc = abs(20*log10(1/sqrt(2)) - h_db(2));
if err_hfc > 0.01 || h_db(1) > -10 || h_db(3) < 0 || h_db(3) > 1
error("Failed high-pass response");
end

end

0 comments on commit d3b7e54

Please sign in to comment.