function [output,info] = bruce2018(stim,fsstim, fc, varargin)
%bruce2018 Auditory-nerve filterbank with improved synapse model
% Usage: [output] = bruce2018(stim,fsstim, fc);
% [output,info] = bruce2018(stim, fsstim, fc, varargin);
%
%
% Input parameters:
% stim : Pressure waveform of stimulus (time series).
%
% fsstim : Sampling frequency of stimulus (in Hz).
%
% fc : Frequency vector containing the CFs (in Hz).
% Use logspace(log10(flow), log10(fhigh),numCF) to
% replicate the results from Bruce et al. (2018).
%
% varargin : Various optional flags and key-value pairs.
%
%
% Output parameters:
% output : A struct containing the various modelstage outputs.
% It contains the following fields:
%
% - cohcs*: Actual COHCs used in the simulations.
%
% - cihcs*: Actual CIHCs used in the simulations.
%
% - sponts*: Actual spontanous rates (SRs) used in the simulations.
%
% - tabss*: Actual absolute timings used in the simulations.
%
% - trels*: Actual relative timings used in the simulations.
%
% - neurogram_ft*: Fine-timing neurogram calculated from the synapse output. Size: (*time x CFs*).
%
% - t_ft*: Time axis (in s) of neurogram_ft.
%
% - neurogram_mr*: Average of spike count in each PSTH bin. Size: (*time x CF*).
%
% - t_mr*: Time axis (in s) of neurogram_mr.
%
% - neurogram_Sout*: Synapse output. Size: (*time x CF*).
%
% - t_Sout*: Time axis (in s) of neurogram_Sout.
%
% - fc*: Actual CFs (in Hz) used in the simulations.
%
% - psth_ft*: Fine-timing PSTH. Size: (*time x CF*).
%
% - meanrate*: Average spiking rate (in spikes/s). Size: (*time x CF*).
%
% - varrate*: Variance of the spiking rate (in spikes/s). Size: (*time x CF*).
%
% - synout*: Output of a synapse. Size: (*time x CF x Syn*).
% Available only if 'outputPerSynapse' is specified.
%
% info : A struct containing the parameter settings applied.
% Available only if 'outputPerSynapse' is specified.
%
%
% BRUCE2018(...) returns modeled responses of multiple AN fibers tuned to
% various characteristic frequencies (CFs).
%
% BRUCE2018(...) also accepts the following flags:
%
% 'no_fitaudiogram' Use directly provided cihcs and cohcs*
% either as scalars or size of fc.
% The default parameters reflect a healthy cochlea. Default.
%
% 'fitaudiogram' Calculate the hearing-loss factors cihcs and
% cohcs from the frequencies ag_fs and threshold
% shifts ag_dbloss (in dB) by using BRUCE2018_FITAUDIOGRAM.
% The default parameters reflect a healthy cochlea.
% Opposite of 'no_fitaudiogram'.
%
% 'human' Use model parameters for humans. Default.
%
% 'cat' Use model parameters for cats. Opposite of 'human'.
%
% 'fixedFGn' Fractional Gaussian noise will be the same in every
% simulation. Default.
%
% 'varFGn' Fractional Gaussian noise will be different in every
% simulation. Opposite of 'fixedFGn'.
%
% 'approxPL' Use approxiate implementation of the power-law
% functions. Default.
%
% 'actualPL' Use actual implementation of the power-law functions.
% Opposite of 'approxPL'.
%
% 'outputPerCF' Output the average results over all synapses.
% This mode is faster than 'outputPerSynapse'. Default.
%
% 'outputPerSynapse' Output the synapse output of each individual
% nerve fibre.
% This can considerably slow down the calculations.
% Opposite of 'outputPerCF'.
%
% 'autoSR' Generate the parameters for the AN
% population wil be generated by the function
% BRUCE2018_GENERATEANPOPULATION based on
% the number of low, medium, and high SR fibres
% numL, numM, numH, respectively. Default.
%
% 'specificSRautoTiming' Generate the timing parameters for the AN
% population by the function
% BRUCE2018_GENERATEANPOPULATION based on
% the number of low, medium, and high SR fibres
% numL, numM, numH, and the spontaneous rates given in
% SRL, SRM, and SRH, respectively.
% Can not be used with 'autoSR' or 'specificSR'.
%
% 'specificSR' Do not generate AN parameters. The overall number
% of fibres numsponts, spontanouse rate spont,
% absolute timing tabs, and relative timing trel*
% must be specifically provided.
% Can not be used with 'autoSR' or 'specificSRautoTiming'.
%
% 'progress' Display the calculation progress. Default.
%
% 'silent' No progress display. Opposite of 'progress'.
%
%
% *Note**: If 'outputPerSynapse' is specified, psth_ft, meanrate, and
% varrate have the size (*time x CF x syn*), with syn being
% the number of synapses.
%
%
% This function takes the following optional key-value pairs:
%
% 'ag_fs',ag_fs Frequencies (in Hz) at which the audiogram should be
% evaluated. Used only when 'fitaudiogram' is specified.
%
% 'ag_db',ag_db Hearing loss (in dB) at frequencies ag_fs.
% Used only in when 'fitaudiogram' is specified.
%
% 'cohcs',cohcs OHC scaling factors:
%
% - 1 denotes normal OHC function. Default.
%
% - 0 denotes complete OHC dysfunction.
%
% cohcs can be a vector of the size of fc or a scalar.
% Used only in when 'no_fitaudiogram' is specified.
%
% 'cihcs',cihcs IHC scaling factors:
%
% - 1 denotes normal IHC function. Default.
%
% - 0 denotes complete IHC dysfunction.
%
% cihcs can be a vector of the size of fc or a scalar.
% Used only in when 'no_fitaudiogram' is specified.
%
% 'numL',nl Number of nerve fibres with low SR. Used only when 'autoSR'
% or 'specificSRautoTiming' specified.
%
% 'numM',nm Number of nerve fibres with medium SR. Used only when 'autoSR'
% or 'specificSRautoTiming' specified.
%
% 'numH',nh Number of nerve fibres with high SR. Used only when 'autoSR'
% or 'specificSRautoTiming' specified.
%
% 'lossL',lls Loss of low-SR fibres ranging from 0 (no fibres)
% to 1 (healthy, all fibres). Used only when 'autoSR'
% or 'specificSRautoTiming' specified.
%
% 'lossM',lms Loss of medium-SR fibres ranging from 0 (no fibres)
% to 1 (healthy, all fibres). Used only when 'autoSR'
% or 'specificSRautoTiming' specified.
%
% 'lossH',lhs Loss of high-SR fibres ranging from 0 (no fibres)
% to 1 (healthy, all fibres). Used only when 'autoSR'
% or 'specificSRautoTiming' specified.
%
% 'SRL',SRL SR (in spikes/s) of the low-SR fibres.
% Used only when 'specificSRautoTiming' specified.
%
% 'SRM',SRM SR (in spikes/s) of the medium-SR fibres.
% Used only when 'specificSRautoTiming' specified.
%
% 'SRH',SRH SR (in spikes/s) of the high-SR fibres.
% Used only when 'specificSRautoTiming' specified.
%
% 'numsponts',n Overall numbers of fibers.
% Used only when 'specificSR' specified.
%
% 'spont',spont SR (in spikes/s). Can be scalar or size of fc.
% Used only when 'specificSR' specified.
%
% 'tabs',tabs Absolute timings (in s). Can be scalar or size of fc.
% Used only when 'specificSR' specified.
%
% 'trel',trel Relative timings (in s). Can be scalar or size of fc.
% Used only when 'specificSR' specified.
%
% 'psthbinwidth_mr',psthbw Mean-rate binwidth (in s).
%
% 'windur_ft',winft Fine-timing neurogram window length (in samples).
%
% 'windur_mr',winmr Mean-rate neurogram window length (in samples).
%
% 'nrep',nrep Number of repetitions for the mean rate,
% rate variance, and PSTH calculation. Default is 1.
%
% 'reptime',rt Length of one repetition of the stimuli with pause.
% Default is 1.2 times the stimulus duration.
%
% 'fsmod',fsmod Model sampling rate (in Hz). The model was tested for fsmod*
% between 100 kHz and 500 kHz. Default is 200 kHz for cats and
% 100 kHz for humans.
%
%
% See also: plot_bruce2018 demo_bruce2018_auditorynervemodel
% demo_bruce2018 bruce2018_synapse bruce2018_generateanpopulation
% bruce2018_innerhaircells bruce2018_fitaudiogram bruce2018_ffgn
% exp_bruce2018 carney2015_fitaudiogram
%
% References:
% I. C. Bruce, Y. Erfani, and M. S. R. Zilany. A phenomenological model
% of the synapse between the inner hair cell and auditory nerve:
% Implications of limited neurotransmitter release sites. Hearing
% Research, 360:40--54, 2018.
%
%
% Url: http://amtoolbox.org/amt-1.6.0/doc/models/bruce2018.php
% #StatusDoc: Perfect
% #StatusCode: Perfect
% #Verification: Verified
% #Requirements: MATLAB MEX M-Signal
% #Author: Ian Bruce: original code of the model.
% #Author: Alejandro Osses (2020): original implementation.
% #Author: Clara Hollomey (2021): adapted to the AMT 1.0.
% #Author: Piotr Majdak (2021): adaptations to exp_osses2022; specificSRautoTiming added.
% #Author: Piotr Majdak (2024): Documentation improvements.
% 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.
%
% The other two modelstages 'bruce2018_innerhaircells' and
% 'bruce2018_synapse' are always active. '_innerhaircells' is called for
% each element in 'fcs' (each characteristic frequency), and '_synapse'
% is called for each nerve fiber. Per default and for execution speed,
% only results from outside of the nerve fiber loop are written to the
% struct 'output'. To retrieve results per fiber, set the flag
% 'ouputPerSynapse'. The actual parameters used in bruce2018 are
% output to the 'info' struct.
if nargin<3
error('%s: Too few input parameters.',upper(mfilename));
end
% Define input flags and values
definput.import = {'bruce2018'}; % load defaults from arg_bruce2018
[flags,kv] = ltfatarghelper({},definput,varargin);
% Derive the number of CF fibres
numCF = length(fc);
fs=kv.fsmod; % sampling rate (Hz) of the model
tdres = 1/fs; % sampling interval (s), i.e., the reciprocal of fs
stim = resample(stim,fs,fsstim); % resample the stimulus to the model fs
T = length(stim)/fs; % actual stimulus duration in seconds
smw_ft = hamming(kv.windur_ft);
smw_mr = hamming(kv.windur_mr);
simdur = ceil(T*kv.reptime/kv.psthbinwidth_mr)*kv.psthbinwidth_mr; % time interval (s) between stimulus repetitions
% species is either "cat" (1) or "human" (2): "1" for cat and "2" for human
if flags.do_cat, species = 1; else, species = 2; end
% noiseType is for fixed fGn (noise will be same in every simulation) or
% variable fGn: "0" for fixed fGn and "1" for variable fGn
if flags.do_varFGn, noiseType = 0; else, noiseType = 1; end
% implnt is for "approxiate" or "actual" implementation of the power-law functions:
%"0" for approx. and "1" for actual implementation
if flags.do_actualPL, implnt = 1; else, implnt = 0; end
if flags.do_fitaudiogram
% mixed loss
dbloss = interp1(kv.ag_fs,kv.ag_dbloss,fcs,'linear','extrap');
[cohcs,cihcs,OHC_loss, IHC_loss]=bruce2018_fitaudiogram(fc,dbloss,species);
info.OHC_loss = OHC_loss;
info.IHC_loss = IHC_loss;
else
if length(kv.cihcs) ~= numCF && length(kv.cihcs) ~= 1
error('cihc needs to either be a scalar or a vector of the same size as fc.')
end
if length(kv.cohcs) ~= numCF && length(kv.cohcs) ~= 1
error('cohc needs to either be a scalar or a vector of the same size as fc.')
end
cohcs = kv.cohcs.* ones(1, numCF);
cihcs = kv.cihcs.* ones(1, numCF);
info.OHC_loss = [];
info.IHC_loss = [];
end
% generate AN parameters?
if flags.do_autoSR
% generate SR and timing for a population based on numL, numM, and numH
[sponts,tabss,trels] = bruce2018_generateanpopulation(numCF,[kv.numL kv.numM kv.numH]);
numsponts = round([kv.lossL kv.lossM kv.lossH].*[kv.numL kv.numM kv.numH]);
cntsponts = sum(numsponts);
end
if flags.do_specificSRautoTiming
% use specific SRs but calculate the timing based on numL, numM, and numH
[~,tabss,trels] = bruce2018_generateanpopulation(numCF,[kv.numL kv.numM kv.numH]);
sponts.LS = kv.SRL*ones(1,kv.numL);
sponts.MS = kv.SRM*ones(1,kv.numM);
sponts.HS = kv.SRH*ones(1,kv.numH);
numsponts = round([kv.lossL kv.lossM kv.lossH].*[kv.numL kv.numM kv.numH]);
cntsponts = sum(numsponts);
end
if flags.do_specificSR
% use predefined SR parameters for all fibers, used in e.g., exp_bruce2018('fig8b');
if ~isscalar(kv.numsponts), error('numsponts needs to a scalar when running bruce2018 in specificSR mode.'); end
cntsponts = kv.numsponts;
sponts = kv.spont;
tabss = kv.tabs;
trels = kv.trel;
end
% initializations
init_size = simdur*fs;
output_len=floor(simdur/tdres+0.5)*kv.nrep; % length of vihc, c1, c2, synout
psth_len=output_len/kv.nrep; % length of vr, mr, psth_ft
psthbins = round(kv.psthbinwidth_mr*fs); % number of psth_ft bins per psth bin
neurogram_ft = zeros(round(init_size),numCF);
neurogram_Sout = zeros(round(init_size)*kv.nrep,numCF);
neurogram_mr = zeros(round(init_size)/round(kv.psthbinwidth_mr*fs),numCF);
output.vihc=zeros(output_len,numCF);
output.C1=zeros(output_len,numCF);
output.C2=zeros(output_len,numCF);
if flags.do_outputPerSynapse
output.psth_ft=zeros(psth_len,numCF,cntsponts);
output.meanrate=zeros(psth_len,numCF,cntsponts);
output.varrate=zeros(psth_len,numCF,cntsponts);
output.psth=zeros(psth_len/psthbins,numCF,cntsponts);
end
if flags.do_outputPerCF
% Memory allocation:
output.psth_ft=zeros(psth_len,numCF);
output.meanrate=zeros(psth_len,numCF);
output.varrate=zeros(psth_len,numCF);
output.psth=zeros(psth_len/psthbins,numCF);
output.meanrate_LSR = zeros(size(output.meanrate));
output.meanrate_MSR = zeros(size(output.meanrate));
output.meanrate_HSR = zeros(size(output.meanrate));
output.psth_LSR = zeros(size(output.psth));
output.psth_MSR = zeros(size(output.psth));
output.psth_HSR = zeros(size(output.psth));
end
if ~flags.do_silent && cntsponts>0, amt_disp(''); end % start the volatile display
for ii = 1:numCF
%for each CF, collect the appropriate tabs, trels and sponts, and calculate
% the inner hair cell potential (for the whole stimulus)
% FC = fc(ii);
% cohc = cohcs(ii);
% cihc = cihcs(ii);
[vihc, C1, C2] = bruce2018_innerhaircells(stim,fc(ii),kv.nrep,tdres,simdur,cohcs(ii),cihcs(ii),species);
% [vihc, C1, C2] = zilany2014_innerhaircells(stim,FC,kv.nrep,tdres,simdur,cohc,cihc,species); equivalent to bruce2018
output.vihc(:,ii) = vihc;
output.C1(:,ii) = C1;
output.C2(:,ii) = C2;
if cntsponts>0
if flags.do_autoSR || flags.do_specificSRautoTiming % use generated SR parameters
spont = [sponts.LS(ii,1:numsponts(1)) sponts.MS(ii,1:numsponts(2)) sponts.HS(ii,1:numsponts(3))];
tabs = [tabss.LS(ii,1:numsponts(1)) tabss.MS(ii,1:numsponts(2)) tabss.HS(ii,1:numsponts(3))];
trel = [trels.LS(ii,1:numsponts(1)) trels.MS(ii,1:numsponts(2)) trels.HS(ii,1:numsponts(3))];
end
if flags.do_specificSR % use the same SR parameters for all fibers
spont = sponts.*ones(1, cntsponts);
tabs = tabss.*ones(1, cntsponts);
trel = trels.*ones(1, cntsponts);
end
for jj = 1:cntsponts % calculate the synapse output for each fibre type
if ~flags.do_silent
amt_disp(['CF = ' int2str(ii) '/' int2str(numCF) '; spont = ' int2str(jj) '/' int2str(cntsponts) '; SR = ' num2str(spont(jj)) ' spikes/s'],'volatile');
end
[psth_ft,mr,vr,synout] = bruce2018_synapse(vihc,fc(ii),kv.nrep,tdres,noiseType,implnt,spont(jj),tabs(jj),trel(jj));
neurogram_Sout(:,ii) = neurogram_Sout(:,ii)+synout;
cnt = sum(reshape(psth_ft,psthbins,length(psth_ft)/psthbins))';
neurogram_ft(:,ii) = neurogram_ft(:,ii)+filter(smw_ft,1,psth_ft);
neurogram_mr(:,ii) = neurogram_mr(:,ii)+filter(smw_mr,1,cnt);
if flags.do_outputPerSynapse
output.psth_ft(:, ii, jj) = psth_ft;
output.meanrate(:, ii, jj) = mr;
output.varrate(:, ii, jj) = vr;
output.synout(:, ii, jj) = synout;
output.psth(:,ii,jj)=cnt;
end
if flags.do_outputPerCF
output.psth_ft(:,ii)=output.psth_ft(:,ii)+psth_ft;
output.meanrate(:,ii)=output.meanrate(:,ii)+mr;
output.varrate(:,ii)=output.varrate(:,ii)+vr;
output.psth(:,ii)=output.psth(:,ii)+cnt;
fiberType = find(jj <= cumsum([kv.numL kv.numM kv.numH]),1,'first');
switch fiberType
case 1
output.meanrate_LSR(:,ii) = output.meanrate_LSR(:,ii) + mr;
output.psth_LSR(:,ii) = output.psth_LSR(:,ii) + cnt;
case 2
output.meanrate_MSR(:,ii) = output.meanrate_MSR(:,ii) + mr;
output.psth_MSR(:,ii) = output.psth_MSR(:,ii) + cnt;
case 3
output.meanrate_HSR(:,ii) = output.meanrate_HSR(:,ii) + mr;
output.psth_HSR(:,ii) = output.psth_HSR(:,ii) + cnt;
end
end
end
if flags.do_outputPerCF
output.psth_ft(:,ii)=output.psth_ft(:,ii)/cntsponts;
output.meanrate(:,ii)=output.meanrate(:,ii)/cntsponts;
output.varrate(:,ii)=output.varrate(:,ii)/cntsponts;
if kv.numL ~= 0
output.meanrate_LSR(:,ii)=output.meanrate_LSR(:,ii)/kv.numL;
end
if kv.numM ~= 0
output.meanrate_MSR(:,ii)=output.meanrate_MSR(:,ii)/kv.numM;
end
if kv.numH ~= 0
output.meanrate_HSR(:,ii)=output.meanrate_HSR(:,ii)/kv.numH;
end
end % flags.do_outputPerCF
end % cntsponts>0
end % per numCF
neurogram_ft = neurogram_ft(1:kv.windur_ft/2:end,:); % 50% overlap in Hamming window
t_ft = 0:kv.windur_ft/2/fs:(size(neurogram_ft,1)-1)*kv.windur_ft/2/fs; % time vector for the fine-timing neurogram
neurogram_mr = neurogram_mr(1:kv.windur_mr/2:end,:); % 50% overlap in Hamming window
t_mr = 0:kv.windur_mr/2*kv.psthbinwidth_mr:(size(neurogram_mr,1)-1)*kv.windur_mr/2*kv.psthbinwidth_mr; % time vector for the mean-rate neurogram
t_Sout = 0:1/fs:(size(neurogram_Sout,1)-1)/fs; % time vector for the synapse output neurogram
%--------------------------------------------------------------------------
% write everything into an output struct
output.cohcs = cohcs;
output.cihcs = cihcs;
output.sponts = sponts;
output.tabss = tabss;
output.trels = trels;
output.t_ft = t_ft;
output.neurogram_ft = neurogram_ft;
output.neurogram_mr = neurogram_mr;
output.neurogram_Sout = neurogram_Sout;
output.t_mr = t_mr;
output.t_Sout = t_Sout;
output.fc = fc;
info.keyvals = kv;
info.flags = flags;