From 50682d0f78a06cff01a9b769128e30c8e0e37c0c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 7 Mar 2005 01:41:11 +0000 Subject: [PATCH] SF #818006: merge from release24-maint branch: add useful read-only attributes to oss_audio_device object: 'closed', 'name', and 'mode'. --- Doc/lib/libossaudiodev.tex | 17 ++++++- Lib/test/output/test_ossaudiodev | 5 +- Lib/test/test_ossaudiodev.py | 34 ++++++++++++-- Modules/ossaudiodev.c | 80 +++++++++++++++++++++----------- 4 files changed, 101 insertions(+), 35 deletions(-) diff --git a/Doc/lib/libossaudiodev.tex b/Doc/lib/libossaudiodev.tex index 8c8e445bb26..762acb766da 100644 --- a/Doc/lib/libossaudiodev.tex +++ b/Doc/lib/libossaudiodev.tex @@ -115,7 +115,7 @@ three audio parameters at once. This is more convenient, but may not be as flexible in all cases. The audio device objects returned by \function{open()} define the -following methods: +following methods and (read-only) attributes: \begin{methoddesc}[audio device]{close}{} Explicitly close the audio device. When you are done writing to or @@ -289,6 +289,21 @@ Returns the number of samples that could be queued into the hardware buffer to be played without blocking. \end{methoddesc} +Audio device objects also support several read-only attributes: + +\begin{memberdesc}[audio device]{closed}{} +Boolean indicating whether the device has been closed. +\end{memberdesc} + +\begin{memberdesc}[audio device]{name}{} +String containing the name of the device file. +\end{memberdesc} + +\begin{memberdesc}[audio device]{mode}{} +The I/O mode for the file, either \code{"r"}, \code{"rw"}, or \code{"w"}. +\end{memberdesc} + + \subsection{Mixer Device Objects \label{mixer-device-objects}} The mixer object provides two file-like methods: diff --git a/Lib/test/output/test_ossaudiodev b/Lib/test/output/test_ossaudiodev index 6903012134a..9f55afa4cb3 100644 --- a/Lib/test/output/test_ossaudiodev +++ b/Lib/test/output/test_ossaudiodev @@ -1,6 +1,3 @@ test_ossaudiodev playing test sound file... -elapsed time: 2.9 sec -setparameters: got OSSAudioError as expected -setparameters: got OSSAudioError as expected -setparameters: got OSSAudioError as expected +elapsed time: 3.1 sec diff --git a/Lib/test/test_ossaudiodev.py b/Lib/test/test_ossaudiodev.py index 3067c03de0d..f668eb41d7e 100644 --- a/Lib/test/test_ossaudiodev.py +++ b/Lib/test/test_ossaudiodev.py @@ -56,6 +56,19 @@ def play_sound_file(data, rate, ssize, nchannels): dsp.getptr() dsp.fileno() + # Make sure the read-only attributes work. + assert dsp.closed is False, "dsp.closed is not False" + assert dsp.name == "/dev/dsp" + assert dsp.mode == 'w', "bad dsp.mode: %r" % dsp.mode + + # And make sure they're really read-only. + for attr in ('closed', 'name', 'mode'): + try: + setattr(dsp, attr, 42) + raise RuntimeError("dsp.%s not read-only" % attr) + except TypeError: + pass + # set parameters based on .au file headers dsp.setparameters(AFMT_S16_NE, nchannels, rate) t1 = time.time() @@ -65,9 +78,7 @@ def play_sound_file(data, rate, ssize, nchannels): t2 = time.time() print "elapsed time: %.1f sec" % (t2-t1) -def test_setparameters(): - dsp = ossaudiodev.open("w") - +def test_setparameters(dsp): # Two configurations for testing: # config1 (8-bit, mono, 8 kHz) should work on even the most # ancient and crufty sound card, but maybe not on special- @@ -96,11 +107,16 @@ def test_setparameters(): assert result == (fmt, channels, rate), \ "setparameters%r: returned %r" % (config + result) +def test_bad_setparameters(dsp): + # Now try some configurations that are presumably bogus: eg. 300 # channels currently exceeds even Hollywood's ambitions, and # negative sampling rate is utter nonsense. setparameters() should # accept these in non-strict mode, returning something other than # was requested, but should barf in strict mode. + fmt = AFMT_S16_NE + rate = 44100 + channels = 2 for config in [(fmt, 300, rate), # ridiculous nchannels (fmt, -5, rate), # impossible nchannels (fmt, channels, -50), # impossible rate @@ -119,6 +135,16 @@ def test_setparameters(): def test(): (data, rate, ssize, nchannels) = read_sound_file(findfile('audiotest.au')) play_sound_file(data, rate, ssize, nchannels) - test_setparameters() + + dsp = ossaudiodev.open("w") + try: + test_setparameters(dsp) + + # Disabled because it fails under Linux 2.6 with ALSA's OSS + # emulation layer. + #test_bad_setparameters(dsp) + finally: + dsp.close() + assert dsp.closed is True, "dsp.closed is not True" test() diff --git a/Modules/ossaudiodev.c b/Modules/ossaudiodev.c index 047355fb784..43bd92b53be 100644 --- a/Modules/ossaudiodev.c +++ b/Modules/ossaudiodev.c @@ -46,11 +46,12 @@ typedef unsigned long uint32_t; typedef struct { PyObject_HEAD; - 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 */ + 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 { @@ -74,7 +75,7 @@ newossobject(PyObject *arg) { oss_audio_t *self; int fd, afmts, imode; - char *basedev = NULL; + char *devicename = NULL; char *mode = NULL; /* Two ways to call open(): @@ -82,11 +83,11 @@ newossobject(PyObject *arg) 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)) + if (!PyArg_ParseTuple(arg, "s|s:open", &devicename, &mode)) return NULL; if (mode == NULL) { /* only one arg supplied */ - mode = basedev; - basedev = NULL; + mode = devicename; + devicename = NULL; } if (strcmp(mode, "r") == 0) @@ -102,18 +103,18 @@ newossobject(PyObject *arg) /* Open the correct device: either the 'device' argument, or the AUDIODEV environment variable, or "/dev/dsp". */ - if (basedev == NULL) { /* called with one arg */ - basedev = getenv("AUDIODEV"); - if (basedev == NULL) /* $AUDIODEV not set */ - basedev = "/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(basedev, imode|O_NONBLOCK)) == -1) { - PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev); + if ((fd = open(devicename, imode|O_NONBLOCK)) == -1) { + PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename); return NULL; } @@ -121,12 +122,12 @@ newossobject(PyObject *arg) expected write() semantics. */ if (fcntl(fd, F_SETFL, 0) == -1) { close(fd); - PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev); + PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename); return NULL; } if (ioctl(fd, SNDCTL_DSP_GETFMTS, &afmts) == -1) { - PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev); + PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename); return NULL; } /* Create and initialize the object */ @@ -134,6 +135,7 @@ newossobject(PyObject *arg) close(fd); return NULL; } + self->devicename = devicename; self->fd = fd; self->mode = imode; self->icount = self->ocount = 0; @@ -158,22 +160,22 @@ oss_dealloc(oss_audio_t *self) static oss_mixer_t * newossmixerobject(PyObject *arg) { - char *basedev = NULL; + char *devicename = NULL; int fd; oss_mixer_t *self; - if (!PyArg_ParseTuple(arg, "|s", &basedev)) { + if (!PyArg_ParseTuple(arg, "|s", &devicename)) { return NULL; } - if (basedev == NULL) { - basedev = getenv("MIXERDEV"); - if (basedev == NULL) /* MIXERDEV not set */ - basedev = "/dev/mixer"; + if (devicename == NULL) { + devicename = getenv("MIXERDEV"); + if (devicename == NULL) /* MIXERDEV not set */ + devicename = "/dev/mixer"; } - if ((fd = open(basedev, O_RDWR)) == -1) { - PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev); + if ((fd = open(devicename, O_RDWR)) == -1) { + PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename); return NULL; } @@ -827,7 +829,33 @@ static PyMethodDef oss_mixer_methods[] = { static PyObject * oss_getattr(oss_audio_t *self, char *name) { - return Py_FindMethod(oss_methods, (PyObject *)self, 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 *