|
- /************************************************************************/
- /* PyRtAudio: a python wrapper around RtAudio
- Copyright (c) 2011 Antoine Lefebvre
-
- Permission is hereby granted, free of charge, to any person
- obtaining a copy of this software and associated documentation files
- (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge,
- publish, distribute, sublicense, and/or sell copies of the Software,
- and to permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
-
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
-
- Any person wishing to distribute modifications to the Software is
- asked to send the modifications to the original developer so that
- they can be incorporated into the canonical version. This is,
- however, not a binding provision of this license.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- /************************************************************************/
-
- // This software is in the development stage
- // Do not expect compatibility with future versions.
- // Comments, suggestions, new features, bug fixes, etc. are welcome
-
- #include <Python.h>
-
- #include "RtAudio.h"
-
- extern "C" {
-
- typedef struct
- {
- PyObject_HEAD;
- RtAudio *dac;
- RtAudioFormat _format;
- int _bufferSize;
- unsigned int inputChannels;
- PyObject *callback_func;
- } PyRtAudio;
-
- static PyObject *RtAudioError;
-
- static int callback(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
- double streamTime, RtAudioStreamStatus status, void *data )
- {
- PyRtAudio* self = (PyRtAudio*) data;
-
- if (status == RTAUDIO_OUTPUT_UNDERFLOW)
- printf("underflow.\n");
-
- if (self == NULL) return -1;
-
- float* in = (float *) inputBuffer;
- float* out = (float *) outputBuffer;
-
- PyObject *py_callback_func = self->callback_func;
-
- int retval = 0;
-
- if (py_callback_func) {
- PyGILState_STATE gstate = PyGILState_Ensure();
-
- PyObject* iBuffer = PyBuffer_FromMemory(in, sizeof(float) * self->inputChannels * nBufferFrames);
- PyObject* oBuffer = PyBuffer_FromReadWriteMemory(out, sizeof(float) * nBufferFrames);
- PyObject *arglist = Py_BuildValue("(O,O)", oBuffer, iBuffer);
-
- if (arglist == NULL) {
- printf("error.\n");
- PyErr_Print();
- PyGILState_Release(gstate);
- return 2;
- }
-
- // Calling the callback
- PyObject *result = PyEval_CallObject(py_callback_func, arglist);
-
- if (PyErr_Occurred() != NULL) {
- PyErr_Print();
- }
- else if PyInt_Check(result) {
- retval = PyInt_AsLong(result);
- }
-
- Py_DECREF(arglist);
- Py_DECREF(oBuffer);
- Py_DECREF(iBuffer);
- Py_XDECREF(result);
-
- PyGILState_Release(gstate);
- }
-
- return retval;
- }
-
-
-
- static void RtAudio_dealloc(PyRtAudio *self)
- {
- printf("RtAudio_dealloc.\n");
- if (self == NULL) return;
-
- if (self->dac) {
- self->dac->closeStream();
- Py_CLEAR(self->callback_func);
- delete self->dac;
- }
-
- self->ob_type->tp_free((PyObject *) self);
- }
-
-
- static PyObject* RtAudio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
- {
- printf("RtAudio_new.\n");
- PyRtAudio *self;
- char *api = NULL;
-
- if(!PyArg_ParseTuple(args, "|s", &api))
- return NULL;
-
- self = (PyRtAudio *) type->tp_alloc(type, 0);
-
- if(self == NULL) return NULL;
-
- self->dac = NULL;
- self->callback_func = NULL;
-
- try {
- if (api == NULL)
- self->dac = new RtAudio;
- else if(!strcmp(api, "jack"))
- self->dac = new RtAudio(RtAudio::UNIX_JACK);
- else if(!strcmp(api, "alsa"))
- self->dac = new RtAudio(RtAudio::LINUX_ALSA);
- else if(!strcmp(api, "oss"))
- self->dac = new RtAudio(RtAudio::LINUX_ALSA);
- else if(!strcmp(api, "core"))
- self->dac = new RtAudio(RtAudio::MACOSX_CORE);
- else if(!strcmp(api, "asio"))
- self->dac = new RtAudio(RtAudio::WINDOWS_ASIO);
- else if(!strcmp(api, "directsound"))
- self->dac = new RtAudio(RtAudio::WINDOWS_DS);
- }
- catch (RtError &error) {
- PyErr_SetString(RtAudioError, error.getMessage().c_str());
- Py_INCREF(RtAudioError);
- return NULL;
- }
-
- self->dac->showWarnings(false);
-
- //Py_XINCREF(self);
- return (PyObject *) self;
- }
-
- static int RtAudio_init(PyRtAudio *self, PyObject *args, PyObject *kwds)
- {
- printf("RtAudio_init.\n");
- //if (self == NULL) return 0;
- return 0;
- }
-
- // This functions does not yet support all the features of the RtAudio::openStream method.
- // Please send your patches if you improves this.
- static PyObject* RtAudio_openStream(PyRtAudio *self, PyObject *args)
- {
- if (self == NULL) return NULL;
-
- if (self->dac == NULL) {
- printf("the dac is null.\n");
- Py_RETURN_NONE;
- }
-
- PyObject *oParamsObj;
- PyObject *iParamsObj;
- int fs;
- unsigned int bf;
- PyObject *pycallback;
-
- if (!PyArg_ParseTuple(args, "OOiiO", &oParamsObj, &iParamsObj, &fs, &bf, &pycallback))
- return NULL;
-
- RtAudio::StreamParameters oParams;
- oParams.deviceId = 1;
- oParams.nChannels = 1;
- oParams.firstChannel = 0;
-
- if (PyDict_Check(oParamsObj)) {
- if (PyDict_Contains(oParamsObj, PyString_FromString("deviceId"))) {
- PyObject *value = PyDict_GetItem(oParamsObj, PyString_FromString("deviceId"));
- oParams.deviceId = PyInt_AsLong(value);
- }
- if (PyDict_Contains(oParamsObj, PyString_FromString("nChannels"))) {
- PyObject *value = PyDict_GetItem(oParamsObj, PyString_FromString("nChannels"));
- oParams.nChannels = PyInt_AsLong(value);
- }
- if (PyDict_Contains(oParamsObj, PyString_FromString("firstChannel"))) {
- PyObject *value = PyDict_GetItem(oParamsObj, PyString_FromString("firstChannel"));
- oParams.firstChannel = PyInt_AsLong(value);
- }
- }
- else {
- printf("First argument must be a dictionary. Default values will be used.\n");
- }
-
- RtAudio::StreamParameters iParams;
- iParams.deviceId = 1;
- iParams.nChannels = 2;
- iParams.firstChannel = 0;
-
- if (PyDict_Check(iParamsObj)) {
- if (PyDict_Contains(iParamsObj, PyString_FromString("deviceId"))) {
- PyObject *value = PyDict_GetItem(iParamsObj, PyString_FromString("deviceId"));
- iParams.deviceId = PyInt_AsLong(value);
- }
- if (PyDict_Contains(iParamsObj, PyString_FromString("nChannels"))) {
- PyObject *value = PyDict_GetItem(iParamsObj, PyString_FromString("nChannels"));
- iParams.nChannels = PyInt_AsLong(value);
- }
- if (PyDict_Contains(iParamsObj, PyString_FromString("firstChannel"))) {
- PyObject *value = PyDict_GetItem(iParamsObj, PyString_FromString("firstChannel"));
- iParams.firstChannel = PyInt_AsLong(value);
- }
- }
- else {
- printf("Second argument must be a dictionary. Default values will be used.\n");
- }
-
-
- if (!PyCallable_Check(pycallback)) {
- PyErr_SetString(PyExc_TypeError, "Need a callable object!");
- Py_XINCREF(PyExc_TypeError);
- return NULL;
- }
-
- // sanity check the callback ?
-
-
- Py_INCREF(pycallback); /* Add a reference to new callback */
- self->callback_func = pycallback; /*Remember new callback */
-
- // add support for other format
- self->_format = RTAUDIO_FLOAT32;
-
- // add support for other options
- RtAudio::StreamOptions options;
- options.flags = RTAUDIO_NONINTERLEAVED;
-
- try {
- if (self->dac->isStreamOpen())
- self->dac->closeStream();
- self->dac->openStream(&oParams, &iParams, self->_format, fs, &bf, &callback, self, &options);
- }
- catch ( RtError& error ) {
- PyErr_SetString(RtAudioError, error.getMessage().c_str());
- Py_INCREF(RtAudioError);
- return NULL;
- }
-
- self->inputChannels = iParams.nChannels;
-
- Py_RETURN_NONE;
- }
-
- static PyObject* RtAudio_closeStream(PyRtAudio *self, PyObject *args)
- {
- printf("RtAudio_closeStream.\n");
- if (self == NULL || self->dac == NULL) return NULL;
-
- try {
- self->dac->closeStream();
- Py_CLEAR(self->callback_func);
- }
- catch(RtError &error) {
- PyErr_SetString(RtAudioError, error.getMessage().c_str());
- Py_INCREF(RtAudioError);
- return NULL;
- }
-
- Py_RETURN_NONE;
- }
-
- static PyObject* RtAudio_startStream(PyRtAudio *self, PyObject *args)
- {
- if (self == NULL || self->dac == NULL) return NULL;
-
- try {
- self->dac->startStream();
- }
- catch(RtError &error) {
- PyErr_SetString(RtAudioError, error.getMessage().c_str());
- Py_INCREF(RtAudioError);
- return NULL;
- }
-
- Py_RETURN_NONE;
- }
-
-
- static PyObject* RtAudio_stopStream(PyRtAudio *self, PyObject *args)
- {
- printf("RtAudio_stopStream.\n");
- if (self == NULL || self->dac == NULL) return NULL;
-
- try {
- self->dac->stopStream();
- }
- catch(RtError &error) {
- PyErr_SetString(RtAudioError, error.getMessage().c_str());
- Py_INCREF(RtAudioError);
- return NULL;
- }
-
- Py_RETURN_NONE;
- }
-
- static PyObject* RtAudio_abortStream(PyRtAudio *self, PyObject *args)
- {
- printf("RtAudio_abortStream.\n");
- if (self == NULL || self->dac == NULL) return NULL;
-
- try {
- self->dac->abortStream();
- }
- catch(RtError &error) {
- PyErr_SetString(RtAudioError, error.getMessage().c_str());
- Py_INCREF(RtAudioError);
- return NULL;
- }
- Py_RETURN_NONE;
- }
-
- static PyObject* RtAudio_isStreamRunning(PyRtAudio *self, PyObject *args)
- {
- if (self == NULL || self->dac == NULL) return NULL;
-
- if (self->dac == NULL) {
- Py_RETURN_FALSE;
- }
- if (self->dac->isStreamRunning())
- Py_RETURN_TRUE;
- else
- Py_RETURN_FALSE;
- }
-
- static PyObject* RtAudio_isStreamOpen(PyRtAudio *self, PyObject *args)
- {
- if (self == NULL || self->dac == NULL) return NULL;
-
- if (self->dac == NULL) {
- Py_RETURN_FALSE;
- }
- if (self->dac->isStreamOpen())
- Py_RETURN_TRUE;
- else
- Py_RETURN_FALSE;
-
- }
-
- static PyObject* RtAudio_getDeviceCount(PyRtAudio *self, PyObject *args)
- {
- if (self == NULL || self->dac == NULL) return NULL;
-
- return PyInt_FromLong(self->dac->getDeviceCount());
- }
-
- static PyObject* RtAudio_getDeviceInfo(PyRtAudio *self, PyObject *args)
- {
- if (self == NULL || self->dac == NULL) return NULL;
-
- int device;
- if (!PyArg_ParseTuple(args, "i", &device))
- return NULL;
-
- try {
- RtAudio::DeviceInfo info = self->dac->getDeviceInfo(device);
-
- PyObject* info_dict = PyDict_New();
-
- if (info.probed) {
- Py_INCREF(Py_True);
- PyDict_SetItemString(info_dict, "probed", Py_True);
- }
- else {
- Py_INCREF(Py_False);
- PyDict_SetItemString(info_dict, "probed", Py_False);
- }
- PyObject* obj;
-
- obj = PyString_FromString(info.name.c_str());
- PyDict_SetItemString(info_dict, "name", obj);
-
- obj = PyInt_FromLong(info.outputChannels);
- PyDict_SetItemString(info_dict, "outputChannels", obj);
-
- obj = PyInt_FromLong(info.inputChannels);
- PyDict_SetItemString(info_dict, "inputChannels", obj);
-
- obj = PyInt_FromLong(info.duplexChannels);
- PyDict_SetItemString(info_dict, "duplexChannels", obj);
-
- if (info.isDefaultOutput) {
- Py_INCREF(Py_True);
- PyDict_SetItemString(info_dict, "isDefaultOutput", Py_True);
- }
- else {
- Py_INCREF(Py_False);
- PyDict_SetItemString(info_dict, "isDefaultOutput", Py_False);
- }
-
- if (info.isDefaultInput) {
- Py_INCREF(Py_True);
- PyDict_SetItemString(info_dict, "isDefaultInput", Py_True);
- }
- else {
- Py_INCREF(Py_False);
- PyDict_SetItemString(info_dict, "isDefaultInput", Py_False);
- }
-
- return info_dict;
-
- }
- catch(RtError &error) {
- PyErr_SetString(RtAudioError, error.getMessage().c_str());
- Py_INCREF(RtAudioError);
- return NULL;
- }
- }
-
- static PyObject* RtAudio_getDefaultOutputDevice(PyRtAudio *self, PyObject *args)
- {
- if (self == NULL || self->dac == NULL) return NULL;
- return PyInt_FromLong(self->dac->getDefaultOutputDevice());
- }
-
- static PyObject* RtAudio_getDefaultInputDevice(PyRtAudio *self, PyObject *args)
- {
- if (self == NULL || self->dac == NULL) return NULL;
- return PyInt_FromLong(self->dac->getDefaultInputDevice());
- }
-
- static PyObject* RtAudio_getStreamTime(PyRtAudio *self, PyObject *args)
- {
- if (self == NULL || self->dac == NULL) return NULL;
- return PyFloat_FromDouble( self->dac->getStreamTime() );
- }
-
- static PyObject* RtAudio_getStreamLatency(PyRtAudio *self, PyObject *args)
- {
- if (self == NULL || self->dac == NULL) return NULL;
- return PyInt_FromLong( self->dac->getStreamLatency() );
- }
-
- static PyObject* RtAudio_getStreamSampleRate(PyRtAudio *self, PyObject *args)
- {
- if (self == NULL || self->dac == NULL) return NULL;
- return PyInt_FromLong( self->dac->getStreamSampleRate() );
- }
-
- static PyObject* RtAudio_showWarnings(PyRtAudio *self, PyObject *args)
- {
- if (self == NULL || self->dac == NULL) return NULL;
-
- PyObject *obj;
- if (!PyArg_ParseTuple(args, "O", &obj))
- return NULL;
-
- if (!PyBool_Check(obj))
- return NULL;
-
- if (obj == Py_True)
- self->dac->showWarnings(true);
- else if (obj == Py_False)
- self->dac->showWarnings(false);
- else {
- printf("not true nor false\n");
- }
- Py_RETURN_NONE;
- }
-
-
- static PyMethodDef RtAudio_methods[] =
- {
- // TO BE DONE: getCurrentApi(void)
- {"getDeviceCount", (PyCFunction) RtAudio_getDeviceCount, METH_NOARGS,
- "A public function that queries for the number of audio devices available."},
- {"getDeviceInfo", (PyCFunction) RtAudio_getDeviceInfo, METH_VARARGS,
- "Return a dictionary with information for a specified device number."},
- {"getDefaultOutputDevice", (PyCFunction) RtAudio_getDefaultOutputDevice, METH_NOARGS,
- "A function that returns the index of the default output device."},
- {"getDefaultInputDevice", (PyCFunction) RtAudio_getDefaultInputDevice, METH_NOARGS,
- "A function that returns the index of the default input device."},
- {"openStream", (PyCFunction) RtAudio_openStream, METH_VARARGS,
- "A public method for opening a stream with the specified parameters."},
- {"closeStream", (PyCFunction) RtAudio_closeStream, METH_NOARGS,
- "A function that closes a stream and frees any associated stream memory. "},
- {"startStream", (PyCFunction) RtAudio_startStream, METH_NOARGS,
- "A function that starts a stream. "},
- {"stopStream", (PyCFunction) RtAudio_stopStream, METH_NOARGS,
- "Stop a stream, allowing any samples remaining in the output queue to be played. "},
- {"abortStream", (PyCFunction) RtAudio_abortStream, METH_NOARGS,
- "Stop a stream, discarding any samples remaining in the input/output queue."},
- {"isStreamOpen", (PyCFunction) RtAudio_isStreamOpen, METH_NOARGS,
- "Returns true if a stream is open and false if not."},
- {"isStreamRunning", (PyCFunction) RtAudio_isStreamRunning, METH_NOARGS,
- "Returns true if the stream is running and false if it is stopped or not open."},
- {"getStreamTime", (PyCFunction) RtAudio_getStreamTime, METH_NOARGS,
- "Returns the number of elapsed seconds since the stream was started."},
- {"getStreamLatency", (PyCFunction) RtAudio_getStreamLatency, METH_NOARGS,
- "Returns the internal stream latency in sample frames."},
- {"getStreamSampleRate", (PyCFunction) RtAudio_getStreamSampleRate, METH_NOARGS,
- "Returns actual sample rate in use by the stream."},
- {"showWarnings", (PyCFunction) RtAudio_showWarnings, METH_VARARGS,
- "Specify whether warning messages should be printed to stderr."},
- // TO BE DONE: getCompiledApi (std::vector< RtAudio::Api > &apis) throw ()
- {NULL}
- };
-
-
- static PyTypeObject RtAudio_type = {
- PyObject_HEAD_INIT(NULL)
- 0, /*ob_size*/
- "rtaudio.RtAudio", /*tp_name*/
- sizeof(RtAudio), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- (destructor) RtAudio_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- 0, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- 0, /*tp_hash */
- 0, /*tp_call*/
- 0, /*tp_str*/
- 0, /*tp_getattro*/
- 0, /*tp_setattro*/
- 0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT, /*tp_flags*/
- "Audio input device", /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- RtAudio_methods, /* tp_methods */
- 0, /* tp_members */
- 0, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- (initproc)RtAudio_init, /* tp_init */
- 0, /* tp_alloc */
- RtAudio_new, /* tp_new */
- 0, /* Low-level free-memory routine */
- 0, /* For PyObject_IS_GC */
- 0, // PyObject *tp_bases;
- 0, // PyObject *tp_mro; /* method resolution order */
- 0, //PyObject *tp_cache;
- 0, //PyObject *tp_subclasses;
- 0, //PyObject *tp_weaklist;
- 0, //destructor tp_del;
- //0, /* Type attribute cache version tag. Added in version 2.6 */
- };
-
-
-
- #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
- #define PyMODINIT_FUNC void
- #endif
- PyMODINIT_FUNC
- initrtaudio(void)
- {
- PyEval_InitThreads();
-
- if (PyType_Ready(&RtAudio_type) < 0)
- return;
-
- PyObject* module = Py_InitModule3("rtaudio", NULL, "RtAudio wrapper.");
- if (module == NULL)
- return;
-
- Py_INCREF(&RtAudio_type);
- PyModule_AddObject(module, "RtAudio", (PyObject *)&RtAudio_type);
-
- RtAudioError = PyErr_NewException("rtaudio.RtError", NULL, NULL);
- Py_INCREF(RtAudioError);
- PyModule_AddObject(module, "RtError", RtAudioError);
- }
- }
|