annotate 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
rev   line source
rmeddis@38 1 classdef cEssexAid
rmeddis@38 2 %ESSEXAID_WRAPCLASS Wrapper for the EssexAid - Nick Clark July 2011
rmeddis@38 3 % This class wraps up the EssexAid algorithm function that processes
rmeddis@38 4 % each block of samples. This wrapper closely emulates the GUI used
rmeddis@38 5 % in the lab and runs stimuli through the exact same algorithm used
rmeddis@38 6 % in the lab. It even includes a helper function to generate C code
rmeddis@38 7 % from the algorithm for use in a real-time framework.
rmeddis@38 8
rmeddis@38 9
rmeddis@38 10 %% *********************************************************
rmeddis@38 11 % properties _ _
rmeddis@38 12 % | | (_)
rmeddis@38 13 % _ __ _ __ ___ _ __ ___ _ __| |_ _ ___ ___
rmeddis@38 14 % | '_ \| '__/ _ \| '_ \ / _ \ '__| __| |/ _ \/ __|
rmeddis@38 15 % | |_) | | | (_) | |_) | __/ | | |_| | __/\__ \
rmeddis@38 16 % | .__/|_| \___/| .__/ \___|_| \__|_|\___||___/
rmeddis@38 17 % | | | |
rmeddis@38 18 % |_| |_|
rmeddis@38 19 %************************************************************
rmeddis@38 20
rmeddis@38 21 %% **********************************************************
rmeddis@38 22 % Public properties - can be set by user
rmeddis@38 23 %************************************************************
rmeddis@38 24 properties(Access = public)
rmeddis@38 25 sr = 48e3;
rmeddis@38 26 numSamples = 1024; %MAX=6912, LAB_USE=48
rmeddis@38 27 stimulusUSER
rmeddis@38 28
rmeddis@38 29 %------------------------------------------------------------------
rmeddis@38 30 % Params for audiometric freqs 250, 500, 1000, 2000, 4000, 8000 Hz
rmeddis@38 31 %------------------------------------------------------------------
rmeddis@38 32 audiometry_dB= [ 0; 0; 0; 0; 0; 0]; %Pure tone threshold in dB SPL
rmeddis@38 33 mainGain_dB = [ 0; 0; 0; 0; 0; 0]; %Gain applied at audiometric frequencies
rmeddis@38 34 TC_dBHL = [40; 40; 40; 40; 40; 40]; %Compression thresholds (in dB HL from 2nd filt)
rmeddis@38 35 TM_dBHL = [10; 10; 10; 10; 10; 10]; %MOC thresholds (in dB OUTPUT from 2nd filt)
rmeddis@38 36 DRNLc = [ 0.2; 0.2; 0.2; 0.2; 0.2; 0.2]; %Compression exponent at audiometric frequencies
rmeddis@38 37
rmeddis@38 38 %------------------------------------------------------------------
rmeddis@38 39 % Dynamic compression properties
rmeddis@38 40 %------------------------------------------------------------------
rmeddis@38 41 ARtau = 60e-3; %decay time constant
rmeddis@38 42 ARthreshold_dB = 85; %dB SPL (input signal level) =>200 to disable
rmeddis@38 43 MOCtau = 450e-3; %Time constant in Seconds
rmeddis@38 44 MOCfactor = 0.5; %dB attenuation applied to the input per dB exceeding output threshold
rmeddis@38 45
rmeddis@38 46 %------------------------------------------------------------------
rmeddis@38 47 % Band filtering properties
rmeddis@38 48 %------------------------------------------------------------------
rmeddis@38 49 bwOct = 1/2; %1/1, 1/2, 1/3, 1/4, 1/5
rmeddis@38 50 filterOrder = 2 %BUTTER=2, GTF=3
rmeddis@38 51 useGTF = false; %If false, revert to butterworth
rmeddis@38 52 end
rmeddis@38 53
rmeddis@38 54 %% **********************************************************
rmeddis@38 55 % Read only properties that are not dependent
rmeddis@38 56 %************************************************************
rmeddis@38 57 properties(SetAccess = private)
rmeddis@38 58 MOCrecord
rmeddis@38 59 end
rmeddis@38 60
rmeddis@38 61 %% **********************************************************
rmeddis@38 62 % Constant properties
rmeddis@38 63 %************************************************************
rmeddis@38 64 properties(Constant = true, Hidden = true)
rmeddis@38 65 numAudiometricFreqs = 6;
rmeddis@38 66 end
rmeddis@38 67
rmeddis@38 68 %% **********************************************************
rmeddis@38 69 % Dependent visable properties - calculated at runtime
rmeddis@38 70 %************************************************************
rmeddis@38 71 properties(Dependent = true, Hidden = false)
rmeddis@38 72 channelBFs %= 250 * 2.^((0:fNmax)'*params.bwOct);
rmeddis@38 73 numChannels %= numel(channelBFs);
rmeddis@38 74 aidOPnice %aid output reformatted to be exactly the same dimensions as the input stimulus
rmeddis@38 75 end
rmeddis@38 76
rmeddis@38 77 %% **********************************************************
rmeddis@38 78 % Dependent invisable properties - calculated at runtime
rmeddis@38 79 %************************************************************
rmeddis@38 80 properties(Dependent = true, Hidden = true)
rmeddis@38 81 TC_dBO_INTERP % Compression threshold in terms of 2nd filter o/p in dB SPL
rmeddis@38 82 TM_dBO_INTERP % MOC threshold in terms of 2nd filter o/p in dB SPL
rmeddis@38 83 bwOct_INTERP
rmeddis@38 84 DRNLb_INTERP %= ( 2e-5 .* 10.^(TCdBO/20)) .^ (1-DRNLc) ;
rmeddis@38 85 DRNLc_INTERP
rmeddis@38 86 mainGain_INTERP %Interp'd and in linear units
rmeddis@38 87
rmeddis@38 88 ARthresholdPa %= 20e-6*10^(ARthreshold_dB/20);% Pa thresh for triggering AR
rmeddis@38 89 stimulusINTERNAL %input stimulus in correct format for the Aid algo
rmeddis@38 90 end
rmeddis@38 91
rmeddis@38 92 %% **********************************************************
rmeddis@38 93 % Protected properties - The user never needs to set
rmeddis@38 94 %************************************************************
rmeddis@38 95 properties(Access = protected)
rmeddis@38 96 aidOP
rmeddis@38 97 emlc_z
rmeddis@38 98
rmeddis@38 99 %--------------------------------------------------------------
rmeddis@38 100 % ENUMERATIONS USED IN THE FRAME PROCESSOR
rmeddis@38 101 %--------------------------------------------------------------
rmeddis@38 102 enumC_ARb = 0;
rmeddis@38 103 enumC_ARa = 2;
rmeddis@38 104 enumC_MOCb = 4;
rmeddis@38 105 enumC_MOCa = 6;
rmeddis@38 106
rmeddis@38 107 % enumC_BPb1 = 8;
rmeddis@38 108 % enumC_BPa1 = 13;
rmeddis@38 109 % enumC_BPb2 = 18;
rmeddis@38 110 % enumC_BPa2 = 23;
rmeddis@38 111 % enumC_BPb3 = 28;
rmeddis@38 112 % enumC_BPa3 = 33;
rmeddis@38 113 % enumC_BPb4 = 38;
rmeddis@38 114 % enumC_BPa4 = 43;
rmeddis@38 115
rmeddis@38 116 enumS_AR = 0;
rmeddis@38 117
rmeddis@38 118 % enumS_MOC1 = 1;
rmeddis@38 119 % enumS_BPin_1_1 = 2;
rmeddis@38 120 % enumS_BPin_2_1 = 6;
rmeddis@38 121 % enumS_BPout_1_1 = 10;
rmeddis@38 122 % enumS_BPout_2_1 = 14;
rmeddis@38 123 %
rmeddis@38 124 % enumS_MOC2 = 18;
rmeddis@38 125 % enumS_BPin_1_2 = 19;
rmeddis@38 126 % enumS_BPin_2_2 = 23;
rmeddis@38 127 % enumS_BPout_1_2 = 27;
rmeddis@38 128 % enumS_BPout_2_2 = 31;
rmeddis@38 129 % ...
rmeddis@38 130 end
rmeddis@38 131
rmeddis@38 132 %% **********************************************************
rmeddis@38 133 % methods _ _ _
rmeddis@38 134 % | | | | | |
rmeddis@38 135 % _ __ ___ ___| |_| |__ ___ __| |___
rmeddis@38 136 %| '_ ` _ \ / _ \ __| '_ \ / _ \ / _` / __|
rmeddis@38 137 %| | | | | | __/ |_| | | | (_) | (_| \__ \
rmeddis@38 138 %|_| |_| |_|\___|\__|_| |_|\___/ \__,_|___/
rmeddis@38 139 %************************************************************
rmeddis@38 140
rmeddis@38 141 methods
rmeddis@38 142 %% **********************************************************
rmeddis@38 143 % Constructor
rmeddis@38 144 %************************************************************
rmeddis@38 145 function obj = EssexAid_WrapClass(sr, stimulus)
rmeddis@38 146
rmeddis@38 147 if nargin > 0
rmeddis@38 148 obj.sr = sr;
rmeddis@38 149 end
rmeddis@38 150
rmeddis@38 151 if nargin > 1
rmeddis@38 152 obj.stimulusUSER = stimulus;
rmeddis@38 153 else
rmeddis@38 154 obj.stimulusUSER = obj.pipSequence(obj.sr);
rmeddis@38 155 end
rmeddis@38 156 end
rmeddis@38 157
rmeddis@38 158 %% **********************************************************
rmeddis@38 159 % Get method for channelBFs
rmeddis@38 160 %************************************************************
rmeddis@38 161 function value = get.channelBFs(obj)
rmeddis@38 162 fNmax = 5/obj.bwOct;
rmeddis@38 163 value = 250 * 2.^((0:fNmax)'*obj.bwOct);
rmeddis@38 164 end
rmeddis@38 165
rmeddis@38 166 %% **********************************************************
rmeddis@38 167 % Get method for numChannels
rmeddis@38 168 %************************************************************
rmeddis@38 169 function value = get.numChannels(obj)
rmeddis@38 170 value = numel(obj.channelBFs);
rmeddis@38 171 end
rmeddis@38 172
rmeddis@38 173 %% **********************************************************
rmeddis@38 174 % Get method for ARthresholdPa
rmeddis@38 175 %************************************************************
rmeddis@38 176 function value = get.ARthresholdPa(obj)
rmeddis@38 177 value = 20e-6*10^(obj.ARthreshold_dB/20);% Pa thresh for triggering AR
rmeddis@38 178 end
rmeddis@38 179
rmeddis@38 180 %% **********************************************************
rmeddis@38 181 % Get method for TC_dBO_INTERP
rmeddis@38 182 %************************************************************
rmeddis@38 183 function value = get.TC_dBO_INTERP(obj)
rmeddis@38 184 TC_dBO = obj.audiometry_dB - obj.mainGain_dB + obj.TC_dBHL;
rmeddis@38 185 value = obj.interpPars(TC_dBO, obj.numChannels);
rmeddis@38 186 end
rmeddis@38 187
rmeddis@38 188 %% **********************************************************
rmeddis@38 189 % Get method for TM_dBO_INTERP
rmeddis@38 190 %************************************************************
rmeddis@38 191 function value = get.TM_dBO_INTERP(obj)
rmeddis@38 192 TM_dBO = obj.audiometry_dB - obj.mainGain_dB + obj.TM_dBHL;
rmeddis@38 193 value = obj.interpPars(TM_dBO, obj.numChannels);
rmeddis@38 194 end
rmeddis@38 195
rmeddis@38 196 %% **********************************************************
rmeddis@38 197 % Get method for bwOct_INTERP
rmeddis@38 198 %************************************************************
rmeddis@38 199 function value = get.bwOct_INTERP(obj)
rmeddis@38 200 value = repmat(obj.bwOct, 1, obj.numChannels);
rmeddis@38 201 end
rmeddis@38 202
rmeddis@38 203 %% **********************************************************
rmeddis@38 204 % Get method for DRNLb_INTERP
rmeddis@38 205 %************************************************************
rmeddis@38 206 function value = get.DRNLb_INTERP(obj)
rmeddis@38 207 value = ( 2e-5 .* 10.^(obj.TC_dBO_INTERP/20)) .^ (1-obj.DRNLc_INTERP);
rmeddis@38 208 end
rmeddis@38 209
rmeddis@38 210 %% **********************************************************
rmeddis@38 211 % Get method for DRNLc_INTERP
rmeddis@38 212 %************************************************************
rmeddis@38 213 function value = get.DRNLc_INTERP(obj)
rmeddis@38 214 value = obj.interpPars(obj.DRNLc, obj.numChannels);
rmeddis@38 215 end
rmeddis@38 216
rmeddis@38 217 %% **********************************************************
rmeddis@38 218 % Get method for mainGain_INTERP
rmeddis@38 219 %************************************************************
rmeddis@38 220 function value = get.mainGain_INTERP(obj)
rmeddis@38 221 mainGainLin = 10.^(obj.mainGain_dB/20); %lin units
rmeddis@38 222 value = obj.interpPars(mainGainLin, obj.numChannels);
rmeddis@38 223 end
rmeddis@38 224
rmeddis@38 225 %% ***********************************************************
rmeddis@38 226 % Get method for stimulus
rmeddis@38 227 % -----------------------
rmeddis@38 228 % The hearing aid expects a stereo signal, as the MOC control is
rmeddis@38 229 % linked for left and right channels. It would be more efficient to
rmeddis@38 230 % use a mono version of the aid for simulation in Matlab. However,
rmeddis@38 231 % I always want to use the exact same code for the hardware in the
rmeddis@38 232 % lab and current simulations. This code will make a mono signal
rmeddis@38 233 % stereo if needs be and/or rotate to 2xN array.
rmeddis@38 234 %*************************************************************
rmeddis@38 235 function value = get.stimulusINTERNAL(obj)
rmeddis@38 236 [nRows, nCols] = size(obj.stimulusUSER);
rmeddis@38 237
rmeddis@38 238 % Assume that the stimulus duration is greater than 2 samples.
rmeddis@38 239 % Therefore the number of channels is the min dim.
rmeddis@38 240 [nChans, I] = min([nRows nCols]);
rmeddis@38 241
rmeddis@38 242 if nChans == 2
rmeddis@38 243 if I == 2
rmeddis@38 244 value = obj.stimulusUSER;
rmeddis@38 245 else
rmeddis@38 246 value = obj.stimulusUSER';
rmeddis@38 247 end
rmeddis@38 248 elseif nChans == 1 %Just to be explicit
rmeddis@38 249 if I == 2
rmeddis@38 250 value = [obj.stimulusUSER obj.stimulusUSER];
rmeddis@38 251 else
rmeddis@38 252 value = [obj.stimulusUSER; obj.stimulusUSER]';
rmeddis@38 253 end
rmeddis@38 254 end
rmeddis@38 255 end
rmeddis@38 256
rmeddis@38 257 %% ***********************************************************
rmeddis@38 258 % Get method for aid output
rmeddis@38 259 % -----------------------
rmeddis@38 260 % This get method is linked to the above internal stimulus method
rmeddis@38 261 % and allows the user to extract the hearing aid output in exactly
rmeddis@38 262 % the same shape and size as the original input stimulus. This is
rmeddis@38 263 % very useful for the speech recognition work and presumably
rmeddis@38 264 % for multithreshold also.
rmeddis@38 265 %*************************************************************
rmeddis@38 266 function value = get.aidOPnice(obj)
rmeddis@38 267 if ~isempty(obj.aidOP)
rmeddis@38 268 [nRows, nCols] = size(obj.stimulusUSER);
rmeddis@38 269
rmeddis@38 270 % Assume that the stimulus duration is greater than 2 samples.
rmeddis@38 271 % Therefore the number of channels is the min dim.
rmeddis@38 272 [nChans, I] = min([nRows nCols]);
rmeddis@38 273
rmeddis@38 274 %** The aid output will ALWAYS be a 2xN array **
rmeddis@38 275 %The fist job is to remove trailing zeros that may have been
rmeddis@38 276 %introduced by the framing process
rmeddis@38 277 aidOPtruncated = obj.aidOP(:, 1:max([nRows nCols]));
rmeddis@38 278
rmeddis@38 279 %The next task is to arrange the op like the ip
rmeddis@38 280 if nChans == 2
rmeddis@38 281 if I == 1
rmeddis@38 282 value = aidOPtruncated;
rmeddis@38 283 else
rmeddis@38 284 value = aidOPtruncated';
rmeddis@38 285 end
rmeddis@38 286 elseif nChans == 1 %Just to be explicit
rmeddis@38 287 if I == 1
rmeddis@38 288 value = aidOPtruncated(1,:);
rmeddis@38 289 else
rmeddis@38 290 value = aidOPtruncated(1,:)';
rmeddis@38 291 end
rmeddis@38 292 end
rmeddis@38 293 else % ---- of if isempty statement
rmeddis@38 294 value = [];
rmeddis@38 295 end
rmeddis@38 296 end
rmeddis@38 297
rmeddis@38 298 %% ***********************************************************
rmeddis@38 299 % *** Set methods ***
rmeddis@38 300 % -----------------------
rmeddis@38 301 % This is a bunch of unexciting error hunting functions. They also
rmeddis@38 302 % flush the aid output if any parameters change. Therefore,
rmeddis@38 303 % processStim will have to be called explicity by the user once
rmeddis@38 304 % again.
rmeddis@38 305 %*************************************************************
rmeddis@38 306 function obj = set.stimulusUSER(obj,value)
rmeddis@38 307 [nRows, nCols] = size(value);
rmeddis@38 308
rmeddis@38 309 % Assume that the stimulus duration is greater than 2 samples.
rmeddis@38 310 % Therefore the number of channels is the min dim.
rmeddis@38 311 nChans = min([nRows nCols]);
rmeddis@38 312 assert(nChans<3 && nChans, 'Number of stimulus channels must be 1 or 2')
rmeddis@38 313
rmeddis@38 314 obj = obj.flushAidData; %flush any previous hearing aid data if the input stimulus changes
rmeddis@38 315 obj.stimulusUSER = value;
rmeddis@38 316 end
rmeddis@38 317 function obj = set.sr(obj,value)
rmeddis@38 318 assert(value>=20e3 && value<=192e3, 'sr must be between 20 and 192 kHz')
rmeddis@38 319 obj = obj.flushAidData;
rmeddis@38 320 obj.sr = value;
rmeddis@38 321 end
rmeddis@38 322 function obj = set.numSamples(obj,value)
rmeddis@38 323 assert(value>=48 && value<=6912, 'must be between 48 and 6912 samples')
rmeddis@38 324 obj = obj.flushAidData;
rmeddis@38 325 obj.numSamples = value;
rmeddis@38 326 end
rmeddis@38 327 function obj = set.audiometry_dB(obj,value)
rmeddis@38 328 [nRows,nCols] = size(value);
rmeddis@38 329 assert(nRows==obj.numAudiometricFreqs && nCols==1, 'must be 6x1 column vector') %#ok<MCSUP>
rmeddis@38 330 obj = obj.flushAidData;
rmeddis@38 331 obj.audiometry_dB = value;
rmeddis@38 332 end
rmeddis@38 333 function obj = set.mainGain_dB(obj,value)
rmeddis@38 334 [nRows,nCols] = size(value);
rmeddis@38 335 assert(nRows==obj.numAudiometricFreqs && nCols==1, 'must be 6x1 column vector') %#ok<MCSUP>
rmeddis@38 336 obj = obj.flushAidData;
rmeddis@38 337 obj.mainGain_dB = value;
rmeddis@38 338 end
rmeddis@38 339 function obj = set.TC_dBHL(obj,value)
rmeddis@38 340 [nRows,nCols] = size(value);
rmeddis@38 341 assert(nRows==obj.numAudiometricFreqs && nCols==1, 'must be 6x1 column vector') %#ok<MCSUP>
rmeddis@38 342 obj = obj.flushAidData;
rmeddis@38 343 obj.TC_dBHL = value;
rmeddis@38 344 end
rmeddis@38 345 function obj = set.TM_dBHL(obj,value)
rmeddis@38 346 [nRows,nCols] = size(value);
rmeddis@38 347 assert(nRows==obj.numAudiometricFreqs && nCols==1, 'must be 6x1 column vector') %#ok<MCSUP>
rmeddis@38 348 obj = obj.flushAidData;
rmeddis@38 349 obj.TM_dBHL = value;
rmeddis@38 350 end
rmeddis@38 351 function obj = set.DRNLc(obj,value)
rmeddis@38 352 [nRows,nCols] = size(value);
rmeddis@38 353 assert(nRows==obj.numAudiometricFreqs && nCols==1, 'must be 6x1 column vector') %#ok<MCSUP>
rmeddis@38 354 assert(all(value)>=0 && all(value)<=1, 'all DRNLc values must be between 0 and 1')
rmeddis@38 355 obj = obj.flushAidData;
rmeddis@38 356 obj.DRNLc = value;
rmeddis@38 357 end
rmeddis@38 358 function obj = set.ARtau(obj,value)
rmeddis@38 359 assert(value>=1e-3 && value<=1, 'must be between 1e-3 and 1s')
rmeddis@38 360 obj = obj.flushAidData;
rmeddis@38 361 obj.ARtau = value;
rmeddis@38 362 end
rmeddis@38 363 function obj = set.ARthreshold_dB(obj,value)
rmeddis@38 364 assert(value>0, 'set AR to a high value to disable it')
rmeddis@38 365 obj = obj.flushAidData;
rmeddis@38 366 obj.ARthreshold_dB = value;
rmeddis@38 367 end
rmeddis@38 368 function obj = set.MOCtau(obj,value)
rmeddis@38 369 assert(value>=1e-3 && value<=2, 'must be between 1e-3 and 2s')
rmeddis@38 370 obj = obj.flushAidData;
rmeddis@38 371 obj.MOCtau = value;
rmeddis@38 372 end
rmeddis@38 373 function obj = set.MOCfactor(obj,value)
rmeddis@38 374 assert(value>=0 && value<=1, 'must be between 0 and 1')
rmeddis@38 375 obj = obj.flushAidData;
rmeddis@38 376 obj.MOCfactor = value;
rmeddis@38 377 end
rmeddis@38 378 function obj = set.bwOct(obj,value)
rmeddis@38 379 assert(value==1/1 || value==1/2 || value==1/3 || value==1/4 || value==1/5, 'must be one of 1./(1:5)')
rmeddis@38 380 obj = obj.flushAidData;
rmeddis@38 381 obj.bwOct = value;
rmeddis@38 382 end
rmeddis@38 383 function obj = set.filterOrder(obj,value)
rmeddis@38 384 assert(value>0 && value<5, 'must be one of 1:4')
rmeddis@38 385 obj = obj.flushAidData;
rmeddis@38 386 obj.filterOrder = value;
rmeddis@38 387 end
rmeddis@38 388 function obj = set.useGTF(obj,value)
rmeddis@38 389 obj = obj.flushAidData;
rmeddis@38 390 obj.useGTF = value;
rmeddis@38 391 end
rmeddis@38 392
rmeddis@38 393 %% **********************************************************
rmeddis@38 394 % flushAidData
rmeddis@38 395 % This second function is a workaround allowing a set method to
rmeddis@38 396 % change another property value.
rmeddis@38 397 %************************************************************
rmeddis@38 398 function obj = flushAidData(obj)
rmeddis@38 399 obj.aidOP = [];
rmeddis@38 400 obj.MOCrecord = [];
rmeddis@38 401 end
rmeddis@38 402
rmeddis@38 403
rmeddis@38 404 %% **********************************************************
rmeddis@38 405 % OVERLOADED plot method
rmeddis@38 406 %************************************************************
rmeddis@38 407 function plot(obj)
rmeddis@38 408 clf
rmeddis@38 409 sig2dBSPL = @(sig)20*log10(abs(sig/20e-6)+(1/(2^32)));
rmeddis@38 410 dt = 1/obj.sr;
rmeddis@38 411 tAxis = dt:dt:dt*size(obj.stimulusINTERNAL,1);
rmeddis@38 412
rmeddis@38 413 subplot(2,1,1)
rmeddis@38 414 plot(tAxis(1:length(obj.stimulusUSER)), sig2dBSPL(obj.stimulusUSER), 'k')
rmeddis@38 415 if ~isempty(obj.aidOPnice)
rmeddis@38 416 hold on
rmeddis@38 417 plot(tAxis(1:length(obj.stimulusUSER)), sig2dBSPL(obj.aidOPnice), 'r')
rmeddis@38 418 end
rmeddis@38 419 ylim([0 100])
rmeddis@38 420 xlim([0 tAxis(length(obj.stimulusUSER))])
rmeddis@38 421 title('Level response')
rmeddis@38 422 xlabel('Time in seconds')
rmeddis@38 423 ylabel('Level in dB SPL')
rmeddis@38 424
rmeddis@38 425 subplot(2,1,2)
rmeddis@38 426 if ~isempty(obj.MOCrecord)
rmeddis@38 427 imagesc(tAxis, 1:obj.numChannels, flipud(-20*log10(obj.MOCrecord)))
rmeddis@38 428 colorbar
rmeddis@38 429 end
rmeddis@38 430 title('MOC attenuation')
rmeddis@38 431 xlabel('Time in seconds')
rmeddis@38 432 ylabel('Band frequency in Hz')
rmeddis@38 433 numSpacers = 1 + (obj.numChannels-numel(obj.DRNLc)) / (numel(obj.DRNLc)-1);
rmeddis@38 434 set(gca, 'YTick', 1:numSpacers:obj.numChannels);
rmeddis@38 435 set(gca, 'YTickLabel', num2str(flipud([250; 500; 1000; 2000; 4000; 8000])));
rmeddis@38 436 end% ------ OVERLOADED plot method
rmeddis@38 437
rmeddis@38 438 %% **********************************************************
rmeddis@38 439 % OVERLOADED soundsc method
rmeddis@38 440 %************************************************************
rmeddis@38 441 function soundsc(obj)
rmeddis@38 442 soundsc(obj.aidOPnice, obj.sr)
rmeddis@38 443 end
rmeddis@38 444
rmeddis@38 445 %% **********************************************************
rmeddis@38 446 % processStim
rmeddis@38 447 %************************************************************
rmeddis@38 448 function obj = processStim(obj)
rmeddis@38 449 %--------------------------------------------------------------
rmeddis@38 450 % EMULATION OF THE GUI PARAMETER CONVERSIONS
rmeddis@38 451 %--------------------------------------------------------------
rmeddis@38 452 biggestNumSamples = obj.numSamples;
rmeddis@38 453
rmeddis@38 454 filterStatesL = (zeros(3000,1));
rmeddis@38 455 filterStatesR = filterStatesL;
rmeddis@38 456 filterCoeffs = (zeros(5000,1));
rmeddis@38 457
rmeddis@38 458 %filter coefficients
rmeddis@38 459 ARcutOff=1/(2*pi*obj.ARtau);
rmeddis@38 460 [b,a] = butter(1,ARcutOff/(obj.sr/2));
rmeddis@38 461 filterCoeffs(obj.enumC_ARb+1:obj.enumC_ARb+2) = b;
rmeddis@38 462 filterCoeffs(obj.enumC_ARa+1:obj.enumC_ARa+2) = a;
rmeddis@38 463
rmeddis@38 464 MOCcutOff=1/(2*pi*obj.MOCtau);
rmeddis@38 465 [bMOC,aMOC] = butter(1,MOCcutOff/(obj.sr/2));
rmeddis@38 466 filterCoeffs(obj.enumC_MOCb+1:obj.enumC_MOCb+2) = bMOC;
rmeddis@38 467 filterCoeffs(obj.enumC_MOCa+1:obj.enumC_MOCa+2) = aMOC;
rmeddis@38 468
rmeddis@38 469
rmeddis@38 470 for filterCount = 1:obj.numChannels
rmeddis@38 471 %-----------------------------------
rmeddis@38 472 % nonlinear path - filter bws
rmeddis@38 473 %-----------------------------------
rmeddis@38 474 lowerCutOff=obj.channelBFs(filterCount)*2^(-obj.bwOct_INTERP(filterCount)/2);
rmeddis@38 475 upperCutOff=obj.channelBFs(filterCount)*2^( obj.bwOct_INTERP(filterCount)/2);
rmeddis@38 476
rmeddis@38 477 if obj.useGTF
rmeddis@38 478 bwHz = upperCutOff - lowerCutOff;
rmeddis@38 479 [b_DRNL,a_DRNL] = obj.gammatone(bwHz, obj.channelBFs(filterCount), 1/obj.sr);
rmeddis@38 480 filterCoeffs(10*(filterCount-1)+9 :10*(filterCount-1)+10) = b_DRNL;
rmeddis@38 481 filterCoeffs(10*(filterCount-1)+14:10*(filterCount-1)+16) = a_DRNL;
rmeddis@38 482 else
rmeddis@38 483 [b_DRNL,a_DRNL] = butter(2,[lowerCutOff upperCutOff]/(obj.sr/2));
rmeddis@38 484 filterCoeffs(10*(filterCount-1)+9 :10*(filterCount-1)+13) = b_DRNL;
rmeddis@38 485 filterCoeffs(10*(filterCount-1)+14:10*(filterCount-1)+18) = a_DRNL;
rmeddis@38 486 end
rmeddis@38 487 end
rmeddis@38 488
rmeddis@38 489 %--------------------------------------------------------------
rmeddis@38 490 % EMULATION OF THE IO CALLBACK THREAD
rmeddis@38 491 %--------------------------------------------------------------
rmeddis@38 492 frameBufferL = buffer(obj.stimulusINTERNAL(:,1), obj.numSamples);
rmeddis@38 493 frameBufferR = buffer(obj.stimulusINTERNAL(:,2), obj.numSamples);
rmeddis@38 494 nFrames = size(frameBufferL,2);
rmeddis@38 495
rmeddis@38 496 pad = zeros(1,biggestNumSamples-obj.numSamples);
rmeddis@38 497 ARampL=ones(1,biggestNumSamples);
rmeddis@38 498 ARampR = ARampL;
rmeddis@38 499 MOCcontrol = ones(obj.numChannels, biggestNumSamples);
rmeddis@38 500
rmeddis@38 501 peakIPL = zeros(5,1);
rmeddis@38 502 peakOPL = peakIPL;
rmeddis@38 503 rmsIPL = peakIPL;
rmeddis@38 504 rmsOPL = peakIPL;
rmeddis@38 505
rmeddis@38 506 peakIPR = peakIPL;
rmeddis@38 507 peakOPR = peakIPL;
rmeddis@38 508 rmsIPR = peakIPL;
rmeddis@38 509 rmsOPR = peakIPL;
rmeddis@38 510
rmeddis@38 511 MOCend = zeros(obj.numChannels,1);
rmeddis@38 512
rmeddis@38 513 op = [];
rmeddis@38 514 moc= [];
rmeddis@38 515 for nn = 1:nFrames
rmeddis@38 516 frameBufferPadL = [frameBufferL(:,nn)' pad];
rmeddis@38 517 frameBufferPadR = [frameBufferR(:,nn)' pad];
rmeddis@38 518
rmeddis@38 519 [ outBufferL, outBufferR, filterStatesL, filterStatesR, ARampL, ARampR, MOCend, peakIPL, peakOPL, rmsIPL, rmsOPL, peakIPR, peakOPR, rmsIPR, rmsOPR, MOCcontrol ] =...
rmeddis@38 520 EssexAidProcessVFrameSwitchable( ...
rmeddis@38 521 frameBufferPadL,...
rmeddis@38 522 frameBufferPadR,...
rmeddis@38 523 filterStatesL,...
rmeddis@38 524 filterStatesR,...
rmeddis@38 525 filterCoeffs,...
rmeddis@38 526 obj.numChannels,...
rmeddis@38 527 obj.numSamples,...
rmeddis@38 528 ARampL,...
rmeddis@38 529 ARampR,...
rmeddis@38 530 obj.ARthresholdPa,...
rmeddis@38 531 obj.filterOrder,...
rmeddis@38 532 obj.DRNLb_INTERP,...
rmeddis@38 533 obj.DRNLc_INTERP,...
rmeddis@38 534 obj.TM_dBO_INTERP,...
rmeddis@38 535 obj.MOCfactor,...
rmeddis@38 536 peakIPL,...
rmeddis@38 537 peakOPL,...
rmeddis@38 538 rmsIPL,...
rmeddis@38 539 rmsOPL,...
rmeddis@38 540 peakIPR,...
rmeddis@38 541 peakOPR,...
rmeddis@38 542 rmsIPR,...
rmeddis@38 543 rmsOPR,...
rmeddis@38 544 MOCend,...
rmeddis@38 545 MOCcontrol,...
rmeddis@38 546 obj.mainGain_INTERP,...
rmeddis@38 547 obj.useGTF);
rmeddis@38 548
rmeddis@38 549
rmeddis@38 550 outBuffer = ( [outBufferL(:, 1:obj.numSamples); outBufferR(:, 1:obj.numSamples)] );
rmeddis@38 551 op = [op outBuffer]; %#ok<AGROW>
rmeddis@38 552 moc= [moc MOCcontrol]; %#ok<AGROW>
rmeddis@38 553
rmeddis@38 554 end %End of frame processing emulation loop
rmeddis@38 555 obj.aidOP = op;
rmeddis@38 556 obj.MOCrecord=moc;
rmeddis@38 557
rmeddis@38 558
rmeddis@38 559 end %End of process stim method
rmeddis@38 560
rmeddis@38 561 end %End of methods block
rmeddis@38 562
rmeddis@38 563 %% *********************************************************
rmeddis@38 564 % _ _ _ _ _ _
rmeddis@38 565 % | | | | (_) | | | | | |
rmeddis@38 566 % ___| |_ __ _| |_ _ ___ _ __ ___ ___| |_| |__ ___ __| |___
rmeddis@38 567 % / __| __/ _` | __| |/ __| | '_ ` _ \ / _ \ __| '_ \ / _ \ / _` / __|
rmeddis@38 568 % \__ \ || (_| | |_| | (__ | | | | | | __/ |_| | | | (_) | (_| \__ \
rmeddis@38 569 % |___/\__\__,_|\__|_|\___| |_| |_| |_|\___|\__|_| |_|\___/ \__,_|___/
rmeddis@38 570 %************************************************************
rmeddis@38 571
rmeddis@38 572 methods(Static)
rmeddis@38 573 %% ********************************************************
rmeddis@38 574 % pipOut - sequence of tone pips at various levels
rmeddis@38 575 %**********************************************************
rmeddis@38 576 function pipOut = pipSequence(sampleRate, freq, dBlevs, pulseDur, silDur)
rmeddis@38 577 if nargin < 5
rmeddis@38 578 silDur = 0.3;
rmeddis@38 579 end
rmeddis@38 580 if nargin < 4
rmeddis@38 581 pulseDur = 0.1;
rmeddis@38 582 end
rmeddis@38 583 if nargin < 3
rmeddis@38 584 dBlevs = 20:20:100;
rmeddis@38 585 end
rmeddis@38 586 if nargin < 2
rmeddis@38 587 freq = 500;
rmeddis@38 588 end
rmeddis@38 589 if nargin < 1
rmeddis@38 590 sampleRate = 48e3;
rmeddis@38 591 end
rmeddis@38 592
rmeddis@38 593 dt = 1/sampleRate;
rmeddis@38 594 tAxis = dt:dt:pulseDur;
rmeddis@38 595 sPulse = sin(2*pi*freq*tAxis);
rmeddis@38 596 sPulse = sPulse./sqrt(mean(sPulse.^2));
rmeddis@38 597 rms2dBspl = @(dBspl)20e-6*10^(dBspl/20); %sneaky short-hand function by (ab)using function handles
rmeddis@38 598 zPad = zeros(1,ceil(sampleRate*silDur));
rmeddis@38 599
rmeddis@38 600 pipOut = [];
rmeddis@38 601 for nn = 1:numel(dBlevs)
rmeddis@38 602 pipOut = [ pipOut sPulse*rms2dBspl(dBlevs(nn)) zPad]; %#ok<AGROW>
rmeddis@38 603 end
rmeddis@38 604
rmeddis@38 605 end% ------ OF pipSequence
rmeddis@38 606
rmeddis@38 607 %% ********************************************************
rmeddis@38 608 % interpPars - Linear interpolation of given parameter to mimic GUI
rmeddis@38 609 % fitting functionality.
rmeddis@38 610 %**********************************************************
rmeddis@38 611 function fullArray = interpPars(shortArray, numBands)
rmeddis@38 612 nGUIbands = numel(shortArray);
rmeddis@38 613 if numBands == nGUIbands
rmeddis@38 614 fullArray = shortArray;
rmeddis@38 615 else
rmeddis@38 616 numSpacers = (numBands-nGUIbands) / (nGUIbands-1);
rmeddis@38 617 fullArray = shortArray(1);
rmeddis@38 618 for nn = 2:nGUIbands
rmeddis@38 619 fullArray = [fullArray,...
rmeddis@38 620 repmat(mean([shortArray(nn) shortArray(nn-1)]),1,numSpacers),...
rmeddis@38 621 shortArray(nn)]; %#ok<AGROW>
rmeddis@38 622 end
rmeddis@38 623 end
rmeddis@38 624 end% ----- OF interpPars
rmeddis@38 625
rmeddis@38 626 %% ********************************************************
rmeddis@38 627 % gammatone - get filter coefficients
rmeddis@38 628 %**********************************************************
rmeddis@38 629 function [b,a] = gammatone(bw, cf, dt)
rmeddis@38 630 phi = 2 * pi * bw * dt;
rmeddis@38 631 theta = 2 * pi * cf * dt;
rmeddis@38 632 cos_theta = cos(theta);
rmeddis@38 633 sin_theta = sin(theta);
rmeddis@38 634 alpha = -exp(-phi) * cos_theta;
rmeddis@38 635 b0 = 1.0;
rmeddis@38 636 b1 = 2 * alpha;
rmeddis@38 637 b2 = exp(-2 * phi);
rmeddis@38 638 z1 = (1 + alpha * cos_theta) - (alpha * sin_theta) * 1i;
rmeddis@38 639 z2 = (1 + b1 * cos_theta) - (b1 * sin_theta) * 1i;
rmeddis@38 640 z3 = (b2 * cos(2 * theta)) - (b2 * sin(2 * theta)) * 1i;
rmeddis@38 641 tf = (z2 + z3) / z1;
rmeddis@38 642 a0 = abs(tf);
rmeddis@38 643 a1 = alpha * a0;
rmeddis@38 644
rmeddis@38 645 a = [b0, b1, b2];
rmeddis@38 646 b = [a0, a1];
rmeddis@38 647 end% ------ OF gammatone
rmeddis@38 648 end% ------ OF static methods
rmeddis@38 649
rmeddis@38 650 end %End of classdef
rmeddis@38 651