From 594e031b6ee38ccd4545d1c4e57184ef7d59d681 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Tue, 31 Mar 2020 13:09:16 +0300 Subject: [PATCH] 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 --- tools/tune/eq/eq_blob_plot.m | 140 +++++++++++++++++++++++++++++ tools/tune/eq/eq_blob_read.m | 69 ++++++++++++++ tools/tune/eq/eq_fir_blob_decode.m | 107 ++++++++++------------ tools/tune/eq/eq_iir_blob_decode.m | 106 ++++++++-------------- 4 files changed, 294 insertions(+), 128 deletions(-) create mode 100644 tools/tune/eq/eq_blob_plot.m create mode 100644 tools/tune/eq/eq_blob_read.m diff --git a/tools/tune/eq/eq_blob_plot.m b/tools/tune/eq/eq_blob_plot.m new file mode 100644 index 000000000..8a6795a3c --- /dev/null +++ b/tools/tune/eq/eq_blob_plot.m @@ -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 + +%% 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 diff --git a/tools/tune/eq/eq_blob_read.m b/tools/tune/eq/eq_blob_read.m new file mode 100644 index 000000000..71271a101 --- /dev/null +++ b/tools/tune/eq/eq_blob_read.m @@ -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 + +%% 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 diff --git a/tools/tune/eq/eq_fir_blob_decode.m b/tools/tune/eq/eq_fir_blob_decode.m index a21a9592e..d9e09ec70 100644 --- a/tools/tune/eq/eq_fir_blob_decode.m +++ b/tools/tune/eq/eq_fir_blob_decode.m @@ -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 -% - -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 diff --git a/tools/tune/eq/eq_iir_blob_decode.m b/tools/tune/eq/eq_iir_blob_decode.m index 55a4ab332..6d04897e4 100644 --- a/tools/tune/eq/eq_iir_blob_decode.m +++ b/tools/tune/eq/eq_iir_blob_decode.m @@ -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 -% - -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)