diff -r ffa851df0825 -r 2fb8b9db1c86 symbian-qemu-0.9.1-12/python-2.6.1/Modules/ossaudiodev.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/symbian-qemu-0.9.1-12/python-2.6.1/Modules/ossaudiodev.c Fri Jul 31 15:01:17 2009 +0100 @@ -0,0 +1,1136 @@ +/* + * 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 , November 2002. + * Mixer interface by Nicholas FitzRoy-Dale , 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 64048 2008-06-09 04:58:54Z gregory.p.smith $ + */ + +#include "Python.h" +#include "structmember.h" + +#ifdef HAVE_FCNTL_H +#include +#else +#define O_RDONLY 00 +#define O_WRONLY 01 +#endif + +#include +#include + +#if defined(linux) + +#ifndef HAVE_STDINT_H +typedef 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 void +oss_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 void +oss_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 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, + "unable to set requested format (wanted %d, got %d)", + wanted_fmt, fmt); + } + + channels = wanted_channels; + if (ioctl(self->fd, SNDCTL_DSP_CHANNELS, &channels) == -1) { + return PyErr_SetFromErrno(PyExc_IOError); + } + if (strict && channels != wanted_channels) { + return PyErr_Format + (OSSAudioError, + "unable to set requested channels (wanted %d, got %d)", + wanted_channels, channels); + } + + rate = wanted_rate; + if (ioctl(self->fd, SNDCTL_DSP_SPEED, &rate) == -1) { + return PyErr_SetFromErrno(PyExc_IOError); + } + if (strict && rate != wanted_rate) { + return PyErr_Format + (OSSAudioError, + "unable to set requested rate (wanted %d, got %d)", + wanted_rate, rate); + } + + /* 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) + return NULL; + 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; +} + +static int +_ssize(oss_audio_t *self, int *nchannels, int *ssize) +{ + int fmt; + + fmt = 0; + if (ioctl(self->fd, SNDCTL_DSP_SETFMT, &fmt) < 0) + return -errno; + + switch (fmt) { + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: + *ssize = 1; /* 8 bit formats: 1 byte */ + break; + case AFMT_S16_LE: + case AFMT_S16_BE: + case AFMT_U16_LE: + case AFMT_U16_BE: + *ssize = 2; /* 16 bit formats: 2 byte */ + break; + case AFMT_MPEG: + case AFMT_IMA_ADPCM: + default: + return -EOPNOTSUPP; + } + if (ioctl(self->fd, SNDCTL_DSP_CHANNELS, nchannels) < 0) + return -errno; + return 0; +} + + +/* bufsize returns the size of the hardware audio buffer in number + of samples */ +static PyObject * +oss_bufsize(oss_audio_t *self, PyObject *unused) +{ + audio_buf_info ai; + int nchannels=0, ssize=0; + + if (_ssize(self, &nchannels, &ssize) < 0 || !nchannels || !ssize) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + if (ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &ai) < 0) { + PyErr_SetFromErrno(PyExc_IOError); + 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 * +oss_obufcount(oss_audio_t *self, PyObject *unused) +{ + audio_buf_info ai; + int nchannels=0, ssize=0; + + if (_ssize(self, &nchannels, &ssize) < 0 || !nchannels || !ssize) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + if (ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &ai) < 0) { + PyErr_SetFromErrno(PyExc_IOError); + 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 * +oss_obuffree(oss_audio_t *self, PyObject *unused) +{ + audio_buf_info ai; + int nchannels=0, ssize=0; + + if (_ssize(self, &nchannels, &ssize) < 0 || !nchannels || !ssize) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + if (ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &ai) < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + return PyInt_FromLong(ai.bytes / (ssize * nchannels)); +} + +static PyObject * +oss_getptr(oss_audio_t *self, PyObject *unused) +{ + count_info info; + int req; + + if (self->mode == O_RDONLY) + req = SNDCTL_DSP_GETIPTR; + else + req = SNDCTL_DSP_GETOPTR; + if (ioctl(self->fd, req, &info) == -1) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + return Py_BuildValue("iii", info.bytes, info.blocks, info.ptr); +} + + +/* ---------------------------------------------------------------------- + * Methods of mixer objects (OSSMixerType) + */ + +static PyObject * +oss_mixer_close(oss_mixer_t *self, PyObject *unused) +{ + if (self->fd >= 0) { + close(self->fd); + self->fd = -1; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +oss_mixer_fileno(oss_mixer_t *self, PyObject *unused) +{ + return PyInt_FromLong(self->fd); +} + +/* Simple mixer interface methods */ + +static PyObject * +oss_mixer_controls(oss_mixer_t *self, PyObject *args) +{ + return _do_ioctl_1_internal(self->fd, args, "controls", + SOUND_MIXER_READ_DEVMASK); +} + +static PyObject * +oss_mixer_stereocontrols(oss_mixer_t *self, PyObject *args) +{ + return _do_ioctl_1_internal(self->fd, args, "stereocontrols", + SOUND_MIXER_READ_STEREODEVS); +} + +static PyObject * +oss_mixer_reccontrols(oss_mixer_t *self, PyObject *args) +{ + return _do_ioctl_1_internal(self->fd, args, "reccontrols", + SOUND_MIXER_READ_RECMASK); +} + +static PyObject * +oss_mixer_get(oss_mixer_t *self, PyObject *args) +{ + int channel, volume; + + /* Can't use _do_ioctl_1 because of encoded arg thingy. */ + if (!PyArg_ParseTuple(args, "i:get", &channel)) + return NULL; + + if (channel < 0 || channel > SOUND_MIXER_NRDEVICES) { + PyErr_SetString(OSSAudioError, "Invalid mixer channel specified."); + return NULL; + } + + if (ioctl(self->fd, MIXER_READ(channel), &volume) == -1) + return PyErr_SetFromErrno(PyExc_IOError); + + return Py_BuildValue("(ii)", volume & 0xff, (volume & 0xff00) >> 8); +} + +static PyObject * +oss_mixer_set(oss_mixer_t *self, PyObject *args) +{ + int channel, volume, leftVol, rightVol; + + /* Can't use _do_ioctl_1 because of encoded arg thingy. */ + if (!PyArg_ParseTuple(args, "i(ii):set", &channel, &leftVol, &rightVol)) + return NULL; + + if (channel < 0 || channel > SOUND_MIXER_NRDEVICES) { + PyErr_SetString(OSSAudioError, "Invalid mixer channel specified."); + return NULL; + } + + if (leftVol < 0 || rightVol < 0 || leftVol > 100 || rightVol > 100) { + PyErr_SetString(OSSAudioError, "Volumes must be between 0 and 100."); + return NULL; + } + + volume = (rightVol << 8) | leftVol; + + if (ioctl(self->fd, MIXER_WRITE(channel), &volume) == -1) + return PyErr_SetFromErrno(PyExc_IOError); + + return Py_BuildValue("(ii)", volume & 0xff, (volume & 0xff00) >> 8); +} + +static PyObject * +oss_mixer_get_recsrc(oss_mixer_t *self, PyObject *args) +{ + return _do_ioctl_1_internal(self->fd, args, "get_recsrc", + SOUND_MIXER_READ_RECSRC); +} + +static PyObject * +oss_mixer_set_recsrc(oss_mixer_t *self, PyObject *args) +{ + return _do_ioctl_1(self->fd, args, "set_recsrc", + SOUND_MIXER_WRITE_RECSRC); +} + + +/* ---------------------------------------------------------------------- + * Method tables and other bureaucracy + */ + +static PyMethodDef oss_methods[] = { + /* Regular file methods */ + { "read", (PyCFunction)oss_read, METH_VARARGS }, + { "write", (PyCFunction)oss_write, METH_VARARGS }, + { "writeall", (PyCFunction)oss_writeall, METH_VARARGS }, + { "close", (PyCFunction)oss_close, METH_NOARGS }, + { "fileno", (PyCFunction)oss_fileno, METH_NOARGS }, + + /* Simple ioctl wrappers */ + { "nonblock", (PyCFunction)oss_nonblock, METH_NOARGS }, + { "setfmt", (PyCFunction)oss_setfmt, METH_VARARGS }, + { "getfmts", (PyCFunction)oss_getfmts, METH_NOARGS }, + { "channels", (PyCFunction)oss_channels, METH_VARARGS }, + { "speed", (PyCFunction)oss_speed, METH_VARARGS }, + { "sync", (PyCFunction)oss_sync, METH_VARARGS }, + { "reset", (PyCFunction)oss_reset, METH_VARARGS }, + { "post", (PyCFunction)oss_post, METH_VARARGS }, + + /* Convenience methods -- wrap a couple of ioctls together */ + { "setparameters", (PyCFunction)oss_setparameters, METH_VARARGS }, + { "bufsize", (PyCFunction)oss_bufsize, METH_NOARGS }, + { "obufcount", (PyCFunction)oss_obufcount, METH_NOARGS }, + { "obuffree", (PyCFunction)oss_obuffree, METH_NOARGS }, + { "getptr", (PyCFunction)oss_getptr, METH_NOARGS }, + + /* Aliases for backwards compatibility */ + { "flush", (PyCFunction)oss_sync, METH_VARARGS }, + + { NULL, NULL} /* sentinel */ +}; + +static PyMethodDef oss_mixer_methods[] = { + /* Regular file method - OSS mixers are ioctl-only interface */ + { "close", (PyCFunction)oss_mixer_close, METH_NOARGS }, + { "fileno", (PyCFunction)oss_mixer_fileno, METH_NOARGS }, + + /* Simple ioctl wrappers */ + { "controls", (PyCFunction)oss_mixer_controls, METH_VARARGS }, + { "stereocontrols", (PyCFunction)oss_mixer_stereocontrols, METH_VARARGS}, + { "reccontrols", (PyCFunction)oss_mixer_reccontrols, METH_VARARGS}, + { "get", (PyCFunction)oss_mixer_get, METH_VARARGS }, + { "set", (PyCFunction)oss_mixer_set, METH_VARARGS }, + { "get_recsrc", (PyCFunction)oss_mixer_get_recsrc, METH_VARARGS }, + { "set_recsrc", (PyCFunction)oss_mixer_set_recsrc, METH_VARARGS }, + + { NULL, NULL} +}; + +static PyObject * +oss_getattr(oss_audio_t *self, char *name) +{ + PyObject * rval = NULL; + if (strcmp(name, "closed") == 0) { + rval = (self->fd == -1) ? Py_True : Py_False; + Py_INCREF(rval); + } + else if (strcmp(name, "name") == 0) { + rval = PyString_FromString(self->devicename); + } + else if (strcmp(name, "mode") == 0) { + /* No need for a "default" in this switch: from newossobject(), + self->mode can only be one of these three values. */ + switch(self->mode) { + case O_RDONLY: + rval = PyString_FromString("r"); + break; + case O_RDWR: + rval = PyString_FromString("rw"); + break; + case O_WRONLY: + rval = PyString_FromString("w"); + break; + } + } + else { + rval = Py_FindMethod(oss_methods, (PyObject *)self, name); + } + return rval; +} + +static PyObject * +oss_mixer_getattr(oss_mixer_t *self, char *name) +{ + return Py_FindMethod(oss_mixer_methods, (PyObject *)self, name); +} + +static PyTypeObject OSSAudioType = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "ossaudiodev.oss_audio_device", /*tp_name*/ + sizeof(oss_audio_t), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)oss_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)oss_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ +}; + +static PyTypeObject OSSMixerType = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "ossaudiodev.oss_mixer_device", /*tp_name*/ + sizeof(oss_mixer_t), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)oss_mixer_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)oss_mixer_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ +}; + + +static PyObject * +ossopen(PyObject *self, PyObject *args) +{ + return (PyObject *)newossobject(args); +} + +static PyObject * +ossopenmixer(PyObject *self, PyObject *args) +{ + return (PyObject *)newossmixerobject(args); +} + +static PyMethodDef ossaudiodev_methods[] = { + { "open", ossopen, METH_VARARGS }, + { "openmixer", ossopenmixer, METH_VARARGS }, + { 0, 0 }, +}; + + +#define _EXPORT_INT(mod, name) \ + if (PyModule_AddIntConstant(mod, #name, (long) (name)) == -1) return; + + +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); + if (labels == NULL || names == NULL) + goto error2; + for (i = 0; i < num_controls; i++) { + s = PyString_FromString(control_labels[i]); + if (s == NULL) + goto error2; + PyList_SET_ITEM(labels, i, s); + + s = PyString_FromString(control_names[i]); + if (s == NULL) + goto error2; + PyList_SET_ITEM(names, i, s); + } + + if (PyModule_AddObject(module, "control_labels", labels) == -1) + goto error2; + if (PyModule_AddObject(module, "control_names", names) == -1) + goto error1; + + return 0; + +error2: + Py_XDECREF(labels); +error1: + Py_XDECREF(names); + return -1; +} + + +void +initossaudiodev(void) +{ + PyObject *m; + + m = Py_InitModule("ossaudiodev", ossaudiodev_methods); + if (m == NULL) + return; + + OSSAudioError = PyErr_NewException("ossaudiodev.OSSAudioError", + NULL, NULL); + if (OSSAudioError) { + /* Each call to PyModule_AddObject decrefs it; compensate: */ + Py_INCREF(OSSAudioError); + Py_INCREF(OSSAudioError); + PyModule_AddObject(m, "error", OSSAudioError); + PyModule_AddObject(m, "OSSAudioError", OSSAudioError); + } + + /* Build 'control_labels' and 'control_names' lists and add them + to the module. */ + if (build_namelists(m) == -1) /* XXX what to do here? */ + return; + + /* 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); +#ifdef AFMT_AC3 + _EXPORT_INT(m, AFMT_AC3); +#endif +#ifdef AFMT_S16_NE + _EXPORT_INT(m, AFMT_S16_NE); +#endif +#ifdef AFMT_U16_NE + _EXPORT_INT(m, AFMT_U16_NE); +#endif +#ifdef AFMT_S32_LE + _EXPORT_INT(m, AFMT_S32_LE); +#endif +#ifdef AFMT_S32_BE + _EXPORT_INT(m, AFMT_S32_BE); +#endif +#ifdef AFMT_MPEG + _EXPORT_INT(m, AFMT_MPEG); +#endif + + /* Expose the sound mixer device numbers. */ + _EXPORT_INT(m, SOUND_MIXER_NRDEVICES); + _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); +#ifdef SOUND_MIXER_DIGITAL1 + _EXPORT_INT(m, SOUND_MIXER_DIGITAL1); +#endif +#ifdef SOUND_MIXER_DIGITAL2 + _EXPORT_INT(m, SOUND_MIXER_DIGITAL2); +#endif +#ifdef SOUND_MIXER_DIGITAL3 + _EXPORT_INT(m, SOUND_MIXER_DIGITAL3); +#endif +#ifdef SOUND_MIXER_PHONEIN + _EXPORT_INT(m, SOUND_MIXER_PHONEIN); +#endif +#ifdef SOUND_MIXER_PHONEOUT + _EXPORT_INT(m, SOUND_MIXER_PHONEOUT); +#endif +#ifdef SOUND_MIXER_VIDEO + _EXPORT_INT(m, SOUND_MIXER_VIDEO); +#endif +#ifdef SOUND_MIXER_RADIO + _EXPORT_INT(m, SOUND_MIXER_RADIO); +#endif +#ifdef SOUND_MIXER_MONITOR + _EXPORT_INT(m, SOUND_MIXER_MONITOR); +#endif + + /* 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); +#ifdef SNDCTL_DSP_BIND_CHANNEL + _EXPORT_INT(m, SNDCTL_DSP_BIND_CHANNEL); +#endif + _EXPORT_INT(m, SNDCTL_DSP_CHANNELS); + _EXPORT_INT(m, SNDCTL_DSP_GETBLKSIZE); + _EXPORT_INT(m, SNDCTL_DSP_GETCAPS); +#ifdef SNDCTL_DSP_GETCHANNELMASK + _EXPORT_INT(m, SNDCTL_DSP_GETCHANNELMASK); +#endif + _EXPORT_INT(m, SNDCTL_DSP_GETFMTS); + _EXPORT_INT(m, SNDCTL_DSP_GETIPTR); + _EXPORT_INT(m, SNDCTL_DSP_GETISPACE); +#ifdef SNDCTL_DSP_GETODELAY + _EXPORT_INT(m, SNDCTL_DSP_GETODELAY); +#endif + _EXPORT_INT(m, SNDCTL_DSP_GETOPTR); + _EXPORT_INT(m, SNDCTL_DSP_GETOSPACE); +#ifdef SNDCTL_DSP_GETSPDIF + _EXPORT_INT(m, SNDCTL_DSP_GETSPDIF); +#endif + _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); +#ifdef SNDCTL_DSP_PROFILE + _EXPORT_INT(m, SNDCTL_DSP_PROFILE); +#endif + _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); +#ifdef SNDCTL_DSP_SETSPDIF + _EXPORT_INT(m, SNDCTL_DSP_SETSPDIF); +#endif + _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); +#ifdef SNDCTL_SEQ_GETTIME + _EXPORT_INT(m, SNDCTL_SEQ_GETTIME); +#endif + _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); +#ifdef SNDCTL_SYNTH_CONTROL + _EXPORT_INT(m, SNDCTL_SYNTH_CONTROL); +#endif +#ifdef SNDCTL_SYNTH_ID + _EXPORT_INT(m, SNDCTL_SYNTH_ID); +#endif + _EXPORT_INT(m, SNDCTL_SYNTH_INFO); + _EXPORT_INT(m, SNDCTL_SYNTH_MEMAVL); +#ifdef SNDCTL_SYNTH_REMOVESAMPLE + _EXPORT_INT(m, SNDCTL_SYNTH_REMOVESAMPLE); +#endif + _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); +}