Mercurial > hg > aimc
view trunk/matlab/bmm/carfac/Carfac.py @ 690:76f749d29b48
Fix memory leak in CARFAC.
Also get rid of most uses of auto, which tend to hurt readability
unless the type name is particularly long, especially when it masks
pointers.
author | ronw@google.com |
---|---|
date | Tue, 11 Jun 2013 21:41:53 +0000 |
parents | 9b478420cbe2 |
children |
line wrap: on
line source
# Carfac.py - Cochlear filter model based on Dick Lyons work. This material taken from his Hearing book (to be published) # Author: Al Strelzoff: strelz@mit.edu # The point of this file is to extract some of the formulas from the book and practice with them so as to better understand the filters. # The file has been written and tested for python 2.7 from numpy import cos, sin, pi, e, real,imag,arctan2,log10 from pylab import figure, plot,loglog, title, axis, show fs = 22050.0 # sampling rate # given a frequency f, return the ERB def ERB_Hz(f): # Ref: Glasberg and Moore: Hearing Research, 47 (1990), 103-138 return 24.7 * (1.0 + 4.37 * f / 1000.0) # ERB parameters ERB_Q = 1000.0/(24.7*4.37) # 9.2645 ERB_break_freq = 1000/4.37 # 228.833 ERB_per_step = 0.3333 # set up channels first_pole_theta = .78 * pi # We start at the top frequency. pole_Hz = first_pole_theta * fs / (2.0*pi) # frequency of top pole min_pole_Hz = 40.0 # bottom frequency # set up the pole frequencies according to the above parameters pole_freqs = [] # empty list of pole frequencies to fill, zeroth will be the top while pole_Hz > min_pole_Hz: pole_Hz = pole_Hz - ERB_per_step * ERB_Hz(pole_Hz) pole_freqs.append(pole_Hz) n_ch = len(pole_freqs) # n_ch is the number of channels or frequency steps print('num channels',n_ch) # Now we have n_ch, the number of channels, so can make the array of filters by instantiating the filter class (see below) # before we make the filters, let's plot the position of the frequencies and the values of ERB at each. fscale = [] erbs = [] figure(1) for i in range(n_ch): f = pole_freqs[i] # the frequencies from the list ERB = ERB_Hz(f) # the ERB value at each frequency fscale.append(f) erbs.append(ERB) # plot a verticle hash at each frequency: u = [] v = [] for j in range(5): u.append(f) v.append(10.0 + float(j)) plot(u,v) loglog(fscale,erbs) title('ERB scale') # This filter class includes some methods useful only in design. They will not be used in run time implementation. # From figure 14.3 in Dick Lyon's book. # When translating to C++, this class will become a struct, and all the methods will be moved outside. #########################################################The Carfac filter class################################################################################# # fixed parameters min_zeta = 0.12 class carfac(): # instantiate the class (in C++, the constructor) def __init__(self,f): self.frequency = f theta = 2.0 * pi * f/fs r = 1.0 - sin(theta) * min_zeta a = r * cos(theta) c = r * sin(theta) h = sin(theta) g = (1.0 - 2.0 * a + r ** 2)/(1.0 - 2.0 * a + h * c + r ** 2) self.gh = g*h # no need to repeat in real time # make all parameters properties of the class self.a = a self.c = c self.r = r self.theta = theta self.h = h self.g = g # the two storage elements. Referring to diagram 14.3 on p.263, z2 is the upper storage register, z1, the lower self.z1 = 0.0 self.z2 = 0.0 # frequency response of this filter self.H = [] # the total frequency magnitude of this filter including all the filters in front of this one self.HT = [] # this list will be filled by multiplying all the H's ahead of it together with its own (H) # execute one clock tick. Take in one input and output one result. Execution semantics taken from fig. 14.3 # This execution model is not tested in this file. Here for reference. See the file Exec.py for testing this execution model. This is the main run time method. def input(self,X): # recover the class definitions of these variables. These statements below take up zero time at execution since they are just compiler declarations. # computation below is organized as some loads, followed by 3 2x2 multiply accumulates # Note: this function is not exercised in this file and is here only for reference a = self.a c = self.c g = self.g gh = self.gh z1 = self.z1 # z1 is the lower storage in fig. 14.3 z2 = self.z2 # calculate what the next value of z1 will be, but don't overwrite current value yet. next_z1 = (a * z1) + (c * z2) # Note: it is a 2 element multiply accumulate. compute first so as not to have to do twice. # the output Y Y = g * X + gh * next_z1 # Note: organized as a 2 element multiply accumulate. #stores self.z2 = X + (a * z2) - (c * z1) #Note: this is a 2 element multiply accumulate self.z1 = next_z1 return Y # The output # complex frequency response of this filter at frequency w. That is, what it contributes to the cascade # this method is used for test only. It finds the frequency magnitude. Not included in run time filter class. def Hw(self,w): a = self.a c = self.c g = self.g h = self.h r = self.r z = e ** (complex(0,w)) # w is in radians so this is z = exp(jw) return g * (1.0 + (h*c*z)/(z**2 - 2.0*a*z + r**2 )) ######################################################End of Carfac filter class######################################################################## # instantiate the filters # n_ch is the number of filters as determined above Filters = [] # the list of all filters, the zeroth is the top frequency for i in range(n_ch): f = pole_freqs[i] filter = carfac(f) # note: get the correct parameters for r and h from Dick's matlab script. Load them here from a table. Filters.append(filter) # sweep parameters steps = 1000 figure(2) title('CarFac individual filter frequency response') # note: the scales are linear, not logrithmic as in the book for i in range(n_ch): filter = Filters[i] # plotting arrays u = [] v = [] # calculate the frequency magnitude by stepping the frequency in radians for j in range(steps): w = pi * float(j)/steps u.append(w) mag = filter.Hw(w) # freq mag at freq w filter.H.append(mag) # save for later use filter.HT.append(mag) # will be total response of cascade to this point after we do the multiplication in a step below v.append(abs(mag)) # y plotting axis plot(u,v) # calculate the phase response of the same group of filters figure(3) title('Carfac individual filter Phase lag') for i in range(n_ch): filter = Filters[i] u = [] v = [] for j in range(steps): x = pi * float(j)/steps u.append(x) mag = filter.H[j] phase = arctan2(-imag(mag),-real(mag)) - pi # this formula used to avoid wrap around v.append(phase) # y plotting axis plot(u,v) axis([0.0,pi,-3.0,0.05]) # calulate and plot cascaded frequency response figure(4) title('CarFac cascaded filter frequency response') for i in range(n_ch-1): filter = Filters[i] next = Filters[i+1] u = [] v = [] for j in range(steps): w = pi * float(j)/steps u.append(w) mag = filter.HT[j] * next.HT[j] next.HT[j] = mag v.append(abs(mag)) plot(u,v) # calculate and plot the phase responses of the cascaded filters figure(5) title('Carfac cascaded filter Phase lag') for i in range(n_ch): filter = Filters[i] u = [] v = [] # store list of phases w = [] # second copy of phases needed for phase unwrapping for j in range(steps): x = pi * float(j)/steps u.append(x) mag = filter.HT[j] a = imag(mag) b = real(mag) phase = arctan2(a,b) v.append(phase) w.append(phase) # unwrap the phase for i in range(1,len(v)): diff = v[i]-v[i-1] if diff > pi: for j in range(i,len(w)): w[j] -= 2.0 * pi elif diff < -pi: for j in range(i,len(w)): w[j] += 2.0 * pi else: continue # convert delay to cycles for i in range(len(w)): w[i] /= 2.0 * pi plot(u,w) axis([0.0,pi,-5.0,0.05]) # calculate group delay as cascaded lag/filter center frequency figure(6) title('Carfac group delay') for i in range(n_ch): filter = Filters[i] u = [] v = [] # store list of phases w = [] # second copy of phases needed for phase unwrapping for j in range(1,steps): x = pi * float(j)/steps u.append(x) mag = filter.HT[j] a = imag(mag) b = real(mag) phase = arctan2(a,b) v.append(phase) w.append(phase) for i in range(1,len(v)): diff = v[i]-v[i-1] if diff > pi: for j in range(i,len(w)): w[j] -= 2.0 * pi elif diff < -pi: for j in range(i,len(w)): w[j] += 2.0 * pi else: continue # calculate the group delay from: GroupDelay(n)=-[((ph(n+1)-ph(n-1))/(w(n+1)-w(n-1))]/2pi # w in radians # or gd[n] = - ((delta ph)/(delta w))/2pi # delta ph = w[n+1] - w[n-1] # delta w = pi* fs/steps # we divide up the frequency range fs into fractions of pi radians. # gd[n] = - steps(w[n+1] - w[n-1])/fs A = -(float(steps))/(2 *fs) # note. could have calculated this once outside the filter loop # gd[n] = A *(w[n+1] - w[n-1]) gd = [] # in radians gd.append(0.0) # pad to equalize size of u array, end points are then meaningless for n in range(1,len(w)-1): gd.append(A * (w[n+1] - w[n-1])) gd.append(0.0) plot(u,gd) axis([0.1,pi,-0.01,0.05]) show()