[5652a8c] | 1 | #include "aubio-types.h" |
---|
[d27634d] | 2 | |
---|
| 3 | typedef struct |
---|
| 4 | { |
---|
| 5 | PyObject_HEAD |
---|
| 6 | aubio_source_t * o; |
---|
| 7 | char_t* uri; |
---|
| 8 | uint_t samplerate; |
---|
[1164bcdf] | 9 | uint_t channels; |
---|
[d27634d] | 10 | uint_t hop_size; |
---|
[cfa46b9] | 11 | uint_t duration; |
---|
[1ee5e21] | 12 | PyObject *read_to; |
---|
| 13 | fvec_t c_read_to; |
---|
| 14 | PyObject *mread_to; |
---|
| 15 | fmat_t c_mread_to; |
---|
[d27634d] | 16 | } Py_source; |
---|
| 17 | |
---|
[a79ec76] | 18 | static char Py_source_doc[] = "" |
---|
[58eb250] | 19 | "source(path, samplerate=0, hop_size=512, channels=0)\n" |
---|
[a79ec76] | 20 | "\n" |
---|
[a5c3f2a] | 21 | "Read audio samples from a media file.\n" |
---|
[a79ec76] | 22 | "\n" |
---|
[58eb250] | 23 | "`source` open the file specified in `path` and creates a callable\n" |
---|
| 24 | "returning `hop_size` new audio samples at each invocation.\n" |
---|
[a79ec76] | 25 | "\n" |
---|
[58eb250] | 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" |
---|
[a79ec76] | 29 | "\n" |
---|
[58eb250] | 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" |
---|
[a79ec76] | 34 | "\n" |
---|
[58eb250] | 35 | "If `path` is a URL, a remote connection will be attempted to\n" |
---|
| 36 | "open the resource and stream data from it.\n" |
---|
[a79ec76] | 37 | "\n" |
---|
[58eb250] | 38 | "The parameter `hop_size` determines how many samples should be\n" |
---|
| 39 | "read at each consecutive calls.\n" |
---|
[a79ec76] | 40 | "\n" |
---|
[58eb250] | 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" |
---|
[a79ec76] | 51 | "\n" |
---|
[58eb250] | 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" |
---|
[a79ec76] | 56 | "\n" |
---|
[58eb250] | 57 | ">>> src = aubio.source('stereo.wav')\n" |
---|
| 58 | ">>> src.uri, src.samplerate, src.channels, src.duration\n" |
---|
| 59 | "('stereo.wav', 48000, 2, 86833)\n" |
---|
[a79ec76] | 60 | "\n" |
---|
[58eb250] | 61 | "A typical loop to read all samples from a local file could\n" |
---|
| 62 | "look like this:\n" |
---|
[a79ec76] | 63 | "\n" |
---|
[58eb250] | 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" |
---|
[a79ec76] | 73 | "\n" |
---|
[58eb250] | 74 | "In a more Pythonic way, it can also look like this:\n" |
---|
[a79ec76] | 75 | "\n" |
---|
[58eb250] | 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" |
---|
[15c3466] | 158 | "... 'from file \"%%s\"' %% source.uri)\n" |
---|
[58eb250] | 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 | ""; |
---|
[a79ec76] | 217 | |
---|
| 218 | static char Py_source_get_samplerate_doc[] = "" |
---|
[58eb250] | 219 | "get_samplerate()\n" |
---|
| 220 | "\n" |
---|
| 221 | "Get sampling rate of source.\n" |
---|
[a79ec76] | 222 | "\n" |
---|
[58eb250] | 223 | "Returns\n" |
---|
| 224 | "-------\n" |
---|
| 225 | "int\n" |
---|
| 226 | " Sampling rate, in Hz.\n" |
---|
| 227 | ""; |
---|
[a79ec76] | 228 | |
---|
| 229 | static char Py_source_get_channels_doc[] = "" |
---|
[58eb250] | 230 | "get_channels()\n" |
---|
| 231 | "\n" |
---|
| 232 | "Get number of channels in source.\n" |
---|
[a79ec76] | 233 | "\n" |
---|
[58eb250] | 234 | "Returns\n" |
---|
| 235 | "-------\n" |
---|
| 236 | "int\n" |
---|
| 237 | " Number of channels.\n" |
---|
| 238 | ""; |
---|
[a79ec76] | 239 | |
---|
| 240 | static char Py_source_do_doc[] = "" |
---|
[58eb250] | 241 | "source.do()\n" |
---|
[a79ec76] | 242 | "\n" |
---|
[58eb250] | 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" |
---|
[fa3b1f1] | 250 | "samples : numpy.ndarray\n" |
---|
[58eb250] | 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 | ""; |
---|
[a79ec76] | 268 | |
---|
| 269 | static char Py_source_do_multi_doc[] = "" |
---|
[58eb250] | 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" |
---|
[a79ec76] | 279 | "\n" |
---|
[58eb250] | 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" |
---|
[fa3b1f1] | 286 | "samples : numpy.ndarray\n" |
---|
[58eb250] | 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 | ""; |
---|
[a79ec76] | 307 | |
---|
| 308 | static char Py_source_close_doc[] = "" |
---|
[58eb250] | 309 | "close()\n" |
---|
[a79ec76] | 310 | "\n" |
---|
[58eb250] | 311 | "Close this source now.\n" |
---|
| 312 | "\n" |
---|
| 313 | ".. note:: Closing twice a source will **not** raise any exception.\n" |
---|
| 314 | ""; |
---|
[d27634d] | 315 | |
---|
[11b49d7] | 316 | static char Py_source_seek_doc[] = "" |
---|
[58eb250] | 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" |
---|
[11b49d7] | 323 | "\n" |
---|
[58eb250] | 324 | "Parameters\n" |
---|
| 325 | "----------\n" |
---|
| 326 | "position : str\n" |
---|
| 327 | " position to seek to, in samples\n" |
---|
| 328 | ""; |
---|
[11b49d7] | 329 | |
---|
[d27634d] | 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; |
---|
[24931d5] | 337 | uint_t channels = 0; |
---|
| 338 | static char *kwlist[] = { "uri", "samplerate", "hop_size", "channels", NULL }; |
---|
[d27634d] | 339 | |
---|
[24931d5] | 340 | if (!PyArg_ParseTupleAndKeywords (args, kwds, "|sIII", kwlist, |
---|
| 341 | &uri, &samplerate, &hop_size, &channels)) { |
---|
[d27634d] | 342 | return NULL; |
---|
| 343 | } |
---|
| 344 | |
---|
| 345 | self = (Py_source *) pytype->tp_alloc (pytype, 0); |
---|
| 346 | |
---|
| 347 | if (self == NULL) { |
---|
| 348 | return NULL; |
---|
| 349 | } |
---|
| 350 | |
---|
[b8cedb6] | 351 | self->uri = NULL; |
---|
[d27634d] | 352 | if (uri != NULL) { |
---|
[b8cedb6] | 353 | self->uri = (char_t *)malloc(sizeof(char_t) * (strnlen(uri, PATH_MAX) + 1)); |
---|
| 354 | strncpy(self->uri, uri, strnlen(uri, PATH_MAX) + 1); |
---|
[d27634d] | 355 | } |
---|
| 356 | |
---|
[2f9af5d] | 357 | self->samplerate = 0; |
---|
[18e22f9] | 358 | if ((sint_t)samplerate > 0) { |
---|
[d27634d] | 359 | self->samplerate = samplerate; |
---|
[18e22f9] | 360 | } else if ((sint_t)samplerate < 0) { |
---|
| 361 | PyErr_SetString (PyExc_ValueError, |
---|
| 362 | "can not use negative value for samplerate"); |
---|
| 363 | return NULL; |
---|
[d27634d] | 364 | } |
---|
| 365 | |
---|
| 366 | self->hop_size = Py_default_vector_length / 2; |
---|
[18e22f9] | 367 | if ((sint_t)hop_size > 0) { |
---|
[d27634d] | 368 | self->hop_size = hop_size; |
---|
[18e22f9] | 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; |
---|
[d27634d] | 373 | } |
---|
| 374 | |
---|
[24931d5] | 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 | |
---|
[d27634d] | 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) { |
---|
[bd8a92d] | 392 | // PyErr_Format(PyExc_RuntimeError, ...) was set above by new_ which called |
---|
| 393 | // AUBIO_ERR when failing |
---|
[d27634d] | 394 | return -1; |
---|
| 395 | } |
---|
| 396 | self->samplerate = aubio_source_get_samplerate ( self->o ); |
---|
[24931d5] | 397 | if (self->channels == 0) { |
---|
| 398 | self->channels = aubio_source_get_channels ( self->o ); |
---|
| 399 | } |
---|
[cfa46b9] | 400 | self->duration = aubio_source_get_duration ( self->o ); |
---|
[d27634d] | 401 | |
---|
[1ee5e21] | 402 | self->read_to = new_py_fvec(self->hop_size); |
---|
| 403 | self->mread_to = new_py_fmat(self->channels, self->hop_size); |
---|
[a28dab6] | 404 | |
---|
[d27634d] | 405 | return 0; |
---|
| 406 | } |
---|
| 407 | |
---|
[a28dab6] | 408 | static void |
---|
| 409 | Py_source_del (Py_source *self, PyObject *unused) |
---|
| 410 | { |
---|
[cd2791f] | 411 | if (self->o) { |
---|
| 412 | del_aubio_source(self->o); |
---|
[c18bbef] | 413 | free(self->c_mread_to.data); |
---|
[cd2791f] | 414 | } |
---|
[4f89154] | 415 | if (self->uri) { |
---|
| 416 | free(self->uri); |
---|
| 417 | } |
---|
[1ee5e21] | 418 | Py_XDECREF(self->read_to); |
---|
| 419 | Py_XDECREF(self->mread_to); |
---|
[5c1200a] | 420 | Py_TYPE(self)->tp_free((PyObject *) self); |
---|
[a28dab6] | 421 | } |
---|
| 422 | |
---|
[d27634d] | 423 | |
---|
| 424 | /* function Py_source_do */ |
---|
[1164bcdf] | 425 | static PyObject * |
---|
[d27634d] | 426 | Py_source_do(Py_source * self, PyObject * args) |
---|
| 427 | { |
---|
[a138975] | 428 | PyObject *outputs; |
---|
[d27634d] | 429 | uint_t read; |
---|
| 430 | read = 0; |
---|
| 431 | |
---|
[1ee5e21] | 432 | Py_INCREF(self->read_to); |
---|
| 433 | if (!PyAubio_ArrayToCFvec(self->read_to, &(self->c_read_to))) { |
---|
| 434 | return NULL; |
---|
| 435 | } |
---|
[d27634d] | 436 | /* compute _do function */ |
---|
[1ee5e21] | 437 | aubio_source_do (self->o, &(self->c_read_to), &read); |
---|
[d27634d] | 438 | |
---|
[519d5d3] | 439 | if (PyErr_Occurred() != NULL) { |
---|
| 440 | return NULL; |
---|
| 441 | } |
---|
| 442 | |
---|
[a138975] | 443 | outputs = PyTuple_New(2); |
---|
[1ee5e21] | 444 | PyTuple_SetItem( outputs, 0, self->read_to ); |
---|
[26eb6d0] | 445 | PyTuple_SetItem( outputs, 1, (PyObject *)PyLong_FromLong(read)); |
---|
[d27634d] | 446 | return outputs; |
---|
| 447 | } |
---|
| 448 | |
---|
[1164bcdf] | 449 | /* function Py_source_do_multi */ |
---|
| 450 | static PyObject * |
---|
| 451 | Py_source_do_multi(Py_source * self, PyObject * args) |
---|
| 452 | { |
---|
[a138975] | 453 | PyObject *outputs; |
---|
[1164bcdf] | 454 | uint_t read; |
---|
| 455 | read = 0; |
---|
| 456 | |
---|
[1ee5e21] | 457 | Py_INCREF(self->mread_to); |
---|
| 458 | if (!PyAubio_ArrayToCFmat(self->mread_to, &(self->c_mread_to))) { |
---|
| 459 | return NULL; |
---|
| 460 | } |
---|
[1164bcdf] | 461 | /* compute _do function */ |
---|
[1ee5e21] | 462 | aubio_source_do_multi (self->o, &(self->c_mread_to), &read); |
---|
[1164bcdf] | 463 | |
---|
[519d5d3] | 464 | if (PyErr_Occurred() != NULL) { |
---|
| 465 | return NULL; |
---|
| 466 | } |
---|
| 467 | |
---|
[a138975] | 468 | outputs = PyTuple_New(2); |
---|
[1ee5e21] | 469 | PyTuple_SetItem( outputs, 0, self->mread_to); |
---|
[26eb6d0] | 470 | PyTuple_SetItem( outputs, 1, (PyObject *)PyLong_FromLong(read)); |
---|
[1164bcdf] | 471 | return outputs; |
---|
| 472 | } |
---|
| 473 | |
---|
[5652a8c] | 474 | static PyMemberDef Py_source_members[] = { |
---|
[a79ec76] | 475 | {"uri", T_STRING, offsetof (Py_source, uri), READONLY, |
---|
[58eb250] | 476 | "str (read-only): pathname or URL"}, |
---|
[a79ec76] | 477 | {"samplerate", T_INT, offsetof (Py_source, samplerate), READONLY, |
---|
[58eb250] | 478 | "int (read-only): sampling rate"}, |
---|
[a79ec76] | 479 | {"channels", T_INT, offsetof (Py_source, channels), READONLY, |
---|
[58eb250] | 480 | "int (read-only): number of channels"}, |
---|
[a79ec76] | 481 | {"hop_size", T_INT, offsetof (Py_source, hop_size), READONLY, |
---|
[58eb250] | 482 | "int (read-only): number of samples read per iteration"}, |
---|
[cfa46b9] | 483 | {"duration", T_INT, offsetof (Py_source, duration), READONLY, |
---|
[58eb250] | 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 | ""}, |
---|
[5652a8c] | 499 | { NULL } // sentinel |
---|
| 500 | }; |
---|
[d27634d] | 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); |
---|
[5c1200a] | 506 | return (PyObject *)PyLong_FromLong (tmp); |
---|
[d27634d] | 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); |
---|
[5c1200a] | 513 | return (PyObject *)PyLong_FromLong (tmp); |
---|
[d27634d] | 514 | } |
---|
| 515 | |
---|
[7b56229] | 516 | static PyObject * |
---|
| 517 | Pyaubio_source_close (Py_source *self, PyObject *unused) |
---|
| 518 | { |
---|
[23be736] | 519 | if (aubio_source_close(self->o) != 0) return NULL; |
---|
[7b56229] | 520 | Py_RETURN_NONE; |
---|
| 521 | } |
---|
| 522 | |
---|
[11b49d7] | 523 | static PyObject * |
---|
| 524 | Pyaubio_source_seek (Py_source *self, PyObject *args) |
---|
| 525 | { |
---|
| 526 | uint_t err = 0; |
---|
| 527 | |
---|
[a2ab20a] | 528 | int position; |
---|
[11b49d7] | 529 | if (!PyArg_ParseTuple (args, "I", &position)) { |
---|
| 530 | return NULL; |
---|
| 531 | } |
---|
| 532 | |
---|
[a2ab20a] | 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 | |
---|
[11b49d7] | 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 | |
---|
[f1f2e7e] | 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 | |
---|
[6dda1c0] | 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) { |
---|
[a6222fc] | 566 | PyObject *done, *size; |
---|
[6dda1c0] | 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 | } |
---|
[a6222fc] | 577 | size = PyTuple_GetItem(done, 1); |
---|
[6dda1c0] | 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) { |
---|
[8b7cdba] | 583 | // short read, return a shorter array |
---|
[966c650] | 584 | PyObject *vec = PyTuple_GetItem(done, 0); |
---|
| 585 | // take a copy to prevent resizing internal arrays |
---|
[c1c3a99] | 586 | PyArrayObject *shortread = (PyArrayObject*)PyArray_FROM_OTF(vec, |
---|
| 587 | NPY_NOTYPE, NPY_ARRAY_ENSURECOPY); |
---|
[6dda1c0] | 588 | PyArray_Dims newdims; |
---|
[8b7cdba] | 589 | PyObject *reshaped; |
---|
[6dda1c0] | 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 | } |
---|
[8b7cdba] | 598 | reshaped = PyArray_Newshape(shortread, &newdims, NPY_CORDER); |
---|
| 599 | Py_DECREF(shortread); |
---|
[966c650] | 600 | Py_DECREF(vec); |
---|
[8b7cdba] | 601 | return reshaped; |
---|
[6dda1c0] | 602 | } else { |
---|
| 603 | PyErr_SetNone(PyExc_StopIteration); |
---|
| 604 | return NULL; |
---|
| 605 | } |
---|
| 606 | } else { |
---|
| 607 | PyErr_SetNone(PyExc_StopIteration); |
---|
| 608 | return NULL; |
---|
| 609 | } |
---|
| 610 | } |
---|
| 611 | |
---|
[d27634d] | 612 | static PyMethodDef Py_source_methods[] = { |
---|
| 613 | {"get_samplerate", (PyCFunction) Pyaubio_source_get_samplerate, |
---|
[a79ec76] | 614 | METH_NOARGS, Py_source_get_samplerate_doc}, |
---|
[d27634d] | 615 | {"get_channels", (PyCFunction) Pyaubio_source_get_channels, |
---|
[a79ec76] | 616 | METH_NOARGS, Py_source_get_channels_doc}, |
---|
| 617 | {"do", (PyCFunction) Py_source_do, |
---|
| 618 | METH_NOARGS, Py_source_do_doc}, |
---|
[1164bcdf] | 619 | {"do_multi", (PyCFunction) Py_source_do_multi, |
---|
[a79ec76] | 620 | METH_NOARGS, Py_source_do_multi_doc}, |
---|
[7b56229] | 621 | {"close", (PyCFunction) Pyaubio_source_close, |
---|
[a79ec76] | 622 | METH_NOARGS, Py_source_close_doc}, |
---|
[11b49d7] | 623 | {"seek", (PyCFunction) Pyaubio_source_seek, |
---|
| 624 | METH_VARARGS, Py_source_seek_doc}, |
---|
[f1f2e7e] | 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}, |
---|
[d27634d] | 629 | {NULL} /* sentinel */ |
---|
| 630 | }; |
---|
| 631 | |
---|
[5652a8c] | 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, |
---|
[6dda1c0] | 658 | Pyaubio_source_iter, |
---|
| 659 | (unaryfunc)Pyaubio_source_iter_next, |
---|
[5652a8c] | 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, |
---|
[0e70ef9] | 671 | 0, |
---|
| 672 | 0, |
---|
| 673 | 0, |
---|
| 674 | 0, |
---|
| 675 | 0, |
---|
| 676 | 0, |
---|
| 677 | 0, |
---|
| 678 | 0, |
---|
| 679 | 0, |
---|
[5652a8c] | 680 | }; |
---|