def process(self): if (self.signals is None or len(self.signals) == 0): raise NameError('No signal to beamform') if self.processing is 'FrequencyDomain': # create window function win = np.concatenate((np.zeros(self.zpf), windows.hann(self.L), np.zeros(self.zpb))) # do real STFT of first signal tfd_sig = stft.stft(self.signals[0], self.L, self.hop, zp_back=self.zpb, zp_front=self.zpf, transform=np.fft.rfft, win=win) * np.conj(self.weights[0]) for i in xrange(1, self.M): tfd_sig += stft.stft(self.signals[i], self.L, self.hop, zp_back=self.zpb, zp_front=self.zpf, transform=np.fft.rfft, win=win) * np.conj(self.weights[i]) # now reconstruct the signal output = stft.istft( tfd_sig, self.L, self.hop, zp_back=self.zpb, zp_front=self.zpf, transform=np.fft.irfft) # remove the zero padding from output signal if self.zpb is 0: output = output[self.zpf:] else: output = output[self.zpf:-self.zpb] elif self.processing is 'TimeDomain': # go back to time domain and shift DC to center tw = np.sqrt(self.weights.shape[1])*np.fft.irfft(np.conj(self.weights), axis=1) tw = np.concatenate((tw[:, self.N/2:], tw[:, :self.N/2]), axis=1) from scipy.signal import fftconvolve # do real STFT of first signal output = fftconvolve(tw[0], self.signals[0]) for i in xrange(1, len(self.signals)): output += fftconvolve(tw[i], self.signals[i]) elif self.processing is 'Total': W = np.concatenate((self.weights, np.conj(self.weights[:,-2:0:-1])), axis=1) W[:,0] = np.real(W[:,0]) W[:,self.N/2] = np.real(W[:,self.N/2]) F_sig = np.zeros(self.signals.shape[1], dtype=complex) for i in xrange(self.M): F_sig += np.fft.fft(self.signals[i])*np.conj(W[i,:]) f_sig = np.fft.ifft(F_sig) print np.abs(np.imag(f_sig)).mean() print np.abs(np.real(f_sig)).mean() output = np.real(np.fft.ifft(F_sig)) return output
def tf_agc(d, sr, t_scale=0.5, f_scale=1.0, causal_tracking=True, plot=False): """ Perform frequency-dependent automatic gain control on an auditory frequency axis. d is the input waveform (at sampling rate sr); y is the output waveform with approximately constant energy in each time-frequency patch. t_scale is the "scale" for smoothing in time (default 0.5 sec). f_scale is the frequency "scale" (default 1.0 "mel"). causal_tracking == 0 selects traditional infinite-attack, exponential release. causal_tracking == 1 selects symmetric, non-causal Gaussian-window smoothing. D returns actual STFT used in analysis. E returns the smoothed amplitude envelope divided out of D to get gain control. """ hop_size = 0.032 # in seconds # Make STFT on ~32 ms grid ftlen = int(2 ** np.round(np.log(hop_size * sr) / np.log(2.))) winlen = ftlen hoplen = winlen / 2 D = stft(d, winlen, hoplen) # using my code ftsr = sr / hoplen ndcols = D.shape[1] # Smooth in frequency on ~ mel resolution # Width of mel filters depends on how many you ask for, # so ask for fewer for larger f_scales nbands = max(10, 20 / f_scale) # 10 bands, or more for very fine f_scale mwidth = f_scale * nbands / 10 # will be 2.0 for small f_scale (f2a_tmp, _) = fft2melmx(ftlen, sr, int(nbands), mwidth) f2a = f2a_tmp[:, :ftlen / 2 + 1] audgram = np.dot(f2a, np.abs(D)) if causal_tracking: # traditional attack/decay smoothing fbg = np.zeros(audgram.shape) # state = zeros(size(audgram,1),1); state = np.zeros(audgram.shape[0]) alpha = np.exp(-(1. / ftsr) / t_scale) for i in range(audgram.shape[1]): state = np.maximum(alpha * state, audgram[:, i]) fbg[:, i] = state else: # noncausal, time-symmetric smoothing # Smooth in time with tapered window of duration ~ t_scale tsd = np.round(t_scale * ftsr) / 2 htlen = 6 * tsd # Go out to 6 sigma twin = np.exp(-0.5 * (((np.arange(-htlen, htlen + 1)) / tsd) ** 2)).T # reflect ends to get smooth stuff AD = audgram x = np.hstack((np.fliplr(AD[:, :htlen]), AD, np.fliplr(AD[:, -htlen:]), np.zeros((AD.shape[0], htlen)))) fbg = signal.lfilter(twin, 1, x, 1) # strip "warm up" points fbg = fbg[:, twin.size + np.arange(ndcols)] # map back to FFT grid, flatten bark loop gain sf2a = np.sum(f2a, 0) sf2a_fix = sf2a sf2a_fix[sf2a == 0] = 1. E = np.dot(np.dot(np.diag(1. / sf2a_fix), f2a.T), fbg) # Remove any zeros in E (shouldn't be any, but who knows?) E[E <= 0] = np.min(E[E > 0]) # invert back to waveform y = istft(D / E, winlen, hoplen, window=np.ones(winlen)) # using my code if plot: try: import matplotlib.pyplot as plt plt.subplot(3, 1, 1) plt.imshow(20. * np.log10(np.flipud(np.abs(D)))) plt.subplot(3, 1, 2) plt.imshow(20. * np.log10(np.flipud(np.abs(E)))) A = stft(y, winlen, hoplen) # using my code plt.subplot(3, 1, 3) plt.imshow(20. * np.log10(np.flipud(np.abs(A)))) plt.show() except Exception, e: print "Failed to plot results" print e
def process(self, FD=False): if self.signals is None or len(self.signals) == 0: raise NameError('No signal to beamform') if FD is True: # STFT processing if self.weights is None and self.filters is not None: self.weightsFromFilters() elif self.weights is None and self.filters is None: raise NameError('Beamforming weights or filters need to be computed first.') # create window function win = np.concatenate((np.zeros(self.zpf), windows.hann(self.L), np.zeros(self.zpb))) # do real STFT of first signal tfd_sig = stft.stft(self.signals[0], self.L, self.hop, zp_back=self.zpb, zp_front=self.zpf, transform=np.fft.rfft, win=win) * np.conj(self.weights[0]) for i in xrange(1, self.M): tfd_sig += stft.stft(self.signals[i], self.L, self.hop, zp_back=self.zpb, zp_front=self.zpf, transform=np.fft.rfft, win=win) * np.conj(self.weights[i]) # now reconstruct the signal output = stft.istft( tfd_sig, self.L, self.hop, zp_back=self.zpb, zp_front=self.zpf, transform=np.fft.irfft) # remove the zero padding from output signal if self.zpb is 0: output = output[self.zpf:] else: output = output[self.zpf:-self.zpb] else: # TD processing if self.weights is not None and self.filters is None: self.filtersFromWeights() elif self.weights is None and self.filters is None: raise NameError('Beamforming weights or filters need to be computed first.') from scipy.signal import fftconvolve # do real STFT of first signal output = fftconvolve(self.filters[0], self.signals[0]) for i in xrange(1, len(self.signals)): output += fftconvolve(self.filters[i], self.signals[i]) return output