Skip to content

Commit

Permalink
Add resample for splited channel(not interleaved) memory layout
Browse files Browse the repository at this point in the history
  • Loading branch information
dofuuz committed Aug 13, 2024
1 parent 4b9c22b commit 50ebbd1
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 0 deletions.
28 changes: 28 additions & 0 deletions src/soxr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,34 @@ def resample(x: ArrayLike, in_rate: float, out_rate: float, quality='HQ') -> np.
raise ValueError('Input must be 1-D or 2-D array')


def _resample_split_ch(x: ArrayLike, in_rate: float, out_rate: float, quality='HQ') -> np.ndarray:
"""
Resample data with splited channel memory.
It has a little speed adventage when data is in Fortran order.
"""
if type(x) != np.ndarray:
x = np.asarray(x, dtype=np.float32)

try:
divide_proc = getattr(soxr_ext, f'csoxr_split_ch_{x.dtype}')
except AttributeError:
raise TypeError(_DTYPE_ERR_STR.format(x.dtype))

q = _quality_to_enum(quality)

if x.ndim == 1:
y = divide_proc(in_rate, out_rate, x[:, np.newaxis], q)
return np.squeeze(y, axis=1)
elif x.ndim == 2:
num_channels = x.shape[1]
if num_channels < 1 or _CH_LIMIT < num_channels:
raise ValueError(_CH_EXEED_ERR_STR.format(num_channels))

return divide_proc(in_rate, out_rate, x, q)
else:
raise ValueError('Input must be 1-D or 2-D array')


def _resample_oneshot(x: np.ndarray, in_rate: float, out_rate: float, quality='HQ') -> np.ndarray:
"""
Resample using libsoxr's `soxr_oneshot()`. Use `resample()` for general use.
Expand Down
79 changes: 79 additions & 0 deletions src/soxr_ext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Python-SoXR is a Python wrapper of libsoxr.
#include <stdint.h>
#include <algorithm>
#include <cmath>
#include <memory>
#include <typeinfo>

#include <nanobind/nanobind.h>
Expand All @@ -23,6 +24,7 @@ Python-SoXR is a Python wrapper of libsoxr.


using std::type_info;
using std::make_unique;

namespace nb = nanobind;
using namespace nb::literals;
Expand All @@ -42,6 +44,19 @@ static soxr_datatype_t to_soxr_datatype(const type_info& ntype) {
throw nb::type_error("Data type not support");
}

static soxr_datatype_t to_soxr_datatype_split(const type_info& ntype) {
if (ntype == typeid(float))
return SOXR_FLOAT32_S;
else if (ntype == typeid(double))
return SOXR_FLOAT64_S;
else if (ntype == typeid(int32_t))
return SOXR_INT32_S;
else if (ntype == typeid(int16_t))
return SOXR_INT16_S;
else
throw nb::type_error("Data type not support");
}


class CSoxr {
soxr_t _soxr = nullptr;
Expand Down Expand Up @@ -226,6 +241,65 @@ auto csoxr_divide_proc(
}


template <typename T>
auto csoxr_split_ch(
double in_rate, double out_rate,
ndarray<const T, nb::ndim<2>, nb::device::cpu> x,
unsigned long quality) {
if (in_rate <= 0 || out_rate <= 0)
throw std::invalid_argument("Sample rate should be over 0");

const size_t ilen = x.shape(0);
const size_t olen = ilen * out_rate / in_rate + 1;
const unsigned channels = x.shape(1);

if (x.stride(0) != 1)
throw std::invalid_argument("Data not continuos");

soxr_error_t err = NULL;

size_t odone = 0;
T *y = nullptr;
{
nb::gil_scoped_release release;

const soxr_datatype_t ntype = to_soxr_datatype_split(typeid(T));

// make soxr config
const soxr_io_spec_t io_spec = soxr_io_spec(ntype, ntype);
const soxr_quality_spec_t quality_spec = soxr_quality_spec(quality, 0);
const auto st = x.stride(1);

y = new T[olen * channels] { 0 };

// get pointers to each channels
auto ibuf_ptrs = make_unique<const T*[]>(channels);
auto obuf_ptrs = make_unique<T*[]>(channels);
for (size_t i = 0; i < channels; ++i) {
ibuf_ptrs[i] = &x.data()[st * i];
obuf_ptrs[i] = &y[olen * i];
}

err = soxr_oneshot(
in_rate, out_rate, channels,
ibuf_ptrs.get(), ilen, NULL,
obuf_ptrs.get(), olen, &odone,
&io_spec, &quality_spec, NULL);
}

if (err) {
delete[] y;
throw std::runtime_error(err);
}

// Delete 'y' when the 'owner' capsule expires
nb::capsule owner(y, [](void *p) noexcept {
delete[] (T *) p;
});
return ndarray<nb::numpy, T>(y, { odone, channels }, owner, { (int64_t)1, (int64_t)olen });
}


template <typename T>
auto csoxr_oneshot(
double in_rate, double out_rate,
Expand Down Expand Up @@ -293,6 +367,11 @@ NB_MODULE(soxr_ext, m) {
m.def("csoxr_divide_proc_int32", csoxr_divide_proc<int32_t>);
m.def("csoxr_divide_proc_int16", csoxr_divide_proc<int16_t>);

m.def("csoxr_split_ch_float32", csoxr_split_ch<float>);
m.def("csoxr_split_ch_float64", csoxr_split_ch<double>);
m.def("csoxr_split_ch_int32", csoxr_split_ch<int32_t>);
m.def("csoxr_split_ch_int16", csoxr_split_ch<int16_t>);

m.def("csoxr_oneshot_float32", csoxr_oneshot<float>);
m.def("csoxr_oneshot_float64", csoxr_oneshot<double>);
m.def("csoxr_oneshot_int32", csoxr_oneshot<int32_t>);
Expand Down

0 comments on commit 50ebbd1

Please sign in to comment.