def rakeMVDRFilters(self, source, interferer, R_n, delay=0.03, epsilon=5e-3):
        '''
        Compute the time-domain filters of the minimum variance distortionless response beamformer.
        '''

        H = buildRIRMatrix(self.R, (source, interferer), self.Lg, self.Fs, epsilon=epsilon, unit_damping=True)
        L = H.shape[1]/2

        # the constraint vector
        kappa = int(delay*self.Fs)
        h = H[:,kappa]

        # We first assume the sample are uncorrelated
        R_xx = np.dot(H[:,:L], H[:,:L].T)
        K_nq = np.dot(H[:,L:], H[:,L:].T) + R_n

        # Compute the TD filters
        C = la.cho_factor(R_xx + K_nq, check_finite=False)
        g_val = la.cho_solve(C, h)

        g_val /= np.inner(h, g_val)
        self.filters = g_val.reshape((self.M,self.Lg))

        # compute and return SNR
        num = np.inner(g_val.T, np.dot(R_xx, g_val))
        denom =  np.inner(np.dot(g_val.T, K_nq), g_val)

        return num/denom
    def rakeDistortionlessFilters(self, source, interferer, R_n, delay=0.03, epsilon=5e-3):
        '''
        Compute time-domain filters of a beamformer minimizing noise and interference
        while forcing a distortionless response towards the source.
        '''

        H = buildRIRMatrix(self.R, (source, interferer), self.Lg, self.Fs, epsilon=epsilon, unit_damping=True)
        L = H.shape[1]/2

        # We first assume the sample are uncorrelated
        K_nq = np.dot(H[:,L:], H[:,L:].T) + R_n

        # constraint
        kappa = int(delay*self.Fs)
        A = H[:,:L]
        b = np.zeros((L,1))
        b[kappa,0] = 1

        # filter computation
        C = la.cho_factor(K_nq, overwrite_a=True, check_finite=False)
        B = la.cho_solve(C, A)
        D = np.dot(A.T, B)
        C = la.cho_factor(D, overwrite_a=True, check_finite=False)
        x = la.cho_solve(C, b)
        g_val = np.dot(B, x)

        # reshape and store
        self.filters = g_val.reshape((self.M, self.Lg))

        # compute and return SNR
        A = np.dot(g_val.T, H[:,:L])
        num = np.dot(A, A.T)
        denom =  np.dot(np.dot(g_val.T, K_nq), g_val)

        return num/denom
    def rakeMaxUDRFilters(self, source, interferer, R_n, delay=0.03, epsilon=5e-3):
        '''
        Compute directly the time-domain filters for a UDR maximizing beamformer.
        '''

        H = buildRIRMatrix(self.R, (source, interferer), self.Lg, self.Fs, epsilon=epsilon, unit_damping=True)
        L = H.shape[1]/2
            
        # Delay of the system in samples
        kappa = int(delay*self.Fs)
        precedence = int(0.030*self.Fs)

        # the constraint
        n = np.minimum(L, kappa+precedence)
        Hnc = H[:,:kappa]
        Hpr = H[:,kappa:n]
        Hc  = H[:,n:L]
        A = np.dot(Hpr, Hpr.T)
        B = np.dot(Hnc, Hnc.T) + np.dot(Hc, Hc.T) + np.dot(H[:,L:], H[:,L:].T) + R_n

        # solve the problem
        SINR, v = la.eigh(A, b=B, eigvals=(self.M*self.Lg-1, self.M*self.Lg-1), overwrite_a=True, overwrite_b=True, check_finite=False)
        g_val = np.real(v[:,0])

        # reshape and store
        self.filters = g_val.reshape((self.M, self.Lg))

        # compute and return SNR
        return SINR[0]
    def rakeMaxSINRFilters(self, source, interferer, R_n, epsilon=5e-3, delay=0.):
        '''
        Compute the time-domain filters of SINR maximizing beamformer.
        '''

        H = buildRIRMatrix(self.R, (source, interferer), self.Lg, self.Fs, epsilon=epsilon, unit_damping=True)
        L = H.shape[1]/2
        
        # We first assume the sample are uncorrelated
        K_s = np.dot(H[:,:L], H[:,:L].T)
        K_nq = np.dot(H[:,L:], H[:,L:].T) + R_n

        # Compute TD filters using generalized Rayleigh coefficient maximization
        SINR, v = la.eigh(K_s, b=K_nq, eigvals=(self.M*self.Lg-1, self.M*self.Lg-1), overwrite_a=True, overwrite_b=True, check_finite=False)
        g_val = np.real(v[:,0])

        self.filters = g_val.reshape((self.M, self.Lg))

        # compute and return SNR
        return SINR[0]
    def rakePerceptualFilters(self, source, interferer, R_n, delay=0.03, d_relax=0.035, epsilon=5e-3):
        '''
        Compute directly the time-domain filters for a perceptually motivated beamformer.
        The beamformer minimizes noise and interference, but relaxes the response of the
        filter within the 30 ms following the delay.
        '''

        # build the channel matrix
        H = buildRIRMatrix(self.R, (source, interferer), self.Lg, self.Fs, epsilon=epsilon, unit_damping=True)
        L = H.shape[1]/2
            
        # Delay of the system in samples
        tau = int(delay*self.Fs)
        kappa = int(d_relax*self.Fs)

        # the constraint
        A = np.concatenate((H[:,:tau+1], H[:,tau+kappa:]), axis=1)
        b = np.zeros((A.shape[1],1))
        b[tau,0] = 1

        # We first assume the sample are uncorrelated
        K_nq = np.dot(H[:,L:], H[:,L:].T) + R_n

        # causal response construction
        C = la.cho_factor(K_nq, overwrite_a=True, check_finite=False)
        B = la.cho_solve(C, A)
        D = np.dot(A.T, B)
        C = la.cho_factor(D, overwrite_a=True, check_finite=False)
        x = la.cho_solve(C, b)
        g_val = np.dot(B, x)

        # reshape and store
        self.filters = g_val.reshape((self.M, self.Lg))

        # compute and return SNR
        A = np.dot(g_val.T, H[:,:L])
        num = np.dot(A, A.T)
        denom =  np.dot(np.dot(g_val.T, K_nq), g_val)

        return num/denom