ai-content-maker/.venv/Lib/site-packages/soxr/cysoxr.pyx

246 lines
6.7 KiB
Cython
Raw Normal View History

2024-05-03 04:18:51 +03:00
#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]