Skip to content

Commit

Permalink
Tools: Tune: Update crossover configurations export for tplg2
Browse files Browse the repository at this point in the history
The script example_crossover.m is converted to export 2, 3, and 4
way crossover configurations for tplg1 and tplg2.

The crossover parameters like number of sinks, pipeline ids of
sinks, sample rate, and band limits are added to generate blob
filenames.

The changes include fixes for running the script with Matlab in
addition to Octave.

Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
  • Loading branch information
singalsu authored and kv2019i committed Aug 9, 2023
1 parent 681135c commit 39edc7a
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 65 deletions.
37 changes: 9 additions & 28 deletions tools/tune/crossover/crossover_build_blob.m
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
function blob8 = crossover_build_blob(blob_struct, endian)

%% Settings
bits_R = 32; %Q2.30
qy_R = 30;
function blob8 = crossover_build_blob(blob_struct, endian, ipc_ver)

if nargin < 2
endian = 'little'
endif
endian = 'little';
end

if nargin < 3
ipc_ver = 3;
end

%% Shift values for little/big endian
switch lower(endian)
Expand All @@ -21,7 +21,7 @@
%% Build Blob
% refer to sof/src/include/user/crossover.h for the config struct.
data_size = 4 * (2 + 4 + 4 + numel(blob_struct.all_coef));
[abi_bytes, abi_size] = crossover_get_abi(data_size);
[abi_bytes, abi_size] = get_abi(data_size, ipc_ver);

blob_size = data_size + abi_size;
blob8 = uint8(zeros(1, blob_size));
Expand Down Expand Up @@ -49,7 +49,7 @@
j=j+4;
end

endfunction
end

function bytes = word2byte(word, sh)
bytes = uint8(zeros(1,4));
Expand All @@ -58,22 +58,3 @@
bytes(3) = bitand(bitshift(word, sh(3)), 255);
bytes(4) = bitand(bitshift(word, sh(4)), 255);
end

function [bytes, nbytes] = crossover_get_abi(setsize)

%% Return current SOF ABI header
%% Use sof-ctl to write ABI header into a file
abifn = 'crossover_get_abi.bin';
cmd = sprintf('sof-ctl -g %d -b -o %s', setsize, abifn);
system(cmd);

%% Read file and delete it
fh = fopen(abifn, 'r');
if fh < 0
error("Failed to get ABI header. Is sof-ctl installed?");
end
[bytes, nbytes] = fread(fh, inf, 'uint8');
fclose(fh);
delete(abifn);

end
2 changes: 1 addition & 1 deletion tools/tune/crossover/crossover_coef_quant.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

addpath ./../eq

if length(lowpass) != length(highpass)
if length(lowpass) ~= length(highpass)
error("length of lowpass and highpass array do not match");
end

Expand Down
12 changes: 6 additions & 6 deletions tools/tune/crossover/crossover_gen_coefs.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function crossover = crossover_gen_coefs(fs, fc_low, fc_mid, fc_high);
function crossover = crossover_gen_coefs(fs, fc_low, fc_mid, fc_high)

addpath ./../eq/
switch nargin
Expand All @@ -10,19 +10,19 @@
rmpath ./../eq
end

function crossover_2way = crossover_generate_2way(fs, fc);
function crossover_2way = crossover_generate_2way(fs, fc)
crossover_2way.lp = [lp_iir(fs, fc, 0)];
crossover_2way.hp = [hp_iir(fs, fc, 0)];
end

function crossover_3way = crossover_generate_3way(fs, fc_low, fc_high);
function crossover_3way = crossover_generate_3way(fs, fc_low, fc_high)
% Duplicate one set of coefficients. The duplicate set will be used to merge back the
% output that is out of phase.
crossover_3way.lp = [lp_iir(fs, fc_low, 0) lp_iir(fs, fc_high, 0) lp_iir(fs, fc_high, 0)];
crossover_3way.hp = [hp_iir(fs, fc_low, 0) hp_iir(fs, fc_high, 0) hp_iir(fs, fc_high, 0)];
end

function crossover_4way = crossover_generate_4way(fs, fc_low, fc_mid, fc_high);
function crossover_4way = crossover_generate_4way(fs, fc_low, fc_mid, fc_high)
crossover_4way.lp = [lp_iir(fs, fc_low, 0) lp_iir(fs, fc_mid, 0) lp_iir(fs, fc_high, 0)];
crossover_4way.hp = [hp_iir(fs, fc_low, 0) hp_iir(fs, fc_mid, 0) hp_iir(fs, fc_high, 0)];
end
Expand Down Expand Up @@ -54,7 +54,7 @@
b = [1 - cutoff, 0, 0];
a = [1, 0, 0];
return;
endif
end

% Compute biquad coefficients for highpass filter
resonance = max(0.0, resonance); % can't go negative
Expand Down Expand Up @@ -90,7 +90,7 @@
b = [cutoff, 0, 0];
a = [1, 0, 0];
return;
endif
end

% Compute biquad coefficients for lowpass filter
resonance = max(0.0, resonance); % can't go negative
Expand Down
8 changes: 5 additions & 3 deletions tools/tune/crossover/crossover_plot_freq.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
% Then to plot the transferfn for y1 we would create a filter such as:
% x(n) ---> LR4 LO-PASS --> LR4 LO-PASS --> y1(n)

figure;

f = linspace(1, fs/2, 500);

if num_sinks == 2
Expand Down Expand Up @@ -63,11 +65,11 @@
end
end

function [h12, w] = cascade_bqs_fr(f, fs, varargin);
function [h12, w] = cascade_bqs_fr(f, fs, varargin)
bq1 = varargin{1};
bq2 = varargin{2};
[h1, w] = freqz(bq1.b, bq1.a, f, fs);
[h2, w] = freqz(bq2.b, bq2.a, f, fs);
[h1, w1] = freqz(bq1.b, bq1.a, f, fs);
[h2, w2] = freqz(bq2.b, bq2.a, f, fs);
h12 = h1.*h2;
for i=3:length(varargin)
bq = varargin{i};
Expand Down
130 changes: 103 additions & 27 deletions tools/tune/crossover/example_crossover.m
Original file line number Diff line number Diff line change
@@ -1,54 +1,130 @@
function example_crossover();
function example_crossover()

% Set the parameters here
tplg_fn = "../../topology/topology1/m4/crossover_coef_default.m4" % Control Bytes File
% Use those files with sof-ctl to update the component's configuration
blob_fn = "../../ctl/crossover_coef.blob" % Blob binary file
alsa_fn = "../../ctl/crossover_coef.txt" % ALSA CSV format file
addpath ./../common

% Sampling Frequency and Frequency cut-offs for crossover
cr.fs = 48e3;
cr.fc_low = 200;
cr.fc_med = 1000;
cr.fc_high = 3000;

% 2 way crossover, pipeline IDs of sinks are 1 and 2 (IPC3)
% and component output pins 0 and 1 (IPC4)
cr.num_sinks = 2;
cr.sinks = [1 2];
export_crossover(cr);
cr.sinks = [0 1];
export_crossover(cr);

% 3 way crossover, pipeline IDs of sinks are 1 - 3
cr.num_sinks = 3;
cr.sinks = [1 2 3];
export_crossover(cr);
cr.sinks = [0 1 2];
export_crossover(cr);

% 4 way crossover, pipeline IDs of sinks are 1 - 4
cr.num_sinks = 4;
cr.sinks = [1 2 3 4];
export_crossover(cr);
cr.sinks = [0 1 2 3];
export_crossover(cr);

rmpath ./../common

end

function export_crossover(cr)

endian = "little";
tpath1 = '../../topology/topology1/m4/crossover';
tpath2 = '../../topology/topology2/include/components/crossover';
ctlpath = '../../ctl/ipc3';

% Sampling Frequency and Frequency cut-offs for crossover
fs = 48e3;
fc_low = 200;
fc_med = 1000;
fc_high = 3000;
str_way = sprintf('%dway', cr.num_sinks);
str_freq = get_str_freq(cr);
str_pid = get_str_pid(cr);

% Set the parameters here
tplg1_fn = sprintf('%s/coef_%s_%s_%s.m4', tpath1, str_way, str_freq, str_pid); % Control Bytes File
tplg2_fn = sprintf('%s/coef_%s_%s_%s.conf', tpath2, str_way, str_freq, str_pid);
% Use those files with sof-ctl to update the component's configuration
blob_fn = sprintf('%s/crossover_coef_%dway.blob', ctlpath, cr.num_sinks); % Blob binary file
alsa_fn = sprintf('%s/crossover_coef_%dway.txt', ctlpath, cr.num_sinks); % ALSA CSV format file

% 4 way crossover
num_sinks = 4;
% This array is an example on how to assign a buffer from pipeline 1 to output 0,
% buffer from pipeline 2 to output 1, etc...
% Refer to sof/src/include/user/crossover.h for more information on assigning
% buffers to outputs.
assign_sinks = zeros(1, 4);
assign_sinks(1) = 1; % sink[0]
assign_sinks(2) = 2; % sink[1]
assign_sinks(3) = 3; % sink[2]
assign_sinks(4) = 4; % sink[3]
assign_sinks(1:cr.num_sinks) = cr.sinks;

% Generate zeros, poles and gain for crossover with the given frequencies
%crossover = crossover_gen_coefs(fs, fc_low); % 2 way crossover
% crossover = crossover_gen_coefs(fs, fc_low, fc_med); % 3 way crossover
crossover = crossover_gen_coefs(fs, fc_low, fc_med, fc_high); % 4 way crossover
switch cr.num_sinks
case 2
crossover = crossover_gen_coefs(cr.fs, cr.fc_low); % 2 way crossover
case 3
crossover = crossover_gen_coefs(cr.fs, cr.fc_low, cr.fc_med); % 3 way crossover
case 4
crossover = crossover_gen_coefs(cr.fs, cr.fc_low, cr.fc_med, cr.fc_high); % 4 way crossover
otherwise
error('Illegal number of sinks %d\n', num_sinks);
end

% Convert the [a,b] coefficients to values usable with SOF
crossover_bqs = crossover_coef_quant(crossover.lp, crossover.hp);

% Convert coefficients to sof_crossover_config struct
config = crossover_generate_config(crossover_bqs, num_sinks, assign_sinks);
config = crossover_generate_config(crossover_bqs, cr.num_sinks, assign_sinks);

% Convert struct to binary blob
blob8 = crossover_build_blob(config, endian);
blob8 = crossover_build_blob(config, endian, 3);
blob8_ipc4 = crossover_build_blob(config, endian, 4);

% Generate output files
addpath ./../common

tplg_write(tplg_fn, blob8, "CROSSOVER");
mkdir_check(tpath1);
mkdir_check(tpath2);
mkdir_check(ctlpath);
tplg_write(tplg1_fn, blob8, "CROSSOVER");
tplg2_write(tplg2_fn, blob8_ipc4, "crossover_config", 'Exported Control Bytes');
blob_write(blob_fn, blob8);
alsactl_write(alsa_fn, blob8);

% Plot Magnitude and Phase Response of each sink
crossover_plot_freq(crossover.lp, crossover.hp, fs, num_sinks);
rmpath ./../common
crossover_plot_freq(crossover.lp, crossover.hp, cr.fs, cr.num_sinks);

end

% Frequencies part for filename
function str = get_str_freq(cr)

switch cr.num_sinks
case 2
str = sprintf('%d_%d', cr.fs, cr.fc_low);
case 3
str = sprintf('%d_%d_%d', cr.fs, cr.fc_low, cr.fc_med);
case 4
str = sprintf('%d_%d_%d_%d', cr.fs, cr.fc_low, cr.fc_med, cr.fc_high);
end

end

% Pipeline IDs part of filename
function str = get_str_pid(cr)

str = sprintf('%d', cr.sinks(1));
for i = 2:cr.num_sinks
str = sprintf('%s_%d', str, cr.sinks(i));
end

end

% Check if directory exists, avoid warning print for existing
function mkdir_check(new_dir)

if ~exist(new_dir, 'dir')
mkdir(new_dir);
end

endfunction
end

0 comments on commit 39edc7a

Please sign in to comment.