THE AUDITORY MODELING TOOLBOX

Applies to version: 1.6.0

View the help

Go to function

eurich2022_processing
Binaural processing stage of the Eurich et al. (2022) model

Program code:

function [out,mpar] = eurich2022_processing(stimulus,mpar)
%eurich2022_processing  Binaural processing stage of the Eurich et al. (2022) model 
%   Usage: [out,mpar] = eurich2022_processing(stimulus,mpar)
%
%   Input parameters:
%     stim: Matrix containing stimulus (columns 1 and 2) and template (columns 3 and 4)
%     
%     mpar: Structure with the following model parameters:
%
%      - fs:                             sampling frequency of model (in Hz)
%      - Filters_per_ERB_aud:            spacing of peripheral filter central frequencies (in ERB)
%      - GT_bandwidth_factor:            factor of gammatone filter bandwidth relative to "standard" (ERB = 79 Hz at fc = 500 Hz)
%      - GT_lowest_center_frequency:     lower bound of gammatone filterbank (in Hz)
%      - GT_highest_center_frequency:    higher bound of gammatone filterbank (in Hz)
%      - GT_fix_center_frequency:        fixed center frequency of one of the filters ((in Hz)
%      - GT_filterorder:                 filter order of each of the gammatone filters
%      - interference_sigma:             standard deviation of the Gaussian window in the across-frequency incoherence interference
%      - iKernelThresh:                  threshold above which a value of the Gaussian filter window is used
%      - rho_max                         upper limit of encoded interaural coherence (i.e. internal noise)
%      - monaural_internal_noise_sigma:  standard deviation of the Gaussian level-dependent internal noise of the monaural feature
%      - binaural_internal_noise_sigma:  standard deviation of the Gaussian internal noise of the binaural feature
%      - target_channel:                 index of the channel to be selected for processing in eurich2022_decision
%
%
%   Output parameters:
%     out: Matrix with four columns: 
%
%       - binaural features of the reference signal (complex correlation coefficient gamma)
%       - monaural feature of the reference signal (DC power of 500 Hz band)
%       - binaural features of the test signal (complex correlation coefficient gamma)
%       - monaural feature of the test signal (DC power of 500 Hz band)
%     
%     mpar: Updated structure with model parameters
%
%   See also: eurich2022 exp_eurich2022
%
%   References:
%     B. Eurich, J. Encke, S. D. Ewert, and M. Dietz. Lower interaural
%     coherence in off-signal bands impairs binaural detection. The Journal
%     of the Acoustical Society of America, 151(6):3927--3936, 06 2022.
%     
%
%   Url: http://amtoolbox.org/amt-1.6.0/doc/modelstages/eurich2022_processing.php


%   #Author: Bernhard Eurich (2022): original implementation
%   #Author: Piotr Majdak (2023): adaptation to AMT 1.4

% This file is licensed unter the GNU General Public License (GPL) either 
% version 3 of the license, or any later version as published by the Free Software 
% Foundation. Details of the GPLv3 can be found in the AMT directory "licences" and 
% at <https://www.gnu.org/licenses/gpl-3.0.html>. 
% You can redistribute this file and/or modify it under the terms of the GPLv3. 
% This file is distributed without any warranty; without even the implied warranty 
% of merchantability or fitness for a particular purpose. 

% ==== Gammatone filtering ====

% filterbank object
sFB = hohmann2002(mpar.fs,mpar.GT_lowest_center_frequency,mpar.GT_fix_center_frequency,...
    mpar.GT_highest_center_frequency, mpar.GT_filters_per_ERBaud,'bandwidth_factor',mpar.GT_bandwidth_factor);

mpar.idx_of_500Hz_channel = find(round(sFB.center_frequencies_hz) == mpar.GT_fix_center_frequency);


% apply filterbank on stimulus
filtered_signal(:,:,1) = hohmann2002_process(sFB,stimulus(:,1));
filtered_signal(:,:,2) = hohmann2002_process(sFB,stimulus(:,2));

 

% ==== Binaural Stage ====
% complex correlation coefficient gamma for every filter channel
gamma = mean(filtered_signal(:,:,1) .* conj(filtered_signal(:,:,2))) ...
./ sqrt(mean(abs(filtered_signal(:,:,1)).^2) .* mean(abs(filtered_signal(:,:,2)).^2));



%  across-channel interference 
channel_index = 1:length(sFB.center_frequencies_hz); % to consider all channels

% for-loop represents convolution with window:
% * limit coherence with respect to one channel (index g)
% * create weighting window
% * apply window to coherence where channel g is center
% * --> for every channel --> same effect as convolution but now with coherence limitation applied with respect to each channel

% need odd number for interference window
flS = length(sFB.center_frequencies_hz);
flidx = mod(flS,2)<1;
mpar.KernelLen = flS - flidx;

if mpar.KernelLen == 1 | mpar.interference_sigma <= 0.11 % somewhat arbitrary --> decision whether interference happens
    interference_window = 1;
    interfered_coherence = abs(gamma);
    interfered_coherence_atanh_rho_max =  atanh(mpar.rho_max * interfered_coherence);
            
    
elseif find(mpar.target_channel)
    Window_range = [-floor(mpar.KernelLen/2):ceil(mpar.KernelLen/2)-1];
    vExpWin0 = exp(-abs(Window_range)/(mpar.GT_filters_per_ERBaud*mpar.interference_sigma));
    vExpWin_ge   = vExpWin0(ge(vExpWin0,mpar.iKernelThresh));
    vExpWin_norm = vExpWin_ge ./ sum(vExpWin_ge);
    
    % interference of IPD fluctuations means only impact if off-frequency coherence lower than on-frequency coherence 
    coherence_lower = min(abs(gamma),abs(gamma(mpar.idx_of_500Hz_channel)));
    
    interference_range = mpar.idx_of_500Hz_channel - floor(length(vExpWin_norm)/2):mpar.idx_of_500Hz_channel + floor(length(vExpWin_norm)/2);
    interfered_coherence = sum(coherence_lower(interference_range) .* vExpWin_norm);

    interfered_coherence_atanh_rho_max = atanh(mpar.rho_max .* interfered_coherence);
    
    
else
    for g = channel_index
        
        % only lower off-frequency coherence is considered (compare with on-freq and take lower)
        coherence_lower = min(abs(gamma),abs(gamma(g)));
        
        zeros_padd = NaN(1, floor(length(coherence_lower)/2));
        coherence_lower_zeropadd = [zeros_padd coherence_lower zeros_padd];
        
        

        
        %     window for channel interaction
        Window_range = [-floor(mpar.KernelLen/2):ceil(mpar.KernelLen/2)-1];
        
        interference_window = NaN(1,2*length(Window_range)-1);
        window_idx1 = floor(length(interference_window)/2)-floor(length(Window_range)/2);
        window_idx2 = floor(length(interference_window)/2)+floor(length(Window_range)/2);
        
        interference_window(window_idx1:window_idx2) = ...
            exp(-abs(Window_range)/(mpar.GT_filters_per_ERBaud*mpar.interference_sigma));
        
        
        
        
        % place the window according to g (= temporary center channel)
        
        [~,center_of_window] = max(interference_window);
        interference_window_temp = interference_window(g:end-g);
        interference_window_areanorm = interference_window./nansum(interference_window);
        
        % actual interference happening here
        interfered_coherence = nansum(coherence_lower .* interference_window_areanorm(window_idx1:window_idx1+length(coherence_lower)-1));
        
        % select current interfered channel and apply internal noise (define maximum coherence)
        interfered_coherence_atanh_rho_max(g) =  atanh(mpar.rho_max * interfered_coherence);
    end
end



% write new IPD at f while the first 19 entries are empty
IPD = angle(gamma);

% binaural decision variable: complex correlation coefficient consisting of interfered coherence and (non-interfered) IPD
binaural_feature =  interfered_coherence_atanh_rho_max .* exp(1i*IPD);%



%% ==== MONAURAL PATHWAY ====

% Long-term DC of central channel: squared mean
monaural_feature = squeeze( mean( abs(filtered_signal(:,channel_index,1)),1) .^2 / 2);




% ==== ROUTE DECISION VALUES TO OUTPUT ====
out = cat(1,binaural_feature,monaural_feature);





end