blob: b0677efb3308288e5d85faa800a9c8f45ea2a705 [file] [log] [blame]
Guido van Rossumb130dc72000-03-30 23:25:49 +00001/* Hey Emacs, this is -*-C-*-
2 ******************************************************************************
3 * linuxaudiodev.c -- Linux audio device for python.
4 *
5 * Author : Peter Bosch
6 * Created On : Thu Mar 2 21:10:33 2000
7 * Last Modified By: Peter Bosch
8 * Last Modified On: Fri Mar 24 11:27:00 2000
9 * Status : Unknown, Use with caution!
10 *
11 * Unless other notices are present in any part of this file
12 * explicitly claiming copyrights for other people and/or
13 * organizations, the contents of this file is fully copyright
14 * (C) 2000 Peter Bosch, all rights reserved.
15 ******************************************************************************
16 */
17
18#include "Python.h"
19#include "structmember.h"
20
21#ifdef HAVE_UNISTD_H
22#include <unistd.h>
23#endif
24
25#ifdef HAVE_FCNTL_H
26#include <fcntl.h>
27#endif
28
Guido van Rossumb130dc72000-03-30 23:25:49 +000029#include <sys/ioctl.h>
30#include <linux/soundcard.h>
31
32typedef unsigned long uint32_t;
33
34typedef struct {
Fred Drakeda940d82000-07-08 06:05:58 +000035 PyObject_HEAD;
36 int x_fd; /* The open file */
37 int x_icount; /* Input count */
38 int x_ocount; /* Output count */
39 uint32_t x_afmts; /* Supported audio formats */
Guido van Rossumb130dc72000-03-30 23:25:49 +000040} lad_t;
41
42static struct {
Fred Drakeda940d82000-07-08 06:05:58 +000043 int a_bps;
44 uint32_t a_fmt;
Guido van Rossumb130dc72000-03-30 23:25:49 +000045} audio_types[] = {
Fred Drakeda940d82000-07-08 06:05:58 +000046 { 8, AFMT_MU_LAW },
47 { 8, AFMT_U8 },
48 { 8, AFMT_S8 },
49 { 16, AFMT_U16_BE },
50 { 16, AFMT_U16_LE },
51 { 16, AFMT_S16_BE },
52 { 16, AFMT_S16_LE },
Guido van Rossumb130dc72000-03-30 23:25:49 +000053};
54
55
56staticforward PyTypeObject Ladtype;
57
58static PyObject *LinuxAudioError;
59
60static lad_t *
61newladobject(PyObject *arg)
62{
Fred Drakeda940d82000-07-08 06:05:58 +000063 lad_t *xp;
64 int fd, afmts, imode;
65 char *mode;
66 char *basedev;
Guido van Rossumb130dc72000-03-30 23:25:49 +000067
Fred Drakeda940d82000-07-08 06:05:58 +000068 /* Check arg for r/w/rw */
69 if (!PyArg_ParseTuple(arg, "s:open", &mode)) return NULL;
70 if (strcmp(mode, "r") == 0)
71 imode = 0;
72 else if (strcmp(mode, "w") == 0)
73 imode = 1;
74 else {
75 PyErr_SetString(LinuxAudioError, "Mode should be one of 'r', or 'w'");
76 return NULL;
Guido van Rossumb130dc72000-03-30 23:25:49 +000077 }
Guido van Rossumb130dc72000-03-30 23:25:49 +000078
Fred Drakeda940d82000-07-08 06:05:58 +000079 /* Open the correct device. The base device name comes from the
80 * AUDIODEV environment variable first, then /dev/audio. The
81 * control device tacks "ctl" onto the base device name.
82 */
83 basedev = getenv("AUDIODEV");
84 if (!basedev)
85 basedev = "/dev/dsp";
Guido van Rossumb130dc72000-03-30 23:25:49 +000086
Fred Drakeda940d82000-07-08 06:05:58 +000087 if ((fd = open(basedev, imode)) < 0) {
88 PyErr_SetFromErrnoWithFilename(LinuxAudioError, basedev);
89 return NULL;
90 }
91 if (imode && ioctl(fd, SNDCTL_DSP_NONBLOCK, NULL) < 0) {
92 PyErr_SetFromErrnoWithFilename(LinuxAudioError, basedev);
93 return NULL;
94 }
95 if (ioctl(fd, SNDCTL_DSP_GETFMTS, &afmts) < 0) {
96 PyErr_SetFromErrnoWithFilename(LinuxAudioError, basedev);
97 return NULL;
98 }
99 /* Create and initialize the object */
100 if ((xp = PyObject_New(lad_t, &Ladtype)) == NULL) {
101 close(fd);
102 return NULL;
103 }
104 xp->x_fd = fd;
105 xp->x_icount = xp->x_ocount = 0;
106 xp->x_afmts = afmts;
107 return xp;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000108}
109
110static void
111lad_dealloc(lad_t *xp)
112{
Fred Drakeda940d82000-07-08 06:05:58 +0000113 close(xp->x_fd);
114 PyObject_Del(xp);
Guido van Rossumb130dc72000-03-30 23:25:49 +0000115}
116
117static PyObject *
118lad_read(lad_t *self, PyObject *args)
119{
Fred Drakeda940d82000-07-08 06:05:58 +0000120 int size, count;
121 char *cp;
122 PyObject *rv;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000123
Fred Drakeda940d82000-07-08 06:05:58 +0000124 if (!PyArg_ParseTuple(args, "i:read", &size))
125 return NULL;
126 rv = PyString_FromStringAndSize(NULL, size);
127 if (rv == NULL)
128 return NULL;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000129
Fred Drakeda940d82000-07-08 06:05:58 +0000130 if (!(cp = PyString_AsString(rv))) {
131 Py_DECREF(rv);
132 return NULL;
133 }
134 if ((count = read(self->x_fd, cp, size)) < 0) {
135 PyErr_SetFromErrno(LinuxAudioError);
136 Py_DECREF(rv);
137 return NULL;
138 }
139 self->x_icount += count;
140 return rv;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000141}
142
143static PyObject *
144lad_write(lad_t *self, PyObject *args)
145{
Fred Drakeda940d82000-07-08 06:05:58 +0000146 char *cp;
147 int rv, size;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000148
Fred Drakeda940d82000-07-08 06:05:58 +0000149 if (!PyArg_ParseTuple(args, "s#:write", &cp, &size)) return NULL;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000150
Fred Drakeda940d82000-07-08 06:05:58 +0000151 while (size > 0) {
152 if ((rv = write(self->x_fd, cp, size)) < 0) {
153 PyErr_SetFromErrno(LinuxAudioError);
154 return NULL;
155 }
156 self->x_ocount += rv;
157 size -= rv;
158 cp += rv;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000159 }
Fred Drakeda940d82000-07-08 06:05:58 +0000160 Py_INCREF(Py_None);
161 return Py_None;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000162}
163
164static PyObject *
165lad_close(lad_t *self, PyObject *args)
166{
Fred Drakeda940d82000-07-08 06:05:58 +0000167 if (!PyArg_ParseTuple(args, ":close")) return NULL;
168 if (self->x_fd >= 0) {
169 close(self->x_fd);
170 self->x_fd = -1;
171 }
172 Py_INCREF(Py_None);
173 return Py_None;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000174}
175
176static PyObject *
177lad_fileno(lad_t *self, PyObject *args)
178{
Fred Drakeda940d82000-07-08 06:05:58 +0000179 if (!PyArg_ParseTuple(args, ":fileno")) return NULL;
180 return PyInt_FromLong(self->x_fd);
Guido van Rossumb130dc72000-03-30 23:25:49 +0000181}
182
183static PyObject *
184lad_setparameters(lad_t *self, PyObject *args)
185{
Fred Drakeda940d82000-07-08 06:05:58 +0000186 int rate, ssize, nchannels, stereo, n, fmt;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000187
Fred Drakeda940d82000-07-08 06:05:58 +0000188 if (!PyArg_ParseTuple(args, "iiii:setparameters",
189 &rate, &ssize, &nchannels, &fmt))
190 return NULL;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000191
Fred Drakeda940d82000-07-08 06:05:58 +0000192 if (rate < 0 || ssize < 0 || (nchannels != 1 && nchannels != 2)) {
193 PyErr_SetFromErrno(LinuxAudioError);
194 return NULL;
195 }
196 if (ioctl(self->x_fd, SOUND_PCM_WRITE_RATE, &rate) < 0) {
197 PyErr_SetFromErrno(LinuxAudioError);
198 return NULL;
199 }
200 if (ioctl(self->x_fd, SNDCTL_DSP_SAMPLESIZE, &ssize) < 0) {
201 PyErr_SetFromErrno(LinuxAudioError);
202 return NULL;
203 }
204 stereo = (nchannels == 1)? 0: (nchannels == 2)? 1: -1;
205 if (ioctl(self->x_fd, SNDCTL_DSP_STEREO, &stereo) < 0) {
206 PyErr_SetFromErrno(LinuxAudioError);
207 return NULL;
208 }
209 for (n = 0; n != sizeof(audio_types) / sizeof(audio_types[0]); n++)
210 if (fmt == audio_types[n].a_fmt)
211 break;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000212
Fred Drakeda940d82000-07-08 06:05:58 +0000213 if (n == sizeof(audio_types) / sizeof(audio_types[0]) ||
214 audio_types[n].a_bps != ssize ||
215 (self->x_afmts & audio_types[n].a_fmt) == 0) {
216 PyErr_SetFromErrno(LinuxAudioError);
217 return NULL;
218 }
219 if (ioctl(self->x_fd, SNDCTL_DSP_SETFMT, &audio_types[n].a_fmt) < 0) {
220 PyErr_SetFromErrno(LinuxAudioError);
221 return NULL;
222 }
223 Py_INCREF(Py_None);
224 return Py_None;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000225}
226
227static int
228_ssize(lad_t *self, int *nchannels, int *ssize)
229{
Fred Drakeda940d82000-07-08 06:05:58 +0000230 int fmt;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000231
Fred Drakeda940d82000-07-08 06:05:58 +0000232 fmt = 0;
233 if (ioctl(self->x_fd, SNDCTL_DSP_SETFMT, &fmt) < 0)
234 return -errno;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000235
Fred Drakeda940d82000-07-08 06:05:58 +0000236 switch (fmt) {
237 case AFMT_MU_LAW:
238 case AFMT_A_LAW:
239 case AFMT_U8:
240 case AFMT_S8:
241 *ssize = sizeof(char);
242 break;
243 case AFMT_S16_LE:
244 case AFMT_S16_BE:
245 case AFMT_U16_LE:
246 case AFMT_U16_BE:
247 *ssize = sizeof(short);
248 break;
249 case AFMT_MPEG:
250 case AFMT_IMA_ADPCM:
251 default:
252 return -EOPNOTSUPP;
253 }
254 *nchannels = 0;
255 if (ioctl(self->x_fd, SNDCTL_DSP_CHANNELS, nchannels) < 0)
256 return -errno;
257 return 0;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000258}
259
260
261/* bufsize returns the size of the hardware audio buffer in number
Fred Drakeda940d82000-07-08 06:05:58 +0000262 of samples */
Guido van Rossumb130dc72000-03-30 23:25:49 +0000263static PyObject *
264lad_bufsize(lad_t *self, PyObject *args)
265{
Fred Drakeda940d82000-07-08 06:05:58 +0000266 audio_buf_info ai;
267 int nchannels, ssize;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000268
Fred Drakeda940d82000-07-08 06:05:58 +0000269 if (!PyArg_ParseTuple(args, ":bufsize")) return NULL;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000270
Fred Drakeda940d82000-07-08 06:05:58 +0000271 if (_ssize(self, &nchannels, &ssize) < 0) {
272 PyErr_SetFromErrno(LinuxAudioError);
273 return NULL;
274 }
275 if (ioctl(self->x_fd, SNDCTL_DSP_GETOSPACE, &ai) < 0) {
276 PyErr_SetFromErrno(LinuxAudioError);
277 return NULL;
278 }
279 return PyInt_FromLong((ai.fragstotal * ai.fragsize) / (nchannels * ssize));
Guido van Rossumb130dc72000-03-30 23:25:49 +0000280}
281
282/* obufcount returns the number of samples that are available in the
283 hardware for playing */
284static PyObject *
285lad_obufcount(lad_t *self, PyObject *args)
286{
Fred Drakeda940d82000-07-08 06:05:58 +0000287 audio_buf_info ai;
288 int nchannels, ssize;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000289
Fred Drakeda940d82000-07-08 06:05:58 +0000290 if (!PyArg_ParseTuple(args, ":obufcount"))
291 return NULL;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000292
Fred Drakeda940d82000-07-08 06:05:58 +0000293 if (_ssize(self, &nchannels, &ssize) < 0) {
294 PyErr_SetFromErrno(LinuxAudioError);
295 return NULL;
296 }
297 if (ioctl(self->x_fd, SNDCTL_DSP_GETOSPACE, &ai) < 0) {
298 PyErr_SetFromErrno(LinuxAudioError);
299 return NULL;
300 }
301 return PyInt_FromLong((ai.fragstotal * ai.fragsize - ai.bytes) /
302 (ssize * nchannels));
Guido van Rossumb130dc72000-03-30 23:25:49 +0000303}
304
305/* obufcount returns the number of samples that can be played without
Fred Drakeda940d82000-07-08 06:05:58 +0000306 blocking */
Guido van Rossumb130dc72000-03-30 23:25:49 +0000307static PyObject *
308lad_obuffree(lad_t *self, PyObject *args)
309{
Fred Drakeda940d82000-07-08 06:05:58 +0000310 audio_buf_info ai;
311 int nchannels, ssize;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000312
Fred Drakeda940d82000-07-08 06:05:58 +0000313 if (!PyArg_ParseTuple(args, ":obuffree"))
314 return NULL;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000315
Fred Drakeda940d82000-07-08 06:05:58 +0000316 if (_ssize(self, &nchannels, &ssize) < 0) {
317 PyErr_SetFromErrno(LinuxAudioError);
318 return NULL;
319 }
320 if (ioctl(self->x_fd, SNDCTL_DSP_GETOSPACE, &ai) < 0) {
321 PyErr_SetFromErrno(LinuxAudioError);
322 return NULL;
323 }
324 return PyInt_FromLong(ai.bytes / (ssize * nchannels));
Guido van Rossumb130dc72000-03-30 23:25:49 +0000325}
326
327/* Flush the device */
328static PyObject *
329lad_flush(lad_t *self, PyObject *args)
330{
Fred Drakeda940d82000-07-08 06:05:58 +0000331 if (!PyArg_ParseTuple(args, ":flush")) return NULL;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000332
Fred Drakeda940d82000-07-08 06:05:58 +0000333 if (ioctl(self->x_fd, SNDCTL_DSP_SYNC, NULL) < 0) {
334 PyErr_SetFromErrno(LinuxAudioError);
335 return NULL;
336 }
337 Py_INCREF(Py_None);
338 return Py_None;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000339}
340
341static PyMethodDef lad_methods[] = {
Fred Drakeda940d82000-07-08 06:05:58 +0000342 { "read", (PyCFunction)lad_read, METH_VARARGS },
343 { "write", (PyCFunction)lad_write, METH_VARARGS },
344 { "setparameters", (PyCFunction)lad_setparameters, METH_VARARGS },
345 { "bufsize", (PyCFunction)lad_bufsize, METH_VARARGS },
346 { "obufcount", (PyCFunction)lad_obufcount, METH_VARARGS },
347 { "obuffree", (PyCFunction)lad_obuffree, METH_VARARGS },
348 { "flush", (PyCFunction)lad_flush, METH_VARARGS },
349 { "close", (PyCFunction)lad_close, METH_VARARGS },
350 { "fileno", (PyCFunction)lad_fileno, METH_VARARGS },
351 { NULL, NULL} /* sentinel */
Guido van Rossumb130dc72000-03-30 23:25:49 +0000352};
353
354static PyObject *
355lad_getattr(lad_t *xp, char *name)
356{
Fred Drakeda940d82000-07-08 06:05:58 +0000357 return Py_FindMethod(lad_methods, (PyObject *)xp, name);
Guido van Rossumb130dc72000-03-30 23:25:49 +0000358}
359
360static PyTypeObject Ladtype = {
Fred Drakeda940d82000-07-08 06:05:58 +0000361 PyObject_HEAD_INIT(&PyType_Type)
362 0, /*ob_size*/
363 "linux_audio_device", /*tp_name*/
364 sizeof(lad_t), /*tp_size*/
365 0, /*tp_itemsize*/
366 /* methods */
367 (destructor)lad_dealloc, /*tp_dealloc*/
368 0, /*tp_print*/
369 (getattrfunc)lad_getattr, /*tp_getattr*/
370 0, /*tp_setattr*/
371 0, /*tp_compare*/
372 0, /*tp_repr*/
Guido van Rossumb130dc72000-03-30 23:25:49 +0000373};
374
375static PyObject *
376ladopen(PyObject *self, PyObject *args)
377{
Fred Drakeda940d82000-07-08 06:05:58 +0000378 return (PyObject *)newladobject(args);
Guido van Rossumb130dc72000-03-30 23:25:49 +0000379}
380
381static PyMethodDef linuxaudiodev_methods[] = {
Fred Drakeda940d82000-07-08 06:05:58 +0000382 { "open", ladopen, METH_VARARGS },
383 { 0, 0 },
Guido van Rossumb130dc72000-03-30 23:25:49 +0000384};
385
Guido van Rossumb130dc72000-03-30 23:25:49 +0000386void
Thomas Woutersf3f33dc2000-07-21 06:00:07 +0000387initlinuxaudiodev(void)
Guido van Rossumb130dc72000-03-30 23:25:49 +0000388{
Fred Drakeda940d82000-07-08 06:05:58 +0000389 PyObject *m, *d, *x;
Guido van Rossumb130dc72000-03-30 23:25:49 +0000390
Fred Drakeda940d82000-07-08 06:05:58 +0000391 m = Py_InitModule("linuxaudiodev", linuxaudiodev_methods);
392 d = PyModule_GetDict(m);
Guido van Rossumb130dc72000-03-30 23:25:49 +0000393
Fred Drakeda940d82000-07-08 06:05:58 +0000394 LinuxAudioError = PyErr_NewException("linuxaudiodev.error", NULL, NULL);
395 if (LinuxAudioError)
396 PyDict_SetItemString(d, "error", LinuxAudioError);
Guido van Rossumb130dc72000-03-30 23:25:49 +0000397
Fred Drakeda940d82000-07-08 06:05:58 +0000398 x = PyInt_FromLong((long) AFMT_MU_LAW);
399 if (x == NULL || PyDict_SetItemString(d, "AFMT_MU_LAW", x) < 0)
400 goto error;
401 Py_DECREF(x);
Guido van Rossumb130dc72000-03-30 23:25:49 +0000402
Fred Drakeda940d82000-07-08 06:05:58 +0000403 x = PyInt_FromLong((long) AFMT_U8);
404 if (x == NULL || PyDict_SetItemString(d, "AFMT_U8", x) < 0)
405 goto error;
406 Py_DECREF(x);
Guido van Rossumb130dc72000-03-30 23:25:49 +0000407
Fred Drakeda940d82000-07-08 06:05:58 +0000408 x = PyInt_FromLong((long) AFMT_S8);
409 if (x == NULL || PyDict_SetItemString(d, "AFMT_S8", x) < 0)
410 goto error;
411 Py_DECREF(x);
Guido van Rossumb130dc72000-03-30 23:25:49 +0000412
Fred Drakeda940d82000-07-08 06:05:58 +0000413 x = PyInt_FromLong((long) AFMT_U16_BE);
414 if (x == NULL || PyDict_SetItemString(d, "AFMT_U16_BE", x) < 0)
415 goto error;
416 Py_DECREF(x);
Guido van Rossumb130dc72000-03-30 23:25:49 +0000417
Fred Drakeda940d82000-07-08 06:05:58 +0000418 x = PyInt_FromLong((long) AFMT_U16_LE);
419 if (x == NULL || PyDict_SetItemString(d, "AFMT_U16_LE", x) < 0)
420 goto error;
421 Py_DECREF(x);
Guido van Rossumb130dc72000-03-30 23:25:49 +0000422
Fred Drakeda940d82000-07-08 06:05:58 +0000423 x = PyInt_FromLong((long) AFMT_S16_BE);
424 if (x == NULL || PyDict_SetItemString(d, "AFMT_S16_BE", x) < 0)
425 goto error;
426 Py_DECREF(x);
Guido van Rossumb130dc72000-03-30 23:25:49 +0000427
Fred Drakeda940d82000-07-08 06:05:58 +0000428 x = PyInt_FromLong((long) AFMT_S16_LE);
429 if (x == NULL || PyDict_SetItemString(d, "AFMT_S16_LE", x) < 0)
430 goto error;
431 Py_DECREF(x);
Guido van Rossumb130dc72000-03-30 23:25:49 +0000432
Fred Drakeda940d82000-07-08 06:05:58 +0000433 /* Check for errors */
434 if (PyErr_Occurred()) {
435 error:
436 Py_FatalError("can't initialize module linuxaudiodev");
437 }
Guido van Rossumb130dc72000-03-30 23:25:49 +0000438}