Skip to content

Commit

Permalink
Merge pull request #109 from bmcfee/032-tweaks
Browse files Browse the repository at this point in the history
faster import, no int outputs
  • Loading branch information
bmcfee committed Aug 5, 2022
2 parents 29d3487 + 716a704 commit 2ca177f
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 97 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci-minimal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
shell: bash -l {0}
run: |
mamba install typing_extensions!=4.2 pytest
mamba install pip numpy==1.17 scipy==1.0 numba==0.47
mamba install pip numpy==1.17 scipy==1.0 numba==0.53
- name: Install
shell: bash -l {0}
Expand Down
7 changes: 7 additions & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Changes
-------

v0.4.0
~~~~~~
2022-08-05

- `#109 <https://github.com/bmcfee/resampy/pull/109>`_ Reduced import time and switched to parallel=False by default.
- `#109 <https://github.com/bmcfee/resampy/pull/109>`_ Integer-valued inputs now produce floating point outputs.

v0.3.1
~~~~~~
2022-07-07
Expand Down
50 changes: 25 additions & 25 deletions docs/example.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,31 @@ The next block illustrates resampling along an arbitrary dimension.
# y_low is now a 10-by-3-(5*11025)-by-2 tensor of data
Integer-valued samples
======================
Integer-valued inputs are supported, but because resampy interpolates between
sample values, it will always produce a floating-point output.
If you really need integer-valued outputs after resampling, you'll have to cast the
output array as demonstrated below.

.. code-block:: python
:linenos:
import numpy as np
import resampy
sr_orig = 22050
# Create 5 seconds of random integer noise
x = np.random.randint(-32768, high=32767, size=5*sr_orig, dtype=np.int16)
# resample, y will be floating-point type
y = resampy.resample(x, sr_orig, 11025)
# Cast back to match x's dtype
y_int = y.astype(x.dtype)
Advanced filtering
==================
resampy allows you to control the design of the filters used in resampling operations.
Expand Down Expand Up @@ -100,28 +125,3 @@ resampy allows you to control the design of the filters used in resampling opera
y = 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`.

.. code-block:: python
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
17 changes: 13 additions & 4 deletions resampy/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@


def resample(
x, sr_orig, sr_new, axis=-1, filter="kaiser_best", parallel=True, **kwargs
x, sr_orig, sr_new, axis=-1, filter="kaiser_best", parallel=False, **kwargs
):
"""Resample a signal x from sr_orig to sr_new along a given axis.
Expand Down Expand Up @@ -116,7 +116,12 @@ def resample(
)

# Preserve contiguity of input (if it exists)
y = np.zeros_like(x, shape=shape)
if np.issubdtype(x.dtype, np.integer):
dtype = np.float32
else:
dtype = x.dtype

y = np.zeros_like(x, dtype=dtype, shape=shape)

interp_win, precision, _ = get_filter(filter, **kwargs)

Expand Down Expand Up @@ -155,7 +160,7 @@ def resample(


def resample_nu(
x, sr_orig, t_out, axis=-1, filter="kaiser_best", parallel=True, **kwargs
x, sr_orig, t_out, axis=-1, filter="kaiser_best", parallel=False, **kwargs
):
"""Interpolate a signal x at specified positions (t_out) along a given axis.
Expand Down Expand Up @@ -239,7 +244,11 @@ def resample_nu(
shape = list(x.shape)
shape[axis] = len(t_out)

y = np.zeros_like(x, shape=shape)
if np.issubdtype(x.dtype, np.integer):
dtype = np.float32
else:
dtype = x.dtype
y = np.zeros_like(x, dtype=dtype, shape=shape)

interp_win, precision, _ = get_filter(filter, **kwargs)

Expand Down
63 changes: 1 addition & 62 deletions resampy/interpn.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
#!/usr/bin/env python
"""Numba implementation of resampler"""

from numba import (
guvectorize,
float32,
float64,
jit,
prange,
int16,
int32,
int64,
complex64,
complex128,
)
from numba import guvectorize, jit, prange


def _resample_loop(x, t_out, interp_win, interp_delta, num_table, scale, y):
Expand Down Expand Up @@ -82,31 +71,6 @@ def _resample_loop(x, t_out, interp_win, interp_delta, num_table, scale, y):


@guvectorize(
[
(int16[:], float64[:], float64[:], float64[:], int32, float32, int16[:]),
(int32[:], float64[:], float64[:], float64[:], int32, float32, int32[:]),
(int64[:], float64[:], float64[:], float64[:], int32, float32, int64[:]),
(float32[:], float64[:], float64[:], float64[:], int32, float32, float32[:]),
(float64[:], float64[:], float64[:], float64[:], int32, float32, float64[:]),
(
complex64[:],
float64[:],
float64[:],
float64[:],
int32,
float32,
complex64[:],
),
(
complex128[:],
float64[:],
float64[:],
float64[:],
int32,
float32,
complex128[:],
),
],
"(n),(m),(p),(p),(),()->(m)",
nopython=True,
)
Expand All @@ -115,31 +79,6 @@ def resample_f_p(x, t_out, interp_win, interp_delta, num_table, scale, y):


@guvectorize(
[
(int16[:], float64[:], float64[:], float64[:], int32, float32, int16[:]),
(int32[:], float64[:], float64[:], float64[:], int32, float32, int32[:]),
(int64[:], float64[:], float64[:], float64[:], int32, float32, int64[:]),
(float32[:], float64[:], float64[:], float64[:], int32, float32, float32[:]),
(float64[:], float64[:], float64[:], float64[:], int32, float32, float64[:]),
(
complex64[:],
float64[:],
float64[:],
float64[:],
int32,
float32,
complex64[:],
),
(
complex128[:],
float64[:],
float64[:],
float64[:],
int32,
float32,
complex128[:],
),
],
"(n),(m),(p),(p),(),()->(m)",
nopython=True,
)
Expand Down
4 changes: 2 additions & 2 deletions resampy/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
# -*- coding: utf-8 -*-
"""Version info"""

short_version = '0.3'
version = '0.3.1'
short_version = '0.4'
version = '0.4.0'
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ install_package_data = True
python_requires >= 3.6
install_requires =
numpy>=1.17
numba>=0.47
numba>=0.53

[options.package_data]
resampy = data/*
Expand Down
22 changes: 20 additions & 2 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def test_bad_num_zeros():


@pytest.mark.parametrize('dtype', [np.float32, np.float64,
np.int16, np.int32, np.int64])
np.complex64, np.complex128])
def test_dtype(dtype):
x = np.random.randn(100).astype(dtype)

Expand All @@ -86,8 +86,26 @@ def test_dtype(dtype):
assert x.dtype == y.dtype


@pytest.mark.parametrize('dtype', [np.int16, np.int32, np.int64])
def test_dtype_int(dtype):
x = (32767 * np.random.randn(100)).astype(dtype)

y = resampy.resample(x, 100, 200)

assert y.dtype == np.float32


@pytest.mark.parametrize('dtype', [np.int16, np.int32, np.int64])
def test_dtype_int_nu(dtype):
x = (32767 * np.random.randn(100)).astype(dtype)
t = np.arange(2 * len(x) - 1) / 2

y = resampy.resample_nu(x, 1., t)
assert y.dtype == np.float32


@pytest.mark.parametrize('dtype', [np.float32, np.float64,
np.int16, np.int32, np.int64])
np.complex64, np.complex128])
def test_resample_nu_dtype(dtype):
x = np.random.randn(100).astype(dtype)
t = np.arange(2 * len(x) - 1) / 2
Expand Down

0 comments on commit 2ca177f

Please sign in to comment.