Skip to content

Commit e135b5c

Browse files
author
gward
committed
SF #818006: merge from release24-maint branch: add useful read-only
attributes to oss_audio_device object: 'closed', 'name', and 'mode'. git-svn-id: http://svn.python.org/projects/python/trunk@38584 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent ea5712c commit e135b5c

4 files changed

Lines changed: 101 additions & 35 deletions

File tree

Doc/lib/libossaudiodev.tex

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ \subsection{Audio Device Objects \label{ossaudio-device-objects}}
115115
as flexible in all cases.
116116

117117
The audio device objects returned by \function{open()} define the
118-
following methods:
118+
following methods and (read-only) attributes:
119119

120120
\begin{methoddesc}[audio device]{close}{}
121121
Explicitly close the audio device. When you are done writing to or
@@ -289,6 +289,21 @@ \subsection{Audio Device Objects \label{ossaudio-device-objects}}
289289
buffer to be played without blocking.
290290
\end{methoddesc}
291291

292+
Audio device objects also support several read-only attributes:
293+
294+
\begin{memberdesc}[audio device]{closed}{}
295+
Boolean indicating whether the device has been closed.
296+
\end{memberdesc}
297+
298+
\begin{memberdesc}[audio device]{name}{}
299+
String containing the name of the device file.
300+
\end{memberdesc}
301+
302+
\begin{memberdesc}[audio device]{mode}{}
303+
The I/O mode for the file, either \code{"r"}, \code{"rw"}, or \code{"w"}.
304+
\end{memberdesc}
305+
306+
292307
\subsection{Mixer Device Objects \label{mixer-device-objects}}
293308

294309
The mixer object provides two file-like methods:

Lib/test/output/test_ossaudiodev

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
11
test_ossaudiodev
22
playing test sound file...
3-
elapsed time: 2.9 sec
4-
setparameters: got OSSAudioError as expected
5-
setparameters: got OSSAudioError as expected
6-
setparameters: got OSSAudioError as expected
3+
elapsed time: 3.1 sec

Lib/test/test_ossaudiodev.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,19 @@ def play_sound_file(data, rate, ssize, nchannels):
5656
dsp.getptr()
5757
dsp.fileno()
5858

59+
# Make sure the read-only attributes work.
60+
assert dsp.closed is False, "dsp.closed is not False"
61+
assert dsp.name == "/dev/dsp"
62+
assert dsp.mode == 'w', "bad dsp.mode: %r" % dsp.mode
63+
64+
# And make sure they're really read-only.
65+
for attr in ('closed', 'name', 'mode'):
66+
try:
67+
setattr(dsp, attr, 42)
68+
raise RuntimeError("dsp.%s not read-only" % attr)
69+
except TypeError:
70+
pass
71+
5972
# set parameters based on .au file headers
6073
dsp.setparameters(AFMT_S16_NE, nchannels, rate)
6174
t1 = time.time()
@@ -65,9 +78,7 @@ def play_sound_file(data, rate, ssize, nchannels):
6578
t2 = time.time()
6679
print "elapsed time: %.1f sec" % (t2-t1)
6780

68-
def test_setparameters():
69-
dsp = ossaudiodev.open("w")
70-
81+
def test_setparameters(dsp):
7182
# Two configurations for testing:
7283
# config1 (8-bit, mono, 8 kHz) should work on even the most
7384
# ancient and crufty sound card, but maybe not on special-
@@ -96,11 +107,16 @@ def test_setparameters():
96107
assert result == (fmt, channels, rate), \
97108
"setparameters%r: returned %r" % (config + result)
98109

110+
def test_bad_setparameters(dsp):
111+
99112
# Now try some configurations that are presumably bogus: eg. 300
100113
# channels currently exceeds even Hollywood's ambitions, and
101114
# negative sampling rate is utter nonsense. setparameters() should
102115
# accept these in non-strict mode, returning something other than
103116
# was requested, but should barf in strict mode.
117+
fmt = AFMT_S16_NE
118+
rate = 44100
119+
channels = 2
104120
for config in [(fmt, 300, rate), # ridiculous nchannels
105121
(fmt, -5, rate), # impossible nchannels
106122
(fmt, channels, -50), # impossible rate
@@ -119,6 +135,16 @@ def test_setparameters():
119135
def test():
120136
(data, rate, ssize, nchannels) = read_sound_file(findfile('audiotest.au'))
121137
play_sound_file(data, rate, ssize, nchannels)
122-
test_setparameters()
138+
139+
dsp = ossaudiodev.open("w")
140+
try:
141+
test_setparameters(dsp)
142+
143+
# Disabled because it fails under Linux 2.6 with ALSA's OSS
144+
# emulation layer.
145+
#test_bad_setparameters(dsp)
146+
finally:
147+
dsp.close()
148+
assert dsp.closed is True, "dsp.closed is not True"
123149

124150
test()

Modules/ossaudiodev.c

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,12 @@ typedef unsigned long uint32_t;
4646

4747
typedef struct {
4848
PyObject_HEAD;
49-
int fd; /* The open file */
50-
int mode; /* file mode */
51-
int icount; /* Input count */
52-
int ocount; /* Output count */
53-
uint32_t afmts; /* Audio formats supported by hardware */
49+
char *devicename; /* name of the device file */
50+
int fd; /* file descriptor */
51+
int mode; /* file mode (O_RDONLY, etc.) */
52+
int icount; /* input count */
53+
int ocount; /* output count */
54+
uint32_t afmts; /* audio formats supported by hardware */
5455
} oss_audio_t;
5556

5657
typedef struct {
@@ -74,19 +75,19 @@ newossobject(PyObject *arg)
7475
{
7576
oss_audio_t *self;
7677
int fd, afmts, imode;
77-
char *basedev = NULL;
78+
char *devicename = NULL;
7879
char *mode = NULL;
7980

8081
/* Two ways to call open():
8182
open(device, mode) (for consistency with builtin open())
8283
open(mode) (for backwards compatibility)
8384
because the *first* argument is optional, parsing args is
8485
a wee bit tricky. */
85-
if (!PyArg_ParseTuple(arg, "s|s:open", &basedev, &mode))
86+
if (!PyArg_ParseTuple(arg, "s|s:open", &devicename, &mode))
8687
return NULL;
8788
if (mode == NULL) { /* only one arg supplied */
88-
mode = basedev;
89-
basedev = NULL;
89+
mode = devicename;
90+
devicename = NULL;
9091
}
9192

9293
if (strcmp(mode, "r") == 0)
@@ -102,38 +103,39 @@ newossobject(PyObject *arg)
102103

103104
/* Open the correct device: either the 'device' argument,
104105
or the AUDIODEV environment variable, or "/dev/dsp". */
105-
if (basedev == NULL) { /* called with one arg */
106-
basedev = getenv("AUDIODEV");
107-
if (basedev == NULL) /* $AUDIODEV not set */
108-
basedev = "/dev/dsp";
106+
if (devicename == NULL) { /* called with one arg */
107+
devicename = getenv("AUDIODEV");
108+
if (devicename == NULL) /* $AUDIODEV not set */
109+
devicename = "/dev/dsp";
109110
}
110111

111112
/* Open with O_NONBLOCK to avoid hanging on devices that only allow
112113
one open at a time. This does *not* affect later I/O; OSS
113114
provides a special ioctl() for non-blocking read/write, which is
114115
exposed via oss_nonblock() below. */
115-
if ((fd = open(basedev, imode|O_NONBLOCK)) == -1) {
116-
PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev);
116+
if ((fd = open(devicename, imode|O_NONBLOCK)) == -1) {
117+
PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename);
117118
return NULL;
118119
}
119120

120121
/* And (try to) put it back in blocking mode so we get the
121122
expected write() semantics. */
122123
if (fcntl(fd, F_SETFL, 0) == -1) {
123124
close(fd);
124-
PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev);
125+
PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename);
125126
return NULL;
126127
}
127128

128129
if (ioctl(fd, SNDCTL_DSP_GETFMTS, &afmts) == -1) {
129-
PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev);
130+
PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename);
130131
return NULL;
131132
}
132133
/* Create and initialize the object */
133134
if ((self = PyObject_New(oss_audio_t, &OSSAudioType)) == NULL) {
134135
close(fd);
135136
return NULL;
136137
}
138+
self->devicename = devicename;
137139
self->fd = fd;
138140
self->mode = imode;
139141
self->icount = self->ocount = 0;
@@ -158,22 +160,22 @@ oss_dealloc(oss_audio_t *self)
158160
static oss_mixer_t *
159161
newossmixerobject(PyObject *arg)
160162
{
161-
char *basedev = NULL;
163+
char *devicename = NULL;
162164
int fd;
163165
oss_mixer_t *self;
164166

165-
if (!PyArg_ParseTuple(arg, "|s", &basedev)) {
167+
if (!PyArg_ParseTuple(arg, "|s", &devicename)) {
166168
return NULL;
167169
}
168170

169-
if (basedev == NULL) {
170-
basedev = getenv("MIXERDEV");
171-
if (basedev == NULL) /* MIXERDEV not set */
172-
basedev = "/dev/mixer";
171+
if (devicename == NULL) {
172+
devicename = getenv("MIXERDEV");
173+
if (devicename == NULL) /* MIXERDEV not set */
174+
devicename = "/dev/mixer";
173175
}
174176

175-
if ((fd = open(basedev, O_RDWR)) == -1) {
176-
PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev);
177+
if ((fd = open(devicename, O_RDWR)) == -1) {
178+
PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename);
177179
return NULL;
178180
}
179181

@@ -827,7 +829,33 @@ static PyMethodDef oss_mixer_methods[] = {
827829
static PyObject *
828830
oss_getattr(oss_audio_t *self, char *name)
829831
{
830-
return Py_FindMethod(oss_methods, (PyObject *)self, name);
832+
PyObject * rval = NULL;
833+
if (strcmp(name, "closed") == 0) {
834+
rval = (self->fd == -1) ? Py_True : Py_False;
835+
Py_INCREF(rval);
836+
}
837+
else if (strcmp(name, "name") == 0) {
838+
rval = PyString_FromString(self->devicename);
839+
}
840+
else if (strcmp(name, "mode") == 0) {
841+
/* No need for a "default" in this switch: from newossobject(),
842+
self->mode can only be one of these three values. */
843+
switch(self->mode) {
844+
case O_RDONLY:
845+
rval = PyString_FromString("r");
846+
break;
847+
case O_RDWR:
848+
rval = PyString_FromString("rw");
849+
break;
850+
case O_WRONLY:
851+
rval = PyString_FromString("w");
852+
break;
853+
}
854+
}
855+
else {
856+
rval = Py_FindMethod(oss_methods, (PyObject *)self, name);
857+
}
858+
return rval;
831859
}
832860

833861
static PyObject *

0 commit comments

Comments
 (0)