comparison pyphon.py @ 9:ef62cca13f36

* Added STL and LTL functionality * Changed cortical_model name to PyPhon because it's a funny pun! * Added wavread2 to utils, lets you read 24bit wavefiles * Added sound to utils, listen to your vector like in Matlab! * changed testElc to equalLoudness
author Carl Bussey <c.bussey@se10.qmul.ac.uk>
date Tue, 22 Apr 2014 00:55:19 +0100
parents cortical_model.py@d5693c7aa0ae
children
comparison
equal deleted inserted replaced
8:d5693c7aa0ae 9:ef62cca13f36
1 """
2 A module used for auditory analysis.
3
4 Models currently implemented:
5 * Simple frequency-modulation analysis model.
6 * Glasberg and Moore model using gammatone filters
7
8 Packaged dependencies:
9 > utils.py and/or utils.pyc
10 > erb.dat
11 > outMidFir.dat
12 > tq.dat
13
14 External dependencies:
15 > scipy
16 > numpy
17 > matplotlib
18 > sys (Built-in)
19
20 Primary functions:
21 > calc_loudness_quiet
22 > calc_loudness_noise
23 > get_excitation_i
24 > plot_excitation_response
25 """
26
27 import sys
28 import utils
29 import scipy.signal as sp
30 import scipy.fftpack as fft
31 import numpy as np
32 import matplotlib.pyplot as plt
33
34 def calc_loudness_noise(input, fs, SPL = None, inputType = 'signal', gammatoneLength=4096, rectify=False, inc_loud_region = True, verbose = False):
35 """"
36 Calculate the specifc loudness, instantaneous loudness, short term loudness and long term loudness of the input signal present in noise.
37
38 Parameters:
39 * input (type: array-like matrix of floats) - signal normalised to an amplitude range of -1 to 1 or excitation pattern. (Required)
40 * fs (type: numerical) - sample frequency of the signal, input. (Required)
41 * SPL (type: float) - the sound pressure level corresponding to 1 of the normalised range -1 to 1. (Optional if inputType='excitation' then doesn't do anything. Required otherwise)
42 * inputType (type: string) - specify whether the input is the excitation pattern, or the input signal. Use 'signal' or 'excitation', respectively. (Optional; Default = 'signal')
43 * gammaToneLength (type: int) - length of gammatone filters. (Optional; Default = 4096)
44 * rectify (type = boolean) - rectify the input to model the direction shearing of the hairs in cochlea. Glasberg and Moore model doesn't consider this, so by default is false. (Optional; Default = False)
45 * inc_loud_region (type: boolean) - Specifies whether to process the high levels differently to the mid levels. (Optional; Default = False)
46 * Verbose (type: boolean) - print verbosely to stdout.
47
48 Returns:
49 * sl (type: numpy array of floats) - specific loudness
50 * loudness (type: numpy array of floats) - instantaneous loudness
51 * STL (type: numpy array of floats) - short term loudness
52 * LTL (type: numpy array of floats) - long term loudness
53 """
54
55
56 if(inputType == 'signal' or inputType == 'sig'):
57 if(SPL == None):
58 raise ValueError("You need to provide the full scale sound pressure level!")
59
60 numTracks = np.shape(input)[0]
61 lenSig = np.shape(input)[-1]
62 excitation = np.zeros((numTracks, 39, lenSig+gammatoneLength+4095))
63
64 for i in range(numTracks):
65 excitation[i] = get_excitation_i(input[i], fs, SPL, gammatoneLength=gammatoneLength, rectify = rectify, verbose = verbose)
66
67 elif(inputType == 'excitation' or inputType == 'exc'):
68 excitation = input
69 numTracks = np.shape(excitation)[0]
70 else:
71 raise ValueError("Unknown inputType. Can be 'signal' (or 'sig') or 'excitation' (or 'exc')")
72
73 del input
74
75 sl = _get_specific_loudness_noise(excitation, inc_loud_region = inc_loud_region)
76 loudness = 2*np.sum(sl,1)
77 STL = np.zeros(np.shape(loudness))
78 LTL = np.array(STL)
79
80 for i in range(numTracks):
81 STL[i] = temporal_integration(loudness[i], fs, 'STL')
82 LTL[i] = temporal_integration(STL[i], fs, 'LTL')
83
84 return sl, loudness, STL, LTL
85
86 def calc_loudness_quiet(input, fs, SPL = None, inputType = 'signal', gammatoneLength=4096, rectify = False, inc_loud_region = True, verbose = False):
87 """"
88 Calculate the specifc loudness, instantaneous loudness, short term loudness and long term loudness of the input signal.
89
90 Parameters:
91 * input (type: array-like matrix of floats) - signal normalised to an amplitude range of -1 to 1 or excitation pattern. (Required)
92 * fs (type: numerical) - sample frequency of the signal, input. (Required)
93 * SPL (type: float) - the sound pressure level corresponding to 1 of the normalised range -1 to 1. (Optional if inputType='excitation' then doesn't do anything. Required otherwise)
94 * inputType (type: string) - specify whether the input is the excitation pattern, or the input signal. Use 'signal' or 'excitation', respectively. (Optional; Default = 'signal')
95 * gammaToneLength (type: int) - length of gammatone filters. (Optional; Default = 4096)
96 * rectify (type = boolean) - rectify the input to model the direction shearing of the hairs in cochlea. Glasberg and Moore model doesn't consider this, so by default is false. (Optional; Default = False)
97 * inc_loud_region (type: boolean) - Specifies whether to process the high levels differently to the mid levels. (Optional; Default = False)
98 * Verbose (type: boolean) - print verbosely to stdout.
99
100 Returns:
101 * sl (type: numpy array of floats) - specific loudness
102 * loudness (type: numpy array of floats) - instantaneous loudness
103 * STL (type: numpy array of floats) - short term loudness
104 * LTL (type: numpy array of floats) - long term loudness
105 """
106
107
108 if(inputType == 'signal' or inputType == 'sig'):
109 if(SPL == None):
110 raise ValueError("You need to provide the full scale sound pressure level!")
111 excitation = get_excitation_i(input, fs, SPL, gammatoneLength=gammatoneLength, rectify = rectify, verbose = verbose)
112 elif(inputType == 'excitation' or inputType == 'exc'):
113 excitation = input
114 else:
115 raise ValueError("Unknown inputType. Can be 'signal' (or 'sig') or 'excitation' (or 'exc')")
116
117 del input
118
119 sl = _get_specific_loudness_quiet(excitation, inc_loud_region = inc_loud_region)
120 loudness = 2*np.sum(sl,0)
121 STL = temporal_integration(loudness, fs, 'STL')
122 LTL = temporal_integration(STL, fs, 'LTL')
123
124 return sl, loudness, STL, LTL
125
126 def _get_specific_loudness_noise(exc_i, analyse = "all", inc_loud_region = True):
127 """
128 A function to calculate the specific loudness of the excitation patterns at each ERB when present in noise. 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.
129
130 Parameters:
131 * 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)
132 * inc_loud_region (type: boolean) - Specifies whether to process the high levels differently to the mid levels. (Optional; Default = False)
133 """
134 exc_i = np.array(exc_i)
135
136 lenSig = np.shape(exc_i)[-1] #length signal
137 numTracks = np.shape(exc_i)[0]
138 tq, G, A, alpha = _get_specific_loudness_parameters(lenSig)
139 K = _calc_k_parameter(lenSig)
140 specific_loudness = exc_low = exc_mid = exc_high = np.zeros(np.shape(exc_i))
141
142 exc_signoise = sum(exc_i, 0)
143 signoise_gt_tl = _get_greater_than_tl(exc_signoise) #boolean array of elements greater than the loud threshold
144 signoise_lt_tl = ~signoise_gt_tl
145
146 if analyse.lower() == "all":
147 loop = range(numTracks);
148 elif analyse.lower() == "first":
149 loop = range(1)
150
151 for i in loop:
152
153 exc_sig = exc_i[i]
154 exc_noise = exc_signoise - exc_sig
155 tn = K*exc_noise + tq
156
157 less_than_tn = _get_less_than_tq(exc_sig, tn) #boolean array of elements less than or equal to the threshold of quiet
158 greater_than_tn = ~less_than_tn #boolean array of elements greater than the threshold of quiet
159
160 if inc_loud_region:
161 c1 = signoise_lt_tl * greater_than_tn #case 1
162 c2 = signoise_lt_tl * less_than_tn #case 2
163 c3 = signoise_gt_tl * greater_than_tn #case 3
164 c4 = signoise_gt_tl * less_than_tn #case 4
165 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])
166 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])
167 else:
168 c1 = greater_than_tn #case 1
169 c2 = less_than_tn #case 2
170
171 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])
172 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])
173
174 return specific_loudness
175
176 def _get_specific_loudness_noise_c1(exc_sig, exc_noise, tq, tn, G, A, alpha):
177 """
178 A function to calculate the specific loudness of a component of the signal for the case exc_noise < 10**10, and exc_quiet > threshold noise (c1).
179
180 Parameters:
181 * tq (type: numpy array of floats) - the threshold of quiet for each centre frequency of the ERBs
182 * tn (type: numpy array of floats) - the threshold of quiet when present in noise for each centre frequency of the ERBs
183 * G (type: numpy array of floats) - the frequency dependent gain values for each centre frequency of the ERBs
184 * A (type: numpy array of floats) - the frequency dependent A values for each centre frequency of the ERBs
185 * alpha (type: numpy array of floats) - the frequency dependent alpha values for each centre frequency of the ERBs
186
187 Returns:
188 * specific_loudness_c1 (type: array-like matrix of floats) - An array with dimensions equal to the exc_sig containing the
189 specific loudness c1.
190 """
191 C = 0.046871 #constant parameter
192 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
193
194 return sl_c1
195
196 def _get_specific_loudness_noise_c2(exc_sig, exc_noise, tq, tn, G, A, alpha):
197 """
198 A function to calculate the specific loudness of a component of the signal for the case exc_noise < 10**10, and exc_quiet < threshold noise (c2).
199
200 Parameters:
201 * tq (type: numpy array of floats) - the threshold of quiet for each centre frequency of the ERBs
202 * tn (type: numpy array of floats) - the threshold of quiet when present in noise for each centre frequency of the ERBs
203 * G (type: numpy array of floats) - the frequency dependent gain values for each centre frequency of the ERBs
204 * A (type: numpy array of floats) - the frequency dependent A values for each centre frequency of the ERBs
205 * alpha (type: numpy array of floats) - the frequency dependent alpha values for each centre frequency of the ERBs
206
207 Returns:
208 * specific_loudness_c2 (type: array-like matrix of floats) - An array with dimensions equal to the exc_sig containing the
209 specific loudness c2.
210 """
211
212 C = 0.046871 #constant parameter
213 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)
214
215 return sl_c2
216
217 def _get_specific_loudness_noise_c3(exc_sig, exc_noise, tq, tn, G, A, alpha):
218 """
219 A function to calculate the specific loudness of a component of the signal for the case exc_noise > 10**10, and exc_quiet > threshold noise (c3).
220
221 Parameters:
222 * tq (type: numpy array of floats) - the threshold of quiet for each centre frequency of the ERBs
223 * tn (type: numpy array of floats) - the threshold of quiet when present in noise for each centre frequency of the ERBs
224 * G (type: numpy array of floats) - the frequency dependent gain values for each centre frequency of the ERBs
225 * A (type: numpy array of floats) - the frequency dependent A values for each centre frequency of the ERBs
226 * alpha (type: numpy array of floats) - the frequency dependent alpha values for each centre frequency of the ERBs
227
228 Returns:
229 * specific_loudness_c3 (type: array-like matrix of floats) - An array with dimensions equal to the exc_sig containing the
230 specific loudness c3.
231 """
232 C = 0.046871 #constant parameter
233 C2 = C/((1.04 * 10**6)**0.5)
234 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
235
236 return sl_c3
237
238 def _get_specific_loudness_noise_c4(exc_sig, exc_noise, tq, tn, G, A, alpha):
239 """
240 A function to calculate the specific loudness of a component of the signal for the case exc_noise > 10**10, and exc_quiet < threshold noise (c4).
241
242 Parameters:
243 * tq (type: numpy array of floats) - the threshold of quiet for each centre frequency of the ERBs
244 * tn (type: numpy array of floats) - the threshold of quiet when present in noise for each centre frequency of the ERBs
245 * G (type: numpy array of floats) - the frequency dependent gain values for each centre frequency of the ERBs
246 * A (type: numpy array of floats) - the frequency dependent A values for each centre frequency of the ERBs
247 * alpha (type: numpy array of floats) - the frequency dependent alpha values for each centre frequency of the ERBs
248
249 Returns:
250 * specific_loudness_c4 (type: array-like matrix of floats) - An array with dimensions equal to the exc_sig containing the
251 specific loudness c4.
252
253 """
254 C = 0.046871 #constant parameter
255 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)
256
257 return sl_c4
258
259 def _get_specific_loudness_quiet(exc_i, inc_loud_region = True):
260 """
261 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.
262
263 Parameters:
264 * 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)
265 * inc_loud_region (type: boolean) - Specifies whether to process the high levels differently to the mid levels. (Optional; Default = False)
266
267 Returns:
268 * specific_loudness (type: array-like matrix of floats) - specific loudness
269 """
270 exc_i = np.array(exc_i)
271
272 lenSig = np.shape(exc_i)[-1] #length signal
273 tq, G, A, alpha = _get_specific_loudness_parameters(lenSig)
274 specific_loudness = exc_low = exc_mid = exc_high = np.zeros(np.shape(exc_i))
275
276 less_than_tq = _get_less_than_tq(exc_i, tq) #boolean array of elements less than or equal to the threshold of quiet
277 greater_than_tq = ~less_than_tq #boolean array of elements greater than the threshold of quiet
278 greater_than_tl = _get_greater_than_tl(exc_i) #boolean array of elements greater than the loud threshold
279 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
280
281 exc_low = exc_i[less_than_tq]
282 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])
283
284 if(inc_loud_region):
285 exc_mid = exc_i[gttq_lttl]
286 exc_high = exc_i[greater_than_tl]
287 specific_loudness[gttq_lttl] = _get_specific_loudness_mid(exc_mid, G[gttq_lttl], A[gttq_lttl], alpha[gttq_lttl])
288 specific_loudness[greater_than_tl] = _get_specific_loudness_high(exc_high)
289 else:
290 exc_mid = exc_i[greater_than_tq]
291 specific_loudness[greater_than_tq] = _get_specific_loudness_mid(exc_mid, G[greater_than_tq], A[greater_than_tq], alpha[greater_than_tq])
292
293 return specific_loudness
294
295 def _get_specific_loudness_parameters(lenSig = 1):
296 """
297 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]).
298
299 Parameters:
300 * lenSig (type: numerical int) - the length of the excitation signal to be analysed. (Optional; Default = 1)
301
302 Returns:
303 * tq (type: numpy array of floats) - the threshold of quiet for each centre frequency of the ERBs
304 * G (type: numpy array of floats) - the frequency dependent gain values for each centre frequency of the ERBs
305 * A (type: numpy array of floats) - the frequency dependent A values for each centre frequency of the ERBs
306 * alpha (type: numpy array of floats) - the frequency dependent alpha values for each centre frequency of the ERBs
307 """
308
309 tq_dB, A, alpha = utils.load_sl_parameters() #load tq, A and alpha parameters
310 tq_dB = np.transpose(np.tile(tq_dB, (lenSig,1)))
311 tq = 10**(tq_dB / 10)
312 A = np.transpose(np.tile(A, (lenSig,1)))
313 alpha = np.transpose(np.tile(alpha, (lenSig,1)))
314 tq500_dB = 3.73 #threshold of quiet at 500Hz reference
315 G = 10**((tq500_dB - tq_dB)/10) #gain parameters
316
317 return tq, G, A, alpha
318
319 def _calc_k_parameter(lenSig = 1):
320 """
321 A function to return the K parameter of the loudness compression curve.
322
323 Parameters:
324 * lenSig (type: int): use if want to shape to the input data for matrix multiplication. (Optional; Default = 1)
325
326 Returns:
327 * K (type: numpy array of floats) - a parameter to the loudness compression curve
328 """
329 nerbs = 39
330 erbs = np.array(range(1,nerbs+1))
331 fc = (10**(erbs/21.366)-1)/0.004368
332
333 fkRef = 10**np.append(np.append(np.array(1), np.linspace(np.log10(50), np.log10(500), 11)), np.linspace(3,4.5,16))
334 kRef = np.append(np.append(13, np.linspace(13,-3,11)), np.ones((1,16))*-3)
335 K = np.interp(fc,fkRef,kRef)
336 K = 10**(K/10)
337 K = np.transpose(np.tile(K, (lenSig,1)))
338
339 return K
340
341 def _get_specific_loudness_low(exc_low, G, tq, A, alpha):
342 """
343 Returns the specific loudness of the low level parts of the signal. Use _get_specific_loudness_parameters() and specify lenSig
344 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
345 excitations.
346
347 EXAMPLE USE
348
349 # exc_i is the excitation intensity
350 lenSig = np.shape(exc_i)[-1] # find the length of the signal
351 tq, G, A, alpha = _get_specific_loudness_parameters(lenSig) # get the shaped loudness parameters
352 less_than_tq = _get_less_than_tq(exc_i, tq) # find which samples are lower level
353 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])
354 # only process the low level part of the signal
355
356 Parameters:
357 * exc_low (type: array-like matrix of floats) - the lower level (less than or equal to tq) excitation pattern for each ERB
358 (Required)
359 * G (type: array-like matrix of floats) - the frequency dependent loudness gain parameters for each ERB, must be same shape
360 as exc_low. (Required)
361 * tq (type: array-like matrix of floats) - the frequency dependent threshold of quiet parameters for each ERB. Must be same
362 shape as exc_low. (Required)
363 * A (type: array-like matrix of floats) - the frequency dependent A parameters for each ERB. Must be same shape as exc_low.
364 * alpha (type: array-like matrix of floats) - the frequency dependent alpha parameters for each ERB. Must be same shape as
365 exc_low. (Required)
366
367 Returns:
368 * specific_loudness_low (type: array-like matrix of floats) - An array with dimensions equal to the exc_low containing the
369 specific loudness of the signal at levels below the threshold of quiet.
370 """
371
372 C = 0.046871 #constant parameter
373 specific_loudness_low = C * ((2.0*exc_low)/(0.0+exc_low+tq))**1.5 * ((G * exc_low + A)**alpha - A**alpha)
374
375 return specific_loudness_low
376
377 def _get_specific_loudness_mid(exc_mid, G, A, alpha):
378 """
379 Returns the specific loudness of the mid level parts of the signal.
380
381 EXAMPLE USE
382
383 # exc_i is the excitation intensity
384 lenSig = np.shape(exc_i)[-1] # find the length of the signal
385 tq, G, A, alpha = _get_specific_loudness_parameters(lenSig) # get the shaped loudness parameters
386 less_than_tq = _get_less_than_tq(exc_i, tq) # find which samples are lower level
387 greater_than_tq = ~less_than_tq # find which samples are greater than the threshold of quiet
388 greater_than_tl = _get_greater_than_tl(exc_i) # find which samples are greater than the loud threshold
389 gttq_lttl = greater_than_tq[~greater_than_tl] # find which samples are mid level
390 specific_loudness[gttq_lttl] = _get_specific_loudness_low(exc_low[gttq_lttl], G[gttq_lttl], tq[gttq_lttl], A[gttq_lttl], alpha[gttq_lttl])
391 # only process the mid level part of the signal
392
393 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.
394
395
396 Parameters:
397 * exc_mid (type: array-like matrix of floats) - the mid level (larger than tq (and, optionally less than high level threshold))
398 excitation pattern for each ERB (Required)
399 * G (type: array-like matrix of floats) - the frequency dependent loudness gain parameters for each ERB, must be same shape as exc_low. (Required)
400 * A (type: array-like matrix of floats) - the frequency dependent A parameters for each ERB. Must be same shape as exc_low.
401 * alpha (type: array-like matrix of floats) - the frequency dependent alpha parameters for each ERB. Must be same shape as exc_low. (Required)
402
403 Returns:
404 * 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.
405 """
406
407 C = 0.046871 #constant parameter
408 specific_loudness_mid = C * ((G * exc_mid + A)**alpha - (A**alpha))
409
410 return specific_loudness_mid
411
412 def _get_specific_loudness_high(exc_high):
413 """
414 Returns the specific loudness of the high level parts of the signal.
415
416 EXAMPLE USE
417
418 # exc_i is the excitation intensity
419 lenSig = np.shape(exc_i)[-1] # find the length of the signal
420 tq, G, A, alpha = _get_specific_loudness_parameters(lenSig) # get the shaped loudness parameters
421 greater_than_tl = _get_greater_than_tl(exc_i) # find which samples are greater than the loud threshold
422 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])
423 # only process the mid level part of the signal
424
425 Parameters:
426 * exc_high (type: array-like matrix of floats) - the high level (larger than the threshold of high level) excitation pattern
427 for each ERB (Required)
428
429 Returns:
430 * 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.
431 """
432
433 C = 0.046871 #constant parameter
434 specific_loudness_high = C * (exc_high / (1.04 * 10**6))**0.5
435
436 return specific_loudness_high
437
438 def _get_greater_than_tl(exc_i):
439 """
440 A function to return if each element of the excitation intensity is greater than the threshold of loud.
441
442 Parameters:
443 * exc_i (type: array-like matrix of floats) - the input excitation intensity
444
445 Returns:
446 * 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
447 """
448
449 lenSig = np.shape(exc_i)[-1]
450 g_tl = exc_i[:,:]>np.transpose(np.tile(10**10,(lenSig,39)))
451
452 return g_tl
453
454 def _get_less_than_tq(exc_i, tq):
455 """
456 A function to return if each element of the excitation intensity is less than the threshold of quiet.
457
458 Parameters:
459 * exc_i (type: array-like matrix of floats) - the input excitation intensity
460 * tq (type: array-like matrix of floats) - the threshold of quiet for each ERB.
461
462 Returns:
463 * 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
464 """
465
466 if (np.shape(exc_i)!=np.shape(tq)):
467 np.transpose(np.tile(exc_i,(np.shape(exc_i)[-1],1)))
468
469 le_tq = exc_i<=tq
470
471 return le_tq
472
473 def get_excitation_i(input, fs, SPL, gammatoneLength=4096, rectify=False, verbose = False):
474 """
475 A function to calculate the excitation intensity of the input signal.
476
477 Parameters:
478 * input (type: array-like matrix of floats) - signal normalised to an amplitude range of -1 to 1. (Required)
479 * fs (type: numerical) - sample frequency of the signal, input. (Required)
480 * 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)
481 * 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)
482
483 Returns:
484 * 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].
485 """
486
487 input = np.array(input)
488
489 input = v_Pascal(input, SPL)
490
491 excitation = outMidFir(input)
492
493 b = gammatone_filter(fs, filterLength=gammatoneLength, normalise = True)
494 excitation = filterbank_decomposition(excitation, b, verbose = verbose)
495
496 if (rectify):
497 excitation = half_rectification(excitation)
498
499 excitation = pa_i(excitation)
500 excitation = at_normalise(excitation)
501
502 return excitation
503
504 def plot_excitation_response(input = None, fs = 44100, outMidFilt = True, nyquist=True, gammatone = True, gammatoneLength=4096, xscale = 'log', yscale = 'log'):
505 """
506 A function that plots the transfer function of the outer middle ear and each gammatone filter.
507
508 Parameters:
509 * fs (type: numerical) - the sampling frequency of the signal. (Optional; Default = 44100)
510 * outMidFilt (type: boolean) - filter the signal by the outer and middle ear FIR filter. (Optional; Default = True)
511 * xscale (type: string) - the scale of the frequency axis. Values are 'log' or 'linear'. (Optional; Default = 'log')
512 * yscale (type: string) - the scale of the amplitude axis. Values are 'log' or 'linear'. (Optional; Default = 'log')
513 """
514
515 if input == None:
516 input = np.zeros((np.ceil(fs)))
517 input[0] = 1
518
519 if(outMidFilt): input = outMidFir(input)
520 if(gammatone):
521 b = gammatone_filter(fs, filterLength=gammatoneLength, normalise = True)
522 input = filterbank_decomposition(input, b)
523 #input = holdsworth_gammatone_filter(input,fs)
524 numPlot = range(np.shape(input)[0])
525 else:
526 numPlot = (0,)
527 input = input.reshape(1,len(input))
528
529 for i in numPlot:
530 utils.plot_fft(input[i],xscale, yscale, nyquist=True, show=False)
531
532 plt.show()
533
534 return
535
536 def get_modulation_i(input, fs):
537 """
538 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.
539
540 Parameters:
541 * input (type: array-like matrix of floats) - the input intensity signal.
542 E.g., use get_excitation_i() to obtain excitation intensity and use as input.
543 * fs (type: numerical) - sampling frequency of input signal
544
545 Returns:
546 * 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].
547 """
548
549 input = np.array(input)
550 b = fir_antialias(fs)
551 input_lp = sp.lfilter(b,(1),input_fr)
552 input_ds = downsample(input_lp, fs)
553 fc = np.array(utils.exp_sequence(-2,4,10))
554 bw = fc/2
555 y = filterbank_decomposition(input_ds, fs, fc, bw)
556
557 return y
558
559 def outMidFir(input):
560 """
561 A function to filter the input signal with the response of the outer and middle ear.
562
563 Parameters:
564 * input (type: array-like matrix of floats) - signal normalised to an amplitude range of -1 to 1. (Required)
565
566 Returns:
567 * y (type: numpy array of floats) - array with dimensions equal to the input signal filtered by the response of the outer and middle ear.
568 """
569
570 input = np.array(input)
571 b = utils.load_outMidFir_coeff()
572 y = sp.fftconvolve(input, b)
573
574 return y
575
576 def gammatone_filter(fs, filterLength=4096, normalise = True):
577 """
578 A function to return the filter coefficients of 39 different gammatones with fc of ERBs 1 to 39.
579
580 Parameters:
581 * fs (type: numerical) - sample frequency of the signal, input. (Required)
582 * filterLength (type: int) - length of the filters (Optional; Default = True)
583 * normalise (type: boolean) - normalise peaks to united (Optional; Default = True)
584
585 Returns:
586 * b (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 bth gammatone filter can be accessed by y[n].
587 """
588
589 nerbs = 39
590 erbs = np.array(range(1,nerbs+1))
591 fc = (10**(erbs/21.366)-1)/0.004368
592 bw = 24.673 * (1 + 0.004368*fc)
593 N = 4
594 t = 1.0*np.array(range(filterLength))/fs
595
596 b = np.zeros((nerbs,filterLength))
597
598 for i in range(39):
599 b[i] = t**(N-1) * np.exp(-2*np.pi*bw[i]*t) * np.cos(2*np.pi*fc[i]*t)
600
601 if normalise:
602 A = 1/(np.abs(fft.fft(b[i], 4*filterLength))).max()
603 b[i] *= A
604
605 return b
606
607 def _holdsworth_gammatone_filter(input, fs):
608 """
609 Feed the input through a bank of gammatone filters, Holdsworth implementation. Not sure how great this works, don't use.
610
611 Parameters:
612 * input (type: array-like matrix of floats) - signal. (Required)
613 * fs (type: numerical) - sample frequency of the signal, input. (Required)
614
615 Returns:
616 * out (type: numpy array of floats) - array with size ((39),np.shape(input)) containing the output of the signal at each gammatone filter.
617 """
618 input = np.array(input)
619 input = input + 0j
620
621 T = 1.0/fs
622
623 ERBs = np.array(range(1,40))
624 f0 = (10**(ERBs/21.4)-1)/4.37e-3
625
626 inLen = len(input)
627 b = 24.673 * (1 + 0.004368*f0)
628 k = np.array(range(inLen)) + 0j
629 out = np.zeros((39,inLen))
630
631 for erb in range(39):
632
633 zArr = input*np.exp(-2*np.pi*1j*f0[erb]*k*T)
634 wArr = np.zeros((inLen+1))
635
636 for i in range(1,inLen+1):
637 wArr[i] = wArr[i-1] + (1 - np.exp(-2*np.pi*b[erb]*T))*(zArr[i-1] - wArr[i-1])
638
639 out[erb] = (wArr[1:]*np.exp(2*np.pi*1j*f0[erb]*k*T)).real
640
641 return out
642
643
644 def v_Pascal(input, SPL):
645 """
646 A function to convert a signal, normalised to an amplitude range of -1 to 1, to a signal represented in pressure (units: Pascal).
647
648 Parameters:
649 * input (type: array-like matrix of floats) - signal normalised to an amplitude range of -1 to 1. (Required)
650 * SPL (type: double) - the sound pressure level (SPL) at 0 dBFS, e.g., the SPL of a sine
651 wave with peaks at amplitude 1 and troughs at amplitude -1. (Required)
652
653 Returns:
654 * y (type: numpy array of floats) - array with dimensions equal to the input signal containing the input represented
655 as a pressure signal.
656
657 NOTE If input signal contains one or more zeros, a divide by zero warning will be outputted. This warning can be ignored.
658 """
659
660 input = np.array(input)
661 sigRMS = rms(input)
662 y = input*0.00002*10**(SPL/20.0)
663
664 return y
665
666 def rms(input):
667
668 sigRMS = np.sqrt(np.mean(input**2))
669
670 return sigRMS
671
672 def pa_i(input, C=406):
673 """
674 A function to convert a pressure signal (unit: Pascal) to an intensity signal.
675
676 Parameters:
677 * input (type: array-like matrix of floats) - pressure signal (unit: Pascal) (Required)
678 * C (type: double) - the acoustic impedance of the air (Optional; Default = 406)
679
680 Returns:
681 * y (type: numpy array of floats) - array with dimensions equal to the input signal containing the input represented
682 as a pressure signal.
683 """
684
685 input = np.array(input)
686 y = (input**2) / C
687
688 return y
689
690 def at_normalise(input):
691 """
692 A function to normalise an intensity signal with the audibility threshold.
693
694 Parameters:
695 * input (type: array-like matrix of floats) - intensity signal (unit: Pascal) (Required)
696
697 Returns:
698 * y (type: numpy array of floats) - array with dimensions equal to the input signal containing the input normalised
699 with the audibility threshold.
700 """
701
702 input = np.array(input)
703 y = input / (1*(10**-12))
704
705 return y
706
707 def downsample(input, factor=100):
708 """
709 A function to downsample a signal, input, with sampling frequency, fs, by a downsample factor of factor.
710
711 NOTE: It is advised to use the fir_antialias() function before downsampling to remove any high frequencies
712 which would otherwise represented as low frequencies due to aliasing.
713
714 Parameters:
715 * input (type: array-like matrix of floats) - input signal. (Required)
716 * factor - downsample factor (Optional; Default = 100)
717
718 Returns:
719 * output (type: numpy array of floats) - array with outer dimensions equivalent to the to the input, and
720 inner dimension equal to np.floor(lenIn / factor) where lenIn is the length of the inner dimension.
721 """
722
723 input = np.array(input)
724 shapeIn = np.shape(input)
725 nDim = np.shape(shapeIn)
726 lenIn = shapeIn[nDim[0]-1]
727 lenOut = np.floor(lenIn / factor)
728 n = np.linspace(0,lenIn,lenOut, endpoint=False).astype(np.int)
729 output = input[...,n]
730
731 return output
732
733 def half_rectification(input):
734 """
735 A function which performs a half-wave rectification on the input signal.
736
737 Parameters:
738 * input (type: array-like matrix of floats) - input signal. (Required)
739
740 Returns:
741 * y (type: numpy array of floats) - an array with dimensions of input containing the half-wave rectification of
742 input.
743 """
744
745
746 y = np.array(input)
747 y[y<0] = 0
748
749 return y
750
751 def filterbank_decomposition(input, b, a = None, verbose = False):
752 """
753 A function to run the input through a bandpass filter bank with parameters defined by the b and a coefficints.
754
755 Parameters:
756 * input (type: array-like matrix of floats) - input signal. (Required)
757 * b (type: array-like matrix of floats) - the b coefficients of each filter in shape b[numOfFilters][numOfCoeffs]. (Required)
758 * a (type: array-like matrix of floats) - the a coefficients of each filter in shape a[numOfFilters][numOfCoeffs].
759 If not specified, the filter is treated as a FIR filter. (Optional; Default = 1)
760 * verbose (type: boolean) - determines whether to display the current subroutine/progress of the procedure.
761 (Optional; Default = False)
762
763 Returns:
764 * y (type: numpy array of floats) - an array with inner dimensions equal to that of the input and outer dimension equal to
765 the length of fc (i.e. the number of bandpass filters in the bank) containing the outputs to each filter. The output
766 signal of the nth filter can be accessed using y[n].
767 """
768
769 input = np.array(input)
770 bShape = np.shape(b)
771 nFilters = bShape[0]
772 lengthFilter = bShape[1]
773
774 shape = (nFilters,) + (np.shape(input))
775 shape = np.array(shape[0:])
776 shape[-1]=shape[-1]+lengthFilter-1
777 y = np.zeros((shape))
778
779 if(verbose): sys.stdout.write("Running filterbank filterbank_decomposition.\n")
780 for i in range(nFilters):
781 if(verbose):
782 sys.stdout.write("\r" + str(int(np.round(100.0*i/nFilters))) + "% complete.")
783 sys.stdout.flush()
784 x = np.array(input)
785 y[i] = sp.fftconvolve(x,b[i])
786
787 if(verbose): sys.stdout.write("\n")
788
789 return y
790
791 def get_Modulation_filter_bank_coefficients(fs, fc, bw, order = 1024, verbose = False):
792 """
793 A function which returns the b and a coefficients of the modulation filter bank of length equal to the length of fc. Each
794 bandpass filter is designed by defining the centre frequencies, fc, and bandwidths, bw.
795
796 Parameters:
797 * fs (type: numerical) - the sampling frequency of the input signal. (Required)
798 * fc (type: array-like vector of floats) - the centre off frequencies (unnormalised) of each bandpass filter.
799 The length of this vector determines the number of filters in the bank.
800 * bw (type: array-like vector of floats) - the bandwidths (unnormalised) of each bandpass filter. Must be equal
801 to or more than the length of fc. If the length is more, all elements exceeding the length of fc - 1 will be
802 ignored.
803 * order (type: numerical int) - the order of the filter (number of taps minus 1) (Optional; Default = 1024)
804 * verbose (type: boolean) - determines whether to display the current subroutine/progress of the procedure.
805 (Optional; Default = False)
806
807 Returns:
808 * b (type: numpy array of floats) - an array of size order + 1 (i.e. a coefficient for each tap)
809 """
810
811
812 nFreqs = len(fc)
813
814 if(verbose): sys.stdout.write("Running frequency filterbank_decomposition.")
815
816 for i in range(nFreqs):
817 if(verbose):
818 sys.stdout.write("\r" + str(int(np.round(100.0*i/nFreqs))) + "% complete.")
819 sys.stdout.flush()
820 low = fc[i]-bw[i]/2;
821 high = fc[i]+bw[i]/2;
822 b = fir_bandpass(low, high, fs, order, verbose)
823
824 return b
825
826 def temporal_integration(input, fs, type='STL'):
827 """
828 Convert from instantaneous loudness to STL (type='STL'), or STL to LTL (type='LTL').
829
830 Parameters:
831 * input (type: array-like matrix of floats) - signal. Should be instantaneous loudness if type='STL', or STL if type='LTL' (Required)
832 * fs (type: numerical) - sample frequency of the signal, input. (Required)
833 * type (type: string) - 'STL' or 'LTL'
834
835 Returns:
836 * Output (type: array-like matrix of floats) - output signal.
837 """
838 Ti = 1.0/fs
839 prevOutput = 0;
840 output = np.zeros(np.shape(input))
841 if(type=='STL'):
842 Ta = 0.0217
843 Tr = 0.0495
844 elif(type=='LTL'):
845 Ta = 0.0995
846 Tr = 1.9995
847 else:
848 str = "Type argument " + type + " is unknown"
849 raise ValueError(str)
850
851 a = 1 - np.exp(-Ti/Ta)
852 r = 1 - np.exp(-Ti/Tr)
853
854 for i in range(len(input)):
855 if(input[i] > prevOutput):
856 output[i] = a*input[i] + (1-a)*prevOutput
857 else:
858 output[i] = r*input[i] + (1-r)*prevOutput
859 prevOutput = output[i]
860
861 return output
862
863 def exp_smoothing(fs, tc = 0.01):
864 """
865 Designs an exponential filter, y[n] = alpha*x[n] - -(1-alpha)*y[n-1], where alpha = T/(tc+T), where T
866 is the inverse of the sampling frequency and time constant, tc, is specified.
867
868 Parameters:
869 * fs (type: numerical) - sampling frequency of the signal to be filtered (Required)
870 * tc (type: numerical) - the time constant of the filter. (Optional; Default = 0.01)
871
872 Returns:
873 * b (type: numerical) - the coefficient of x[n]. Equal to alpha.
874 * a (type: numpy array of floats) - an array of size 2 of the coefficients of y[n] (a[0] = 1) and y[n-1]
875 """
876
877 T = 1.0/fs
878 alpha = T/(tc + T)
879 b = [alpha]
880 a = [1, -(1-alpha)]
881
882 return b, a
883
884 def antialias_fir(fs, fc=100, order=64):
885 """
886 A function which returns the b coefficients for a lowpass fir filter with specified requirements.
887 Made specifically to remove aliasing when downsampling, but can be used for any application that
888 requires a lowpass filter.
889
890 Parameters:
891 * fs (type: numerical) - sampling frequency of the signal to be filtered (Required)
892 * fc (type: numerical) - unnormalised cut off frequency of the filter (Optional; Default = 100)
893 * order (type: numerical int) - the order of the filter (number of taps minus 1) (Optional; Default = 64)
894
895 Returns:
896 * b (type: numpy array of floats) - an array of size order + 1 (i.e. a coefficient for each tap)
897 """
898
899 nyquist = 0.5*fs
900 fcNorm = fc/nyquist
901 b = sp.firwin(order+1, fcNorm)
902
903 return b
904
905 def fir_bandpass(low, high, fs, order = 4096, verbose = False):
906 """
907 A function which returns the b coefficients for a bandpass fir filter with specified requirements.
908
909 Parameters:
910 * low - the lower cutoff frequency of the filter. (Required)
911 * high - the upper cutoff frequency of the filter. (Required)
912 * fs (type: numerical) - sampling frequency of the signal to be filtered. (Required)
913 * order (type: numerical int) - the order of the filter (number of taps minus 1) (Optional; Default = 1024)
914 * verbose (type: boolean) - determines whether to display info on the current subroutine
915 of the procedure. (Optional; Default = False)
916
917 Returns:
918 * b (type: numpy array of floats) - an array of size order + 1 (i.e. a coefficient for each tap).
919 """
920
921 nyquist = 0.5*fs
922 lowNorm = low/nyquist
923 highNorm = high/nyquist
924 if(verbose): sys.stdout.write("Low: " + str(lowNorm) + "; High: " + str(highNorm))
925 b = sp.firwin(order+1, [lowNorm, highNorm], pass_zero=False)
926
927 return b