cinap_lenrekadd hg and python4bb9e2215 years agoHistory
1138 lines · 32.0 KB · C
RawDownload
/* * 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. * Mixer interface by Nicholas FitzRoy-Dale <wzdd@lardcave.net>, Dec 2002. * * (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: ossaudiodev.c 52137 2006-10-04 10:23:57Z armin.rigo $ */ #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>#include <sys/soundcard.h> #if defined(linux) #ifndef HAVE_STDINT_Htypedef unsigned long uint32_t;#endif #elif defined(__FreeBSD__) # ifndef SNDCTL_DSP_CHANNELS#  define SNDCTL_DSP_CHANNELS SOUND_PCM_WRITE_CHANNELS# endif #endif typedef struct {    PyObject_HEAD    char    *devicename;              /* name of the device file */    int      fd;                      /* file descriptor */    int      mode;                    /* file mode (O_RDONLY, etc.) */    int      icount;                  /* input count */    int      ocount;                  /* output count */    uint32_t afmts;                   /* audio formats supported by hardware */} oss_audio_t; typedef struct {    PyObject_HEAD    int      fd;                      /* The open mixer device */} oss_mixer_t;  static PyTypeObject OSSAudioType;static PyTypeObject OSSMixerType; static PyObject *OSSAudioError;  /* ---------------------------------------------------------------------- * DSP object initialization/deallocation */ static oss_audio_t *newossobject(PyObject *arg){    oss_audio_t *self;    int fd, afmts, imode;    char *devicename = NULL;    char *mode = NULL;     /* Two ways to call open():         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", &devicename, &mode))       return NULL;    if (mode == NULL) {                 /* only one arg supplied */       mode = devicename;       devicename = NULL;    }     if (strcmp(mode, "r") == 0)        imode = O_RDONLY;    else if (strcmp(mode, "w") == 0)        imode = O_WRONLY;    else if (strcmp(mode, "rw") == 0)        imode = O_RDWR;    else {        PyErr_SetString(OSSAudioError, "mode must be 'r', 'w', or 'rw'");        return NULL;    }     /* Open the correct device: either the 'device' argument,       or the AUDIODEV environment variable, or "/dev/dsp". */    if (devicename == NULL) {              /* called with one arg */       devicename = getenv("AUDIODEV");       if (devicename == NULL)             /* $AUDIODEV not set */          devicename = "/dev/dsp";    }     /* 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(devicename, imode|O_NONBLOCK)) == -1) {        PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename);        return NULL;    }     /* 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, devicename);        return NULL;    }     if (ioctl(fd, SNDCTL_DSP_GETFMTS, &afmts) == -1) {        PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename);        return NULL;    }    /* Create and initialize the object */    if ((self = PyObject_New(oss_audio_t, &OSSAudioType)) == NULL) {        close(fd);        return NULL;    }    self->devicename = devicename;    self->fd = fd;    self->mode = imode;    self->icount = self->ocount = 0;    self->afmts  = afmts;    return self;} static voidoss_dealloc(oss_audio_t *self){    /* if already closed, don't reclose it */    if (self->fd != -1)        close(self->fd);    PyObject_Del(self);}  /* ---------------------------------------------------------------------- * Mixer object initialization/deallocation */ static oss_mixer_t *newossmixerobject(PyObject *arg){    char *devicename = NULL;    int fd;    oss_mixer_t *self;     if (!PyArg_ParseTuple(arg, "|s", &devicename)) {        return NULL;    }     if (devicename == NULL) {        devicename = getenv("MIXERDEV");        if (devicename == NULL)            /* MIXERDEV not set */            devicename = "/dev/mixer";    }     if ((fd = open(devicename, O_RDWR)) == -1) {        PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename);        return NULL;    }     if ((self = PyObject_New(oss_mixer_t, &OSSMixerType)) == NULL) {        close(fd);        return NULL;    }     self->fd = fd;     return self;} static voidoss_mixer_dealloc(oss_mixer_t *self){    /* if already closed, don't reclose it */    if (self->fd != -1)        close(self->fd);    PyObject_Del(self);}  /* 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.*/  /* ---------------------------------------------------------------------- * Helper functions */ /* _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 *_do_ioctl_1(int fd, PyObject *args, char *fname, int cmd){    char argfmt[33] = "i:";    int arg;     assert(strlen(fname) <= 30);    strcat(argfmt, fname);    if (!PyArg_ParseTuple(args, argfmt, &arg))        return NULL;     if (ioctl(fd, cmd, &arg) == -1)        return PyErr_SetFromErrno(PyExc_IOError);    return PyInt_FromLong(arg);}  /* _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))        return NULL;     if (ioctl(fd, cmd, &arg) == -1)        return PyErr_SetFromErrno(PyExc_IOError);    return PyInt_FromLong(arg);}   /* _do_ioctl_0() is a private helper for the no-argument ioctls:   SNDCTL_DSP_{SYNC,RESET,POST}. */static PyObject *_do_ioctl_0(int fd, PyObject *args, char *fname, int cmd){    char argfmt[32] = ":";    int rv;     assert(strlen(fname) <= 30);    strcat(argfmt, fname);    if (!PyArg_ParseTuple(args, argfmt))        return NULL;     /* 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)        return PyErr_SetFromErrno(PyExc_IOError);    Py_INCREF(Py_None);    return Py_None;}  /* ---------------------------------------------------------------------- * Methods of DSP objects (OSSAudioType) */ static PyObject *oss_nonblock(oss_audio_t *self, PyObject *unused){    /* Hmmm: it doesn't appear to be possible to return to blocking       mode once we're in non-blocking mode! */    if (ioctl(self->fd, SNDCTL_DSP_NONBLOCK, NULL) == -1)        return PyErr_SetFromErrno(PyExc_IOError);    Py_INCREF(Py_None);    return Py_None;} static PyObject *oss_setfmt(oss_audio_t *self, PyObject *args){    return _do_ioctl_1(self->fd, args, "setfmt", SNDCTL_DSP_SETFMT);} static PyObject *oss_getfmts(oss_audio_t *self, PyObject *unused){    int mask;    if (ioctl(self->fd, SNDCTL_DSP_GETFMTS, &mask) == -1)        return PyErr_SetFromErrno(PyExc_IOError);    return PyInt_FromLong(mask);} static PyObject *oss_channels(oss_audio_t *self, PyObject *args){    return _do_ioctl_1(self->fd, args, "channels", SNDCTL_DSP_CHANNELS);} static PyObject *oss_speed(oss_audio_t *self, PyObject *args){    return _do_ioctl_1(self->fd, args, "speed", SNDCTL_DSP_SPEED);} static PyObject *oss_sync(oss_audio_t *self, PyObject *args){    return _do_ioctl_0(self->fd, args, "sync", SNDCTL_DSP_SYNC);} static PyObject *oss_reset(oss_audio_t *self, PyObject *args){    return _do_ioctl_0(self->fd, args, "reset", SNDCTL_DSP_RESET);} static PyObject *oss_post(oss_audio_t *self, PyObject *args){    return _do_ioctl_0(self->fd, args, "post", SNDCTL_DSP_POST);}  /* Regular file methods: read(), write(), close(), etc. as well   as one convenience method, writeall(). */ static PyObject *oss_read(oss_audio_t *self, PyObject *args){    int size, count;    char *cp;    PyObject *rv;     if (!PyArg_ParseTuple(args, "i:read", &size))        return NULL;    rv = PyString_FromStringAndSize(NULL, size);    if (rv == NULL)        return NULL;    cp = PyString_AS_STRING(rv);     Py_BEGIN_ALLOW_THREADS    count = read(self->fd, cp, size);    Py_END_ALLOW_THREADS     if (count < 0) {        PyErr_SetFromErrno(PyExc_IOError);        Py_DECREF(rv);        return NULL;    }    self->icount += count;    _PyString_Resize(&rv, count);    return rv;} static PyObject *oss_write(oss_audio_t *self, PyObject *args){    char *cp;    int rv, size;     if (!PyArg_ParseTuple(args, "s#:write", &cp, &size)) {        return NULL;    }     Py_BEGIN_ALLOW_THREADS    rv = write(self->fd, cp, size);    Py_END_ALLOW_THREADS     if (rv == -1) {        return PyErr_SetFromErrno(PyExc_IOError);    } else {        self->ocount += rv;    }    return PyInt_FromLong(rv);} static PyObject *oss_writeall(oss_audio_t *self, PyObject *args){    char *cp;    int rv, size;    fd_set write_set_fds;    int select_rv;     /* 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. */     if (!PyArg_ParseTuple(args, "s#:write", &cp, &size))        return NULL;     /* use select to wait for audio device to be available */    FD_ZERO(&write_set_fds);    FD_SET(self->fd, &write_set_fds);     while (size > 0) {        Py_BEGIN_ALLOW_THREADS        select_rv = select(self->fd+1, NULL, &write_set_fds, NULL, NULL);        Py_END_ALLOW_THREADS        assert(select_rv != 0);         /* no timeout, can't expire */        if (select_rv == -1)            return PyErr_SetFromErrno(PyExc_IOError);         Py_BEGIN_ALLOW_THREADS        rv = write(self->fd, cp, size);        Py_END_ALLOW_THREADS        if (rv == -1) {            if (errno == EAGAIN) {      /* buffer is full, try again */                errno = 0;                continue;            } else                      /* it's a real error */                return PyErr_SetFromErrno(PyExc_IOError);        } else {                        /* wrote rv bytes */            self->ocount += rv;            size -= rv;            cp += rv;        }    }    Py_INCREF(Py_None);    return Py_None;} static PyObject *oss_close(oss_audio_t *self, PyObject *unused){    if (self->fd >= 0) {        Py_BEGIN_ALLOW_THREADS        close(self->fd);        Py_END_ALLOW_THREADS        self->fd = -1;    }    Py_INCREF(Py_None);    return Py_None;} static PyObject *oss_fileno(oss_audio_t *self, PyObject *unused){    return PyInt_FromLong(self->fd);}  /* Convenience methods: these generally wrap a couple of ioctls into one   common task. */ static PyObject *oss_setparameters(oss_audio_t *self, PyObject *args){    int wanted_fmt, wanted_channels, wanted_rate, strict=0;    int fmt, channels, rate;    PyObject * rv;                    /* return tuple (fmt, channels, rate) */     if (!PyArg_ParseTuple(args, "iii|i:setparameters",                          &wanted_fmt, &wanted_channels, &wanted_rate,                          &strict))        return NULL;     fmt = wanted_fmt;    if (ioctl(self->fd, SNDCTL_DSP_SETFMT, &fmt) == -1) {        return PyErr_SetFromErrno(PyExc_IOError);    }    if (strict && fmt != wanted_fmt) {        return PyErr_Format            (OSSAudioError,

Lines 1–500 of 1138.