1 /************************************************************************/ 2 /* PyRtAudio: a python wrapper around RtAudio 3 Copyright (c) 2011 Antoine Lefebvre 4 5 Permission is hereby granted, free of charge, to any person 6 obtaining a copy of this software and associated documentation files 7 (the "Software"), to deal in the Software without restriction, 8 including without limitation the rights to use, copy, modify, merge, 9 publish, distribute, sublicense, and/or sell copies of the Software, 10 and to permit persons to whom the Software is furnished to do so, 11 subject to the following conditions: 12 13 The above copyright notice and this permission notice shall be 14 included in all copies or substantial portions of the Software. 15 16 Any person wishing to distribute modifications to the Software is 17 asked to send the modifications to the original developer so that 18 they can be incorporated into the canonical version. This is, 19 however, not a binding provision of this license. 20 21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 25 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 26 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 27 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 */ 29 /************************************************************************/ 30 31 // This software is in the development stage 32 // Do not expect compatibility with future versions. 33 // Comments, suggestions, new features, bug fixes, etc. are welcome 34 35 #include <Python.h> 36 37 #include "RtAudio.h" 38 39 extern "C" { 40 41 typedef struct 42 { 43 PyObject_HEAD; 44 RtAudio *dac; 45 RtAudioFormat _format; 46 int _bufferSize; 47 unsigned int inputChannels; 48 PyObject *callback_func; 49 } PyRtAudio; 50 51 static PyObject *RtAudioErrorException; 52 callback(void * outputBuffer,void * inputBuffer,unsigned int nBufferFrames,double streamTime,RtAudioStreamStatus status,void * data)53 static int callback(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, 54 double streamTime, RtAudioStreamStatus status, void *data ) 55 { 56 PyRtAudio* self = (PyRtAudio*) data; 57 58 if (status == RTAUDIO_OUTPUT_UNDERFLOW) 59 printf("underflow.\n"); 60 61 if (self == NULL) return -1; 62 63 float* in = (float *) inputBuffer; 64 float* out = (float *) outputBuffer; 65 66 PyObject *py_callback_func = self->callback_func; 67 68 int retval = 0; 69 70 if (py_callback_func) { 71 PyGILState_STATE gstate = PyGILState_Ensure(); 72 73 PyObject* iBuffer = PyBuffer_FromMemory(in, sizeof(float) * self->inputChannels * nBufferFrames); 74 PyObject* oBuffer = PyBuffer_FromReadWriteMemory(out, sizeof(float) * nBufferFrames); 75 PyObject *arglist = Py_BuildValue("(O,O)", oBuffer, iBuffer); 76 77 if (arglist == NULL) { 78 printf("error.\n"); 79 PyErr_Print(); 80 PyGILState_Release(gstate); 81 return 2; 82 } 83 84 // Calling the callback 85 PyObject *result = PyEval_CallObject(py_callback_func, arglist); 86 87 if (PyErr_Occurred() != NULL) { 88 PyErr_Print(); 89 } 90 else if PyInt_Check(result) { 91 retval = PyInt_AsLong(result); 92 } 93 94 Py_DECREF(arglist); 95 Py_DECREF(oBuffer); 96 Py_DECREF(iBuffer); 97 Py_XDECREF(result); 98 99 PyGILState_Release(gstate); 100 } 101 102 return retval; 103 } 104 105 106 RtAudio_dealloc(PyRtAudio * self)107 static void RtAudio_dealloc(PyRtAudio *self) 108 { 109 printf("RtAudio_dealloc.\n"); 110 if (self == NULL) return; 111 112 if (self->dac) { 113 self->dac->closeStream(); 114 Py_CLEAR(self->callback_func); 115 delete self->dac; 116 } 117 118 self->ob_type->tp_free((PyObject *) self); 119 } 120 121 RtAudio_new(PyTypeObject * type,PyObject * args,PyObject * kwds)122 static PyObject* RtAudio_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 123 { 124 printf("RtAudio_new.\n"); 125 PyRtAudio *self; 126 char *api = NULL; 127 128 if(!PyArg_ParseTuple(args, "|s", &api)) 129 return NULL; 130 131 self = (PyRtAudio *) type->tp_alloc(type, 0); 132 133 if(self == NULL) return NULL; 134 135 self->dac = NULL; 136 self->callback_func = NULL; 137 138 try { 139 if (api == NULL) 140 self->dac = new RtAudio; 141 else if(!strcmp(api, "jack")) 142 self->dac = new RtAudio(RtAudio::UNIX_JACK); 143 else if(!strcmp(api, "alsa")) 144 self->dac = new RtAudio(RtAudio::LINUX_ALSA); 145 else if(!strcmp(api, "oss")) 146 self->dac = new RtAudio(RtAudio::LINUX_ALSA); 147 else if(!strcmp(api, "core")) 148 self->dac = new RtAudio(RtAudio::MACOSX_CORE); 149 else if(!strcmp(api, "asio")) 150 self->dac = new RtAudio(RtAudio::WINDOWS_ASIO); 151 else if(!strcmp(api, "directsound")) 152 self->dac = new RtAudio(RtAudio::WINDOWS_DS); 153 } 154 catch (RtAudioError &error) { 155 PyErr_SetString(RtAudioErrorException, error.getMessage().c_str()); 156 Py_INCREF(RtAudioErrorException); 157 return NULL; 158 } 159 160 self->dac->showWarnings(false); 161 162 //Py_XINCREF(self); 163 return (PyObject *) self; 164 } 165 RtAudio_init(PyRtAudio * self,PyObject * args,PyObject * kwds)166 static int RtAudio_init(PyRtAudio *self, PyObject *args, PyObject *kwds) 167 { 168 printf("RtAudio_init.\n"); 169 //if (self == NULL) return 0; 170 return 0; 171 } 172 173 // This functions does not yet support all the features of the RtAudio::openStream method. 174 // Please send your patches if you improves this. RtAudio_openStream(PyRtAudio * self,PyObject * args)175 static PyObject* RtAudio_openStream(PyRtAudio *self, PyObject *args) 176 { 177 if (self == NULL) return NULL; 178 179 if (self->dac == NULL) { 180 printf("the dac is null.\n"); 181 Py_RETURN_NONE; 182 } 183 184 PyObject *oParamsObj; 185 PyObject *iParamsObj; 186 int fs; 187 unsigned int bf; 188 PyObject *pycallback; 189 190 if (!PyArg_ParseTuple(args, "OOiiO", &oParamsObj, &iParamsObj, &fs, &bf, &pycallback)) 191 return NULL; 192 193 RtAudio::StreamParameters oParams; 194 oParams.deviceId = 1; 195 oParams.nChannels = 1; 196 oParams.firstChannel = 0; 197 198 if (PyDict_Check(oParamsObj)) { 199 if (PyDict_Contains(oParamsObj, PyString_FromString("deviceId"))) { 200 PyObject *value = PyDict_GetItem(oParamsObj, PyString_FromString("deviceId")); 201 oParams.deviceId = PyInt_AsLong(value); 202 } 203 if (PyDict_Contains(oParamsObj, PyString_FromString("nChannels"))) { 204 PyObject *value = PyDict_GetItem(oParamsObj, PyString_FromString("nChannels")); 205 oParams.nChannels = PyInt_AsLong(value); 206 } 207 if (PyDict_Contains(oParamsObj, PyString_FromString("firstChannel"))) { 208 PyObject *value = PyDict_GetItem(oParamsObj, PyString_FromString("firstChannel")); 209 oParams.firstChannel = PyInt_AsLong(value); 210 } 211 } 212 else { 213 printf("First argument must be a dictionary. Default values will be used.\n"); 214 } 215 216 RtAudio::StreamParameters iParams; 217 iParams.deviceId = 1; 218 iParams.nChannels = 2; 219 iParams.firstChannel = 0; 220 221 if (PyDict_Check(iParamsObj)) { 222 if (PyDict_Contains(iParamsObj, PyString_FromString("deviceId"))) { 223 PyObject *value = PyDict_GetItem(iParamsObj, PyString_FromString("deviceId")); 224 iParams.deviceId = PyInt_AsLong(value); 225 } 226 if (PyDict_Contains(iParamsObj, PyString_FromString("nChannels"))) { 227 PyObject *value = PyDict_GetItem(iParamsObj, PyString_FromString("nChannels")); 228 iParams.nChannels = PyInt_AsLong(value); 229 } 230 if (PyDict_Contains(iParamsObj, PyString_FromString("firstChannel"))) { 231 PyObject *value = PyDict_GetItem(iParamsObj, PyString_FromString("firstChannel")); 232 iParams.firstChannel = PyInt_AsLong(value); 233 } 234 } 235 else { 236 printf("Second argument must be a dictionary. Default values will be used.\n"); 237 } 238 239 240 if (!PyCallable_Check(pycallback)) { 241 PyErr_SetString(PyExc_TypeError, "Need a callable object!"); 242 Py_XINCREF(PyExc_TypeError); 243 return NULL; 244 } 245 246 // sanity check the callback ? 247 248 249 Py_INCREF(pycallback); /* Add a reference to new callback */ 250 self->callback_func = pycallback; /*Remember new callback */ 251 252 // add support for other format 253 self->_format = RTAUDIO_FLOAT32; 254 255 // add support for other options 256 RtAudio::StreamOptions options; 257 options.flags = RTAUDIO_NONINTERLEAVED; 258 259 try { 260 if (self->dac->isStreamOpen()) 261 self->dac->closeStream(); 262 self->dac->openStream(&oParams, &iParams, self->_format, fs, &bf, &callback, self, &options); 263 } 264 catch ( RtAudioError& error ) { 265 PyErr_SetString(RtAudioErrorException, error.getMessage().c_str()); 266 Py_INCREF(RtAudioErrorException); 267 return NULL; 268 } 269 270 self->inputChannels = iParams.nChannels; 271 272 Py_RETURN_NONE; 273 } 274 RtAudio_closeStream(PyRtAudio * self,PyObject * args)275 static PyObject* RtAudio_closeStream(PyRtAudio *self, PyObject *args) 276 { 277 printf("RtAudio_closeStream.\n"); 278 if (self == NULL || self->dac == NULL) return NULL; 279 280 try { 281 self->dac->closeStream(); 282 Py_CLEAR(self->callback_func); 283 } 284 catch(RtAudioError &error) { 285 PyErr_SetString(RtAudioErrorException, error.getMessage().c_str()); 286 Py_INCREF(RtAudioErrorException); 287 return NULL; 288 } 289 290 Py_RETURN_NONE; 291 } 292 RtAudio_startStream(PyRtAudio * self,PyObject * args)293 static PyObject* RtAudio_startStream(PyRtAudio *self, PyObject *args) 294 { 295 if (self == NULL || self->dac == NULL) return NULL; 296 297 try { 298 self->dac->startStream(); 299 } 300 catch(RtAudioError &error) { 301 PyErr_SetString(RtAudioErrorException, error.getMessage().c_str()); 302 Py_INCREF(RtAudioErrorException); 303 return NULL; 304 } 305 306 Py_RETURN_NONE; 307 } 308 309 RtAudio_stopStream(PyRtAudio * self,PyObject * args)310 static PyObject* RtAudio_stopStream(PyRtAudio *self, PyObject *args) 311 { 312 printf("RtAudio_stopStream.\n"); 313 if (self == NULL || self->dac == NULL) return NULL; 314 315 try { 316 self->dac->stopStream(); 317 } 318 catch(RtAudioError &error) { 319 PyErr_SetString(RtAudioErrorException, error.getMessage().c_str()); 320 Py_INCREF(RtAudioErrorException); 321 return NULL; 322 } 323 324 Py_RETURN_NONE; 325 } 326 RtAudio_abortStream(PyRtAudio * self,PyObject * args)327 static PyObject* RtAudio_abortStream(PyRtAudio *self, PyObject *args) 328 { 329 printf("RtAudio_abortStream.\n"); 330 if (self == NULL || self->dac == NULL) return NULL; 331 332 try { 333 self->dac->abortStream(); 334 } 335 catch(RtAudioError &error) { 336 PyErr_SetString(RtAudioErrorException, error.getMessage().c_str()); 337 Py_INCREF(RtAudioErrorException); 338 return NULL; 339 } 340 Py_RETURN_NONE; 341 } 342 RtAudio_isStreamRunning(PyRtAudio * self,PyObject * args)343 static PyObject* RtAudio_isStreamRunning(PyRtAudio *self, PyObject *args) 344 { 345 if (self == NULL || self->dac == NULL) return NULL; 346 347 if (self->dac == NULL) { 348 Py_RETURN_FALSE; 349 } 350 if (self->dac->isStreamRunning()) 351 Py_RETURN_TRUE; 352 else 353 Py_RETURN_FALSE; 354 } 355 RtAudio_isStreamOpen(PyRtAudio * self,PyObject * args)356 static PyObject* RtAudio_isStreamOpen(PyRtAudio *self, PyObject *args) 357 { 358 if (self == NULL || self->dac == NULL) return NULL; 359 360 if (self->dac == NULL) { 361 Py_RETURN_FALSE; 362 } 363 if (self->dac->isStreamOpen()) 364 Py_RETURN_TRUE; 365 else 366 Py_RETURN_FALSE; 367 368 } 369 RtAudio_getDeviceCount(PyRtAudio * self,PyObject * args)370 static PyObject* RtAudio_getDeviceCount(PyRtAudio *self, PyObject *args) 371 { 372 if (self == NULL || self->dac == NULL) return NULL; 373 374 return PyInt_FromLong(self->dac->getDeviceCount()); 375 } 376 RtAudio_getDeviceInfo(PyRtAudio * self,PyObject * args)377 static PyObject* RtAudio_getDeviceInfo(PyRtAudio *self, PyObject *args) 378 { 379 if (self == NULL || self->dac == NULL) return NULL; 380 381 int device; 382 if (!PyArg_ParseTuple(args, "i", &device)) 383 return NULL; 384 385 try { 386 RtAudio::DeviceInfo info = self->dac->getDeviceInfo(device); 387 388 PyObject* info_dict = PyDict_New(); 389 390 if (info.probed) { 391 Py_INCREF(Py_True); 392 PyDict_SetItemString(info_dict, "probed", Py_True); 393 } 394 else { 395 Py_INCREF(Py_False); 396 PyDict_SetItemString(info_dict, "probed", Py_False); 397 } 398 PyObject* obj; 399 400 obj = PyString_FromString(info.name.c_str()); 401 PyDict_SetItemString(info_dict, "name", obj); 402 403 obj = PyInt_FromLong(info.outputChannels); 404 PyDict_SetItemString(info_dict, "outputChannels", obj); 405 406 obj = PyInt_FromLong(info.inputChannels); 407 PyDict_SetItemString(info_dict, "inputChannels", obj); 408 409 obj = PyInt_FromLong(info.duplexChannels); 410 PyDict_SetItemString(info_dict, "duplexChannels", obj); 411 412 if (info.isDefaultOutput) { 413 Py_INCREF(Py_True); 414 PyDict_SetItemString(info_dict, "isDefaultOutput", Py_True); 415 } 416 else { 417 Py_INCREF(Py_False); 418 PyDict_SetItemString(info_dict, "isDefaultOutput", Py_False); 419 } 420 421 if (info.isDefaultInput) { 422 Py_INCREF(Py_True); 423 PyDict_SetItemString(info_dict, "isDefaultInput", Py_True); 424 } 425 else { 426 Py_INCREF(Py_False); 427 PyDict_SetItemString(info_dict, "isDefaultInput", Py_False); 428 } 429 430 return info_dict; 431 432 } 433 catch(RtAudioError &error) { 434 PyErr_SetString(RtAudioErrorException, error.getMessage().c_str()); 435 Py_INCREF(RtAudioErrorException); 436 return NULL; 437 } 438 } 439 RtAudio_getDefaultOutputDevice(PyRtAudio * self,PyObject * args)440 static PyObject* RtAudio_getDefaultOutputDevice(PyRtAudio *self, PyObject *args) 441 { 442 if (self == NULL || self->dac == NULL) return NULL; 443 return PyInt_FromLong(self->dac->getDefaultOutputDevice()); 444 } 445 RtAudio_getDefaultInputDevice(PyRtAudio * self,PyObject * args)446 static PyObject* RtAudio_getDefaultInputDevice(PyRtAudio *self, PyObject *args) 447 { 448 if (self == NULL || self->dac == NULL) return NULL; 449 return PyInt_FromLong(self->dac->getDefaultInputDevice()); 450 } 451 RtAudio_getStreamTime(PyRtAudio * self,PyObject * args)452 static PyObject* RtAudio_getStreamTime(PyRtAudio *self, PyObject *args) 453 { 454 if (self == NULL || self->dac == NULL) return NULL; 455 return PyFloat_FromDouble( self->dac->getStreamTime() ); 456 } 457 RtAudio_getStreamLatency(PyRtAudio * self,PyObject * args)458 static PyObject* RtAudio_getStreamLatency(PyRtAudio *self, PyObject *args) 459 { 460 if (self == NULL || self->dac == NULL) return NULL; 461 return PyInt_FromLong( self->dac->getStreamLatency() ); 462 } 463 RtAudio_getStreamSampleRate(PyRtAudio * self,PyObject * args)464 static PyObject* RtAudio_getStreamSampleRate(PyRtAudio *self, PyObject *args) 465 { 466 if (self == NULL || self->dac == NULL) return NULL; 467 return PyInt_FromLong( self->dac->getStreamSampleRate() ); 468 } 469 RtAudio_showWarnings(PyRtAudio * self,PyObject * args)470 static PyObject* RtAudio_showWarnings(PyRtAudio *self, PyObject *args) 471 { 472 if (self == NULL || self->dac == NULL) return NULL; 473 474 PyObject *obj; 475 if (!PyArg_ParseTuple(args, "O", &obj)) 476 return NULL; 477 478 if (!PyBool_Check(obj)) 479 return NULL; 480 481 if (obj == Py_True) 482 self->dac->showWarnings(true); 483 else if (obj == Py_False) 484 self->dac->showWarnings(false); 485 else { 486 printf("not true nor false\n"); 487 } 488 Py_RETURN_NONE; 489 } 490 491 492 static PyMethodDef RtAudio_methods[] = 493 { 494 // TO BE DONE: getCurrentApi(void) 495 {"getDeviceCount", (PyCFunction) RtAudio_getDeviceCount, METH_NOARGS, 496 "A public function that queries for the number of audio devices available."}, 497 {"getDeviceInfo", (PyCFunction) RtAudio_getDeviceInfo, METH_VARARGS, 498 "Return a dictionary with information for a specified device number."}, 499 {"getDefaultOutputDevice", (PyCFunction) RtAudio_getDefaultOutputDevice, METH_NOARGS, 500 "A function that returns the index of the default output device."}, 501 {"getDefaultInputDevice", (PyCFunction) RtAudio_getDefaultInputDevice, METH_NOARGS, 502 "A function that returns the index of the default input device."}, 503 {"openStream", (PyCFunction) RtAudio_openStream, METH_VARARGS, 504 "A public method for opening a stream with the specified parameters."}, 505 {"closeStream", (PyCFunction) RtAudio_closeStream, METH_NOARGS, 506 "A function that closes a stream and frees any associated stream memory. "}, 507 {"startStream", (PyCFunction) RtAudio_startStream, METH_NOARGS, 508 "A function that starts a stream. "}, 509 {"stopStream", (PyCFunction) RtAudio_stopStream, METH_NOARGS, 510 "Stop a stream, allowing any samples remaining in the output queue to be played. "}, 511 {"abortStream", (PyCFunction) RtAudio_abortStream, METH_NOARGS, 512 "Stop a stream, discarding any samples remaining in the input/output queue."}, 513 {"isStreamOpen", (PyCFunction) RtAudio_isStreamOpen, METH_NOARGS, 514 "Returns true if a stream is open and false if not."}, 515 {"isStreamRunning", (PyCFunction) RtAudio_isStreamRunning, METH_NOARGS, 516 "Returns true if the stream is running and false if it is stopped or not open."}, 517 {"getStreamTime", (PyCFunction) RtAudio_getStreamTime, METH_NOARGS, 518 "Returns the number of elapsed seconds since the stream was started."}, 519 {"getStreamLatency", (PyCFunction) RtAudio_getStreamLatency, METH_NOARGS, 520 "Returns the internal stream latency in sample frames."}, 521 {"getStreamSampleRate", (PyCFunction) RtAudio_getStreamSampleRate, METH_NOARGS, 522 "Returns actual sample rate in use by the stream."}, 523 {"showWarnings", (PyCFunction) RtAudio_showWarnings, METH_VARARGS, 524 "Specify whether warning messages should be printed to stderr."}, 525 // TO BE DONE: getCompiledApi (std::vector< RtAudio::Api > &apis) throw () 526 {NULL} 527 }; 528 529 530 static PyTypeObject RtAudio_type = { 531 PyObject_HEAD_INIT(NULL) 532 0, /*ob_size*/ 533 "rtaudio.RtAudio", /*tp_name*/ 534 sizeof(RtAudio), /*tp_basicsize*/ 535 0, /*tp_itemsize*/ 536 (destructor) RtAudio_dealloc, /*tp_dealloc*/ 537 0, /*tp_print*/ 538 0, /*tp_getattr*/ 539 0, /*tp_setattr*/ 540 0, /*tp_compare*/ 541 0, /*tp_repr*/ 542 0, /*tp_as_number*/ 543 0, /*tp_as_sequence*/ 544 0, /*tp_as_mapping*/ 545 0, /*tp_hash */ 546 0, /*tp_call*/ 547 0, /*tp_str*/ 548 0, /*tp_getattro*/ 549 0, /*tp_setattro*/ 550 0, /*tp_as_buffer*/ 551 Py_TPFLAGS_DEFAULT, /*tp_flags*/ 552 "Audio input device", /* tp_doc */ 553 0, /* tp_traverse */ 554 0, /* tp_clear */ 555 0, /* tp_richcompare */ 556 0, /* tp_weaklistoffset */ 557 0, /* tp_iter */ 558 0, /* tp_iternext */ 559 RtAudio_methods, /* tp_methods */ 560 0, /* tp_members */ 561 0, /* tp_getset */ 562 0, /* tp_base */ 563 0, /* tp_dict */ 564 0, /* tp_descr_get */ 565 0, /* tp_descr_set */ 566 0, /* tp_dictoffset */ 567 (initproc)RtAudio_init, /* tp_init */ 568 0, /* tp_alloc */ 569 RtAudio_new, /* tp_new */ 570 0, /* Low-level free-memory routine */ 571 0, /* For PyObject_IS_GC */ 572 0, // PyObject *tp_bases; 573 0, // PyObject *tp_mro; /* method resolution order */ 574 0, //PyObject *tp_cache; 575 0, //PyObject *tp_subclasses; 576 0, //PyObject *tp_weaklist; 577 0, //destructor tp_del; 578 //0, /* Type attribute cache version tag. Added in version 2.6 */ 579 }; 580 581 582 583 #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ 584 #define PyMODINIT_FUNC void 585 #endif 586 PyMODINIT_FUNC initrtaudio(void)587 initrtaudio(void) 588 { 589 PyEval_InitThreads(); 590 591 if (PyType_Ready(&RtAudio_type) < 0) 592 return; 593 594 PyObject* module = Py_InitModule3("rtaudio", NULL, "RtAudio wrapper."); 595 if (module == NULL) 596 return; 597 598 Py_INCREF(&RtAudio_type); 599 PyModule_AddObject(module, "RtAudio", (PyObject *)&RtAudio_type); 600 601 RtAudioErrorException = PyErr_NewException("rtaudio.RtError", NULL, NULL); 602 Py_INCREF(RtAudioErrorException); 603 PyModule_AddObject(module, "RtError", RtAudioErrorException); 604 } 605 } 606