annotate utils.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
rev   line source
c@0 1 """
c@0 2 A utility module to assist cortical_model.py with general procedures, e.g., file reading and writing.
c@0 3
c@0 4 Packaged dependencies:
c@0 5 * erb.dat
c@0 6 * outMidFir.dat
c@1 7 * tq.dat
c@0 8
c@0 9 External dependencies:
c@0 10 * scipy
c@0 11 * numpy
c@0 12 * matplotlib
c@9 13 * struct
c@9 14 * wave
c@9 15 * pyaudio
c@9 16 * os
c@0 17 """
c@0 18
c@0 19 import numpy as np
c@8 20 import scipy.io.wavfile as wav
c@0 21 import matplotlib.pyplot as plt
c@0 22 import scipy.fftpack as fft
c@8 23 import wave
c@8 24 import struct
c@8 25 from os import getcwd
c@9 26 import pyaudio
c@8 27
c@8 28 gPath = getcwd()
c@9 29 p = None
c@8 30
c@8 31 def set_path_to_data(path):
c@9 32 """
c@9 33 Set path to data.
c@9 34
c@9 35 Parameters:
c@9 36 * path (type: string) - directory path to data
c@9 37 """
c@8 38 global gPath
c@8 39 gPath = path
c@8 40
c@8 41 return
c@0 42
c@0 43 def load_erb_data():
c@0 44 """
c@0 45 Loads and returns 39 ERB frequencies and bandwidths
c@0 46
c@0 47 Parameters:
c@0 48 NONE
c@0 49
c@0 50 Returns:
c@0 51 * fc (type: numpy array of floats) - a vector of length 39 with elements containing
c@0 52 the centre frequencies of each ERB.
c@0 53 * bw (type: numpy array of floats) - a vector of length 39 with elements containing
c@0 54 the bandwidths of each ERB.
c@0 55
c@0 56 Dependencies:
c@0 57 * erb.dat (place in the same folder as utils.pyc)
c@0 58 """
c@1 59 # read data from a text file
c@8 60 data=np.loadtxt(gPath + "/erb.dat",delimiter=",")
c@0 61
c@0 62 # load centre frequencies from the first column into fc
c@0 63 fc=np.array(data[:,0])
c@0 64 # load bandwidths from the second column into bw
c@0 65 bw=np.array(data[:,1])
c@0 66
c@0 67 return fc,bw
c@0 68
c@0 69 def load_outMidFir_coeff():
c@0 70 """
c@0 71 Loads and returns 39 ERB frequencies and bandwidths
c@0 72
c@0 73 Parameters:
c@0 74 NONE
c@0 75
c@0 76 Returns:
c@0 77 * b (type: numpy array of floats) - a vector of length 4097 containing the impulse
c@0 78 response of the outer middle ear.
c@0 79
c@0 80 Dependencies:
c@0 81 * outMidFir.dat (place in the same folder as utils.pyc)
c@0 82 """
c@0 83
c@8 84 b=np.array(np.loadtxt(gPath + "/outMidFir.dat", delimiter=","))
c@0 85
c@0 86 return b
c@0 87
c@1 88 def load_sl_parameters():
c@1 89 """
c@1 90 Loads the loudness parameters for each ERB fc into a tuple. A shortcut for load_tq_data(), load_A_data() and load_alpha_data().
c@1 91
c@1 92 Parameters:
c@1 93 NONE
c@1 94
c@1 95 Returns:
c@1 96 * tq (type: numpy array of floats) - a vector of length 39 containing the threshold excitation
c@1 97 intensity in quietat each ERB centre frequency
c@1 98 * A (type: numpy array of floats) - a vector of length 39 containing the parameter A for each ERB fc
c@1 99 * alpha (type: numpy array of floats) - a vector of length 39 containing the parameter alpha for each ERB fc
c@1 100
c@1 101 Dependencies:
c@1 102 * tq.dat (place in the same folder as utils.pyc)
c@1 103 * A.dat (place in the same folder as utils.pyc)
c@1 104 * alpha.dat (place in the same folder as utils.pyc)
c@1 105 """
c@5 106 tq_dB = load_tqdB_data()
c@1 107 A = load_A_data()
c@1 108 alpha = load_alpha_data()
c@1 109
c@5 110 return tq_dB, A, alpha
c@1 111
c@5 112 def load_tqdB_data():
c@1 113 """
c@1 114 Loads and returns the excitation threshold of quiet for each ERB fc.
c@1 115
c@1 116 Parameters:
c@1 117 NONE
c@1 118
c@1 119 Returns:
c@1 120 * tq (type: numpy array of floats) - a vector of length 39 containing the threshold excitation
c@1 121 intensity in quietat each ERB centre frequency
c@1 122
c@1 123 Dependencies:
c@1 124 * tq.dat (place in the same folder as utils.pyc)
c@1 125 """
c@1 126
c@8 127 tq = np.array(np.loadtxt(gPath + "/tq_dB.dat",delimiter=","))
c@1 128
c@1 129 return tq
c@1 130
c@1 131 def load_A_data():
c@1 132 """
c@1 133 Loads and returns the excitation A parameters for each ERB fc.
c@1 134
c@1 135 Parameters:
c@1 136 NONE
c@1 137
c@1 138 Returns:
c@1 139 * A (type: numpy array of floats) - a vector of length 39 containing the parameter A for each ERB fc
c@1 140
c@1 141 Dependencies:
c@1 142 * A.dat (place in the same folder as utils.pyc)
c@1 143 """
c@1 144
c@8 145 A = np.array(np.loadtxt(gPath + "/A.dat",delimiter=","))
c@1 146
c@1 147 return A
c@1 148
c@1 149 def load_alpha_data():
c@1 150 """
c@1 151 Loads and returns the excitation alpha parameters for each ERB fc.
c@1 152
c@1 153 Parameters:
c@1 154 NONE
c@1 155
c@1 156 Returns:
c@1 157 * alpha (type: numpy array of floats) - a vector of length 39 containing the parameter alpha for each ERB fc
c@1 158
c@1 159 Dependencies:
c@1 160 * alpha.dat (place in the same folder as utils.pyc)
c@1 161 """
c@1 162
c@8 163 alpha = np.array(np.loadtxt(gPath + "/alpha.dat",delimiter=","))
c@1 164
c@1 165 return alpha
c@1 166
c@0 167 def exp_sequence(start, stop, n, base=2):
c@0 168 """
c@0 169 Creates a linear sequence with n points starting from start and ending at stop. For each
c@1 170 element in the sequence, i, the output sequence is 2**i, generating an exponential sequence
c@0 171 with base 2.
c@0 172
c@0 173 Parameters:
c@0 174 * start (type: numerical int) - determines the first element of the sequence, i.e.,
c@1 175 base**start. (Required)
c@0 176 * stop (type: numerical int) - determines the last element of the sequence, i.e.,
c@1 177 base**stop. (Required)
c@0 178 * n (type = numerical int) - determines the number of elements in the sequence. (Required)
c@0 179 * base (type: numerical) - determines the exponential base. (Optional; Default = 2)
c@0 180
c@0 181 Returns:
c@0 182 * seq - the exponential sequence
c@0 183 """
c@0 184
c@0 185 seq = [base**x for x in np.linspace(start,stop,n)]
c@0 186
c@0 187 return seq
c@0 188
c@0 189 def wavread(file):
c@0 190 """
c@0 191 Reads the audio data from a wav file and converts it to floating point ranging from -1 to 1.
c@0 192
c@0 193 Parameters:
c@0 194 * file (type: string) - the name of the wav file to read from. (Required)
c@0 195
c@0 196 Returns:
c@0 197 * fs (type: numerical) - the sampling frequency of the signal storied in file
c@0 198 * data (type: numpy array of floats) - the data of the signal stored in file normalised
c@0 199 to an amplitude range of -1 to 1.
c@9 200
c@9 201 NOTE: Works for 16 bit and 32 bit files, and any endian.
c@0 202 """
c@0 203
c@8 204 fs,data = wav.read(file)
c@1 205 data = np.array(int_to_nfloat(data))
c@0 206
c@0 207 return fs,data
c@0 208
c@9 209 def sound(data, fs):
c@9 210 """
c@9 211 Play the audio signal stored in data.
c@9 212
c@9 213 Parameters:
c@9 214 * data (type: array-like matrix of floats) - the signal data normalised from -1 to 1. The signal will be clipped if not
c@9 215 in this range. (Required)
c@9 216 * fs (type: numerical) - the sampling frequency of the signal. (Required)
c@9 217
c@9 218 NOTE: Only works for mono signals. Place data in a vector.
c@9 219 """
c@9 220 global p
c@9 221
c@9 222 if p == None:
c@9 223 p = pyaudio.PyAudio()
c@9 224
c@9 225 try:
c@9 226 N = data.dtype.itemsize/8
c@9 227 stream = p.open(format = p.get_format_from_width(4), channels=1, rate=fs, output=True)
c@9 228
c@9 229 for i in range(len(data)):
c@9 230 stream.write(np.float32(data[i]), num_frames=1)
c@9 231
c@9 232 stream.close()
c@9 233 except KeyboardInterrupt:
c@9 234 print "\nClosing stream."
c@9 235 stream.close()
c@9 236
c@9 237 return
c@9 238
c@8 239 def wavread2(filename):
c@9 240 """
c@9 241 Reads the audio data from a wav file and converts it to floating point ranging from -1 to 1.
c@9 242
c@9 243 Parameters:
c@9 244 * file (type: string) - the name of the wav file to read from. (Required)
c@9 245
c@9 246 Returns:
c@9 247 * fs (type: numerical) - the sampling frequency of the signal storied in file
c@9 248 * data (type: numpy array of floats) - the data of the signal stored in file normalised
c@9 249 to an amplitude range of -1 to 1.
c@9 250
c@9 251 NOTE: This works for 16bit, 24bit and 32bit files, but must be little endian.
c@9 252 """
c@8 253 file = wave.open(filename)
c@8 254 nChans, sampwidth, fs, nFrames, comptype, compname = file.getparams()
c@8 255
c@8 256 N = 8*sampwidth
c@8 257
c@8 258 data = np.zeros((nChans, nFrames))
c@8 259
c@8 260 if sampwidth == 2:
c@8 261 unpackChar = 'h'
c@8 262 elif sampwidth == 3:
c@8 263 unpackChar = 'hc'
c@8 264 elif sampwidth == 4:
c@8 265 unpackChar = 'i'
c@8 266
c@8 267 frame = [[] for i in range(nChans)]
c@8 268
c@8 269
c@8 270 for i in range(nFrames):
c@8 271 bytes = file.readframes(1)
c@8 272
c@8 273 if nChans > 1:
c@8 274 for j in range(nChans):
c@8 275 index = j*sampwidth
c@8 276 frame[j] = bytes[index:index+sampwidth]
c@8 277 else:
c@8 278 frame[0] = bytes
c@8 279
c@8 280 for j in range(nChans):
c@8 281 if sampwidth == 2:
c@8 282 data[j][i] = struct.unpack('<h', frame[j])[0]
c@8 283 elif sampwidth == 3:
c@8 284 data[j][i] = struct.unpack('<i', frame[j] + ('\0' if frame[j][2] < '\x80' else '\xff'))[0]
c@8 285 elif sampwidth == 4:
c@8 286 data[j][i] = struct.unpack('<i', frame[j])[0]
c@8 287
c@8 288 data[data<0] = data[data<0]/(2**N)
c@8 289 data[data>0] = data[data>0]/((2**N)-1)
c@8 290
c@8 291 if nChans == 1:
c@8 292 data = data.reshape((nFrames))
c@8 293
c@8 294 file.close()
c@8 295
c@8 296 return fs,data
c@8 297
c@0 298 def wavwrite(file, fs, data, precision = 16):
c@0 299 """
c@0 300 Unnormalises the audio data to a specified integer precision and converts to an int, then
c@0 301 writes the audio data to a wav file. (E.g., if 16-bit precision, then highest amplitude
c@1 302 is equal to 2**16).
c@0 303
c@0 304 Parameters:
c@0 305 * file (type: string) - the name of the wav file to write to. (Required)
c@0 306 * fs (type: numerical) - the sampling frequency of the signal. (Required)
c@0 307 * data (type: array-like matrix of floats) - the signal data normalised from -1 to 1. The signal will be clipped if not
c@0 308 in this range. (Required)
c@0 309 * precision (type: numerical int)- the bit precision to store at. Can only be 16 or 32 bit, because that is
c@0 310 all scipy allows.
c@0 311
c@0 312 Returns:
c@0 313 NONE
c@0 314
c@0 315 TODO explore WAVE package to be allow for 24bit precision.
c@0 316 """
c@0 317
c@0 318 data[data>1] = 1
c@0 319 data[data<-1] = -1
c@0 320
c@0 321 if(precision == 16):
c@0 322 dtype = np.int16
c@0 323 elif(precision == 32):
c@0 324 dtype = np.int32
c@0 325 else:
c@0 326 print "Error: precision can only be 16 or 32 bit due to scipy package."
c@0 327 return
c@0 328
c@0 329 data = nfloat_to_int(data)
c@8 330 wav.write(file, fs, data)
c@0 331
c@0 332 return
c@0 333
c@9 334 def plot_fft(x, xscale = 'log', yscale = 'log', nyquist = True, show = True):
c@0 335 """
c@0 336 Plots the fft of signal x. If the figure is not shown, the current plot is held to allow other plots to
c@0 337 be added to the same figure.
c@0 338
c@0 339 Parameters:
c@0 340 * x (type: array-like matrix of floats) - the signal to be analysed. (Required)
c@0 341 * xscale (type: string) - the scale of the frequency axis. Values are 'log' or 'linear'. (Optional; Default = 'log')
c@0 342 * yscale (type: string) - the scale of the amplitude axis. Values are 'log' or 'linear'. (Optional; Default = 'log')
c@0 343 * show (type: boolean) - specifies whether the figure should be shown. If False, the current plot will be held so
c@0 344 other plots can be added to the figure. (Optional; Default = True)
c@0 345
c@0 346 Returns:
c@0 347 NONE
c@0 348 """
c@0 349
c@4 350 plt.gca().set_xscale(xscale)
c@1 351
c@1 352 x = np.array(x)
c@0 353 fftx = np.absolute(fft.fft(x))
c@9 354 if(nyquist):
c@9 355 fftx = fftx[0:len(fftx)/2]
c@9 356
c@9 357 if(yscale=='log'):
c@9 358 fftx=20*np.log10(fftx)
c@9 359 else:
c@9 360 plt.gca().set_yscale(yscale)
c@9 361
c@9 362 plt.plot(fftx)
c@9 363 plt.xlabel('Frequency (Hz)')
c@9 364 plt.ylabel('Frequency Response (dB)')
c@9 365
c@0 366 if(show):
c@0 367 plt.show()
c@0 368 else:
c@0 369 plt.hold(True)
c@0 370
c@0 371 return
c@0 372
c@1 373 def plot_waveform(x, show = True):
c@1 374 """
c@1 375 Plots the waveform of signal x. If the figure is not shown, the current plot is held to allow other plots to
c@1 376 be added to the same figure.
c@1 377
c@1 378 Parameters:
c@1 379 * x (type: array-like matrix of floats) - the signal to be plotted. (Required)
c@1 380 * show (type: boolean) - specifies whether the figure should be shown. If False, the current plot will be held so
c@1 381 other plots can be added to the figure. (Optional; Default = True)
c@1 382
c@1 383 Returns:
c@1 384 NONE
c@1 385 """
c@1 386
c@1 387 x = np.array(x)
c@1 388 plt.plot(x)
c@1 389
c@1 390 if(show): plt.show()
c@1 391 else: plt.hold(True)
c@1 392
c@1 393 return
c@1 394
c@1 395 def int_to_nfloat(input, outputtype=np.float32):
c@0 396 """
c@0 397 Convert integer with to floating point with a range from -1 to 1.
c@0 398
c@0 399 Parameters:
c@0 400 * input (type: array-like matrix of ints) - a signed integer array. (Required)
c@0 401 dtype : the output datatype. (Optional; Default = np.float32)
c@0 402
c@0 403 Returns:
c@0 404 * y (type: array-like matrix of floats) - a float array normalised to a
c@0 405 range of -1 to 1.
c@0 406 """
c@0 407
c@0 408 input = np.array(input)
c@0 409 assert input.dtype.kind == 'i', "'input' must be an array-like matrix of integers."
c@1 410 outputtype = np.dtype(outputtype)
c@0 411 inputdtype = np.dtype(type(input[0]))
c@1 412 input = input.astype(outputtype)
c@0 413
c@0 414 input[input > 0] = input[input > 0] / np.iinfo(inputdtype).max
c@0 415 input[input < 0] = input[input < 0] / -np.iinfo(inputdtype).min
c@0 416
c@0 417 y = input
c@0 418 return y
c@0 419
c@0 420 def nfloat_to_int(input, type=np.int16):
c@0 421 """
c@0 422 Convert a float array with amplitude ranging from -1 to 1 to a unnormalised signed
c@0 423 integer array with specified precision.
c@0 424
c@0 425 Parameters:
c@0 426 * input (type: array-like matrix of floats) - a float array. (Required)
c@0 427 dtype : the output datatype (also determines the precision).
c@0 428 (Optional; Default = np.int16)
c@0 429
c@0 430 Returns:
c@0 431 * y (type: array-like matrix of ints) - an unnormalised to a
c@0 432 range of -1 to 1.
c@0 433 """
c@0 434
c@0 435 input = np.array(input)
c@0 436 assert input.dtype.kind == 'f', "'input' must be an array of floats!"
c@0 437
c@1 438 input[input > 0] = input[ input > 0 ] * np.iinfo(type).max
c@1 439 input[input < 0] = input[ input < 0 ] * -np.iinfo(type).min
c@0 440
c@0 441 y = input.astype(type)
c@0 442
c@0 443 return y