Source code for pycochleagram.erbfilter

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import warnings
import numpy as np

from pycochleagram import utils


def _identity(x):
  """Identity function. Return x.
  """
  return x


[docs]def freq2lin(freq_hz): """Compatibility hack to allow for linearly spaced cosine filters with `make_erb_cos_filters_nx`; intended to generalize the functionality of `make_lin_cos_filters`. """ return _identity(freq_hz)
[docs]def lin2freq(n_lin): """Compatibility hack to allow for linearly spaced cosine filters with `make_erb_cos_filters_nx`; intended to generalize the functionality of `make_lin_cos_filters`. """ return _identity(n_lin)
[docs]def freq2erb(freq_hz): """Converts Hz to human-defined ERBs, using the formula of Glasberg and Moore. Args: freq_hz (array_like): frequency to use for ERB. Returns: ndarray: **n_erb** -- Human-defined ERB representation of input. """ return 9.265 * np.log(1 + freq_hz / (24.7 * 9.265))
[docs]def erb2freq(n_erb): """Converts human ERBs to Hz, using the formula of Glasberg and Moore. Args: n_erb (array_like): Human-defined ERB to convert to frequency. Returns: ndarray: **freq_hz** -- Frequency representation of input. """ return 24.7 * 9.265 * (np.exp(n_erb / 9.265) - 1)
[docs]def make_cosine_filter(freqs, l, h, convert_to_erb=True): """Generate a half-cosine filter. Represents one subband of the cochleagram. A half-cosine filter is created using the values of freqs that are within the interval [l, h]. The half-cosine filter is centered at the center of this interval, i.e., (h - l) / 2. Values outside the valid interval [l, h] are discarded. So, if freqs = [1, 2, 3, ... 10], l = 4.5, h = 8, the cosine filter will only be defined on the domain [5, 6, 7] and the returned output will only contain 3 elements. Args: freqs (array_like): Array containing the domain of the filter, in ERB space; see convert_to_erb parameter below.. A single half-cosine filter will be defined only on the valid section of these values; specifically, the values between cutoffs ``l`` and ``h``. A half-cosine filter centered at (h - l ) / 2 is created on the interval [l, h]. l (float): The lower cutoff of the half-cosine filter in ERB space; see convert_to_erb parameter below. h (float): The upper cutoff of the half-cosine filter in ERB space; see convert_to_erb parameter below. convert_to_erb (bool, default=True): If this is True, the values in input arguments ``freqs``, ``l``, and ``h`` will be transformed from Hz to ERB space before creating the half-cosine filter. If this is False, the input arguments are assumed to be in ERB space. Returns: ndarray: **half_cos_filter** -- A half-cosine filter defined using elements of freqs within [l, h]. """ if convert_to_erb: freqs_erb = freq2erb(freqs) l_erb = freq2erb(l) h_erb = freq2erb(h) else: freqs_erb = freqs l_erb = l h_erb = h avg_in_erb = (l_erb + h_erb) / 2 # center of filter rnge_in_erb = h_erb - l_erb # width of filter # return np.cos((freq2erb(freqs[a_l_ind:a_h_ind+1]) - avg)/rnge * np.pi) # h_ind+1 to include endpoint # return np.cos((freqs_erb[(freqs_erb >= l_erb) & (freqs_erb <= h_erb)]- avg_in_erb) / rnge_in_erb * np.pi) # map cutoffs to -pi/2, pi/2 interval return np.cos((freqs_erb[(freqs_erb > l_erb) & (freqs_erb < h_erb)]- avg_in_erb) / rnge_in_erb * np.pi) # map cutoffs to -pi/2, pi/2 interval
[docs]def make_full_filter_set(filts, signal_length=None): """Create the full set of filters by extending the filterbank to negative FFT frequencies. Args: filts (array_like): Array containing the cochlear filterbank in frequency space, i.e., the output of make_erb_cos_filters_nx. Each row of ``filts`` is a single filter, with columns indexing frequency. signal_length (int, optional): Length of the signal to be filtered with this filterbank. This should be equal to filter length * 2 - 1, i.e., 2*filts.shape[1] - 1, and if signal_length is None, this value will be computed with the above formula. This parameter might be deprecated later. Returns: ndarray: **full_filter_set** -- Array containing the complete filterbank in frequency space. This output can be directly applied to the frequency representation of a signal. """ if signal_length is None: signal_length = 2 * filts.shape[1] - 1 # note that filters are currently such that each ROW is a filter and COLUMN idxs freq if np.remainder(signal_length, 2) == 0: # even -- don't take the DC & don't double sample nyquist neg_filts = np.flipud(filts[1:filts.shape[0] - 1, :]) else: # odd -- don't take the DC neg_filts = np.flipud(filts[1:filts.shape[0], :]) fft_filts = np.vstack((filts, neg_filts)) # we need to switch representation to apply filters to fft of the signal, not sure why, but do it here return fft_filts.T
[docs]def make_ref_cos_filters_nx(signal_length, sr, n, low_lim, hi_lim, sample_factor, padding_size=None, full_filter=True, strict=True, ref_spacing_mode='erb', **kwargs): """Create ERB cosine filters, oversampled by a factor provided by "sample_factor" Args: signal_length (int): Length of signal to be filtered with the generated filterbank. The signal length determines the length of the filters. sr (int): Sampling rate associated with the signal waveform. n (int): Number of filters (subbands) to be generated with standard sampling (i.e., using a sampling factor of 1). Note, the actual number of filters in the generated filterbank depends on the sampling factor, and will also include lowpass and highpass filters that allow for perfect reconstruction of the input signal (the exact number of lowpass and highpass filters is determined by the sampling factor). The number of filters in the generated filterbank is given below: +---------------+---------------+-+------------+---+---------------------+ | sample factor | n_out |=| bandpass |\ +| highpass + lowpass | +===============+===============+=+============+===+=====================+ | 1 | n+2 |=| n |\ +| 1 + 1 | +---------------+---------------+-+------------+---+---------------------+ | 2 | 2*n+1+4 |=| 2*n+1 |\ +| 2 + 2 | +---------------+---------------+-+------------+---+---------------------+ | 4 | 4*n+3+8 |=| 4*n+3 |\ +| 4 + 4 | +---------------+---------------+-+------------+---+---------------------+ | s | s*(n+1)-1+2*s |=| s*(n+1)-1 |\ +| s + s | +---------------+---------------+-+------------+---+---------------------+ low_lim (int): Lower limit of frequency range. Filters will not be defined below this limit. hi_lim (int): Upper limit of frequency range. Filters will not be defined above this limit. sample_factor (int): Positive integer that determines how densely ERB function will be sampled to create bandpass filters. 1 represents standard sampling; adjacent bandpass filters will overlap by 50%. 2 represents 2x overcomplete sampling; adjacent bandpass filters will overlap by 75%. 4 represents 4x overcomplete sampling; adjacent bandpass filters will overlap by 87.5%. padding_size (int, optional): If None (default), the signal will not be padded before filtering. Otherwise, the filters will be created assuming the waveform signal will be padded to length padding_size*signal_length. full_filter (bool, default=True): If True (default), the complete filter that is ready to apply to the signal is returned. If False, only the first half of the filter is returned (likely positive terms of FFT). strict (bool, default=True): If True (default), will throw an error if sample_factor is not a power of two. This facilitates comparison across sample_factors. Also, if True, will throw an error if provided hi_lim is greater than the Nyquist rate. Returns: tuple: A tuple containing the output: * **filts** (*array*)-- The filterbank consisting of filters have cosine-shaped frequency responses, with center frequencies equally spaced on an ERB scale from low_lim to hi_lim. * **center_freqs** (*array*) -- something * **freqs** (*array*) -- something Raises: ValueError: Various value errors for bad choices of sample_factor; see description for strict parameter. """ ref_spacing_mode = ref_spacing_mode.lower() if ref_spacing_mode == 'erb': _freq2ref = freq2erb _ref2freq = erb2freq elif ref_spacing_mode == 'lin' or 'linear': _freq2ref = freq2lin _ref2freq = lin2freq # elif callable(spacing_mode) # need fx and inv_fx for this else: raise NotImplementedError('unrecognized spacing mode: %s' % ref_spacing_mode) if not isinstance(sample_factor, int): raise ValueError('sample_factor must be an integer, not %s' % type(sample_factor)) if sample_factor <= 0: raise ValueError('sample_factor must be positive') if sample_factor != 1 and np.remainder(sample_factor, 2) != 0: msg = 'sample_factor odd, and will change ERB filter widths. Use even sample factors for comparison.' if strict: raise ValueError(msg) else: warnings.warn(msg, RuntimeWarning, stacklevel=2) if padding_size is not None and padding_size >= 1: signal_length += padding_size if np.remainder(signal_length, 2) == 0: # even length n_freqs = signal_length // 2 # .0 does not include DC, likely the sampling grid max_freq = sr / 2 # go all the way to nyquist else: # odd length n_freqs = (signal_length - 1) // 2 # .0 max_freq = sr * (signal_length - 1) / 2 / signal_length # just under nyquist # verify the high limit is allowed by the sampling rate if hi_lim > sr / 2: hi_lim = max_freq msg = 'input arg "hi_lim" exceeds nyquist limit for max frequency; ignore with "strict=False"' if strict: raise ValueError(msg) else: warnings.warn(msg, RuntimeWarning, stacklevel=2) # changing the sampling density without changing the filter locations # (and, thereby changing their widths) requires that a certain number of filters # be used. n_filters = sample_factor * (n + 1) - 1 n_lp_hp = 2 * sample_factor freqs = utils.matlab_arange(0, max_freq, n_freqs) filts = np.zeros((n_freqs + 1 , n_filters + n_lp_hp)) # ?? n_freqs+1 # cutoffs are evenly spaced on an "ref-spaced" scale -- interpolate linearly in "ref-space" then convert back # get the actual spacing use to generate the sequence (in case numpy does something weird) center_freqs, erb_spacing = np.linspace(_freq2ref(low_lim), _freq2ref(hi_lim), n_filters + 2, retstep=True) # +2 for bin endpoints # we need to exclude the endpoints center_freqs = center_freqs[1:-1] freqs_erb = _freq2ref(freqs) for i in range(n_filters): i_offset = i + sample_factor l = center_freqs[i] - sample_factor * erb_spacing h = center_freqs[i] + sample_factor * erb_spacing # the first sample_factor # of rows in filts will be lowpass filters filts[(freqs_erb > l) & (freqs_erb < h), i_offset] = make_cosine_filter(freqs_erb, l, h, convert_to_erb=False) # not converting to ERB/ref means we can use freq2lin (and arbitrary "ref-spaced" transforms) # be sample_factor number of each for i in range(sample_factor): # account for the fact that the first sample_factor # of filts are lowpass i_offset = i + sample_factor lp_h_ind = max(np.where(freqs < _ref2freq(center_freqs[i]))[0]) # lowpass filter goes up to peak of first cos filter lp_filt = np.sqrt(1 - np.power(filts[:lp_h_ind+1, i_offset], 2)) hp_l_ind = min(np.where(freqs > _ref2freq(center_freqs[-1-i]))[0]) # highpass filter goes down to peak of last cos filter hp_filt = np.sqrt(1 - np.power(filts[hp_l_ind:, -1-i_offset], 2)) filts[:lp_h_ind+1, i] = lp_filt filts[hp_l_ind:, -1-i] = hp_filt # ensure that squared freq response adds to one filts = filts / np.sqrt(sample_factor) # get center freqs for lowpass and highpass filters cfs_low = np.copy(center_freqs[:sample_factor]) - sample_factor * erb_spacing cfs_hi = np.copy(center_freqs[-sample_factor:]) + sample_factor * erb_spacing center_freqs = _ref2freq(np.concatenate((cfs_low, center_freqs, cfs_hi))) # rectify center_freqs[center_freqs < 0] = 1 # discard highpass and lowpass filters, if requested if kwargs.get('no_lowpass'): filts = filts[:, sample_factor:] if kwargs.get('no_highpass'): filts = filts[:, :-sample_factor] # make the full filter by adding negative components if full_filter: filts = make_full_filter_set(filts, signal_length) return filts, center_freqs, freqs
[docs]def make_erb_cos_filters_nx(signal_length, sr, n, low_lim, hi_lim, sample_factor, padding_size=None, full_filter=True, strict=True, **kwargs): """Create ERB cosine filters, oversampled by a factor provided by "sample_factor" Args: signal_length (int): Length of signal to be filtered with the generated filterbank. The signal length determines the length of the filters. sr (int): Sampling rate associated with the signal waveform. n (int): Number of filters (subbands) to be generated with standard sampling (i.e., using a sampling factor of 1). Note, the actual number of filters in the generated filterbank depends on the sampling factor, and will also include lowpass and highpass filters that allow for perfect reconstruction of the input signal (the exact number of lowpass and highpass filters is determined by the sampling factor). The number of filters in the generated filterbank is given below: +---------------+---------------+-+------------+---+---------------------+ | sample factor | n_out |=| bandpass |\ +| highpass + lowpass | +===============+===============+=+============+===+=====================+ | 1 | n+2 |=| n |\ +| 1 + 1 | +---------------+---------------+-+------------+---+---------------------+ | 2 | 2*n+1+4 |=| 2*n+1 |\ +| 2 + 2 | +---------------+---------------+-+------------+---+---------------------+ | 4 | 4*n+3+8 |=| 4*n+3 |\ +| 4 + 4 | +---------------+---------------+-+------------+---+---------------------+ | s | s*(n+1)-1+2*s |=| s*(n+1)-1 |\ +| s + s | +---------------+---------------+-+------------+---+---------------------+ low_lim (int): Lower limit of frequency range. Filters will not be defined below this limit. hi_lim (int): Upper limit of frequency range. Filters will not be defined above this limit. sample_factor (int): Positive integer that determines how densely ERB function will be sampled to create bandpass filters. 1 represents standard sampling; adjacent bandpass filters will overlap by 50%. 2 represents 2x overcomplete sampling; adjacent bandpass filters will overlap by 75%. 4 represents 4x overcomplete sampling; adjacent bandpass filters will overlap by 87.5%. padding_size (int, optional): If None (default), the signal will not be padded before filtering. Otherwise, the filters will be created assuming the waveform signal will be padded to length padding_size*signal_length. full_filter (bool, default=True): If True (default), the complete filter that is ready to apply to the signal is returned. If False, only the first half of the filter is returned (likely positive terms of FFT). strict (bool, default=True): If True (default), will throw an error if sample_factor is not a power of two. This facilitates comparison across sample_factors. Also, if True, will throw an error if provided hi_lim is greater than the Nyquist rate. Returns: tuple: A tuple containing the output: * **filts** (*array*)-- The filterbank consisting of filters have cosine-shaped frequency responses, with center frequencies equally spaced on an ERB scale from low_lim to hi_lim. * **center_freqs** (*array*) -- something * **freqs** (*array*) -- something Raises: ValueError: Various value errors for bad choices of sample_factor; see description for strict parameter. """ if not isinstance(sample_factor, int): raise ValueError('sample_factor must be an integer, not %s' % type(sample_factor)) if sample_factor <= 0: raise ValueError('sample_factor must be positive') if sample_factor != 1 and np.remainder(sample_factor, 2) != 0: msg = 'sample_factor odd, and will change ERB filter widths. Use even sample factors for comparison.' if strict: raise ValueError(msg) else: warnings.warn(msg, RuntimeWarning, stacklevel=2) if padding_size is not None and padding_size >= 1: signal_length += padding_size if np.remainder(signal_length, 2) == 0: # even length n_freqs = signal_length // 2 # .0 does not include DC, likely the sampling grid max_freq = sr / 2 # go all the way to nyquist else: # odd length n_freqs = (signal_length - 1) // 2 # .0 max_freq = sr * (signal_length - 1) / 2 / signal_length # just under nyquist # verify the high limit is allowed by the sampling rate if hi_lim > sr / 2: hi_lim = max_freq msg = 'input arg "hi_lim" exceeds nyquist limit for max frequency; ignore with "strict=False"' if strict: raise ValueError(msg) else: warnings.warn(msg, RuntimeWarning, stacklevel=2) # changing the sampling density without changing the filter locations # (and, thereby changing their widths) requires that a certain number of filters # be used. n_filters = sample_factor * (n + 1) - 1 n_lp_hp = 2 * sample_factor freqs = utils.matlab_arange(0, max_freq, n_freqs) filts = np.zeros((n_freqs + 1 , n_filters + n_lp_hp)) # ?? n_freqs+1 # cutoffs are evenly spaced on an erb scale -- interpolate linearly in erb space then convert back # get the actual spacing use to generate the sequence (in case numpy does something weird) center_freqs, erb_spacing = np.linspace(freq2erb(low_lim), freq2erb(hi_lim), n_filters + 2, retstep=True) # +2 for bin endpoints # we need to exclude the endpoints center_freqs = center_freqs[1:-1] freqs_erb = freq2erb(freqs) for i in range(n_filters): i_offset = i + sample_factor l = center_freqs[i] - sample_factor * erb_spacing h = center_freqs[i] + sample_factor * erb_spacing # the first sample_factor # of rows in filts will be lowpass filters filts[(freqs_erb > l) & (freqs_erb < h), i_offset] = make_cosine_filter(freqs_erb, l, h, convert_to_erb=False) # be sample_factor number of each for i in range(sample_factor): # account for the fact that the first sample_factor # of filts are lowpass i_offset = i + sample_factor lp_h_ind = max(np.where(freqs < erb2freq(center_freqs[i]))[0]) # lowpass filter goes up to peak of first cos filter lp_filt = np.sqrt(1 - np.power(filts[:lp_h_ind+1, i_offset], 2)) hp_l_ind = min(np.where(freqs > erb2freq(center_freqs[-1-i]))[0]) # highpass filter goes down to peak of last cos filter hp_filt = np.sqrt(1 - np.power(filts[hp_l_ind:, -1-i_offset], 2)) filts[:lp_h_ind+1, i] = lp_filt filts[hp_l_ind:, -1-i] = hp_filt # ensure that squared freq response adds to one filts = filts / np.sqrt(sample_factor) # get center freqs for lowpass and highpass filters cfs_low = np.copy(center_freqs[:sample_factor]) - sample_factor * erb_spacing cfs_hi = np.copy(center_freqs[-sample_factor:]) + sample_factor * erb_spacing center_freqs = erb2freq(np.concatenate((cfs_low, center_freqs, cfs_hi))) # rectify center_freqs[center_freqs < 0] = 1 # discard highpass and lowpass filters, if requested if kwargs.get('no_lowpass'): filts = filts[:, sample_factor:] if kwargs.get('no_highpass'): filts = filts[:, :-sample_factor] # make the full filter by adding negative components if full_filter: filts = make_full_filter_set(filts, signal_length) return filts, center_freqs, freqs
[docs]def make_erb_cos_filters_1x(signal_length, sr, n, low_lim, hi_lim, padding_size=None, full_filter=False, strict=False): """Create ERB cosine filterbank, sampled from ERB at 1x overcomplete. Returns n+2 filters as ??column vector filters have cosine-shaped frequency responses, with center frequencies equally spaced on an ERB scale from low_lim to hi_lim Adjacent filters overlap by 50%. The squared frequency responses of the filters sums to 1, so that they can be applied once to generate subbands and then again to collapse the subbands to generate a sound signal, without changing the frequency content of the signal. intended for use with GENERATE_SUBBANDS and COLLAPSE_SUBBANDS Args: signal_length (int): Length of input signal. Filters are to be applied multiplicatively in the frequency domain and thus have a length that scales with the signal length (signal_length). sr (int): is the sampling rate n (int): number of filters to create low_lim (int): low cutoff of lowest band hi_lim (int): high cutoff of highest band padding_size (int, optional): If None (default), the signal will not be padded before filtering. Otherwise, the filters will be created assuming the waveform signal will be padded to length padding_size*signal_length. full_filter (bool, optional): If True, the complete filter that is ready to apply to the signal is returned. If False (default), only the first half of the filter is returned (likely positive terms of FFT). strict (bool, optional): If True (default), will throw an error if provided hi_lim is greater than the Nyquist rate. Returns: tuple: **filts** (*array*): There are n+2 filters because filts also contains lowpass and highpass filters to cover the ends of the spectrum. **hz_cutoffs** (*array*): is a vector of the cutoff frequencies of each filter. Because of the overlap arrangement, the upper cutoff of one filter is the center frequency of its neighbor. **freqs** (*array*): is a vector of frequencies the same length as filts, that can be used to plot the frequency response of the filters. """ return make_erb_cos_filters_nx(signal_length, sr, n, low_lim, hi_lim, 1, padding_size=padding_size, full_filter=full_filter, strict=strict)
[docs]def make_erb_cos_filters_2x(signal_length, sr, n, low_lim, hi_lim, padding_size=None, full_filter=False, strict=False): """Create ERB cosine filterbank, sampled from ERB at 2x overcomplete. Returns 2*n+5 filters as column vectors filters have cosine-shaped frequency responses, with center frequencies equally spaced on an ERB scale from low_lim to hi_lim This function returns a filterbank that is 2x overcomplete compared to make_erb_cos_filts_1x (to get filterbanks that can be compared with each other, use the same value of n in both cases). Adjacent filters overlap by 75%. The squared frequency responses of the filters sums to 1, so that they can be applied once to generate subbands and then again to collapse the subbands to generate a sound signal, without changing the frequency content of the signal. intended for use with GENERATE_SUBBANDS and COLLAPSE_SUBBANDS Args: signal_length (int): Length of input signal. Filters are to be applied multiplicatively in the frequency domain and thus have a length that scales with the signal length (signal_length). sr (int): the sampling rate n (int): number of filters to create low_lim (int): low cutoff of lowest band hi_lim (int): high cutoff of highest band padding_size (int, optional): If None (default), the signal will not be padded before filtering. Otherwise, the filters will be created assuming the waveform signal will be padded to length padding_size*signal_length. full_filter (bool, optional): If True, the complete filter that is ready to apply to the signal is returned. If False (default), only the first half of the filter is returned (likely positive terms of FFT). strict (bool, optional): If True, will throw an error if provided hi_lim is greater than the Nyquist rate. Returns: tuple: tuple containing: **filts** (*array*): There are 2*n+5 filters because filts also contains lowpass and highpass filters to cover the ends of the spectrum and sampling is 2x overcomplete. **hz_cutoffs** (*array*): is a vector of the cutoff frequencies of each filter. Because of the overlap arrangement, the upper cutoff of one filter is the center frequency of its neighbor. **freqs** (*array*): is a vector of frequencies the same length as filts, that can be used to plot the frequency response of the filters. """ return make_erb_cos_filters_nx(signal_length, sr, n, low_lim, hi_lim, 2, padding_size=padding_size, full_filter=full_filter, strict=strict)
[docs]def make_erb_cos_filters_4x(signal_length, sr, n, low_lim, hi_lim, padding_size=None, full_filter=False, strict=False): """Create ERB cosine filterbank, sampled from ERB at 4x overcomplete. Returns 4*n+11 filters as column vectors filters have cosine-shaped frequency responses, with center frequencies equally spaced on an ERB scale from low_lim to hi_lim This function returns a filterbank that is 4x overcomplete compared to MAKE_ERB_COS_FILTS (to get filterbanks that can be compared with each other, use the same value of n in both cases). Adjacent filters overlap by 87.5%. The squared frequency responses of the filters sums to 1, so that they can be applied once to generate subbands and then again to collapse the subbands to generate a sound signal, without changing the frequency content of the signal. intended for use with GENERATE_SUBBANDS and COLLAPSE_SUBBANDS Args: signal_length (int): Length of input signal. Filters are to be applied multiplicatively in the frequency domain and thus have a length that scales with the signal length (signal_length). sr (int): the sampling rate n (int): number of filters to create low_lim (int): low cutoff of lowest band hi_lim (int): high cutoff of highest band padding_size (int, optional): If None (default), the signal will not be padded before filtering. Otherwise, the filters will be created assuming the waveform signal will be padded to length padding_size*signal_length. full_filter (bool, optional): If True, the complete filter that is ready to apply to the signal is returned. If False (default), only the first half of the filter is returned (likely positive terms of FFT). strict (bool, optional): If True, will throw an error if provided hi_lim is greater than the Nyquist rate. Returns: tuple: **filts** (*array*): There are 4*n+11 filters because filts also contains lowpass and highpass filters to cover the ends of the spectrum and sampling is 4x overcomplete. **hz_cutoffs** (*array*): is a vector of the cutoff frequencies of each filter. Because of the overlap arrangement, the upper cutoff of one filter is the center frequency of its neighbor. **freqs** (*array*): is a vector of frequencies the same length as filts, that can be used to plot the frequency response of the filters. """ return make_erb_cos_filters_nx(signal_length, sr, n, low_lim, hi_lim, 4, padding_size=padding_size, full_filter=full_filter, strict=strict)
[docs]def make_erb_cos_filters(signal_length, sr, n, low_lim, hi_lim, full_filter=False, strict=False): """Fairly literal port of Josh McDermott's MATLAB make_erb_cos_filters. Useful for debugging, but isn't very generalizable. Use make_erb_cos_filters_1x or make_erb_cos_filters_nx with sample_factor=1 instead. Returns n+2 filters as ??column vectors of FILTS filters have cosine-shaped frequency responses, with center frequencies equally spaced on an ERB scale from low_lim to hi_lim Adjacent filters overlap by 50%. The squared frequency responses of the filters sums to 1, so that they can be applied once to generate subbands and then again to collapse the subbands to generate a sound signal, without changing the frequency content of the signal. intended for use with GENERATE_SUBBANDS and COLLAPSE_SUBBANDS Args: signal_length (int): Length of input signal. Filters are to be applied multiplicatively in the frequency domain and thus have a length that scales with the signal length (signal_length). sr (int): is the sampling rate n (int): number of filters to create low_lim (int): low cutoff of lowest band hi_lim (int): high cutoff of highest band Returns: tuple: **filts** (*array*): There are n+2 filters because filts also contains lowpass and highpass filters to cover the ends of the spectrum. **hz_cutoffs** (*array*): is a vector of the cutoff frequencies of each filter. Because of the overlap arrangement, the upper cutoff of one filter is the center frequency of its neighbor. **freqs** (*array*): is a vector of frequencies the same length as filts, that can be used to plot the frequency response of the filters. """ if np.remainder(signal_length, 2) == 0: # even length n_freqs = signal_length / 2 # .0 does not include DC, likely the sampling grid max_freq = sr / 2 # go all the way to nyquist else: # odd length n_freqs = (signal_length - 1) / 2 # .0 max_freq = sr * (signal_length - 1) / 2 / signal_length # just under nyquist freqs = utils.matlab_arange(0, max_freq, n_freqs) cos_filts = np.zeros((n_freqs + 1, n)) # ?? n_freqs+1 a_cos_filts = np.zeros((n_freqs+1, n)) # ?? n_freqs+1 if hi_lim > sr / 2: hi_lim = max_freq if strict: raise ValueError('input arg "hi_lim" exceeds nyquist limit for max ' 'frequency ignore with "strict=False"') # cutoffs are evenly spaced on an erb scale -- interpolate linearly in erb space then convert back cutoffs_in_erb = utils.matlab_arange(freq2erb(low_lim), freq2erb(hi_lim), n + 1) # ?? n+1 cutoffs = erb2freq(cutoffs_in_erb) # generate cosine filters for k in range(n): l = cutoffs[k] h = cutoffs[k + 2] # adjacent filters overlap by 50% l_ind = min(np.where(freqs > l)[0]) h_ind = max(np.where(freqs < h)[0]) avg = (freq2erb(l) + freq2erb(h)) / 2 # center of filter rnge = freq2erb(h) - freq2erb(l) # width of filter cos_filts[l_ind:h_ind+1,k] = np.cos((freq2erb(freqs[l_ind:h_ind+1]) - avg)/rnge * np.pi) # h_ind+1 to include endpoint # add lowpass and highpass for perfect reconstruction filts = np.zeros((n_freqs + 1, n + 2)) filts[:,1:n+1] = cos_filts lp_filt = np.zeros_like(cos_filts[:, :0]) hp_filt = np.copy(lp_filt) # add lowpass and highpass for perfect reconstruction filts = np.zeros((n_freqs+1,n+2)) filts[:,1:n+1] = cos_filts h_ind = max(np.where(freqs < cutoffs[1])[0]) # lowpass filter goes up to peak of first cos filter filts[:h_ind+1,0] = np.sqrt( 1 - filts[:h_ind+1,1]**2) l_ind = min(np.where(freqs > cutoffs[n])[0]) # lowpass filter goes up to peak of first cos filter filts[l_ind:n_freqs+2,n+1] = np.sqrt(1.0 - filts[l_ind:n_freqs+2,n]**2.0) # make the full filter by adding negative components if full_filter: filts = make_full_filter_set(filts, signal_length) return filts, cutoffs, freqs
[docs]def make_lin_cos_filters(signal_length, sr, n, low_lim, hi_lim, full_filter=False, strict=False): """Fairly literal port of Josh McDermott's MATLAB make_lin_cos_filters. Useful for debugging, but isn't very generalizable. Use make_erb_cos_filters_1x or make_erb_cos_filters_nx with sample_factor=1 instead. Returns n+2 filters as ??column vectors of FILTS filters have cosine-shaped frequency responses, with center frequencies equally spaced on an ERB scale from low_lim to hi_lim Adjacent filters overlap by 50%. The squared frequency responses of the filters sums to 1, so that they can be applied once to generate subbands and then again to collapse the subbands to generate a sound signal, without changing the frequency content of the signal. intended for use with GENERATE_SUBBANDS and COLLAPSE_SUBBANDS Args: signal_length (int): Length of input signal. Filters are to be applied multiplicatively in the frequency domain and thus have a length that scales with the signal length (signal_length). sr (int): is the sampling rate n (int): number of filters to create low_lim (int): low cutoff of lowest band hi_lim (int): high cutoff of highest band Returns: tuple: **filts** (*array*): There are n+2 filters because filts also contains lowpass and highpass filters to cover the ends of the spectrum. **hz_cutoffs** (*array*): is a vector of the cutoff frequencies of each filter. Because of the overlap arrangement, the upper cutoff of one filter is the center frequency of its neighbor. **freqs** (*array*): is a vector of frequencies the same length as filts, that can be used to plot the frequency response of the filters. """ if np.remainder(signal_length, 2) == 0: # even length n_freqs = signal_length / 2 # .0 does not include DC, likely the sampling grid max_freq = sr / 2 # go all the way to nyquist else: # odd length n_freqs = (signal_length - 1) / 2 # .0 max_freq = sr * (signal_length - 1) / 2 / signal_length # just under nyquist freqs = utils.matlab_arange(0, max_freq, n_freqs) cos_filts = np.zeros((n_freqs + 1, n)) # ?? n_freqs+1 a_cos_filts = np.zeros((n_freqs+1, n)) # ?? n_freqs+1 if hi_lim > sr / 2: hi_lim = max_freq if strict: raise ValueError('input arg "hi_lim" exceeds nyquist limit for max ' 'frequency ignore with "strict=False"') # cutoffs are evenly spaced on an linear scale cutoffs = utils.matlab_arange(low_lim, hi_lim, n + 1) # ?? n+1 # generate cosine filters for k in range(n): l = cutoffs[k] h = cutoffs[k + 2] # adjacent filters overlap by 50% l_ind = min(np.where(freqs > l)[0]) h_ind = max(np.where(freqs < h)[0]) avg = (l + h) / 2 # center of filter rnge = h - l # width of filter cos_filts[l_ind:h_ind+1,k] = np.cos((freqs[l_ind:h_ind+1] - avg)/rnge * np.pi) # h_ind+1 to include endpoint # add lowpass and highpass for perfect reconstruction filts = np.zeros((n_freqs + 1, n + 2)) filts[:,1:n+1] = cos_filts lp_filt = np.zeros_like(cos_filts[:, :0]) hp_filt = np.copy(lp_filt) # add lowpass and highpass for perfect reconstruction filts = np.zeros((n_freqs+1,n+2)) filts[:,1:n+1] = cos_filts h_ind = max(np.where(freqs < cutoffs[1])[0]) # lowpass filter goes up to peak of first cos filter filts[:h_ind+1,0] = np.sqrt( 1 - filts[:h_ind+1,1]**2) l_ind = min(np.where(freqs > cutoffs[n])[0]) # lowpass filter goes up to peak of first cos filter filts[l_ind:n_freqs+2,n+1] = np.sqrt(1.0 - filts[l_ind:n_freqs+2,n]**2.0) # make the full filter by adding negative components if full_filter: filts = make_full_filter_set(filts, signal_length) return filts, cutoffs, freqs