Python synthesizer stuff
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

614 lines
17KB

  1. #include <Python.h>
  2. #include <SDL/SDL.h>
  3. #include <numpy/arrayobject.h>
  4. #include <mpg123.h>
  5. /* SDL error handling */
  6. PyObject *SDLError;
  7. /* MPG123 error handling */
  8. PyObject *MP3Error;
  9. /* The synthesis context, used mainly
  10. * for storing audio settings and state.
  11. */
  12. typedef struct {
  13. PyObject_HEAD
  14. int
  15. samplerate,
  16. channels;
  17. int active;
  18. PyObject *callback;
  19. } PyYSynthContext;
  20. void PyYSynthContext_Dealloc(PyYSynthContext *self)
  21. {
  22. puts("_ysynth: Dealloc context");
  23. if (self->active) {
  24. /* Allow any running Python audio routines
  25. * to finish their current chunk, then lock out
  26. * the audio callback and shut the audio device
  27. * down.
  28. * It is important to first unlock the GIL otherwise
  29. * we're at risk of causing deadlock.
  30. */
  31. Py_BEGIN_ALLOW_THREADS
  32. SDL_LockAudio();
  33. SDL_CloseAudio();
  34. SDL_UnlockAudio();
  35. Py_END_ALLOW_THREADS
  36. self->active = 0;
  37. }
  38. Py_XDECREF(self->callback);
  39. return;
  40. }
  41. PyTypeObject PyYSynthContext_Type;
  42. PyTypeObject PyYSynthContext_Type = {
  43. PyVarObject_HEAD_INIT(NULL, 0)
  44. "_ysynth.ysynth context", /*tp_name*/
  45. sizeof(PyYSynthContext), /*tp_basicsize*/
  46. 0, /*tp_itemsize*/
  47. /* methods */
  48. (destructor)PyYSynthContext_Dealloc, /*tp_dealloc*/
  49. 0, /*tp_print*/
  50. 0, /*tp_getattr*/
  51. 0, /*tp_setattr*/
  52. 0, /*tp_compare*/
  53. 0, /*tp_repr*/
  54. 0, /*tp_as_number*/
  55. 0, /*tp_as_sequence*/
  56. 0, /*tp_as_mapping*/
  57. 0, /*tp_hash*/
  58. };
  59. /* SDL Audio callback */
  60. void _sdl_audio_callback(void *user, Uint8 *stream, int len)
  61. {
  62. PyYSynthContext *ctx = (PyYSynthContext*)user;
  63. PyObject *r, *args;
  64. NPY_AO *channels;
  65. PyGILState_STATE gstate;
  66. int local_len = len / sizeof(Sint16) / 2;
  67. Sint16 *local_stream = (Sint16*)stream;;
  68. npy_int dims[2] = {local_len, 2};
  69. /* This function is called from SDL's own thread, so we need to
  70. * register it with the Python interpreter before calling any
  71. * Python functions.
  72. */
  73. gstate = PyGILState_Ensure();
  74. /* Setup NumPy array for use within the Python synthesizer */
  75. channels = PyArray_New(&PyArray_Type, 2, dims, NPY_INT16, NULL, NULL, 2, 0, NULL);
  76. if (channels)
  77. args = Py_BuildValue("(O)", channels);
  78. /* Finally execute the callback */
  79. if (args)
  80. r = PyObject_CallObject(ctx->callback, (PyObject*)args);
  81. /* Print stacktrace if necessary */
  82. if (!r) {
  83. if (PyErr_Occurred())
  84. PyErr_Print();
  85. } else {
  86. /* Copy array data to local_stream */
  87. memcpy(local_stream, channels->data, len);
  88. /* Free the call's return value, probably Py_None */
  89. Py_DECREF(r);
  90. }
  91. /* Free arguments */
  92. Py_XDECREF(args);
  93. /* Free the channels */
  94. Py_XDECREF(channels);
  95. /* And leave the GIL */
  96. PyGILState_Release(gstate);
  97. return;
  98. }
  99. PyDoc_STRVAR(hello_doc, "Print hello message from Python module");
  100. /* Output hello message */
  101. static PyObject *pymod_hello(PyObject *self, PyObject *args)
  102. {
  103. PyObject *r = NULL;;
  104. if (PyArg_ParseTuple(args, "")) {
  105. printf("Hello from Python module\n");
  106. Py_INCREF(Py_None);
  107. r = Py_None;
  108. }
  109. return r;
  110. }
  111. PyDoc_STRVAR(_ysynth_init_doc, "Initialise SDL audio and return context");
  112. /* Initialise synthesizer */
  113. static PyObject *_ysynth_init(PyObject *self, PyObject *args)
  114. {
  115. PyObject *r = NULL;
  116. PyYSynthContext *ctx;
  117. int
  118. samplerate,
  119. channels;
  120. SDL_AudioSpec fmt, fmt_obtained;
  121. if (PyArg_ParseTuple(args, "ii;samplerate, channels", &samplerate, &channels)) {
  122. ctx = PyObject_NEW(PyYSynthContext, &PyYSynthContext_Type);
  123. r = (PyObject*)ctx;
  124. ctx->callback = NULL;
  125. /* Setup SDL Audio device */
  126. fmt.freq = samplerate;
  127. fmt.format = AUDIO_S16;
  128. fmt.channels = channels;
  129. fmt.samples = 512;
  130. fmt.callback = _sdl_audio_callback;
  131. fmt.userdata = r;
  132. /* Handle SDL error */
  133. if (SDL_OpenAudio(&fmt, &fmt_obtained) < 0) {
  134. PyErr_SetString(SDLError, SDL_GetError());
  135. Py_DECREF(r);
  136. return NULL;
  137. }
  138. SDL_PauseAudio(0);
  139. ctx->samplerate = fmt_obtained.freq;
  140. ctx->channels = fmt_obtained.channels;
  141. ctx->active = 1;
  142. }
  143. return r;
  144. }
  145. PyDoc_STRVAR(_ysynth_shutdown_doc, "Shutdown SDL audio device");
  146. /* Shutdown SDL audio */
  147. static PyObject *_ysynth_shutdown(PyObject *self, PyObject *args)
  148. {
  149. PyObject *r = NULL;
  150. PyYSynthContext *ctx;
  151. if (!PyArg_ParseTuple(args, "O!", &PyYSynthContext_Type, (PyObject**)&ctx))
  152. {
  153. ctx->active = 0;
  154. SDL_CloseAudio();
  155. Py_XDECREF(ctx->callback);
  156. r = Py_None;
  157. Py_INCREF(r);
  158. }
  159. return r;
  160. }
  161. PyDoc_STRVAR(_ysynth_set_callback_doc, "Set audio callback");
  162. /* Set Python audio callback */
  163. static PyObject *_ysynth_set_callback(PyObject *self, PyObject *args)
  164. {
  165. PyObject *r = NULL;
  166. PyYSynthContext *ctx;
  167. PyObject *callback;
  168. if (PyArg_ParseTuple(args, "O!O", &PyYSynthContext_Type, (PyObject**)&ctx,
  169. &callback)) {
  170. /* We don't have to lock SDL audio because Python's GIL will
  171. * have locked out the C callback for us, only this Python thread
  172. * can be executing at this time.
  173. */
  174. Py_XDECREF(ctx->callback);
  175. ctx->callback = callback;
  176. Py_INCREF(callback);
  177. r = Py_None;
  178. Py_INCREF(r);
  179. }
  180. return r;
  181. }
  182. PyDoc_STRVAR(_ysynth_get_callback_doc, "Get audio callback");
  183. /* Get Python audio callback */
  184. static PyObject *_ysynth_get_callback(PyObject *self, PyObject *args)
  185. {
  186. PyObject *r = NULL;
  187. PyYSynthContext *ctx;
  188. if (PyArg_ParseTuple(args, "O!", PyYSynthContext_Type, (PyObject**)&ctx)) {
  189. if (ctx->callback) {
  190. Py_INCREF(ctx->callback);
  191. r = ctx->callback;
  192. } else {
  193. r = Py_None;
  194. Py_INCREF(r);
  195. }
  196. }
  197. return r;
  198. }
  199. /* MP3 Support */
  200. typedef struct {
  201. PyObject_HEAD
  202. mpg123_handle *mh;
  203. int open, eof;
  204. long rate;
  205. int
  206. channels,
  207. encoding;
  208. } PyYMP3Context;
  209. void PyYMP3Context_Dealloc(PyYMP3Context *ctx)
  210. {
  211. puts("_ysynth: Dealloc mp3 context");
  212. /* Close if necessary */
  213. if (ctx->open)
  214. mpg123_close(ctx->mh);
  215. /* Finally free the MP3 decoder handle */
  216. if (ctx->mh) {
  217. mpg123_delete(ctx->mh);
  218. ctx->mh = NULL;
  219. }
  220. return;
  221. }
  222. PyTypeObject PyYMP3Context_Type;
  223. PyTypeObject PyYMP3Context_Type = {
  224. PyVarObject_HEAD_INIT(NULL, 0)
  225. "_ysynth.ymp3 context", /*tp_name*/
  226. sizeof(PyYMP3Context), /*tp_basicsize*/
  227. 0, /*tp_itemsize*/
  228. /* methods */
  229. (destructor)PyYMP3Context_Dealloc, /*tp_dealloc*/
  230. 0, /*tp_print*/
  231. 0, /*tp_getattr*/
  232. 0, /*tp_setattr*/
  233. 0, /*tp_compare*/
  234. 0, /*tp_repr*/
  235. 0, /*tp_as_number*/
  236. 0, /*tp_as_sequence*/
  237. 0, /*tp_as_mapping*/
  238. 0, /*tp_hash*/
  239. };
  240. PyDoc_STRVAR(_ysynth_mp3_open_doc, "Open MP3 file and return context,"
  241. " raises exception if file does not exists\n"
  242. "Args:\n"
  243. " filename: Path string to file\n"
  244. "Returns:\n"
  245. " MP3 context");
  246. /* Open MP3 file */
  247. static PyObject *_ysynth_mp3_open(PyObject *self, PyObject *args)
  248. {
  249. PyObject *r = NULL;
  250. char *filename;
  251. PyYMP3Context *ctx;
  252. mpg123_handle *mh;
  253. int err;
  254. if (PyArg_ParseTuple(args, "s;filename", &filename)) {
  255. /* Try to open MP3 */
  256. mh = mpg123_new(NULL, &err);
  257. if (!mh) {
  258. PyErr_SetString(MP3Error, mpg123_plain_strerror(err));
  259. return NULL;
  260. }
  261. /* Setup float samples */
  262. mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_FORCE_FLOAT, 0.);
  263. /* Open the actual MP3 file */
  264. if (mpg123_open(mh, filename) != MPG123_OK) {
  265. PyErr_SetString(MP3Error, mpg123_strerror(mh));
  266. mpg123_delete(mh);
  267. return NULL;
  268. }
  269. /* Create object */
  270. ctx = PyObject_NEW(PyYMP3Context, &PyYMP3Context_Type);
  271. ctx->open = 1;
  272. ctx->eof = 0;
  273. ctx->mh = mh;
  274. r = (PyObject*)ctx;
  275. if (!ctx) {
  276. mpg123_close(mh);
  277. mpg123_delete(mh);
  278. return NULL;
  279. }
  280. /* Check-out file stream format */
  281. mpg123_getformat(mh, &ctx->rate, &ctx->channels, &ctx->encoding);
  282. if (ctx->encoding != MPG123_ENC_FLOAT_32) {
  283. PyErr_SetString(MP3Error,
  284. "Got non-float encoder while forcing float");
  285. Py_DECREF(ctx);
  286. return NULL;
  287. }
  288. /* Now don't allow the format to change.
  289. *
  290. * FIXME: Later on we probably do want to change this, as to
  291. * allow YSynth to handle any changes in samplerate instead of
  292. * allowing MPG123's crude resampler to do the job.
  293. */
  294. mpg123_format_none(mh);
  295. mpg123_format(mh, ctx->rate, ctx->channels, ctx->encoding);
  296. }
  297. return r;
  298. }
  299. PyDoc_STRVAR(_ysynth_mp3_close_doc, "Close MP3 file context\n"
  300. "Args:\n"
  301. " context: MP3 context\n"
  302. "Returns:\n"
  303. " None, but raises an MP3Error if the file has already been closed");
  304. /* Close MP3 file */
  305. static PyObject *_ysynth_mp3_close(PyObject *self, PyObject *args)
  306. {
  307. PyObject *r = NULL;
  308. PyYMP3Context *ctx;
  309. if (PyArg_ParseTuple(args, "O!", &PyYMP3Context_Type, (PyObject**)&ctx)) {
  310. if (ctx->open) {
  311. ctx->open = 0;
  312. mpg123_close(ctx->mh);
  313. r = Py_None;
  314. Py_INCREF(r);
  315. } else {
  316. PyErr_SetString(MP3Error, "File already closed");
  317. r = NULL;
  318. }
  319. }
  320. return r;
  321. }
  322. PyDoc_STRVAR(_ysynth_mp3_get_format_doc, "Get MP3 format configuration\n"
  323. "Args: None\n"
  324. "Returns:\n"
  325. " Tuple containing: (samplerate, channels)");
  326. /* Read MP3 data */
  327. static PyObject *_ysynth_mp3_get_format(PyObject *self, PyObject *args)
  328. {
  329. PyObject *r = NULL;
  330. PyYMP3Context *ctx;
  331. if (PyArg_ParseTuple(args, "O!", &PyYMP3Context_Type, (PyObject**)&ctx)) {
  332. if (ctx->open) {
  333. r = Py_BuildValue("(ii)", ctx->rate, ctx->channels);
  334. } else {
  335. PyErr_SetString(MP3Error, "File has been closed");
  336. }
  337. }
  338. return r;
  339. }
  340. PyDoc_STRVAR(_ysynth_mp3_read_doc, "Read MP3 data\n"
  341. "Args:\n"
  342. " context: Open MP3 context\n"
  343. " samples: Number of samples to decode\n"
  344. "Returns:\n"
  345. " A NumPy array of shape (samples, channels)\n"
  346. " This function raises an MP3Error on decode failure.\n"
  347. " It shall output silence on EOF");
  348. /* Read MP3 data */
  349. static PyObject *_ysynth_mp3_read(PyObject *self, PyObject *args)
  350. {
  351. PyObject *r = NULL;
  352. PyYMP3Context *ctx;
  353. int samples, bytes_read;
  354. int err;
  355. npy_int dims[2];
  356. NPY_AO *stream;
  357. if (PyArg_ParseTuple(args, "O!i", &PyYMP3Context_Type, (PyObject**)&ctx, &samples)) {
  358. if (ctx->open) {
  359. /* Allocate output array */
  360. dims[0] = samples;
  361. dims[1] = ctx->channels;
  362. stream = PyArray_New(&PyArray_Type, 2, dims, NPY_FLOAT32, NULL, NULL, 4, 0, NULL);
  363. if (!stream)
  364. return NULL;
  365. r = (PyObject*)stream;
  366. /* EOF Generates a silence stream */
  367. if (ctx->eof) {
  368. memset(PyArray_DATA(stream), 0x0, PyArray_NBYTES(stream));
  369. } else {
  370. /* Read the next set of MP3 samples */
  371. err = mpg123_read(ctx->mh, PyArray_DATA(stream),
  372. PyArray_NBYTES(stream), &bytes_read);
  373. /* Handle any weird errors */
  374. if (err != MPG123_OK) {
  375. if (err == MPG123_DONE) {
  376. ctx->eof = 1;
  377. } else {
  378. Py_DECREF(stream);
  379. PyErr_SetString(MP3Error, mpg123_strerror(ctx->mh));
  380. return NULL;
  381. }
  382. }
  383. /* Fill any missing data with silence, and of course complain
  384. * whenever necessary
  385. */
  386. if (bytes_read != PyArray_NBYTES(stream)) {
  387. if (!ctx->eof)
  388. fprintf(stderr, "warning: MPG123 did not return"
  389. " enough data but did not set MPG123_DONE"
  390. " either..\n");
  391. memset(PyArray_BYTES(stream) + bytes_read, 0x0,
  392. PyArray_NBYTES(stream) - bytes_read);
  393. }
  394. }
  395. } else {
  396. PyErr_SetString(MP3Error, "File has been closed");
  397. }
  398. }
  399. return r;
  400. }
  401. /* TODO: Let's use the same strategy NumPy uses for its built-ins, provide
  402. * the function signature in the docstrings
  403. */
  404. PyDoc_STRVAR(_ysynth_mp3_seek_doc, "Seek to position in MP3\n"
  405. "Args:\n"
  406. " context: Open MP3 context, providing somehting else\n"
  407. " will result in an exception\n"
  408. " position: Integer position in samples to seek to\n"
  409. "Returns:\n"
  410. " None, this functions raises an MP3Error when errors are encountered");
  411. /* Seek MP3 data */
  412. static PyObject *_ysynth_mp3_seek(PyObject *self, PyObject *args)
  413. {
  414. PyObject *r = NULL;
  415. PyYMP3Context *ctx;
  416. int sample;
  417. int err;
  418. if (PyArg_ParseTuple(args, "O!i;context, position", &PyYMP3Context_Type,
  419. (PyObject**)&ctx, &sample)) {
  420. if (ctx->open) {
  421. err = mpg123_seek(ctx->mh, sample, SEEK_SET);
  422. if (err < 0) {
  423. PyErr_SetString(MP3Error, mpg123_strerror(ctx->mh));
  424. } else {
  425. r = Py_None;
  426. Py_INCREF(r);
  427. }
  428. } else {
  429. PyErr_SetString(MP3Error, "File has been closed");
  430. }
  431. }
  432. return r;
  433. }
  434. /* Exported methods */
  435. static PyMethodDef pymod_methods[] = {
  436. {"_ysynth_init", (PyCFunction)_ysynth_init,
  437. METH_VARARGS, _ysynth_init_doc},
  438. {"_ysynth_shutdown", (PyCFunction)_ysynth_shutdown,
  439. METH_VARARGS, _ysynth_shutdown_doc},
  440. {"_ysynth_set_callback", (PyCFunction)_ysynth_set_callback,
  441. METH_VARARGS, _ysynth_set_callback_doc},
  442. {"_ysynth_get_callback", (PyCFunction)_ysynth_get_callback,
  443. METH_VARARGS, _ysynth_get_callback_doc},
  444. {"_ysynth_mp3_open", (PyCFunction)_ysynth_mp3_open,
  445. METH_VARARGS, _ysynth_mp3_open_doc},
  446. {"_ysynth_mp3_close", (PyCFunction)_ysynth_mp3_close,
  447. METH_VARARGS, _ysynth_mp3_close_doc},
  448. {"_ysynth_mp3_get_format", (PyCFunction)_ysynth_mp3_get_format,
  449. METH_VARARGS, _ysynth_mp3_get_format_doc},
  450. {"_ysynth_mp3_read", (PyCFunction)_ysynth_mp3_read,
  451. METH_VARARGS, _ysynth_mp3_read_doc},
  452. {"_ysynth_mp3_seek", (PyCFunction)_ysynth_mp3_seek,
  453. METH_VARARGS, _ysynth_mp3_seek_doc},
  454. {"hello", (PyCFunction)pymod_hello,
  455. METH_VARARGS, hello_doc},
  456. {NULL, NULL} /* sentinel */
  457. };
  458. /* Shutdown SDL/MPG123 */
  459. void exitpymod(void)
  460. {
  461. mpg123_exit();
  462. /* XXX: Should we check for successful initialisation of SDL? */
  463. SDL_Quit();
  464. }
  465. PyDoc_STRVAR(module_doc, "Simple test module");
  466. /* Module initialisation, called by Python on import */
  467. PyMODINIT_FUNC
  468. initpymod(void)
  469. {
  470. PyObject *m;
  471. int err;
  472. /* Setup SDL audio without signal handlers */
  473. if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE) < 0)
  474. return;
  475. #if 1
  476. /* Setup MPG123 */
  477. if ((err = mpg123_init()) != MPG123_OK) {
  478. fprintf(stderr, "Failure while initialising mgp123: %s\n",
  479. mpg123_plain_strerror(err));
  480. return;
  481. }
  482. #endif
  483. /* Register exit handler
  484. * XXX: Later on try to use Python's atexit module?
  485. */
  486. atexit(exitpymod);
  487. puts("Hoi, module pymod loaded.");
  488. m = Py_InitModule3("pymod", pymod_methods, module_doc);
  489. if (m == NULL)
  490. return;
  491. /* Setup exceptions */
  492. SDLError = PyErr_NewException("_ysynth.SDLError", NULL, NULL);
  493. Py_INCREF(SDLError);
  494. PyModule_AddObject(m, "SDLError", SDLError);
  495. MP3Error = PyErr_NewException("_ysynth.MP3Error", NULL, NULL);
  496. Py_INCREF(MP3Error);
  497. PyModule_AddObject(m, "MP3Error", MP3Error);
  498. /* Setup types */
  499. if (PyType_Ready(&PyYSynthContext_Type) < 0)
  500. return;
  501. if (PyType_Ready(&PyYMP3Context_Type) < 0)
  502. return;
  503. /* Our module requires the GIL to be available */
  504. if (!PyEval_ThreadsInitialized()) {
  505. puts("Initialising multithreading for Python");
  506. PyEval_InitThreads();
  507. }
  508. import_array();
  509. return;
  510. }