Updates for release 4.0.8, including new python binding, new teststops.cpp program...
[rtaudio.git] / contrib / python / pyrtaudio / rtaudiomodule.cpp
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 *RtAudioError;
52
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
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
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 (RtError &error) {
155             PyErr_SetString(RtAudioError, error.getMessage().c_str());
156             Py_INCREF(RtAudioError);
157             return NULL;
158         }
159
160         self->dac->showWarnings(false);
161
162         //Py_XINCREF(self);
163         return (PyObject *) self;
164     }
165
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.
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 ( RtError& error ) {
265             PyErr_SetString(RtAudioError, error.getMessage().c_str());
266             Py_INCREF(RtAudioError);
267             return NULL;
268         }
269
270         self->inputChannels = iParams.nChannels;
271
272         Py_RETURN_NONE;
273     }
274
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(RtError &error) {
285             PyErr_SetString(RtAudioError, error.getMessage().c_str());
286             Py_INCREF(RtAudioError);
287             return NULL;
288         }
289
290         Py_RETURN_NONE;
291     }
292
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(RtError &error) {
301             PyErr_SetString(RtAudioError, error.getMessage().c_str());
302             Py_INCREF(RtAudioError);
303             return NULL;
304         }
305
306         Py_RETURN_NONE;
307     }
308
309
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(RtError &error) {
319             PyErr_SetString(RtAudioError, error.getMessage().c_str());
320             Py_INCREF(RtAudioError);
321             return NULL;
322         }
323
324         Py_RETURN_NONE;
325     }
326
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(RtError &error) {
336             PyErr_SetString(RtAudioError, error.getMessage().c_str());
337             Py_INCREF(RtAudioError);
338             return NULL;
339         }
340         Py_RETURN_NONE;
341     }
342
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
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
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
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(RtError &error) {
434             PyErr_SetString(RtAudioError, error.getMessage().c_str());
435             Py_INCREF(RtAudioError);
436             return NULL;
437         }
438     }
439
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
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
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
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
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
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
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         RtAudioError = PyErr_NewException("rtaudio.RtError", NULL, NULL);
602         Py_INCREF(RtAudioError);
603         PyModule_AddObject(module, "RtError", RtAudioError);
604     }
605 }