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