]> git.alsa-project.org Git - alsa-utils.git/commitdiff
Add handling of dB to amixer
authorTakashi Iwai <tiwai@suse.de>
Fri, 25 Aug 2006 10:00:14 +0000 (12:00 +0200)
committerTakashi Iwai <tiwai@suse.de>
Fri, 25 Aug 2006 10:00:14 +0000 (12:00 +0200)
Added the dB value handling to amixer sset command.
Also, simplify the parser code.

Updated man page for dB suffix and some examples.

amixer/amixer.1
amixer/amixer.c

index e8fc63b320ad12a64e4b0ef9e0dd8d9b251e8b5a..a8d95e13e8ee6376a54971e65d68171a488d09f2 100644 (file)
@@ -33,14 +33,23 @@ Shows a complete list of simple mixer controls with their contents.
 .TP
 \fIset\fP or \fIsset\fP <\fISCONTROL\fP> <\fIPARAMETER\fP> ...
 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.
-The parameters \fIcap, nocap, mute, unmute, toggle\fP are used to
-change capture (recording) and muting for the group specified.
-The parameters \fIfront, rear, center, woofer\fP are used to specify
-channels to be changed. When plus(+) or minus(\-) letter is appended after
+either as a percentage from 0% to 100% with \fI%\fP suffix,
+a dB gain with \fIdB\fP suffix (like -12.5dB), or an exact hardware value.
+The dB gain can be used only for the mixer elements with available
+dB information.
+When plus(+) or minus(\-) letter is appended after
 volume value, the volume is incremented or decremented from the current
 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
 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
 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
 \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%
index 921a5e0ca199d1ac8635a972c7b7d7070d04b306..691c1c5f58fb5d422cd003241c4a3db58543ea93 100644 (file)
@@ -161,17 +161,8 @@ static const char *control_access(snd_ctl_elem_info_t *info)
        return result;
 }
 
-static int check_range(int val, int min, int max)
-{
-       if (no_check)
-               return val;
-       if (val < min)
-               return min;
-       if (val > max)
-               return max;
-       return val;
-}
-
+#define check_range(val, min, max) \
+       (no_check ? (val) : ((val < min) ? (min) : (val > max) ? (max) : (val))) 
 #if 0
 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 */
 
-static int convert_prange1(int val, int min, int max)
-{
-       int range = max - min;
-       int tmp;
-
-       if (range == 0)
-               return 0;
-
-       tmp = rint((double)range * ((double)val*.01)) + min;
-       return tmp;
-}
+#define convert_prange1(val, min, max) \
+       ceil((val) * ((max) - (min)) * 0.01 + (min))
 
 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)
 {
-       int tmp, tmp1, tmp2;
-
-       if (**ptr == ':')
-               (*ptr)++;
-       if (**ptr == '\0' || (!isdigit(**ptr) && **ptr != '-'))
-               return min;
-       tmp = strtol(*ptr, ptr, 10);
-       tmp1 = tmp;
-       tmp2 = 0;
-       if (**ptr == '.') {
-               (*ptr)++;
-               tmp2 = strtol(*ptr, ptr, 10);
-       }
-       if (**ptr == '%') {
-               tmp1 = convert_prange1(tmp, min, max);
-               (*ptr)++;
-       }
-       tmp1 = check_range(tmp1, min, max);
-       if (**ptr == ',')
-               (*ptr)++;
-       return tmp1;
+       long val = min;
+       char *p = *ptr, *s;
+
+       if (*p == ':')
+               p++;
+       if (*p == '\0' || (!isdigit(*p) && *p != '-'))
+               goto out;
+
+       s = p;
+       val = strtol(s, &p, 10);
+       if (*p == '.') {
+               p++;
+               strtol(p, &p, 10);
+       }
+       if (*p == '%') {
+               val = (long)convert_prange1(strtod(s, NULL), min, max);
+               p++;
+       }
+       val = check_range(val, min, max);
+       if (*p == ',')
+               p++;
+ out:
+       *ptr = p;
+       return val;
 }
 
 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 (*p == ':')
+               p++;
+       if (*p == '\0' || (!isdigit(*p) && *p != '-'))
+               goto out;
+
+       s = p;
+       val = strtol(s, &p, 10);
+       if (*p == '.') {
+               p++;
+               strtol(p, &p, 10);
+       }
+       if (*p == '%') {
+               val = (long long)convert_prange1(strtod(s, NULL), min, max);
+               p++;
+       }
+       val = check_range(val, min, max);
+       if (*p == ',')
+               p++;
+ out:
+       *ptr = p;
+       return val;
+}
 
-       if (**ptr == ':')
-               (*ptr)++;
-       if (**ptr == '\0' || (!isdigit(**ptr) && **ptr != '-'))
-               return min;
-       tmp = strtol(*ptr, ptr, 10);
-       tmp1 = tmp;
-       tmp2 = 0;
-       if (**ptr == '.') {
-               (*ptr)++;
-               tmp2 = strtol(*ptr, ptr, 10);
-       }
-       if (**ptr == '%') {
-               tmp1 = convert_prange1(tmp, min, max);
-               (*ptr)++;
-       }
-       tmp1 = check_range(tmp1, min, max);
-       if (**ptr == ',')
-               (*ptr)++;
-       return tmp1;
+struct volume_ops {
+       int (*get_range)(snd_mixer_elem_t *elem, long *min, long *max);
+       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 };
+
+struct volume_ops_set {
+       int (*has_volume)(snd_mixer_elem_t *elem);
+       struct volume_ops v[2];
+};
+
+static int set_playback_dB(snd_mixer_elem_t *elem,
+                          snd_mixer_selem_channel_id_t c, long value)
+{
+       return snd_mixer_selem_set_playback_dB(elem, c, value, 0);
 }
 
-static int get_volume_simple(char **ptr, int min, int max, int orig)
+static int set_capture_dB(snd_mixer_elem_t *elem,
+                         snd_mixer_selem_channel_id_t c, long value)
 {
-       int tmp, tmp1, tmp2;
+       return snd_mixer_selem_set_capture_dB(elem, c, value, 0);
+}
 
-       if (**ptr == ':')
-               (*ptr)++;
-       if (**ptr == '\0' || (!isdigit(**ptr) && **ptr != '-'))
-               return min;
-       tmp = atoi(*ptr);
-       if (**ptr == '-')
-               (*ptr)++;
-       while (isdigit(**ptr))
-               (*ptr)++;
-       tmp1 = tmp;
-       tmp2 = 0;
-       if (**ptr == '.') {
-               (*ptr)++;
-               tmp2 = atoi(*ptr);
-               while (isdigit(**ptr))
-                       (*ptr)++;
-       }
-       if (**ptr == '%') {
-               tmp1 = convert_prange1(tmp, min, max);
-               (*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 (*p == '%') {
+               if (! invalid)
+                       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 (*p == '+' || *p == '-') {
+               if (! invalid) {
+                       if (vol_ops[dir].v[vol_type].get(elem, chn, &orig) < 0)
+                               invalid = 1;
+                       if (*p == '+')
+                               val = orig + val;
+                       else
+                               val = orig - val;
+               }
+               p++;
        }
-       if (**ptr == '+') {
-               tmp1 = orig + tmp1;
-               (*ptr)++;
-       } else if (**ptr == '-') {
-               tmp1 = orig - tmp1;
-               (*ptr)++;
+       if (! invalid) {
+               val = check_range(val, pmin, pmax);
+               vol_ops[dir].v[vol_type].set(elem, chn, val);
        }
-       tmp1 = check_range(tmp1, min, max);
-       if (**ptr == ',')
-               (*ptr)++;
-       return tmp1;
+ skip:
+       if (*p == ',')
+               p++;
+       *ptr = p;
+       return 0;
 }
 
 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;
        unsigned int channels = ~0U;
        unsigned int dir = 3, okflag = 3;
-       long pmin, pmax, cmin, cmax;
        static snd_mixer_t *handle = NULL;
        snd_mixer_elem_t *elem;
        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)
                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++) {
                char *ptr = argv[idx], *optr;
                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++) {
                        char *sptr = NULL;
                        int ival;
-                       long lval;
 
                        if (!(channels & (1 << chn)))
                                continue;
@@ -1390,12 +1445,7 @@ static int sset(unsigned int argc, char *argv[], int roflag, int keep_handle)
                                        }
                                        simple_skip_word(&ptr, "toggle");
                                } else if (isdigit(*ptr) || *ptr == '-' || *ptr == '+') {
-                                       if (snd_mixer_selem_has_playback_volume(elem)) {
-                                               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);
-                                       }
+                                       set_volume_simple(elem, chn, &ptr, 0);
                                } else if (simple_skip_word(&ptr, "cap") || simple_skip_word(&ptr, "rec") ||
                                           simple_skip_word(&ptr, "nocap") || simple_skip_word(&ptr, "norec")) {
                                        /* nothing */
@@ -1426,12 +1476,7 @@ static int sset(unsigned int argc, char *argv[], int roflag, int keep_handle)
                                        }
                                        simple_skip_word(&ptr, "toggle");
                                } else if (isdigit(*ptr) || *ptr == '-' || *ptr == '+') {
-                                       if (snd_mixer_selem_has_capture_volume(elem)) {
-                                               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);
-                                       }
+                                       set_volume_simple(elem, chn, &ptr, 1);
                                } else if (simple_skip_word(&ptr, "mute") || simple_skip_word(&ptr, "off") ||
                                           simple_skip_word(&ptr, "unmute") || simple_skip_word(&ptr, "on")) {
                                        /* nothing */