File 0006-amixer-Add-the-support-for-mapped-volumes-like-alsam.patch of Package alsa-utils

From a2b66855e69b0f1dc27dab37f24057f717359884 Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Mon, 12 Mar 2012 10:56:22 +0100
Subject: [PATCH 06/13] amixer: Add the support for mapped volumes like
 alsamixer

When -M option is given, amixer handles the volumes in percent unit
with the mapping more intuitive to human ears just like in alsamixer.

The -R option is to take the raw value as the volume percentage.
This mode is still used as default.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 amixer/Makefile.am |    2 +-
 amixer/amixer.c    |  238 ++++++++++++++++++++++++++++++----------------------
 2 files changed, 141 insertions(+), 99 deletions(-)

diff --git a/amixer/Makefile.am b/amixer/Makefile.am
index fcd0e81..ba67a57 100644
--- a/amixer/Makefile.am
+++ b/amixer/Makefile.am
@@ -4,7 +4,7 @@ LDADD = -lm
 # CFLAGS += -g -Wall
 
 bin_PROGRAMS = amixer
-amixer_SOURCES = amixer.c
+amixer_SOURCES = amixer.c ../alsamixer/volume_mapping.c
 noinst_HEADERS = amixer.h
 man_MANS = amixer.1
 EXTRA_DIST = amixer.1
diff --git a/amixer/amixer.c b/amixer/amixer.c
index 9d2855d..a0a7387 100644
--- a/amixer/amixer.c
+++ b/amixer/amixer.c
@@ -29,7 +29,9 @@
 #include <assert.h>
 #include <alsa/asoundlib.h>
 #include <sys/poll.h>
+#include <stdint.h>
 #include "amixer.h"
+#include "../alsamixer/volume_mapping.h"
 
 #define LEVEL_BASIC		(1<<0)
 #define LEVEL_INACTIVE		(1<<1)
@@ -68,6 +70,8 @@ static int help(void)
 	printf("  -i,--inactive   show also inactive controls\n");
 	printf("  -a,--abstract L select abstraction level (none or basic)\n");
 	printf("  -s,--stdin      Read and execute commands from stdin sequentially\n");
+	printf("  -R,--raw-volume Use the raw value (default)\n");
+	printf("  -M,--mapped-volume Use the mapped volume\n");
 	printf("\nAvailable commands:\n");
 	printf("  scontrols       show all mixer simple controls\n");
 	printf("  scontents	  show contents of all mixer simple controls (default command)\n");
@@ -187,9 +191,9 @@ static int convert_db_range(int val, int omin, int omax, int nmin, int nmax)
 
 /* Fuction to convert from volume to percentage. val = volume */
 
-static int convert_prange(int val, int min, int max)
+static int convert_prange(long val, long min, long max)
 {
-	int range = max - min;
+	long range = max - min;
 	int tmp;
 
 	if (range == 0)
@@ -204,29 +208,6 @@ static int convert_prange(int val, int min, int max)
 #define convert_prange1(val, min, max) \
 	ceil((val) * ((max) - (min)) * 0.01 + (min))
 
-static const char *get_percent(int val, int min, int max)
-{
-	static char str[32];
-	int p;
-	
-	p = convert_prange(val, min, max);
-	sprintf(str, "%i [%i%%]", val, p);
-	return str;
-}
-
-#if 0
-static const char *get_percent1(int val, int min, int max, int min_dB, int max_dB)
-{
-	static char str[32];
-	int p, db;
-
-	p = convert_prange(val, min, max);
-	db = convert_db_range(val, min, max, min_dB, max_dB);
-	sprintf(str, "%i [%i%%] [%i.%02idB]", val, p, db / 100, abs(db % 100));
-	return str;
-}
-#endif
-
 static long get_integer(char **ptr, long min, long max)
 {
 	long val = min;
@@ -288,26 +269,83 @@ struct volume_ops {
 	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);
+		   long value, int dir);
 };
 	
-enum { VOL_RAW, VOL_DB };
+enum { VOL_RAW, VOL_DB, VOL_MAP };
 
 struct volume_ops_set {
 	int (*has_volume)(snd_mixer_elem_t *elem);
-	struct volume_ops v[2];
+	struct volume_ops v[3];
 };
 
 static int set_playback_dB(snd_mixer_elem_t *elem,
-			   snd_mixer_selem_channel_id_t c, long value)
+			   snd_mixer_selem_channel_id_t c, long value, int dir)
 {
-	return snd_mixer_selem_set_playback_dB(elem, c, value, 0);
+	return snd_mixer_selem_set_playback_dB(elem, c, value, dir);
 }
 
 static int set_capture_dB(snd_mixer_elem_t *elem,
-			  snd_mixer_selem_channel_id_t c, long value)
+			  snd_mixer_selem_channel_id_t c, long value, int dir)
+{
+	return snd_mixer_selem_set_capture_dB(elem, c, value, dir);
+}
+
+static int set_playback_raw_volume(snd_mixer_elem_t *elem,
+				   snd_mixer_selem_channel_id_t c,
+				   long value, int dir)
+{
+	return snd_mixer_selem_set_playback_volume(elem, c, value);
+}
+
+static int set_capture_raw_volume(snd_mixer_elem_t *elem,
+				  snd_mixer_selem_channel_id_t c,
+				  long value, int dir)
+{
+	return snd_mixer_selem_set_capture_volume(elem, c, value);
+}
+
+/* FIXME: normalize to int32 space to be compatible with other types */
+#define MAP_VOL_RES	(INT32_MAX / 100)
+
+static int get_mapped_volume_range(snd_mixer_elem_t *elem,
+				   long *pmin, long *pmax)
+{
+	*pmin = 0;
+	*pmax = MAP_VOL_RES;
+	return 0;
+}
+
+static int get_playback_mapped_volume(snd_mixer_elem_t *elem,
+				      snd_mixer_selem_channel_id_t c,
+				      long *value)
+{
+	*value = (rint)(get_normalized_playback_volume(elem, c) * MAP_VOL_RES);
+	return 0;
+}
+
+static int set_playback_mapped_volume(snd_mixer_elem_t *elem,
+				      snd_mixer_selem_channel_id_t c,
+				      long value, int dir)
 {
-	return snd_mixer_selem_set_capture_dB(elem, c, value, 0);
+	return set_normalized_playback_volume(elem, c,
+					      (double)value / MAP_VOL_RES, dir);
+}
+
+static int get_capture_mapped_volume(snd_mixer_elem_t *elem,
+				     snd_mixer_selem_channel_id_t c,
+				     long *value)
+{
+	*value = (rint)(get_normalized_capture_volume(elem, c) * MAP_VOL_RES);
+	return 0;
+}
+
+static int set_capture_mapped_volume(snd_mixer_elem_t *elem,
+				     snd_mixer_selem_channel_id_t c,
+				     long value, int dir)
+{
+	return set_normalized_capture_volume(elem, c,
+					     (double)value / MAP_VOL_RES, dir);
 }
 
 static const struct volume_ops_set vol_ops[2] = {
@@ -315,29 +353,42 @@ static const 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 },
+			set_playback_raw_volume },
 		      { snd_mixer_selem_get_playback_dB_range,
 			snd_mixer_selem_get_playback_dB,
-			set_playback_dB }},
+			set_playback_dB },
+		      { get_mapped_volume_range,
+			get_playback_mapped_volume,
+			set_playback_mapped_volume },
+		},
 	},
 	{
 		.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 },
+			set_capture_raw_volume },
 		      { snd_mixer_selem_get_capture_dB_range,
 			snd_mixer_selem_get_capture_dB,
-			set_capture_dB }},
+			set_capture_dB },
+		      { get_mapped_volume_range,
+			get_capture_mapped_volume,
+			set_capture_mapped_volume },
+		},
 	},
 };
 
+static int std_vol_type = VOL_RAW;
+
 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, err = 0, vol_type = VOL_RAW;
+	int invalid = 0, percent = 0, err = 0;
+	int vol_type = std_vol_type;
+	double scale = 1.0;
+	int correct = 0;
 
 	if (! vol_ops[dir].has_volume(elem))
 		invalid = 1;
@@ -347,10 +398,6 @@ static int set_volume_simple(snd_mixer_elem_t *elem,
 	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 == '.') {
@@ -358,32 +405,37 @@ static int set_volume_simple(snd_mixer_elem_t *elem,
 		strtol(p, &p, 10);
 	}
 	if (*p == '%') {
-		if (! invalid)
-			val = (long)convert_prange1(strtod(s, NULL), pmin, pmax);
+		percent = 1;
 		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;
-		}
+		vol_type = VOL_DB;
 		p += 2;
-	}
+		scale = 100;
+	} else
+		vol_type = VOL_RAW;
+
+	val = (long)(strtod(s, NULL) * scale);
+	if (vol_ops[dir].v[vol_type].get_range(elem, &pmin, &pmax) < 0)
+		invalid = 1;
+	if (percent)
+		val = (long)convert_prange1(val, pmin, pmax);
 	if (*p == '+' || *p == '-') {
 		if (! invalid) {
 			if (vol_ops[dir].v[vol_type].get(elem, chn, &orig) < 0)
 				invalid = 1;
-			if (*p == '+')
+			if (*p == '+') {
 				val = orig + val;
-			else
+				correct = -1;
+			} else {
 				val = orig - val;
+				correct = 1;
+			}
 		}
 		p++;
 	}
 	if (! invalid) {
 		val = check_range(val, pmin, pmax);
-		err = vol_ops[dir].v[vol_type].set(elem, chn, val);
+		err = vol_ops[dir].v[vol_type].set(elem, chn, val, correct);
 	}
  skip:
 	if (*p == ',')
@@ -711,15 +763,33 @@ static int controls(int level)
 	return 0;
 }
 
+static void show_selem_volume(snd_mixer_elem_t *elem, 
+			      snd_mixer_selem_channel_id_t chn, int dir,
+			      long min, long max)
+{
+	long raw, val;
+	vol_ops[dir].v[VOL_RAW].get(elem, chn, &raw);
+	if (std_vol_type == VOL_RAW)
+		val = convert_prange(raw, min, max);
+	else {
+		vol_ops[dir].v[std_vol_type].get(elem, chn, &val);
+		val = convert_prange(val, 0, MAP_VOL_RES);
+	}
+	printf(" %li [%li%%]", raw, val);
+	if (!vol_ops[dir].v[VOL_DB].get(elem, chn, &val)) {
+		printf(" [");
+		print_dB(val);
+		printf("]");
+	}
+}
+
 static int show_selem(snd_mixer_t *handle, snd_mixer_selem_id_t *id, const char *space, int level)
 {
 	snd_mixer_selem_channel_id_t chn;
 	long pmin = 0, pmax = 0;
 	long cmin = 0, cmax = 0;
-	long pvol, cvol;
 	int psw, csw;
 	int pmono, cmono, mono_ok = 0;
-	long db;
 	snd_mixer_elem_t *elem;
 	
 	elem = snd_mixer_find_selem(handle, id);
@@ -868,13 +938,7 @@ static int show_selem(snd_mixer_t *handle, snd_mixer_selem_id_t *id, const char
 				mono_ok = 1;
 			}
 			if (snd_mixer_selem_has_common_volume(elem)) {
-				snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_MONO, &pvol);
-				printf(" %s", get_percent(pvol, pmin, pmax));
-				if (!snd_mixer_selem_get_playback_dB(elem, SND_MIXER_SCHN_MONO, &db)) {
-					printf(" [");
-					print_dB(db);
-					printf("]");
-				}
+				show_selem_volume(elem, SND_MIXER_SCHN_MONO, 0, pmin, pmax);
 			}
 			if (snd_mixer_selem_has_common_switch(elem)) {
 				snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_MONO, &psw);
@@ -891,13 +955,7 @@ static int show_selem(snd_mixer_t *handle, snd_mixer_selem_id_t *id, const char
 				if (snd_mixer_selem_has_playback_volume(elem)) {
 					printf(" Playback");
 					title = 1;
-					snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_MONO, &pvol);
-					printf(" %s", get_percent(pvol, pmin, pmax));
-					if (!snd_mixer_selem_get_playback_dB(elem, SND_MIXER_SCHN_MONO, &db)) {
-						printf(" [");
-						print_dB(db);
-						printf("]");
-					}
+					show_selem_volume(elem, SND_MIXER_SCHN_MONO, 0, pmin, pmax);
 				}
 			}
 			if (!snd_mixer_selem_has_common_switch(elem)) {
@@ -919,13 +977,7 @@ static int show_selem(snd_mixer_t *handle, snd_mixer_selem_id_t *id, const char
 				if (snd_mixer_selem_has_capture_volume(elem)) {
 					printf(" Capture");
 					title = 1;
-					snd_mixer_selem_get_capture_volume(elem, SND_MIXER_SCHN_MONO, &cvol);
-					printf(" %s", get_percent(cvol, cmin, cmax));
-					if (!snd_mixer_selem_get_capture_dB(elem, SND_MIXER_SCHN_MONO, &db)) {
-						printf(" [");
-						print_dB(db);
-						printf("]");
-					}
+					show_selem_volume(elem, SND_MIXER_SCHN_MONO, 1, cmin, cmax);
 				}
 			}
 			if (!snd_mixer_selem_has_common_switch(elem)) {
@@ -946,13 +998,7 @@ static int show_selem(snd_mixer_t *handle, snd_mixer_selem_id_t *id, const char
 					continue;
 				printf("%s%s:", space, snd_mixer_selem_channel_name(chn));
 				if (!pmono && !cmono && snd_mixer_selem_has_common_volume(elem)) {
-					snd_mixer_selem_get_playback_volume(elem, chn, &pvol);
-					printf(" %s", get_percent(pvol, pmin, pmax));
-					if (!snd_mixer_selem_get_playback_dB(elem, chn, &db)) {
-						printf(" [");
-						print_dB(db);
-						printf("]");
-					}
+					show_selem_volume(elem, chn, 0, pmin, pmax);
 				}
 				if (!pmono && !cmono && snd_mixer_selem_has_common_switch(elem)) {
 					snd_mixer_selem_get_playback_switch(elem, chn, &psw);
@@ -964,13 +1010,7 @@ static int show_selem(snd_mixer_t *handle, snd_mixer_selem_id_t *id, const char
 						if (snd_mixer_selem_has_playback_volume(elem)) {
 							printf(" Playback");
 							title = 1;
-							snd_mixer_selem_get_playback_volume(elem, chn, &pvol);
-							printf(" %s", get_percent(pvol, pmin, pmax));
-							if (!snd_mixer_selem_get_playback_dB(elem, chn, &db)) {
-								printf(" [");
-								print_dB(db);
-								printf("]");
-							}
+							show_selem_volume(elem, chn, 0, pmin, pmax);
 						}
 					}
 					if (!snd_mixer_selem_has_common_switch(elem)) {
@@ -988,13 +1028,7 @@ static int show_selem(snd_mixer_t *handle, snd_mixer_selem_id_t *id, const char
 						if (snd_mixer_selem_has_capture_volume(elem)) {
 							printf(" Capture");
 							title = 1;
-							snd_mixer_selem_get_capture_volume(elem, chn, &cvol);
-							printf(" %s", get_percent(cvol, cmin, cmax));
-							if (!snd_mixer_selem_get_capture_dB(elem, chn, &db)) {
-								printf(" [");
-								print_dB(db);
-								printf("]");
-							}
+							show_selem_volume(elem, chn, 1, cmin, cmax);
 						}
 					}
 					if (!snd_mixer_selem_has_common_switch(elem)) {
@@ -1927,6 +1961,8 @@ int main(int argc, char *argv[])
 		{"version", 0, NULL, 'v'},
 		{"abstract", 1, NULL, 'a'},
 		{"stdin", 0, NULL, 's'},
+		{"raw-volume", 0, NULL, 'R'},
+		{"mapped-volume", 0, NULL, 'M'},
 		{NULL, 0, NULL, 0},
 	};
 
@@ -1934,7 +1970,7 @@ int main(int argc, char *argv[])
 	while (1) {
 		int c;
 
-		if ((c = getopt_long(argc, argv, "hc:D:qidnva:s", long_option, NULL)) < 0)
+		if ((c = getopt_long(argc, argv, "hc:D:qidnva:sRM", long_option, NULL)) < 0)
 			break;
 		switch (c) {
 		case 'h':
@@ -1987,6 +2023,12 @@ int main(int argc, char *argv[])
 		case 's':
 			read_stdin = 1;
 			break;
+		case 'R':
+			std_vol_type = VOL_RAW;
+			break;
+		case 'M':
+			std_vol_type = VOL_MAP;
+			break;
 		default:
 			fprintf(stderr, "Invalid switch or option needs an argument.\n");
 			morehelp++;
-- 
1.7.9.2

openSUSE Build Service is sponsored by