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