Source code for resampy.filters

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''Filter construction and loading.
--------------------------------

`resampy` provides two pre-computed resampling filters which are tuned for either
high-quality or fast calculation.  These filters are constructed by the `create_filters.py`
script.

    - `kaiser_best` :
        > Parameters for kaiser_best:
        >   ----------------------------------------
        >       beta        = 12.9846
        >       roll        = 0.917347
        >       # zeros     = 50
        >       precision   = 13
        >       attenuation = -120.0
        >   ----------------------------------------

    - `kaiser_fast` :
        > Parameters for kaiser_fast:
        > ----------------------------------------
        >     beta        = 9.90322
        >     roll        = 0.868212
        >     # zeros     = 24
        >     precision   = 9
        >     attenuation = -93.0
        > ----------------------------------------


These filters can be used by calling `resample` as follows:

    >>> # High-quality
    >>> resampy.resample(x, sr_orig, sr_new, filter='kaiser_best')  # doctest: +SKIP
    >>> # Fast calculation
    >>> resampy.resample(x, sr_orig, sr_new, filter='kaiser_fast')  # doctest: +SKIP


It is also possible to construct custom filters as follows:

    >>> resampy.resample(x, sr_orig, sr_new, filter='sinc_window',
    ...                  **kwargs)                                  # doctest: +SKIP

where ``**kwargs`` are additional parameters to `sinc_window`.

'''

import numpy as np
import os
import pkg_resources
import sys

FILTER_FUNCTIONS = ['sinc_window']
FILTER_CACHE = dict()

__all__ = ['get_filter', 'clear_cache'] + FILTER_FUNCTIONS


[docs]def sinc_window(num_zeros=64, precision=9, window=None, rolloff=0.945): '''Construct a windowed sinc interpolation filter Parameters ---------- num_zeros : int > 0 The number of zero-crossings to retain in the sinc filter precision : int > 0 The number of filter coefficients to retain for each zero-crossing window : callable The window function. By default, uses a Hann window. rolloff : float > 0 The roll-off frequency (as a fraction of nyquist) Returns ------- interp_window: np.ndarray [shape=(num_zeros * num_table + 1)] The interpolation window (right-hand side) num_bits: int The number of bits of precision to use in the filter table rolloff : float > 0 The roll-off frequency of the filter, as a fraction of Nyquist Raises ------ TypeError if `window` is not callable or `None` ValueError if `num_zeros < 1`, `precision < 1`, or `rolloff` is outside the range `(0, 1]`. Examples -------- >>> import scipy, scipy.signal >>> import resampy >>> np.set_printoptions(threshold=5, suppress=False) >>> # A filter with 10 zero-crossings, 32 samples per crossing, and a >>> # Hann window for tapering. >>> halfwin, prec, rolloff = resampy.filters.sinc_window(num_zeros=10, precision=5, ... window=scipy.signal.hann) >>> halfwin array([ 9.450e-01, 9.436e-01, ..., -7.455e-07, -0.000e+00]) >>> prec 32 >>> rolloff 0.945 >>> # Or using sinc-window filter construction directly in resample >>> y = resampy.resample(x, sr_orig, sr_new, filter='sinc_window', ... num_zeros=10, precision=5, ... window=scipy.signal.hann) # doctest: +SKIP ''' if window is None: window = np.hanning elif not callable(window): raise TypeError('window must be callable, not type(window)={}'.format(type(window))) if not 0 < rolloff <= 1: raise ValueError('Invalid roll-off: rolloff={}'.format(rolloff)) if num_zeros < 1: raise ValueError('Invalid num_zeros: num_zeros={}'.format(num_zeros)) if precision < 0: raise ValueError('Invalid precision: precision={}'.format(precision)) # Generate the right-wing of the sinc num_bits = 2**precision n = num_bits * num_zeros sinc_win = rolloff * np.sinc(rolloff * np.linspace(0, num_zeros, num=n + 1, endpoint=True)) # Build the window function and cut off the left half taper = window(2 * n + 1)[n:] interp_win = (taper * sinc_win) return interp_win, num_bits, rolloff
[docs]def get_filter(name_or_function, **kwargs): '''Retrieve a window given its name or function handle. Parameters ---------- name_or_function : str or callable If a function, returns `name_or_function(**kwargs)`. If a string, and it matches the name of one of the defined filter functions, the corresponding function is called with `**kwargs`. If a string, and it matches the name of a pre-computed filter, the corresponding filter is retrieved, and kwargs is ignored. Valid pre-computed filter names are: - 'kaiser_fast' - 'kaiser_best' **kwargs Additional keyword arguments passed to `name_or_function` (if callable) Returns ------- half_window : np.ndarray The right wing of the interpolation filter precision : int > 0 The number of samples between zero-crossings of the filter rolloff : float > 0 The roll-off frequency of the filter as a fraction of Nyquist Raises ------ NotImplementedError If `name_or_function` cannot be found as a filter. ''' if name_or_function in FILTER_FUNCTIONS: return getattr(sys.modules[__name__], name_or_function)(**kwargs) elif callable(name_or_function): return name_or_function(**kwargs) else: try: return load_filter(name_or_function) except (IOError, ValueError): raise NotImplementedError('Cannot load filter definition for ' '{}'.format(name_or_function))
def load_filter(filter_name): '''Retrieve a pre-computed filter. Parameters ---------- filter_name : str The key of the filter, e.g., 'kaiser_fast' Returns ------- half_window : np.ndarray The right wing of the interpolation filter precision : int > 0 The number of samples between zero-crossings of the filter rolloff : float > 0 The roll-off frequency of the filter, as a fraction of Nyquist ''' if filter_name not in FILTER_CACHE: fname = os.path.join('data', os.path.extsep.join([filter_name, 'npz'])) data = np.load(pkg_resources.resource_filename(__name__, fname)) FILTER_CACHE[filter_name] = data['half_window'], data['precision'], data['rolloff'] return FILTER_CACHE[filter_name]
[docs]def clear_cache(): '''Clear the filter cache. Calling this function will ensure that packaged filters are reloaded upon the next usage. ''' FILTER_CACHE.clear()