Add handling of dB to amixer

Added the dB value handling to amixer sset command.
Also, simplify the parser code.

Updated man page for dB suffix and some examples.
This commit is contained in:
Takashi Iwai 2006-08-25 12:00:14 +02:00
parent 0413ca230f
commit ae9ddeb634
2 changed files with 175 additions and 111 deletions

View file

@ -33,14 +33,23 @@ Shows a complete list of simple mixer controls with their contents.
.TP .TP
\fIset\fP or \fIsset\fP <\fISCONTROL\fP> <\fIPARAMETER\fP> ... \fIset\fP or \fIsset\fP <\fISCONTROL\fP> <\fIPARAMETER\fP> ...
Sets the simple mixer control contents. The parameter can be the volume Sets the simple mixer control contents. The parameter can be the volume
either as a percentage from 0% to 100% or an exact hardware value. either as a percentage from 0% to 100% with \fI%\fP suffix,
The parameters \fIcap, nocap, mute, unmute, toggle\fP are used to a dB gain with \fIdB\fP suffix (like -12.5dB), or an exact hardware value.
change capture (recording) and muting for the group specified. The dB gain can be used only for the mixer elements with available
The parameters \fIfront, rear, center, woofer\fP are used to specify dB information.
channels to be changed. When plus(+) or minus(\-) letter is appended after When plus(+) or minus(\-) letter is appended after
volume value, the volume is incremented or decremented from the current volume value, the volume is incremented or decremented from the current
value, respectively. value, respectively.
The parameters \fIcap, nocap, mute, unmute, toggle\fP are used to
change capture (recording) and muting for the group specified.
The optional modifiers can be put as extra parameters to specify
the stream direction or channels to apply.
The modifiers \fIplayback\fP and \fIcapture\fP specify the stream,
and the modifiers \fIfront, rear, center, woofer\fP are used to specify
channels to be changed.
A simple mixer control must be specified. Only one device can be controlled A simple mixer control must be specified. Only one device can be controlled
at a time. at a time.
@ -107,6 +116,16 @@ will set the second soundcard's left line input volume to 80% and
right line input to 40%, unmute it, and select it as a source for right line input to 40%, unmute it, and select it as a source for
capture (recording).\fR capture (recording).\fR
.TP
\fBamixer \-c 1 \-\- sset Master playback -20dB\fR
will set the master volume of the second card to -20dB. If the master
has multiple channels, all channels are set to the same value.
.TP
\fBamixer \-c 1 set PCM 2dB+\fR
will increase the PCM volume of the second card with 2dB. When both
playback and capture volumes exist, this is applied to both volumes.
.TP .TP
\fBamixer \-c 2 cset iface=MIXER,name='Line Playback Volume",index=1 40%\fR \fBamixer \-c 2 cset iface=MIXER,name='Line Playback Volume",index=1 40%\fR
will set the third soundcard's second line playback volume(s) to 40% will set the third soundcard's second line playback volume(s) to 40%

View file

@ -161,17 +161,8 @@ static const char *control_access(snd_ctl_elem_info_t *info)
return result; return result;
} }
static int check_range(int val, int min, int max) #define check_range(val, min, max) \
{ (no_check ? (val) : ((val < min) ? (min) : (val > max) ? (max) : (val)))
if (no_check)
return val;
if (val < min)
return min;
if (val > max)
return max;
return val;
}
#if 0 #if 0
static int convert_range(int val, int omin, int omax, int nmin, int nmax) static int convert_range(int val, int omin, int omax, int nmin, int nmax)
{ {
@ -210,17 +201,8 @@ static int convert_prange(int val, int min, int max)
/* Function to convert from percentage to volume. val = percentage */ /* Function to convert from percentage to volume. val = percentage */
static int convert_prange1(int val, int min, int max) #define convert_prange1(val, min, max) \
{ ceil((val) * ((max) - (min)) * 0.01 + (min))
int range = max - min;
int tmp;
if (range == 0)
return 0;
tmp = rint((double)range * ((double)val*.01)) + min;
return tmp;
}
static const char *get_percent(int val, int min, int max) static const char *get_percent(int val, int min, int max)
{ {
@ -247,90 +229,167 @@ static const char *get_percent1(int val, int min, int max, int min_dB, int max_d
static long get_integer(char **ptr, long min, long max) static long get_integer(char **ptr, long min, long max)
{ {
int tmp, tmp1, tmp2; long val = min;
char *p = *ptr, *s;
if (**ptr == ':') if (*p == ':')
(*ptr)++; p++;
if (**ptr == '\0' || (!isdigit(**ptr) && **ptr != '-')) if (*p == '\0' || (!isdigit(*p) && *p != '-'))
return min; goto out;
tmp = strtol(*ptr, ptr, 10);
tmp1 = tmp; s = p;
tmp2 = 0; val = strtol(s, &p, 10);
if (**ptr == '.') { if (*p == '.') {
(*ptr)++; p++;
tmp2 = strtol(*ptr, ptr, 10); strtol(p, &p, 10);
} }
if (**ptr == '%') { if (*p == '%') {
tmp1 = convert_prange1(tmp, min, max); val = (long)convert_prange1(strtod(s, NULL), min, max);
(*ptr)++; p++;
} }
tmp1 = check_range(tmp1, min, max); val = check_range(val, min, max);
if (**ptr == ',') if (*p == ',')
(*ptr)++; p++;
return tmp1; out:
*ptr = p;
return val;
} }
static long get_integer64(char **ptr, long long min, long long max) static long get_integer64(char **ptr, long long min, long long max)
{ {
long long tmp, tmp1, tmp2; long long val = min;
char *p = *ptr, *s;
if (**ptr == ':') if (*p == ':')
(*ptr)++; p++;
if (**ptr == '\0' || (!isdigit(**ptr) && **ptr != '-')) if (*p == '\0' || (!isdigit(*p) && *p != '-'))
return min; goto out;
tmp = strtol(*ptr, ptr, 10);
tmp1 = tmp; s = p;
tmp2 = 0; val = strtol(s, &p, 10);
if (**ptr == '.') { if (*p == '.') {
(*ptr)++; p++;
tmp2 = strtol(*ptr, ptr, 10); strtol(p, &p, 10);
} }
if (**ptr == '%') { if (*p == '%') {
tmp1 = convert_prange1(tmp, min, max); val = (long long)convert_prange1(strtod(s, NULL), min, max);
(*ptr)++; p++;
} }
tmp1 = check_range(tmp1, min, max); val = check_range(val, min, max);
if (**ptr == ',') if (*p == ',')
(*ptr)++; p++;
return tmp1; out:
*ptr = p;
return val;
} }
static int get_volume_simple(char **ptr, int min, int max, int orig) struct volume_ops {
{ int (*get_range)(snd_mixer_elem_t *elem, long *min, long *max);
int tmp, tmp1, tmp2; int (*get)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t c,
long *value);
int (*set)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t c,
long value);
};
enum { VOL_RAW, VOL_DB };
if (**ptr == ':') struct volume_ops_set {
(*ptr)++; int (*has_volume)(snd_mixer_elem_t *elem);
if (**ptr == '\0' || (!isdigit(**ptr) && **ptr != '-')) struct volume_ops v[2];
return min; };
tmp = atoi(*ptr);
if (**ptr == '-') static int set_playback_dB(snd_mixer_elem_t *elem,
(*ptr)++; snd_mixer_selem_channel_id_t c, long value)
while (isdigit(**ptr)) {
(*ptr)++; return snd_mixer_selem_set_playback_dB(elem, c, value, 0);
tmp1 = tmp; }
tmp2 = 0;
if (**ptr == '.') { static int set_capture_dB(snd_mixer_elem_t *elem,
(*ptr)++; snd_mixer_selem_channel_id_t c, long value)
tmp2 = atoi(*ptr); {
while (isdigit(**ptr)) return snd_mixer_selem_set_capture_dB(elem, c, value, 0);
(*ptr)++; }
static struct volume_ops_set vol_ops[2] = {
{
.has_volume = snd_mixer_selem_has_playback_volume,
.v = {{ snd_mixer_selem_get_playback_volume_range,
snd_mixer_selem_get_playback_volume,
snd_mixer_selem_set_playback_volume },
{ snd_mixer_selem_get_playback_dB_range,
snd_mixer_selem_get_playback_dB,
set_playback_dB }},
},
{
.has_volume = snd_mixer_selem_has_capture_volume,
.v = {{ snd_mixer_selem_get_capture_volume_range,
snd_mixer_selem_get_capture_volume,
snd_mixer_selem_set_capture_volume },
{ snd_mixer_selem_get_capture_dB_range,
snd_mixer_selem_get_capture_dB,
set_capture_dB }},
},
};
static int set_volume_simple(snd_mixer_elem_t *elem,
snd_mixer_selem_channel_id_t chn,
char **ptr, int dir)
{
long val, orig, pmin, pmax;
char *p = *ptr, *s;
int invalid = 0, vol_type = VOL_RAW;
if (! vol_ops[dir].has_volume(elem))
invalid = 1;
if (*p == ':')
p++;
if (*p == '\0' || (!isdigit(*p) && *p != '-'))
goto skip;
if (! invalid &&
vol_ops[dir].v[VOL_RAW].get_range(elem, &pmin, &pmax) < 0)
invalid = 1;
s = p;
val = strtol(s, &p, 10);
if (*p == '.') {
p++;
strtol(p, &p, 10);
} }
if (**ptr == '%') { if (*p == '%') {
tmp1 = convert_prange1(tmp, min, max); if (! invalid)
(*ptr)++; val = (long)convert_prange1(strtod(s, NULL), pmin, pmax);
p++;
} else if (p[0] == 'd' && p[1] == 'B') {
if (! invalid) {
val = (long)(strtod(s, NULL) * 100.0);
vol_type = VOL_DB;
if (vol_ops[dir].v[vol_type].get_range(elem, &pmin, &pmax) < 0)
invalid = 1;
}
p += 2;
} }
if (**ptr == '+') { if (*p == '+' || *p == '-') {
tmp1 = orig + tmp1; if (! invalid) {
(*ptr)++; if (vol_ops[dir].v[vol_type].get(elem, chn, &orig) < 0)
} else if (**ptr == '-') { invalid = 1;
tmp1 = orig - tmp1; if (*p == '+')
(*ptr)++; val = orig + val;
else
val = orig - val;
}
p++;
} }
tmp1 = check_range(tmp1, min, max); if (! invalid) {
if (**ptr == ',') val = check_range(val, pmin, pmax);
(*ptr)++; vol_ops[dir].v[vol_type].set(elem, chn, val);
return tmp1; }
skip:
if (*p == ',')
p++;
*ptr = p;
return 0;
} }
static int get_bool_simple(char **ptr, char *str, int invert, int orig) static int get_bool_simple(char **ptr, char *str, int invert, int orig)
@ -1283,7 +1342,6 @@ static int sset(unsigned int argc, char *argv[], int roflag, int keep_handle)
snd_mixer_selem_channel_id_t chn; snd_mixer_selem_channel_id_t chn;
unsigned int channels = ~0U; unsigned int channels = ~0U;
unsigned int dir = 3, okflag = 3; unsigned int dir = 3, okflag = 3;
long pmin, pmax, cmin, cmax;
static snd_mixer_t *handle = NULL; static snd_mixer_t *handle = NULL;
snd_mixer_elem_t *elem; snd_mixer_elem_t *elem;
snd_mixer_selem_id_t *sid; snd_mixer_selem_id_t *sid;
@ -1337,8 +1395,6 @@ static int sset(unsigned int argc, char *argv[], int roflag, int keep_handle)
} }
if (roflag) if (roflag)
goto __skip_write; goto __skip_write;
snd_mixer_selem_get_playback_volume_range(elem, &pmin, &pmax);
snd_mixer_selem_get_capture_volume_range(elem, &cmin, &cmax);
for (idx = 1; idx < argc; idx++) { for (idx = 1; idx < argc; idx++) {
char *ptr = argv[idx], *optr; char *ptr = argv[idx], *optr;
int multi, firstchn = 1; int multi, firstchn = 1;
@ -1353,7 +1409,6 @@ static int sset(unsigned int argc, char *argv[], int roflag, int keep_handle)
for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++) { for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++) {
char *sptr = NULL; char *sptr = NULL;
int ival; int ival;
long lval;
if (!(channels & (1 << chn))) if (!(channels & (1 << chn)))
continue; continue;
@ -1390,12 +1445,7 @@ static int sset(unsigned int argc, char *argv[], int roflag, int keep_handle)
} }
simple_skip_word(&ptr, "toggle"); simple_skip_word(&ptr, "toggle");
} else if (isdigit(*ptr) || *ptr == '-' || *ptr == '+') { } else if (isdigit(*ptr) || *ptr == '-' || *ptr == '+') {
if (snd_mixer_selem_has_playback_volume(elem)) { set_volume_simple(elem, chn, &ptr, 0);
snd_mixer_selem_get_playback_volume(elem, chn, &lval);
snd_mixer_selem_set_playback_volume(elem, chn, get_volume_simple(&ptr, pmin, pmax, lval));
} else {
get_volume_simple(&ptr, 0, 100, 0);
}
} else if (simple_skip_word(&ptr, "cap") || simple_skip_word(&ptr, "rec") || } else if (simple_skip_word(&ptr, "cap") || simple_skip_word(&ptr, "rec") ||
simple_skip_word(&ptr, "nocap") || simple_skip_word(&ptr, "norec")) { simple_skip_word(&ptr, "nocap") || simple_skip_word(&ptr, "norec")) {
/* nothing */ /* nothing */
@ -1426,12 +1476,7 @@ static int sset(unsigned int argc, char *argv[], int roflag, int keep_handle)
} }
simple_skip_word(&ptr, "toggle"); simple_skip_word(&ptr, "toggle");
} else if (isdigit(*ptr) || *ptr == '-' || *ptr == '+') { } else if (isdigit(*ptr) || *ptr == '-' || *ptr == '+') {
if (snd_mixer_selem_has_capture_volume(elem)) { set_volume_simple(elem, chn, &ptr, 1);
snd_mixer_selem_get_capture_volume(elem, chn, &lval);
snd_mixer_selem_set_capture_volume(elem, chn, get_volume_simple(&ptr, cmin, cmax, lval));
} else {
get_volume_simple(&ptr, 0, 100, 0);
}
} else if (simple_skip_word(&ptr, "mute") || simple_skip_word(&ptr, "off") || } else if (simple_skip_word(&ptr, "mute") || simple_skip_word(&ptr, "off") ||
simple_skip_word(&ptr, "unmute") || simple_skip_word(&ptr, "on")) { simple_skip_word(&ptr, "unmute") || simple_skip_word(&ptr, "on")) {
/* nothing */ /* nothing */