/Modules/sunaudiodev.c

http://unladen-swallow.googlecode.com/ · C · 467 lines · 381 code · 66 blank · 20 comment · 50 complexity · 27391aab7749750f88854ef9b030802a MD5 · raw file

  1. /* Sad objects */
  2. #include "Python.h"
  3. #include "structmember.h"
  4. #ifdef HAVE_SYS_AUDIOIO_H
  5. #define SOLARIS
  6. #endif
  7. #ifdef HAVE_FCNTL_H
  8. #include <fcntl.h>
  9. #endif
  10. #include <stropts.h>
  11. #include <sys/ioctl.h>
  12. #ifdef SOLARIS
  13. #include <sys/audioio.h>
  14. #else
  15. #include <sun/audioio.h>
  16. #endif
  17. /* #define offsetof(str,mem) ((int)(((str *)0)->mem)) */
  18. typedef struct {
  19. PyObject_HEAD
  20. int x_fd; /* The open file */
  21. int x_icount; /* # samples read */
  22. int x_ocount; /* # samples written */
  23. int x_isctl; /* True if control device */
  24. } sadobject;
  25. typedef struct {
  26. PyObject_HEAD
  27. audio_info_t ai;
  28. } sadstatusobject;
  29. static PyTypeObject Sadtype;
  30. static PyTypeObject Sadstatustype;
  31. static sadstatusobject *sads_alloc(void); /* Forward */
  32. static PyObject *SunAudioError;
  33. #define is_sadobject(v) (Py_TYPE(v) == &Sadtype)
  34. #define is_sadstatusobject(v) (Py_TYPE(v) == &Sadstatustype)
  35. static sadobject *
  36. newsadobject(PyObject *args)
  37. {
  38. sadobject *xp;
  39. int fd;
  40. char *mode;
  41. int imode;
  42. char* basedev;
  43. char* ctldev;
  44. char* opendev;
  45. /* Check arg for r/w/rw */
  46. if (!PyArg_ParseTuple(args, "s", &mode))
  47. return NULL;
  48. if (strcmp(mode, "r") == 0)
  49. imode = 0;
  50. else if (strcmp(mode, "w") == 0)
  51. imode = 1;
  52. else if (strcmp(mode, "rw") == 0)
  53. imode = 2;
  54. else if (strcmp(mode, "control") == 0)
  55. imode = -1;
  56. else {
  57. PyErr_SetString(SunAudioError,
  58. "Mode should be one of 'r', 'w', 'rw' or 'control'");
  59. return NULL;
  60. }
  61. /* Open the correct device. The base device name comes from the
  62. * AUDIODEV environment variable first, then /dev/audio. The
  63. * control device tacks "ctl" onto the base device name.
  64. */
  65. basedev = getenv("AUDIODEV");
  66. if (!basedev)
  67. basedev = "/dev/audio";
  68. ctldev = PyMem_NEW(char, strlen(basedev) + 4);
  69. if (!ctldev) {
  70. PyErr_NoMemory();
  71. return NULL;
  72. }
  73. strcpy(ctldev, basedev);
  74. strcat(ctldev, "ctl");
  75. if (imode < 0) {
  76. opendev = ctldev;
  77. fd = open(ctldev, 2);
  78. }
  79. else {
  80. opendev = basedev;
  81. fd = open(basedev, imode);
  82. }
  83. if (fd < 0) {
  84. PyErr_SetFromErrnoWithFilename(SunAudioError, opendev);
  85. PyMem_DEL(ctldev);
  86. return NULL;
  87. }
  88. PyMem_DEL(ctldev);
  89. /* Create and initialize the object */
  90. xp = PyObject_New(sadobject, &Sadtype);
  91. if (xp == NULL) {
  92. close(fd);
  93. return NULL;
  94. }
  95. xp->x_fd = fd;
  96. xp->x_icount = xp->x_ocount = 0;
  97. xp->x_isctl = (imode < 0);
  98. return xp;
  99. }
  100. /* Sad methods */
  101. static void
  102. sad_dealloc(sadobject *xp)
  103. {
  104. close(xp->x_fd);
  105. PyObject_Del(xp);
  106. }
  107. static PyObject *
  108. sad_read(sadobject *self, PyObject *args)
  109. {
  110. int size, count;
  111. char *cp;
  112. PyObject *rv;
  113. if (!PyArg_ParseTuple(args, "i:read", &size))
  114. return NULL;
  115. rv = PyString_FromStringAndSize(NULL, size);
  116. if (rv == NULL)
  117. return NULL;
  118. if (!(cp = PyString_AsString(rv)))
  119. goto finally;
  120. count = read(self->x_fd, cp, size);
  121. if (count < 0) {
  122. PyErr_SetFromErrno(SunAudioError);
  123. goto finally;
  124. }
  125. #if 0
  126. /* TBD: why print this message if you can handle the condition?
  127. * assume it's debugging info which we can just as well get rid
  128. * of. in any case this message should *not* be using printf!
  129. */
  130. if (count != size)
  131. printf("sunaudio: funny read rv %d wtd %d\n", count, size);
  132. #endif
  133. self->x_icount += count;
  134. return rv;
  135. finally:
  136. Py_DECREF(rv);
  137. return NULL;
  138. }
  139. static PyObject *
  140. sad_write(sadobject *self, PyObject *args)
  141. {
  142. char *cp;
  143. int count, size;
  144. if (!PyArg_ParseTuple(args, "s#:write", &cp, &size))
  145. return NULL;
  146. count = write(self->x_fd, cp, size);
  147. if (count < 0) {
  148. PyErr_SetFromErrno(SunAudioError);
  149. return NULL;
  150. }
  151. #if 0
  152. if (count != size)
  153. printf("sunaudio: funny write rv %d wanted %d\n", count, size);
  154. #endif
  155. self->x_ocount += count;
  156. Py_INCREF(Py_None);
  157. return Py_None;
  158. }
  159. static PyObject *
  160. sad_getinfo(sadobject *self)
  161. {
  162. sadstatusobject *rv;
  163. if (!(rv = sads_alloc()))
  164. return NULL;
  165. if (ioctl(self->x_fd, AUDIO_GETINFO, &rv->ai) < 0) {
  166. PyErr_SetFromErrno(SunAudioError);
  167. Py_DECREF(rv);
  168. return NULL;
  169. }
  170. return (PyObject *)rv;
  171. }
  172. static PyObject *
  173. sad_setinfo(sadobject *self, sadstatusobject *arg)
  174. {
  175. if (!is_sadstatusobject(arg)) {
  176. PyErr_SetString(PyExc_TypeError,
  177. "Must be sun audio status object");
  178. return NULL;
  179. }
  180. if (ioctl(self->x_fd, AUDIO_SETINFO, &arg->ai) < 0) {
  181. PyErr_SetFromErrno(SunAudioError);
  182. return NULL;
  183. }
  184. Py_INCREF(Py_None);
  185. return Py_None;
  186. }
  187. static PyObject *
  188. sad_ibufcount(sadobject *self)
  189. {
  190. audio_info_t ai;
  191. if (ioctl(self->x_fd, AUDIO_GETINFO, &ai) < 0) {
  192. PyErr_SetFromErrno(SunAudioError);
  193. return NULL;
  194. }
  195. return PyInt_FromLong(ai.record.samples - self->x_icount);
  196. }
  197. static PyObject *
  198. sad_obufcount(sadobject *self)
  199. {
  200. audio_info_t ai;
  201. if (ioctl(self->x_fd, AUDIO_GETINFO, &ai) < 0) {
  202. PyErr_SetFromErrno(SunAudioError);
  203. return NULL;
  204. }
  205. /* x_ocount is in bytes, whereas play.samples is in frames */
  206. /* we want frames */
  207. return PyInt_FromLong(self->x_ocount / (ai.play.channels *
  208. ai.play.precision / 8) -
  209. ai.play.samples);
  210. }
  211. static PyObject *
  212. sad_drain(sadobject *self)
  213. {
  214. if (ioctl(self->x_fd, AUDIO_DRAIN, 0) < 0) {
  215. PyErr_SetFromErrno(SunAudioError);
  216. return NULL;
  217. }
  218. Py_INCREF(Py_None);
  219. return Py_None;
  220. }
  221. #ifdef SOLARIS
  222. static PyObject *
  223. sad_getdev(sadobject *self)
  224. {
  225. struct audio_device ad;
  226. if (ioctl(self->x_fd, AUDIO_GETDEV, &ad) < 0) {
  227. PyErr_SetFromErrno(SunAudioError);
  228. return NULL;
  229. }
  230. return Py_BuildValue("(sss)", ad.name, ad.version, ad.config);
  231. }
  232. #endif
  233. static PyObject *
  234. sad_flush(sadobject *self)
  235. {
  236. if (ioctl(self->x_fd, I_FLUSH, FLUSHW) < 0) {
  237. PyErr_SetFromErrno(SunAudioError);
  238. return NULL;
  239. }
  240. Py_INCREF(Py_None);
  241. return Py_None;
  242. }
  243. static PyObject *
  244. sad_close(sadobject *self)
  245. {
  246. if (self->x_fd >= 0) {
  247. close(self->x_fd);
  248. self->x_fd = -1;
  249. }
  250. Py_INCREF(Py_None);
  251. return Py_None;
  252. }
  253. static PyObject *
  254. sad_fileno(sadobject *self)
  255. {
  256. return PyInt_FromLong(self->x_fd);
  257. }
  258. static PyMethodDef sad_methods[] = {
  259. { "read", (PyCFunction)sad_read, METH_VARARGS },
  260. { "write", (PyCFunction)sad_write, METH_VARARGS },
  261. { "ibufcount", (PyCFunction)sad_ibufcount, METH_NOARGS },
  262. { "obufcount", (PyCFunction)sad_obufcount, METH_NOARGS },
  263. #define CTL_METHODS 4
  264. { "getinfo", (PyCFunction)sad_getinfo, METH_NOARGS },
  265. { "setinfo", (PyCFunction)sad_setinfo, METH_O},
  266. { "drain", (PyCFunction)sad_drain, METH_NOARGS },
  267. { "flush", (PyCFunction)sad_flush, METH_NOARGS },
  268. #ifdef SOLARIS
  269. { "getdev", (PyCFunction)sad_getdev, METH_NOARGS },
  270. #endif
  271. { "close", (PyCFunction)sad_close, METH_NOARGS },
  272. { "fileno", (PyCFunction)sad_fileno, METH_NOARGS },
  273. {NULL, NULL} /* sentinel */
  274. };
  275. static PyObject *
  276. sad_getattr(sadobject *xp, char *name)
  277. {
  278. if (xp->x_isctl)
  279. return Py_FindMethod(sad_methods+CTL_METHODS,
  280. (PyObject *)xp, name);
  281. else
  282. return Py_FindMethod(sad_methods, (PyObject *)xp, name);
  283. }
  284. /* ----------------------------------------------------------------- */
  285. static sadstatusobject *
  286. sads_alloc(void) {
  287. return PyObject_New(sadstatusobject, &Sadstatustype);
  288. }
  289. static void
  290. sads_dealloc(sadstatusobject *xp)
  291. {
  292. PyMem_DEL(xp);
  293. }
  294. #define OFF(x) offsetof(audio_info_t,x)
  295. static struct memberlist sads_ml[] = {
  296. { "i_sample_rate", T_UINT, OFF(record.sample_rate) },
  297. { "i_channels", T_UINT, OFF(record.channels) },
  298. { "i_precision", T_UINT, OFF(record.precision) },
  299. { "i_encoding", T_UINT, OFF(record.encoding) },
  300. { "i_gain", T_UINT, OFF(record.gain) },
  301. { "i_port", T_UINT, OFF(record.port) },
  302. { "i_samples", T_UINT, OFF(record.samples) },
  303. { "i_eof", T_UINT, OFF(record.eof) },
  304. { "i_pause", T_UBYTE, OFF(record.pause) },
  305. { "i_error", T_UBYTE, OFF(record.error) },
  306. { "i_waiting", T_UBYTE, OFF(record.waiting) },
  307. { "i_open", T_UBYTE, OFF(record.open) , RO},
  308. { "i_active", T_UBYTE, OFF(record.active) , RO},
  309. #ifdef SOLARIS
  310. { "i_buffer_size", T_UINT, OFF(record.buffer_size) },
  311. { "i_balance", T_UBYTE, OFF(record.balance) },
  312. { "i_avail_ports", T_UINT, OFF(record.avail_ports) },
  313. #endif
  314. { "o_sample_rate", T_UINT, OFF(play.sample_rate) },
  315. { "o_channels", T_UINT, OFF(play.channels) },
  316. { "o_precision", T_UINT, OFF(play.precision) },
  317. { "o_encoding", T_UINT, OFF(play.encoding) },
  318. { "o_gain", T_UINT, OFF(play.gain) },
  319. { "o_port", T_UINT, OFF(play.port) },
  320. { "o_samples", T_UINT, OFF(play.samples) },
  321. { "o_eof", T_UINT, OFF(play.eof) },
  322. { "o_pause", T_UBYTE, OFF(play.pause) },
  323. { "o_error", T_UBYTE, OFF(play.error) },
  324. { "o_waiting", T_UBYTE, OFF(play.waiting) },
  325. { "o_open", T_UBYTE, OFF(play.open) , RO},
  326. { "o_active", T_UBYTE, OFF(play.active) , RO},
  327. #ifdef SOLARIS
  328. { "o_buffer_size", T_UINT, OFF(play.buffer_size) },
  329. { "o_balance", T_UBYTE, OFF(play.balance) },
  330. { "o_avail_ports", T_UINT, OFF(play.avail_ports) },
  331. #endif
  332. { "monitor_gain", T_UINT, OFF(monitor_gain) },
  333. { NULL, 0, 0},
  334. };
  335. static PyObject *
  336. sads_getattr(sadstatusobject *xp, char *name)
  337. {
  338. return PyMember_Get((char *)&xp->ai, sads_ml, name);
  339. }
  340. static int
  341. sads_setattr(sadstatusobject *xp, char *name, PyObject *v)
  342. {
  343. if (v == NULL) {
  344. PyErr_SetString(PyExc_TypeError,
  345. "can't delete sun audio status attributes");
  346. return -1;
  347. }
  348. return PyMember_Set((char *)&xp->ai, sads_ml, name, v);
  349. }
  350. /* ------------------------------------------------------------------- */
  351. static PyTypeObject Sadtype = {
  352. PyVarObject_HEAD_INIT(&PyType_Type, 0)
  353. "sunaudiodev.sun_audio_device", /*tp_name*/
  354. sizeof(sadobject), /*tp_size*/
  355. 0, /*tp_itemsize*/
  356. /* methods */
  357. (destructor)sad_dealloc, /*tp_dealloc*/
  358. 0, /*tp_print*/
  359. (getattrfunc)sad_getattr, /*tp_getattr*/
  360. 0, /*tp_setattr*/
  361. 0, /*tp_compare*/
  362. 0, /*tp_repr*/
  363. };
  364. static PyTypeObject Sadstatustype = {
  365. PyVarObject_HEAD_INIT(&PyType_Type, 0)
  366. "sunaudiodev.sun_audio_device_status", /*tp_name*/
  367. sizeof(sadstatusobject), /*tp_size*/
  368. 0, /*tp_itemsize*/
  369. /* methods */
  370. (destructor)sads_dealloc, /*tp_dealloc*/
  371. 0, /*tp_print*/
  372. (getattrfunc)sads_getattr, /*tp_getattr*/
  373. (setattrfunc)sads_setattr, /*tp_setattr*/
  374. 0, /*tp_compare*/
  375. 0, /*tp_repr*/
  376. };
  377. /* ------------------------------------------------------------------- */
  378. static PyObject *
  379. sadopen(PyObject *self, PyObject *args)
  380. {
  381. return (PyObject *)newsadobject(args);
  382. }
  383. static PyMethodDef sunaudiodev_methods[] = {
  384. { "open", sadopen, METH_VARARGS },
  385. { 0, 0 },
  386. };
  387. void
  388. initsunaudiodev(void)
  389. {
  390. PyObject *m, *d;
  391. if (PyErr_WarnPy3k("the sunaudiodev module has been removed in "
  392. "Python 3.0", 2) < 0)
  393. return;
  394. m = Py_InitModule("sunaudiodev", sunaudiodev_methods);
  395. if (m == NULL)
  396. return;
  397. d = PyModule_GetDict(m);
  398. SunAudioError = PyErr_NewException("sunaudiodev.error", NULL, NULL);
  399. if (SunAudioError)
  400. PyDict_SetItemString(d, "error", SunAudioError);
  401. }