annotate equalLoudness.py @ 10:c5e7162fb8ea

* A bit of commenting
author Carl Bussey <c.bussey@se10.qmul.ac.uk>
date Tue, 22 Apr 2014 01:20:56 +0100
parents ef62cca13f36
children ca98f5f26bcb
rev   line source
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@8 8 fArray = [20, 50, 100, 200, 400, 800, 1000, 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