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) (Py_TYPE(v) == &Sadtype) 46 #define is_sadstatusobject(v) (Py_TYPE(v) == &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 PyVarObject_HEAD_INIT(&PyType_Type, 0) 413 "sunaudiodev.sun_audio_device", /*tp_name*/ 414 sizeof(sadobject), /*tp_size*/ 415 0, /*tp_itemsize*/ 416 /* methods */ 417 (destructor)sad_dealloc, /*tp_dealloc*/ 418 0, /*tp_print*/ 419 (getattrfunc)sad_getattr, /*tp_getattr*/ 420 0, /*tp_setattr*/ 421 0, /*tp_compare*/ 422 0, /*tp_repr*/ 423 }; 424 425 static PyTypeObject Sadstatustype = { 426 PyVarObject_HEAD_INIT(&PyType_Type, 0) 427 "sunaudiodev.sun_audio_device_status", /*tp_name*/ 428 sizeof(sadstatusobject), /*tp_size*/ 429 0, /*tp_itemsize*/ 430 /* methods */ 431 (destructor)sads_dealloc, /*tp_dealloc*/ 432 0, /*tp_print*/ 433 (getattrfunc)sads_getattr, /*tp_getattr*/ 434 (setattrfunc)sads_setattr, /*tp_setattr*/ 435 0, /*tp_compare*/ 436 0, /*tp_repr*/ 437 }; 438 /* ------------------------------------------------------------------- */ 439 440 static PyObject * 441 sadopen(PyObject *self, PyObject *args) 442 { 443 return (PyObject *)newsadobject(args); 444 } 445 446 static PyMethodDef sunaudiodev_methods[] = { 447 { "open", sadopen, METH_VARARGS }, 448 { 0, 0 }, 449 }; 450 451 void 452 initsunaudiodev(void) 453 { 454 PyObject *m, *d; 455 456 if (PyErr_WarnPy3k("the sunaudiodev module has been removed in " 457 "Python 3.0", 2) < 0) 458 return; 459 460 m = Py_InitModule("sunaudiodev", sunaudiodev_methods); 461 if (m == NULL) 462 return; 463 d = PyModule_GetDict(m); 464 SunAudioError = PyErr_NewException("sunaudiodev.error", NULL, NULL); 465 if (SunAudioError) 466 PyDict_SetItemString(d, "error", SunAudioError); 467 } 468