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