From 3d9994d3b0773fd07c15f5e5c3e3b6406a1165d1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 11 Dec 2002 15:12:01 +0000 Subject: Add OSS mixer interface (from Nicholas FitzRoy-Dale ): * add oss_mixer_t and OSSMixerType * add newossmixerobject(), oss_mixer_dealloc(), ossopenmixer() * add _do_ioctl_1_internal() to support mixer ioctls * add mixer methods: oss_mixer_{close,fileno,channels,stereo_channels, rec_channels,getvol,setvol,getrecsrc,setrecsrc}() * add oss_mixer_methods list * add oss_mixer_getattr() (why?!) * export SOUND_MIXER_* constants from soundcard.h --- Modules/ossaudiodev.c | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 267 insertions(+) diff --git a/Modules/ossaudiodev.c b/Modules/ossaudiodev.c index 5078904..e1b465d 100644 --- a/Modules/ossaudiodev.c +++ b/Modules/ossaudiodev.c @@ -8,6 +8,7 @@ * * 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. @@ -53,6 +54,11 @@ typedef struct { uint32_t x_afmts; /* Audio formats supported by hardware*/ } oss_t; +typedef struct { + PyObject_HEAD; + int x_fd; /* The open mixer device */ +} oss_mixer_t; + /* XXX several format defined in soundcard.h are not supported, including _NE (native endian) options and S32 options */ @@ -76,6 +82,7 @@ static struct { static int n_audio_types = sizeof(audio_types) / sizeof(audio_types[0]); static PyTypeObject OSSType; +static PyTypeObject OSSMixerType; static PyObject *OSSAudioError; @@ -154,6 +161,58 @@ oss_dealloc(oss_t *xp) PyObject_Del(xp); } +static oss_mixer_t * +newossmixerobject(PyObject *arg) +{ + char *basedev = NULL, *mode = NULL; + int fd, imode; + oss_mixer_t *xp; + + if (!PyArg_ParseTuple (arg, "|ss", &basedev, &mode)) { + return NULL; + } + + if (basedev == NULL) { + basedev = getenv("MIXERDEV"); + if (basedev == NULL) /* MIXERDEV not set */ + basedev = "/dev/mixer"; + } + + if (mode == NULL || 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; + } + + if ((fd = open (basedev, imode)) == -1) { + PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev); + return NULL; + } + + if ((xp = PyObject_New(oss_mixer_t, &OSSMixerType)) == NULL) { + close(fd); + return NULL; + } + + xp->x_fd = fd; + + return xp; +} + +static void +oss_mixer_dealloc(oss_mixer_t *xp) +{ + /* if already closed, don't reclose it */ + if (xp->x_fd != -1) + close(xp->x_fd); + PyObject_Del(xp); +} + /* Methods to wrap the OSS ioctls. The calling convention is pretty simple: @@ -188,6 +247,33 @@ _do_ioctl_1(int fd, PyObject *args, char *fname, int cmd) 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 * @@ -567,6 +653,114 @@ oss_getptr(oss_t *self, PyObject *args) return Py_BuildValue("iii", info.bytes, info.blocks, info.ptr); } +/* Mixer methods */ +static PyObject * +oss_mixer_close(oss_mixer_t *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":close")) + return NULL; + + if (self->x_fd >= 0) { + close(self->x_fd); + self->x_fd = -1; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +oss_mixer_fileno(oss_mixer_t *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":fileno")) + return NULL; + return PyInt_FromLong(self->x_fd); +} + +/* Simple mixer interface methods */ + +static PyObject * +oss_mixer_channels (oss_mixer_t *self, PyObject *args) +{ + return _do_ioctl_1_internal(self->x_fd, args, "channels", + SOUND_MIXER_READ_DEVMASK); +} + +static PyObject * +oss_mixer_stereo_channels (oss_mixer_t *self, PyObject *args) +{ + return _do_ioctl_1_internal(self->x_fd, args, "stereochannels", + SOUND_MIXER_READ_STEREODEVS); +} + +static PyObject * +oss_mixer_rec_channels (oss_mixer_t *self, PyObject *args) +{ + return _do_ioctl_1_internal(self->x_fd, args, "recchannels", + SOUND_MIXER_READ_STEREODEVS); +} + +static PyObject * +oss_mixer_getvol (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:getvol", &channel)) + return NULL; + + if (channel < 0 || channel > SOUND_MIXER_NRDEVICES) { + PyErr_SetString (OSSAudioError, "Invalid mixer channel specified."); + return NULL; + } + + if (ioctl (self->x_fd, MIXER_READ(channel), &volume) == -1) + return PyErr_SetFromErrno(PyExc_IOError); + + return Py_BuildValue ("(ii)", volume & 0xff, (volume & 0xff00) >> 8); +} + +static PyObject * +oss_mixer_setvol (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):setvol", &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->x_fd, MIXER_WRITE(channel), &volume) == -1) + return PyErr_SetFromErrno(PyExc_IOError); + + return Py_BuildValue ("(ii)", volume & 0xff, (volume & 0xff00) >> 8); +} + +static PyObject * +oss_mixer_getrecsrc (oss_mixer_t *self, PyObject *args) +{ + return _do_ioctl_1_internal(self->x_fd, args, "getrecsrc", + SOUND_MIXER_READ_RECSRC); +} + +static PyObject * +oss_mixer_setrecsrc (oss_mixer_t *self, PyObject *args) +{ + return _do_ioctl_1(self->x_fd, args, "setrecsrc", + SOUND_MIXER_WRITE_RECSRC); +} + + static PyMethodDef oss_methods[] = { /* Regular file methods */ { "read", (PyCFunction)oss_read, METH_VARARGS }, @@ -598,12 +792,35 @@ static PyMethodDef oss_methods[] = { { NULL, NULL} /* sentinel */ }; +static PyMethodDef oss_mixer_methods[] = { + /* Regular file method - OSS mixers are ioctl-only interface */ + { "close", (PyCFunction)oss_mixer_close, METH_VARARGS }, + { "fileno", (PyCFunction)oss_mixer_fileno, METH_VARARGS }, + + /* Simple ioctl wrappers */ + { "channels", (PyCFunction)oss_mixer_channels, METH_VARARGS }, + { "stereochannels", (PyCFunction)oss_mixer_stereo_channels, METH_VARARGS}, + { "recchannels", (PyCFunction)oss_mixer_rec_channels, METH_VARARGS}, + { "getvol", (PyCFunction)oss_mixer_getvol, METH_VARARGS }, + { "setvol", (PyCFunction)oss_mixer_setvol, METH_VARARGS }, + { "getrecsrc", (PyCFunction)oss_mixer_getrecsrc, METH_VARARGS }, + { "setrecsrc", (PyCFunction)oss_mixer_setrecsrc, METH_VARARGS }, + + { NULL, NULL} +}; + static PyObject * oss_getattr(oss_t *xp, char *name) { return Py_FindMethod(oss_methods, (PyObject *)xp, name); } +static PyObject * +oss_mixer_getattr(oss_mixer_t *xp, char *name) +{ + return Py_FindMethod(oss_mixer_methods, (PyObject *)xp, name); +} + static PyTypeObject OSSType = { PyObject_HEAD_INIT(&PyType_Type) 0, /*ob_size*/ @@ -619,14 +836,37 @@ static PyTypeObject OSSType = { 0, /*tp_repr*/ }; +static PyTypeObject OSSMixerType = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "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 }, }; @@ -659,6 +899,33 @@ initossaudiodev(void) _EXPORT_INT(m, AFMT_MPEG); _EXPORT_INT(m, AFMT_AC3); _EXPORT_INT(m, AFMT_S16_NE); + + /* Expose the sound mixer channels. */ + _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); /* Expose all the ioctl numbers for masochists who like to do this stuff directly. */ -- cgit v0.12