Anonymous avatar Anonymous committed 41a9ca8

SF #818006: add useful read-only attributes to oss_audio_device object:
'closed', 'name', and 'mode' (as recommended by
http://python.org/doc/current/lib/bltin-file-objects.html).

Comments (0)

Files changed (4)

Doc/lib/libossaudiodev.tex

 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
 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:

Lib/test/output/test_ossaudiodev

 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

Lib/test/test_ossaudiodev.py

     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()
     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-
     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
 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()

Modules/ossaudiodev.c

 
 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 {
 {
     oss_audio_t *self;
     int fd, afmts, imode;
-    char *basedev = NULL;
+    char *devicename = NULL;
     char *mode = NULL;
 
     /* Two ways to call 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))
+    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)
 
     /* 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;
     }
 
        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 */
         close(fd);
         return NULL;
     }
+    self->devicename = devicename;
     self->fd = fd;
     self->mode = imode;
     self->icount = self->ocount = 0;
 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;
     }
 
 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 *
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.