|
- #include <Python.h>
- #include <SDL/SDL.h>
- #include <numpy/arrayobject.h>
-
- /* SDL error handling */
- PyObject *SDLError;
-
- typedef struct {
- PyObject_HEAD
- int
- samplerate,
- channels;
- int active;
- PyObject *callback;
- } PyYSynthContext;
-
- void PyYSynthContext_Dealloc(PyYSynthContext *self)
- {
- puts("_ysynth: Dealloc context");
- if (self->active) {
- /* Allow any running Python audio routines
- * to finish their current chunk, then lock out
- * the audio callback and shut the audio device
- * down.
- * It is important to first unlock the GIL otherwise
- * we're at risk of causing deadlock.
- */
- Py_BEGIN_ALLOW_THREADS
- SDL_LockAudio();
- SDL_CloseAudio();
- SDL_UnlockAudio();
- Py_END_ALLOW_THREADS
- self->active = 0;
- }
-
- Py_XDECREF(self->callback);
- return;
- }
-
- PyTypeObject PyYSynthContext_Type;
-
- PyTypeObject PyYSynthContext_Type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "_ysynth.ysynth context", /*tp_name*/
- sizeof(PyYSynthContext), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- /* methods */
- (destructor)PyYSynthContext_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*/
- };
-
- /* SDL Audio callback */
- void _sdl_audio_callback(void *user, Uint8 *stream, int len)
- {
- PyYSynthContext *ctx = (PyYSynthContext*)user;
- PyObject *r, *args;
- NPY_AO *channels;
- PyGILState_STATE gstate;
- int local_len = len / sizeof(Sint16) / 2;
- Sint16 *local_stream = (Sint16*)stream;;
- npy_int dims[2] = {local_len, 2};
-
- /* This function is called from SDL's own thread, so we need to
- * register it with the Python interpreter before calling any
- * Python functions.
- */
- gstate = PyGILState_Ensure();
-
- /* Setup NumPy array for use within the Python synthesizer */
- channels = PyArray_New(&PyArray_Type, 2, dims, NPY_INT16, NULL, NULL, 2, 0, NULL);
-
- if (channels)
- args = Py_BuildValue("(O)", channels);
-
- /* Finally execute the callback */
- if (args)
- r = PyObject_CallObject(ctx->callback, (PyObject*)args);
-
- /* Print stacktrace if necessary */
- if (!r) {
- if (PyErr_Occurred())
- PyErr_Print();
- } else {
- /* Copy array data to local_stream */
- memcpy(local_stream, channels->data, len);
-
- /* Free the call's return value, probably Py_None */
- Py_DECREF(r);
- }
-
- /* Free arguments */
- Py_XDECREF(args);
-
- /* Free the channels */
- Py_XDECREF(channels);
-
- /* And leave the GIL */
- PyGILState_Release(gstate);
- return;
- }
-
- PyDoc_STRVAR(hello_doc, "Print hello message from Python module");
-
- /* Output hello message */
- static PyObject *pymod_hello(PyObject *self, PyObject *args)
- {
- PyObject *r = NULL;;
-
- if (PyArg_ParseTuple(args, "")) {
- printf("Hello from Python module\n");
-
- Py_INCREF(Py_None);
- r = Py_None;
- }
-
- return r;
- }
-
- PyDoc_STRVAR(_ysynth_init_doc, "Initialise SDL audio and return context");
-
- /* Initialise synthesizer */
- static PyObject *_ysynth_init(PyObject *self, PyObject *args)
- {
- PyObject *r = NULL;
- PyYSynthContext *ctx;
- int
- samplerate,
- channels;
- SDL_AudioSpec fmt, fmt_obtained;
-
- if (PyArg_ParseTuple(args, "ii;samplerate, channels", &samplerate, &channels)) {
- ctx = PyObject_NEW(PyYSynthContext, &PyYSynthContext_Type);
- r = (PyObject*)ctx;
-
- ctx->callback = NULL;
-
- /* Setup SDL Audio device */
- fmt.freq = samplerate;
- fmt.format = AUDIO_S16;
- fmt.channels = channels;
- fmt.samples = 512;
- fmt.callback = _sdl_audio_callback;
- fmt.userdata = r;
-
- /* Handle SDL error */
- if (SDL_OpenAudio(&fmt, &fmt_obtained) < 0) {
- PyErr_SetString(SDLError, SDL_GetError());
- Py_DECREF(r);
- return NULL;
- }
- SDL_PauseAudio(0);
-
- ctx->samplerate = fmt_obtained.freq;
- ctx->channels = fmt_obtained.channels;
- ctx->active = 1;
-
- }
-
- return r;
- }
-
- PyDoc_STRVAR(_ysynth_shutdown_doc, "Shutdown SDL audio device");
-
- /* Shutdown SDL audio */
- static PyObject *_ysynth_shutdown(PyObject *self, PyObject *args)
- {
- PyObject *r = NULL;
- PyYSynthContext *ctx;
-
- if (!PyArg_ParseTuple(args, "O!", &PyYSynthContext_Type, (PyObject**)&ctx))
- {
- ctx->active = 0;
- SDL_CloseAudio();
- Py_XDECREF(ctx->callback);
- r = Py_None;
- Py_INCREF(r);
- }
-
- return r;
- }
-
- PyDoc_STRVAR(_ysynth_set_callback_doc, "Set audio callback");
-
- /* Set Python audio callback */
- static PyObject *_ysynth_set_callback(PyObject *self, PyObject *args)
- {
- PyObject *r = NULL;
- PyYSynthContext *ctx;
- PyObject *callback;
-
- if (PyArg_ParseTuple(args, "O!O", &PyYSynthContext_Type, (PyObject**)&ctx,
- &callback)) {
- /* We don't have to lock SDL audio because Python's GIL will
- * have locked out the C callback for us, only this Python thread
- * can be executing at this time.
- */
- Py_XDECREF(ctx->callback);
- ctx->callback = callback;
- Py_INCREF(callback);
-
- r = Py_None;
- Py_INCREF(r);
- }
-
- return r;
- }
-
- PyDoc_STRVAR(_ysynth_get_callback_doc, "Get audio callback");
-
- /* Get Python audio callback */
- static PyObject *_ysynth_get_callback(PyObject *self, PyObject *args)
- {
- PyObject *r = NULL;
- PyYSynthContext *ctx;
-
- if (PyArg_ParseTuple(args, "O!", PyYSynthContext_Type, (PyObject**)&ctx)) {
- if (ctx->callback) {
- Py_INCREF(ctx->callback);
- r = ctx->callback;
- } else {
- r = Py_None;
- Py_INCREF(r);
- }
- }
-
- return r;
- }
-
- /* Exported methods */
- static PyMethodDef pymod_methods[] = {
- {"_ysynth_init", (PyCFunction)_ysynth_init,
- METH_VARARGS, _ysynth_init_doc},
- {"_ysynth_shutdown", (PyCFunction)_ysynth_shutdown,
- METH_VARARGS, _ysynth_shutdown_doc},
- {"_ysynth_set_callback", (PyCFunction)_ysynth_set_callback,
- METH_VARARGS, _ysynth_set_callback_doc},
- {"_ysynth_get_callback", (PyCFunction)_ysynth_get_callback,
- METH_VARARGS, _ysynth_get_callback_doc},
- {"hello", (PyCFunction)pymod_hello,
- METH_VARARGS, hello_doc},
- {NULL, NULL} /* sentinel */
- };
-
- /* Shutdown SDL */
- void exitpymod(void)
- {
- SDL_Quit();
- }
-
- PyDoc_STRVAR(module_doc, "Simple test module");
-
- /* Module initialisation, called by Python on import */
- PyMODINIT_FUNC
- initpymod(void)
- {
- PyObject *m;
-
- /* Setup SDL audio without signal handlers */
- if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE) < 0)
- return;
-
- /* Register exit handler
- * XXX: Later on try to use Python's atexit module?
- */
- atexit(exitpymod);
-
- puts("Hoi, module pymod loaded.");
- m = Py_InitModule3("pymod", pymod_methods, module_doc);
- if (m == NULL)
- return;
-
- /* Setup exceptions */
- SDLError = PyErr_NewException("_ysynth.SDLError", NULL, NULL);
- Py_INCREF(SDLError);
- PyModule_AddObject(m, "SDLError", SDLError);
-
- /* Our module requires the GIL to be available */
- if (!PyEval_ThreadsInitialized()) {
- puts("Initialising multithreading for Python");
- PyEval_InitThreads();
- }
-
- import_array();
-
- return;
- }
|