mirror of
https://github.com/larsimmisch/pyalsaaudio.git
synced 2026-06-01 02:47:01 +00:00
Allow card index or device name for mixers.
This change breaks API compatibility. Sorry.
This commit is contained in:
+48
-33
@@ -91,19 +91,6 @@ char *translate_cardname(char *name)
|
||||
return full;
|
||||
}
|
||||
|
||||
/* Translate a card index to a ALSA cardname
|
||||
|
||||
Returns a newly allocated string.
|
||||
*/
|
||||
char *translate_cardidx(int idx)
|
||||
{
|
||||
char name[32];
|
||||
|
||||
sprintf(name, "hw:%d", idx);
|
||||
|
||||
return strdup(name);
|
||||
}
|
||||
|
||||
/******************************************/
|
||||
/* PCM object wrapper */
|
||||
/******************************************/
|
||||
@@ -927,26 +914,39 @@ alsamixer_gethandle(char *cardname, snd_mixer_t **handle)
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
alsamixer_list(PyObject *self, PyObject *args)
|
||||
alsamixer_list(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
snd_mixer_t *handle;
|
||||
snd_mixer_selem_id_t *sid;
|
||||
snd_mixer_elem_t *elem;
|
||||
int err;
|
||||
int cardidx = 0;
|
||||
char cardname[32];
|
||||
int cardidx = -1;
|
||||
char hw_device[32];
|
||||
char *device = "default";
|
||||
PyObject *result;
|
||||
|
||||
if (!PyArg_ParseTuple(args,"|i:mixers",&cardidx))
|
||||
return NULL;
|
||||
|
||||
sprintf(cardname, "hw:%d", cardidx);
|
||||
|
||||
char *kw[] = { "device", "cardindex", NULL };
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|si", kw,
|
||||
&device, &cardidx))
|
||||
return NULL;
|
||||
|
||||
if (cardidx >= 0) {
|
||||
if (cardidx >= 0 && cardidx < 32) {
|
||||
snprintf(hw_device, sizeof(hw_device), "hw:%d", cardidx);
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(ALSAAudioError, "Invalid card number");
|
||||
return NULL;
|
||||
}
|
||||
device = hw_device;
|
||||
}
|
||||
|
||||
snd_mixer_selem_id_alloca(&sid);
|
||||
err = alsamixer_gethandle(cardname, &handle);
|
||||
err = alsamixer_gethandle(device, &handle);
|
||||
if (err < 0)
|
||||
{
|
||||
PyErr_SetString(ALSAAudioError,snd_strerror(err));
|
||||
PyErr_SetString(ALSAAudioError, snd_strerror(err));
|
||||
snd_mixer_close(handle);
|
||||
return NULL;
|
||||
}
|
||||
@@ -991,22 +991,35 @@ alsamixer_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
alsamixer_t *self;
|
||||
int err;
|
||||
int cardindex = 0;
|
||||
char *control = "Master";
|
||||
char *device = "default";
|
||||
char hw_device[32];
|
||||
int cardidx = -1;
|
||||
int id = 0;
|
||||
snd_mixer_elem_t *elem;
|
||||
int channel;
|
||||
char *kw[] = { "control", "id", "cardindex", NULL };
|
||||
char *kw[] = { "control", "id", "device", "cardindex", NULL };
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sii", kw,
|
||||
&control, &id, &cardindex))
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sisi", kw,
|
||||
&control, &id, &device, &cardidx))
|
||||
return NULL;
|
||||
|
||||
if (cardidx >= 0) {
|
||||
if (cardidx >= 0 && cardidx < 32) {
|
||||
snprintf(hw_device, sizeof(hw_device), "hw:%d", cardidx);
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(ALSAAudioError, "Invalid card number");
|
||||
return NULL;
|
||||
}
|
||||
device = hw_device;
|
||||
}
|
||||
|
||||
if (!(self = (alsamixer_t *)PyObject_New(alsamixer_t, &ALSAMixerType)))
|
||||
return NULL;
|
||||
|
||||
self->handle = 0;
|
||||
self->cardname = translate_cardidx(cardindex);
|
||||
self->cardname = strdup(device);
|
||||
|
||||
err = alsamixer_gethandle(self->cardname, &self->handle);
|
||||
if (err<0)
|
||||
@@ -1019,15 +1032,17 @@ alsamixer_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
self->controlname = strdup(control);
|
||||
self->controlid = id;
|
||||
|
||||
elem = alsamixer_find_elem(self->handle,control,id);
|
||||
elem = alsamixer_find_elem(self->handle,control, id);
|
||||
if (!elem)
|
||||
{
|
||||
char errtext[128];
|
||||
sprintf(errtext,"Unable to find mixer control '%s',%i",
|
||||
sprintf(errtext,"Unable to find mixer control '%s',%i on card '%s'",
|
||||
self->controlname,
|
||||
self->controlid);
|
||||
self->controlid,
|
||||
self->cardname);
|
||||
snd_mixer_close(self->handle);
|
||||
PyErr_SetString(ALSAAudioError,errtext);
|
||||
free(self->cardname);
|
||||
return NULL;
|
||||
}
|
||||
/* Determine mixer capabilities */
|
||||
@@ -2050,8 +2065,8 @@ static PyTypeObject ALSAMixerType = {
|
||||
/******************************************/
|
||||
|
||||
static PyMethodDef alsaaudio_methods[] = {
|
||||
{ "cards", alsacard_list, METH_VARARGS, cards_doc},
|
||||
{ "mixers", alsamixer_list, METH_VARARGS, mixers_doc},
|
||||
{ "cards", (PyCFunction)alsacard_list, METH_VARARGS, cards_doc},
|
||||
{ "mixers", (PyCFunction)alsamixer_list, METH_VARARGS|METH_KEYWORDS, mixers_doc},
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
|
||||
+64
-22
@@ -38,11 +38,23 @@ The :mod:`alsaaudio` module defines functions and classes for using ALSA.
|
||||
|
||||
List the available cards by name (suitable for PCM objects).
|
||||
|
||||
.. function:: mixers([cardindex])
|
||||
.. function:: mixers(device='default', cardindex=-1)
|
||||
|
||||
List the available mixers. The optional *cardindex* specifies which card
|
||||
should be queried. The default is 0.
|
||||
List the available mixers. The arguments are:
|
||||
|
||||
*device* - the name of the device on which the mixer resides. The default is
|
||||
'default'.
|
||||
|
||||
*cardindex* - specifies which card should be used [#f3]_. If this argument
|
||||
is given, the device name is constructed like this: `hw:<cardindex>` and
|
||||
the `device` keyword argument is ignored. 0 is the
|
||||
first sound card.
|
||||
|
||||
**Note:** The arguments for this function were changed in
|
||||
version 0.8 and this change is not completely backward compatible.
|
||||
Old versions accepted just the optional parameter *cardindex*.
|
||||
The current version accepts either a device name or a cardindex.
|
||||
|
||||
.. class:: PCM(type=PCM_PLAYBACK, mode=PCM_NORMAL, card='default')
|
||||
|
||||
This class is used to represent a PCM device (both for playback and
|
||||
@@ -52,14 +64,24 @@ The :mod:`alsaaudio` module defines functions and classes for using ALSA.
|
||||
* *mode* - can be either ``PCM_NONBLOCK``, or ``PCM_NORMAL`` (default).
|
||||
* *card* - specifies the name of the card that should be used.
|
||||
|
||||
.. class:: Mixer(control='Master', id=0, cardindex=0)
|
||||
.. class:: Mixer(control='Master', id=0, device='default', cardindex=-1)
|
||||
|
||||
This class is used to access a specific ALSA mixer. The arguments
|
||||
are:
|
||||
|
||||
* *control* - Name of the chosen mixed (default is 'Master').
|
||||
* *id* - id of mixer -- More explanation needed here
|
||||
* *cardindex* specifies which card should be used.
|
||||
* *id* - id of mixer -- More explanation needed here
|
||||
*device* - the name of the device on which the mixer resides. The default is
|
||||
'default'.
|
||||
*cardindex* - specifies which card should be used [#f3]_. If this argument
|
||||
is given, the device name is constructed like this: `hw:<cardindex>` and
|
||||
the `device` keyword argument is ignored. 0 is the
|
||||
first sound card.
|
||||
|
||||
**Note:** The arguments for this constructor were changed in
|
||||
version 0.8 and this change is not completely backward compatible.
|
||||
Old versions accepted just the optional parameter *cardindex*.
|
||||
The current version accepts either a device name or a cardindex.
|
||||
|
||||
.. exception:: ALSAAudioError
|
||||
|
||||
@@ -243,7 +265,7 @@ Mixer Objects
|
||||
Mixer objects provides access to the ALSA mixer API.
|
||||
|
||||
|
||||
.. class:: Mixer(control='Master', id=0, cardindex=0)
|
||||
.. class:: Mixer(control='Master', id=0, device='default', cardindex=-1)
|
||||
|
||||
*control* - specifies which control to manipulate using this mixer
|
||||
object. The list of available controls can be found with the
|
||||
@@ -252,10 +274,19 @@ Mixer objects provides access to the ALSA mixer API.
|
||||
|
||||
*id* - the id of the mixer control. Default is 0
|
||||
|
||||
*cardindex* - specifies which card should be used [#f3]_. 0 is the
|
||||
first sound card.
|
||||
|
||||
*device* - the name of the device on which the mixer resides. The default is
|
||||
'default'.
|
||||
|
||||
*cardindex* - specifies which card should be used [#f3]_. If this argument
|
||||
is given, the device name is constructed like this: `hw:<cardindex>` and
|
||||
the `device` keyword argument is ignored. 0 is the
|
||||
first sound card.
|
||||
|
||||
**Note:** The arguments for this function were changed in
|
||||
version 0.8 and this change is not completely backward compatible.
|
||||
Old versions accepted just the optional parameter *cardindex*.
|
||||
The current version accepts either a device name or a cardindex.
|
||||
|
||||
**Note:** For a list of available controls, you can also use **amixer**::
|
||||
|
||||
amixer
|
||||
@@ -471,8 +502,8 @@ or::
|
||||
>>> import alsaaudio
|
||||
>>> alsaaudio.cards()
|
||||
|
||||
mixertest.py accepts the commandline option *-c <cardindex>*. Card
|
||||
indices start at 0.
|
||||
mixertest.py accepts the commandline options *-d <device> and
|
||||
*-c <cardindex>*.
|
||||
|
||||
playwav.py
|
||||
~~~~~~~~~~
|
||||
@@ -502,11 +533,13 @@ Play back the recording with::
|
||||
mixertest.py
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Without arguments, **mixertest.py** will list all available *controls*.
|
||||
Without arguments, **mixertest.py** will list all available *controls* on the
|
||||
default soundcard.
|
||||
|
||||
The output might look like this::
|
||||
|
||||
$ ./mixertest.py
|
||||
Available mixer controls:
|
||||
$ ./mixertest.py
|
||||
Available mixer controls:
|
||||
'Master'
|
||||
'Master Mono'
|
||||
'Headphone'
|
||||
@@ -525,25 +558,34 @@ The output might look like this::
|
||||
With a single argument - the *control*, it will display the settings of
|
||||
that control; for example::
|
||||
|
||||
$ ./mixertest.py Master
|
||||
Mixer name: 'Master'
|
||||
Capabilities: Playback Volume Playback Mute
|
||||
Channel 0 volume: 61%
|
||||
Channel 1 volume: 61%
|
||||
$ ./mixertest.py Master
|
||||
Mixer name: 'Master'
|
||||
Capabilities: Playback Volume Playback Mute
|
||||
Channel 0 volume: 61%
|
||||
Channel 1 volume: 61%
|
||||
|
||||
With two arguments, the *control* and a *parameter*, it will set the
|
||||
parameter on the mixer::
|
||||
|
||||
$ ./mixertest.py Master mute
|
||||
$ ./mixertest.py Master mute
|
||||
|
||||
This will mute the Master mixer.
|
||||
|
||||
Or::
|
||||
|
||||
$ ./mixertest.py Master 40
|
||||
$ ./mixertest.py Master 40
|
||||
|
||||
This sets the volume to 40% on all channels.
|
||||
|
||||
To select a different soundcard, use either the *device* or *cardindex*
|
||||
argument::
|
||||
|
||||
$ ./mixertest.py -c 0 Master
|
||||
Mixer name: 'Master'
|
||||
Capabilities: Playback Volume Playback Mute
|
||||
Channel 0 volume: 61%
|
||||
Channel 1 volume: 61%
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#f1] ALSA also allows ``PCM_ASYNC``, but this is not supported yet.
|
||||
|
||||
+3
-3
@@ -2,10 +2,10 @@
|
||||
Introduction
|
||||
************
|
||||
|
||||
:Author: Casper Wilstrup
|
||||
:Author: Lars Immisch
|
||||
:Author: Casper Wilstrup <cwi@aves.dk>
|
||||
:Author: Lars Immisch <lars@ibp.de>
|
||||
|
||||
.. |release| replace:: 0.4
|
||||
.. |release| replace:: 0.8
|
||||
|
||||
.. % At minimum, give your name and an email address. You can include a
|
||||
.. % snail-mail address if you like.
|
||||
|
||||
+22
-24
@@ -18,39 +18,35 @@
|
||||
## python mixertest.py Capture 0,[un]rec # [dis/en]able capture on channel 0
|
||||
|
||||
|
||||
# Footnote: I'd normally use print instead of sys.std(out|err).write,
|
||||
# but we're in the middle of the conversion between python 2 and 3
|
||||
# and this code runs on both versions without conversion
|
||||
|
||||
import sys
|
||||
import getopt
|
||||
import alsaaudio
|
||||
|
||||
def list_mixers(idx=0):
|
||||
sys.stdout.write("Available mixer controls:\n")
|
||||
for m in alsaaudio.mixers(idx):
|
||||
sys.stdout.write(" '%s'\n" % m)
|
||||
def list_mixers(kwargs):
|
||||
print("Available mixer controls:")
|
||||
for m in alsaaudio.mixers(**kwargs):
|
||||
print(" '%s'\n" % m)
|
||||
|
||||
def show_mixer(name, idx=0):
|
||||
def show_mixer(name, kwargs):
|
||||
# Demonstrates how mixer settings are queried.
|
||||
try:
|
||||
mixer = alsaaudio.Mixer(name, cardindex=idx)
|
||||
mixer = alsaaudio.Mixer(name, **kwargs)
|
||||
except alsaaudio.ALSAAudioError:
|
||||
sys.stderr.write("No such mixer\n")
|
||||
sys.exit(1)
|
||||
|
||||
sys.stdout.write("Mixer name: '%s'\n" % mixer.mixer())
|
||||
sys.stdout.write("Capabilities: %s %s\n" % (' '.join(mixer.volumecap()),
|
||||
' '.join(mixer.switchcap())))
|
||||
print("Mixer name: '%s'" % mixer.mixer())
|
||||
print("Capabilities: %s %s" % (' '.join(mixer.volumecap()),
|
||||
' '.join(mixer.switchcap())))
|
||||
volumes = mixer.getvolume()
|
||||
for i in range(len(volumes)):
|
||||
sys.stdout.write("Channel %i volume: %i%%\n" % (i,volumes[i]))
|
||||
print("Channel %i volume: %i%%" % (i,volumes[i]))
|
||||
|
||||
try:
|
||||
mutes = mixer.getmute()
|
||||
for i in range(len(mutes)):
|
||||
if mutes[i]:
|
||||
sys.stdout.write("Channel %i is muted\n" % i)
|
||||
print("Channel %i is muted" % i)
|
||||
except alsaaudio.ALSAAudioError:
|
||||
# May not support muting
|
||||
pass
|
||||
@@ -59,15 +55,15 @@ def show_mixer(name, idx=0):
|
||||
recs = mixer.getrec()
|
||||
for i in range(len(recs)):
|
||||
if recs[i]:
|
||||
sys.stdout.write("Channel %i is recording\n" % i)
|
||||
print("Channel %i is recording" % i)
|
||||
except alsaaudio.ALSAAudioError:
|
||||
# May not support recording
|
||||
pass
|
||||
|
||||
def set_mixer(name, args, idx=0):
|
||||
def set_mixer(name, args, kwargs):
|
||||
# Demonstrates how to set mixer settings
|
||||
try:
|
||||
mixer = alsaaudio.Mixer(name, cardindex=idx)
|
||||
mixer = alsaaudio.Mixer(name, **kwargs)
|
||||
except alsaaudio.ALSAAudioError:
|
||||
sys.stderr.write("No such mixer")
|
||||
sys.exit(1)
|
||||
@@ -106,17 +102,19 @@ def usage():
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
cardindex = 0
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'c:?h')
|
||||
kwargs = {}
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'c:d:?h')
|
||||
for o, a in opts:
|
||||
if o == '-c':
|
||||
cardindex = int(a)
|
||||
kwargs = { 'cardindex': int(a) }
|
||||
elif o == '-d':
|
||||
kwargs = { 'device': a }
|
||||
else:
|
||||
usage()
|
||||
|
||||
if not len(args):
|
||||
list_mixers(cardindex)
|
||||
list_mixers(kwargs)
|
||||
elif len(args) == 1:
|
||||
show_mixer(args[0], cardindex)
|
||||
show_mixer(args[0], kwargs)
|
||||
else:
|
||||
set_mixer(args[0], args[1], cardindex)
|
||||
set_mixer(args[0], args[1], kwargs)
|
||||
|
||||
@@ -42,13 +42,18 @@ class MixerTest(unittest.TestCase):
|
||||
"""Test Mixer objects"""
|
||||
|
||||
def testMixer(self):
|
||||
"""Open a Mixer on every card"""
|
||||
"""Open the default Mixers and the Mixers on every card"""
|
||||
|
||||
for d in ['default'] + range(len(alsaaudio.cards())):
|
||||
if type(d) == type(0):
|
||||
kwargs = { 'cardindex': d }
|
||||
else:
|
||||
kwargs = { 'device': d }
|
||||
|
||||
# Mixers are addressed by index, not name
|
||||
for i in range(len(alsaaudio.cards())):
|
||||
mixers = alsaaudio.mixers(i)
|
||||
mixers = alsaaudio.mixers(**kwargs)
|
||||
|
||||
for m in mixers:
|
||||
mixer = alsaaudio.Mixer(m, cardindex=i)
|
||||
mixer = alsaaudio.Mixer(m, **kwargs)
|
||||
mixer.close()
|
||||
|
||||
def testMixerAll(self):
|
||||
|
||||
Reference in New Issue
Block a user