diff userProgramsASRforDummies/cEssexAid.m @ 38:c2204b18f4a2 tip

End nov big change
author Ray Meddis <rmeddis@essex.ac.uk>
date Mon, 28 Nov 2011 13:34:28 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/userProgramsASRforDummies/cEssexAid.m	Mon Nov 28 13:34:28 2011 +0000
@@ -0,0 +1,651 @@
+classdef cEssexAid
+    %ESSEXAID_WRAPCLASS Wrapper for the EssexAid - Nick Clark July 2011
+    %   This class wraps up the EssexAid algorithm function that processes
+    %   each block of samples. This wrapper closely emulates the GUI used
+    %   in the lab and runs stimuli through the exact same algorithm used
+    %   in the lab. It even includes a helper function to generate C code
+    %   from the algorithm for use in a real-time framework.
+
+    
+    %% *********************************************************
+    %  properties                      _   _
+    %                                 | | (_)
+    %  _ __  _ __ ___  _ __   ___ _ __| |_ _  ___  ___
+    % | '_ \| '__/ _ \| '_ \ / _ \ '__| __| |/ _ \/ __|
+    % | |_) | | | (_) | |_) |  __/ |  | |_| |  __/\__ \
+    % | .__/|_|  \___/| .__/ \___|_|   \__|_|\___||___/
+    % | |             | |
+    % |_|             |_|
+    %************************************************************
+    
+    %% **********************************************************
+    % Public properties - can be set by user
+    %************************************************************
+    properties(Access = public)
+        sr         = 48e3;
+        numSamples = 1024; %MAX=6912, LAB_USE=48
+        stimulusUSER                   
+        
+        %------------------------------------------------------------------
+        % Params for audiometric freqs 250, 500, 1000, 2000, 4000, 8000 Hz
+        %------------------------------------------------------------------
+        audiometry_dB= [ 0;    0;    0;    0;    0;   0];   %Pure tone threshold in dB SPL
+        mainGain_dB  = [ 0;    0;    0;    0;    0;   0];   %Gain applied at audiometric frequencies
+        TC_dBHL      = [40;   40;   40;   40;   40;  40];   %Compression thresholds (in dB HL from 2nd filt)
+        TM_dBHL      = [10;   10;   10;   10;   10;  10];   %MOC thresholds (in dB OUTPUT from 2nd filt)
+        DRNLc        = [ 0.2;  0.2;  0.2;  0.2;  0.2; 0.2]; %Compression exponent at audiometric frequencies
+        
+        %------------------------------------------------------------------
+        % Dynamic compression properties
+        %------------------------------------------------------------------
+        ARtau = 60e-3;       %decay time constant
+        ARthreshold_dB = 85; %dB SPL (input signal level) =>200 to disable
+        MOCtau = 450e-3;     %Time constant in Seconds
+        MOCfactor = 0.5;     %dB attenuation applied to the input per dB exceeding output threshold
+        
+        %------------------------------------------------------------------
+        % Band filtering properties
+        %------------------------------------------------------------------
+        bwOct = 1/2; %1/1, 1/2, 1/3, 1/4, 1/5
+        filterOrder = 2 %BUTTER=2, GTF=3
+        useGTF = false; %If false, revert to butterworth
+    end
+    
+    %% **********************************************************
+    % Read only properties that are not dependent
+    %************************************************************
+    properties(SetAccess = private)          
+        MOCrecord
+    end
+    
+    %% **********************************************************
+    % Constant properties 
+    %************************************************************
+    properties(Constant = true, Hidden = true)  
+        numAudiometricFreqs = 6;
+    end
+    
+    %% **********************************************************
+    % Dependent visable properties - calculated at runtime
+    %************************************************************
+    properties(Dependent = true, Hidden = false)
+        channelBFs %= 250 * 2.^((0:fNmax)'*params.bwOct);        
+        numChannels %= numel(channelBFs);        
+        aidOPnice %aid output reformatted to be exactly the same dimensions as the input stimulus                               
+    end
+    
+    %% **********************************************************
+    % Dependent invisable properties - calculated at runtime
+    %************************************************************
+    properties(Dependent = true, Hidden = true)
+        TC_dBO_INTERP % Compression threshold in terms of 2nd filter o/p in dB SPL
+        TM_dBO_INTERP % MOC threshold in terms of 2nd filter o/p in dB SPL                    
+        bwOct_INTERP
+        DRNLb_INTERP %=  ( 2e-5 .* 10.^(TCdBO/20)) .^ (1-DRNLc)  ; 
+        DRNLc_INTERP               
+        mainGain_INTERP %Interp'd and in linear units 
+        
+        ARthresholdPa %=  20e-6*10^(ARthreshold_dB/20);% Pa thresh for triggering AR        
+        stimulusINTERNAL %input stimulus in correct format for the Aid algo
+    end
+    
+    %% **********************************************************
+    % Protected properties - The user never needs to set
+    %************************************************************
+    properties(Access = protected)  
+        aidOP
+        emlc_z
+        
+        %--------------------------------------------------------------
+        % ENUMERATIONS USED IN THE FRAME PROCESSOR
+        %--------------------------------------------------------------
+        enumC_ARb  = 0;
+        enumC_ARa  = 2;
+        enumC_MOCb = 4;
+        enumC_MOCa = 6;
+        
+        % enumC_BPb1 = 8;
+        % enumC_BPa1 = 13;
+        % enumC_BPb2 = 18;
+        % enumC_BPa2 = 23;
+        % enumC_BPb3 = 28;
+        % enumC_BPa3 = 33;
+        % enumC_BPb4 = 38;
+        % enumC_BPa4 = 43;
+        
+        enumS_AR  = 0;
+        
+        % enumS_MOC1  = 1;
+        % enumS_BPin_1_1 = 2;
+        % enumS_BPin_2_1 = 6;
+        % enumS_BPout_1_1 = 10;
+        % enumS_BPout_2_1 = 14;
+        %
+        % enumS_MOC2 = 18;
+        % enumS_BPin_1_2 = 19;
+        % enumS_BPin_2_2 = 23;
+        % enumS_BPout_1_2 = 27;
+        % enumS_BPout_2_2 = 31;
+        % ...
+    end
+        
+    %% **********************************************************
+    % methods        _   _               _
+    %               | | | |             | |
+    % _ __ ___   ___| |_| |__   ___   __| |___
+    %| '_ ` _ \ / _ \ __| '_ \ / _ \ / _` / __|
+    %| | | | | |  __/ |_| | | | (_) | (_| \__ \
+    %|_| |_| |_|\___|\__|_| |_|\___/ \__,_|___/
+    %************************************************************
+    
+    methods
+        %% **********************************************************
+        % Constructor
+        %************************************************************
+        function obj = EssexAid_WrapClass(sr, stimulus)          
+            
+            if nargin > 0
+                obj.sr = sr;
+            end
+            
+            if nargin > 1
+                obj.stimulusUSER = stimulus;
+            else         
+                obj.stimulusUSER = obj.pipSequence(obj.sr);
+            end
+        end
+        
+        %% **********************************************************
+        % Get method for channelBFs 
+        %************************************************************
+        function value = get.channelBFs(obj)
+            fNmax = 5/obj.bwOct;
+            value = 250 * 2.^((0:fNmax)'*obj.bwOct);
+        end
+        
+        %% **********************************************************
+        % Get method for numChannels 
+        %************************************************************
+        function value = get.numChannels(obj)
+            value = numel(obj.channelBFs);
+        end
+        
+        %% **********************************************************
+        % Get method for ARthresholdPa 
+        %************************************************************
+        function value = get.ARthresholdPa(obj)
+            value = 20e-6*10^(obj.ARthreshold_dB/20);% Pa thresh for triggering AR
+        end
+        
+        %% **********************************************************
+        % Get method for TC_dBO_INTERP
+        %************************************************************
+        function value = get.TC_dBO_INTERP(obj)
+            TC_dBO = obj.audiometry_dB - obj.mainGain_dB + obj.TC_dBHL;
+            value  = obj.interpPars(TC_dBO, obj.numChannels);
+        end
+        
+        %% **********************************************************
+        % Get method for TM_dBO_INTERP
+        %************************************************************
+        function value = get.TM_dBO_INTERP(obj)
+            TM_dBO = obj.audiometry_dB - obj.mainGain_dB + obj.TM_dBHL;
+            value  = obj.interpPars(TM_dBO, obj.numChannels);
+        end
+        
+        %% **********************************************************
+        % Get method for bwOct_INTERP 
+        %************************************************************
+        function value = get.bwOct_INTERP(obj)
+            value = repmat(obj.bwOct, 1, obj.numChannels);
+        end
+        
+        %% **********************************************************
+        % Get method for DRNLb_INTERP 
+        %************************************************************
+        function value = get.DRNLb_INTERP(obj)
+            value = ( 2e-5 .* 10.^(obj.TC_dBO_INTERP/20)) .^ (1-obj.DRNLc_INTERP); 
+        end
+        
+        %% **********************************************************
+        % Get method for DRNLc_INTERP 
+        %************************************************************
+        function value = get.DRNLc_INTERP(obj)
+            value  = obj.interpPars(obj.DRNLc, obj.numChannels);
+        end
+        
+        %% **********************************************************
+        % Get method for mainGain_INTERP 
+        %************************************************************
+        function value = get.mainGain_INTERP(obj)
+            mainGainLin = 10.^(obj.mainGain_dB/20); %lin units
+            value  = obj.interpPars(mainGainLin, obj.numChannels);
+        end                
+        
+        %% ***********************************************************
+        % Get method for stimulus
+        % -----------------------
+        % The hearing aid expects a stereo signal, as the MOC control is
+        % linked for left and right channels. It would be more efficient to
+        % use a mono version of the aid for simulation in Matlab. However,
+        % I always want to use the exact same code for the hardware in the
+        % lab and current simulations. This code will make a mono signal
+        % stereo if needs be and/or rotate to 2xN array.
+        %*************************************************************
+        function value = get.stimulusINTERNAL(obj)            
+            [nRows, nCols] = size(obj.stimulusUSER);
+            
+            % Assume that the stimulus duration is greater than 2 samples.
+            % Therefore the number of channels is the min dim.            
+            [nChans, I] = min([nRows nCols]);                                                
+            
+            if nChans == 2
+                if I == 2
+                    value = obj.stimulusUSER;
+                else
+                    value = obj.stimulusUSER';
+                end
+            elseif nChans == 1 %Just to be explicit
+                if I == 2
+                    value = [obj.stimulusUSER obj.stimulusUSER];   
+                else
+                    value = [obj.stimulusUSER; obj.stimulusUSER]';
+                end
+            end
+        end
+        
+        %% ***********************************************************
+        % Get method for aid output
+        % -----------------------
+        % This get method is linked to the above internal stimulus method
+        % and allows the user to extract the hearing aid output in exactly
+        % the same shape and size as the original input stimulus. This is
+        % very useful for the speech recognition work and presumably
+        % for multithreshold also.
+        %*************************************************************
+        function value = get.aidOPnice(obj)
+            if ~isempty(obj.aidOP)
+                [nRows, nCols] = size(obj.stimulusUSER);
+                
+                % Assume that the stimulus duration is greater than 2 samples.
+                % Therefore the number of channels is the min dim.
+                [nChans, I] = min([nRows nCols]);
+                
+                %** The aid output will ALWAYS be a 2xN array **
+                %The fist job is to remove trailing zeros that may have been
+                %introduced by the framing process
+                aidOPtruncated = obj.aidOP(:, 1:max([nRows nCols]));
+                
+                %The next task is to arrange the op like the ip
+                if nChans == 2
+                    if I == 1
+                        value = aidOPtruncated;
+                    else
+                        value = aidOPtruncated';
+                    end
+                elseif nChans == 1 %Just to be explicit
+                    if I == 1
+                        value = aidOPtruncated(1,:);
+                    else
+                        value = aidOPtruncated(1,:)';
+                    end
+                end
+            else % ---- of if isempty statement
+                value = [];
+            end
+        end
+        
+        %% ***********************************************************
+        % *** Set methods ***
+        % -----------------------
+        % This is a bunch of unexciting error hunting functions. They also
+        % flush the aid output if any parameters change. Therefore,
+        % processStim will have to be called explicity by the user once
+        % again. 
+        %*************************************************************
+        function obj = set.stimulusUSER(obj,value)
+            [nRows, nCols] = size(value);
+            
+            % Assume that the stimulus duration is greater than 2 samples.
+            % Therefore the number of channels is the min dim.            
+            nChans = min([nRows nCols]);            
+            assert(nChans<3 && nChans, 'Number of stimulus channels must be 1 or 2')
+            
+            obj = obj.flushAidData; %flush any previous hearing aid data if the input stimulus changes
+            obj.stimulusUSER = value;
+        end                     
+        function obj = set.sr(obj,value)
+            assert(value>=20e3 && value<=192e3, 'sr must be between 20 and 192 kHz')            
+            obj = obj.flushAidData;
+            obj.sr = value;
+        end
+        function obj = set.numSamples(obj,value)
+            assert(value>=48 && value<=6912, 'must be between 48 and 6912 samples')            
+            obj = obj.flushAidData;
+            obj.numSamples = value;
+        end
+        function obj = set.audiometry_dB(obj,value)
+            [nRows,nCols] = size(value);
+            assert(nRows==obj.numAudiometricFreqs && nCols==1, 'must be 6x1 column vector') %#ok<MCSUP>
+            obj = obj.flushAidData;
+            obj.audiometry_dB = value;
+        end
+        function obj = set.mainGain_dB(obj,value)
+            [nRows,nCols] = size(value);
+            assert(nRows==obj.numAudiometricFreqs && nCols==1, 'must be 6x1 column vector') %#ok<MCSUP>
+            obj = obj.flushAidData;
+            obj.mainGain_dB = value;
+        end
+        function obj = set.TC_dBHL(obj,value)
+            [nRows,nCols] = size(value);
+            assert(nRows==obj.numAudiometricFreqs && nCols==1, 'must be 6x1 column vector') %#ok<MCSUP>
+            obj = obj.flushAidData;
+            obj.TC_dBHL = value;
+        end
+        function obj = set.TM_dBHL(obj,value)
+            [nRows,nCols] = size(value);
+            assert(nRows==obj.numAudiometricFreqs && nCols==1, 'must be 6x1 column vector') %#ok<MCSUP>
+            obj = obj.flushAidData;
+            obj.TM_dBHL = value;
+        end
+        function obj = set.DRNLc(obj,value)
+            [nRows,nCols] = size(value);
+            assert(nRows==obj.numAudiometricFreqs && nCols==1, 'must be 6x1 column vector') %#ok<MCSUP>
+            assert(all(value)>=0 && all(value)<=1, 'all DRNLc values must be between 0 and 1')
+            obj = obj.flushAidData;
+            obj.DRNLc = value;
+        end
+        function obj = set.ARtau(obj,value)
+            assert(value>=1e-3 && value<=1, 'must be between 1e-3 and 1s')            
+            obj = obj.flushAidData;
+            obj.ARtau = value;
+        end
+        function obj = set.ARthreshold_dB(obj,value)
+            assert(value>0, 'set AR to a high value to disable it')            
+            obj = obj.flushAidData;
+            obj.ARthreshold_dB = value;
+        end
+        function obj = set.MOCtau(obj,value)
+            assert(value>=1e-3 && value<=2, 'must be between 1e-3 and 2s')            
+            obj = obj.flushAidData;
+            obj.MOCtau = value;
+        end
+        function obj = set.MOCfactor(obj,value)
+            assert(value>=0 && value<=1, 'must be between 0 and 1')
+            obj = obj.flushAidData;
+            obj.MOCfactor = value;
+        end
+        function obj = set.bwOct(obj,value)
+            assert(value==1/1 || value==1/2 || value==1/3 || value==1/4 || value==1/5, 'must be one of 1./(1:5)')
+            obj = obj.flushAidData;
+            obj.bwOct = value;
+        end
+        function obj = set.filterOrder(obj,value)
+            assert(value>0 && value<5, 'must be one of 1:4')
+            obj = obj.flushAidData;
+            obj.filterOrder = value;
+        end
+        function obj = set.useGTF(obj,value)
+            obj = obj.flushAidData;
+            obj.useGTF = value;
+        end                    
+        
+        %% **********************************************************
+        % flushAidData        
+        % This second function is a workaround allowing a set method to
+        % change another property value.
+        %************************************************************
+        function obj = flushAidData(obj) 
+            obj.aidOP = [];
+            obj.MOCrecord = [];
+        end 
+        
+        
+        %% **********************************************************
+        % OVERLOADED plot method 
+        %************************************************************
+        function plot(obj)
+            clf
+            sig2dBSPL = @(sig)20*log10(abs(sig/20e-6)+(1/(2^32)));
+            dt = 1/obj.sr;
+            tAxis = dt:dt:dt*size(obj.stimulusINTERNAL,1);
+            
+            subplot(2,1,1)
+            plot(tAxis(1:length(obj.stimulusUSER)), sig2dBSPL(obj.stimulusUSER), 'k')
+            if ~isempty(obj.aidOPnice)
+                hold on
+                plot(tAxis(1:length(obj.stimulusUSER)), sig2dBSPL(obj.aidOPnice), 'r')
+            end                                   
+            ylim([0 100])
+            xlim([0 tAxis(length(obj.stimulusUSER))])
+            title('Level response')
+            xlabel('Time in seconds')
+            ylabel('Level in dB SPL')
+            
+            subplot(2,1,2) 
+            if ~isempty(obj.MOCrecord)
+                imagesc(tAxis, 1:obj.numChannels, flipud(-20*log10(obj.MOCrecord)))
+                colorbar                               
+            end         
+            title('MOC attenuation')
+            xlabel('Time in seconds')
+            ylabel('Band frequency in Hz')
+            numSpacers = 1 + (obj.numChannels-numel(obj.DRNLc)) / (numel(obj.DRNLc)-1);
+            set(gca, 'YTick', 1:numSpacers:obj.numChannels);
+            set(gca, 'YTickLabel', num2str(flipud([250; 500; 1000; 2000; 4000; 8000])));
+        end% ------ OVERLOADED plot method
+        
+        %% **********************************************************
+        % OVERLOADED soundsc method 
+        %************************************************************
+        function soundsc(obj)
+            soundsc(obj.aidOPnice, obj.sr)
+        end                 
+        
+        %% **********************************************************
+        % processStim
+        %************************************************************
+        function obj = processStim(obj)
+            %--------------------------------------------------------------
+            % EMULATION OF THE GUI PARAMETER CONVERSIONS
+            %--------------------------------------------------------------
+            biggestNumSamples = obj.numSamples; 
+            
+            filterStatesL = (zeros(3000,1));
+            filterStatesR = filterStatesL;
+            filterCoeffs = (zeros(5000,1));
+            
+            %filter coefficients
+            ARcutOff=1/(2*pi*obj.ARtau);
+            [b,a] = butter(1,ARcutOff/(obj.sr/2));
+            filterCoeffs(obj.enumC_ARb+1:obj.enumC_ARb+2) = b;
+            filterCoeffs(obj.enumC_ARa+1:obj.enumC_ARa+2) = a;
+            
+            MOCcutOff=1/(2*pi*obj.MOCtau);
+            [bMOC,aMOC] = butter(1,MOCcutOff/(obj.sr/2));
+            filterCoeffs(obj.enumC_MOCb+1:obj.enumC_MOCb+2) = bMOC;
+            filterCoeffs(obj.enumC_MOCa+1:obj.enumC_MOCa+2) = aMOC;
+            
+            
+            for filterCount = 1:obj.numChannels
+                %-----------------------------------
+                % nonlinear path - filter bws
+                %-----------------------------------
+                lowerCutOff=obj.channelBFs(filterCount)*2^(-obj.bwOct_INTERP(filterCount)/2);
+                upperCutOff=obj.channelBFs(filterCount)*2^( obj.bwOct_INTERP(filterCount)/2);
+                
+                if obj.useGTF
+                    bwHz = upperCutOff - lowerCutOff;
+                    [b_DRNL,a_DRNL] = obj.gammatone(bwHz, obj.channelBFs(filterCount), 1/obj.sr);
+                    filterCoeffs(10*(filterCount-1)+9 :10*(filterCount-1)+10) = b_DRNL;
+                    filterCoeffs(10*(filterCount-1)+14:10*(filterCount-1)+16) = a_DRNL;
+                else                                             
+                    [b_DRNL,a_DRNL] = butter(2,[lowerCutOff upperCutOff]/(obj.sr/2));
+                    filterCoeffs(10*(filterCount-1)+9 :10*(filterCount-1)+13) = b_DRNL;
+                    filterCoeffs(10*(filterCount-1)+14:10*(filterCount-1)+18) = a_DRNL;
+                end
+            end
+            
+            %--------------------------------------------------------------
+            % EMULATION OF THE IO CALLBACK THREAD
+            %--------------------------------------------------------------
+            frameBufferL = buffer(obj.stimulusINTERNAL(:,1), obj.numSamples);
+            frameBufferR = buffer(obj.stimulusINTERNAL(:,2), obj.numSamples);
+            nFrames = size(frameBufferL,2);
+            
+            pad = zeros(1,biggestNumSamples-obj.numSamples);
+            ARampL=ones(1,biggestNumSamples);
+            ARampR = ARampL;
+            MOCcontrol = ones(obj.numChannels, biggestNumSamples);
+            
+            peakIPL = zeros(5,1);
+            peakOPL = peakIPL;
+            rmsIPL  = peakIPL;
+            rmsOPL  = peakIPL;
+            
+            peakIPR = peakIPL;
+            peakOPR = peakIPL;
+            rmsIPR  = peakIPL;
+            rmsOPR  = peakIPL;
+            
+            MOCend = zeros(obj.numChannels,1);
+            
+            op = [];
+            moc= [];
+            for nn = 1:nFrames
+                frameBufferPadL = [frameBufferL(:,nn)' pad];
+                frameBufferPadR = [frameBufferR(:,nn)' pad];
+                
+                [ outBufferL, outBufferR, filterStatesL, filterStatesR,  ARampL, ARampR, MOCend, peakIPL, peakOPL, rmsIPL, rmsOPL, peakIPR, peakOPR, rmsIPR, rmsOPR, MOCcontrol ] =...
+                    EssexAidProcessVFrameSwitchable( ...
+                    frameBufferPadL,...
+                    frameBufferPadR,...
+                    filterStatesL,...
+                    filterStatesR,...
+                    filterCoeffs,...
+                    obj.numChannels,...
+                    obj.numSamples,...
+                    ARampL,...
+                    ARampR,...
+                    obj.ARthresholdPa,...
+                    obj.filterOrder,...
+                    obj.DRNLb_INTERP,...
+                    obj.DRNLc_INTERP,...
+                    obj.TM_dBO_INTERP,...
+                    obj.MOCfactor,...
+                    peakIPL,...
+                    peakOPL,...
+                    rmsIPL,...
+                    rmsOPL,...
+                    peakIPR,...
+                    peakOPR,...
+                    rmsIPR,...
+                    rmsOPR,...
+                    MOCend,...
+                    MOCcontrol,...
+                    obj.mainGain_INTERP,...
+                    obj.useGTF);
+                                
+                
+                outBuffer = ( [outBufferL(:, 1:obj.numSamples); outBufferR(:, 1:obj.numSamples)] );                
+                op = [op outBuffer]; %#ok<AGROW>   
+                moc= [moc MOCcontrol]; %#ok<AGROW>
+                
+            end %End of frame processing emulation loop
+            obj.aidOP = op;
+            obj.MOCrecord=moc;
+                       
+            
+        end %End of process stim method          
+        
+    end %End of methods block
+    
+    %%  *********************************************************
+    %      _        _   _                       _   _               _
+    %     | |      | | (_)                     | | | |             | |
+    %  ___| |_ __ _| |_ _  ___   _ __ ___   ___| |_| |__   ___   __| |___
+    % / __| __/ _` | __| |/ __| | '_ ` _ \ / _ \ __| '_ \ / _ \ / _` / __|
+    % \__ \ || (_| | |_| | (__  | | | | | |  __/ |_| | | | (_) | (_| \__ \
+    % |___/\__\__,_|\__|_|\___| |_| |_| |_|\___|\__|_| |_|\___/ \__,_|___/
+    %************************************************************
+    
+    methods(Static)        
+        %% ********************************************************
+        % pipOut - sequence of tone pips at various levels
+        %**********************************************************        
+        function pipOut = pipSequence(sampleRate, freq, dBlevs, pulseDur, silDur)
+            if nargin < 5
+                silDur = 0.3;
+            end
+            if nargin < 4
+                pulseDur = 0.1;
+            end
+            if nargin < 3
+                dBlevs = 20:20:100;
+            end
+            if nargin < 2
+                freq = 500;
+            end
+            if nargin < 1
+                sampleRate = 48e3;
+            end
+            
+            dt = 1/sampleRate;
+            tAxis = dt:dt:pulseDur;
+            sPulse = sin(2*pi*freq*tAxis);
+            sPulse = sPulse./sqrt(mean(sPulse.^2));
+            rms2dBspl = @(dBspl)20e-6*10^(dBspl/20); %sneaky short-hand function by (ab)using function handles
+            zPad = zeros(1,ceil(sampleRate*silDur));
+            
+            pipOut = [];
+            for nn = 1:numel(dBlevs)                
+                pipOut = [ pipOut sPulse*rms2dBspl(dBlevs(nn))  zPad]; %#ok<AGROW>
+            end
+
+        end% ------ OF pipSequence
+        
+        %% ********************************************************
+        % interpPars - Linear interpolation of given parameter to mimic GUI
+        % fitting functionality.
+        %**********************************************************        
+        function fullArray = interpPars(shortArray, numBands)
+            nGUIbands = numel(shortArray);
+            if numBands == nGUIbands
+                fullArray = shortArray;
+            else
+                numSpacers = (numBands-nGUIbands) / (nGUIbands-1);
+                fullArray = shortArray(1);
+                for nn = 2:nGUIbands
+                    fullArray = [fullArray,...
+                        repmat(mean([shortArray(nn) shortArray(nn-1)]),1,numSpacers),...
+                        shortArray(nn)]; %#ok<AGROW>
+                end                    
+            end                        
+        end% ----- OF interpPars
+        
+        %% ********************************************************
+        % gammatone - get filter coefficients
+        %********************************************************** 
+        function [b,a] = gammatone(bw, cf, dt)
+            phi = 2 * pi * bw * dt;
+            theta = 2 * pi * cf * dt;
+            cos_theta = cos(theta);
+            sin_theta = sin(theta);
+            alpha = -exp(-phi) * cos_theta;
+            b0 = 1.0;
+            b1 = 2 * alpha;
+            b2 = exp(-2 * phi);
+            z1 = (1 + alpha * cos_theta) - (alpha * sin_theta) * 1i;
+            z2 = (1 + b1 * cos_theta) - (b1 * sin_theta) * 1i;
+            z3 = (b2 * cos(2 * theta)) - (b2 * sin(2 * theta)) * 1i;
+            tf = (z2 + z3) / z1;
+            a0 = abs(tf);
+            a1 = alpha * a0;
+            
+            a = [b0, b1, b2];
+            b = [a0, a1];
+        end% ------ OF gammatone
+    end% ------ OF static methods
+    
+end %End of classdef
+