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