ossaudiodev.c 30.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/*
 * ossaudiodev -- Python interface to the OSS (Open Sound System) API.
 *                This is the standard audio API for Linux and some
 *                flavours of BSD [XXX which ones?]; it is also available
 *                for a wide range of commercial Unices.
 *
 * Originally written by Peter Bosch, March 2000, as linuxaudiodev.
 *
 * Renamed to ossaudiodev and rearranged/revised/hacked up
 * by Greg Ward <gward@python.net>, November 2002.
11
 * Mixer interface by Nicholas FitzRoy-Dale <wzdd@lardcave.net>, Dec 2002.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
 *              
 * (c) 2000 Peter Bosch.  All Rights Reserved.
 * (c) 2002 Gregory P. Ward.  All Rights Reserved.
 * (c) 2002 Python Software Foundation.  All Rights Reserved.
 *
 * XXX need a license statement
 *
 * $Id$
 */

#include "Python.h"
#include "structmember.h"

#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#else
#define O_RDONLY 00
#define O_WRONLY 01
#endif

#include <sys/ioctl.h>
33 34
#include <sys/soundcard.h>

35 36 37 38 39 40
#if defined(linux)

typedef unsigned long uint32_t;

#elif defined(__FreeBSD__)

41 42 43
# ifndef SNDCTL_DSP_CHANNELS
#  define SNDCTL_DSP_CHANNELS SOUND_PCM_WRITE_CHANNELS
# endif
44 45 46 47 48

#endif

typedef struct {
    PyObject_HEAD;
49 50 51 52 53
    int      fd;                      /* The open file */
    int      mode;                    /* file mode */
    int      icount;                  /* Input count */
    int      ocount;                  /* Output count */
    uint32_t afmts;                   /* Audio formats supported by hardware */
54
} oss_audio_t;
55

56 57
typedef struct {
    PyObject_HEAD;
Greg Ward's avatar
Greg Ward committed
58
    int      fd;                      /* The open mixer device */
59 60
} oss_mixer_t;

61

62
static PyTypeObject OSSAudioType;
63
static PyTypeObject OSSMixerType;
64

65
static PyObject *OSSAudioError;
66

67 68 69 70 71

/* ----------------------------------------------------------------------
 * DSP object initialization/deallocation
 */

72
static oss_audio_t *
73
newossobject(PyObject *arg)
74
{
75
    oss_audio_t *self;
76 77 78 79
    int fd, afmts, imode;
    char *basedev = NULL;
    char *mode = NULL;

80
    /* Two ways to call open():
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
         open(device, mode) (for consistency with builtin open())
         open(mode)         (for backwards compatibility)
       because the *first* argument is optional, parsing args is
       a wee bit tricky. */
    if (!PyArg_ParseTuple(arg, "s|s:open", &basedev, &mode))
       return NULL;
    if (mode == NULL) {                 /* only one arg supplied */
       mode = basedev;
       basedev = NULL;
    }

    if (strcmp(mode, "r") == 0)
        imode = O_RDONLY;
    else if (strcmp(mode, "w") == 0)
        imode = O_WRONLY;
96 97
    else if (strcmp(mode, "rw") == 0)
        imode = O_RDWR;
98
    else {
99
        PyErr_SetString(OSSAudioError, "mode must be 'r', 'w', or 'rw'");
100 101 102
        return NULL;
    }

Greg Ward's avatar
Greg Ward committed
103 104
    /* Open the correct device: either the 'device' argument,
       or the AUDIODEV environment variable, or "/dev/dsp". */
105 106 107 108 109 110
    if (basedev == NULL) {              /* called with one arg */
       basedev = getenv("AUDIODEV");
       if (basedev == NULL)             /* $AUDIODEV not set */
          basedev = "/dev/dsp";
    }

111 112 113 114 115
    /* Open with O_NONBLOCK to avoid hanging on devices that only allow
       one open at a time.  This does *not* affect later I/O; OSS
       provides a special ioctl() for non-blocking read/write, which is
       exposed via oss_nonblock() below. */
    if ((fd = open(basedev, imode|O_NONBLOCK)) == -1) {
116
        PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev);
117 118
        return NULL;
    }
119 120 121 122 123 124 125 126 127

    /* And (try to) put it back in blocking mode so we get the
       expected write() semantics. */
    if (fcntl(fd, F_SETFL, 0) == -1) {
        close(fd);
        PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev);
        return NULL;
    }

128
    if (ioctl(fd, SNDCTL_DSP_GETFMTS, &afmts) == -1) {
129
        PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev);
130 131 132
        return NULL;
    }
    /* Create and initialize the object */
133
    if ((self = PyObject_New(oss_audio_t, &OSSAudioType)) == NULL) {
134 135 136
        close(fd);
        return NULL;
    }
137 138 139 140 141
    self->fd = fd;
    self->mode = imode;
    self->icount = self->ocount = 0;
    self->afmts  = afmts;
    return self;
142 143 144
}

static void
145
oss_dealloc(oss_audio_t *self)
146 147
{
    /* if already closed, don't reclose it */
148 149 150
    if (self->fd != -1)
        close(self->fd);
    PyObject_Del(self);
151 152
}

153 154 155 156 157

/* ----------------------------------------------------------------------
 * Mixer object initialization/deallocation
 */

158 159 160
static oss_mixer_t *
newossmixerobject(PyObject *arg)
{
161 162
    char *basedev = NULL;
    int fd;
163
    oss_mixer_t *self;
164
    
165
    if (!PyArg_ParseTuple(arg, "|s", &basedev)) {
Greg Ward's avatar
Greg Ward committed
166
        return NULL;
167 168 169
    }
    
    if (basedev == NULL) {
Greg Ward's avatar
Greg Ward committed
170 171 172
        basedev = getenv("MIXERDEV");
        if (basedev == NULL)            /* MIXERDEV not set */
            basedev = "/dev/mixer";
173 174
    }

175
    if ((fd = open(basedev, O_RDWR)) == -1) {
176 177 178
        PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev);
        return NULL;
    }
179

180
    if ((self = PyObject_New(oss_mixer_t, &OSSMixerType)) == NULL) {
181 182 183 184
        close(fd);
        return NULL;
    }
    
185
    self->fd = fd;
186
    
187
    return self;
188 189 190
}

static void
191
oss_mixer_dealloc(oss_mixer_t *self)
192 193
{
    /* if already closed, don't reclose it */
194 195 196
    if (self->fd != -1)
        close(self->fd);
    PyObject_Del(self);
197 198
}

199 200 201 202 203 204 205 206 207

/* Methods to wrap the OSS ioctls.  The calling convention is pretty
   simple:
     nonblock()        -> ioctl(fd, SNDCTL_DSP_NONBLOCK)
     fmt = setfmt(fmt) -> ioctl(fd, SNDCTL_DSP_SETFMT, &fmt)
     etc.
*/


208 209 210 211
/* ----------------------------------------------------------------------
 * Helper functions
 */

212 213 214 215 216 217 218 219 220 221
/* _do_ioctl_1() is a private helper function used for the OSS ioctls --
   SNDCTL_DSP_{SETFMT,CHANNELS,SPEED} -- that that are called from C
   like this:
     ioctl(fd, SNDCTL_DSP_cmd, &arg)

   where arg is the value to set, and on return the driver sets arg to
   the value that was actually set.  Mapping this to Python is obvious:
     arg = dsp.xxx(arg)
*/
static PyObject *
222
_do_ioctl_1(int fd, PyObject *args, char *fname, int cmd)
223
{
224
    char argfmt[33] = "i:";
225 226
    int arg;

227
    assert(strlen(fname) <= 30);
228 229
    strcat(argfmt, fname);
    if (!PyArg_ParseTuple(args, argfmt, &arg))
Greg Ward's avatar
Greg Ward committed
230
        return NULL;
231

232
    if (ioctl(fd, cmd, &arg) == -1)
233
        return PyErr_SetFromErrno(PyExc_IOError);
234 235 236
    return PyInt_FromLong(arg);
}

237

238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
/* _do_ioctl_1_internal() is a wrapper for ioctls that take no inputs
   but return an output -- ie. we need to pass a pointer to a local C
   variable so the driver can write its output there, but from Python
   all we see is the return value.  For example,
   SOUND_MIXER_READ_DEVMASK returns a bitmask of available mixer
   devices, but does not use the value of the parameter passed-in in any
   way.
*/
static PyObject *
_do_ioctl_1_internal(int fd, PyObject *args, char *fname, int cmd)
{
    char argfmt[32] = ":";
    int arg = 0;

    assert(strlen(fname) <= 30);
    strcat(argfmt, fname);
    if (!PyArg_ParseTuple(args, argfmt, &arg))
Greg Ward's avatar
Greg Ward committed
255
        return NULL;
256 257 258 259 260 261 262 263

    if (ioctl(fd, cmd, &arg) == -1)
        return PyErr_SetFromErrno(PyExc_IOError);
    return PyInt_FromLong(arg);
}



264 265 266
/* _do_ioctl_0() is a private helper for the no-argument ioctls:
   SNDCTL_DSP_{SYNC,RESET,POST}. */
static PyObject *
267
_do_ioctl_0(int fd, PyObject *args, char *fname, int cmd)
268
{
269
    char argfmt[32] = ":";
270
    int rv;
271

272
    assert(strlen(fname) <= 30);
273 274
    strcat(argfmt, fname);
    if (!PyArg_ParseTuple(args, argfmt))
Greg Ward's avatar
Greg Ward committed
275
        return NULL;
276

277 278 279 280 281 282 283 284 285
    /* According to hannu@opensound.com, all three of the ioctls that
       use this function can block, so release the GIL.  This is
       especially important for SYNC, which can block for several
       seconds. */
    Py_BEGIN_ALLOW_THREADS
    rv = ioctl(fd, cmd, 0);
    Py_END_ALLOW_THREADS

    if (rv == -1)
286
        return PyErr_SetFromErrno(PyExc_IOError);
287 288 289 290 291
    Py_INCREF(Py_None);
    return Py_None;
}


292
/* ----------------------------------------------------------------------
293
 * Methods of DSP objects (OSSAudioType)
294 295
 */

296
static PyObject *
297
oss_nonblock(oss_audio_t *self, PyObject *args)
298 299 300 301
{
    /* Hmmm: it doesn't appear to be possible to return to blocking
       mode once we're in non-blocking mode! */
    if (!PyArg_ParseTuple(args, ":nonblock"))
Greg Ward's avatar
Greg Ward committed
302
        return NULL;
303
    if (ioctl(self->fd, SNDCTL_DSP_NONBLOCK, NULL) == -1)
304
        return PyErr_SetFromErrno(PyExc_IOError);
305 306 307 308 309
    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *
310
oss_setfmt(oss_audio_t *self, PyObject *args)
311
{
312
    return _do_ioctl_1(self->fd, args, "setfmt", SNDCTL_DSP_SETFMT);
313 314 315
}

static PyObject *
316
oss_getfmts(oss_audio_t *self, PyObject *args)
317 318 319
{
    int mask;
    if (!PyArg_ParseTuple(args, ":getfmts"))
Greg Ward's avatar
Greg Ward committed
320
        return NULL;
321
    if (ioctl(self->fd, SNDCTL_DSP_GETFMTS, &mask) == -1)
322
        return PyErr_SetFromErrno(PyExc_IOError);
323 324 325 326
    return PyInt_FromLong(mask);
}

static PyObject *
327
oss_channels(oss_audio_t *self, PyObject *args)
328
{
329
    return _do_ioctl_1(self->fd, args, "channels", SNDCTL_DSP_CHANNELS);
330 331 332
}

static PyObject *
333
oss_speed(oss_audio_t *self, PyObject *args)
334
{
335
    return _do_ioctl_1(self->fd, args, "speed", SNDCTL_DSP_SPEED);
336 337 338
}

static PyObject *
339
oss_sync(oss_audio_t *self, PyObject *args)
340
{
341
    return _do_ioctl_0(self->fd, args, "sync", SNDCTL_DSP_SYNC);
342 343 344
}
    
static PyObject *
345
oss_reset(oss_audio_t *self, PyObject *args)
346
{
347
    return _do_ioctl_0(self->fd, args, "reset", SNDCTL_DSP_RESET);
348 349 350
}
    
static PyObject *
351
oss_post(oss_audio_t *self, PyObject *args)
352
{
353
    return _do_ioctl_0(self->fd, args, "post", SNDCTL_DSP_POST);
354 355 356 357 358 359
}


/* Regular file methods: read(), write(), close(), etc. as well
   as one convenience method, writeall(). */

360
static PyObject *
361
oss_read(oss_audio_t *self, PyObject *args)
362 363 364 365
{
    int size, count;
    char *cp;
    PyObject *rv;
Greg Ward's avatar
Greg Ward committed
366
        
367 368 369 370 371 372
    if (!PyArg_ParseTuple(args, "i:read", &size))
        return NULL;
    rv = PyString_FromStringAndSize(NULL, size);
    if (rv == NULL)
        return NULL;
    cp = PyString_AS_STRING(rv);
373 374 375 376 377 378

    Py_BEGIN_ALLOW_THREADS
    count = read(self->fd, cp, size);
    Py_END_ALLOW_THREADS

    if (count < 0) {
379
        PyErr_SetFromErrno(PyExc_IOError);
380 381 382
        Py_DECREF(rv);
        return NULL;
    }
383
    self->icount += count;
384 385 386 387 388
    _PyString_Resize(&rv, count);
    return rv;
}

static PyObject *
389
oss_write(oss_audio_t *self, PyObject *args)
390 391 392 393 394
{
    char *cp;
    int rv, size;

    if (!PyArg_ParseTuple(args, "s#:write", &cp, &size)) {
Greg Ward's avatar
Greg Ward committed
395
        return NULL;
396
    }
397 398 399 400 401 402

    Py_BEGIN_ALLOW_THREADS
    rv = write(self->fd, cp, size);
    Py_END_ALLOW_THREADS

    if (rv == -1) {
403
        return PyErr_SetFromErrno(PyExc_IOError);
404
    } else {
405
        self->ocount += rv;
406 407 408 409 410
    }
    return PyInt_FromLong(rv);
}

static PyObject *
411
oss_writeall(oss_audio_t *self, PyObject *args)
412 413 414 415
{
    char *cp;
    int rv, size;
    fd_set write_set_fds;
416
    int select_rv;
417
    
418 419 420 421 422 423 424
    /* NB. writeall() is only useful in non-blocking mode: according to
       Guenter Geiger <geiger@xdv.org> on the linux-audio-dev list
       (http://eca.cx/lad/2002/11/0380.html), OSS guarantees that
       write() in blocking mode consumes the whole buffer.  In blocking
       mode, the behaviour of write() and writeall() from Python is
       indistinguishable. */

425
    if (!PyArg_ParseTuple(args, "s#:write", &cp, &size)) 
426
        return NULL;
427 428 429

    /* use select to wait for audio device to be available */
    FD_ZERO(&write_set_fds);
430
    FD_SET(self->fd, &write_set_fds);
431 432

    while (size > 0) {
433
        Py_BEGIN_ALLOW_THREADS
434
        select_rv = select(self->fd+1, NULL, &write_set_fds, NULL, NULL);
435
        Py_END_ALLOW_THREADS
436 437
        assert(select_rv != 0);         /* no timeout, can't expire */
        if (select_rv == -1)
438
            return PyErr_SetFromErrno(PyExc_IOError);
439

440
        Py_BEGIN_ALLOW_THREADS
441
        rv = write(self->fd, cp, size);
442
        Py_END_ALLOW_THREADS
443 444 445 446 447
        if (rv == -1) {
            if (errno == EAGAIN) {      /* buffer is full, try again */
                errno = 0;
                continue;
            } else                      /* it's a real error */
448
                return PyErr_SetFromErrno(PyExc_IOError);
449
        } else {                        /* wrote rv bytes */
450
            self->ocount += rv;
451 452 453
            size -= rv;
            cp += rv;
        }
454 455 456 457 458 459
    }
    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *
460
oss_close(oss_audio_t *self, PyObject *args)
461 462
{
    if (!PyArg_ParseTuple(args, ":close"))
Greg Ward's avatar
Greg Ward committed
463
        return NULL;
464

465
    if (self->fd >= 0) {
466
        Py_BEGIN_ALLOW_THREADS
467
        close(self->fd);
468
        Py_END_ALLOW_THREADS
469
        self->fd = -1;
470 471 472 473 474 475
    }
    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *
476
oss_fileno(oss_audio_t *self, PyObject *args)
477 478
{
    if (!PyArg_ParseTuple(args, ":fileno")) 
Greg Ward's avatar
Greg Ward committed
479
        return NULL;
480
    return PyInt_FromLong(self->fd);
481 482
}

483 484 485 486

/* Convenience methods: these generally wrap a couple of ioctls into one
   common task. */

487
static PyObject *
488
oss_setparameters(oss_audio_t *self, PyObject *args)
489
{
490 491 492
    int wanted_fmt, wanted_channels, wanted_rate, strict=0;
    int fmt, channels, rate;
    PyObject * rv;                    /* return tuple (fmt, channels, rate) */
493

494 495 496
    if (!PyArg_ParseTuple(args, "iii|i:setparameters",
                          &wanted_fmt, &wanted_channels, &wanted_rate,
                          &strict))
Greg Ward's avatar
Greg Ward committed
497
        return NULL;
498 499 500 501

    fmt = wanted_fmt;
    if (ioctl(self->fd, SNDCTL_DSP_SETFMT, &fmt) == -1) {
        return PyErr_SetFromErrno(PyExc_IOError);
502
    }
503 504 505 506 507
    if (strict && fmt != wanted_fmt) {
        return PyErr_Format
            (OSSAudioError,
             "unable to set requested format (wanted %d, got %d)",
             wanted_fmt, fmt);
508 509
    }

510 511 512
    channels = wanted_channels;
    if (ioctl(self->fd, SNDCTL_DSP_CHANNELS, &channels) == -1) {
        return PyErr_SetFromErrno(PyExc_IOError);
513
    }
514 515 516 517 518
    if (strict && channels != wanted_channels) {
        return PyErr_Format
            (OSSAudioError,
             "unable to set requested channels (wanted %d, got %d)",
             wanted_channels, channels);
519 520
    }

521 522 523
    rate = wanted_rate;
    if (ioctl(self->fd, SNDCTL_DSP_SPEED, &rate) == -1) {
        return PyErr_SetFromErrno(PyExc_IOError);
524
    }
525 526 527 528 529
    if (strict && rate != wanted_rate) {
        return PyErr_Format
            (OSSAudioError,
             "unable to set requested rate (wanted %d, got %d)",
             wanted_rate, rate);
530
    }
531 532 533 534 535
    
    /* Construct the return value: a (fmt, channels, rate) tuple that
       tells what the audio hardware was actually set to. */
    rv = PyTuple_New(3);
    if (rv == NULL)
536
        return NULL;
537 538 539 540
    PyTuple_SET_ITEM(rv, 0, PyInt_FromLong(fmt));
    PyTuple_SET_ITEM(rv, 1, PyInt_FromLong(channels));
    PyTuple_SET_ITEM(rv, 2, PyInt_FromLong(rate));
    return rv;
541 542 543
}

static int
544
_ssize(oss_audio_t *self, int *nchannels, int *ssize)
545 546 547 548
{
    int fmt;

    fmt = 0;
549
    if (ioctl(self->fd, SNDCTL_DSP_SETFMT, &fmt) < 0) 
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
        return -errno;

    switch (fmt) {
    case AFMT_MU_LAW:
    case AFMT_A_LAW:
    case AFMT_U8:
    case AFMT_S8:
        *ssize = sizeof(char);
        break;
    case AFMT_S16_LE:
    case AFMT_S16_BE:
    case AFMT_U16_LE:
    case AFMT_U16_BE:
        *ssize = sizeof(short);
        break;
    case AFMT_MPEG:
    case AFMT_IMA_ADPCM:
    default:
        return -EOPNOTSUPP;
    }
    *nchannels = 0;
571
    if (ioctl(self->fd, SNDCTL_DSP_CHANNELS, nchannels) < 0)
572 573 574 575 576 577 578 579
        return -errno;
    return 0;
}


/* bufsize returns the size of the hardware audio buffer in number 
   of samples */
static PyObject *
580
oss_bufsize(oss_audio_t *self, PyObject *args)
581 582 583 584 585 586 587
{
    audio_buf_info ai;
    int nchannels, ssize;

    if (!PyArg_ParseTuple(args, ":bufsize")) return NULL;

    if (_ssize(self, &nchannels, &ssize) < 0) {
588
        PyErr_SetFromErrno(PyExc_IOError);
589 590
        return NULL;
    }
591
    if (ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &ai) < 0) {
592
        PyErr_SetFromErrno(PyExc_IOError);
593 594 595 596 597 598 599 600
        return NULL;
    }
    return PyInt_FromLong((ai.fragstotal * ai.fragsize) / (nchannels * ssize));
}

/* obufcount returns the number of samples that are available in the 
   hardware for playing */
static PyObject *
601
oss_obufcount(oss_audio_t *self, PyObject *args)
602 603 604 605 606 607 608 609
{
    audio_buf_info ai;
    int nchannels, ssize;

    if (!PyArg_ParseTuple(args, ":obufcount"))
        return NULL;

    if (_ssize(self, &nchannels, &ssize) < 0) {
610
        PyErr_SetFromErrno(PyExc_IOError);
611 612
        return NULL;
    }
613
    if (ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &ai) < 0) {
614
        PyErr_SetFromErrno(PyExc_IOError);
615 616 617 618 619 620 621 622 623
        return NULL;
    }
    return PyInt_FromLong((ai.fragstotal * ai.fragsize - ai.bytes) / 
                          (ssize * nchannels));
}

/* obufcount returns the number of samples that can be played without
   blocking */
static PyObject *
624
oss_obuffree(oss_audio_t *self, PyObject *args)
625 626 627 628 629 630 631 632
{
    audio_buf_info ai;
    int nchannels, ssize;

    if (!PyArg_ParseTuple(args, ":obuffree"))
        return NULL;

    if (_ssize(self, &nchannels, &ssize) < 0) {
633
        PyErr_SetFromErrno(PyExc_IOError);
634 635
        return NULL;
    }
636
    if (ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &ai) < 0) {
637
        PyErr_SetFromErrno(PyExc_IOError);
638 639 640 641 642 643
        return NULL;
    }
    return PyInt_FromLong(ai.bytes / (ssize * nchannels));
}

static PyObject *
644
oss_getptr(oss_audio_t *self, PyObject *args)
645 646 647 648 649
{
    count_info info;
    int req;

    if (!PyArg_ParseTuple(args, ":getptr"))
Greg Ward's avatar
Greg Ward committed
650
        return NULL;
651
    
652
    if (self->mode == O_RDONLY)
Greg Ward's avatar
Greg Ward committed
653
        req = SNDCTL_DSP_GETIPTR;
654
    else
Greg Ward's avatar
Greg Ward committed
655
        req = SNDCTL_DSP_GETOPTR;
656
    if (ioctl(self->fd, req, &info) == -1) {
657
        PyErr_SetFromErrno(PyExc_IOError);
658 659 660 661 662
        return NULL;
    }
    return Py_BuildValue("iii", info.bytes, info.blocks, info.ptr);
}

663 664 665 666 667

/* ----------------------------------------------------------------------
 * Methods of mixer objects (OSSMixerType)
 */

668 669 670 671
static PyObject *
oss_mixer_close(oss_mixer_t *self, PyObject *args)
{
    if (!PyArg_ParseTuple(args, ":close"))
Greg Ward's avatar
Greg Ward committed
672
        return NULL;
673

674 675 676
    if (self->fd >= 0) {
        close(self->fd);
        self->fd = -1;
677 678 679 680 681 682 683 684 685
    }
    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *
oss_mixer_fileno(oss_mixer_t *self, PyObject *args)
{
    if (!PyArg_ParseTuple(args, ":fileno")) 
Greg Ward's avatar
Greg Ward committed
686
        return NULL;
687
    return PyInt_FromLong(self->fd);
688 689 690 691 692
}

/* Simple mixer interface methods */

static PyObject *
693
oss_mixer_controls(oss_mixer_t *self, PyObject *args)
694
{
695
    return _do_ioctl_1_internal(self->fd, args, "controls",
696 697 698 699
        SOUND_MIXER_READ_DEVMASK);
}

static PyObject *
700
oss_mixer_stereocontrols(oss_mixer_t *self, PyObject *args)
701
{
702
    return _do_ioctl_1_internal(self->fd, args, "stereocontrols",
703 704 705 706
        SOUND_MIXER_READ_STEREODEVS);
}

static PyObject *
707
oss_mixer_reccontrols(oss_mixer_t *self, PyObject *args)
708
{
709
    return _do_ioctl_1_internal(self->fd, args, "reccontrols",
Greg Ward's avatar
Greg Ward committed
710
        SOUND_MIXER_READ_RECMASK);
711 712 713
}

static PyObject *
714
oss_mixer_get(oss_mixer_t *self, PyObject *args)
715 716 717 718
{
    int channel, volume;
    
    /* Can't use _do_ioctl_1 because of encoded arg thingy. */
719
    if (!PyArg_ParseTuple(args, "i:get", &channel))
Greg Ward's avatar
Greg Ward committed
720
        return NULL;
721 722
    
    if (channel < 0 || channel > SOUND_MIXER_NRDEVICES) {
Greg Ward's avatar
Greg Ward committed
723 724
        PyErr_SetString(OSSAudioError, "Invalid mixer channel specified.");
        return NULL;
725 726
    }
    
727
    if (ioctl(self->fd, MIXER_READ(channel), &volume) == -1)
Greg Ward's avatar
Greg Ward committed
728
        return PyErr_SetFromErrno(PyExc_IOError);
729
    
730
    return Py_BuildValue("(ii)", volume & 0xff, (volume & 0xff00) >> 8);
731 732 733
}

static PyObject *
734
oss_mixer_set(oss_mixer_t *self, PyObject *args)
735 736 737 738
{
    int channel, volume, leftVol, rightVol;
    
    /* Can't use _do_ioctl_1 because of encoded arg thingy. */
739
    if (!PyArg_ParseTuple(args, "i(ii):set", &channel, &leftVol, &rightVol))
Greg Ward's avatar
Greg Ward committed
740 741
        return NULL;
            
742
    if (channel < 0 || channel > SOUND_MIXER_NRDEVICES) {
Greg Ward's avatar
Greg Ward committed
743 744
        PyErr_SetString(OSSAudioError, "Invalid mixer channel specified.");
        return NULL;
745 746 747
    }
    
    if (leftVol < 0 || rightVol < 0 || leftVol > 100 || rightVol > 100) {
Greg Ward's avatar
Greg Ward committed
748 749
        PyErr_SetString(OSSAudioError, "Volumes must be between 0 and 100.");
        return NULL;
750 751 752 753
    }

    volume = (rightVol << 8) | leftVol;
    
754
    if (ioctl(self->fd, MIXER_WRITE(channel), &volume) == -1)
Greg Ward's avatar
Greg Ward committed
755
        return PyErr_SetFromErrno(PyExc_IOError);
756
   
757
    return Py_BuildValue("(ii)", volume & 0xff, (volume & 0xff00) >> 8);
758 759 760
}

static PyObject *
761
oss_mixer_get_recsrc(oss_mixer_t *self, PyObject *args)
762
{
763
    return _do_ioctl_1_internal(self->fd, args, "get_recsrc",
764 765 766 767
        SOUND_MIXER_READ_RECSRC);
}

static PyObject *
768
oss_mixer_set_recsrc(oss_mixer_t *self, PyObject *args)
769
{
770
    return _do_ioctl_1(self->fd, args, "set_recsrc",
771 772 773 774
        SOUND_MIXER_WRITE_RECSRC);
}


775 776 777 778
/* ----------------------------------------------------------------------
 * Method tables and other bureaucracy
 */

779
static PyMethodDef oss_methods[] = {
780
    /* Regular file methods */
Greg Ward's avatar
Greg Ward committed
781 782 783 784 785
    { "read",           (PyCFunction)oss_read, METH_VARARGS },
    { "write",          (PyCFunction)oss_write, METH_VARARGS },
    { "writeall",       (PyCFunction)oss_writeall, METH_VARARGS },
    { "close",          (PyCFunction)oss_close, METH_VARARGS },
    { "fileno",         (PyCFunction)oss_fileno, METH_VARARGS },
786 787

    /* Simple ioctl wrappers */
788
    { "nonblock",       (PyCFunction)oss_nonblock, METH_VARARGS },
Greg Ward's avatar
Greg Ward committed
789
    { "setfmt",         (PyCFunction)oss_setfmt, METH_VARARGS },
790 791 792
    { "getfmts",        (PyCFunction)oss_getfmts, METH_VARARGS },
    { "channels",       (PyCFunction)oss_channels, METH_VARARGS },
    { "speed",          (PyCFunction)oss_speed, METH_VARARGS },
Greg Ward's avatar
Greg Ward committed
793 794 795
    { "sync",           (PyCFunction)oss_sync, METH_VARARGS },
    { "reset",          (PyCFunction)oss_reset, METH_VARARGS },
    { "post",           (PyCFunction)oss_post, METH_VARARGS },
796 797

    /* Convenience methods -- wrap a couple of ioctls together */
Greg Ward's avatar
Greg Ward committed
798 799 800 801
    { "setparameters",  (PyCFunction)oss_setparameters, METH_VARARGS },
    { "bufsize",        (PyCFunction)oss_bufsize, METH_VARARGS },
    { "obufcount",      (PyCFunction)oss_obufcount, METH_VARARGS },
    { "obuffree",       (PyCFunction)oss_obuffree, METH_VARARGS },
802
    { "getptr",         (PyCFunction)oss_getptr, METH_VARARGS },
803 804

    /* Aliases for backwards compatibility */
Greg Ward's avatar
Greg Ward committed
805
    { "flush",          (PyCFunction)oss_sync, METH_VARARGS },
806

Greg Ward's avatar
Greg Ward committed
807
    { NULL,             NULL}           /* sentinel */
808 809
};

810 811
static PyMethodDef oss_mixer_methods[] = {
    /* Regular file method - OSS mixers are ioctl-only interface */
Greg Ward's avatar
Greg Ward committed
812 813
    { "close",          (PyCFunction)oss_mixer_close, METH_VARARGS },   
    { "fileno",         (PyCFunction)oss_mixer_fileno, METH_VARARGS },
814 815

    /* Simple ioctl wrappers */
816 817 818
    { "controls",       (PyCFunction)oss_mixer_controls, METH_VARARGS }, 
    { "stereocontrols", (PyCFunction)oss_mixer_stereocontrols, METH_VARARGS},
    { "reccontrols",    (PyCFunction)oss_mixer_reccontrols, METH_VARARGS},   
Greg Ward's avatar
Greg Ward committed
819 820
    { "get",            (PyCFunction)oss_mixer_get, METH_VARARGS },
    { "set",            (PyCFunction)oss_mixer_set, METH_VARARGS },
821 822
    { "get_recsrc",     (PyCFunction)oss_mixer_get_recsrc, METH_VARARGS },
    { "set_recsrc",     (PyCFunction)oss_mixer_set_recsrc, METH_VARARGS },
823
    
Greg Ward's avatar
Greg Ward committed
824
    { NULL,             NULL}
825 826
};

827
static PyObject *
828
oss_getattr(oss_audio_t *self, char *name)
829
{
830
    return Py_FindMethod(oss_methods, (PyObject *)self, name);
831 832
}

833
static PyObject *
834
oss_mixer_getattr(oss_mixer_t *self, char *name)
835
{
836
    return Py_FindMethod(oss_mixer_methods, (PyObject *)self, name);
837 838
}

839
static PyTypeObject OSSAudioType = {
840
    PyObject_HEAD_INIT(&PyType_Type)
Greg Ward's avatar
Greg Ward committed
841
    0,                          /*ob_size*/
842
    "ossaudiodev.oss_audio_device", /*tp_name*/
843
    sizeof(oss_audio_t),        /*tp_size*/
Greg Ward's avatar
Greg Ward committed
844
    0,                          /*tp_itemsize*/
845
    /* methods */
Greg Ward's avatar
Greg Ward committed
846 847 848 849 850 851
    (destructor)oss_dealloc,    /*tp_dealloc*/
    0,                          /*tp_print*/
    (getattrfunc)oss_getattr,   /*tp_getattr*/
    0,                          /*tp_setattr*/
    0,                          /*tp_compare*/
    0,                          /*tp_repr*/
852 853
};

854 855
static PyTypeObject OSSMixerType = {
    PyObject_HEAD_INIT(&PyType_Type)
Greg Ward's avatar
Greg Ward committed
856
    0,                              /*ob_size*/
857
    "ossaudiodev.oss_mixer_device", /*tp_name*/
Greg Ward's avatar
Greg Ward committed
858 859
    sizeof(oss_mixer_t),            /*tp_size*/
    0,                              /*tp_itemsize*/
860 861
    /* methods */
    (destructor)oss_mixer_dealloc,  /*tp_dealloc*/
Greg Ward's avatar
Greg Ward committed
862
    0,                              /*tp_print*/
863
    (getattrfunc)oss_mixer_getattr, /*tp_getattr*/
Greg Ward's avatar
Greg Ward committed
864 865 866
    0,                              /*tp_setattr*/
    0,                              /*tp_compare*/
    0,                              /*tp_repr*/
867 868 869
};


870
static PyObject *
871
ossopen(PyObject *self, PyObject *args)
872
{
873
    return (PyObject *)newossobject(args);
874 875
}

876 877 878 879 880 881
static PyObject *
ossopenmixer(PyObject *self, PyObject *args)
{
    return (PyObject *)newossmixerobject(args);
}

882 883
static PyMethodDef ossaudiodev_methods[] = {
    { "open", ossopen, METH_VARARGS },
884
    { "openmixer", ossopenmixer, METH_VARARGS },
885 886 887
    { 0, 0 },
};

888 889 890 891

#define _EXPORT_INT(mod, name) \
  if (PyModule_AddIntConstant(mod, #name, (long) (name)) == -1) return;

892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931

static char *control_labels[] = SOUND_DEVICE_LABELS;
static char *control_names[] = SOUND_DEVICE_NAMES;


static int
build_namelists (PyObject *module)
{
    PyObject *labels;
    PyObject *names;
    PyObject *s;
    int num_controls;
    int i;

    num_controls = sizeof(control_labels) / sizeof(control_labels[0]);
    assert(num_controls == sizeof(control_names) / sizeof(control_names[0]));

    labels = PyList_New(num_controls);
    names = PyList_New(num_controls);
    for (i = 0; i < num_controls; i++) {
        s = PyString_FromString(control_labels[i]);
        if (s == NULL)
            return -1;
        PyList_SET_ITEM(labels, i, s);
   
        s = PyString_FromString(control_names[i]);
        if (s == NULL)
            return -1;
        PyList_SET_ITEM(names, i, s);
    }

    if (PyModule_AddObject(module, "control_labels", labels) == -1)
        return -1;
    if (PyModule_AddObject(module, "control_names", names) == -1)
        return -1;

    return 0;
}   


932
void
933
initossaudiodev(void)
934 935 936
{
    PyObject *m;
  
937
    m = Py_InitModule("ossaudiodev", ossaudiodev_methods);
938

939 940
    OSSAudioError = PyErr_NewException("ossaudiodev.OSSAudioError", NULL, NULL);
    if (OSSAudioError) {
Greg Ward's avatar
Greg Ward committed
941
        PyModule_AddObject(m, "error", OSSAudioError);
942 943
        PyModule_AddObject(m, "OSSAudioError", OSSAudioError);
    }
944

945 946 947 948 949
    /* Build 'control_labels' and 'control_names' lists and add them
       to the module. */
    if (build_namelists(m) == -1)       /* XXX what to do here? */
        return;

950 951 952 953 954 955 956 957 958 959 960 961
    /* Expose the audio format numbers -- essential! */
    _EXPORT_INT(m, AFMT_QUERY);
    _EXPORT_INT(m, AFMT_MU_LAW);
    _EXPORT_INT(m, AFMT_A_LAW);
    _EXPORT_INT(m, AFMT_IMA_ADPCM);
    _EXPORT_INT(m, AFMT_U8);
    _EXPORT_INT(m, AFMT_S16_LE);
    _EXPORT_INT(m, AFMT_S16_BE);
    _EXPORT_INT(m, AFMT_S8);
    _EXPORT_INT(m, AFMT_U16_LE);
    _EXPORT_INT(m, AFMT_U16_BE);
    _EXPORT_INT(m, AFMT_MPEG);
962
#ifdef AFMT_AC3
963
    _EXPORT_INT(m, AFMT_AC3);
964
#endif
965
#ifdef AFMT_S16_NE
966
    _EXPORT_INT(m, AFMT_S16_NE);
967
#endif
Greg Ward's avatar
Greg Ward committed
968
        
Greg Ward's avatar
Greg Ward committed
969
    /* Expose the sound mixer device numbers. */
970
    _EXPORT_INT(m, SOUND_MIXER_NRDEVICES);
971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995
    _EXPORT_INT(m, SOUND_MIXER_VOLUME);
    _EXPORT_INT(m, SOUND_MIXER_BASS);
    _EXPORT_INT(m, SOUND_MIXER_TREBLE);
    _EXPORT_INT(m, SOUND_MIXER_SYNTH);
    _EXPORT_INT(m, SOUND_MIXER_PCM);
    _EXPORT_INT(m, SOUND_MIXER_SPEAKER);
    _EXPORT_INT(m, SOUND_MIXER_LINE);
    _EXPORT_INT(m, SOUND_MIXER_MIC);
    _EXPORT_INT(m, SOUND_MIXER_CD);
    _EXPORT_INT(m, SOUND_MIXER_IMIX);
    _EXPORT_INT(m, SOUND_MIXER_ALTPCM);
    _EXPORT_INT(m, SOUND_MIXER_RECLEV);
    _EXPORT_INT(m, SOUND_MIXER_IGAIN);
    _EXPORT_INT(m, SOUND_MIXER_OGAIN);
    _EXPORT_INT(m, SOUND_MIXER_LINE1);
    _EXPORT_INT(m, SOUND_MIXER_LINE2);
    _EXPORT_INT(m, SOUND_MIXER_LINE3);
    _EXPORT_INT(m, SOUND_MIXER_DIGITAL1);
    _EXPORT_INT(m, SOUND_MIXER_DIGITAL2);
    _EXPORT_INT(m, SOUND_MIXER_DIGITAL3);
    _EXPORT_INT(m, SOUND_MIXER_PHONEIN);
    _EXPORT_INT(m, SOUND_MIXER_PHONEOUT);
    _EXPORT_INT(m, SOUND_MIXER_VIDEO);
    _EXPORT_INT(m, SOUND_MIXER_RADIO);
    _EXPORT_INT(m, SOUND_MIXER_MONITOR);
996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008

    /* Expose all the ioctl numbers for masochists who like to do this
       stuff directly. */
    _EXPORT_INT(m, SNDCTL_COPR_HALT);
    _EXPORT_INT(m, SNDCTL_COPR_LOAD);
    _EXPORT_INT(m, SNDCTL_COPR_RCODE);
    _EXPORT_INT(m, SNDCTL_COPR_RCVMSG);
    _EXPORT_INT(m, SNDCTL_COPR_RDATA);
    _EXPORT_INT(m, SNDCTL_COPR_RESET);
    _EXPORT_INT(m, SNDCTL_COPR_RUN);
    _EXPORT_INT(m, SNDCTL_COPR_SENDMSG);
    _EXPORT_INT(m, SNDCTL_COPR_WCODE);
    _EXPORT_INT(m, SNDCTL_COPR_WDATA);
1009
#ifdef SNDCTL_DSP_BIND_CHANNEL
1010
    _EXPORT_INT(m, SNDCTL_DSP_BIND_CHANNEL);
1011
#endif
1012 1013 1014
    _EXPORT_INT(m, SNDCTL_DSP_CHANNELS);
    _EXPORT_INT(m, SNDCTL_DSP_GETBLKSIZE);
    _EXPORT_INT(m, SNDCTL_DSP_GETCAPS);
1015
#ifdef SNDCTL_DSP_GETCHANNELMASK
1016
    _EXPORT_INT(m, SNDCTL_DSP_GETCHANNELMASK);
1017
#endif
1018 1019 1020 1021 1022 1023
    _EXPORT_INT(m, SNDCTL_DSP_GETFMTS);
    _EXPORT_INT(m, SNDCTL_DSP_GETIPTR);
    _EXPORT_INT(m, SNDCTL_DSP_GETISPACE);
    _EXPORT_INT(m, SNDCTL_DSP_GETODELAY);
    _EXPORT_INT(m, SNDCTL_DSP_GETOPTR);
    _EXPORT_INT(m, SNDCTL_DSP_GETOSPACE);
1024
#ifdef SNDCTL_DSP_GETSPDIF
1025
    _EXPORT_INT(m, SNDCTL_DSP_GETSPDIF);
1026
#endif
1027 1028 1029 1030 1031
    _EXPORT_INT(m, SNDCTL_DSP_GETTRIGGER);
    _EXPORT_INT(m, SNDCTL_DSP_MAPINBUF);
    _EXPORT_INT(m, SNDCTL_DSP_MAPOUTBUF);
    _EXPORT_INT(m, SNDCTL_DSP_NONBLOCK);
    _EXPORT_INT(m, SNDCTL_DSP_POST);
1032
#ifdef SNDCTL_DSP_PROFILE
1033
    _EXPORT_INT(m, SNDCTL_DSP_PROFILE);
1034
#endif
1035 1036 1037 1038 1039
    _EXPORT_INT(m, SNDCTL_DSP_RESET);
    _EXPORT_INT(m, SNDCTL_DSP_SAMPLESIZE);
    _EXPORT_INT(m, SNDCTL_DSP_SETDUPLEX);
    _EXPORT_INT(m, SNDCTL_DSP_SETFMT);
    _EXPORT_INT(m, SNDCTL_DSP_SETFRAGMENT);
1040
#ifdef SNDCTL_DSP_SETSPDIF
1041
    _EXPORT_INT(m, SNDCTL_DSP_SETSPDIF);
1042
#endif
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
    _EXPORT_INT(m, SNDCTL_DSP_SETSYNCRO);
    _EXPORT_INT(m, SNDCTL_DSP_SETTRIGGER);
    _EXPORT_INT(m, SNDCTL_DSP_SPEED);
    _EXPORT_INT(m, SNDCTL_DSP_STEREO);
    _EXPORT_INT(m, SNDCTL_DSP_SUBDIVIDE);
    _EXPORT_INT(m, SNDCTL_DSP_SYNC);
    _EXPORT_INT(m, SNDCTL_FM_4OP_ENABLE);
    _EXPORT_INT(m, SNDCTL_FM_LOAD_INSTR);
    _EXPORT_INT(m, SNDCTL_MIDI_INFO);
    _EXPORT_INT(m, SNDCTL_MIDI_MPUCMD);
    _EXPORT_INT(m, SNDCTL_MIDI_MPUMODE);
    _EXPORT_INT(m, SNDCTL_MIDI_PRETIME);
    _EXPORT_INT(m, SNDCTL_SEQ_CTRLRATE);
    _EXPORT_INT(m, SNDCTL_SEQ_GETINCOUNT);
    _EXPORT_INT(m, SNDCTL_SEQ_GETOUTCOUNT);
1058
#ifdef SNDCTL_SEQ_GETTIME
1059
    _EXPORT_INT(m, SNDCTL_SEQ_GETTIME);
1060
#endif
1061 1062 1063 1064 1065 1066 1067 1068 1069 1070
    _EXPORT_INT(m, SNDCTL_SEQ_NRMIDIS);
    _EXPORT_INT(m, SNDCTL_SEQ_NRSYNTHS);
    _EXPORT_INT(m, SNDCTL_SEQ_OUTOFBAND);
    _EXPORT_INT(m, SNDCTL_SEQ_PANIC);
    _EXPORT_INT(m, SNDCTL_SEQ_PERCMODE);
    _EXPORT_INT(m, SNDCTL_SEQ_RESET);
    _EXPORT_INT(m, SNDCTL_SEQ_RESETSAMPLES);
    _EXPORT_INT(m, SNDCTL_SEQ_SYNC);
    _EXPORT_INT(m, SNDCTL_SEQ_TESTMIDI);
    _EXPORT_INT(m, SNDCTL_SEQ_THRESHOLD);
1071
#ifdef SNDCTL_SYNTH_CONTROL
1072
    _EXPORT_INT(m, SNDCTL_SYNTH_CONTROL);
1073 1074
#endif
#ifdef SNDCTL_SYNTH_ID
1075
    _EXPORT_INT(m, SNDCTL_SYNTH_ID);
1076
#endif
1077 1078
    _EXPORT_INT(m, SNDCTL_SYNTH_INFO);
    _EXPORT_INT(m, SNDCTL_SYNTH_MEMAVL);
1079
#ifdef SNDCTL_SYNTH_REMOVESAMPLE
1080
    _EXPORT_INT(m, SNDCTL_SYNTH_REMOVESAMPLE);
1081
#endif
1082 1083 1084 1085 1086 1087 1088 1089
    _EXPORT_INT(m, SNDCTL_TMR_CONTINUE);
    _EXPORT_INT(m, SNDCTL_TMR_METRONOME);
    _EXPORT_INT(m, SNDCTL_TMR_SELECT);
    _EXPORT_INT(m, SNDCTL_TMR_SOURCE);
    _EXPORT_INT(m, SNDCTL_TMR_START);
    _EXPORT_INT(m, SNDCTL_TMR_STOP);
    _EXPORT_INT(m, SNDCTL_TMR_TEMPO);
    _EXPORT_INT(m, SNDCTL_TMR_TIMEBASE);
1090
}