246 lines
6.7 KiB
Cython
246 lines
6.7 KiB
Cython
#cython: language_level=3
|
|
|
|
# Cython wrapper for libsoxr
|
|
# https://github.com/dofuuz/python-soxr
|
|
|
|
cimport cython
|
|
import numpy as np
|
|
cimport numpy as np
|
|
|
|
from . cimport csoxr
|
|
|
|
|
|
QQ = csoxr.SOXR_QQ
|
|
LQ = csoxr.SOXR_LQ
|
|
MQ = csoxr.SOXR_MQ
|
|
HQ = csoxr.SOXR_HQ
|
|
VHQ = csoxr.SOXR_VHQ
|
|
|
|
|
|
ctypedef fused datatype_t:
|
|
cython.float
|
|
cython.double
|
|
cython.int
|
|
cython.short
|
|
|
|
|
|
cpdef str libsoxr_version():
|
|
return csoxr.libsoxr_version().decode('UTF-8')
|
|
|
|
|
|
# NumPy scalar type to soxr_io_spec_t
|
|
cdef csoxr.soxr_io_spec_t to_io_spec(type ntype):
|
|
cdef csoxr.soxr_datatype_t io_type
|
|
if ntype == np.float32:
|
|
io_type = csoxr.SOXR_FLOAT32_I
|
|
elif ntype == np.float64:
|
|
io_type = csoxr.SOXR_FLOAT64_I
|
|
elif ntype == np.int32:
|
|
io_type = csoxr.SOXR_INT32_I
|
|
elif ntype == np.int16:
|
|
io_type = csoxr.SOXR_INT16_I
|
|
else:
|
|
raise ValueError('Data type not support')
|
|
|
|
return csoxr.soxr_io_spec(io_type, io_type)
|
|
|
|
|
|
cdef class CySoxr:
|
|
cdef csoxr.soxr_t _soxr
|
|
cdef double _in_rate
|
|
cdef double _out_rate
|
|
cdef type _ntype
|
|
cdef unsigned _channels
|
|
cdef bint _ended
|
|
|
|
def __cinit__(self,
|
|
double in_rate, double out_rate, unsigned num_channels,
|
|
type ntype, unsigned long quality):
|
|
self._in_rate = in_rate
|
|
self._out_rate = out_rate
|
|
self._ntype = ntype
|
|
self._channels = num_channels
|
|
self._ended = False
|
|
|
|
cdef csoxr.soxr_error_t err = NULL
|
|
cdef csoxr.soxr_io_spec_t io_spec = to_io_spec(ntype)
|
|
cdef csoxr.soxr_quality_spec_t quality_spec = csoxr.soxr_quality_spec(quality, 0)
|
|
|
|
self._soxr = csoxr.soxr_create(
|
|
in_rate, out_rate, num_channels,
|
|
&err, &io_spec, &quality_spec, NULL)
|
|
|
|
if err is not NULL:
|
|
raise RuntimeError(err)
|
|
|
|
def __dealloc__(self):
|
|
csoxr.soxr_delete(self._soxr)
|
|
|
|
@cython.boundscheck(False)
|
|
@cython.wraparound(False)
|
|
cpdef np.ndarray process(self, const datatype_t[:, ::1] x, bint last=False):
|
|
cdef size_t ilen = x.shape[0]
|
|
cdef size_t olen = np.ceil(ilen * self._out_rate / self._in_rate)
|
|
cdef unsigned channels = x.shape[1]
|
|
|
|
if self._ended:
|
|
raise RuntimeError('Input after last input')
|
|
|
|
if channels != self._channels:
|
|
raise ValueError('Channel num mismatch')
|
|
|
|
cdef type ntype
|
|
if datatype_t is cython.float:
|
|
ntype = np.float32
|
|
elif datatype_t is cython.double:
|
|
ntype = np.float64
|
|
elif datatype_t is cython.int:
|
|
ntype = np.int32
|
|
elif datatype_t is cython.short:
|
|
ntype = np.int16
|
|
|
|
if ntype != self._ntype:
|
|
raise ValueError('Data type mismatch')
|
|
|
|
cdef np.ndarray y = np.zeros([olen, channels], dtype=ntype, order='c')
|
|
|
|
cdef size_t odone
|
|
csoxr.soxr_process(
|
|
self._soxr,
|
|
&x[0,0], ilen, NULL,
|
|
y.data, olen, &odone)
|
|
|
|
y = y[:odone]
|
|
|
|
# flush if last input
|
|
cdef np.ndarray last_buf
|
|
cdef int delay
|
|
if last:
|
|
self._ended = True
|
|
delay = int(csoxr.soxr_delay(self._soxr) + .5)
|
|
|
|
if 0 < delay:
|
|
last_buf = np.zeros([delay, channels], dtype=ntype, order='c')
|
|
|
|
csoxr.soxr_process(
|
|
self._soxr,
|
|
NULL, 0, NULL,
|
|
last_buf.data, delay, &odone)
|
|
|
|
last_buf = last_buf[:odone]
|
|
|
|
y = np.concatenate([y, last_buf])
|
|
|
|
return y
|
|
|
|
|
|
@cython.boundscheck(False)
|
|
@cython.wraparound(False)
|
|
cpdef np.ndarray cysoxr_divide_proc(double in_rate, double out_rate,
|
|
const datatype_t[:, ::1] x,
|
|
unsigned long quality):
|
|
cdef size_t ilen = x.shape[0]
|
|
cdef size_t olen = np.ceil(ilen * out_rate / in_rate)
|
|
cdef size_t chunk_len = int(48000 * in_rate / out_rate)
|
|
cdef unsigned channels = x.shape[1]
|
|
|
|
cdef type ntype
|
|
if datatype_t is cython.float:
|
|
ntype = np.float32
|
|
elif datatype_t is cython.double:
|
|
ntype = np.float64
|
|
elif datatype_t is cython.int:
|
|
ntype = np.int32
|
|
elif datatype_t is cython.short:
|
|
ntype = np.int16
|
|
|
|
# init soxr
|
|
cdef csoxr.soxr_error_t err = NULL
|
|
cdef csoxr.soxr_io_spec_t io_spec = to_io_spec(ntype)
|
|
cdef csoxr.soxr_quality_spec_t quality_spec = csoxr.soxr_quality_spec(quality, 0)
|
|
|
|
cdef csoxr.soxr_t soxr = csoxr.soxr_create(
|
|
in_rate, out_rate, channels,
|
|
&err, &io_spec, &quality_spec, NULL)
|
|
|
|
if err is not NULL:
|
|
raise RuntimeError(err)
|
|
|
|
# alloc
|
|
cdef np.ndarray y = np.zeros([olen, channels], dtype=ntype, order='c')
|
|
cdef datatype_t[:, ::1] y_view = y
|
|
|
|
# divide and process
|
|
cdef size_t odone
|
|
cdef size_t out_pos = 0
|
|
cdef size_t idx = 0
|
|
with nogil:
|
|
while idx + chunk_len < ilen:
|
|
csoxr.soxr_process(
|
|
soxr,
|
|
&x[idx,0], chunk_len, NULL,
|
|
&y_view[out_pos,0], olen-out_pos, &odone)
|
|
out_pos += odone
|
|
idx += chunk_len
|
|
|
|
# last chunk
|
|
if idx < ilen:
|
|
csoxr.soxr_process(
|
|
soxr,
|
|
&x[idx,0], ilen-idx, NULL,
|
|
&y_view[out_pos,0], olen-out_pos, &odone)
|
|
out_pos += odone
|
|
|
|
# flush
|
|
if out_pos < olen:
|
|
csoxr.soxr_process(
|
|
soxr,
|
|
NULL, 0, NULL,
|
|
&y_view[out_pos,0], olen-out_pos, &odone)
|
|
out_pos += odone
|
|
|
|
# destruct
|
|
csoxr.soxr_delete(soxr)
|
|
|
|
return y[:out_pos]
|
|
|
|
|
|
cpdef np.ndarray cysoxr_oneshot(double in_rate, double out_rate,
|
|
np.ndarray x,
|
|
unsigned long quality):
|
|
if 2 < x.ndim:
|
|
raise ValueError('Input must be 1-D or 2-D array')
|
|
|
|
cdef size_t ilen = x.shape[0]
|
|
cdef size_t olen = np.ceil(ilen * out_rate / in_rate)
|
|
cdef unsigned channels = 1
|
|
if 2 == x.ndim:
|
|
channels = x.shape[1]
|
|
|
|
cdef type ntype = x.dtype.type
|
|
|
|
# make soxr config
|
|
cdef csoxr.soxr_error_t err = NULL
|
|
cdef csoxr.soxr_io_spec_t io_spec = to_io_spec(ntype)
|
|
cdef csoxr.soxr_quality_spec_t quality_spec = csoxr.soxr_quality_spec(quality, 0)
|
|
|
|
x = np.ascontiguousarray(x) # make array C-contiguous
|
|
|
|
cdef size_t odone
|
|
cdef np.ndarray y
|
|
if 1 == x.ndim:
|
|
y = np.zeros([olen], dtype=ntype, order='c')
|
|
else:
|
|
y = np.zeros([olen, channels], dtype=ntype, order='c')
|
|
|
|
err = csoxr.soxr_oneshot(
|
|
in_rate, out_rate, channels,
|
|
x.data, ilen, NULL,
|
|
y.data, olen, &odone,
|
|
&io_spec, &quality_spec, NULL)
|
|
|
|
if err is not NULL:
|
|
raise RuntimeError(err)
|
|
|
|
return y[:odone]
|