The Auditory Modeling Toolbox

Applies to version: 0.10.0

View the help

Go to function

GLASBERG2002 - - Loudness model for time-variant signals

Program code:

function [results] = glasberg2002(inSig,fs)
%GLASBERG2002 - Loudness model for time-variant signals
%   Usage: [results] = glasberg2002(inSig,fs);
%   
%   Example:
%
%     fs = 32000; 
%     t = linspace(0,1,fs);
%     sig = sin(2*pi*1000*t).';
%     inSig = setdbspl(sig,100);  
%
%   Note that currently fs must be 32000 Hz.
%
%   References:
%     B. R. Glasberg and B. C. J. Moore. A Model of Loudness Applicable to
%     Time-Varying Sounds. J. Audio Eng. Soc, 50(5):331--342, 2002.
%     
%
%   Url: http://amtoolbox.sourceforge.net/data/amt-test/htdocs/amt-0.10.0/doc/models/glasberg2002.php

% Copyright (C) 2009-2020 Piotr Majdak and the AMT team.
% This file is part of Auditory Modeling Toolbox (AMT) version 0.10.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: Thomas Deppisch
% 15.5.2020: PM, preallocation added, renamed from exp_moore2002

% todo: two-channel inSig, different fs (-> window sizes), inSig normalization

    %% model
    fVec = 20:fs/2;
    data = data_glasberg2002('tfOuterMiddle1997','fieldType','free','fVec',fVec);

    % filter order as in glasberg2002
    order = 4096;
    % create FIR filter
    tfLinear = 10.^(data.tfOuterMiddle/10);
    outerMiddleFilter = fir2(order, linspace(0, 1, length(fVec)), tfLinear);
    earSig = filtfilt(outerMiddleFilter,1,inSig);   % why does filter(..) not work?

    fftLen = 2048; % according to glasberg2002

    % compute windows
    hannLenMs = [2,4,8,16,32,64]; % hanning window size (glasberg2002) in ms
    hannLenSmp = round(hannLenMs./1000 * fs); % windows size in samples

    % calculate hann windows
    hannWin2 = hann(hannLenSmp(1));
    hannWin4 = hann(hannLenSmp(2));
    hannWin8 = hann(hannLenSmp(3));
    hannWin16 = hann(hannLenSmp(4));
    hannWin32 = hann(hannLenSmp(5));
    hannWin64 = hann(hannLenSmp(6));

    timeStep = 0.001; % 1ms steps as in glasberg2002
    updateRate = round(timeStep*fs);

    numBlocks = ceil(length(earSig)./updateRate);

    earSigPad = earSig;
    earSigPad(end+1:end+hannLenSmp(6)) = zeros(hannLenSmp(6),1);  % zero padding
%     compute ffts in 1ms intervals
%     todo: preallocation for speed...
    for ii=1:numBlocks
        % fft for each window
        lower = (ii-1)*updateRate+1;
        upper = (ii-1)*updateRate+hannLenSmp(1);
        spect1(ii,:) = fft(earSigPad(lower:upper) .* hannWin2, fftLen);
        
        upper = (ii-1)*updateRate+hannLenSmp(2);
        spect2(ii,:) = fft(earSigPad(lower:upper) .* hannWin4, fftLen);
        
        upper = (ii-1)*updateRate+hannLenSmp(3);
        spect3(ii,:) = fft(earSigPad(lower:upper) .* hannWin8, fftLen);
    
        upper = (ii-1)*updateRate+hannLenSmp(4);
        spect4(ii,:) = fft(earSigPad(lower:upper) .* hannWin16, fftLen);
    
        upper = (ii-1)*updateRate+hannLenSmp(5);
        spect5(ii,:) = fft(earSigPad(lower:upper) .* hannWin32, fftLen);
    
        upper = (ii-1)*updateRate+hannLenSmp(6);
        spect6(ii,:) = fft(earSigPad(lower:upper) .* hannWin64, fftLen);
    end

    binWidth = fs/(fftLen+2); %  bandwidth in Hz represented by 1 fft frequency bin
    oneHz = (fftLen+2)/fs;  % number of frequency bins representing 1Hz
    
    % truncate ffts to match the frequency ranges specified in glasberg2002
    % and put PSD for each window and each time interval in matrix -> window
    % normalization
    spect = zeros(numBlocks,fftLen/2+1);
    spect(:,round(4050*oneHz)+1:fftLen/2+1) = abs(spect1(:,round(4050*oneHz)+1:fftLen/2+1)).^2/sum(hannWin2.^2); % 4050-fs/2
    spect(:,round(2540*oneHz)+1:round(4050*oneHz)) = abs(spect2(:,round(2540*oneHz)+1:round(4050*oneHz))).^2/sum(hannWin4.^2); % 2540-4050Hz
    spect(:,round(1250*oneHz)+1:round(2540*oneHz)) = abs(spect3(:,round(1250*oneHz)+1:round(2540*oneHz))).^2/sum(hannWin8.^2); % 1250-2540Hz
    spect(:,round(500*oneHz)+1:round(1250*oneHz)) = abs(spect4(:,round(500*oneHz)+1:round(1250*oneHz))).^2/sum(hannWin16.^2); % 500-1250Hz
    spect(:,round(80*oneHz)+1:round(500*oneHz)) = abs(spect5(:,round(80*oneHz)+1:round(500*oneHz))).^2/sum(hannWin32.^2); % 80-500Hz
    spect(:,1:round(80*oneHz)) = abs(spect6(:,1:round(80*oneHz))).^2/sum(hannWin64.^2); % 0-80Hz
    compInt = 2*spect./fs;  %   psd
    compdB = 10*log10(compInt./(20e-6)^2); % intensity level of each frequency component in dB
    compFq = linspace(0,fs/2,fftLen/2+1);

    %% calculating excitation patterns
    % calculate ERB numbers corresponding to ERB mid frequencies
    erbStep = 0.25;    % according to moore1997, glasberg2002
    erbFcMin = 50;
    erbFcMax = 15000;
    erbNMin = fc2erbN(erbFcMin);
    erbNMax = fc2erbN(erbFcMax);
    erbN = erbNMin:erbStep:erbNMax;    % numbers of erb bands
    erbFc = erbN2fc(erbN);               % center frequency of erb bands

    erbLoFreq = erbN2fc(erbN-0.5); % lower limit of each ERB filter
    erbHiFreq = erbN2fc(erbN+0.5); % upper limit of each ERB filter

    % calculate intensity for each ERB (dB/ERB) and each time step
    for ii=1:length(erbFc)
        range = round(erbLoFreq(ii)*oneHz):round(erbHiFreq(ii)*oneHz);
        erbInt(:,ii) = sum(compInt(:,range),2);   % intensity sum in each erb
    end
    erbdB = 10*log10(erbInt./(20e-6)^2);   % intensity level in each erb using reference SPL of 20 uPa

    % p determines bandwidth and slope of the erb filter and is generally
    % asymmetrical
    % p is roughly symmetrical for an excitation level of 51dB per ERB
    p51 = 4*erbFc./f2erb(erbFc);    % p for erb center frequencies and a level of 51dB
    p511 = 4*1000/f2erb(1000);    % p for fc=1kHz and a level of 51dB (at 1kHz filters are symmetrical)

    pU = p51;   % pU for all erbFc and all time steps
    g = abs(repmat(compFq,149,1) - repmat(erbFc.',1,length(compFq)))./repmat(erbFc.',1,length(compFq));    % normalized deviation of each f to erbFc for each erb band

    pL=zeros(numBlocks,length(compFq),length(erbFc));
    p=zeros(size(pL));
    w=p; e=p; 
    eL=zeros(numBlocks,length(erbFc));
    erbdB2f=zeros(numBlocks,length(compFq));
    for ii = 1:numBlocks % time steps
        erbdB2f(ii,:) = interp1([0 erbFc fs/2], [min(erbdB(ii,:)) erbdB(ii,:) min(erbdB(ii,:))], compFq);   % map erbFc to compFq
        pL(ii,:,:) = repmat(p51,length(compFq),1) - 0.35.*(repmat(p51,length(compFq),1)./p511).*(repmat(erbdB2f(ii,:).',1,length(erbN))-51); 
        p(ii,:,:) = pL(ii,:,:);
        for jj = 1:length(erbN)
            p(ii,round(erbFc(jj)*oneHz)+1:end,jj) = pU(jj); % p(f>erbFc) = pU
        end
        w(ii,:,:) = (1+squeeze(p(ii,:,:)).*g.').*exp(-squeeze(p(ii,:,:)).*g.');    % calculate weighting function
        e(ii,:,:) = squeeze(w(ii,:,:)).*repmat(compInt(ii,:).',1,length(erbN));
        eL(ii,:) = sum(squeeze(e(ii,:,:)),1);   % sum excitation level in each erb
    end
    results.eLdB = 10*log10(eL./(20e-6)^2);
    results.erbN = erbN;
    % plot excitation pattern at some time
    % figure
    % plot(erbN,results.eLdB(50,:))
    % grid on

    %% calculating specific loudness 

    dataSL = data_glasberg2002('specLoud','fVec',erbFc);
    tQdB = dataSL.tQ;
    tQ = 10.^(tQdB./10);
    tQdB500 = dataSL.tQ500;
    gdB = dataSL.g;    % low level gain in cochlea amplifier
    g = 10.^((tQdB500-tQdB)/10);
    a = dataSL.a;    % parameter for linearization around absolute threshold
    alpha = dataSL.alpha;    % compressive exponent
    c = dataSL.c; % constant to get loudness scale to sone

    specLoud = zeros(size(eL));

    specLoud1 = c*(2*eL./(eL+repmat(tQ,numBlocks,1))).^1.5 .*((repmat(g,numBlocks,1).* ...
        eL+repmat(a,numBlocks,1)).^repmat(alpha,numBlocks,1)-repmat(a.^alpha,numBlocks,1));
    specLoud2 = c * ((repmat(g,numBlocks,1) .*eL+repmat(a,numBlocks,1)).^repmat(alpha,numBlocks,1) - ...
        repmat(a.^alpha,numBlocks,1));
    specLoud3 = c*(eL./1.04e6).^0.5;
    specLoud(eL<repmat(tQ,numBlocks,1)) = specLoud1(eL<repmat(tQ,numBlocks,1));
    specLoud(eL<=10^10 & eL>repmat(tQ,numBlocks,1)) = specLoud2(eL<=10^10 & eL>repmat(tQ,numBlocks,1));
    specLoud(eL>10^10) = specLoud3(eL>10^10);

    %% monaural/binaural loudness (= instantaneous loudness), short term loudness (STL), long term loudness (LTL)
    results.monauralLoudness = sum(specLoud,2) * erbStep;     % integrate over the erbs
    results.binauralLoudness = 2*results.monauralLoudness;  % integrate moore2007 (Modeling binaural loudness) for better results

    % STL and LTL:
    aSTL = 0.045;
    rSTL = 0.02;
    STL = zeros(length(results.binauralLoudness),1);
    aLTL = 0.01;
    rLTL = 0.0005;
    LTL = zeros(length(results.binauralLoudness),1);
    for ii = 2:length(results.binauralLoudness)
        if results.binauralLoudness(ii)>STL(ii-1)
            STL(ii) = aSTL*results.binauralLoudness(ii)+(1-aSTL)*STL(ii-1);
        else
            STL(ii) = rSTL*results.binauralLoudness(ii)+(1-rSTL)*STL(ii-1);
        end
        if STL(ii)>LTL(ii-1)
            LTL(ii) = aLTL*STL(ii)+(1-aLTL)*LTL(ii-1);
        else
            LTL(ii) = rLTL*STL(ii)+(1-rLTL)*LTL(ii-1);
        end
    end
    results.STL = STL;
    results.LTL = LTL;