mirror of
https://github.com/python/cpython.git
synced 2025-09-04 16:01:10 +00:00
SF #818006: merge from release24-maint branch: add useful read-only
attributes to oss_audio_device object: 'closed', 'name', and 'mode'.
This commit is contained in:
parent
78be7df9e4
commit
50682d0f78
4 changed files with 101 additions and 35 deletions
|
@ -115,7 +115,7 @@ three audio parameters at once. This is more convenient, but may not be
|
||||||
as flexible in all cases.
|
as flexible in all cases.
|
||||||
|
|
||||||
The audio device objects returned by \function{open()} define the
|
The audio device objects returned by \function{open()} define the
|
||||||
following methods:
|
following methods and (read-only) attributes:
|
||||||
|
|
||||||
\begin{methoddesc}[audio device]{close}{}
|
\begin{methoddesc}[audio device]{close}{}
|
||||||
Explicitly close the audio device. When you are done writing to or
|
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.
|
buffer to be played without blocking.
|
||||||
\end{methoddesc}
|
\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}}
|
\subsection{Mixer Device Objects \label{mixer-device-objects}}
|
||||||
|
|
||||||
The mixer object provides two file-like methods:
|
The mixer object provides two file-like methods:
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
test_ossaudiodev
|
test_ossaudiodev
|
||||||
playing test sound file...
|
playing test sound file...
|
||||||
elapsed time: 2.9 sec
|
elapsed time: 3.1 sec
|
||||||
setparameters: got OSSAudioError as expected
|
|
||||||
setparameters: got OSSAudioError as expected
|
|
||||||
setparameters: got OSSAudioError as expected
|
|
||||||
|
|
|
@ -56,6 +56,19 @@ def play_sound_file(data, rate, ssize, nchannels):
|
||||||
dsp.getptr()
|
dsp.getptr()
|
||||||
dsp.fileno()
|
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
|
# set parameters based on .au file headers
|
||||||
dsp.setparameters(AFMT_S16_NE, nchannels, rate)
|
dsp.setparameters(AFMT_S16_NE, nchannels, rate)
|
||||||
t1 = time.time()
|
t1 = time.time()
|
||||||
|
@ -65,9 +78,7 @@ def play_sound_file(data, rate, ssize, nchannels):
|
||||||
t2 = time.time()
|
t2 = time.time()
|
||||||
print "elapsed time: %.1f sec" % (t2-t1)
|
print "elapsed time: %.1f sec" % (t2-t1)
|
||||||
|
|
||||||
def test_setparameters():
|
def test_setparameters(dsp):
|
||||||
dsp = ossaudiodev.open("w")
|
|
||||||
|
|
||||||
# Two configurations for testing:
|
# Two configurations for testing:
|
||||||
# config1 (8-bit, mono, 8 kHz) should work on even the most
|
# config1 (8-bit, mono, 8 kHz) should work on even the most
|
||||||
# ancient and crufty sound card, but maybe not on special-
|
# ancient and crufty sound card, but maybe not on special-
|
||||||
|
@ -96,11 +107,16 @@ def test_setparameters():
|
||||||
assert result == (fmt, channels, rate), \
|
assert result == (fmt, channels, rate), \
|
||||||
"setparameters%r: returned %r" % (config + result)
|
"setparameters%r: returned %r" % (config + result)
|
||||||
|
|
||||||
|
def test_bad_setparameters(dsp):
|
||||||
|
|
||||||
# Now try some configurations that are presumably bogus: eg. 300
|
# Now try some configurations that are presumably bogus: eg. 300
|
||||||
# channels currently exceeds even Hollywood's ambitions, and
|
# channels currently exceeds even Hollywood's ambitions, and
|
||||||
# negative sampling rate is utter nonsense. setparameters() should
|
# negative sampling rate is utter nonsense. setparameters() should
|
||||||
# accept these in non-strict mode, returning something other than
|
# accept these in non-strict mode, returning something other than
|
||||||
# was requested, but should barf in strict mode.
|
# was requested, but should barf in strict mode.
|
||||||
|
fmt = AFMT_S16_NE
|
||||||
|
rate = 44100
|
||||||
|
channels = 2
|
||||||
for config in [(fmt, 300, rate), # ridiculous nchannels
|
for config in [(fmt, 300, rate), # ridiculous nchannels
|
||||||
(fmt, -5, rate), # impossible nchannels
|
(fmt, -5, rate), # impossible nchannels
|
||||||
(fmt, channels, -50), # impossible rate
|
(fmt, channels, -50), # impossible rate
|
||||||
|
@ -119,6 +135,16 @@ def test_setparameters():
|
||||||
def test():
|
def test():
|
||||||
(data, rate, ssize, nchannels) = read_sound_file(findfile('audiotest.au'))
|
(data, rate, ssize, nchannels) = read_sound_file(findfile('audiotest.au'))
|
||||||
play_sound_file(data, rate, ssize, nchannels)
|
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()
|
test()
|
||||||
|
|
|
@ -46,11 +46,12 @@ typedef unsigned long uint32_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD;
|
PyObject_HEAD;
|
||||||
int fd; /* The open file */
|
char *devicename; /* name of the device file */
|
||||||
int mode; /* file mode */
|
int fd; /* file descriptor */
|
||||||
int icount; /* Input count */
|
int mode; /* file mode (O_RDONLY, etc.) */
|
||||||
int ocount; /* Output count */
|
int icount; /* input count */
|
||||||
uint32_t afmts; /* Audio formats supported by hardware */
|
int ocount; /* output count */
|
||||||
|
uint32_t afmts; /* audio formats supported by hardware */
|
||||||
} oss_audio_t;
|
} oss_audio_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -74,7 +75,7 @@ newossobject(PyObject *arg)
|
||||||
{
|
{
|
||||||
oss_audio_t *self;
|
oss_audio_t *self;
|
||||||
int fd, afmts, imode;
|
int fd, afmts, imode;
|
||||||
char *basedev = NULL;
|
char *devicename = NULL;
|
||||||
char *mode = NULL;
|
char *mode = NULL;
|
||||||
|
|
||||||
/* Two ways to call open():
|
/* Two ways to call open():
|
||||||
|
@ -82,11 +83,11 @@ newossobject(PyObject *arg)
|
||||||
open(mode) (for backwards compatibility)
|
open(mode) (for backwards compatibility)
|
||||||
because the *first* argument is optional, parsing args is
|
because the *first* argument is optional, parsing args is
|
||||||
a wee bit tricky. */
|
a wee bit tricky. */
|
||||||
if (!PyArg_ParseTuple(arg, "s|s:open", &basedev, &mode))
|
if (!PyArg_ParseTuple(arg, "s|s:open", &devicename, &mode))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (mode == NULL) { /* only one arg supplied */
|
if (mode == NULL) { /* only one arg supplied */
|
||||||
mode = basedev;
|
mode = devicename;
|
||||||
basedev = NULL;
|
devicename = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(mode, "r") == 0)
|
if (strcmp(mode, "r") == 0)
|
||||||
|
@ -102,18 +103,18 @@ newossobject(PyObject *arg)
|
||||||
|
|
||||||
/* Open the correct device: either the 'device' argument,
|
/* Open the correct device: either the 'device' argument,
|
||||||
or the AUDIODEV environment variable, or "/dev/dsp". */
|
or the AUDIODEV environment variable, or "/dev/dsp". */
|
||||||
if (basedev == NULL) { /* called with one arg */
|
if (devicename == NULL) { /* called with one arg */
|
||||||
basedev = getenv("AUDIODEV");
|
devicename = getenv("AUDIODEV");
|
||||||
if (basedev == NULL) /* $AUDIODEV not set */
|
if (devicename == NULL) /* $AUDIODEV not set */
|
||||||
basedev = "/dev/dsp";
|
devicename = "/dev/dsp";
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Open with O_NONBLOCK to avoid hanging on devices that only allow
|
/* 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
|
one open at a time. This does *not* affect later I/O; OSS
|
||||||
provides a special ioctl() for non-blocking read/write, which is
|
provides a special ioctl() for non-blocking read/write, which is
|
||||||
exposed via oss_nonblock() below. */
|
exposed via oss_nonblock() below. */
|
||||||
if ((fd = open(basedev, imode|O_NONBLOCK)) == -1) {
|
if ((fd = open(devicename, imode|O_NONBLOCK)) == -1) {
|
||||||
PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev);
|
PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,12 +122,12 @@ newossobject(PyObject *arg)
|
||||||
expected write() semantics. */
|
expected write() semantics. */
|
||||||
if (fcntl(fd, F_SETFL, 0) == -1) {
|
if (fcntl(fd, F_SETFL, 0) == -1) {
|
||||||
close(fd);
|
close(fd);
|
||||||
PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev);
|
PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ioctl(fd, SNDCTL_DSP_GETFMTS, &afmts) == -1) {
|
if (ioctl(fd, SNDCTL_DSP_GETFMTS, &afmts) == -1) {
|
||||||
PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev);
|
PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* Create and initialize the object */
|
/* Create and initialize the object */
|
||||||
|
@ -134,6 +135,7 @@ newossobject(PyObject *arg)
|
||||||
close(fd);
|
close(fd);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
self->devicename = devicename;
|
||||||
self->fd = fd;
|
self->fd = fd;
|
||||||
self->mode = imode;
|
self->mode = imode;
|
||||||
self->icount = self->ocount = 0;
|
self->icount = self->ocount = 0;
|
||||||
|
@ -158,22 +160,22 @@ oss_dealloc(oss_audio_t *self)
|
||||||
static oss_mixer_t *
|
static oss_mixer_t *
|
||||||
newossmixerobject(PyObject *arg)
|
newossmixerobject(PyObject *arg)
|
||||||
{
|
{
|
||||||
char *basedev = NULL;
|
char *devicename = NULL;
|
||||||
int fd;
|
int fd;
|
||||||
oss_mixer_t *self;
|
oss_mixer_t *self;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(arg, "|s", &basedev)) {
|
if (!PyArg_ParseTuple(arg, "|s", &devicename)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (basedev == NULL) {
|
if (devicename == NULL) {
|
||||||
basedev = getenv("MIXERDEV");
|
devicename = getenv("MIXERDEV");
|
||||||
if (basedev == NULL) /* MIXERDEV not set */
|
if (devicename == NULL) /* MIXERDEV not set */
|
||||||
basedev = "/dev/mixer";
|
devicename = "/dev/mixer";
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((fd = open(basedev, O_RDWR)) == -1) {
|
if ((fd = open(devicename, O_RDWR)) == -1) {
|
||||||
PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev);
|
PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -827,7 +829,33 @@ static PyMethodDef oss_mixer_methods[] = {
|
||||||
static PyObject *
|
static PyObject *
|
||||||
oss_getattr(oss_audio_t *self, char *name)
|
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 *
|
static PyObject *
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue