THE AUDITORY MODELING TOOLBOX

Applies to version: 0.9.6

View the help

Go to function

LINDEMANN1986BINCORR - Cross-correlation between two input signals a la Lindemann

Program code:

function [crosscorr,t] = lindemann1986bincorr(insig,fs,varargin)
%LINDEMANN1986BINCORR Cross-correlation between two input signals a la Lindemann
%   Usage: crosscorr = bincorr(insig,fs,c_s,w_f,M_f,T_int,N_1)
%
%   Input parameters:
%     insig      : signal with t xnfc x2 (right, left channel)
%     fs         : sampling rate
%
%   Output parameters:
%     crosscorr  : output matrix containing the correlations                     
%     t          : time axis corresponding to the n time samples in crosscorr
%
%   LINDEMANN1986BINCORR(insig,fs) is an implementation of the Lindemann
%   cross-correlation algorithm to simulate a binaural delay line. The
%   output is a matrix of size n xm xamplitude, where
%   n = length(t)/fs.
%
%   The cross-correlation is calculated using:
%
%                  t
%     CC(tau,t) = int R(l-tau/2) * L(k+tau/2) exp(-(t-k)/T_int) dk
%                -inf
%
%   where T_{int} denotes an integration time constant and R, L the right and 
%   left input signal.
%
%   LINDEMANN1986BINCORR takes the following key/value pairs at the end of
%   the command line:
%
%     'c_s',c_s     Stationary inhibition factor, 0 <= c_s <= 1 
%                   (0.0 = no inhibition). Default value is 0.3.
%
%     'w_f',w_f     Monaural sensitivity at the end of the delay line, 
%                   0 <= w_f < 1. Default value is 0.035.
%
%     'M_f',M_f     Determines the decrease of the monaural sensitivity along 
%                   the delay line. Default value is 6
%
%     'T_int',t_int
%                   integration time window (ms). This is the memory of the 
%                   correlation process with exp(-1/T_int). Also this
%                   determines the time steps in the binaural activity map,
%                   because every time step T_int a new running
%                   cross-correlation is started, so every T_int we have a new
%                   result in crosscorr. You can set T_int = inf if you like
%                   to have no memory effects, then you will get only one
%                   time step in crosscorr. Default value is 5 ms.
%
%     'N_1',N_1     Sample at which the first running cross-correlation should
%                   be started to avoid onset effects (see Lindemann (1986a) p.
%                   1614). Default: 1, 17640 (*200/f*fs with f=500 and fs=44100) 
%                   for 'stationary' (see above)  
%
%     'stationary'  will set the default values of N_1=17640 and T_int=inf, use
%                   this for stationary input signals.
%
%   The key/values can also be specified first in the line of arguments
%   in the following order: c_s, w_f, M_f, T_int, N_1.
% 
%   See also: lindemann1986
%
%   References:
%     W. Lindemann. Extension of a binaural cross-correlation model by
%     contralateral inhibition. I. Simulation of lateralization for
%     stationary signals. J. Acoust. Soc. Am., 80:1608-1622, 1986.
%     
%     W. Lindemann. Extension of a binaural cross-correlation model by
%     contralateral inhibition. II. The law of the first wave front. J.
%     Acoust. Soc. Am., 80:1623-1630, 1986.
%     
%
%   Url: http://amtoolbox.sourceforge.net/amt-0.9.6/doc/modelstages/lindemann1986bincorr.php

% Copyright (C) 2009-2014 Peter L. Søndergaard.
% This file is part of AMToolbox version 1.0.0
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.

% AUTHOR: Hagen Wierstorf

%
% --- Used abbreviations ---
% 
%   l,L    : left signal
%   r,R    : right signal
%   m      : discrete time steps on the delay line (tau)
%   n      : discrete time (t)
%   M      : number of discrete steps on the delay line, 
%             length(delay line) = length(-M:M)
%   w_r    : monaural sensitivity in dependence of l
%   w_l    : monaural sensitivity in dependence of r
%   c_s    : stationary inhibition factor
%   w_f    : monaural sensitivity at the end of the delay line
%   M_f    : decrease of monaural sensitivity along the delay line
%   T_int  : integration time window (see above)
%   N_1    : lower summation boundary for the running cross-correlation
%   N_2    : upper summation boundary for the running cross-correlation
%   cc     : running cross-correlation
% 

%% ------ Checking of input parameters -----------------------------------
  
if nargin<2
  error('%s: Too few input parameters.',upper(mfilename));
end;

definput.import={'lindemann1986bincorr'};
[flags,keyvals,c_s,w_f,M_f,T_int,N_1]  = ...
    ltfatarghelper({'c_s','w_f','M_f','T_int','N_1'},definput,varargin);


if ~isnumeric(insig) || min(size(insig))~=2
    error('%s: insig has to be a numeric two channel signal!',upper(mfilename));
end

if ~isnumeric(fs) || ~isscalar(fs) || fs<=0
    error('%s: fs has to be a positive scalar!',upper(mfilename));
end

if ~isnumeric(c_s) || ~isscalar(c_s) || c_s<0 || c_s>1
    error('%s: 0 <= c_s <= 1',upper(mfilename));
end

if ~isnumeric(w_f) || ~isscalar(w_f) || w_f<0 || w_f>=1
    error('%s: 0 <= w_f < 1',upper(mfilename));
end

if ~isnumeric(M_f) || ~isscalar(M_f) || M_f<=0
    error('%s: M_f has to be a positive scalar!',upper(mfilename));
end

if ~isnumeric(T_int) || ~isscalar(T_int) || T_int<=0
    error('%s: T_int has to be a positive scalar!',upper(mfilename));
end

if ~isnumeric(N_1) || ~isscalar(N_1) || N_1<=0
    error('%s: N_1 has to be a positive scalar!',upper(mfilename));
end


%% ------ Computation ---------------------------------------------------- 

siglen = size(insig,1);
nfc = size(insig,2);   % number of frequency channels

% Ensure 0 <= insig <= 1, so that 0 <= r,l <= 1 (see lindemann1986a, eq. 4)
insig = insig ./ (max(insig(:))+eps);

% Integration time of summing cross-correlation
T_int = round(T_int/1000 * fs);

% Check if the given signal length is long enough to provide an output for the
% given start N_1 of the running cross-correlation (see lindemann1986a, p. 1614)
if T_int<Inf && siglen<=N_1+T_int
    error('%s: siglen has to be longer than N_1+T_int==%i', ...
        upper(mfilename),N_1+T_int);
elseif T_int==Inf && siglen<=N_1
    error('%s: siglen has to be longer than N_1==%i for T_int==Inf', ...
        upper(mfilename),N_1);
end

% ------ Time steps on the delay line ------------------------------------
% -M:M are the time steps of the delay.
% Maximum delay (in samples): 1ms (this is for 500 Hz pure tones (T/2). In 
% common this should be different for every frequency band, see 
% Lindemann (1986a) S. 1613, eq. 21.)
% NOTE:
% The delay-line needs a sampling rate of fs*2. Therefore the signals are
% not doubled and filled with 0 as in Lindemann (1986a), page 1610 and 
% 1611, but the delay line time is halfed. If the signals are really filled with
% zeros instead of doubling every entry Lindemanns inhibition process won't work
% anymore!
M = round(fs/2 / 1000);
% Length of the delay line
ndl = length(-M:M);


% ------ Monaural sensitives of the correlator ---------------------------
% The following equations are from Lindemann (1986a) equation 9 on page
%>1611. Obviously is w_r(m) = w_l(-m).

% Monaural sensitivities for the left ear
w_r = w_f .* exp(-(2*M:-1:0)./M_f);
w_r = w_r';
% Duplicate columns of w_r for every band
w_r = w_r(:,ones(1,nfc));

% Monaural sensitivities for the right ear
w_l = w_f .* exp(-(0:2*M)./M_f);
w_l = w_l';
% Duplicate columns of w_l for every band
w_l = w_l(:,ones(1,nfc));


% ------ Calculate the cross-correlation ---------------------------------

% Prepare the left and right delay line signal
l = zeros(ndl,nfc);
r = zeros(ndl,nfc);


% Set upper summation index for running cross-correaltion
% See lindemann1986a, eq. 24
N_2 = setN_2(N_1,T_int,siglen);

% Generate time axis
t = (N_2:(N_2-N_1):siglen)'/fs;

% Memory preallocation
crosscorr = zeros( floor( (siglen-N_1)/(N_2-N_1) ),ndl,nfc );
cc = zeros(ndl,nfc);

ii = 1; % crosscorr index
for n = 1:siglen
    
    % ------ Inhibition --------------------------------------------------
    % Stationary inhibition after Lindemann (1986a, p. 1612 eq. 11a):
    % r(m+1,n+1) = r(m,n) * [1 - c_s l(m,n)]
    % l(m-1,n+1) = l(m,n) * [1 - c_s r(m,n)]
    % The length of r and l are the same as the length of the delay line
    % (ndl = length(-M:M)). Also the signals are mirror-inverted, 
    % because of their different directions passing the delay line. l starts
    % on the right sight and r on the left side of the delay line. If you 
    % want to mirror the axes of the delay line, you have to exchange the
    % input direction of r and l into the delay line.
    r_mn = r;
    l_mn = l;
    r = [ insig(n,:,2); r_mn(1:ndl-1,:) .* (1-c_s.*l_mn(1:ndl-1,:)) ];
    l = [ l_mn(2:ndl,:) .* (1-c_s.*r_mn(2:ndl,:)); insig(n,:,1) ];
    % TODO: the spectral shape is also needed for some application, so we should
    % check if the following solution can be fixed to work as it should.
    % The normalization with max([r l]+eps) is done, to avoid a
    % dependence on the amplitude for the inhibition mechanism (see Gaik
    % 1993, p. 109). This preserves the spectral shape across frequency
    % channels. Therefore the inhibition depends now only on its stationary 
    % inhibition factor c_s.
    %l = [ l(2:ndl,:) .* (1 - (c_s .* r(2:ndl,:) ./ ...
	%	      max(max([l r]+eps)) ) ); insig(n,:,1) ];
    %r = [ insig(n,:,2);   r(1:ndl-1,:) .* ...
	%	      (1 - (c_s .* l(1:ndl-1,:) ./ max(max([l r]+eps))) ) ];
 
    % ------ Monaural sensitivity and trading ----------------------------
    %
    % Monaural sensitivities after Lindemann (1986a, p. 1611 eq. 6a + 6b):
    % r'(m,n) = r(m,n) * [1 - w_l(m)] + w_l(m)
    % l'(m,n) = l(m,n) * [1 - w_r(m)] + w_r(m)
    %
    % Trading factors after Gaik (1993, p. 106).
    %>Multiplication by a level factor to reach an ILD of 0 for a given
    %>ITD, if this ILD corresponded to a natural ILD for this ITD.
    %
    %NOTE: trading will be implemented later
    %R(m) = r(m) .* trading(m,1) .* (1-w_l(m))  +  w_l(m);
    %L(m) = l(m) .* trading(m,2)  .* (1-w_r(m))  +  w_r(m);
    R = r .* (1-w_l)  +  w_l;
    L = l .* (1-w_r)  +  w_r;
    
    % ------ Cross-correlation -------------------------------------------
    % Calculate running cross-correlation (e.g. Lindemann 1986a, eq. 10 + 
    % eq. 24). 
    %              __
    %             \   N_2
    %   cc(m,n) = /__ n=N_1  R(m,n) * L(m,n) * exp(-(N_2-n)/T_int)
    %
    % NOTE: For simplicity with stationary signals Lindemann uses only this
    % formula which can be managed by setting T_int=Inf:
    %              __                           
    %             \   N_2                       
    %   cc(m,n) = /__ n=N_1  R(m,n) * L(m,n)    
    %                                          
    if n>=N_1 && n<=N_2
        cc = cc + R.*L .* exp( -(N_2-n) / T_int );
    end
    % If we have reached the upper summation index, store the result in
    % crosscorr and start a new summation
    if n==N_2
        crosscorr(ii,:,:) = cc;
        cc = zeros(ndl,nfc);
        ii = ii+1;
        N_1 = N_2+1;
        N_2 = setN_2(N_1,T_int,siglen);
    end

end


% ------ Subfunctions ----------------------------------------------------
function N_2 = setN_2(N_1,T_int,siglen)
% SETN_2 Sets the summation boundary N_2 to a new value
%
if T_int==Inf
    N_2 = siglen;
else
    N_2 = N_1 + T_int;
end