function [audout, fc] = bramslow2004(insig, fs, varargin)
%bramslow2004 Loudness model considering hearing loss (AUDMOD)
%
% Usage: [audout, fc] = bramslow2004(insig,fs);
% [audout, fc] = bramslow2004(insig,fs,...);
%
% Input parameters:
% insig : Acoustic signal (in Pa).
% fs : Sampling rate (in Hz).
%
% Output parameters:
% audout : Structure containing the following:
%
% - EC*: Row vector with ERB rates (in Cams) of the filters (typically 3 to 32).
% Size: NoChan (defined by the input parameter 'NoChan',
% default: 30 channels).
%
% - fc*: Row vector with center frequencies (in Hz) of the filters. Size: NoChan.
%
% - ERB_R*: Row vector with the excitation pattern at the hearing thresholds (in dB). Size: NoChan.
%
% - ERB_UCL*: Row vector with the excitation pattern at the uncomfortabe levels (UCLs, in dB). Size: NoChan.
%
% - FFT_Power*: Column vector with total powers (in dB) of individual signal frames.
% Size: frames, being floor(insig/In_FrmSize)
% defined by the input parameter 'In_FrmSize' (default: 8192 samples).
%
% - ERB_SPL*: Matrix with SPLs in ERB bands, used for level dependencies of filterbank (in dB).
% Size: (*frames x NoChan*).
%
% - ERB_Power*: Vector with the total power (in dB) in ERB bands. Size: (*frames x 1*).
%
% - Roex_SPL*: Matrix with SPLs (in dB) of the excitation patterns (from the Roex filters).
% Size: (*frames x NoChan*).
%
% - Total_Excitation*: Column vector with the total SPL (in dB) of the excitation
% of the individual signal frames. Size: frames.
%
% - ERB_Loudness*: Matrix with the specific loudness (in sone) per ERB band and
% the individual signal frames. Size: (*frames x NoChan*).
%
% - Loudness*: Column vector with the total loudness (in sone) of the
% individual signal frames. Size: frames.
%
% fc : Output channel center frequencies (in Hz). The same as audout.fc.
%
%
% audout = BRAMSLOW2004(insig,fs) computes the internal representation of the
% signal insig sampled at a frequency of fs. The main output is
% audout.Loudness which returns the loudness (in sone) of the individual
% frames of the input signal insig.
%
% BRAMSLOW2004(..) also accepts the following flags among many others:
%
% 'no_debug' No display of debugging information. Default.
%
% 'debug' Display additional debugging information.
% Opposite of 'no_debug'.
%
% See also other flags and key-value pairs in arg_BRAMSLOW2004.
%
%
% [audout,fc] = BRAMSLOW2004(...) additionally returns the center
% frequencies of the filterbank.
%
% The processing blocks in the model are as follows:
%
% - FFT spectrum, e.g., 512 frequencies to obtain a power spectrum.
%
% - Corrections for sound-field type, coupler type, and static transmission
% factors (e.g., middle ear), applied in the frequency domain.
%
% - The signal power is determined in 1 ERB wide bands, by summing the
% power spectrum (f) within the limits of each band. These powers are
% used to adjust the filterbank.
%
% - The FFT power spectrum is multiplied by a filterbank, consisting of
% 30 auditory filters whose shape depends on hearing loss and on the
% signal power. The filterbank concept is based on work from Moore,
% Glasberg, Patterson and others at the University of Cambridge and uses
% rounded exponential (roex) filters. The roex filterbank output is
% called the excitation pattern.
%
% - The parameters for hearing loss (thresholds) are converted from dB HL
% to dB SPL and used to modify frequency selectivity in the filterbank
% and sensitivity in the loudness function.
%
% - The roex filterbank output (E) is passed on to the specific loudness
% function converting excitation in each channel to specific loudness (N').
% The total loudness of an incoming signal is calculated by summing the
% specific loudness across bands.
%
%
%
% See also: demo_bramslow2004 exp_bramslow2004
%
% References:
% L. Bramsløw Nielsen. An Auditory Model with Hearing Loss. Technical
% report, Eriksholm Research Centre, Snekkersten, 1993.
%
% L. Bramsløw. An objective estimate of the perceived quality of
% reproduced sound in normal and impaired hearing. Acta Acustica united
% with Acustica, 90(6):1007--1018, 2004.
%
% L. Bramsløw. An auditory loudness model with hearing loss. In
% Baltic-Nordic Acoustics Meeting, pages 318--323, 2024.
%
% B. R. Glasberg and B. Moore. Derivation of auditory filter shapes from
% notched-noise data. Hearing Research, 47(1-2):103--138, 1990.
%
%
% Url: http://amtoolbox.org/amt-1.6.0/doc/models/bramslow2004.php
% #StatusDoc: Good
% #StatusCode: Good
% #Verification: Verified
% #Requirements: M-Signal
% #Author: Lars Bramslow (1993): Original C code
% #Author: Graham Naylor (1994): Updates to model
% #Author: Tayyib Arshad (2007): Ported to Matlab
% #Author: Lars Bramslow (2024): Integration into AMT
% #Author: Piotr Majdak (2024): Integration for AMT 1.6.0
% 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.
%% ------ Checking of input parameters and setting defaults ------------
if nargin<2
error('%s: Too few input arguments.',upper(mfilename));
end
if ~isnumeric(insig)
error('%s: insig must be numeric.',upper(mfilename));
end
if ~isnumeric(fs) || ~isscalar(fs) || fs<=0
error('%s: fs must be a positive scalar.',upper(mfilename));
end
definput.import={'bramslow2004'}; % load defaults from arg_bramslow2004
[flags,keyvals] = ltfatarghelper({},definput,varargin);
InSamps = length(insig);
NoFrames= floor(InSamps/keyvals.In_FrmSize); % labw / szymon: skip last partial frame
%% default matlab version here
if flags.do_MATLAB
% initialization
EXTAGRAMPOINTS = 13;
E_Bin = [keyvals.In_FrmSize,1];
for Chan = 1:1:EXTAGRAMPOINTS
% from dB HL to dB SPL in 4153 artificial ear (ISO 389)
keyvals.AGLoss(Chan) = keyvals.AGLoss(Chan) + keyvals.RET4153(Chan);
keyvals.AG_UCL(Chan) = keyvals.AG_UCL(Chan) + keyvals.RET4153(Chan);
end
% Calc fc (in Hz)
for Chan = 1:1:keyvals.NoChan
audout.EC(Chan) = Chan + (keyvals.E_Beg-1);
audout.fc(Chan) = erbrate2f(audout.EC(Chan));
end
AGF = [125 250 500 750 1000 1500 2000 3000 4000 6000 8000 10000 12500];
AGFs_E = f2erbrate(AGF);
if keyvals.Process == 0
AvgFrames = 'ALL';
FramesToAvg = 1;
else
if keyvals.Process == 1
AvgFrames = 'SINGLE';
FramesToAvg = 1;
else
AvgFrames = 'SINGLE';
FramesToAvg = keyvals.Process;
end
end
% Read entire input signal into matrix of frames
[In_Buffer, In_NoSamps, keyvals.RMS]=bramslow2004_getframe(insig, keyvals.In_FrmSize, NoFrames, keyvals.Cal_RMS, keyvals.RMS, keyvals.Cal_dB);
amt_disp(sprintf(' bramslow2004: Read signal, duration %2.1f s', In_NoSamps/fs),flags.disp);
for j = 1:(keyvals.In_FrmSize/2)
f_Hz = j*fs/keyvals.In_FrmSize;
E_Bin(j) = f2erbrate(f_Hz);
end
% Filters do not widen with loss here
Widen = false;
amt_disp(' bramslow2004: Initialize: Calibrating 0 dB SPL, HTL and UCL...',flags.disp);
ts = tic;
E_0=bramslow2004_exc0dbspl(keyvals.In_FrmSize, fs, flags.TransFact, Widen, keyvals.AGLoss, ...
keyvals.RET4153, AGFs_E, keyvals.NoChan, keyvals.E_Beg, keyvals.E_End, E_Bin(1:end));
amt_disp(' bramslow2004: Calculating HTL...',flags.disp);
[ERB_R, E_TQ]=bramslow2004_htl(keyvals.In_FrmSize, fs, flags.TransFact, Widen, keyvals.AGLoss, keyvals.RET4153, AGFs_E, keyvals.NoChan, keyvals.E_Beg, keyvals.E_End, E_Bin(1:end));
audout.ERB_R = ERB_R';
amt_disp(' bramslow2004: Calculating UCL...',flags.disp);
[audout.ERB_UCL]=bramslow2004_ucl(keyvals.In_FrmSize, fs, flags.TransFact, keyvals.NoChan, Widen, keyvals.AG_UCL,keyvals.AGLoss, keyvals.RET4153, AGFs_E, keyvals.E_Beg, keyvals.E_End, E_Bin(1:end));
telapsed = toc(ts);
amt_disp(sprintf(' bramslow2004: Initialization done in %2.3f s', telapsed),flags.disp);
% Normal widen from here
Widen = true;
PowerSpec = zeros(floor(size(In_Buffer,1)/FramesToAvg), keyvals.In_FrmSize/2);
Power_Spec = PowerSpec;
for CurFrame = 1:1:size(In_Buffer,1)
Power_Spec(CurFrame,:) = bramslow2004_powerspectrum(In_Buffer(CurFrame,:));
end
% calcualting power spectra...
if strcmp (AvgFrames, 'ALL')
% Power spectrum averaging
amt_disp(' bramslow2004: Power spectrum averaging...',flags.disp);
PowerSpec = sum (Power_Spec(1:end,:))./NoFrames;
else
% Loudness averaging / frame averaging
for Currframe = 1:1:(ceil(In_NoSamps/keyvals.In_FrmSize)-1)
% Spectrum averaging
if strcmp (AvgFrames, 'SINGLE')
if FramesToAvg < 2
PowerSpec(Currframe,:) = Power_Spec(Currframe,:);
else
a = 1;
b = FramesToAvg;
for avg = 1:1: ((ceil(In_NoSamps/keyvals.In_FrmSize)-1)/FramesToAvg)
PowerSpec(avg,:) = sum(Power_Spec(a:b,:))./FramesToAvg;
a = a + FramesToAvg;
b = b + FramesToAvg;
end
end
end
end
end
siz = size(PowerSpec);
siz = siz(1);
% Process signal frame-by-frame----------------------------------------
amt_disp(sprintf(' bramslow2004: Loudness calculation %d frames', siz),flags.disp);
ts = tic;
audout.FFT_Power=zeros(siz,1);
audout.ERB_Power=zeros(siz,1);
audout.Loudness=zeros(siz,1);
audout.Total_Excitation=zeros(siz,1);
for frame = 1:1:siz
% Input power
audout.FFT_Power(frame) = 10*log10(sum(PowerSpec(frame,:)));
% Coupler correction
PowerSpec(frame,:) = bramslow2004_couplcorr(PowerSpec(frame,:), flags.Coupler, keyvals.In_FrmSize, fs);
% Static frequency weighting (equal loudness)
PowerSpec(frame,:) = bramslow2004_equloudn(PowerSpec(frame,:), flags.TransFact, keyvals.In_FrmSize, fs);
% Energy in ERB bands
[audout.ERB_SPL(frame,1:keyvals.NoChan), audout.ERB_Power(frame)] = bramslow2004_erbenergy(PowerSpec(frame,:), keyvals.NoChan, keyvals.In_FrmSize, fs, Widen, keyvals.AGLoss, keyvals.RET4153, AGFs_E, keyvals.E_Beg, keyvals.E_End);
% Excitation pattern from roex filters
[audout.Roex_SPL(frame,:), E_Vector, HTLL]=bramslow2004_roexfilt(PowerSpec(frame,:), audout.ERB_SPL(frame,:), keyvals.In_FrmSize, fs, keyvals.NoChan, Widen, AGFs_E, keyvals.AGLoss, keyvals.RET4153, keyvals.E_Beg, keyvals.E_End, E_Bin);
% Specific and total loudness
[audout.ERB_Loudness(frame,:), TotLoudn]= bramslow2004_specloudn(E_Vector, E_0, E_TQ, audout.ERB_UCL, HTLL, keyvals.NoChan, keyvals.E_Beg, keyvals.E_End, keyvals.Binaural);
audout.Loudness(frame) = TotLoudn;
[~, audout.Total_Excitation(frame)]=bramslow2004_excpatrn(E_Vector, keyvals.NoChan);
end % frame
telapsed = toc(ts);
amt_disp(sprintf(' bramslow2004: Loudness calc done in %2.3f s, %2.3f s per frame', telapsed, telapsed/siz),flags.disp);
fc = audout.fc;
end % if do_MATLAB
%% Python version
if flags.do_PYTHON
% should be the call mechanism..:
in.keyvals = keyvals;
in.flags = flags;
audout = []; % empty for now
% audout=amt_extern('Python','bramslow2004','pyaudmod.py',in,audout); % to be implemented
end
amt_disp(' bramslow2004: Done',flags.disp);