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