annotate cortical_model.py @ 7:0ee61aeb789a

* Changed normalisation of gammatone_filter * Changed run.py to test calc_loudness_quiet()
author Carl Bussey <c.bussey@se10.qmul.ac.uk>
date Mon, 24 Feb 2014 15:31:15 +0000
parents 546dfcd45281
children d5693c7aa0ae
rev   line source
c@0 1 """
c@0 2 A module used for auditory analysis.
c@0 3
c@0 4 Models currently implemented:
c@6 5 * Simple frequency-modulation analysis model.
c@6 6 * Glasberg and Moore model using gammatone filters
c@0 7
c@0 8 Packaged dependencies:
c@6 9 > utils.py and/or utils.pyc
c@6 10 > erb.dat
c@6 11 > outMidFir.dat
c@6 12 > tq.dat
c@0 13
c@0 14 External dependencies:
c@6 15 > scipy
c@6 16 > numpy
c@6 17 > copy
c@6 18 > matplotlib
c@6 19
c@6 20 Primary functions:
c@7 21 > calc_partial_loudness
c@7 22 > calc_loudness
c@6 23 > get_specific_loudness_noise
c@6 24 > get_specific_loudness_quiet
c@6 25 > get_excitation_i
c@6 26 > plot_excitation_response
c@6 27 > gammatone_filter
c@6 28 > holdsworth_gammatone_filter
c@0 29 """
c@0 30
c@5 31 import sys
c@0 32 import utils
c@0 33 import scipy.signal as sp
c@1 34 import scipy.fftpack as fft
c@0 35 import numpy as np
c@0 36 from copy import deepcopy
c@0 37 import matplotlib.pyplot as plt
c@0 38
c@6 39 def calc_loudness_noise(input, fs, SPL, rectify=False, inc_loud_region = False, verbose = False):
c@5 40
c@6 41 numTracks = np.shape(input)[0]
c@6 42 lenSig = np.shape(input)[-1]
c@6 43 exc_i = np.zeros((numTracks, 39, lenSig))
c@6 44
c@6 45 for i in range(numTracks):
c@6 46 exc_i[i] = get_excitation_i(input[i], fs, SPL, rectify = rectify, verbose = verbose)
c@6 47
c@6 48 sl = get_specific_loudness_noise(exc_i, inc_loud_region = inc_loud_region)
c@6 49 loudness = 2*np.sum(sl,1)
c@5 50
c@6 51 return loudness
c@5 52
c@7 53 def calc_loudness_quiet(input, fs, SPL, rectify = False, inc_loud_region = False, verbose = False):
c@6 54
c@6 55 exc_i = get_excitation_i(input, fs, SPL, rectify = rectify, verbose = verbose)
c@7 56 sl = get_specific_loudness_quiet(exc_i, inc_loud_region = inc_loud_region)
c@6 57
c@6 58 loudness = 2*np.sum(sl,0)
c@6 59
c@6 60 return loudness
c@5 61
c@6 62 def get_specific_loudness_noise(exc_i, analyse = "all", inc_loud_region = False):
c@0 63 """
c@6 64
c@6 65 """
c@6 66 exc_i = np.array(exc_i)
c@6 67
c@6 68 lenSig = np.shape(exc_i)[-1] #length signal
c@6 69 numTracks = np.shape(exc_i)[0]
c@6 70 tq, G, A, alpha = get_specific_loudness_parameters(lenSig)
c@6 71 K = calc_K_parameter(lenSig)
c@6 72 specific_loudness = exc_low = exc_mid = exc_high = np.zeros(np.shape(exc_i))
c@6 73
c@6 74 exc_signoise = sum(exc_i, 0)
c@6 75 signoise_gt_tl = get_greater_than_tl(exc_signoise) #boolean array of elements greater than the loud threshold
c@6 76 signoise_lt_tl = ~signoise_gt_tl
c@6 77
c@7 78 if analyse.lower() == "all":
c@7 79 loop = range(numTracks);
c@7 80 elif analyse.lower() == "first":
c@7 81 loop = range(1)
c@6 82
c@6 83 for i in loop:
c@6 84
c@6 85 exc_sig = exc_i[i]
c@6 86 exc_noise = exc_signoise - exc_sig
c@6 87 tn = K*exc_noise + tq
c@6 88
c@6 89 less_than_tq = get_less_than_tq(exc_sig, tn) #boolean array of elements less than or equal to the threshold of quiet
c@6 90 greater_than_tq = ~less_than_tq #boolean array of elements greater than the threshold of quiet
c@6 91
c@6 92 if inc_loud_region:
c@6 93
c@7 94 c1 = signoise_lt_tl * greater_than_tn #case 1
c@7 95 c2 = signoise_lt_tl * less_than_tn #case 2
c@7 96 c3 = signoise_gt_tl * greater_than_tn #case 3
c@6 97 c4 = signoise_gt_tl * less_than_tn
c@7 98 specific_loudness[i, c3] = get_specific_loudness_noise_c3(exc_sig[c3], exc_noise[c3], tq[c3], tn[c3], G[c3], A[c3], alpha[c3])
c@7 99 specific_loudness[i, c4] = get_specific_loudness_noise_c4(exc_sig[c4], exc_noise[c4], tq[c4], tn[c4], G[c4], A[c4], alpha[c4])
c@6 100
c@6 101 else:
c@7 102 c1 = greater_than_tq #case 1
c@7 103 c2 = less_than_tq #case 2
c@6 104
c@7 105 specific_loudness[i][c1] = get_specific_loudness_noise_c1(exc_sig[c1], exc_noise[c1], tq[c1], tn[c1], G[c1], A[c1], alpha[c1])
c@7 106 specific_loudness[i][c2] = get_specific_loudness_noise_c2(exc_sig[c2], exc_noise[c2], tq[c2], tn[c2], G[c2], A[c2], alpha[c2])
c@6 107
c@6 108 return specific_loudness
c@6 109
c@6 110 def get_specific_loudness_noise_c1(exc_sig, exc_noise, tq, tn, G, A, alpha):
c@6 111 """
c@6 112
c@6 113 """
c@6 114 C = 0.046871 #constant parameter
c@6 115 sl_c1 = C*(((exc_sig + exc_noise)*G + A)**alpha - A**alpha) - C*((((tn + exc_noise)*G + A)**alpha) - (tq*G + A)**alpha) * (tn/exc_sig)**0.3
c@6 116
c@6 117 return sl_c1
c@6 118
c@6 119 def get_specific_loudness_noise_c2(exc_sig, exc_noise, tq, tn, G, A, alpha):
c@6 120 """
c@6 121 Parameters:
c@6 122 exc_sig
c@6 123 exc_noise
c@6 124 tq
c@6 125 tn
c@6 126 G
c@6 127 A
c@6 128 alpha
c@6 129 """
c@6 130
c@6 131 C = 0.046871 #constant parameter
c@7 132 sl_c2 = (C*((2*exc_sig)/(exc_sig + tn))**1.5) * (((tq*G + A)**alpha - A**alpha)/(((0.0 + tn + exc_noise)*G + A)**alpha - (exc_noise*G + A)**alpha)) * (((exc_sig + exc_noise)*G + A)**alpha - (exc_noise*G + A)**alpha)
c@6 133
c@6 134 return sl_c2
c@6 135
c@6 136 def get_specific_loudness_noise_c3(exc_sig, exc_noise, tq, tn, G, A, alpha):
c@6 137
c@6 138 C = 0.046871 #constant parameter
c@6 139 C2 = C/((1.04 * 10**6)**0.5)
c@6 140 sl_c3 = (C2*(exc_sig + exc_noise)**0.5) - C2*((tn + exc_noise)**0.5 - (tq*G + A)**alpha + A**alpha) * (tn/exc_sig)**0.3
c@6 141
c@6 142 return sl_c3
c@6 143
c@6 144 def get_specific_loudness_noise_c4(exc_sig, exc_noise, tq, tn, G, A, alpha):
c@6 145
c@6 146 C = 0.046871 #constant parameter
c@6 147 sl_c4 = (C*((2*exc_sig)/(exc_sig + tn))**1.5) * (((tq*G + A)**alpha - A**alpha)/((tn + exc_noise)**0.5 - (exc_noise)**0.5)) * ((exc_sig + exc_noise)**0.5 - (exc_noise)**0.5)
c@6 148
c@6 149 return sl_c4
c@6 150
c@6 151 def get_specific_loudness_quiet(exc_i, inc_loud_region = False):
c@6 152 """
c@6 153 A function to calculate the specific loudness of the excitation patterns at each ERB. Specific loudness is calculated for three regions of signal intensity, low level, mid level and high level. The high level processing is optional, and by default, ignored. When ignored the high level region is processed equivalent to that of the mid region.
c@3 154
c@3 155 Parameters:
c@6 156 * exc_i (type: array-like of floats) - an array of shape (39, lenSig) of the excitation intensity of the signal. The outer dimension determines the ERB channel. (Required)
c@3 157 * inc_loud_region (type: boolean) - Specifies whether to process the high levels differently to the mid levels. (Optional; Default = False)
c@0 158 """
c@3 159 exc_i = np.array(exc_i)
c@3 160
c@1 161 lenSig = np.shape(exc_i)[-1] #length signal
c@3 162 tq, G, A, alpha = get_specific_loudness_parameters(lenSig)
c@1 163 specific_loudness = exc_low = exc_mid = exc_high = np.zeros(np.shape(exc_i))
c@0 164
c@1 165 less_than_tq = get_less_than_tq(exc_i, tq) #boolean array of elements less than or equal to the threshold of quiet
c@1 166 greater_than_tq = ~less_than_tq #boolean array of elements greater than the threshold of quiet
c@1 167 greater_than_tl = get_greater_than_tl(exc_i) #boolean array of elements greater than the loud threshold
c@1 168 gttq_lttl = greater_than_tq[~greater_than_tl] #boolean array of elements greater than the threshold of quiet but less than the threshold of loud
c@0 169
c@1 170 exc_low = exc_i[less_than_tq]
c@1 171 specific_loudness[less_than_tq] = get_specific_loudness_low(exc_low, G[less_than_tq], tq[less_than_tq], A[less_than_tq], alpha[less_than_tq])
c@1 172
c@1 173 if(inc_loud_region):
c@1 174 exc_mid = exc_i[gttq_lttl]
c@1 175 exc_high = exc_i[greater_than_tl]
c@1 176 specific_loudness[gttq_lttl] = get_specific_loudness_mid(exc_mid, G[gttq_lttl], A[gttq_lttl], alpha[gttq_lttl])
c@1 177 specific_loudness[greater_than_tl] = get_specific_loudness_high(exc_high)
c@1 178 else:
c@1 179 exc_mid = exc_i[greater_than_tq]
c@1 180 specific_loudness[greater_than_tq] = get_specific_loudness_mid(exc_mid, G[greater_than_tq], A[greater_than_tq], alpha[greater_than_tq])
c@1 181
c@1 182 return specific_loudness
c@0 183
c@3 184 def get_specific_loudness_parameters(lenSig = 1):
c@3 185 """
c@6 186 Loads and returns the specific loudness values. If lenSig is specified, the parameters are shaped equal to the excitation signal to allow for matrix elementwise operations between the parameters and signal. (Assumes excitation signal shape is [39, lenSig]).
c@3 187
c@3 188 Parameters:
c@3 189 * lenSig (type: numerical int) - the length of the excitation signal to be analysed. (Optional; Default = 1)
c@3 190
c@3 191 Returns:
c@3 192 * tq (type: numpy array of floats) - the threshold of quiet for each centre frequency of the ERBs
c@3 193 * G (type: numpy array of floats) - the frequency dependent gain values for each centre frequency of the ERBs
c@3 194 * A (type: numpy array of floats) - the frequency dependent A values for each centre frequency of the ERBs
c@3 195 * alpha (type: numpy array of floats) - the frequency dependent alpha values for each centre frequency of the ERBs
c@3 196 """
c@3 197
c@3 198 tq_dB, A, alpha = utils.load_sl_parameters() #load tq, A and alpha parameters
c@3 199 tq_dB = np.transpose(np.tile(tq_dB, (lenSig,1)))
c@3 200 tq = 10**(tq_dB / 10)
c@3 201 A = np.transpose(np.tile(A, (lenSig,1)))
c@3 202 alpha = np.transpose(np.tile(alpha, (lenSig,1)))
c@3 203 tq500_dB = 3.73 #threshold of quiet at 500Hz reference
c@3 204 G = 10**((tq500_dB - tq_dB)/10) #gain parameters
c@3 205
c@3 206 return tq, G, A, alpha
c@3 207
c@6 208 def calc_K_parameter(lenSig = 1):
c@6 209 """
c@6 210
c@6 211 """
c@6 212
c@6 213
c@6 214 nerbs = 39
c@6 215 erbs = np.array(range(1,nerbs+1))
c@6 216 fc = (10**(erbs/21.366)-1)/0.004368
c@6 217
c@6 218 fkRef = 10**np.append(np.append(np.array(1), np.linspace(np.log10(50), np.log10(500), 11)), np.linspace(3,4.5,16))
c@6 219 kRef = np.append(np.append(13, np.linspace(13,-3,11)), np.ones((1,16))*-3)
c@6 220 K = np.interp(fc,fkRef,kRef)
c@6 221 K = np.transpose(np.tile(K, (lenSig,1)))
c@6 222
c@6 223 return K
c@6 224
c@1 225 def get_specific_loudness_low(exc_low, G, tq, A, alpha):
c@3 226 """
c@3 227 Returns the specific loudness of the low level parts of the signal. Use get_specific_loudness_parameters() and specify lenSig
c@3 228 to obtain the correct values for G, tq, A and alpha. Use get_less_than_tq() to find the indexes of the samples with lower level
c@3 229 excitations.
c@3 230
c@6 231 EXAMPLE USE
c@3 232
c@3 233 # exc_i is the excitation intensity
c@3 234 lenSig = np.shape(exc_i)[-1] # find the length of the signal
c@3 235 tq, G, A, alpha = get_specific_loudness_parameters(lenSig) # get the shaped loudness parameters
c@3 236 less_than_tq = get_less_than_tq(exc_i, tq) # find which samples are lower level
c@3 237 specific_loudness[less_than_tq] = get_specific_loudness_low(exc_low[less_than_tq], G[less_than_tq], tq[less_than_tq], A[less_than_tq], alpha[less_than_tq])
c@3 238 # only process the low level part of the signal
c@3 239
c@3 240 Parameters:
c@3 241 * exc_low (type: array-like matrix of floats) - the lower level (less than or equal to tq) excitation pattern for each ERB
c@3 242 (Required)
c@3 243 * G (type: array-like matrix of floats) - the frequency dependent loudness gain parameters for each ERB, must be same shape
c@3 244 as exc_low. (Required)
c@3 245 * tq (type: array-like matrix of floats) - the frequency dependent threshold of quiet parameters for each ERB. Must be same
c@3 246 shape as exc_low. (Required)
c@3 247 * A (type: array-like matrix of floats) - the frequency dependent A parameters for each ERB. Must be same shape as exc_low.
c@3 248 * alpha (type: array-like matrix of floats) - the frequency dependent alpha parameters for each ERB. Must be same shape as
c@3 249 exc_low. (Required)
c@3 250
c@3 251 Returns:
c@3 252 * specific_loudness_low (type: array-like matrix of floats) - An array with dimensions equal to the exc_low containing the
c@3 253 specific loudness of the signal at levels below the threshold of quiet.
c@3 254 """
c@1 255
c@5 256 C = 0.046871 #constant parameter
c@5 257 specific_loudness_low = C * ((2.0*exc_low)/(0.0+exc_low+tq))**1.5 * ((G * exc_low + A)**alpha - A**alpha)
c@1 258
c@1 259 return specific_loudness_low
c@1 260
c@1 261 def get_specific_loudness_mid(exc_mid, G, A, alpha):
c@3 262 """
c@3 263 Returns the specific loudness of the mid level parts of the signal.
c@3 264
c@6 265 EXAMPLE USE
c@3 266
c@3 267 # exc_i is the excitation intensity
c@3 268 lenSig = np.shape(exc_i)[-1] # find the length of the signal
c@3 269 tq, G, A, alpha = get_specific_loudness_parameters(lenSig) # get the shaped loudness parameters
c@3 270 less_than_tq = get_less_than_tq(exc_i, tq) # find which samples are lower level
c@3 271 greater_than_tq = ~less_than_tq # find which samples are greater than the threshold of quiet
c@3 272 greater_than_tl = get_greater_than_tl(exc_i) # find which samples are greater than the loud threshold
c@3 273 gttq_lttl = greater_than_tq[~greater_than_tl] # find which samples are mid level
c@3 274 specific_loudness[gttq_lttl] = get_specific_loudness_low(exc_low[gttq_lttl], G[gttq_lttl], tq[gttq_lttl], A[gttq_lttl], alpha[gttq_lttl])
c@3 275 # only process the mid level part of the signal
c@3 276
c@6 277 NOTE: The above is an example of use assuming the higher level processing IS NOT IGNORED. Use variable greater_than_tq if processing higher levels equivalent to the mid.
c@3 278
c@3 279
c@3 280 Parameters:
c@3 281 * exc_mid (type: array-like matrix of floats) - the mid level (larger than tq (and, optionally less than high level threshold))
c@3 282 excitation pattern for each ERB (Required)
c@6 283 * G (type: array-like matrix of floats) - the frequency dependent loudness gain parameters for each ERB, must be same shape as exc_low. (Required)
c@3 284 * A (type: array-like matrix of floats) - the frequency dependent A parameters for each ERB. Must be same shape as exc_low.
c@6 285 * alpha (type: array-like matrix of floats) - the frequency dependent alpha parameters for each ERB. Must be same shape as exc_low. (Required)
c@3 286
c@3 287 Returns:
c@6 288 * specific_loudness_mid (type: array-like matrix of floats) - An array with dimensions equal to the exc_mid containing the specific loudness of the signal at mid levels.
c@3 289 """
c@1 290
c@6 291 C = 0.046871 #constant parameter
c@5 292 specific_loudness_mid = C * ((G * exc_mid + A)**alpha - (A**alpha))
c@1 293
c@1 294 return specific_loudness_mid
c@1 295
c@1 296 def get_specific_loudness_high(exc_high):
c@1 297 """
c@3 298 Returns the specific loudness of the high level parts of the signal.
c@3 299
c@6 300 EXAMPLE USE
c@3 301
c@3 302 # exc_i is the excitation intensity
c@3 303 lenSig = np.shape(exc_i)[-1] # find the length of the signal
c@3 304 tq, G, A, alpha = get_specific_loudness_parameters(lenSig) # get the shaped loudness parameters
c@3 305 greater_than_tl = get_greater_than_tl(exc_i) # find which samples are greater than the loud threshold
c@3 306 specific_loudness[greater_than_tl] = get_specific_loudness_low(exc_low[greater_than_tl], G[greater_than_tl], tq[greater_than_tl], A[greater_than_tl], alpha[greater_than_tl])
c@3 307 # only process the mid level part of the signal
c@3 308
c@3 309 Parameters:
c@3 310 * exc_high (type: array-like matrix of floats) - the high level (larger than the threshold of high level) excitation pattern
c@3 311 for each ERB (Required)
c@3 312
c@3 313 Returns:
c@6 314 * specific_loudness_high (type: array-like matrix of floats) - An array with dimensions equal to the exc_high containing the specific loudness of the signal at high levels.
c@1 315 """
c@1 316
c@6 317 C = 0.046871 #constant parameter
c@1 318 specific_loudness_high = C * (exc_high / (1.04 * 10**6))**0.5
c@1 319
c@1 320 return specific_loudness_high
c@1 321
c@1 322 def get_greater_than_tl(exc_i):
c@1 323 """
c@1 324 A function to return if each element of the excitation intensity is greater than the threshold of loud.
c@1 325
c@1 326 Parameters:
c@1 327 * exc_i (type: array-like matrix of floats) - the input excitation intensity
c@1 328
c@1 329 Returns:
c@6 330 * le_tq (type: array-like matrix of booleans) - a boolean array with dimensions equal to the input specifying if the excitation intensity is greater than the threshold of loud
c@1 331 """
c@1 332
c@1 333 lenSig = np.shape(exc_i)[-1]
c@1 334 g_tl = exc_i[:,:]>np.transpose(np.tile(10**10,(lenSig,39)))
c@1 335
c@1 336 return g_tl
c@1 337
c@1 338 def get_less_than_tq(exc_i, tq):
c@1 339 """
c@1 340 A function to return if each element of the excitation intensity is less than the threshold of quiet.
c@1 341
c@1 342 Parameters:
c@1 343 * exc_i (type: array-like matrix of floats) - the input excitation intensity
c@1 344 * tq (type: array-like matrix of floats) - the threshold of quiet for each ERB.
c@1 345
c@1 346 Returns:
c@6 347 * le_tq (type: array-like matrix of booleans) - a boolean array with dimensions equal to the input specifying if the excitation intensity is less than the threshold of quiet
c@1 348 """
c@1 349
c@1 350 if (np.shape(exc_i)!=np.shape(tq)):
c@1 351 np.transpose(np.tile(exc_i,(np.shape(exc_i)[-1],1)))
c@1 352
c@1 353 le_tq = exc_i<=tq
c@1 354
c@1 355 return le_tq
c@1 356
c@1 357 def get_excitation_i(input, fs, SPL, rectify=False, verbose = False):
c@0 358 """
c@0 359 A function to calculate the excitation intensity of the input signal.
c@0 360
c@0 361 Parameters:
c@0 362 * input (type: array-like matrix of floats) - signal normalised to an amplitude range of -1 to 1. (Required)
c@0 363 * fs (type: numerical) - sample frequency of the signal, input. (Required)
c@6 364 * SPL (type: double) - the sound pressure level (SPL) at 0 dBFS, e.g., the SPL of a sine wave with peaks at amplitude 1 and troughs at amplitude -1. (Required)
c@6 365 * rectify (type: boolean) - Specifies whether to include half wave rectification, modelling the direction of that the cochlear nerves vibrate. True to include, False to ignore. (Optional; Default = False)
c@0 366
c@0 367 Returns:
c@6 368 * gtfs (type: numpy array of floats) - array with size ((39,) + np.shape(input)) containing the excitation pattern (in sound intensity) for each ERB of input signal. The excitation pattern for the nth ERB can be accessed with gtfs[n].
c@0 369 """
c@0 370
c@0 371 input = np.array(input)
c@5 372
c@5 373 inputPa = v_Pascal(input, SPL)
c@5 374
c@5 375 inputOMFir = outMidFir(inputPa)
c@5 376
c@6 377 b = gammatone_filter(fs, normalise = True)
c@5 378 gtfs = decomposition(inputOMFir, b, verbose = verbose)
c@5 379
c@0 380 if (rectify):
c@0 381 gtfs = half_rectification(gtfs)
c@5 382
c@5 383 gtfsI = pa_i(gtfs)
c@5 384 gtfsN = at_normalise(gtfsI)
c@0 385
c@5 386 b1,a1 = exp_smoothing(fs)
c@5 387 gtfsF = np.zeros(np.shape(gtfsI))
c@5 388
c@5 389 for i in range(39):
c@5 390 gtfsF[i] = sp.lfilter(b1,a1,gtfsN[i])
c@5 391
c@5 392 return gtfsF
c@0 393
c@4 394 def plot_excitation_response(input = None, fs = 44100, outMidFilt = True, gammatone = True, xscale = 'log', yscale = 'log'):
c@0 395 """
c@0 396 A function that plots the transfer function of the outer middle ear and each gammatone filter.
c@0 397
c@0 398 Parameters:
c@0 399 * fs (type: numerical) - the sampling frequency of the signal. (Optional; Default = 44100)
c@0 400 * outMidFilt (type: boolean) - filter the signal by the outer and middle ear FIR filter. (Optional; Default = True)
c@0 401 * xscale (type: string) - the scale of the frequency axis. Values are 'log' or 'linear'. (Optional; Default = 'log')
c@0 402 * yscale (type: string) - the scale of the amplitude axis. Values are 'log' or 'linear'. (Optional; Default = 'log')
c@0 403 """
c@1 404
c@4 405 if input == None:
c@4 406 input = np.zeros((np.ceil(fs)))
c@1 407 input[0] = 1
c@4 408
c@0 409 if(outMidFilt): input = outMidFir(input)
c@4 410 if(gammatone):
c@6 411 b = gammatone_filter(fs, normalise = True)
c@4 412 input = decomposition(input, b)
c@6 413 #input = holdsworth_gammatone_filter(input,fs)
c@4 414 numPlot = range(np.shape(input)[0])
c@4 415 else:
c@4 416 numPlot = (0,)
c@4 417 input = input.reshape(1,len(input))
c@4 418
c@4 419 for i in numPlot:
c@4 420 utils.plot_fft(input[i],xscale, yscale, False)
c@0 421
c@0 422 plt.show()
c@0 423
c@4 424 return
c@0 425
c@0 426 def get_modulation_i(input, fs):
c@0 427 """
c@6 428 A function to calculate the modulation intensity of the input intensity signal. The function implements a filter bank of bandpass filters with cut off frequencies ranging from 0.25 to 16 Hz.
c@0 429
c@0 430 Parameters:
c@0 431 * input (type: array-like matrix of floats) - the input intensity signal.
c@0 432 E.g., use get_excitation_i() to obtain excitation intensity and use as input.
c@0 433 * fs (type: numerical) - sampling frequency of input signal
c@0 434
c@0 435 Returns:
c@6 436 * y (type: numpy array of floats) - array with size ((10,) + np.shape(input)) containing the modulation intensity of the signal at each modulation filter. The modulation intensity for the nth filter can be accessed with y[n].
c@0 437 """
c@0 438
c@0 439 input = np.array(input)
c@0 440 b = fir_antialias(fs)
c@0 441 input_lp = sp.lfilter(b,(1),input_fr)
c@0 442 input_ds = downsample(input_lp, fs)
c@0 443 fc = np.array(utils.exp_sequence(-2,4,10))
c@0 444 bw = fc/2
c@0 445 y = decomposition(input_ds, fs, fc, bw)
c@0 446
c@0 447 return y
c@0 448
c@0 449 def outMidFir(input):
c@0 450 """
c@0 451 A function to filter the input signal with the response of the outer and middle ear.
c@0 452
c@0 453 Parameters:
c@0 454 * input (type: array-like matrix of floats) - signal normalised to an amplitude range of -1 to 1. (Required)
c@0 455
c@0 456 Returns:
c@6 457 * y (type: numpy array of floats) - array with dimensions equal to the input signal filtered by the response of the outer and middle ear.
c@0 458 """
c@0 459
c@0 460 input = np.array(input)
c@0 461 b = utils.load_outMidFir_coeff()
c@0 462 y = sp.lfilter(b, (1), input)
c@0 463
c@0 464 return y
c@0 465
c@6 466 def gammatone_filter(fs, normalise = False):
c@0 467 """
c@6 468 A function to return the filter coefficients of 39 different gammatones with fc of ERBs 1 to 39.
c@0 469
c@0 470 Parameters:
c@0 471 * input (type: array-like matrix of floats) - signal normalised to an amplitude range of -1 to 1. (Required)
c@0 472 * fs (type: numerical) - sample frequency of the signal, input. (Required)
c@0 473
c@0 474 Returns:
c@6 475 * y (type: numpy array of floats) - array with size ((39),np.shape(input)) containing the impulse response of the signal at each gammatone filter. The response at the nth gammatone filter can be accessed by y[n].
c@0 476 """
c@0 477
c@4 478 nerbs = 39
c@4 479 erbs = np.array(range(1,nerbs+1))
c@4 480 fc = (10**(erbs/21.366)-1)/0.004368
c@4 481 bw = 24.673 * (1 + 0.004368*fc)
c@4 482 N = 4
c@4 483 filterLength = 4096
c@4 484 t = 1.0*np.array(range(filterLength))/fs
c@4 485
c@4 486 b = np.zeros((nerbs,filterLength))
c@4 487
c@4 488 for i in range(39):
c@7 489 b[i] = t**(N-1) * np.exp(-2*np.pi*bw[i]*t) * np.cos(2*np.pi*fc[i]*t)
c@4 490
c@7 491 if normalise:
c@7 492 A = 1/(np.abs(fft.fft(b[i], 4*filterLength))).max()
c@7 493 b[i] *= A
c@7 494
c@4 495 return b
c@4 496
c@6 497 def holdsworth_gammatone_filter(input, fs):
c@4 498 """
c@4 499
c@4 500 """
c@0 501 input = np.array(input)
c@4 502 input = input + 0j
c@4 503
c@4 504 T = 1.0/fs
c@4 505
c@4 506 ERBs = np.array(range(1,40))
c@4 507 f0 = (10**(ERBs/21.4)-1)/4.37e-3
c@4 508
c@4 509 inLen = len(input)
c@4 510 b = 24.673 * (1 + 0.004368*f0)
c@4 511 k = np.array(range(inLen)) + 0j
c@4 512 out = np.zeros((39,inLen))
c@4 513
c@4 514 for erb in range(39):
c@4 515
c@4 516 zArr = input*np.exp(-2*np.pi*1j*f0[erb]*k*T)
c@4 517 wArr = np.zeros((inLen+1))
c@4 518
c@4 519 for i in range(1,inLen+1):
c@4 520 wArr[i] = wArr[i-1] + (1 - np.exp(-2*np.pi*b[erb]*T))*(zArr[i-1] - wArr[i-1])
c@4 521
c@4 522 out[erb] = (wArr[1:]*np.exp(2*np.pi*1j*f0[erb]*k*T)).real
c@4 523
c@4 524 return out
c@4 525
c@0 526
c@0 527 def v_Pascal(input, SPL):
c@0 528 """
c@0 529 A function to convert a signal, normalised to an amplitude range of -1 to 1, to a signal represented in pressure (units: Pascal).
c@0 530
c@0 531 Parameters:
c@0 532 * input (type: array-like matrix of floats) - signal normalised to an amplitude range of -1 to 1. (Required)
c@0 533 * SPL (type: double) - the sound pressure level (SPL) at 0 dBFS, e.g., the SPL of a sine
c@0 534 wave with peaks at amplitude 1 and troughs at amplitude -1. (Required)
c@0 535
c@0 536 Returns:
c@0 537 * y (type: numpy array of floats) - array with dimensions equal to the input signal containing the input represented
c@0 538 as a pressure signal.
c@6 539
c@6 540 NOTE If input signal contains one or more zeros, a divide by zero warning will be outputted. This warning can be ignored.
c@0 541 """
c@0 542
c@0 543 input = np.array(input)
c@5 544 y = np.sign(input)*(0.00002*10**(np.log10(np.abs(input))+SPL/20.0))
c@0 545
c@0 546 return y
c@0 547
c@0 548 def pa_i(input, C=406):
c@0 549 """
c@0 550 A function to convert a pressure signal (unit: Pascal) to an intensity signal.
c@0 551
c@0 552 Parameters:
c@0 553 * input (type: array-like matrix of floats) - pressure signal (unit: Pascal) (Required)
c@0 554 * C (type: double) - the acoustic impedance of the air (Optional; Default = 406)
c@0 555
c@0 556 Returns:
c@0 557 * y (type: numpy array of floats) - array with dimensions equal to the input signal containing the input represented
c@0 558 as a pressure signal.
c@0 559 """
c@0 560
c@0 561 input = np.array(input)
c@0 562 y = (input**2) / C
c@0 563
c@0 564 return y
c@0 565
c@0 566 def at_normalise(input):
c@0 567 """
c@0 568 A function to normalise an intensity signal with the audibility threshold.
c@0 569
c@0 570 Parameters:
c@0 571 * input (type: array-like matrix of floats) - intensity signal (unit: Pascal) (Required)
c@0 572
c@0 573 Returns:
c@0 574 * y (type: numpy array of floats) - array with dimensions equal to the input signal containing the input normalised
c@0 575 with the audibility threshold.
c@0 576 """
c@0 577
c@0 578 input = np.array(input)
c@5 579 y = input / (1*(10**-12))
c@0 580
c@1 581 return y
c@0 582
c@0 583 def downsample(input, factor=100):
c@0 584 """
c@0 585 A function to downsample a signal, input, with sampling frequency, fs, by a downsample factor of factor.
c@0 586
c@0 587 NOTE: It is advised to use the fir_antialias() function before downsampling to remove any high frequencies
c@0 588 which would otherwise represented as low frequencies due to aliasing.
c@0 589
c@0 590 Parameters:
c@0 591 * input (type: array-like matrix of floats) - input signal. (Required)
c@0 592 * factor - downsample factor (Optional; Default = 100)
c@0 593
c@0 594 Returns:
c@0 595 * output (type: numpy array of floats) - array with outer dimensions equivalent to the to the input, and
c@0 596 inner dimension equal to np.floor(lenIn / factor) where lenIn is the length of the inner dimension.
c@0 597 """
c@0 598
c@0 599 input = np.array(input)
c@0 600 shapeIn = np.shape(input)
c@0 601 nDim = np.shape(shapeIn)
c@0 602 lenIn = shapeIn[nDim[0]-1]
c@0 603 lenOut = np.floor(lenIn / factor)
c@0 604 n = np.linspace(0,lenIn,lenOut, endpoint=False).astype(np.int)
c@0 605 output = input[...,n]
c@0 606
c@0 607 return output
c@0 608
c@0 609 def half_rectification(input):
c@0 610 """
c@0 611 A function which performs a half-wave rectification on the input signal.
c@0 612
c@0 613 Parameters:
c@0 614 * input (type: array-like matrix of floats) - input signal. (Required)
c@0 615
c@0 616 Returns:
c@0 617 * y (type: numpy array of floats) - an array with dimensions of input containing the half-wave rectification of
c@0 618 input.
c@0 619 """
c@0 620
c@0 621
c@0 622 y = np.array(input)
c@0 623 y[y<0] = 0
c@0 624
c@0 625 return y
c@0 626
c@4 627 def decomposition(input, b, a = None, verbose = False):
c@0 628 """
c@4 629 A function to run the input through a bandpass filter bank with parameters defined by the b and a coefficints.
c@0 630
c@0 631 Parameters:
c@0 632 * input (type: array-like matrix of floats) - input signal. (Required)
c@4 633 * b (type: array-like matrix of floats) - the b coefficients of each filter in shape b[numOfFilters][numOfCoeffs]. (Required)
c@4 634 * a (type: array-like matrix of floats) - the a coefficients of each filter in shape a[numOfFilters][numOfCoeffs].
c@4 635 If not specified, the filter is treated as a FIR filter. (Optional; Default = 1)
c@4 636 * verbose (type: boolean) - determines whether to display the current subroutine/progress of the procedure.
c@4 637 (Optional; Default = False)
c@4 638
c@4 639 Returns:
c@4 640 * y (type: numpy array of floats) - an array with inner dimensions equal to that of the input and outer dimension equal to
c@4 641 the length of fc (i.e. the number of bandpass filters in the bank) containing the outputs to each filter. The output
c@4 642 signal of the nth filter can be accessed using y[n].
c@4 643 """
c@4 644
c@4 645 input = np.array(input)
c@4 646 bShape = np.shape(b)
c@4 647 nFilters = bShape[0]
c@4 648
c@4 649 if a == None:
c@4 650 a = np.ones(nFilters)
c@4 651
c@4 652 shape = (nFilters,) + np.shape(input)
c@4 653 shape = shape[0:]
c@4 654 y = np.zeros(shape)
c@4 655
c@6 656 if(verbose): sys.stdout.write("Running filterbank decomposition.\n")
c@4 657 for i in range(nFilters):
c@5 658 if(verbose):
c@5 659 sys.stdout.write("\r" + str(int(np.round(100.0*i/nFilters))) + "% complete.")
c@5 660 sys.stdout.flush()
c@4 661 x = deepcopy(input)
c@4 662 y[i] = sp.lfilter(b[i],a[i],x)
c@6 663
c@6 664 if(verbose): sys.stdout.write("\n")
c@6 665
c@4 666 return y
c@4 667
c@4 668 def get_Modulation_filter_bank_coefficients(fs, fc, bw, order = 1024, verbose = False):
c@4 669 """
c@4 670 A function which returns the b and a coefficients of the modulation filter bank of length equal to the length of fc. Each
c@4 671 bandpass filter is designed by defining the centre frequencies, fc, and bandwidths, bw.
c@4 672
c@4 673 Parameters:
c@0 674 * fs (type: numerical) - the sampling frequency of the input signal. (Required)
c@0 675 * fc (type: array-like vector of floats) - the centre off frequencies (unnormalised) of each bandpass filter.
c@0 676 The length of this vector determines the number of filters in the bank.
c@0 677 * bw (type: array-like vector of floats) - the bandwidths (unnormalised) of each bandpass filter. Must be equal
c@0 678 to or more than the length of fc. If the length is more, all elements exceeding the length of fc - 1 will be
c@0 679 ignored.
c@0 680 * order (type: numerical int) - the order of the filter (number of taps minus 1) (Optional; Default = 1024)
c@0 681 * verbose (type: boolean) - determines whether to display the current subroutine/progress of the procedure.
c@0 682 (Optional; Default = False)
c@0 683
c@0 684 Returns:
c@4 685 * b (type: numpy array of floats) - an array of size order + 1 (i.e. a coefficient for each tap)
c@0 686 """
c@4 687
c@4 688
c@0 689 nFreqs = len(fc)
c@4 690
c@5 691 if(verbose): sys.stdout.write("Running frequency decomposition.")
c@4 692
c@0 693 for i in range(nFreqs):
c@5 694 if(verbose):
c@5 695 sys.stdout.write("\r" + str(int(np.round(100.0*i/nFreqs))) + "% complete.")
c@5 696 sys.stdout.flush()
c@0 697 low = fc[i]-bw[i]/2;
c@0 698 high = fc[i]+bw[i]/2;
c@0 699 b = fir_bandpass(low, high, fs, order, verbose)
c@4 700
c@4 701 return b
c@0 702
c@3 703 def exp_smoothing(fs, tc = 0.01):
c@3 704 """
c@3 705 Designs an exponential filter, y[n] = alpha*x[n] - -(1-alpha)*y[n-1], where alpha = T/(tc+T), where T
c@3 706 is the inverse of the sampling frequency and time constant, tc, is specified.
c@3 707
c@3 708 Parameters:
c@3 709 * fs (type: numerical) - sampling frequency of the signal to be filtered (Required)
c@3 710 * tc (type: numerical) - the time constant of the filter. (Optional; Default = 0.01)
c@3 711
c@3 712 Returns:
c@3 713 * b (type: numerical) - the coefficient of x[n]. Equal to alpha.
c@3 714 * a (type: numpy array of floats) - an array of size 2 of the coefficients of y[n] (a[0] = 1) and y[n-1]
c@3 715 """
c@3 716
c@3 717 T = 1.0/fs
c@3 718 alpha = T/(tc + T)
c@3 719 b = [alpha]
c@3 720 a = [1, -(1-alpha)]
c@3 721
c@3 722 return b, a
c@3 723
c@3 724 def antialias_fir(fs, fc=100, order=64):
c@3 725 """
c@3 726 A function which returns the b coefficients for a lowpass fir filter with specified requirements.
c@3 727 Made specifically to remove aliasing when downsampling, but can be used for any application that
c@3 728 requires a lowpass filter.
c@3 729
c@3 730 Parameters:
c@3 731 * fs (type: numerical) - sampling frequency of the signal to be filtered (Required)
c@3 732 * fc (type: numerical) - unnormalised cut off frequency of the filter (Optional; Default = 100)
c@3 733 * order (type: numerical int) - the order of the filter (number of taps minus 1) (Optional; Default = 64)
c@3 734
c@3 735 Returns:
c@3 736 * b (type: numpy array of floats) - an array of size order + 1 (i.e. a coefficient for each tap)
c@3 737 """
c@3 738
c@3 739 nyquist = 0.5*fs
c@3 740 fcNorm = fc/nyquist
c@3 741 b = sp.firwin(order+1, fcNorm)
c@3 742
c@3 743 return b
c@3 744
c@4 745 def fir_bandpass(low, high, fs, order = 4096, verbose = False):
c@0 746 """
c@0 747 A function which returns the b coefficients for a bandpass fir filter with specified requirements.
c@0 748
c@0 749 Parameters:
c@0 750 * low - the lower cutoff frequency of the filter. (Required)
c@0 751 * high - the upper cutoff frequency of the filter. (Required)
c@0 752 * fs (type: numerical) - sampling frequency of the signal to be filtered. (Required)
c@0 753 * order (type: numerical int) - the order of the filter (number of taps minus 1) (Optional; Default = 1024)
c@6 754 * verbose (type: boolean) - determines whether to display info on the current subroutine
c@0 755 of the procedure. (Optional; Default = False)
c@0 756
c@0 757 Returns:
c@0 758 * b (type: numpy array of floats) - an array of size order + 1 (i.e. a coefficient for each tap).
c@0 759 """
c@0 760
c@0 761 nyquist = 0.5*fs
c@0 762 lowNorm = low/nyquist
c@0 763 highNorm = high/nyquist
c@5 764 if(verbose): sys.stdout.write("Low: " + str(lowNorm) + "; High: " + str(highNorm))
c@0 765 b = sp.firwin(order+1, [lowNorm, highNorm], pass_zero=False)
c@0 766
c@0 767 return b