Tools: Tune: Improve EQ blob decode scripts

This patch adds user friendly function eq_blob_plot(file) to
plot the IIR or FIR equalizer response that will be applied for
each channel.

The IIR and FIR decoders are modified to better support the higher
level script.

The copyright texts in impacted scripts are are updated to short
BSD-3-Clause style.

Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
This commit is contained in:
Seppo Ingalsuo 2020-03-31 13:09:16 +03:00 committed by Liam Girdwood
parent 365ffbd02f
commit 594e031b6e
4 changed files with 294 additions and 128 deletions

View File

@ -0,0 +1,140 @@
function eq = eq_blob_plot(blobfn, eqtype, fs, f, doplot)
% eq = eq_blob_plot(blobfn, eqtype, fs, f, doplot)
%
% Plot frequency response of IIR or FIR EQ coefficients blob
%
% Inputs
%
% blobfn - filename of the blob
% eqtype - 'iir' or 'fir', if omitted done via string search from blobfn
% fs - sample rate, defaults to 48 kHz if omitted
% f - frequency vector
% dpplot
% SPDX-License-Identifier: BSD-3-Clause
%
% Copyright (c) 2016-2020, Intel Corporation. All rights reserved.
%
% Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
%% Handle input parameters
if nargin < 2
if findstr(blobfn, '_fir_')
eqtype = 'FIR';
else
eqtype = 'IIR';
end
end
if nargin < 3
fs = 48e3;
end
if nargin < 4 || isempty(f)
eq.f = logspace(log10(10),log10(fs/2), 1000);
else
% Freqz() needs two frequency points or more
if length(f) < 2
f_single = 1;
eq.f = [f f];
else
f_single = 0;
eq.f = f;
end
end
if nargin < 5
doplot = 1;
end
%% Read blob as 32 bit integers
blob = eq_blob_read(blobfn);
%% Group delay with grpdelay() is not working in Octave
do_group_delay = ~exist('OCTAVE_VERSION', 'builtin');
%% Decode and compute response
eq.m = [];
eq.gd = [];
switch lower(eqtype)
case 'fir'
hd = eq_fir_blob_decode(blob);
eq.m = zeros(length(eq.f), hd.channels_in_config);
for i = 1:hd.channels_in_config
teq = eq_fir_blob_decode(blob, hd.assign_response(i));
h = freqz(teq.b, 1, eq.f, fs);
eq.m(:,i) = 20*log10(abs(h));
if do_group_delay
gd = grpdelay(teq.b, 1, eq.f, fs) / fs;
eq.gd(:,i) = gd;
end
end
case 'iir'
hd = eq_iir_blob_decode(blob);
eq.m = zeros(length(eq.f), hd.channels_in_config);
for i = 1:hd.channels_in_config
teq = eq_iir_blob_decode(blob, hd.assign_response(i));
h = freqz(teq.b, teq.a, eq.f, fs);
eq.m(:,i) = 20*log10(abs(h));
if do_group_delay
gd = grpdelay(teq.b, teq.a, eq.f, fs) / fs;
eq.gd(:,i) = gd;
end
end
end
%% Optional plots
if doplot
eq.fh(1) = figure;
semilogx(eq.f, eq.m);
ymin = max(min(min(eq.m)), -60);
ymax = max(max(eq.m));
axis([10 round(fs/2/10)*10 ymin-3 ymax+3 ]);
grid on;
channel_legends(hd.channels_in_config);
xlabel('Frequency (Hz)');
ylabel('Amplitude (dB)');
title(blobfn, 'Interpreter', 'None');
if do_group_delay
eq.fh(2) = figure;
semilogx(eq.f, eq.gd * 1e6);
ax = axis();
axis([10 round(fs/2/10)*10 ax(3:4) ]);
grid on;
channel_legends(hd.channels_in_config);
xlabel('Frequency (Hz)');
ylabel('Group delay (us)');
title(blobfn, 'Interpreter', 'None');
end
end
%% Trim avay the duplicated frequency point
if f_single
eq.f = eq.f(1);
if do_group_delay
eq.gd = eq.gd(1);
end
end
end
%% Helper functions
function channel_legends(n)
switch n
case 2
legend('ch1', 'ch2');
case 4
legend('ch1', 'ch2', 'ch3', 'ch4');
case 6
legend('ch1', 'ch2', 'ch3', 'ch4', 'ch5', 'ch6');
case 8
legend('ch1', 'ch2', 'ch3', 'ch4', 'ch5', 'ch6', 'ch7', 'ch8');
otherwise
error('Illegal number of channels found');
end
end

View File

@ -0,0 +1,69 @@
function blob = eq_blob_read(blobfn, fntype)
% SPDX-License-Identifier: BSD-3-Clause
%
% Copyright (c) 2020, Intel Corporation. All rights reserved.
%
% Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
%% Use file suffix as type
if nargin < 2
idx = findstr(blobfn, '.');
fntype = blobfn(idx(end)+1:end);
end
%% Read the file
switch lower(fntype)
case 'bin'
fh = fopen(blobfn, 'rb');
tmp = fread(fh, inf, 'uint32');
fclose(fh);
case 'txt'
fh = fopen(blobfn, 'r');
tmp = fscanf(fh, '%u,', Inf);
fclose(fh);
case 'm4'
tmp = get_parse_m4(blobfn);
otherwise
error('Illegal file type, please give fntype argument');
end
blob = uint32(tmp);
end
%% Parse m4 topology blob
function blob = get_parse_m4(blobfn)
fh = fopen(blobfn, 'r');
% Ignore two lines from beginning
ln = fgets(fh);
ln = fgets(fh);
% Loop until end of file
n = 1;
ln = fgets(fh);
while ln ~= -1
idx = findstr(ln, '0x');
for i = 1:length(idx)
bytes(n) = hex2dec(ln(idx(i)+2:idx(i)+3));
n = n + 1;
end
ln = fgets(fh);
end
fclose(fh);
% Convert to 32 bit
n32 = round(length(bytes)/4);
blob = zeros(n32, 1);
for i = 1:n32
i8 = (i - 1)*4 + 1;
blob(i) = bytes(i8) * 2^0 ...
+ bytes(i8 + 1) * 2^8 ...
+ bytes(i8 + 2) * 2^16 ...
+ bytes(i8 + 3) * 2^24;
end
end

View File

@ -1,4 +1,4 @@
function eq = eq_fir_blob_decode(blobfn, resp_n, fs, do_plot)
function eq = eq_fir_blob_decode(blob, resp_n)
%% Decode a FIR EQ binary blob
%
@ -16,48 +16,22 @@ function eq = eq_fir_blob_decode(blobfn, resp_n, fs, do_plot)
% assign_response - vector of EQ indexes assigned to channels
% size - length in bytes
%%
% Copyright (c) 2016, Intel Corporation
% All rights reserved.
% SPDX-License-Identifier: BSD-3-Clause
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
% * Redistributions of source code must retain the above copyright
% notice, this list of conditions and the following disclaimer.
% * Redistributions in binary form must reproduce the above copyright
% notice, this list of conditions and the following disclaimer in the
% documentation and/or other materials provided with the distribution.
% * Neither the name of the Intel Corporation nor the
% names of its contributors may be used to endorse or promote products
% derived from this software without specific prior written permission.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.
% Copyright (c) 2016-2020, Intel Corporation. All rights reserved.
%
% Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
%
if nargin < 4
do_plot = 0;
end
if nargin < 3
fs = 48e3;
end
if nargin < 2
resp_n = 0;
verbose = 1;
resp_n = [];
else
verbose = 0;
end
%% Get ABI information
[abi_bytes, nbytes_abi] = eq_get_abi(0);
%% Defaults
eq.b = [];
eq.a = [];
@ -65,13 +39,17 @@ eq.channels_in_config = 0;
eq.number_of_responses = 0;
eq.assign_response = [];
%% Read binary file
fh = fopen(blobfn, 'rb');
blob16 = fread(fh, inf, 'int16');
fclose(fh);
%% Convert to 16 bits
blob16 = zeros(1, length(blob)*2);
for i = 1:length(blob)
i16 = 2*(i - 1) + 1;
blob16(i16) = bitand(blob(i), 65535);
blob16(i16 + 1) = bitshift(blob(i), -16);
end
blob16 = signedint(double(blob16), 16);
%% Decode
abi = 16;
abi = nbytes_abi / 2;
eq.size = blob16(abi + 1) + 65536*blob16(abi + 2);
eq.channels_in_config = blob16(abi + 3);
eq.number_of_responses = blob16(abi + 4);
@ -85,15 +63,31 @@ reserved7 = blob16(abi + 11);
reserved8 = blob16(abi + 12);
eq.assign_response = blob16(abi + 13:abi + 13 + eq.channels_in_config-1);
fprintf('Blob size = %d\n', eq.size);
fprintf('Channels in config = %d\n', eq.channels_in_config);
fprintf('Number of responses = %d\n', eq.number_of_responses);
fprintf('Assign responses =');
for i=1:length(eq.assign_response)
fprintf(' %d', eq.assign_response(i));
if verbose
fprintf('Blob size = %d\n', eq.size);
fprintf('Channels in config = %d\n', eq.channels_in_config);
fprintf('Number of responses = %d\n', eq.number_of_responses);
fprintf('Assign responses =');
for i=1:length(eq.assign_response)
fprintf(' %d', eq.assign_response(i));
end
fprintf('\n');
end
fprintf('\n');
% Just header request
if isempty(resp_n)
eq.b = [];
eq.a = [];
return
end
% Handle pass-through
if resp_n < 0
eq.b = 1;
eq.a = 1;
end
% Normal filter taps retrieve
if resp_n > eq.number_of_responses-1;
error('Request of non-available response');
end
@ -104,24 +98,19 @@ b16 = blob16(abi + n_blob_header + eq.channels_in_config + 1:end);
j = 1;
for i=1:eq.number_of_responses
filter_length = b16(j);
output_shift = b16(j+1);
output_shift = double(b16(j+1));
if i-1 == resp_n
bi = b16(j+n_fir_header:j+n_fir_header+filter_length-1);
eq.b = 2^(-output_shift)*double(bi)/32768;
eq.b = 2^(-output_shift)*bi/32768;
end
j = j+filter_length+n_fir_header;
end
eq.a = 1;
if do_plot > 0
figure;
f = logspace(log10(10),log10(fs/2), 500);
h = freqz(eq.b, eq.a, f, fs);
semilogx(f, 20*log10(abs(h)));
axis([20 20e3 -40 20]);
grid on;
xlabel('Frequency (Hz)');
ylabel('Amplitude (dB)');
end
function y = signedint(x, bits)
y = x;
idx = find(x > 2^(bits-1)-1);
y(idx) = x(idx)-2^bits;
end

View File

@ -1,8 +1,8 @@
function eq = eq_iir_blob_decode(blobfn, resp_n, fs, do_plot)
function eq = eq_iir_blob_decode(ublob, resp_n)
%% Decode an IIR EQ binary blob
%
% eq = eq_fir_decode_blob(blobfn, resp_n, fs, do_plot)
% eq = eq_fir_decode_blob(blobfn, resp_n)
%
% blobfn - file name of EQ setup blob
% resp_n - index of response to decode
@ -16,48 +16,22 @@ function eq = eq_iir_blob_decode(blobfn, resp_n, fs, do_plot)
% assign response - vector of EQ indexes assigned to channels
%
%%
% Copyright (c) 2016, Intel Corporation
% All rights reserved.
% SPDX-License-Identifier: BSD-3-Clause
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
% * Redistributions of source code must retain the above copyright
% notice, this list of conditions and the following disclaimer.
% * Redistributions in binary form must reproduce the above copyright
% notice, this list of conditions and the following disclaimer in the
% documentation and/or other materials provided with the distribution.
% * Neither the name of the Intel Corporation nor the
% names of its contributors may be used to endorse or promote products
% derived from this software without specific prior written permission.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.
% Copyright (c) 2016-2020, Intel Corporation. All rights reserved.
%
% Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
%
if nargin < 4
do_plot = 0;
end
if nargin < 3
fs = 48e3;
end
if nargin < 2
resp_n = 0;
verbose = 1;
resp_n = [];
else
verbose = 0;
end
%% Get ABI information
[abi_bytes, nbytes_abi] = eq_get_abi(0);
%% Defaults
eq.b = [];
eq.a = [];
@ -65,13 +39,9 @@ eq.channels_in_config = 0;
eq.number_of_responses = 0;
eq.assign_response = [];
%% Read binary file
fh = fopen(blobfn, 'rb');
blob = fread(fh, inf, 'uint32');
fclose(fh);
%% Decode
abi = 8;
blob = double(ublob);
abi = nbytes_abi / 4;
eq.size = blob(abi + 1);
eq.channels_in_config = blob(abi + 2);
eq.number_of_responses = blob(abi + 3);
@ -79,17 +49,33 @@ reserved1 = blob(abi + 4);
reserved2 = blob(abi + 5);
reserved3 = blob(abi + 6);
reserved4 = blob(abi + 7);
eq.assign_response = blob(abi + 8:abi + 8 + eq.channels_in_config - 1);
eq.assign_response = signedint(blob(abi + 8:abi + 8 + eq.channels_in_config - 1), 32);
fprintf('Blob size = %d\n', eq.size);
fprintf('Channels in config = %d\n', eq.channels_in_config);
fprintf('Number of responses = %d\n', eq.number_of_responses);
fprintf('Assign responses =');
for i=1:length(eq.assign_response)
fprintf(' %d', eq.assign_response(i));
if verbose
fprintf('Blob size = %d\n', eq.size);
fprintf('Channels in config = %d\n', eq.channels_in_config);
fprintf('Number of responses = %d\n', eq.number_of_responses);
fprintf('Assign responses =');
for i=1:length(eq.assign_response)
fprintf(' %d', eq.assign_response(i));
end
fprintf('\n');
end
fprintf('\n');
% Just header request
if isempty(resp_n)
eq.b = [];
eq.a = [];
return
end
% Handle pass-through
if resp_n < 0
eq.b = 1;
eq.a = 1;
end
% Normal filter taps retrieve
if resp_n > eq.number_of_responses-1;
error('Request of non-available response');
end
@ -98,7 +84,6 @@ n_blob_header = 7;
n_iir_header = 6;
n_iir_section = 7;
f = logspace(log10(10),log10(fs/2), 500);
j = abi + n_blob_header + eq.channels_in_config + 1;
eq.b = 1.0;
eq.a = 1.0;
@ -116,13 +101,6 @@ for i=1:eq.number_of_responses
a0 = [1 -(ai/2^30)];
gain = gaini/2^14;
b0 = b0 * 2^(-shifti) * gain;
if do_plot > 1
figure;
h = freqz(b0, a0, f, fs);
semilogx(f, 20*log10(abs(h)));
ax = axis; axis([20 20e3 -40 20]);
grid on;
end
eq.b = conv(eq.b, b0);
eq.a = conv(eq.a, a0);
k = k + n_iir_section;
@ -131,16 +109,6 @@ for i=1:eq.number_of_responses
j = j+section_length;
end
if do_plot > 0
figure;
h = freqz(eq.b, eq.a, f, fs);
semilogx(f, 20*log10(abs(h)));
axis([20 20e3 -40 20]);
grid on;
xlabel('Frequency (Hz)');
ylabel('Amplitude (dB)');
end
end
function y = signedint(x, bits)