Python synthesizer stuff
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

296 lines
7.7KB

  1. #include <Python.h>
  2. #include <SDL/SDL.h>
  3. #include <numpy/arrayobject.h>
  4. /* SDL error handling */
  5. PyObject *SDLError;
  6. typedef struct {
  7. PyObject_HEAD
  8. int
  9. samplerate,
  10. channels;
  11. int active;
  12. PyObject *callback;
  13. } PyYSynthContext;
  14. void PyYSynthContext_Dealloc(PyYSynthContext *self)
  15. {
  16. puts("_ysynth: Dealloc context");
  17. if (self->active) {
  18. /* Allow any running Python audio routines
  19. * to finish their current chunk, then lock out
  20. * the audio callback and shut the audio device
  21. * down.
  22. * It is important to first unlock the GIL otherwise
  23. * we're at risk of causing deadlock.
  24. */
  25. Py_BEGIN_ALLOW_THREADS
  26. SDL_LockAudio();
  27. SDL_CloseAudio();
  28. SDL_UnlockAudio();
  29. Py_END_ALLOW_THREADS
  30. self->active = 0;
  31. }
  32. Py_XDECREF(self->callback);
  33. return;
  34. }
  35. PyTypeObject PyYSynthContext_Type;
  36. PyTypeObject PyYSynthContext_Type = {
  37. PyVarObject_HEAD_INIT(NULL, 0)
  38. "_ysynth.ysynth context", /*tp_name*/
  39. sizeof(PyYSynthContext), /*tp_basicsize*/
  40. 0, /*tp_itemsize*/
  41. /* methods */
  42. (destructor)PyYSynthContext_Dealloc, /*tp_dealloc*/
  43. 0, /*tp_print*/
  44. 0, /*tp_getattr*/
  45. 0, /*tp_setattr*/
  46. 0, /*tp_compare*/
  47. 0, /*tp_repr*/
  48. 0, /*tp_as_number*/
  49. 0, /*tp_as_sequence*/
  50. 0, /*tp_as_mapping*/
  51. 0, /*tp_hash*/
  52. };
  53. /* SDL Audio callback */
  54. void _sdl_audio_callback(void *user, Uint8 *stream, int len)
  55. {
  56. PyYSynthContext *ctx = (PyYSynthContext*)user;
  57. PyObject *r, *args;
  58. NPY_AO *channels;
  59. PyGILState_STATE gstate;
  60. int local_len = len / sizeof(Sint16) / 2;
  61. Sint16 *local_stream = (Sint16*)stream;;
  62. npy_int dims[2] = {local_len, 2};
  63. /* This function is called from SDL's own thread, so we need to
  64. * register it with the Python interpreter before calling any
  65. * Python functions.
  66. */
  67. gstate = PyGILState_Ensure();
  68. /* Setup NumPy array for use within the Python synthesizer */
  69. channels = PyArray_New(&PyArray_Type, 2, dims, NPY_INT16, NULL, NULL, 2, 0, NULL);
  70. if (channels)
  71. args = Py_BuildValue("(O)", channels);
  72. /* Finally execute the callback */
  73. if (args)
  74. r = PyObject_CallObject(ctx->callback, (PyObject*)args);
  75. /* Print stacktrace if necessary */
  76. if (!r) {
  77. if (PyErr_Occurred())
  78. PyErr_Print();
  79. } else {
  80. /* Copy array data to local_stream */
  81. memcpy(local_stream, channels->data, len);
  82. /* Free the call's return value, probably Py_None */
  83. Py_DECREF(r);
  84. }
  85. /* Free arguments */
  86. Py_XDECREF(args);
  87. /* Free the channels */
  88. Py_XDECREF(channels);
  89. /* And leave the GIL */
  90. PyGILState_Release(gstate);
  91. return;
  92. }
  93. PyDoc_STRVAR(hello_doc, "Print hello message from Python module");
  94. /* Output hello message */
  95. static PyObject *pymod_hello(PyObject *self, PyObject *args)
  96. {
  97. PyObject *r = NULL;;
  98. if (PyArg_ParseTuple(args, "")) {
  99. printf("Hello from Python module\n");
  100. Py_INCREF(Py_None);
  101. r = Py_None;
  102. }
  103. return r;
  104. }
  105. PyDoc_STRVAR(_ysynth_init_doc, "Initialise SDL audio and return context");
  106. /* Initialise synthesizer */
  107. static PyObject *_ysynth_init(PyObject *self, PyObject *args)
  108. {
  109. PyObject *r = NULL;
  110. PyYSynthContext *ctx;
  111. int
  112. samplerate,
  113. channels;
  114. SDL_AudioSpec fmt, fmt_obtained;
  115. if (PyArg_ParseTuple(args, "ii;samplerate, channels", &samplerate, &channels)) {
  116. ctx = PyObject_NEW(PyYSynthContext, &PyYSynthContext_Type);
  117. r = (PyObject*)ctx;
  118. ctx->callback = NULL;
  119. /* Setup SDL Audio device */
  120. fmt.freq = samplerate;
  121. fmt.format = AUDIO_S16;
  122. fmt.channels = channels;
  123. fmt.samples = 512;
  124. fmt.callback = _sdl_audio_callback;
  125. fmt.userdata = r;
  126. /* Handle SDL error */
  127. if (SDL_OpenAudio(&fmt, &fmt_obtained) < 0) {
  128. PyErr_SetString(SDLError, SDL_GetError());
  129. Py_DECREF(r);
  130. return NULL;
  131. }
  132. SDL_PauseAudio(0);
  133. ctx->samplerate = fmt_obtained.freq;
  134. ctx->channels = fmt_obtained.channels;
  135. ctx->active = 1;
  136. }
  137. return r;
  138. }
  139. PyDoc_STRVAR(_ysynth_shutdown_doc, "Shutdown SDL audio device");
  140. /* Shutdown SDL audio */
  141. static PyObject *_ysynth_shutdown(PyObject *self, PyObject *args)
  142. {
  143. PyObject *r = NULL;
  144. PyYSynthContext *ctx;
  145. if (!PyArg_ParseTuple(args, "O!", &PyYSynthContext_Type, (PyObject**)&ctx))
  146. {
  147. ctx->active = 0;
  148. SDL_CloseAudio();
  149. Py_XDECREF(ctx->callback);
  150. r = Py_None;
  151. Py_INCREF(r);
  152. }
  153. return r;
  154. }
  155. PyDoc_STRVAR(_ysynth_set_callback_doc, "Set audio callback");
  156. /* Set Python audio callback */
  157. static PyObject *_ysynth_set_callback(PyObject *self, PyObject *args)
  158. {
  159. PyObject *r = NULL;
  160. PyYSynthContext *ctx;
  161. PyObject *callback;
  162. if (PyArg_ParseTuple(args, "O!O", &PyYSynthContext_Type, (PyObject**)&ctx,
  163. &callback)) {
  164. /* We don't have to lock SDL audio because Python's GIL will
  165. * have locked out the C callback for us, only this Python thread
  166. * can be executing at this time.
  167. */
  168. Py_XDECREF(ctx->callback);
  169. ctx->callback = callback;
  170. Py_INCREF(callback);
  171. r = Py_None;
  172. Py_INCREF(r);
  173. }
  174. return r;
  175. }
  176. PyDoc_STRVAR(_ysynth_get_callback_doc, "Get audio callback");
  177. /* Get Python audio callback */
  178. static PyObject *_ysynth_get_callback(PyObject *self, PyObject *args)
  179. {
  180. PyObject *r = NULL;
  181. PyYSynthContext *ctx;
  182. if (PyArg_ParseTuple(args, "O!", PyYSynthContext_Type, (PyObject**)&ctx)) {
  183. if (ctx->callback) {
  184. Py_INCREF(ctx->callback);
  185. r = ctx->callback;
  186. } else {
  187. r = Py_None;
  188. Py_INCREF(r);
  189. }
  190. }
  191. return r;
  192. }
  193. /* Exported methods */
  194. static PyMethodDef pymod_methods[] = {
  195. {"_ysynth_init", (PyCFunction)_ysynth_init,
  196. METH_VARARGS, _ysynth_init_doc},
  197. {"_ysynth_shutdown", (PyCFunction)_ysynth_shutdown,
  198. METH_VARARGS, _ysynth_shutdown_doc},
  199. {"_ysynth_set_callback", (PyCFunction)_ysynth_set_callback,
  200. METH_VARARGS, _ysynth_set_callback_doc},
  201. {"_ysynth_get_callback", (PyCFunction)_ysynth_get_callback,
  202. METH_VARARGS, _ysynth_get_callback_doc},
  203. {"hello", (PyCFunction)pymod_hello,
  204. METH_VARARGS, hello_doc},
  205. {NULL, NULL} /* sentinel */
  206. };
  207. /* Shutdown SDL */
  208. void exitpymod(void)
  209. {
  210. SDL_Quit();
  211. }
  212. PyDoc_STRVAR(module_doc, "Simple test module");
  213. /* Module initialisation, called by Python on import */
  214. PyMODINIT_FUNC
  215. initpymod(void)
  216. {
  217. PyObject *m;
  218. /* Setup SDL audio without signal handlers */
  219. if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE) < 0)
  220. return;
  221. /* Register exit handler
  222. * XXX: Later on try to use Python's atexit module?
  223. */
  224. atexit(exitpymod);
  225. puts("Hoi, module pymod loaded.");
  226. m = Py_InitModule3("pymod", pymod_methods, module_doc);
  227. if (m == NULL)
  228. return;
  229. /* Setup exceptions */
  230. SDLError = PyErr_NewException("_ysynth.SDLError", NULL, NULL);
  231. Py_INCREF(SDLError);
  232. PyModule_AddObject(m, "SDLError", SDLError);
  233. /* Our module requires the GIL to be available */
  234. if (!PyEval_ThreadsInitialized()) {
  235. puts("Initialising multithreading for Python");
  236. PyEval_InitThreads();
  237. }
  238. import_array();
  239. return;
  240. }