Introduction¶
resampy is a python module for efficient time-series resampling. It is based on the band-limited sinc interpolation method for sampling rate conversion as described by 1.
- 1
Smith, Julius O. Digital Audio Resampling Home Page Center for Computer Research in Music and Acoustics (CCRMA), Stanford University, 2015-02-23. Web published at http://ccrma.stanford.edu/~jos/resample/.
resampy supports multi-dimensional resampling on numpy arrays, and is well-suited to audio applications. For long-duration signals — e.g., minutes at a high-quality sampling rate — resampy will be considerably faster than scipy.signal.resample and have little perceivable difference in audio quality.
Its dependencies are numpy, scipy, and numba.
For a quick introduction to using resampy, please refer to the Examples section.
Installation¶
resampy can be installed from source through pip:
pip install resampy
Conda users can install pre-compiled packages:
conda install -c conda-forge resampy
Advanced users and developers may wish to install from source by cloning the source repository:
git clone https://github.com/bmcfee/resampy.git
cd resampy
python setup.py build_ext -i
pip install -e .
Running tests¶
Developers that wish to run the included unit test suite can do so by installing from source, and then executing the following commands from the source directory:
pip install -e .[tests]
pip install pytest pytest-cov pytest-faulthandler
py.test --cov-report term-missing --cov resampy
Examples¶
Monophonic resampling¶
The following code block demonstrates how to resample an audio signal.
We use librosa for loading the audio, but this is purely for ease of demonstration. resampy does not depend on librosa.
1import librosa
2import resampy
3
4# Load in librosa's example audio file at its native sampling rate
5x, sr_orig = librosa.load(librosa.ex('trumpet'), sr=None)
6
7# x is now a 1-d numpy array, with `sr_orig` audio samples per second
8
9# We can resample this to any sampling rate we like, say 16000 Hz
10y_low = resampy.resample(x, sr_orig, 16000)
11
12# That's it!
Stereo and multi-dimensional data¶
The previous example operates on monophonic signals, but resampy also supports stereo resampling, as demonstrated below.
1import librosa
2import resampy
3
4# Load in librosa's example audio file at its native sampling rate.
5# This time, also disable the stereo->mono downmixing
6x, sr_orig = librosa.load(librosa.ex('trumpet', hq=True), sr=None, mono=False)
7
8# x is now a 2-d numpy array, with `sr_orig` audio samples per second
9# The first dimension of x indexes the channels, the second dimension indexes
10# samples.
11# x[0] is the left channel, x[1] is the right channel.
12
13# We can again resample. By default, resample assumes the last index is time.
14y_low = resampy.resample(x, sr_orig, 16000)
15
16# To be more explicit, provide a target axis
17y_low = resampy.resample(x, sr_orig, 16000, axis=1)
The next block illustrates resampling along an arbitrary dimension.
1import numpy as np
2import resampy
3
4# Generate 4-dimensional white noise. The third axis (axis=2) will index time.
5sr_orig = 22050
6x = np.random.randn(10, 3, sr_orig * 5, 2)
7
8# x is now a 10-by-3-by-(5*22050)-by-2 tensor of data.
9
10# We can resample along the time axis as follows
11y_low = resampy.resample(x, sr_orig, 11025, axis=2)
12
13# y_low is now a 10-by-3-(5*11025)-by-2 tensor of data
Advanced filtering¶
resampy allows you to control the design of the filters used in resampling operations.
1import numpy as np
2import scipy.signal
3import librosa
4import resampy
5
6# Load in some audio
7x, sr_orig = librosa.load(librosa.ex('trumpet'), sr=None, mono=False)
8
9# Resample to 22050Hz using a Hann-windowed sinc-filter
10y = resampy.resample(x, sr_orig, sr_new, filter='sinc_window', window=scipy.signal.hann)
11
12# Or a shorter sinc-filter than the default (num_zeros=64)
13y = resampy.resample(x, sr_orig, sr_new, filter='sinc_window', num_zeros=32)
14
15# Or use the pre-built high-quality filter
16y = resampy.resample(x, sr_orig, sr_new, filter='kaiser_best')
17
18# Or use the pre-built fast filter
19y = resampy.resample(x, sr_orig, sr_new, filter='kaiser_fast')
Benchmarking¶
Benchmarking resampy is relatively simple, using ipython’s %timeit
magic.
The following example demonstrates resampling a monophonic signal of 400000 samples from
22.05 KHz to 16 KHz using both resampy and scipy.signal.resample.
In [1]: import numpy as np
In [2]: import scipy
In [3]: import resampy
In [4]: x = np.random.randn(400000)
In [5]: sr_in, sr_out = 22050, 16000
In [6]: %timeit resampy.resample(x, sr_in, sr_out, axis=-1)
1 loop, best of 3: 199 ms per loop
In [7]: %timeit scipy.signal.resample(x,
...: int(x.shape[-1] * sr_out / float(sr_in)),
...: axis=-1)
1 loop, best of 3: 6min 5s per loop
API Reference¶
Core functionality¶
Core resampling interface
- resampy.core.resample(x, sr_orig, sr_new, axis=- 1, filter='kaiser_best', parallel=True, **kwargs)[source]¶
Resample a signal x from sr_orig to sr_new along a given axis.
- Parameters
- xnp.ndarray, dtype=np.float*
The input signal(s) to resample.
- sr_origint > 0
The sampling rate of x
- sr_newint > 0
The target sampling rate of the output signal(s)
If sr_new == sr_orig, then a copy of x is returned with no interpolation performed.
- axisint
The target axis along which to resample x
- filteroptional, str or callable
The resampling filter to use.
By default, uses the kaiser_best (pre-computed filter).
- paralleloptional, bool
Enable/disable parallel computation exploiting multi-threading.
Default: True.
- **kwargs
additional keyword arguments provided to the specified filter
- Returns
- ynp.ndarray
x resampled to sr_new
- Raises
- ValueError
if sr_orig or sr_new is not positive
- TypeError
if the input signal x has an unsupported data type.
Examples
>>> import resampy >>> np.set_printoptions(precision=3, suppress=True) >>> # Generate a sine wave at 440 Hz for 5 seconds >>> sr_orig = 44100.0 >>> x = np.sin(2 * np.pi * 440.0 / sr_orig * np.arange(5 * sr_orig)) >>> x array([ 0. , 0.063, ..., -0.125, -0.063]) >>> # Resample to 22050 with default parameters >>> resampy.resample(x, sr_orig, 22050) array([ 0.011, 0.122, 0.25 , ..., -0.366, -0.25 , -0.122]) >>> # Resample using the fast (low-quality) filter >>> resampy.resample(x, sr_orig, 22050, filter='kaiser_fast') array([ 0.012, 0.121, 0.251, ..., -0.365, -0.251, -0.121]) >>> # Resample using a high-quality filter >>> resampy.resample(x, sr_orig, 22050, filter='kaiser_best') array([ 0.011, 0.122, 0.25 , ..., -0.366, -0.25 , -0.122]) >>> # Resample using a Hann-windowed sinc filter >>> import scipy.signal >>> resampy.resample(x, sr_orig, 22050, filter='sinc_window', ... window=scipy.signal.hann) array([ 0.011, 0.123, 0.25 , ..., -0.366, -0.25 , -0.123])
>>> # Generate stereo data >>> x_right = np.sin(2 * np.pi * 880.0 / sr_orig * np.arange(len(x))) >>> x_stereo = np.stack([x, x_right]) >>> x_stereo.shape (2, 220500) >>> # Resample along the time axis (1) >>> y_stereo = resampy.resample(x_stereo, sr_orig, 22050, axis=1) >>> y_stereo.shape (2, 110250)
- resampy.core.resample_nu(x, sr_orig, t_out, axis=- 1, filter='kaiser_best', parallel=True, **kwargs)[source]¶
Interpolate a signal x at specified positions (t_out) along a given axis.
- Parameters
- xnp.ndarray, dtype=np.float*
The input signal(s) to resample.
- sr_origfloat
Sampling rate of the input signal (x).
- t_outnp.ndarray, dtype=np.float*
Position of the output samples.
- axisint
The target axis along which to resample x
- filteroptional, str or callable
The resampling filter to use.
By default, uses the kaiser_best (pre-computed filter).
- paralleloptional, bool
Enable/disable parallel computation exploiting multi-threading.
Default: True.
- **kwargs
additional keyword arguments provided to the specified filter
- Returns
- ynp.ndarray
x resampled to t_out
- Raises
- TypeError
if the input signal x has an unsupported data type.
Notes
Differently form the resample function the filter rolloff is not automatically adapted in case of subsampling. For this reason results obtained with the resample_nu could be slightly different form the ones obtained with resample if the filter parameters are not carefully set by the user.
Examples
>>> import resampy >>> np.set_printoptions(precision=3, suppress=True) >>> # Generate a sine wave at 100 Hz for 5 seconds >>> sr_orig = 100.0 >>> f0 = 1 >>> t = np.arange(5 * sr_orig) / sr_orig >>> x = np.sin(2 * np.pi * f0 * t) >>> x array([ 0. , 0.063, 0.125, ..., -0.187, -0.125, -0.063]) >>> # Resample to non-uniform sampling >>> t_new = np.log2(1 + t)[::5] - t[0] >>> resampy.resample_nu(x, sr_orig, t_new) array([ 0.001, 0.427, 0.76 , ..., -0.3 , -0.372, -0.442])
Filters¶
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')
>>> # Fast calculation
>>> resampy.resample(x, sr_orig, sr_new, filter='kaiser_fast')
It is also possible to construct custom filters as follows:
>>> resampy.resample(x, sr_orig, sr_new, filter='sinc_window',
... **kwargs)
where **kwargs
are additional parameters to sinc_window.
- resampy.filters.clear_cache()[source]¶
Clear the filter cache.
Calling this function will ensure that packaged filters are reloaded upon the next usage.
- resampy.filters.get_filter(name_or_function, **kwargs)[source]¶
Retrieve a window given its name or function handle.
- Parameters
- name_or_functionstr 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_windownp.ndarray
The right wing of the interpolation filter
- precisionint > 0
The number of samples between zero-crossings of the filter
- rollofffloat > 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.
- resampy.filters.sinc_window(num_zeros=64, precision=9, window=None, rolloff=0.945)[source]¶
Construct a windowed sinc interpolation filter
- Parameters
- num_zerosint > 0
The number of zero-crossings to retain in the sinc filter
- precisionint > 0
The number of filter coefficients to retain for each zero-crossing
- windowcallable
The window function. By default, uses a Hann window.
- rollofffloat > 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
- rollofffloat > 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)
Changes¶
Changes¶
v0.3.0¶
2022-06-29
#99 Enable caching of pre-computed filters to improve runtime efficiency.
#98 Automate pre-computed filter generation. Regenerated and improved kaiser_fast and kaiser_best filters.
#95 Improved documentation
#93 Enable parallel processing for sample rate conversion. Antonio Valentino
#91 Improved python packaging workflow.
#90 Fixed a bug in resampling high-dimensional data.
#89 Removed support for python 2.7.
#88 Bypass sample rate conversion if input and output rates are identical.
#87 Added continuous integration tests for linting.
#82 Non-uniform output sample positions. Antonio Valentio
v0.2.2¶
2019-08-15
#68 Preserve array ordering (C- or F-contiguity) from input to output.
v0.2.1¶
2018-06-04
#63 Fixed an error in filter response boundary calculations.
v0.2.0¶
2017-09-16
v0.1.5¶
2017-02-16
#44 Added type-checking to ensure floating-point inputs
v0.1.4¶
2016-07-13
#27 Fixed cython packaging
v0.1.3¶
2016-06-21
#23 updated the Cython version requirement.
v0.1.2¶
2016-05-26
#20 Expose the
rolloff
parameter of (pre-computed) filters
v0.1.1¶
2016-05-23
Fixed a cython installation and distribution issue
v0.1.0¶
2016-04-21
Initial release.