Python wrapper for Xiph.org rnnoise ( https://gitlab.xiph.org/xiph/rnnoise )
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pyrnnoise.c 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. /* Copyright (c) 2023 Yann Weber */
  2. /*
  3. Redistribution and use in source and binary forms, with or without
  4. modification, are permitted provided that the following conditions
  5. are met:
  6. - Redistributions of source code must retain the above copyright
  7. notice, this list of conditions and the following disclaimer.
  8. - Redistributions in binary form must reproduce the above copyright
  9. notice, this list of conditions and the following disclaimer in the
  10. documentation and/or other materials provided with the distribution.
  11. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  12. ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  13. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  14. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
  15. CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  16. EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  17. PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  18. PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  19. LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  20. NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  21. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  22. */
  23. #include <errno.h>
  24. #include <limits.h>
  25. #include <poll.h>
  26. #include <stdio.h>
  27. #include <unistd.h>
  28. #include <rnnoise.h>
  29. #define PY_SSIZE_T_CLEAN
  30. #include <Python.h>
  31. #include "structmember.h"
  32. /**The number of samples processed by RNNoise.process_frame()
  33. * @note There is two samples per frame
  34. * @note Initialized by PyInit_rnnoise module initialization function
  35. */
  36. static int frame_size;
  37. /**@brief Represent a RNNoise python object */
  38. typedef struct
  39. {
  40. PyObject_VAR_HEAD;
  41. /** The RNNoise structure */
  42. DenoiseState *st;
  43. /** Read only attribute on rnnoise frame size
  44. * @note The frame size is in sample (2 bytes per sample)
  45. */
  46. int frame_size;
  47. /** When iterating use this iterator as source */
  48. PyObject *in_iter;
  49. /** Previous partially processed buffer */
  50. PyObject *prev_buf_obj;
  51. /** Pointer on the part of the buffer left to copy */
  52. char *prev_buf;
  53. /** Size in bytes of the buffer left to copy */
  54. Py_ssize_t left_in_buf;
  55. } PyRNNoise_t;
  56. /**@brief RNNoise.__new__ */
  57. PyObject* pyrnnoise_new(PyTypeObject *subtype, PyObject *args, PyObject* kwds)
  58. {
  59. PyObject *ret = PyType_GenericNew(subtype, args, kwds);
  60. if(PyErr_Occurred())
  61. {
  62. return NULL;
  63. }
  64. return ret;
  65. }
  66. /**@brief RNNoise.__init__
  67. * @todo implement model support in argument
  68. */
  69. int pyrnnoise_init(PyObject *_self, PyObject *args, PyObject *kwds)
  70. {
  71. PyRNNoise_t *self = (PyRNNoise_t*)_self;
  72. self->st = rnnoise_create(NULL); // TODO implement model instead of NULL
  73. self->frame_size = frame_size;
  74. self->in_iter = NULL;
  75. self->prev_buf_obj = NULL;
  76. return 0;
  77. }
  78. /** @brief RNNoise.__del__ */
  79. void pyrnnoise_del(PyObject *_self)
  80. {
  81. PyRNNoise_t *self = (PyRNNoise_t*)_self;
  82. rnnoise_destroy(self->st);
  83. if(self->prev_buf_obj) { Py_DECREF(self->prev_buf_obj); }
  84. if(self->in_iter) { Py_DECREF(self->in_iter); }
  85. return;
  86. }
  87. /**@brief RNNoise.process_frame(frame:bytes) -> bytes method
  88. * @param PyObject* Current RNNoise instance
  89. * @param PyObject** List of arguments (1 expected)
  90. * @param Py_ssize_t Number of arguments given
  91. * @return A PyObject* with processed frame (bytes) or NULL (on error)
  92. */
  93. PyObject *pyrnnoise_process_frame(PyObject *_self, PyObject *const *args, Py_ssize_t nargs)
  94. {
  95. PyRNNoise_t *self = (PyRNNoise_t*)_self;
  96. if(nargs != 1)
  97. {
  98. PyErr_SetString(PyExc_ValueError, "Expected one bytes argument");
  99. return NULL;
  100. }
  101. if(!PyBytes_Check(args[0]))
  102. {
  103. PyErr_SetString(PyExc_TypeError, "Excpected argument to be bytes");
  104. return NULL;
  105. }
  106. Py_ssize_t sz = PyBytes_Size(args[0]);
  107. if(sz != 2*self->frame_size)
  108. {
  109. PyErr_Format(PyExc_ValueError,
  110. "Expected %ld bytes but got %ld",
  111. self->frame_size*2, sz);
  112. return NULL;
  113. }
  114. short *data = (short*)PyBytes_AsString(args[0]);
  115. short out[self->frame_size];
  116. float frame[self->frame_size];
  117. for(int i=0; i<self->frame_size; i++) { frame[i] = data[i]; }
  118. rnnoise_process_frame(self->st, frame, frame);
  119. for(int i=0; i<self->frame_size; i++) { out[i] = frame[i]; }
  120. PyObject *ret = PyBytes_FromStringAndSize((char*)out, self->frame_size*2);
  121. return ret;
  122. }
  123. /**@brief pyrnnoise_iternext() utility function
  124. *
  125. * Fill the short buffer given in argument with previous buffer if set
  126. * or with next buffer(s) in iterator.
  127. *
  128. * @param PyRNNoise_t self
  129. * @param short* The buffer (with @ref frame_size samples)
  130. * @return The number of bytes read. Should be @ref frame_size * 2,
  131. * may be less than this value a last time before falling to 0 on
  132. * iterator exhauste
  133. */
  134. static ssize_t _iter_frame(PyRNNoise_t *self, short *buf)
  135. {
  136. const ssize_t to_read = frame_size * 2;
  137. PyObject *inbuf;
  138. ssize_t readed;
  139. if(self->prev_buf_obj)
  140. {
  141. Py_ssize_t from_buf = self->left_in_buf > to_read?\
  142. to_read:self->left_in_buf;
  143. memcpy(buf, self->prev_buf, from_buf);
  144. readed = from_buf;
  145. if(from_buf == self->left_in_buf)
  146. {
  147. Py_DECREF(self->prev_buf_obj);
  148. self->prev_buf_obj = NULL;
  149. self->prev_buf = NULL;
  150. }
  151. else
  152. {
  153. self->left_in_buf -= from_buf;
  154. self->prev_buf = &self->prev_buf[from_buf];
  155. }
  156. }
  157. else
  158. {
  159. readed = 0;
  160. }
  161. while(readed < to_read)
  162. {
  163. const ssize_t left = to_read - readed;
  164. if(!(inbuf = PyIter_Next(self->in_iter)))
  165. {
  166. break;
  167. }
  168. if(!PyBytes_Check(inbuf))
  169. {
  170. PyErr_SetString(PyExc_TypeError, "Expected input iterator to return bytes");
  171. return -1;
  172. }
  173. Py_ssize_t inbuf_sz = PyBytes_Size(inbuf);
  174. char *inbuf_ptr = PyBytes_AsString(inbuf);
  175. memcpy((char*)buf+readed, inbuf_ptr,
  176. inbuf_sz > left?left:inbuf_sz);
  177. readed += inbuf_sz > left?left:inbuf_sz;
  178. if(inbuf_sz > left)
  179. {
  180. self->prev_buf = &inbuf_ptr[left];
  181. self->prev_buf_obj = inbuf;
  182. self->left_in_buf = inbuf_sz - left;
  183. }
  184. else
  185. {
  186. Py_DECREF(inbuf);
  187. }
  188. }
  189. return readed;
  190. }
  191. /**@brief The tp_iternext method of RNNoise
  192. * @return Next processed frame from input_iterator (bytes instance)
  193. */
  194. PyObject *pyrnnoise_iternext(PyObject *_self)
  195. {
  196. PyRNNoise_t *self = (PyRNNoise_t*)_self;
  197. if(!self->in_iter)
  198. {
  199. PyErr_SetString(PyExc_StopIteration, "No input use the iter_on class method");
  200. return NULL;
  201. }
  202. float frame[frame_size];
  203. short res[frame_size];
  204. ssize_t readed = _iter_frame(self, res);
  205. if(readed < 0)
  206. {
  207. return NULL;
  208. }
  209. else if(!readed)
  210. {
  211. Py_DECREF(self->in_iter);
  212. self->in_iter = NULL;
  213. PyErr_SetString(PyExc_StopIteration, "Input exhausted");
  214. return NULL;
  215. }
  216. else if(readed % 2)
  217. {
  218. PyErr_SetString(PyExc_ValueError,
  219. "Iterator exhausted on invalid odd bytes count for as single channel 16bits PCM input");
  220. return NULL;
  221. }
  222. for(int i=0; i<readed/2; i++) { frame[i] = res[i]; }
  223. for(int i=readed/2; i<frame_size; i++) { frame[i] = 0; }
  224. rnnoise_process_frame(self->st, frame, frame);
  225. for(int i=0; i<frame_size; i++) { res[i] = frame[i]; }
  226. return PyBytes_FromStringAndSize((char*)res, readed);
  227. }
  228. /**@brief RNNoise.iter_on(iterator) method
  229. * @return A new reference on self iterator
  230. */
  231. PyObject* pyrnnoise_iter_on(PyObject *_self, PyObject *iterator)
  232. {
  233. PyRNNoise_t *self = (PyRNNoise_t*)_self;
  234. if(!PyIter_Check(iterator))
  235. {
  236. PyErr_SetString(PyExc_TypeError, "Expected an iterator");
  237. return NULL;
  238. }
  239. if(self->in_iter)
  240. {
  241. Py_DECREF(self->in_iter);
  242. }
  243. Py_INCREF(iterator);
  244. self->in_iter = iterator;
  245. return PyObject_GetIter(_self);
  246. }
  247. /*
  248. * RNNoise class definition
  249. */
  250. /**@brief RNNoise methods list */
  251. PyMethodDef RNNoise_methods[] = {
  252. {"process_frame", (PyCFunction)pyrnnoise_process_frame, METH_FASTCALL,
  253. "Process a frame of data\n\
  254. Arguments :\n\
  255. - frame : bytes representing a 16bit PCM audio signal (len must be \n\
  256. rnnoise.frame_size() * rnnoise.bytes_per_sample())\n\
  257. Returns : The processed frame bytes"},
  258. {"iter_on", (PyCFunction)pyrnnoise_iter_on, METH_O,
  259. "Consume an iterator and iterate on processed frames\n\
  260. Arguments :\n\
  261. - iterator : an iterator on PCM 16bit bytes\n\
  262. Returns : an iterator on processed frames"},
  263. { NULL } // Sentinel
  264. };
  265. /**@brief RNNoise attributes list */
  266. PyMemberDef RNNoise_members[] = {
  267. {"frame_size", T_INT, offsetof(PyRNNoise_t, frame_size), READONLY,
  268. "The number of sample processed by RNNoise.process_frame()"},
  269. {NULL} // Sentinel
  270. };
  271. /**@brief RNNoise type object */
  272. PyTypeObject RNNoiseType = {
  273. PyVarObject_HEAD_INIT(NULL, 0)
  274. .tp_name = "rnnoise.RNNoise",
  275. .tp_doc= "Xiph RNNoise voice noise reduction wrapper",
  276. .tp_basicsize = sizeof(PyRNNoise_t),
  277. .tp_del = pyrnnoise_del,
  278. .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
  279. .tp_methods = RNNoise_methods,
  280. .tp_members = RNNoise_members,
  281. .tp_iter = PyObject_SelfIter,
  282. .tp_iternext = pyrnnoise_iternext,
  283. .tp_init = pyrnnoise_init,
  284. .tp_new = pyrnnoise_new,
  285. };
  286. /*
  287. * rnnoise module definition
  288. */
  289. /**@brief Module's method without argument, returning the number
  290. * of samples processed by RNNoise.process_frame()
  291. */
  292. PyObject* pyrnnoise_frame_size(PyObject *mod, PyObject *nullval)
  293. {
  294. return PyLong_FromLong(frame_size);
  295. }
  296. /**@brief Module's method without argument, returning the number of
  297. * bytes per sample
  298. */
  299. PyObject* pyrnnoise_bytes_per_sample(PyObject *mod, PyObject *nullval)
  300. {
  301. return PyLong_FromLong(2);
  302. }
  303. /**@brief rnnoise module's methods */
  304. PyMethodDef pyrnnoise_methods[] = {
  305. {"frame_size", (PyCFunction)pyrnnoise_frame_size, METH_NOARGS,
  306. "Return the number of samples processed by RNNoise.process_frame() method"},
  307. {"bytes_per_sample", (PyCFunction)pyrnnoise_bytes_per_sample, METH_NOARGS,
  308. "Return the number of bytes per sample"},
  309. {NULL}
  310. };
  311. /**@brief rnnoise module object */
  312. PyModuleDef pyrnnoise_module = {
  313. PyModuleDef_HEAD_INIT,
  314. .m_name = "rnnoise",
  315. .m_doc = "Python bindings to xiph rnnoise",
  316. .m_size = -1,
  317. .m_methods = pyrnnoise_methods,
  318. .m_slots = NULL,
  319. .m_traverse = NULL,
  320. .m_clear = NULL,
  321. .m_free = NULL,
  322. };
  323. /**@brief Module initialisation function (kind of main or __init__) */
  324. PyMODINIT_FUNC
  325. PyInit_rnnoise(void)
  326. {
  327. PyObject *mod;
  328. mod = PyModule_Create(&pyrnnoise_module);
  329. if(mod == NULL) { return NULL; }
  330. if(PyType_Ready(&RNNoiseType) < 0)
  331. {
  332. goto fail_rnnoisetype_ready;
  333. }
  334. Py_INCREF(&RNNoiseType);
  335. if(PyModule_AddObject(mod, "RNNoise", (PyObject*)&RNNoiseType) < 0)
  336. {
  337. goto fail_rnnoisetype_add;
  338. }
  339. frame_size = rnnoise_get_frame_size();
  340. return mod;
  341. fail_rnnoisetype_add:
  342. Py_DECREF(&RNNoiseType);
  343. fail_rnnoisetype_ready:
  344. Py_DECREF(mod);
  345. return NULL;
  346. }