soft/tune/eq/eq_norm.m

115 lines
4.0 KiB
Mathematica
Raw Normal View History

function eq = eq_norm(eq)
%%
% Copyright (c) 2016, Intel Corporation
% All rights reserved.
%
% 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.
%
% Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
%
%% Normalize loudness of EQ by computing weighted average of linear
% response. Find scale need to make the average one.
w_lin = level_norm_fweight(eq.f);
m_lin_fir = 10.^(eq.fir_eq_db/20);
m_lin_iir = 10.^(eq.iir_eq_db/20);
i1k = find(eq.f > 1e3, 1, 'first') - 1;
m_max_fir = max(eq.fir_eq_db);
m_max_iir = max(eq.iir_eq_db);
sens_fir = sum(m_lin_fir.*w_lin)/sum(w_lin);
sens_iir = sum(m_lin_iir.*w_lin)/sum(w_lin);
g_offs = 10^(eq.norm_offs_db/20);
%% Determine scaling gain
switch lower(eq.norm_type)
case 'loudness'
g_fir = 1/sens_fir;
g_iir = 1/sens_iir;
case '1k'
g_fir = 1/m_lin_fir(i1k);
g_iir = 1/m_lin_iir(i1k);
case 'peak'
g_fir = 10^(-m_max_fir/20);
g_iir = 10^(-m_max_iir/20);
otherwise
error('Requested normalization is not supported');
end
%% Adjust FIR and IIR gains if enabled
if eq.enable_fir && eq.enable_iir
eq.b_fir = eq.b_fir * g_fir * g_offs;
eq.p_k = eq.p_k * g_iir * g_offs;
end
if eq.enable_fir && eq.enable_iir == 0
eq.b_fir = eq.b_fir * g_fir * g_offs;
end
if eq.enable_fir == 0 && eq.enable_iir
eq.p_k = eq.p_k * g_iir * g_offs;
end
%% Re-compute response after adjusting gain
eq = eq_compute_tot_response(eq);
end
function m_lin = level_norm_fweight(f)
%% w_lin = level_norm_fweight(f)
%
% Provides frequency weight that is useful in normalization of
% loudness of effect like equalizers. The weight consists of pink noise
% like spectral shape that attenuates higher frequencies 3 dB per
% octave. The low frequencies are shaped by 2nd order high-pass
% response at 20 Hz and higher frequencies by 3rd order low-pass at 20
% kHz.
%
% Note: This weight may have similarity with a standard defined test signal
% characteristic for evaluating music player output levels but this is not
% an implementation of it. This weight may help in avoiding a designed EQ to
% exceed safe playback levels in othervise compliant device.
%
% Input f - frequencies vector in Hz
%
% Output w_lin - weight curve, aligned to peak of 1.0 in the requested f
%
[z_hp, p_hp] = butter(2, 22.4,'high','s');
[z_lp, p_lp] = butter(3, 22.4e3,'s');
z_bp = conv(z_hp, z_lp);
p_bp = conv(p_hp, p_lp);
h = freqs(z_bp, p_bp, f);
m0 = 20*log10(abs(h));
noct = log(max(f)/min(f))/log(2);
att = 3*noct;
nf = length(f);
w = zeros(1,nf);
w = w - linspace(0,att,nf);
m = m0 + w;
m = m-max(m);
m_lin = 10.^(m/20);
end