Support decibel, percentage, and raw volumes in getvolume, setvolume, and getrange (#109)

* Use `pcmtype` keyword for range

Update methods that accept a `direction` argument (i.e.
capture/playback) to get this via positional _or_ keyword arguments.

Code using keyword arguments can be more robust; however the main reason
for this change is to prepare the way for an extra `units` argument to
many of these methods.

Update documentation to consistently use `pcmtype` instead of
a mixture of that and `direction`.

* Support units
This commit is contained in:
Chris Diamand
2022-03-28 20:46:40 +01:00
committed by GitHub
parent 4d9f6e5b50
commit 3f6fb9844d
3 changed files with 207 additions and 49 deletions
+172 -39
View File
@@ -28,6 +28,7 @@
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include <alsa/version.h> #include <alsa/version.h>
#include <stdio.h> #include <stdio.h>
#include <stdbool.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof *(a)) #define ARRAY_SIZE(a) (sizeof(a) / sizeof *(a))
static const snd_pcm_format_t ALSAFormats[] = { static const snd_pcm_format_t ALSAFormats[] = {
@@ -88,6 +89,12 @@ static const unsigned ALSARates[] = {
192000 192000
}; };
typedef enum volume_units_t {
VOLUME_UNITS_PERCENTAGE,
VOLUME_UNITS_RAW,
VOLUME_UNITS_DB,
} volume_units_t;
PyDoc_STRVAR(alsaaudio_module_doc, PyDoc_STRVAR(alsaaudio_module_doc,
"This modules provides support for the ALSA audio API.\n" "This modules provides support for the ALSA audio API.\n"
"\n" "\n"
@@ -129,10 +136,13 @@ typedef struct {
unsigned int cchannels; unsigned int cchannels;
/* min and max values for playback and capture volumes */ /* min and max values for playback and capture volumes */
long pmin; long pmin, pmax;
long pmax; long cmin, cmax;
long cmin; /* min and max values for playback and capture volumes, in dB * 100 as
long cmax; * reported by ALSA */
long pmin_dB, pmax_dB;
long cmin_dB, cmax_dB;
snd_mixer_t *handle; snd_mixer_t *handle;
} alsamixer_t; } alsamixer_t;
@@ -181,6 +191,16 @@ get_pcmtype(PyObject *obj)
return -1; return -1;
} }
static bool is_value_volume_unit(long unit)
{
if (unit == VOLUME_UNITS_PERCENTAGE ||
unit == VOLUME_UNITS_RAW ||
unit == VOLUME_UNITS_DB) {
return true;
}
return false;
}
static PyObject * static PyObject *
alsacard_list(PyObject *self, PyObject *args) alsacard_list(PyObject *self, PyObject *args)
{ {
@@ -306,7 +326,7 @@ Return the card name and long name for card 'card_index'.");
static PyObject * static PyObject *
alsapcm_list(PyObject *self, PyObject *args) alsapcm_list(PyObject *self, PyObject *args, PyObject *kwds)
{ {
PyObject *pcmtypeobj = NULL; PyObject *pcmtypeobj = NULL;
long pcmtype; long pcmtype;
@@ -316,8 +336,11 @@ alsapcm_list(PyObject *self, PyObject *args)
char *name, *io; char *name, *io;
const char *filter; const char *filter;
if (!PyArg_ParseTuple(args,"|O:pcms", &pcmtypeobj)) char *kw[] = { "pcmtype", NULL };
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:pcms", kw, &pcmtypeobj)) {
return NULL; return NULL;
}
pcmtype = get_pcmtype(pcmtypeobj); pcmtype = get_pcmtype(pcmtypeobj);
if (pcmtype < 0) { if (pcmtype < 0) {
@@ -2079,6 +2102,8 @@ alsamixer_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
} }
snd_mixer_selem_get_playback_volume_range(elem, &self->pmin, &self->pmax); snd_mixer_selem_get_playback_volume_range(elem, &self->pmin, &self->pmax);
snd_mixer_selem_get_capture_volume_range(elem, &self->cmin, &self->cmax); snd_mixer_selem_get_capture_volume_range(elem, &self->cmin, &self->cmax);
snd_mixer_selem_get_playback_dB_range(elem, &self->pmin_dB, &self->pmax_dB);
snd_mixer_selem_get_capture_dB_range(elem, &self->cmin_dB, &self->cmax_dB);
return (PyObject *)self; return (PyObject *)self;
} }
@@ -2350,18 +2375,22 @@ static long alsamixer_getphysvolume(long min, long max, int percentage)
} }
static PyObject * static PyObject *
alsamixer_getvolume(alsamixer_t *self, PyObject *args) alsamixer_getvolume(alsamixer_t *self, PyObject *args, PyObject *kwds)
{ {
snd_mixer_elem_t *elem; snd_mixer_elem_t *elem;
int channel; int channel;
long ival; long ival;
PyObject *pcmtypeobj = NULL; PyObject *pcmtypeobj = NULL;
long pcmtype; long pcmtype;
int iunits = VOLUME_UNITS_PERCENTAGE;
PyObject *result; PyObject *result;
PyObject *item; PyObject *item;
if (!PyArg_ParseTuple(args,"|O:getvolume", &pcmtypeobj)) char *kw[] = { "pcmtype", "units", NULL };
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:getvolume", kw, &pcmtypeobj, &iunits)) {
return NULL; return NULL;
}
if (!self->handle) if (!self->handle)
{ {
@@ -2374,6 +2403,12 @@ alsamixer_getvolume(alsamixer_t *self, PyObject *args)
return NULL; return NULL;
} }
if (!is_value_volume_unit(iunits)) {
PyErr_SetString(ALSAAudioError, "Invalid volume units");
return NULL;
}
volume_units_t units = iunits;
elem = alsamixer_find_elem(self->handle,self->controlname,self->controlid); elem = alsamixer_find_elem(self->handle,self->controlname,self->controlid);
if (!pcmtypeobj || (pcmtypeobj == Py_None)) { if (!pcmtypeobj || (pcmtypeobj == Py_None)) {
@@ -2391,20 +2426,42 @@ alsamixer_getvolume(alsamixer_t *self, PyObject *args)
if (pcmtype == SND_PCM_STREAM_PLAYBACK && if (pcmtype == SND_PCM_STREAM_PLAYBACK &&
snd_mixer_selem_has_playback_channel(elem, channel)) snd_mixer_selem_has_playback_channel(elem, channel))
{ {
snd_mixer_selem_get_playback_volume(elem, channel, &ival); switch (units)
item = PyLong_FromLong(alsamixer_getpercentage(self->pmin, {
self->pmax, case VOLUME_UNITS_PERCENTAGE:
ival)); snd_mixer_selem_get_playback_volume(elem, channel, &ival);
ival = alsamixer_getpercentage(self->pmin, self->pmax, ival);
break;
case VOLUME_UNITS_RAW:
snd_mixer_selem_get_playback_volume(elem, channel, &ival);
break;
case VOLUME_UNITS_DB:
snd_mixer_selem_get_playback_dB(elem, channel, &ival);
break;
}
item = PyLong_FromLong(ival);
PyList_Append(result, item); PyList_Append(result, item);
Py_DECREF(item); Py_DECREF(item);
} }
else if (pcmtype == SND_PCM_STREAM_CAPTURE else if (pcmtype == SND_PCM_STREAM_CAPTURE
&& snd_mixer_selem_has_capture_channel(elem, channel) && snd_mixer_selem_has_capture_channel(elem, channel)
&& snd_mixer_selem_has_capture_volume(elem)) { && snd_mixer_selem_has_capture_volume(elem)) {
snd_mixer_selem_get_capture_volume(elem, channel, &ival); switch (units)
item = PyLong_FromLong(alsamixer_getpercentage(self->cmin, {
self->cmax, case VOLUME_UNITS_PERCENTAGE:
ival)); snd_mixer_selem_get_capture_volume(elem, channel, &ival);
ival = alsamixer_getpercentage(self->cmin, self->cmax, ival);
break;
case VOLUME_UNITS_RAW:
snd_mixer_selem_get_capture_volume(elem, channel, &ival);
break;
case VOLUME_UNITS_DB:
snd_mixer_selem_get_capture_dB(elem, channel, &ival);
break;
}
item = PyLong_FromLong(ival);
PyList_Append(result, item); PyList_Append(result, item);
Py_DECREF(item); Py_DECREF(item);
} }
@@ -2426,13 +2483,17 @@ if the mixer has this capability, otherwise PCM_CAPTURE");
static PyObject * static PyObject *
alsamixer_getrange(alsamixer_t *self, PyObject *args) alsamixer_getrange(alsamixer_t *self, PyObject *args, PyObject *kwds)
{ {
snd_mixer_elem_t *elem; snd_mixer_elem_t *elem;
PyObject *pcmtypeobj = NULL; PyObject *pcmtypeobj = NULL;
int iunits = VOLUME_UNITS_RAW;
long pcmtype; long pcmtype;
long min = -1, max = -1;
if (!PyArg_ParseTuple(args,"|O:getrange", &pcmtypeobj)) { char *kw[] = { "pcmtype", "units", NULL };
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:getrange", kw, &pcmtypeobj, &iunits)) {
return NULL; return NULL;
} }
@@ -2447,6 +2508,12 @@ alsamixer_getrange(alsamixer_t *self, PyObject *args)
return NULL; return NULL;
} }
if (!is_value_volume_unit(iunits)) {
PyErr_SetString(ALSAAudioError, "Invalid volume units");
return NULL;
}
volume_units_t units = iunits;
elem = alsamixer_find_elem(self->handle, self->controlname, elem = alsamixer_find_elem(self->handle, self->controlname,
self->controlid); self->controlid);
@@ -2464,7 +2531,22 @@ alsamixer_getrange(alsamixer_t *self, PyObject *args)
{ {
if (snd_mixer_selem_has_playback_channel(elem, 0)) if (snd_mixer_selem_has_playback_channel(elem, 0))
{ {
return Py_BuildValue("[ii]", self->pmin, self->pmax); switch (units)
{
case VOLUME_UNITS_PERCENTAGE:
min = 0;
max = 100;
break;
case VOLUME_UNITS_RAW:
min = self->pmin;
max = self->pmax;
break;
case VOLUME_UNITS_DB:
min = self->pmin_dB;
max = self->pmax_dB;
break;
}
return Py_BuildValue("[ii]", min, max);
} }
PyErr_Format(ALSAAudioError, "Mixer %s,%d has no playback channel [%s]", PyErr_Format(ALSAAudioError, "Mixer %s,%d has no playback channel [%s]",
@@ -2475,7 +2557,22 @@ alsamixer_getrange(alsamixer_t *self, PyObject *args)
{ {
if (snd_mixer_selem_has_capture_channel(elem, 0) if (snd_mixer_selem_has_capture_channel(elem, 0)
&& snd_mixer_selem_has_capture_volume(elem)) { && snd_mixer_selem_has_capture_volume(elem)) {
return Py_BuildValue("[ii]", self->cmin, self->cmax); switch (units)
{
case VOLUME_UNITS_PERCENTAGE:
min = 0;
max = 100;
break;
case VOLUME_UNITS_RAW:
min = self->cmin;
max = self->cmax;
break;
case VOLUME_UNITS_DB:
min = self->cmin_dB;
max = self->cmax_dB;
break;
}
return Py_BuildValue("[ii]", min, max);
} }
PyErr_Format(ALSAAudioError, "Mixer %s,%d has no capture channel " PyErr_Format(ALSAAudioError, "Mixer %s,%d has no capture channel "
@@ -2494,7 +2591,7 @@ PyDoc_STRVAR(getrange_doc,
\n\ \n\
Returns a list of tuples with the volume range (ints).\n\ Returns a list of tuples with the volume range (ints).\n\
\n\ \n\
The optional 'direction' argument can be either PCM_PLAYBACK or\n\ The optional 'pcmtype' argument can be either PCM_PLAYBACK or\n\
PCM_CAPTURE, which is relevant if the mixer can control both\n\ PCM_CAPTURE, which is relevant if the mixer can control both\n\
playback and capture volume. The default value is 'playback'\n\ playback and capture volume. The default value is 'playback'\n\
if the mixer has this capability, otherwise 'capture'"); if the mixer has this capability, otherwise 'capture'");
@@ -2748,7 +2845,7 @@ This method will fail if the mixer has no capture switch capabilities.");
static PyObject * static PyObject *
alsamixer_setvolume(alsamixer_t *self, PyObject *args) alsamixer_setvolume(alsamixer_t *self, PyObject *args, PyObject *kwds)
{ {
snd_mixer_elem_t *elem; snd_mixer_elem_t *elem;
int i; int i;
@@ -2756,16 +2853,14 @@ alsamixer_setvolume(alsamixer_t *self, PyObject *args)
int physvolume; int physvolume;
PyObject *pcmtypeobj = NULL; PyObject *pcmtypeobj = NULL;
long pcmtype; long pcmtype;
int iunits = VOLUME_UNITS_PERCENTAGE;
int channel = MIXER_CHANNEL_ALL; int channel = MIXER_CHANNEL_ALL;
int done = 0; int done = 0;
if (!PyArg_ParseTuple(args,"l|iO:setvolume", &volume, &channel, char *kw[] = { "volume", "channel", "pcmtype", "units", NULL };
&pcmtypeobj))
return NULL;
if (volume < 0 || volume > 100) if (!PyArg_ParseTupleAndKeywords(args, kwds, "l|iOi:setvolume", kw, &volume,
{ &channel, &pcmtypeobj, &iunits)) {
PyErr_SetString(ALSAAudioError, "Volume must be between 0 and 100");
return NULL; return NULL;
} }
@@ -2774,6 +2869,18 @@ alsamixer_setvolume(alsamixer_t *self, PyObject *args)
return NULL; return NULL;
} }
if (!is_value_volume_unit(iunits)) {
PyErr_SetString(ALSAAudioError, "Invalid volume units");
return NULL;
}
volume_units_t units = iunits;
if (units == VOLUME_UNITS_PERCENTAGE && (volume < 0 || volume > 100))
{
PyErr_SetString(ALSAAudioError, "Volume out of range");
return NULL;
}
if (!self->handle) if (!self->handle)
{ {
PyErr_SetString(ALSAAudioError, "Mixer is closed"); PyErr_SetString(ALSAAudioError, "Mixer is closed");
@@ -2796,18 +2903,40 @@ alsamixer_setvolume(alsamixer_t *self, PyObject *args)
{ {
if (pcmtype == SND_PCM_STREAM_PLAYBACK && if (pcmtype == SND_PCM_STREAM_PLAYBACK &&
snd_mixer_selem_has_playback_channel(elem, i)) { snd_mixer_selem_has_playback_channel(elem, i)) {
physvolume = alsamixer_getphysvolume(self->pmin, switch (units)
self->pmax, volume); {
snd_mixer_selem_set_playback_volume(elem, i, physvolume); case VOLUME_UNITS_PERCENTAGE:
physvolume = alsamixer_getphysvolume(self->pmin,
self->pmax, volume);
snd_mixer_selem_set_playback_volume(elem, i, physvolume);
break;
case VOLUME_UNITS_RAW:
snd_mixer_selem_set_playback_volume(elem, i, volume);
break;
case VOLUME_UNITS_DB:
snd_mixer_selem_set_playback_dB(elem, i, volume, 0);
break;
}
done++; done++;
} }
else if (pcmtype == SND_PCM_STREAM_CAPTURE else if (pcmtype == SND_PCM_STREAM_CAPTURE
&& snd_mixer_selem_has_capture_channel(elem, i) && snd_mixer_selem_has_capture_channel(elem, i)
&& snd_mixer_selem_has_capture_volume(elem)) && snd_mixer_selem_has_capture_volume(elem))
{ {
physvolume = alsamixer_getphysvolume(self->cmin, self->cmax, switch (units)
volume); {
snd_mixer_selem_set_capture_volume(elem, i, physvolume); case VOLUME_UNITS_PERCENTAGE:
physvolume = alsamixer_getphysvolume(self->cmin, self->cmax,
volume);
snd_mixer_selem_set_capture_volume(elem, i, physvolume);
break;
case VOLUME_UNITS_RAW:
snd_mixer_selem_set_capture_volume(elem, i, volume);
break;
case VOLUME_UNITS_DB:
snd_mixer_selem_set_capture_dB(elem, i, volume, 0);
break;
}
done++; done++;
} }
} }
@@ -2833,7 +2962,7 @@ If the optional argument channel is present, the volume is set only for\n\
this channel. This assumes that the mixer can control the volume for the\n\ this channel. This assumes that the mixer can control the volume for the\n\
channels independently.\n\ channels independently.\n\
\n\ \n\
The optional direction argument can be either PCM_PLAYBACK or PCM_CAPTURE.\n\ The optional 'pcmtype' argument can be either PCM_PLAYBACK or PCM_CAPTURE.\n\
It is relevant if the mixer has independent playback and capture volume\n\ It is relevant if the mixer has independent playback and capture volume\n\
capabilities, and controls which of the volumes will be changed.\n\ capabilities, and controls which of the volumes will be changed.\n\
The default is 'playback' if the mixer has this capability, otherwise\n\ The default is 'playback' if the mixer has this capability, otherwise\n\
@@ -3055,13 +3184,13 @@ static PyMethodDef alsamixer_methods[] = {
switchcap_doc}, switchcap_doc},
{"volumecap", (PyCFunction)alsamixer_volumecap, METH_VARARGS, {"volumecap", (PyCFunction)alsamixer_volumecap, METH_VARARGS,
volumecap_doc}, volumecap_doc},
{"getvolume", (PyCFunction)alsamixer_getvolume, METH_VARARGS, {"getvolume", (PyCFunction)alsamixer_getvolume, METH_VARARGS | METH_KEYWORDS,
getvolume_doc}, getvolume_doc},
{"getrange", (PyCFunction)alsamixer_getrange, METH_VARARGS, getrange_doc}, {"getrange", (PyCFunction)alsamixer_getrange, METH_VARARGS | METH_KEYWORDS, getrange_doc},
{"getenum", (PyCFunction)alsamixer_getenum, METH_VARARGS, getenum_doc}, {"getenum", (PyCFunction)alsamixer_getenum, METH_VARARGS, getenum_doc},
{"getmute", (PyCFunction)alsamixer_getmute, METH_VARARGS, getmute_doc}, {"getmute", (PyCFunction)alsamixer_getmute, METH_VARARGS, getmute_doc},
{"getrec", (PyCFunction)alsamixer_getrec, METH_VARARGS, getrec_doc}, {"getrec", (PyCFunction)alsamixer_getrec, METH_VARARGS, getrec_doc},
{"setvolume", (PyCFunction)alsamixer_setvolume, METH_VARARGS, {"setvolume", (PyCFunction)alsamixer_setvolume, METH_VARARGS | METH_KEYWORDS,
setvolume_doc}, setvolume_doc},
{"setenum", (PyCFunction)alsamixer_setenum, METH_VARARGS, setenum_doc}, {"setenum", (PyCFunction)alsamixer_setenum, METH_VARARGS, setenum_doc},
{"setmute", (PyCFunction)alsamixer_setmute, METH_VARARGS, setmute_doc}, {"setmute", (PyCFunction)alsamixer_setmute, METH_VARARGS, setmute_doc},
@@ -3137,7 +3266,7 @@ static PyMethodDef alsaaudio_methods[] = {
{ "card_indexes", (PyCFunction)alsacard_list_indexes, METH_VARARGS, card_indexes_doc}, { "card_indexes", (PyCFunction)alsacard_list_indexes, METH_VARARGS, card_indexes_doc},
{ "card_name", (PyCFunction)alsacard_name, METH_VARARGS, card_name_doc}, { "card_name", (PyCFunction)alsacard_name, METH_VARARGS, card_name_doc},
{ "cards", (PyCFunction)alsacard_list, METH_VARARGS, cards_doc}, { "cards", (PyCFunction)alsacard_list, METH_VARARGS, cards_doc},
{ "pcms", (PyCFunction)alsapcm_list, METH_VARARGS, pcms_doc}, { "pcms", (PyCFunction)alsapcm_list, METH_VARARGS|METH_KEYWORDS, pcms_doc},
{ "mixers", (PyCFunction)alsamixer_list, METH_VARARGS|METH_KEYWORDS, mixers_doc}, { "mixers", (PyCFunction)alsamixer_list, METH_VARARGS|METH_KEYWORDS, mixers_doc},
{ 0, 0 }, { 0, 0 },
}; };
@@ -3287,6 +3416,10 @@ PyObject *PyInit_alsaaudio(void)
_EXPORT_INT(m, "MIXER_SCHN_MONO", SND_MIXER_SCHN_MONO); _EXPORT_INT(m, "MIXER_SCHN_MONO", SND_MIXER_SCHN_MONO);
#endif #endif
_EXPORT_INT(m, "VOLUME_UNITS_PERCENTAGE", VOLUME_UNITS_PERCENTAGE)
_EXPORT_INT(m, "VOLUME_UNITS_RAW", VOLUME_UNITS_RAW)
_EXPORT_INT(m, "VOLUME_UNITS_DB", VOLUME_UNITS_DB)
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
return m; return m;
#endif #endif
+8 -8
View File
@@ -33,13 +33,13 @@ The :mod:`alsaaudio` module defines functions and classes for using ALSA.
.. % should be enclosed in \var{...}. .. % should be enclosed in \var{...}.
.. function:: pcms([type=PCM_PLAYBACK]) .. function:: pcms(pcmtype=PCM_PLAYBACK)
List available PCM devices by name. List available PCM devices by name.
Arguments are: Arguments are:
* *type* - can be either :const:`PCM_CAPTURE` or :const:`PCM_PLAYBACK` * *pcmtype* - can be either :const:`PCM_CAPTURE` or :const:`PCM_PLAYBACK`
(default). (default).
**Note:** **Note:**
@@ -466,11 +466,11 @@ Mixer objects have the following methods:
This method will fail if the mixer has no playback switch capabilities. This method will fail if the mixer has no playback switch capabilities.
.. method:: Mixer.getrange([direction]) .. method:: Mixer.getrange(pcmtype=PCM_PLAYBACK)
Return the volume range of the ALSA mixer controlled by this object. Return the volume range of the ALSA mixer controlled by this object.
The optional *direction* argument can be either :const:`PCM_PLAYBACK` or The optional *pcmtype* argument can be either :const:`PCM_PLAYBACK` or
:const:`PCM_CAPTURE`, which is relevant if the mixer can control both :const:`PCM_CAPTURE`, which is relevant if the mixer can control both
playback and capture volume. The default value is :const:`PCM_PLAYBACK` playback and capture volume. The default value is :const:`PCM_PLAYBACK`
if the mixer has playback channels, otherwise it is :const:`PCM_CAPTURE`. if the mixer has playback channels, otherwise it is :const:`PCM_CAPTURE`.
@@ -484,18 +484,18 @@ Mixer objects have the following methods:
This method will fail if the mixer has no capture switch capabilities. This method will fail if the mixer has no capture switch capabilities.
.. method:: Mixer.getvolume([direction]) .. method:: Mixer.getvolume(pcmtype=PCM_PLAYBACK)
Returns a list with the current volume settings for each channel. The list Returns a list with the current volume settings for each channel. The list
elements are integer percentages. elements are integer percentages.
The optional *direction* argument can be either :const:`PCM_PLAYBACK` or The optional *pcmtype* argument can be either :const:`PCM_PLAYBACK` or
:const:`PCM_CAPTURE`, which is relevant if the mixer can control both :const:`PCM_CAPTURE`, which is relevant if the mixer can control both
playback and capture volume. The default value is :const:`PCM_PLAYBACK` playback and capture volume. The default value is :const:`PCM_PLAYBACK`
if the mixer has playback channels, otherwise it is :const:`PCM_CAPTURE`. if the mixer has playback channels, otherwise it is :const:`PCM_CAPTURE`.
.. method:: Mixer.setvolume(volume, [channel], [direction]) .. method:: Mixer.setvolume(volume, channel=None, pcmtype=PCM_PLAYBACK)
Change the current volume settings for this mixer. The *volume* argument Change the current volume settings for this mixer. The *volume* argument
controls the new volume setting as an integer percentage. controls the new volume setting as an integer percentage.
@@ -504,7 +504,7 @@ Mixer objects have the following methods:
only for this channel. This assumes that the mixer can control the only for this channel. This assumes that the mixer can control the
volume for the channels independently. volume for the channels independently.
The optional *direction* argument can be either :const:`PCM_PLAYBACK` or The optional *pcmtype* argument can be either :const:`PCM_PLAYBACK` or
:const:`PCM_CAPTURE`, which is relevant if the mixer can control both :const:`PCM_CAPTURE`, which is relevant if the mixer can control both
playback and capture volume. The default value is :const:`PCM_PLAYBACK` playback and capture volume. The default value is :const:`PCM_PLAYBACK`
if the mixer has playback channels, otherwise it is :const:`PCM_CAPTURE`. if the mixer has playback channels, otherwise it is :const:`PCM_CAPTURE`.
+27 -2
View File
@@ -43,11 +43,36 @@ def show_mixer(name, kwargs):
sys.exit(1) sys.exit(1)
print("Mixer name: '%s'" % mixer.mixer()) print("Mixer name: '%s'" % mixer.mixer())
print("Capabilities: %s %s" % (' '.join(mixer.volumecap()), volcap = mixer.volumecap()
print("Capabilities: %s %s" % (' '.join(volcap),
' '.join(mixer.switchcap()))) ' '.join(mixer.switchcap())))
if "Volume" in volcap or "Joined Volume" in volcap or "Playback Volume" in volcap:
pmin, pmax = mixer.getrange(alsaaudio.PCM_PLAYBACK)
pmin_keyword, pmax_keyword = mixer.getrange(pcmtype=alsaaudio.PCM_PLAYBACK, units=alsaaudio.VOLUME_UNITS_RAW)
pmin_default, pmax_default = mixer.getrange()
assert pmin == pmin_keyword
assert pmax == pmax_keyword
assert pmin == pmin_default
assert pmax == pmax_default
print("Raw playback volume range {}-{}".format(pmin, pmax))
pmin_dB, pmax_dB = mixer.getrange(units=alsaaudio.VOLUME_UNITS_DB)
print("dB playback volume range {}-{}".format(pmin_dB / 100.0, pmax_dB / 100.0))
if "Capture Volume" in volcap or "Joined Capture Volume" in volcap:
# Check that `getrange` works with keyword and positional arguments
cmin, cmax = mixer.getrange(alsaaudio.PCM_CAPTURE)
cmin_keyword, cmax_keyword = mixer.getrange(pcmtype=alsaaudio.PCM_CAPTURE, units=alsaaudio.VOLUME_UNITS_RAW)
assert cmin == cmin_keyword
assert cmax == cmax_keyword
print("Raw capture volume range {}-{}".format(cmin, cmax))
cmin_dB, cmax_dB = mixer.getrange(pcmtype=alsaaudio.PCM_CAPTURE, units=alsaaudio.VOLUME_UNITS_DB)
print("dB capture volume range {}-{}".format(cmin_dB / 100.0, cmax_dB / 100.0))
volumes = mixer.getvolume() volumes = mixer.getvolume()
volumes_dB = mixer.getvolume(units=alsaaudio.VOLUME_UNITS_DB)
for i in range(len(volumes)): for i in range(len(volumes)):
print("Channel %i volume: %i%%" % (i,volumes[i])) print("Channel %i volume: %i%% (%.1f dB)" % (i, volumes[i], volumes_dB[i] / 100.0))
try: try:
mutes = mixer.getmute() mutes = mixer.getmute()