1function eq = eq_compute( eq ) 2 3%% 4% Copyright (c) 2016, Intel Corporation 5% All rights reserved. 6% 7% Redistribution and use in source and binary forms, with or without 8% modification, are permitted provided that the following conditions are met: 9% * Redistributions of source code must retain the above copyright 10% notice, this list of conditions and the following disclaimer. 11% * Redistributions in binary form must reproduce the above copyright 12% notice, this list of conditions and the following disclaimer in the 13% documentation and/or other materials provided with the distribution. 14% * Neither the name of the Intel Corporation nor the 15% names of its contributors may be used to endorse or promote products 16% derived from this software without specific prior written permission. 17% 18% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28% POSSIBILITY OF SUCH DAMAGE. 29% 30% Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> 31% 32 33%% Sanity checks 34if eq.enable_iir == 0 && eq.enable_fir == 0 35 fprintf('Warning: Nothing to do. Please enable FIR or IIR!\n'); 36end 37 38%% Extrapolate response to 0..fs/2, convert to logaritmic grid, and smooth 39% with 1/N octave filter 40eq = preprocess_responses(eq); 41 42%% Define target (e.g. speaker) response as parametric filter. This could also 43% be numerical data interpolated to the grid. 44if length(eq.parametric_target_response) > 0 45 [eq.t_z, eq.t_p, eq.t_k] = eq_define_parametric_eq( ... 46 eq.parametric_target_response, eq.fs); 47 eq.t_db = eq_compute_response(eq.t_z, eq.t_p, eq.t_k, eq.f, eq.fs); 48end 49 50if isempty(eq.t_db) 51 fprintf('Warning: No target response is defined.\n'); 52end 53 54%% Align responses at some frequency and dB 55[eq.m_db_s, offs] = eq_align(eq.f, eq.m_db_s, eq.f_align, eq.db_align); 56eq.raw_m_db = eq.raw_m_db + offs; 57eq.m_db = eq.m_db + offs; 58eq.t_db = eq_align(eq.f, eq.t_db, eq.f_align, eq.db_align); 59 60%% Error to equalize = target - raw response, apply 1/N octave smoothing to 61% soften the EQ shape 62eq.err_db = eq.t_db - eq.m_db; 63[eq.err_db_s, eq.logsmooth_noct] = logsmooth(eq.f, eq.err_db, eq.logsmooth_eq); 64 65%% Parametric IIR EQ definition 66if eq.enable_iir 67 [eq.p_z, eq.p_p, eq.p_k] = eq_define_parametric_eq(eq.peq, eq.fs); 68 if max(length(eq.p_z), length(eq.p_p)) > 2*eq.iir_biquads_max 69 error('Maximum number of IIR biquads is exceeded'); 70 end 71else 72 [eq.p_z, eq.p_p, eq.p_k] = tf2zp(1, 1); 73end 74[eq.iir_eq_db, eq.iir_eq_ph, eq.iir_eq_gd] = eq_compute_response(eq.p_z, ... 75 eq.p_p, eq.p_k, eq.f, eq.fs); 76 77 78%% FIR EQ computation 79% Find remaining responses error ater IIR for FIR to handle 80if eq.fir_compensate_iir 81 eq.err2_db = eq.err_db_s-eq.iir_eq_db; 82else 83 eq.err2_db = eq.err_db_s; 84end 85 86if eq.enable_fir 87 [eq.t_fir_db, eq.fmin_fir, eq.fmax_fir] = ... 88 get_fir_target(eq.f, eq.err2_db, eq.fmin_fir, eq.fmax_fir, ... 89 eq.amin_fir, eq.amax_fir, eq.logsmooth_noct, eq.fir_autoband); 90 91 if eq.fir_minph 92 eq.b_fir = compute_minph_fir(eq.f, eq.t_fir_db, ... 93 eq.fir_length, eq.fs, eq.fir_beta); 94 else 95 eq.b_fir = compute_linph_fir(eq.f, eq.t_fir_db, ... 96 eq.fir_length, eq.fs, eq.fir_beta); 97 end 98else 99 eq.b_fir = 1; 100 eq.tfirdb = zeros(1,length(eq.f)); 101end 102 103%% Update all responses 104eq = eq_compute_tot_response(eq); 105 106%% Normalize 107eq = eq_norm(eq); 108 109end 110 111function eq = preprocess_responses(eq) 112%% Usually too narrow measurement without the lowest and highest frequencies 113[f0, m0, gd0] = fix_response_dcnyquist_mult(eq.raw_f, eq.raw_m_db, ... 114 eq.raw_gd_s, eq.fs); 115 116%% Create dense logarithmic frequency grid, then average possible multiple 117% measurements 118[eq.f, eq.m_db, eq.gd_s, eq.num_responses] = map_to_logfreq_mult(f0, m0, ... 119 gd0, 1, eq.fs/2, eq.np_fine); 120 121%% Smooth response with 1/N octave filter for plotting 122eq.m_db_s = logsmooth(eq.f, eq.m_db, eq.logsmooth_plot); 123 124if length(eq.target_m_db) > 0 125 % Use target_m_db as dummy group delay, ignore other than magnitude 126 [f0, m0, ~] = fix_response_dcnyquist_mult(eq.target_f, ... 127 eq.target_m_db, [], eq.fs); 128 [~, eq.t_db, ~, ~] = map_to_logfreq_mult(f0, m0, [], 1, ... 129 eq.fs/2, eq.np_fine); 130end 131end 132 133 134function [f_hz, m_db, gd_s] = fix_response_dcnyquist(f_hz0, m_db0, gd_s0, fs) 135%% Append DC and Fs/2 if missing 136f_hz = f_hz0; 137m_db = m_db0; 138gd_s = gd_s0; 139if min(f_hz) > 0 140 f_hz = [0 f_hz]; 141 m_db = [m_db(1) m_db]; % DC the same as 1st measured point 142 if length(gd_s) > 0 143 gd_s = [gd_s(1) gd_s]; % DC the same as 1st measured point 144 end 145end 146if max(f_hz) < fs/2 147 f_hz = [f_hz fs/2]; 148 m_db = [m_db m_db(end)]; % Fs/2 the same as last measured point 149 if length(gd_s) > 0 150 gd_s = [gd_s gd_s(end)]; % Fs/2 the same as last measured point 151 end 152end 153end 154 155function [f_hz, m_db, gd_s] = fix_response_dcnyquist_mult(f_hz0, m_db0, ... 156 gd_s0, fs) 157 158if iscolumn(f_hz0) 159 f_hz0 = f_hz0.'; 160end 161if iscolumn(m_db0) 162 m_db0 = m_db0.'; 163end 164if iscolumn(gd_s0) 165 gd_s0 = gd_s0.'; 166end 167 168s1 = size(f_hz0); 169s2 = size(m_db0); 170s3 = size(gd_s0); 171 172if s1(1) == 0 173 error('Frequencies vector is empty'); 174end 175if (s1(1) ~= s2(1)) 176 error('There must be equal number of frequency and magnitude data'); 177end 178if (s1(2) ~= s2(2)) 179 error('There must be equal number of points in frequency, magnitude, and group delay data'); 180end 181if sum(s3) == 0 182 gd_s0 = zeros(s2(1),s2(2)); 183end 184for i=1:s1(1) 185 [f_hz(i,:), m_db(i,:), gd_s(i,:)] = fix_response_dcnyquist(... 186 f_hz0(i,:), m_db0(i,:), gd_s0(i,:), fs); 187end 188 189end 190 191function [f_hz, m_db, gd_s] = map_to_logfreq(f_hz0, m_db0, gd_s0, f1, f2, np) 192%% Create logarithmic frequency vector and interpolate 193f_hz = logspace(log10(f1),log10(f2), np); 194m_db = interp1(f_hz0, m_db0, f_hz); 195gd_s = interp1(f_hz0, gd_s0, f_hz); 196m_db(end) = m_db(end-1); % Fix NaN in the end 197gd_s(end) = gd_s(end-1); % Fix NaN in the end 198end 199 200function [f_hz, mm_db, mgd_s, num] = map_to_logfreq_mult(f_hz0, m_db0, ... 201 gd_s0, f1, f2, np) 202 203s1 = size(f_hz0); 204s2 = size(m_db0); 205s3 = size(gd_s0); 206if (s1(1) ~= s2(1)) 207 error('There must be equal number of frequency and magnitude data sets'); 208end 209if (s1(2) ~= s2(2)) 210 error('There must be equal number of points in frequency and magnitude data'); 211end 212num = s1(1); 213if sum(s3) == 0 214 gd_s0 = zeros(s2(1),s2(2)); 215end 216for i=1:num 217 [f_hz, m_db(i,:), gd_s(i,:)] = map_to_logfreq(f_hz0(i,:), ... 218 m_db0(i,:), gd_s0(i,:), f1, f2, np); 219end 220 221if num > 1 222 mm_db = mean(m_db); 223 mgd_s = mean(gd_s); 224else 225 mm_db = m_db; 226 mgd_s = gd_s; 227end 228end 229 230function [ms_db, noct] = logsmooth(f, m_db, c) 231 232%% Create a 1/N octave smoothing filter 233ind1 = find(f < 1000); 234ind2 = find(f < 2000); 235noct = ind2(end)-ind1(end); 236n = 2*round(c*noct/2); 237b_smooth = ones(1,n)*1/n; 238 239%% Smooth the response 240tmp = filter(b_smooth, 1, m_db); 241ms_db = [tmp(n/2+1:end) linspace(tmp(end), m_db(end), n/2)]; 242ms_db(1:n) = ones(1,n)*ms_db(n); 243end 244 245function [m_db, fmin_fir, fmax_fir] = get_fir_target(fhz, err2db, fmin_fir, ... 246 fmax_fir, amin_fir, amax_fir, noct, auto) 247 248 249%% Find maximum in 1-6 kHz band 250idx = find(fhz > 1e3, 1, 'first') - 1; 251m_db = err2db - err2db(idx); 252if auto 253 cf = [1e3 6e3]; 254 ind1 = find(fhz < cf(2)); 255 ind2 = find(fhz(ind1) > cf(1)); 256 ipeak = find(m_db(ind2) == max(m_db(ind2))) + ind2(1); 257 ind1 = find(fhz < cf(1)); 258 ind2 = find(m_db(ind1) > m_db(ipeak)); 259 if length(ind2) > 0 260 fmin_fir = fhz(ind2(end)); 261 end 262 ind1 = find(fhz > cf(2)); 263 ind2 = find(m_db(ind1) > m_db(ipeak)) + ind1(1); 264 if length(ind2) > 0 265 fmax_fir = fhz(ind2(1)); 266 end 267end 268 269%% Find FIR target response 270ind1 = find(fhz < fmin_fir); 271ind2 = find(fhz > fmax_fir); 272p1 = ind1(end)+1; 273if length(ind2) > 0 274 p2 = ind2(1)-1; 275else 276 p2 = length(fhz); 277end 278m_db(ind1) = m_db(p1); 279m_db(ind2) = m_db(p2); 280ind = find(m_db > amax_fir); 281m_db(ind) = amax_fir; 282ind = find(m_db < amin_fir); 283m_db(ind) = amin_fir; 284 285%% Smooth high frequency corner with spline 286nn = round(noct/8); 287x = [p2-nn p2-nn+1 p2+nn-1 p2+nn]; 288if max(x) < length(m_db) 289 y = m_db(x); 290 xx = p2-nn:p2+nn; 291 yy = spline(x, y, xx); 292 m_db(p2-nn:p2+nn) = yy; 293end 294 295%% Smooth low frequency corner with spline 296nn = round(noct/8); 297x = [p1-nn p1-nn+1 p1+nn-1 p1+nn]; 298if min(x) > 0 299 y = m_db(x); 300 xx = p1-nn:p1+nn; 301 yy = spline(x, y, xx); 302 m_db(p1-nn:p1+nn) = yy; 303end 304 305end 306 307function b = compute_linph_fir(f_hz, m_db, taps, fs, beta) 308if nargin < 5 309 beta = 4; 310end 311if mod(taps,2) == 0 312 fprintf('Warning: Even FIR length requested.\n'); 313end 314n_fft = 2*2^ceil(log(taps)/log(2)); 315n_half = n_fft/2+1; 316f_fft = linspace(0, fs/2, n_half); 317m_lin = 10.^(m_db/20); 318if f_hz(1) > 0 319 f_hz = [0 f_hz]; 320 m_lin = [m_lin(1) m_lin]; 321end 322a_half = interp1(f_hz, m_lin, f_fft, 'linear'); 323a = [a_half conj(a_half(end-1:-1:2))]; 324h = real(fftshift(ifft(a))); 325b0 = h(n_half-floor((taps-1)/2):n_half+floor((taps-1)/2)); 326win = kaiser(length(b0), beta)'; 327b = b0 .* win; 328if length(b) < taps 329 % Append to even length 330 b = [b 0]; 331end 332end 333 334function b_fir = compute_minph_fir(f, m_db, fir_length, fs, beta) 335 336%% Design double length H^2 FIR 337n = 2*fir_length+1; 338m_lin2 = (10.^(m_db/20)).^2; 339m_db2 = 20*log10(m_lin2); 340blin = compute_linph_fir(f, m_db2, n, fs, beta); 341 342%% Find zeros inside unit circle 343myeps = 1e-3; 344hdzeros = roots(blin); 345ind1 = find( abs(hdzeros) < (1-myeps) ); 346minzeros = hdzeros(ind1); 347 348%% Find double zeros at unit circle 349ind2 = find( abs(hdzeros) > (1-myeps) ); 350outzeros = hdzeros(ind2); 351ind3 = find( abs(outzeros) < (1+myeps) ); 352circlezeros = outzeros(ind3); 353 354%% Get half of the unit circle zeros 355if isempty(circlezeros) 356 %% We are fine ... 357else 358 %% Eliminate double zeros 359 cangle = angle(circlezeros); 360 [sorted_cangle, ind] = sort(cangle); 361 sorted_czeros = circlezeros(ind); 362 pos = find(angle(sorted_czeros) > 0); 363 neg = find(angle(sorted_czeros) < 0); 364 pos_czeros = sorted_czeros(pos); 365 neg_czeros = sorted_czeros(neg(end:-1:1)); 366 h1 = []; 367 for i = 1:2:length(pos_czeros)-1 368 x=mean(angle(pos_czeros(i:i+1))); 369 h1 = [h1' complex(cos(x),sin(x))]'; 370 end 371 h2 = []; 372 for i = 1:2:length(neg_czeros)-1; 373 x=mean(angle(neg_czeros(i:i+1))); 374 h2 = [h2' complex(cos(x),sin(x))]'; 375 end 376 halfcirclezeros = [h1' h2']'; 377 if length(halfcirclezeros)*2 < length(circlezeros)-0.1 378 %% Replace the last zero pair 379 halfcirclezeros = [halfcirclezeros' complex(-1, 0)]'; 380 end 381 minzeros = [ minzeros' halfcirclezeros' ]'; 382end 383 384%% Convert to transfer function 385bmin = mypoly(minzeros); 386 387%% Scale peak in passhz to max m_db 388hmin = freqz(bmin, 1, 512, fs); 389b_fir = 10^(max(m_db)/20)*bmin/max(abs(hmin)); 390 391end 392 393function tf = mypoly( upolyroots ) 394 395% Sort roots to increasing angle to ensure more consistent behavior 396aa = abs(angle(upolyroots)); 397[sa, ind] = sort(aa); 398polyroots = upolyroots(ind); 399 400n = length(polyroots); 401n1 = 16; % do not change, hardwired to 16 code below 402 403if n < (2*n1+1) 404 % No need to split 405 tf = poly(polyroots); 406else 407 % Split roots evenly to 16 poly computations 408 % The fist polys will rpb+1 roots and the rest 409 % rpb roots to compute 410 rpb = floor(n/n1); 411 rem = mod(n,n1); 412 i1 = zeros(1,n1); 413 i2 = zeros(1,n1); 414 i1(1) = 1; 415 for i = 1:n1-1; 416 if rem > 0 417 i2(i) = i1(i)+rpb; 418 rem = rem-1; 419 else 420 i2(i) = i1(i)+rpb-1; 421 end 422 i1(i+1) = i2(i)+1; 423 end 424 i2(n1) = n; 425 tf101 = poly(polyroots(i1(1):i2(1))); 426 tf102 = poly(polyroots(i1(2):i2(2))); 427 tf103 = poly(polyroots(i1(3):i2(3))); 428 tf104 = poly(polyroots(i1(4):i2(4))); 429 tf105 = poly(polyroots(i1(5):i2(5))); 430 tf106 = poly(polyroots(i1(6):i2(6))); 431 tf107 = poly(polyroots(i1(7):i2(7))); 432 tf108 = poly(polyroots(i1(8):i2(8))); 433 tf109 = poly(polyroots(i1(9):i2(9))); 434 tf110 = poly(polyroots(i1(10):i2(10))); 435 tf111 = poly(polyroots(i1(11):i2(11))); 436 tf112 = poly(polyroots(i1(12):i2(12))); 437 tf113 = poly(polyroots(i1(13):i2(13))); 438 tf114 = poly(polyroots(i1(14):i2(14))); 439 tf115 = poly(polyroots(i1(15):i2(15))); 440 tf116 = poly(polyroots(i1(16):i2(16))); 441 % Combine coefficients with convolution 442 tf21 = conv(tf101, tf116); 443 tf22 = conv(tf102, tf115); 444 tf23 = conv(tf103, tf114); 445 tf24 = conv(tf104, tf113); 446 tf25 = conv(tf105, tf112); 447 tf26 = conv(tf106, tf111); 448 tf27 = conv(tf107, tf110); 449 tf28 = conv(tf108, tf109); 450 tf31 = conv(tf21, tf28); 451 tf32 = conv(tf22, tf27); 452 tf33 = conv(tf23, tf26); 453 tf34 = conv(tf24, tf25); 454 tf41 = conv(tf31, tf34); 455 tf42 = conv(tf32, tf33); 456 tf = conv(tf41, tf42); 457 458 % Ensure the tf coefficients are real if rounding issues 459 tf = real(tf); 460end 461 462end 463