c@9
|
1 import pyphon as pp
|
c@8
|
2 import numpy as np
|
c@8
|
3 import matplotlib.pyplot as plt
|
c@8
|
4 import pdb
|
c@8
|
5 import scipy.optimize as opt
|
c@8
|
6 import scipy.interpolate as interp
|
c@8
|
7
|
c@11
|
8 fArray = [20, 50, 100, 200, 400, 800, 1000, 1500, 2000, 3000, 4000, 5000, 6000, 10000, 12000, 15000]
|
c@9
|
9 #dArray = np.array([5, 10, 50, 100, 150, 200, 500, 1000])/1000.0
|
c@9
|
10 dArray = np.array([8, 16, 32, 64, 128, 256, 512, 1024])/1000.0
|
c@8
|
11
|
c@9
|
12 def get_elc_range(xtol=0.0000005, verbose=False):
|
c@9
|
13 """
|
c@9
|
14 Plot the prediction of the equal loudness contours of frequencies for a range of SPL levels from 10 to 120dB, intervals of 10dB.
|
c@10
|
15
|
c@10
|
16 Parameters:
|
c@10
|
17 * xtol (type: float) - x tolerance of fsolve
|
c@10
|
18 * verbose (type: boolean) - I tell you what I know
|
c@10
|
19
|
c@10
|
20 Returns:
|
c@10
|
21 * elc (type: numpy array of floats) - SPL at which each frequency in fArray is equal to i*10 phons, where i is the first dimension index
|
c@10
|
22
|
c@9
|
23 """
|
c@9
|
24 levels = range(20, 100, 20)
|
c@8
|
25 elc = np.zeros((len(levels), len(fArray)))
|
c@8
|
26
|
c@8
|
27 for i in range(len(levels)):
|
c@8
|
28 if(verbose):
|
c@8
|
29 print "Level: " + str(levels[i])
|
c@9
|
30 elc[i] = get_elc(levels[i], xtol=xtol, verbose=verbose)
|
c@8
|
31 plot_elc(elc[i], show=False)
|
c@8
|
32
|
c@8
|
33 plt.show()
|
c@8
|
34
|
c@8
|
35 return elc
|
c@8
|
36
|
c@9
|
37 def get_elc(SPL = 90, xtol=0.0000005, verbose=False):
|
c@9
|
38 """
|
c@9
|
39 Plot the prediction of the equal loudness contours for specified level.
|
c@10
|
40
|
c@10
|
41 Parameters:
|
c@10
|
42 * SPL (type: float) - SPL of the reference, or the loudness level
|
c@10
|
43 * xtol (type: float) - x tolerance of fsolve
|
c@10
|
44 * verbose (type: boolean) - I tell you what I know if you give me True
|
c@10
|
45
|
c@10
|
46 Returns:
|
c@10
|
47 * elc (type: numpy array of floats) - SPL at which each frequency in fArray is equal to SPL phons
|
c@9
|
48 """
|
c@8
|
49 fs = 44100
|
c@9
|
50 t = np.array(range(44100))
|
c@8
|
51 elc = np.zeros((len(fArray)))
|
c@9
|
52 env = get_cos2_env(44100, fs)
|
c@9
|
53
|
c@9
|
54 refSig = np.sin(1000 * t * 2 * np.pi / fs) * env
|
c@9
|
55 refSig = refSig/rms(refSig)
|
c@9
|
56 refLoudness = pp.calc_loudness_quiet(refSig, fs, SPL, rectify=False, inc_loud_region=True)[2].max()
|
c@8
|
57
|
c@8
|
58 for i in range(len(fArray)):
|
c@8
|
59 if (fArray[i] == 1000):
|
c@8
|
60 elc[i] = SPL
|
c@8
|
61 else:
|
c@8
|
62 if(verbose):
|
c@8
|
63 print fArray[i]
|
c@8
|
64 input = np.sin(fArray[i] * t * 2 * np.pi / fs) * env
|
c@9
|
65 input = input / rms(input)
|
c@9
|
66 elc[i] = get_el(input, fs, refLoudness, SPL, xtol=xtol, verbose=verbose)
|
c@8
|
67
|
c@8
|
68 return elc
|
c@8
|
69
|
c@9
|
70 def get_temporal_descrimination(verbose=False):
|
c@9
|
71 """
|
c@10
|
72 Return the prediction of STL and peak of STL for tones with short duration.
|
c@10
|
73
|
c@10
|
74 Parameters
|
c@10
|
75 * verbose (type: boolean) - I tell you what I know if you give me True
|
c@10
|
76
|
c@10
|
77 Returns:
|
c@10
|
78 * STLMax (type: array-like of floats) - peak short term loudness for each duration
|
c@10
|
79 * STL (type: array-like of floats) - short term loudness function for each duration
|
c@9
|
80 """
|
c@9
|
81
|
c@9
|
82
|
c@9
|
83 fs = 44100
|
c@9
|
84 STLMax = np.zeros((len(dArray)))
|
c@9
|
85 STL = []
|
c@8
|
86
|
c@9
|
87 for i in range(len(dArray)):
|
c@9
|
88 if(verbose):
|
c@9
|
89 print dArray[i]
|
c@9
|
90 length = np.floor(dArray[i]*fs)
|
c@9
|
91 env = get_cos2_env(length, fs, 0.005*fs)
|
c@9
|
92 input = np.sin(np.arange(length)*2*np.pi*1000/fs) * env
|
c@9
|
93 input = input/rms(input)
|
c@9
|
94 STL.append(pp.calc_loudness_quiet(input, fs, 60, inc_loud_region=True)[2])
|
c@9
|
95 STLMax[i] = STL[i].max()
|
c@9
|
96
|
c@9
|
97 return STLMax, STL
|
c@9
|
98
|
c@9
|
99 def get_cos2_env(lengthSig, fs, lengthEnv = None):
|
c@9
|
100 """
|
c@9
|
101 Return a cos**2 envelope.
|
c@9
|
102
|
c@9
|
103 Parameters:
|
c@9
|
104 * lengthSig (type: int) - length of signal to be enveloped
|
c@9
|
105 * fs - (type: int) - sampling frequency
|
c@9
|
106 * lengthEnv (type: int) - length of ramps
|
c@9
|
107
|
c@9
|
108 Returns:
|
c@9
|
109 * env (type: numpy array of floats)- envelope
|
c@9
|
110 """
|
c@9
|
111
|
c@9
|
112 if(lengthEnv == None):
|
c@9
|
113 lengthEnv = np.floor(0.02*fs)
|
c@9
|
114
|
c@9
|
115 env = np.ones((lengthSig))
|
c@9
|
116 T = int(lengthEnv)
|
c@9
|
117 tRamp = np.array(range(T))
|
c@9
|
118 f = 1/(lengthEnv/fs*4)
|
c@9
|
119 env[tRamp] = np.sin(2*np.pi*f*tRamp/fs)**2
|
c@9
|
120 env[-tRamp] = np.sin(2*np.pi*f*tRamp/fs)**2
|
c@9
|
121
|
c@9
|
122 return env
|
c@9
|
123
|
c@9
|
124 def get_el(input, fs, refL, SPL = 90, xtol=0.0000005, verbose=False):
|
c@9
|
125 """
|
c@9
|
126 Get the SPL of the input at equal loudness to the reference.
|
c@9
|
127
|
c@9
|
128 Parameters:
|
c@9
|
129 * input (type: array-like of floats) - input signal
|
c@9
|
130 * fs (type: int) - sampling frequency
|
c@9
|
131 * SPL (type: float) - the level to start searching at
|
c@9
|
132 * xtol (type: float) - x tolerance of fsolve
|
c@10
|
133 * verbose (type: boolean) - I tell you what I know if you give me True
|
c@10
|
134
|
c@10
|
135 Returns:
|
c@10
|
136 x (type: float) - sound pressure level at equal loudness
|
c@9
|
137 """
|
c@9
|
138
|
c@9
|
139 x = opt.fsolve(getLoudnessDifference, SPL, (input, fs, refL, verbose), factor=0.1, xtol=xtol)
|
c@8
|
140
|
c@8
|
141 return x
|
c@8
|
142
|
c@8
|
143 def getLoudnessDifference(SPL, input, fs, refL, verbose=False):
|
c@9
|
144 """
|
c@9
|
145 Used for get_el fsolve
|
c@9
|
146
|
c@9
|
147 Parameters:
|
c@9
|
148 * SPL (type: float) - the level to set input to
|
c@9
|
149 * input (type: array-like of floats) - input signal
|
c@9
|
150 * fs (type: int) - sampling frequency
|
c@10
|
151
|
c@10
|
152 Returns:
|
c@10
|
153 difference (type: float) - difference in loudness
|
c@9
|
154 """
|
c@8
|
155 if(verbose):
|
c@8
|
156 print "SPL: ", SPL
|
c@9
|
157 input = input/rms(input)
|
c@9
|
158 compLoudness = pp.calc_loudness_quiet(input, fs, SPL, rectify=False, inc_loud_region=True)[2].max()
|
c@8
|
159 difference = np.abs(refL - compLoudness)
|
c@8
|
160 if(verbose):
|
c@8
|
161 print "Difference: ", difference
|
c@8
|
162
|
c@8
|
163 return difference
|
c@8
|
164
|
c@8
|
165 def rms(input):
|
c@9
|
166 """
|
c@9
|
167 RMS of the input
|
c@10
|
168
|
c@10
|
169 Parameters:
|
c@10
|
170 * input (type: array-like of floats): input array
|
c@10
|
171
|
c@10
|
172 Returns:
|
c@10
|
173 * output (type: float): the rms
|
c@9
|
174 """
|
c@10
|
175 output = np.sqrt(np.mean(input**2, -1))
|
c@8
|
176
|
c@8
|
177 return output
|
c@8
|
178
|
c@10
|
179 def plot_elc(elc, show = True):
|
c@9
|
180 """
|
c@9
|
181 Plot an equal loudness contour
|
c@10
|
182
|
c@10
|
183 Parameters:
|
c@10
|
184 * elc (type: array-like of floats) - equal loudness contour x, obtained from get_elc
|
c@10
|
185 * show (type: boolean) - show the figure or turn hold on
|
c@9
|
186 """
|
c@8
|
187 plt.gca().set_xscale('log')
|
c@8
|
188 plt.ylim((0,170))
|
c@10
|
189 plt.plot(fArray, elc, 'x-')
|
c@8
|
190 if(show):
|
c@8
|
191 plt.show()
|
c@9
|
192 else:
|
c@9
|
193 plt.hold(True)
|
c@8
|
194
|
c@8
|
195 return |