function out = relanoiborra2019_drnl(insig,CF,fs,subj)
%RELANOIBORRA2019_DRNL DRNL filter used in relanoiborra2019
%
%   Usage: out = relanoiborra2019_drnl(insig,CF,fs,subj)
%
%   Input parameters:
%     insig : input signal
%     CF    : center frequency [Hz]
%     fs    : sampling frequency [Hz]
%     subj  : subject ID
%
%   Output parameters:
%     out   : filtered signal
%
%   Description:
%     DRNL model as parametrized in Relano-Iborra et al. 2019
%
%   Url: http://amtoolbox.sourceforge.net/amt-0.10.0/doc/modelstages/relanoiborra2019_drnl.php

% Copyright (C) 2009-2020 Piotr Majdak and the AMT team.
% This file is part of Auditory Modeling Toolbox (AMT) 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/>.

[N,idx_major_dim] = min(size(insig)); % Adding compatibility for column-
    % and row-wise processing

[linDRNLpar,nlinDRNLpar] = local_getDRNLparam_NH(CF,subj);

[GTlin_b,GTlin_a] = local_coefGtDRNL(linDRNLpar(1).vals,linDRNLpar(3).vals,1,fs); %get GT filter coeffs
[LPlin_b,LPlin_a] = local_coefLPDRNL(linDRNLpar(5).vals,fs); % get LP filter coeffs

% linDRNLpar(4).vals
% size(x)
% CF
y_lin = insig.*linDRNLpar(4).vals; % Apply linear gain

% Now filtering
for n = 1:linDRNLpar(2).vals % Gammatone filtering multiple times for cascading
    y_lin = real(filter(GTlin_b,GTlin_a,y_lin));  
end
for n = 1:linDRNLpar(6).vals % cascade of lowpass filters
    y_lin = filter(LPlin_b,LPlin_a,y_lin);           
end
% end of linear part %%%%%%%%%%%%%%%%%%%%%%%

% Non-linear part%%%%%%%%%%%%%%%%%%%%%%%%%%%
[GTnlin_b,GTnlin_a] = local_coefGtDRNL(nlinDRNLpar(1).vals,nlinDRNLpar(3).vals,1,fs); %get GT filter coeffs
[LPnlin_b,LPnlin_a] = local_coefLPDRNL(nlinDRNLpar(7).vals,fs); % get LP filter coeffs

y_nlin = insig;

% Now GT filtering
for n = 1:nlinDRNLpar(2).vals % Gammatone filtering multiple times for cascading
    y_nlin = filter(GTnlin_b,GTnlin_a,y_nlin);  
end

% Broken stick nonlinearity
a = nlinDRNLpar(4).vals;
b = nlinDRNLpar(5).vals;
c = nlinDRNLpar(6).vals;
% pause

%%% Correction by AO:
% % Old code: y_nlin is always a column vector, by generating y_decide in this
% %    way you generate a y_decide variable that is twice as long as the input
% %    signal (maybe related to the 'prepanne'?) and this means that the broken
% %    stick non-linearity does not work as expected, because the minimum of
% %    a column array is a single value, and very low, so probably the whole
% %    nonlinear path was being inhibited:
% y_decide = [a*abs(y_nlin); b*(abs(y_nlin)).^c];

y_factor_a = a*abs(y_nlin);
y_factor_b = b*(abs(y_nlin)).^c;

switch idx_major_dim
    case 1
        y_decide = min([y_factor_a; y_factor_b],[],idx_major_dim); 
    case 2
        y_decide = min([y_factor_a y_factor_b],[],idx_major_dim); 
end
% figure; plot(y_factor_a,'b-'); hold on; plot(y_factor_b,'r--'); plot(y_decide,'k-')
y_nlin = sign(y_nlin).* y_decide;

% Now GT filtering again
for n = 1:nlinDRNLpar(2).vals % Gammatone filtering multiple times for cascading
    y_nlin = filter(GTnlin_b,GTnlin_a,y_nlin);  
end

% then LP filtering
for n = 1:nlinDRNLpar(8).vals % cascade of lowpass filters
    y_nlin = filter(LPnlin_b,LPnlin_a,y_nlin);           
end

out = (y_lin + y_nlin);
end 
% -------------------------------------------------------------------------
% Get Gammatone filter coef for DRNL implementation
% Morten Loeve Jepsen
function [b,a]=local_coefGtDRNL(fc,BW,n,fs);
theta = 2*pi*fc/fs; phi   = 2*pi*BW/fs; alpha = -exp(-phi)*cos(theta);

b1 = 2*alpha; b2 = exp(-2*phi); 
a0 = abs( (1+b1*cos(theta)-i*b1*sin(theta)+b2*cos(2*theta)-i*b2*sin(2*theta)) / (1+alpha*cos(theta)-i*alpha*sin(theta))  );
a1 = alpha*a0;

% adapt to matlab filter terminology
b=[a0, a1];
a=[1, b1, b2];
end 
% -------------------------------------------------------------------------
% rev Morten L�ve Jepsen, 2.nov 2005
function [b,a]=local_coefLPDRNL(fc,fs)

    theta = pi*fc/fs;
    
    C = 1/(1+sqrt(2)*cot(theta)+(cot(theta))^2);
    D = 2*C*(1-(cot(theta))^2);
    E = C*(1-sqrt(2)*cot(theta)+(cot(theta))^2);

    b = [C, 2*C, C]; a = [1, D, E];

% script for getting the (DRNL) filter parameters (Lopez-Poveda, meddis 2001)
% Author: Morten L?ve Jepsen, 2.nov 2005, rev. 15 feb 2006, 19 feb 2007
%
% usage:  [linDRNLparOut,nlinDRNLparOut] = getDRNLparam(CF);
%
% The returned DRNLparam is a strucures containing the parameter name and
% values
end 
% ------------------------ %
% v 2.0 by Borys K. 23rd April 2015
% v 3.0 by Helia Rela�o-Iborra December, 2018

% -------------------------------------------------------------------------
function [linDRNLparOut,NlinDRNLparOut] = local_getDRNLparam_NH(CF,subj)
%% DRNL for normal hearing, Morten 2007

%% Initialize the parameter structures
linDRNLstruct  = struct('parname',{},'vals',{}); % initialize linear paramater vector
NlinDRNLstruct = struct('parname',{},'vals',{}); % initialize nonlinear paramater vector
% linDRNLparOut  = struct('parname',{},'vals',{});  
% NlinDRNLparOut = struct('parname',{},'vals',{});

linDRNLstruct(1).parname = 'CF_lin';  linDRNLstruct(2).parname = 'nGTfilt_lin';
linDRNLstruct(3).parname = 'BW_lin';  linDRNLstruct(4).parname = 'g';
linDRNLstruct(5).parname = 'LP_lin_cutoff';  linDRNLstruct(6).parname = 'nLPfilt_lin';
linDRNLparOut=linDRNLstruct;

NlinDRNLstruct(1).parname = 'CF_nlin';  NlinDRNLstruct(2).parname = 'nGTfilt_nlin';
NlinDRNLstruct(3).parname = 'BW_nlin';  NlinDRNLstruct(4).parname = 'a';
NlinDRNLstruct(5).parname = 'b';  NlinDRNLstruct(6).parname = 'c';
NlinDRNLstruct(7).parname = 'LP_nlin_cutoff';  NlinDRNLstruct(8).parname = 'nLPfilt_nlin';
NlinDRNLparOut = NlinDRNLstruct;

%% Common parameters not subject to immediate change by HI
linDRNLstruct(1).vals = 10^(-0.06762+1.01679*log10(CF)); % Hz, CF_lin,
linDRNLstruct(2).vals = 3; % number of cascaded gammatone filters
linDRNLstruct(3).vals = 10^(.03728+.75*log10(CF)); % Hz, BW_lin.
linDRNLstruct(5).vals = 10^(-0.06762+1.01*log10(CF)); % Hz, LP_lin cutoff
linDRNLstruct(6).vals = 4; % no. of cascaded LP filters,
NlinDRNLstruct(1).vals = 10^(-0.05252+1.01650*log10(CF)); % Hz, CF_nlin
NlinDRNLstruct(2).vals = 3; % number of cascaded gammatone filters,
NlinDRNLstruct(3).vals = 10^(-0.03193+.77*log10(CF)); % Hz, BW_nlin
NlinDRNLstruct(7).vals = 10^(-0.05252+1.01650*log10(CF)); % LP_nlincutoff
NlinDRNLstruct(8).vals = 3; % no. of cascaded LP filters in nlin path,

switch subj
    case 'NH' % Model for normal-hearing CASP
        %%  NH PARAMETERS
        linDRNLstruct(4).vals = 10^(4.20405 -.47909*log10(CF)); %g
        if CF<=1000
            NlinDRNLstruct(4).vals = 10^(1.40298+.81916*log10(CF)); % a,
            NlinDRNLstruct(5).vals = 10^(1.61912-.81867*log10(CF)); % b [(m/s)^(1-c)]
        else
            NlinDRNLstruct(4).vals = 10^(1.40298+.81916*log10(1500)); % a,
            NlinDRNLstruct(5).vals = 10^(1.61912-.81867*log10(1500)); % b [(m/s)^(1-c)]
        end
        NlinDRNLstruct(6).vals = 10^(-.60206); % c, compression coeff
        
    case 'HIx'
        %% %% Example HI listener with no compression
        linDRNLstruct(4).vals = 10^(4.20405 -.47909*log10(CF)); %g
        NlinDRNLstruct(4).vals = 0; % a,
        NlinDRNLstruct(5).vals = 10^(1.61912-.81867*log10(CF)); % b [(m/s)^(1-c)]
        NlinDRNLstruct(6).vals = .25; % c, compression coeff
end

%% Other params
for k=1:6
    linDRNLparOut(k).vals = linDRNLstruct(k).vals;
end
for k=1:8
    NlinDRNLparOut(k).vals = NlinDRNLstruct(k).vals;
end
end
%% EOF

