1 | #include "aubio-types.h" |
---|
2 | |
---|
3 | typedef struct |
---|
4 | { |
---|
5 | PyObject_HEAD |
---|
6 | aubio_source_t * o; |
---|
7 | char_t* uri; |
---|
8 | uint_t samplerate; |
---|
9 | uint_t channels; |
---|
10 | uint_t hop_size; |
---|
11 | uint_t duration; |
---|
12 | PyObject *read_to; |
---|
13 | fvec_t c_read_to; |
---|
14 | PyObject *mread_to; |
---|
15 | fmat_t c_mread_to; |
---|
16 | } Py_source; |
---|
17 | |
---|
18 | static char Py_source_doc[] = "" |
---|
19 | "source(path, samplerate=0, hop_size=512, channels=0)\n" |
---|
20 | "\n" |
---|
21 | "Read audio samples from a media file.\n" |
---|
22 | "\n" |
---|
23 | "`source` open the file specified in `path` and creates a callable\n" |
---|
24 | "returning `hop_size` new audio samples at each invocation.\n" |
---|
25 | "\n" |
---|
26 | "If `samplerate=0` (default), the original sampling rate of `path`\n" |
---|
27 | "will be used. Otherwise, the output audio samples will be\n" |
---|
28 | "resampled at the desired sampling-rate.\n" |
---|
29 | "\n" |
---|
30 | "If `channels=0` (default), the original number of channels\n" |
---|
31 | "in `path` will be used. Otherwise, the output audio samples\n" |
---|
32 | "will be down-mixed or up-mixed to the desired number of\n" |
---|
33 | "channels.\n" |
---|
34 | "\n" |
---|
35 | "If `path` is a URL, a remote connection will be attempted to\n" |
---|
36 | "open the resource and stream data from it.\n" |
---|
37 | "\n" |
---|
38 | "The parameter `hop_size` determines how many samples should be\n" |
---|
39 | "read at each consecutive calls.\n" |
---|
40 | "\n" |
---|
41 | "Parameters\n" |
---|
42 | "----------\n" |
---|
43 | "path : str\n" |
---|
44 | " pathname (or URL) of the file to be opened for reading\n" |
---|
45 | "samplerate : int, optional\n" |
---|
46 | " sampling rate of the file\n" |
---|
47 | "hop_size : int, optional\n" |
---|
48 | " number of samples to be read per iteration\n" |
---|
49 | "channels : int, optional\n" |
---|
50 | " number of channels of the file\n" |
---|
51 | "\n" |
---|
52 | "Examples\n" |
---|
53 | "--------\n" |
---|
54 | "By default, when only `path` is given, the file will be opened\n" |
---|
55 | "with its original sampling rate and channel:\n" |
---|
56 | "\n" |
---|
57 | ">>> src = aubio.source('stereo.wav')\n" |
---|
58 | ">>> src.uri, src.samplerate, src.channels, src.duration\n" |
---|
59 | "('stereo.wav', 48000, 2, 86833)\n" |
---|
60 | "\n" |
---|
61 | "A typical loop to read all samples from a local file could\n" |
---|
62 | "look like this:\n" |
---|
63 | "\n" |
---|
64 | ">>> src = aubio.source('stereo.wav')\n" |
---|
65 | ">>> total_read = 0\n" |
---|
66 | ">>> while True:\n" |
---|
67 | "... samples, read = src()\n" |
---|
68 | "... # do something with samples\n" |
---|
69 | "... total_read += read\n" |
---|
70 | "... if read < src.hop_size:\n" |
---|
71 | "... break\n" |
---|
72 | "...\n" |
---|
73 | "\n" |
---|
74 | "In a more Pythonic way, it can also look like this:\n" |
---|
75 | "\n" |
---|
76 | ">>> total_read = 0\n" |
---|
77 | ">>> with aubio.source('stereo.wav') as src:\n" |
---|
78 | "... for frames in src:\n" |
---|
79 | "... total_read += samples.shape[-1]\n" |
---|
80 | "...\n" |
---|
81 | "\n" |
---|
82 | ".. rubric:: Basic interface\n" |
---|
83 | "\n" |
---|
84 | "`source` is a **callable**; its :meth:`__call__` method\n" |
---|
85 | "returns a tuple containing:\n" |
---|
86 | "\n" |
---|
87 | "- a vector of shape `(hop_size,)`, filled with the `read` next\n" |
---|
88 | " samples available, zero-padded if `read < hop_size`\n" |
---|
89 | "- `read`, an integer indicating the number of samples read\n" |
---|
90 | "\n" |
---|
91 | "To read the first `hop_size` samples from the source, simply call\n" |
---|
92 | "the instance itself, with no argument:\n" |
---|
93 | "\n" |
---|
94 | ">>> src = aubio.source('song.ogg')\n" |
---|
95 | ">>> samples, read = src()\n" |
---|
96 | ">>> samples.shape, read, src.hop_size\n" |
---|
97 | "((512,), 512, 512)\n" |
---|
98 | "\n" |
---|
99 | "The first call returned the slice of samples `[0 : hop_size]`.\n" |
---|
100 | "The next call will return samples `[hop_size: 2*hop_size]`.\n" |
---|
101 | "\n" |
---|
102 | "After several invocations of :meth:`__call__`, when reaching the end\n" |
---|
103 | "of the opened stream, `read` might become less than `hop_size`:\n" |
---|
104 | "\n" |
---|
105 | ">>> samples, read = src()\n" |
---|
106 | ">>> samples.shape, read\n" |
---|
107 | "((512,), 354)\n" |
---|
108 | "\n" |
---|
109 | "The end of the vector `samples` is filled with zeros.\n" |
---|
110 | "\n" |
---|
111 | "After the end of the stream, `read` will be `0` since no more\n" |
---|
112 | "samples are available:\n" |
---|
113 | "\n" |
---|
114 | ">>> samples, read = src()\n" |
---|
115 | ">>> samples.shape, read\n" |
---|
116 | "((512,), 0)\n" |
---|
117 | "\n" |
---|
118 | "**Note**: when the source has more than one channels, they\n" |
---|
119 | "are be down-mixed to mono when invoking :meth:`__call__`.\n" |
---|
120 | "To read from each individual channel, see :meth:`__next__`.\n" |
---|
121 | "\n" |
---|
122 | ".. rubric:: ``for`` statements\n" |
---|
123 | "\n" |
---|
124 | "The `source` objects are **iterables**. This allows using them\n" |
---|
125 | "directly in a ``for`` loop, which calls :meth:`__next__` until\n" |
---|
126 | "the end of the stream is reached:\n" |
---|
127 | "\n" |
---|
128 | ">>> src = aubio.source('stereo.wav')\n" |
---|
129 | ">>> for frames in src:\n" |
---|
130 | ">>> print (frames.shape)\n" |
---|
131 | "...\n" |
---|
132 | "(2, 512)\n" |
---|
133 | "(2, 512)\n" |
---|
134 | "(2, 230)\n" |
---|
135 | "\n" |
---|
136 | "**Note**: When `next(self)` is called on a source with multiple\n" |
---|
137 | "channels, an array of shape `(channels, read)` is returned,\n" |
---|
138 | "unlike with :meth:`__call__` which always returns the down-mixed\n" |
---|
139 | "channels.\n" |
---|
140 | "\n" |
---|
141 | "If the file is opened with a single channel, `next(self)` returns\n" |
---|
142 | "an array of shape `(read,)`:\n" |
---|
143 | "\n" |
---|
144 | ">>> src = aubio.source('stereo.wav', channels=1)\n" |
---|
145 | ">>> next(src).shape\n" |
---|
146 | "(512,)\n" |
---|
147 | "\n" |
---|
148 | ".. rubric:: ``with`` statements\n" |
---|
149 | "\n" |
---|
150 | "The `source` objects are **context managers**, which allows using\n" |
---|
151 | "them in ``with`` statements:\n" |
---|
152 | "\n" |
---|
153 | ">>> with aubio.source('audiotrack.wav') as source:\n" |
---|
154 | "... n_frames=0\n" |
---|
155 | "... for samples in source:\n" |
---|
156 | "... n_frames += len(samples)\n" |
---|
157 | "... print('read', n_frames, 'samples in', samples.shape[0], 'channels',\n" |
---|
158 | "... 'from file \"%%s\"' %% source.uri)\n" |
---|
159 | "...\n" |
---|
160 | "read 239334 samples in 2 channels from file \"audiotrack.wav\"\n" |
---|
161 | "\n" |
---|
162 | "The file will be closed before exiting the statement.\n" |
---|
163 | "\n" |
---|
164 | "See also the methods implementing the context manager,\n" |
---|
165 | ":meth:`__enter__` and :meth:`__exit__`.\n" |
---|
166 | "\n" |
---|
167 | ".. rubric:: Seeking and closing\n" |
---|
168 | "\n" |
---|
169 | "At any time, :meth:`seek` can be used to move to any position in\n" |
---|
170 | "the file. For instance, to rewind to the start of the stream:\n" |
---|
171 | "\n" |
---|
172 | ">>> src.seek(0)\n" |
---|
173 | "\n" |
---|
174 | "The opened file will be automatically closed when the object falls\n" |
---|
175 | "out of scope and is scheduled for garbage collection.\n" |
---|
176 | "\n" |
---|
177 | "In some cases, it is useful to manually :meth:`close` a given source,\n" |
---|
178 | "for instance to limit the number of simultaneously opened files:\n" |
---|
179 | "\n" |
---|
180 | ">>> src.close()\n" |
---|
181 | "\n" |
---|
182 | ".. rubric:: Input formats\n" |
---|
183 | "\n" |
---|
184 | "Depending on how aubio was compiled, :class:`source` may or may not\n" |
---|
185 | "open certain **files format**. Below are some examples that assume\n" |
---|
186 | "support for compressed files and remote urls was compiled in:\n" |
---|
187 | "\n" |
---|
188 | "- open a local file using its original sampling rate and channels,\n" |
---|
189 | " and with the default hop size:\n" |
---|
190 | "\n" |
---|
191 | ">>> s = aubio.source('sample.wav')\n" |
---|
192 | ">>> s.uri, s.samplerate, s.channels, s.hop_size\n" |
---|
193 | "('sample.wav', 44100, 2, 512)\n" |
---|
194 | "\n" |
---|
195 | "- open a local compressed audio file, resampling to 32000Hz if needed:\n" |
---|
196 | "\n" |
---|
197 | ">>> s = aubio.source('song.mp3', samplerate=32000)\n" |
---|
198 | ">>> s.uri, s.samplerate, s.channels, s.hop_size\n" |
---|
199 | "('song.mp3', 32000, 2, 512)\n" |
---|
200 | "\n" |
---|
201 | "- open a local video file, down-mixing and resampling it to 16kHz:\n" |
---|
202 | "\n" |
---|
203 | ">>> s = aubio.source('movie.mp4', samplerate=16000, channels=1)\n" |
---|
204 | ">>> s.uri, s.samplerate, s.channels, s.hop_size\n" |
---|
205 | "('movie.mp4', 16000, 1, 512)\n" |
---|
206 | "\n" |
---|
207 | "- open a remote resource, with hop_size = 1024:\n" |
---|
208 | "\n" |
---|
209 | ">>> s = aubio.source('https://aubio.org/drum.ogg', hop_size=1024)\n" |
---|
210 | ">>> s.uri, s.samplerate, s.channels, s.hop_size\n" |
---|
211 | "('https://aubio.org/drum.ogg', 48000, 2, 1024)\n" |
---|
212 | "\n" |
---|
213 | "See Also\n" |
---|
214 | "--------\n" |
---|
215 | "sink: write audio samples to a file.\n" |
---|
216 | ""; |
---|
217 | |
---|
218 | static char Py_source_get_samplerate_doc[] = "" |
---|
219 | "get_samplerate()\n" |
---|
220 | "\n" |
---|
221 | "Get sampling rate of source.\n" |
---|
222 | "\n" |
---|
223 | "Returns\n" |
---|
224 | "-------\n" |
---|
225 | "int\n" |
---|
226 | " Sampling rate, in Hz.\n" |
---|
227 | ""; |
---|
228 | |
---|
229 | static char Py_source_get_channels_doc[] = "" |
---|
230 | "get_channels()\n" |
---|
231 | "\n" |
---|
232 | "Get number of channels in source.\n" |
---|
233 | "\n" |
---|
234 | "Returns\n" |
---|
235 | "-------\n" |
---|
236 | "int\n" |
---|
237 | " Number of channels.\n" |
---|
238 | ""; |
---|
239 | |
---|
240 | static char Py_source_do_doc[] = "" |
---|
241 | "source.do()\n" |
---|
242 | "\n" |
---|
243 | "Read vector of audio samples.\n" |
---|
244 | "\n" |
---|
245 | "If the audio stream in the source has more than one channel,\n" |
---|
246 | "the channels will be down-mixed.\n" |
---|
247 | "\n" |
---|
248 | "Returns\n" |
---|
249 | "-------\n" |
---|
250 | "samples : numpy.ndarray\n" |
---|
251 | " `fvec` of size `hop_size` containing the new samples.\n" |
---|
252 | "read : int\n" |
---|
253 | " Number of samples read from the source, equals to `hop_size`\n" |
---|
254 | " before the end-of-file is reached, less when it is reached,\n" |
---|
255 | " and `0` after.\n" |
---|
256 | "\n" |
---|
257 | "See Also\n" |
---|
258 | "--------\n" |
---|
259 | "do_multi\n" |
---|
260 | "\n" |
---|
261 | "Examples\n" |
---|
262 | "--------\n" |
---|
263 | ">>> src = aubio.source('sample.wav', hop_size=1024)\n" |
---|
264 | ">>> src.do()\n" |
---|
265 | "(array([-0.00123001, -0.00036685, 0.00097106, ..., -0.2031033 ,\n" |
---|
266 | " -0.2025854 , -0.20221856], dtype=" AUBIO_NPY_SMPL_STR "), 1024)\n" |
---|
267 | ""; |
---|
268 | |
---|
269 | static char Py_source_do_multi_doc[] = "" |
---|
270 | "do_multi()\n" |
---|
271 | "\n" |
---|
272 | "Read multiple channels of audio samples.\n" |
---|
273 | "\n" |
---|
274 | "If the source was opened with the same number of channels\n" |
---|
275 | "found in the stream, each channel will be read individually.\n" |
---|
276 | "\n" |
---|
277 | "If the source was opened with less channels than the number\n" |
---|
278 | "of channels in the stream, only the first channels will be read.\n" |
---|
279 | "\n" |
---|
280 | "If the source was opened with more channels than the number\n" |
---|
281 | "of channel in the original stream, the first channels will\n" |
---|
282 | "be duplicated on the additional output channel.\n" |
---|
283 | "\n" |
---|
284 | "Returns\n" |
---|
285 | "-------\n" |
---|
286 | "samples : numpy.ndarray\n" |
---|
287 | " NumPy array of shape `(hop_size, channels)` containing the new\n" |
---|
288 | " audio samples.\n" |
---|
289 | "read : int\n" |
---|
290 | " Number of samples read from the source, equals to `hop_size`\n" |
---|
291 | " before the end-of-file is reached, less when it is reached,\n" |
---|
292 | " and `0` after.\n" |
---|
293 | "\n" |
---|
294 | "See Also\n" |
---|
295 | "--------\n" |
---|
296 | "do\n" |
---|
297 | "\n" |
---|
298 | "Examples\n" |
---|
299 | "--------\n" |
---|
300 | ">>> src = aubio.source('sample.wav')\n" |
---|
301 | ">>> src.do_multi()\n" |
---|
302 | "(array([[ 0.00668335, 0.0067749 , 0.00714111, ..., -0.05737305,\n" |
---|
303 | " -0.05856323, -0.06018066],\n" |
---|
304 | " [-0.00842285, -0.0072937 , -0.00576782, ..., -0.09405518,\n" |
---|
305 | " -0.09558105, -0.09725952]], dtype=" AUBIO_NPY_SMPL_STR "), 512)\n" |
---|
306 | ""; |
---|
307 | |
---|
308 | static char Py_source_close_doc[] = "" |
---|
309 | "close()\n" |
---|
310 | "\n" |
---|
311 | "Close this source now.\n" |
---|
312 | "\n" |
---|
313 | ".. note:: Closing twice a source will **not** raise any exception.\n" |
---|
314 | ""; |
---|
315 | |
---|
316 | static char Py_source_seek_doc[] = "" |
---|
317 | "seek(position)\n" |
---|
318 | "\n" |
---|
319 | "Seek to position in file.\n" |
---|
320 | "\n" |
---|
321 | "If the source was not opened with its original sampling-rate,\n" |
---|
322 | "`position` corresponds to the position in the re-sampled file.\n" |
---|
323 | "\n" |
---|
324 | "Parameters\n" |
---|
325 | "----------\n" |
---|
326 | "position : str\n" |
---|
327 | " position to seek to, in samples\n" |
---|
328 | ""; |
---|
329 | |
---|
330 | static PyObject * |
---|
331 | Py_source_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) |
---|
332 | { |
---|
333 | Py_source *self; |
---|
334 | char_t* uri = NULL; |
---|
335 | uint_t samplerate = 0; |
---|
336 | uint_t hop_size = 0; |
---|
337 | uint_t channels = 0; |
---|
338 | static char *kwlist[] = { "uri", "samplerate", "hop_size", "channels", NULL }; |
---|
339 | |
---|
340 | if (!PyArg_ParseTupleAndKeywords (args, kwds, "|sIII", kwlist, |
---|
341 | &uri, &samplerate, &hop_size, &channels)) { |
---|
342 | return NULL; |
---|
343 | } |
---|
344 | |
---|
345 | self = (Py_source *) pytype->tp_alloc (pytype, 0); |
---|
346 | |
---|
347 | if (self == NULL) { |
---|
348 | return NULL; |
---|
349 | } |
---|
350 | |
---|
351 | self->uri = NULL; |
---|
352 | if (uri != NULL) { |
---|
353 | self->uri = (char_t *)malloc(sizeof(char_t) * (strnlen(uri, PATH_MAX) + 1)); |
---|
354 | strncpy(self->uri, uri, strnlen(uri, PATH_MAX) + 1); |
---|
355 | } |
---|
356 | |
---|
357 | self->samplerate = 0; |
---|
358 | if ((sint_t)samplerate > 0) { |
---|
359 | self->samplerate = samplerate; |
---|
360 | } else if ((sint_t)samplerate < 0) { |
---|
361 | PyErr_SetString (PyExc_ValueError, |
---|
362 | "can not use negative value for samplerate"); |
---|
363 | return NULL; |
---|
364 | } |
---|
365 | |
---|
366 | self->hop_size = Py_default_vector_length / 2; |
---|
367 | if ((sint_t)hop_size > 0) { |
---|
368 | self->hop_size = hop_size; |
---|
369 | } else if ((sint_t)hop_size < 0) { |
---|
370 | PyErr_SetString (PyExc_ValueError, |
---|
371 | "can not use negative value for hop_size"); |
---|
372 | return NULL; |
---|
373 | } |
---|
374 | |
---|
375 | self->channels = 1; |
---|
376 | if ((sint_t)channels >= 0) { |
---|
377 | self->channels = channels; |
---|
378 | } else if ((sint_t)channels < 0) { |
---|
379 | PyErr_SetString (PyExc_ValueError, |
---|
380 | "can not use negative value for channels"); |
---|
381 | return NULL; |
---|
382 | } |
---|
383 | |
---|
384 | return (PyObject *) self; |
---|
385 | } |
---|
386 | |
---|
387 | static int |
---|
388 | Py_source_init (Py_source * self, PyObject * args, PyObject * kwds) |
---|
389 | { |
---|
390 | self->o = new_aubio_source ( self->uri, self->samplerate, self->hop_size ); |
---|
391 | if (self->o == NULL) { |
---|
392 | // PyErr_Format(PyExc_RuntimeError, ...) was set above by new_ which called |
---|
393 | // AUBIO_ERR when failing |
---|
394 | return -1; |
---|
395 | } |
---|
396 | self->samplerate = aubio_source_get_samplerate ( self->o ); |
---|
397 | if (self->channels == 0) { |
---|
398 | self->channels = aubio_source_get_channels ( self->o ); |
---|
399 | } |
---|
400 | self->duration = aubio_source_get_duration ( self->o ); |
---|
401 | |
---|
402 | self->read_to = new_py_fvec(self->hop_size); |
---|
403 | self->mread_to = new_py_fmat(self->channels, self->hop_size); |
---|
404 | |
---|
405 | return 0; |
---|
406 | } |
---|
407 | |
---|
408 | static void |
---|
409 | Py_source_del (Py_source *self, PyObject *unused) |
---|
410 | { |
---|
411 | if (self->o) { |
---|
412 | del_aubio_source(self->o); |
---|
413 | free(self->c_mread_to.data); |
---|
414 | } |
---|
415 | if (self->uri) { |
---|
416 | free(self->uri); |
---|
417 | } |
---|
418 | Py_XDECREF(self->read_to); |
---|
419 | Py_XDECREF(self->mread_to); |
---|
420 | Py_TYPE(self)->tp_free((PyObject *) self); |
---|
421 | } |
---|
422 | |
---|
423 | |
---|
424 | /* function Py_source_do */ |
---|
425 | static PyObject * |
---|
426 | Py_source_do(Py_source * self, PyObject * args) |
---|
427 | { |
---|
428 | PyObject *outputs; |
---|
429 | uint_t read; |
---|
430 | read = 0; |
---|
431 | |
---|
432 | Py_INCREF(self->read_to); |
---|
433 | if (!PyAubio_ArrayToCFvec(self->read_to, &(self->c_read_to))) { |
---|
434 | return NULL; |
---|
435 | } |
---|
436 | /* compute _do function */ |
---|
437 | aubio_source_do (self->o, &(self->c_read_to), &read); |
---|
438 | |
---|
439 | if (PyErr_Occurred() != NULL) { |
---|
440 | return NULL; |
---|
441 | } |
---|
442 | |
---|
443 | outputs = PyTuple_New(2); |
---|
444 | PyTuple_SetItem( outputs, 0, self->read_to ); |
---|
445 | PyTuple_SetItem( outputs, 1, (PyObject *)PyLong_FromLong(read)); |
---|
446 | return outputs; |
---|
447 | } |
---|
448 | |
---|
449 | /* function Py_source_do_multi */ |
---|
450 | static PyObject * |
---|
451 | Py_source_do_multi(Py_source * self, PyObject * args) |
---|
452 | { |
---|
453 | PyObject *outputs; |
---|
454 | uint_t read; |
---|
455 | read = 0; |
---|
456 | |
---|
457 | Py_INCREF(self->mread_to); |
---|
458 | if (!PyAubio_ArrayToCFmat(self->mread_to, &(self->c_mread_to))) { |
---|
459 | return NULL; |
---|
460 | } |
---|
461 | /* compute _do function */ |
---|
462 | aubio_source_do_multi (self->o, &(self->c_mread_to), &read); |
---|
463 | |
---|
464 | if (PyErr_Occurred() != NULL) { |
---|
465 | return NULL; |
---|
466 | } |
---|
467 | |
---|
468 | outputs = PyTuple_New(2); |
---|
469 | PyTuple_SetItem( outputs, 0, self->mread_to); |
---|
470 | PyTuple_SetItem( outputs, 1, (PyObject *)PyLong_FromLong(read)); |
---|
471 | return outputs; |
---|
472 | } |
---|
473 | |
---|
474 | static PyMemberDef Py_source_members[] = { |
---|
475 | {"uri", T_STRING, offsetof (Py_source, uri), READONLY, |
---|
476 | "str (read-only): pathname or URL"}, |
---|
477 | {"samplerate", T_INT, offsetof (Py_source, samplerate), READONLY, |
---|
478 | "int (read-only): sampling rate"}, |
---|
479 | {"channels", T_INT, offsetof (Py_source, channels), READONLY, |
---|
480 | "int (read-only): number of channels"}, |
---|
481 | {"hop_size", T_INT, offsetof (Py_source, hop_size), READONLY, |
---|
482 | "int (read-only): number of samples read per iteration"}, |
---|
483 | {"duration", T_INT, offsetof (Py_source, duration), READONLY, |
---|
484 | "int (read-only): total number of frames in the source\n" |
---|
485 | "\n" |
---|
486 | "Can be estimated, for instance if the opened stream is\n" |
---|
487 | "a compressed media or a remote resource.\n" |
---|
488 | "\n" |
---|
489 | "Example\n" |
---|
490 | "-------\n" |
---|
491 | ">>> n = 0\n" |
---|
492 | ">>> src = aubio.source('track1.mp3')\n" |
---|
493 | ">>> for samples in src:\n" |
---|
494 | "... n += samples.shape[-1]\n" |
---|
495 | "...\n" |
---|
496 | ">>> n, src.duration\n" |
---|
497 | "(9638784, 9616561)\n" |
---|
498 | ""}, |
---|
499 | { NULL } // sentinel |
---|
500 | }; |
---|
501 | |
---|
502 | static PyObject * |
---|
503 | Pyaubio_source_get_samplerate (Py_source *self, PyObject *unused) |
---|
504 | { |
---|
505 | uint_t tmp = aubio_source_get_samplerate (self->o); |
---|
506 | return (PyObject *)PyLong_FromLong (tmp); |
---|
507 | } |
---|
508 | |
---|
509 | static PyObject * |
---|
510 | Pyaubio_source_get_channels (Py_source *self, PyObject *unused) |
---|
511 | { |
---|
512 | uint_t tmp = aubio_source_get_channels (self->o); |
---|
513 | return (PyObject *)PyLong_FromLong (tmp); |
---|
514 | } |
---|
515 | |
---|
516 | static PyObject * |
---|
517 | Pyaubio_source_close (Py_source *self, PyObject *unused) |
---|
518 | { |
---|
519 | if (aubio_source_close(self->o) != 0) return NULL; |
---|
520 | Py_RETURN_NONE; |
---|
521 | } |
---|
522 | |
---|
523 | static PyObject * |
---|
524 | Pyaubio_source_seek (Py_source *self, PyObject *args) |
---|
525 | { |
---|
526 | uint_t err = 0; |
---|
527 | |
---|
528 | int position; |
---|
529 | if (!PyArg_ParseTuple (args, "I", &position)) { |
---|
530 | return NULL; |
---|
531 | } |
---|
532 | |
---|
533 | if (position < 0) { |
---|
534 | PyErr_Format(PyExc_ValueError, |
---|
535 | "error when seeking in source: can not seek to negative value %d", |
---|
536 | position); |
---|
537 | return NULL; |
---|
538 | } |
---|
539 | |
---|
540 | err = aubio_source_seek(self->o, position); |
---|
541 | if (err != 0) { |
---|
542 | PyErr_SetString (PyExc_ValueError, |
---|
543 | "error when seeking in source"); |
---|
544 | return NULL; |
---|
545 | } |
---|
546 | Py_RETURN_NONE; |
---|
547 | } |
---|
548 | |
---|
549 | static char Pyaubio_source_enter_doc[] = ""; |
---|
550 | static PyObject* Pyaubio_source_enter(Py_source *self, PyObject *unused) { |
---|
551 | Py_INCREF(self); |
---|
552 | return (PyObject*)self; |
---|
553 | } |
---|
554 | |
---|
555 | static char Pyaubio_source_exit_doc[] = ""; |
---|
556 | static PyObject* Pyaubio_source_exit(Py_source *self, PyObject *unused) { |
---|
557 | return Pyaubio_source_close(self, unused); |
---|
558 | } |
---|
559 | |
---|
560 | static PyObject* Pyaubio_source_iter(PyObject *self) { |
---|
561 | Py_INCREF(self); |
---|
562 | return (PyObject*)self; |
---|
563 | } |
---|
564 | |
---|
565 | static PyObject* Pyaubio_source_iter_next(Py_source *self) { |
---|
566 | PyObject *done, *size; |
---|
567 | if (self->channels == 1) { |
---|
568 | done = Py_source_do(self, NULL); |
---|
569 | } else { |
---|
570 | done = Py_source_do_multi(self, NULL); |
---|
571 | } |
---|
572 | if (!PyTuple_Check(done)) { |
---|
573 | PyErr_Format(PyExc_ValueError, |
---|
574 | "error when reading source: not opened?"); |
---|
575 | return NULL; |
---|
576 | } |
---|
577 | size = PyTuple_GetItem(done, 1); |
---|
578 | if (size != NULL && PyLong_Check(size)) { |
---|
579 | if (PyLong_AsLong(size) == (long)self->hop_size) { |
---|
580 | PyObject *vec = PyTuple_GetItem(done, 0); |
---|
581 | return vec; |
---|
582 | } else if (PyLong_AsLong(size) > 0) { |
---|
583 | // short read, return a shorter array |
---|
584 | PyObject *vec = PyTuple_GetItem(done, 0); |
---|
585 | // take a copy to prevent resizing internal arrays |
---|
586 | PyArrayObject *shortread = (PyArrayObject*)PyArray_FROM_OTF(vec, |
---|
587 | NPY_NOTYPE, NPY_ARRAY_ENSURECOPY); |
---|
588 | PyArray_Dims newdims; |
---|
589 | PyObject *reshaped; |
---|
590 | newdims.len = PyArray_NDIM(shortread); |
---|
591 | newdims.ptr = PyArray_DIMS(shortread); |
---|
592 | // mono or multiple channels? |
---|
593 | if (newdims.len == 1) { |
---|
594 | newdims.ptr[0] = PyLong_AsLong(size); |
---|
595 | } else { |
---|
596 | newdims.ptr[1] = PyLong_AsLong(size); |
---|
597 | } |
---|
598 | reshaped = PyArray_Newshape(shortread, &newdims, NPY_CORDER); |
---|
599 | Py_DECREF(shortread); |
---|
600 | Py_DECREF(vec); |
---|
601 | return reshaped; |
---|
602 | } else { |
---|
603 | PyErr_SetNone(PyExc_StopIteration); |
---|
604 | return NULL; |
---|
605 | } |
---|
606 | } else { |
---|
607 | PyErr_SetNone(PyExc_StopIteration); |
---|
608 | return NULL; |
---|
609 | } |
---|
610 | } |
---|
611 | |
---|
612 | static PyMethodDef Py_source_methods[] = { |
---|
613 | {"get_samplerate", (PyCFunction) Pyaubio_source_get_samplerate, |
---|
614 | METH_NOARGS, Py_source_get_samplerate_doc}, |
---|
615 | {"get_channels", (PyCFunction) Pyaubio_source_get_channels, |
---|
616 | METH_NOARGS, Py_source_get_channels_doc}, |
---|
617 | {"do", (PyCFunction) Py_source_do, |
---|
618 | METH_NOARGS, Py_source_do_doc}, |
---|
619 | {"do_multi", (PyCFunction) Py_source_do_multi, |
---|
620 | METH_NOARGS, Py_source_do_multi_doc}, |
---|
621 | {"close", (PyCFunction) Pyaubio_source_close, |
---|
622 | METH_NOARGS, Py_source_close_doc}, |
---|
623 | {"seek", (PyCFunction) Pyaubio_source_seek, |
---|
624 | METH_VARARGS, Py_source_seek_doc}, |
---|
625 | {"__enter__", (PyCFunction)Pyaubio_source_enter, METH_NOARGS, |
---|
626 | Pyaubio_source_enter_doc}, |
---|
627 | {"__exit__", (PyCFunction)Pyaubio_source_exit, METH_VARARGS, |
---|
628 | Pyaubio_source_exit_doc}, |
---|
629 | {NULL} /* sentinel */ |
---|
630 | }; |
---|
631 | |
---|
632 | PyTypeObject Py_sourceType = { |
---|
633 | PyVarObject_HEAD_INIT (NULL, 0) |
---|
634 | "aubio.source", |
---|
635 | sizeof (Py_source), |
---|
636 | 0, |
---|
637 | (destructor) Py_source_del, |
---|
638 | 0, |
---|
639 | 0, |
---|
640 | 0, |
---|
641 | 0, |
---|
642 | 0, |
---|
643 | 0, |
---|
644 | 0, |
---|
645 | 0, |
---|
646 | 0, |
---|
647 | (ternaryfunc)Py_source_do, |
---|
648 | 0, |
---|
649 | 0, |
---|
650 | 0, |
---|
651 | 0, |
---|
652 | Py_TPFLAGS_DEFAULT, |
---|
653 | Py_source_doc, |
---|
654 | 0, |
---|
655 | 0, |
---|
656 | 0, |
---|
657 | 0, |
---|
658 | Pyaubio_source_iter, |
---|
659 | (unaryfunc)Pyaubio_source_iter_next, |
---|
660 | Py_source_methods, |
---|
661 | Py_source_members, |
---|
662 | 0, |
---|
663 | 0, |
---|
664 | 0, |
---|
665 | 0, |
---|
666 | 0, |
---|
667 | 0, |
---|
668 | (initproc) Py_source_init, |
---|
669 | 0, |
---|
670 | Py_source_new, |
---|
671 | 0, |
---|
672 | 0, |
---|
673 | 0, |
---|
674 | 0, |
---|
675 | 0, |
---|
676 | 0, |
---|
677 | 0, |
---|
678 | 0, |
---|
679 | 0, |
---|
680 | }; |
---|