mirror of
https://github.com/larsimmisch/pyalsaaudio.git
synced 2026-05-30 09:57:02 +00:00
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:
+172
-39
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user