[PATCH 2/8] Add native ALSA mixer support

Doug Torrance <[email protected]>
Newsgroups gmane.compw.window-managers.windowmaker.devel
Message-ID <[email protected]>
From: Johannes Holmberg <[email protected]>

---
 wmix/Makefile             |   6 +-
 wmix/config.c             |  38 +++--
 wmix/include/config.h     |   7 +-
 wmix/include/mixer-alsa.h |  43 +++++
 wmix/include/mixer-oss.h  |  42 +++++
 wmix/include/mixer.h      |  49 +++---
 wmix/mixer-alsa.c         | 399 ++++++++++++++++++++++++++++++++++++++++++++++
 wmix/mixer-oss.c          | 116 +++++++-------
 wmix/wmix.c               |  57 ++++++-
 9 files changed, 654 insertions(+), 103 deletions(-)
 create mode 100644 wmix/include/mixer-alsa.h
 create mode 100644 wmix/include/mixer-oss.h
 create mode 100644 wmix/mixer-alsa.c

diff --git a/wmix/Makefile b/wmix/Makefile
index 8f9a2eb..6e94a34 100644
--- a/wmix/Makefile
+++ b/wmix/Makefile
@@ -1,8 +1,8 @@
 CC		= gcc
-CFLAGS		= -O3 -W -Wall
+CFLAGS		= -std=gnu99 -O3 -W -Wall `pkg-config --cflags alsa`
 LDFLAGS		= -L/usr/X11R6/lib
-LIBS		= -lXpm -lXext -lX11 -lm
-OBJECTS		= misc.o config.o mixer-oss.o ui_x.o mmkeys.o wmix.o
+LIBS		= -lXpm -lXext -lX11 -lm `pkg-config --libs alsa`
+OBJECTS		= misc.o config.o mixer-alsa.o mixer-oss.o ui_x.o mmkeys.o wmix.o
 
 # where to install this program (also for packaging stuff)
 PREFIX		= /usr/local
diff --git a/wmix/config.c b/wmix/config.c
index 7034271..c11c2fe 100644
--- a/wmix/config.c
+++ b/wmix/config.c
@@ -30,19 +30,21 @@
 
 #include "include/common.h"
 #include "include/config.h"
-
+#include "include/misc.h"
 
 #define VERSION_TEXT \
 	"WMixer " VERSION " by [email protected] + [email protected]\n"
 
 #define HELP_TEXT \
 	"usage:\n" \
+        "  -a <api>  use this sound api (oss or alsa) [alsa]\n" \
 	"  -d <dsp>  connect to remote X display\n" \
 	"  -e <name> exclude channel, can be used many times\n" \
 	"  -f <file> parse this config [~/.wmixrc]\n" \
 	"  -h        print this help\n" \
 	"  -k        disable grabing volume control keys\n" \
-	"  -m <dev>  mixer device [/dev/mixer]\n" \
+	"  -m <dev>  oss mixer device [/dev/mixer]\n" \
+	"            or alsa card name [default]\n" \
 	"  -v        verbose -> id, long name, name\n" \
 
 /* The global configuration */
@@ -50,7 +52,7 @@ struct _Config config;
 
 /* The default device used for Mixer control */
 static const char default_mixer_device[] = "/dev/mixer";
-
+static const char default_card_name[] = "default";
 /* Default color for OSD */
 const char default_osd_color[] = "green";
 
@@ -61,8 +63,8 @@ const char default_osd_color[] = "green";
 void config_init(void)
 {
 	memset(&config, 0, sizeof(config));
-
-	config.mixer_device = (char *) default_mixer_device;
+        config.api = 0;
+	config.mixer_device = NULL;
 	config.mousewheel = 1;
 	config.scrolltext = 1;
 	config.mmkeys = 1;
@@ -89,13 +91,14 @@ void config_release(void)
 	if (config.display_name)
 		free(config.display_name);
 
-	if (config.mixer_device != default_mixer_device)
+	if (config.mixer_device != default_mixer_device
+            && config.mixer_device != default_card_name)
 		free(config.mixer_device);
 
 	if (config.osd_color != default_osd_color)
 		free(config.osd_color);
 
-	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+	for (i = 0; i < EXCLUDE_MAX_COUNT; i++) {
 		if (config.exclude_channel[i])
 			free(config.exclude_channel[i]);
 		else
@@ -119,7 +122,7 @@ void parse_cli_options(int argc, char **argv)
 	config.verbose = false;
 	error_found = false;
 	for (;;) {
-		opt = getopt(argc, argv, ":d:e:f:hkm:v");
+		opt = getopt(argc, argv, ":a:d:e:f:hkm:v");
 		if (opt == -1)
 			break;
 
@@ -133,7 +136,12 @@ void parse_cli_options(int argc, char **argv)
 			fprintf(stderr, "wmix:error: missing argument for option '-%c'\n", optopt);
 			error_found = true;
 			break;
-
+		case 'a':
+			if(!strcmp("oss", optarg))
+				config.api = 1;
+			else if (strcmp("alsa", optarg))
+				fprintf(stderr, "Warning: Incorrect sound api specified, defaulting to alsa\n");
+			break;
 		case 'd':
 			if (config.display_name)
 				free(config.display_name);
@@ -141,7 +149,7 @@ void parse_cli_options(int argc, char **argv)
 			break;
 
 		case 'e':
-			if (count_exclude < SOUND_MIXER_NRDEVICES) {
+			if (count_exclude < EXCLUDE_MAX_COUNT) {
 				config.exclude_channel[count_exclude] = strdup(optarg);
 				count_exclude++;
 			} else
@@ -180,6 +188,13 @@ void parse_cli_options(int argc, char **argv)
 	}
 	config.exclude_channel[count_exclude] = NULL;
 
+	if (!config.mixer_device) {
+		if (config.api == 0)
+			config.mixer_device = (char *)default_card_name;
+		else if (config.api == 1)
+			config.mixer_device = (char *)default_mixer_device;
+	}
+
 	if (optind < argc) {
 		fprintf(stderr, "wmix:error: argument '%s' not understood\n", argv[optind]);
 		error_found = true;
@@ -296,9 +311,10 @@ void config_read(void)
 		} else if (strcmp(keyword, "exclude") == 0) {
 			int i;
 
-			for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+			for (i = 0; i < EXCLUDE_MAX_COUNT; i++) {
 				if (config.exclude_channel[i] == NULL) {
 					config.exclude_channel[i] = strdup(value);
+					config.exclude_channel[i+1] = NULL;
 					break;
 				}
 
diff --git a/wmix/include/config.h b/wmix/include/config.h
index 1f45fc9..609336d 100644
--- a/wmix/include/config.h
+++ b/wmix/include/config.h
@@ -20,9 +20,7 @@
 #ifndef WMIX_CONFIG_H
 #define WMIX_CONFIG_H
 
-/* Needed for SOUND_MIXER_NRDEVICES */
-#include <sys/soundcard.h>
-
+#define EXCLUDE_MAX_COUNT 100
 
 /* Global Configuration */
 extern struct _Config {
@@ -30,6 +28,7 @@ extern struct _Config {
 	char        *display_name;		/* X Display to connect to */
 	char        *mixer_device;		/* device file to use for controlling Mixer volumes */
 
+	unsigned int api;               /* Sound API (0 = ALSA, 1 = OSS) */
 	unsigned int verbose    : 1;	/* be Verbose when starting */
 	unsigned int osd        : 1;	/* show OSD? */
 	unsigned int mousewheel : 1;	/* mousewheel enabled? */
@@ -42,7 +41,7 @@ extern struct _Config {
 	float        scrollstep;		/* scroll mouse step adjustment */
 	char        *osd_color;			/* osd color */
 
-	char        *exclude_channel[SOUND_MIXER_NRDEVICES + 1];	/* Devices to exclude from GUI's list */
+	char        *exclude_channel[EXCLUDE_MAX_COUNT + 1];	/* Devices to exclude from GUI's list */
 } config;
 
 /* Default color for OSD */
diff --git a/wmix/include/mixer-alsa.h b/wmix/include/mixer-alsa.h
new file mode 100644
index 0000000..263fb47
--- /dev/null
+++ b/wmix/include/mixer-alsa.h
@@ -0,0 +1,43 @@
+/* WMix 3.0 -- a mixer using the OSS mixer API.
+ * Copyright (C) 2000, 2001
+ *	Daniel Richard G. <[email protected]>,
+ *	timecop <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+void mixer_alsa_init(const char *mixer_oss_device,
+                     bool verbose,
+                     const char *exclude[]);
+bool mixer_alsa_is_changed(void);
+int mixer_alsa_get_channel_count(void);
+int mixer_alsa_get_channel(void);
+const char *mixer_alsa_get_channel_name(void);
+const char *mixer_alsa_get_short_name(void);
+void mixer_alsa_set_channel(int channel);
+void mixer_alsa_set_channel_rel(int delta_channel);
+float mixer_alsa_get_volume(void);
+void mixer_alsa_set_volume(float volume);
+void mixer_alsa_set_volume_rel(float delta_volume);
+float mixer_alsa_get_balance(void);
+void mixer_alsa_set_balance(float balance);
+void mixer_alsa_set_balance_rel(float delta_balance);
+void mixer_alsa_toggle_mute(void);
+void mixer_alsa_toggle_rec(void);
+bool mixer_alsa_is_muted(void);
+bool mixer_alsa_is_stereo(void);
+bool mixer_alsa_is_rec(void);
+bool mixer_alsa_can_rec (void);
+void mixer_alsa_tick(void);
diff --git a/wmix/include/mixer-oss.h b/wmix/include/mixer-oss.h
new file mode 100644
index 0000000..ae358cf
--- /dev/null
+++ b/wmix/include/mixer-oss.h
@@ -0,0 +1,42 @@
+/* WMix 3.0 -- a mixer using the OSS mixer API.
+ * Copyright (C) 2000, 2001
+ *	Daniel Richard G. <[email protected]>,
+ *	timecop <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+void mixer_oss_init(const char *mixer_oss_device,
+                    bool verbose,
+                    const char *exclude[]);
+bool mixer_oss_is_changed(void);
+int mixer_oss_get_channel_count(void);
+int mixer_oss_get_channel(void);
+const char *mixer_oss_get_channel_name(void);
+const char *mixer_oss_get_short_name(void);
+void mixer_oss_set_channel(int channel);
+void mixer_oss_set_channel_rel(int delta_channel);
+float mixer_oss_get_volume(void);
+void mixer_oss_set_volume(float volume);
+void mixer_oss_set_volume_rel(float delta_volume);
+float mixer_oss_get_balance(void);
+void mixer_oss_set_balance(float balance);
+void mixer_oss_set_balance_rel(float delta_balance);
+void mixer_oss_toggle_mute(void);
+void mixer_oss_toggle_rec(void);
+bool mixer_oss_is_muted(void);
+bool mixer_oss_is_stereo(void);
+bool mixer_oss_is_rec(void);
+bool mixer_oss_can_rec(void);
diff --git a/wmix/include/mixer.h b/wmix/include/mixer.h
index 0195291..116d8ed 100644
--- a/wmix/include/mixer.h
+++ b/wmix/include/mixer.h
@@ -55,27 +55,28 @@
  * - Muting must occur independently of the volume level.
  */
 
-void		mixer_init			(const char *mixer_device,
-						 bool verbose,
-						 const char *exclude[]);
-bool		mixer_is_changed		(void);
-int		mixer_get_channel_count		(void);
-int		mixer_get_channel		(void);
-const char *	mixer_get_channel_name		(void);
-const char *	mixer_get_short_name		(void);
-void		mixer_set_channel		(int channel);
-void		mixer_set_channel_rel		(int delta_channel);
-float		mixer_get_volume		(void);
-void		mixer_set_volume		(float volume);
-void		mixer_set_volume_rel		(float delta_volume);
-float		mixer_get_balance		(void);
-void		mixer_set_balance		(float balance);
-void		mixer_set_balance_rel		(float delta_balance);
-void		mixer_toggle_mute		(void);
-void		mixer_toggle_rec		(void);
-bool		mixer_is_muted			(void);
-bool		mixer_is_stereo			(void);
-bool		mixer_is_rec			(void);
-bool		mixer_can_rec			(void);
-bool            is_exclude                      (const char *short_name,
-						 const char *exclude[]);
+void (*mixer_init)(const char *mixer_device,
+                   bool verbose,
+                   const char *exclude[]);
+bool (*mixer_is_changed)(void);
+int (*mixer_get_channel_count)(void);
+int (*mixer_get_channel)(void);
+const char *(*mixer_get_channel_name)(void);
+const char *(*mixer_get_short_name)(void);
+void (*mixer_set_channel)(int channel);
+void (*mixer_set_channel_rel)(int delta_channel);
+float (*mixer_get_volume)(void);
+void (*mixer_set_volume)(float volume);
+void (*mixer_set_volume_rel)(float delta_volume);
+float (*mixer_get_balance)(void);
+void (*mixer_set_balance)(float balance);
+void (*mixer_set_balance_rel)(float delta_balance);
+void (*mixer_toggle_mute)(void);
+void (*mixer_toggle_rec)(void);
+bool (*mixer_is_muted)(void);
+bool (*mixer_is_stereo)(void);
+bool (*mixer_is_rec)(void);
+bool (*mixer_can_rec)(void);
+bool (*is_exclude)(const char *short_name,
+                   const char *exclude[]);
+void (*mixer_tick)(void);
diff --git a/wmix/mixer-alsa.c b/wmix/mixer-alsa.c
new file mode 100644
index 0000000..065e01a
--- /dev/null
+++ b/wmix/mixer-alsa.c
@@ -0,0 +1,399 @@
+#include "include/common.h"
+#include "include/misc.h"
+#include "include/mixer-alsa.h"
+
+#include <alsa/asoundlib.h>
+
+static bool get_mixer_state(void);
+
+struct mixer_element {
+    const char *name;		/* name of channel */
+    const char *sname;		/* short name of the channel */
+    snd_mixer_elem_t *element;  /* Mixer element pointer */
+    long min;                   /* Min volume */
+    long max;                   /* Max volume */
+    int dev;			/* channel device number */
+    int prev_dev_lr_volume;	/* last known left/right volume
+				 * (in device format) */
+    float volume;		/* volume, in [0, 1] */
+    float balance;		/* balance, in [-1, 1] */
+    bool has_playback;
+    bool has_playback_switch;
+    bool has_capture;
+    bool has_capture_switch;
+    bool is_recording;		/* is it recording? */
+    bool is_stereo;		/* capable of stereo? */
+    bool is_muted;		/* is it muted? */
+    int (*get_volume)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, long *);
+    int (*set_volume)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, long);
+};
+
+static snd_mixer_t *mixer;
+static struct mixer_element *element;
+static int cur_element = 0;
+static int n_elements;
+static bool needs_update = true;
+
+static int elem_callback(__attribute__((unused)) snd_mixer_elem_t *elem,
+                         __attribute__((unused)) unsigned int mask)
+{
+    needs_update = true;
+    return 0;
+}
+
+static int mixer_callback(__attribute__((unused)) snd_mixer_t *ctl,
+                          unsigned int mask,
+			  snd_mixer_elem_t *elem)
+{
+    if (mask & SND_CTL_EVENT_MASK_ADD) {
+        snd_mixer_elem_set_callback(elem, elem_callback);
+        needs_update = true;
+    }
+    return 0;
+}
+
+static bool is_exclude(const char *short_name,
+                       const char *exclude[])
+{
+    for (int i = 0; exclude[i] != NULL; i++) {
+        if (!strcmp(short_name, exclude[i])) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void mixer_alsa_init(const char *mixer_device, bool verbose, const char * exclude[])
+{
+    int err;
+    static struct snd_mixer_selem_regopt selem_regopt = {
+        .ver = 1,
+        .abstract = SND_MIXER_SABSTRACT_NONE,
+    };
+    selem_regopt.device = mixer_device;
+    if (verbose) {
+        printf("Sound card: %s\n", mixer_device);
+        puts("Supported elements:");
+    }
+
+    if ((err = snd_mixer_open(&mixer, 0)) < 0) {
+        fprintf(stderr, "snd_mixer_open error");
+        mixer = NULL;
+        return;
+    }
+
+    if ((err = snd_mixer_selem_register(mixer, &selem_regopt, NULL)) < 0) {
+        fprintf(stderr, "snd_mixer_selem_register error");
+        snd_mixer_close(mixer);
+        mixer = NULL;
+        return;
+    }
+
+    snd_mixer_set_callback(mixer, mixer_callback);
+
+    if ((err = snd_mixer_load(mixer)) < 0) {
+        fprintf(stderr, "snd_mixer_load error");
+        snd_mixer_close(mixer);
+        mixer = NULL;
+        return;
+    }
+
+    int all_elements = snd_mixer_get_count(mixer);
+    element = (struct mixer_element *)malloc(all_elements *
+                                             sizeof(struct mixer_element));
+    memset(element, 0, all_elements * sizeof(struct mixer_element));
+    snd_mixer_elem_t *elem = snd_mixer_first_elem(mixer);
+
+    int f = 0;
+    for (int e = 0; e < all_elements; e++) {
+        element[f].element = elem;
+        element[f].name = snd_mixer_selem_get_name(elem);
+
+        if (is_exclude(element[f].name, exclude)) {
+            if (verbose)
+                printf("  x: '%s' - disabled\n", element[f].name);
+            elem = snd_mixer_elem_next(elem);
+            continue;
+        }
+
+        element[f].sname = element[f].name;
+
+        element[f].has_capture = snd_mixer_selem_has_capture_volume(elem);
+        element[f].has_capture &= snd_mixer_selem_has_capture_channel(elem, SND_MIXER_SCHN_FRONT_LEFT);
+
+        element[f].has_playback = snd_mixer_selem_has_playback_volume(elem);
+        element[f].has_playback &= snd_mixer_selem_has_playback_channel(elem,SND_MIXER_SCHN_FRONT_LEFT);
+
+        if (element[f].has_playback) {
+            snd_mixer_selem_get_playback_volume_range(elem, &(element[f].min), &(element[f].max));
+            element[f].is_stereo = !snd_mixer_selem_is_playback_mono(elem);
+            element[f].is_stereo &= snd_mixer_selem_has_playback_channel(elem,SND_MIXER_SCHN_FRONT_RIGHT);
+            element[f].get_volume = snd_mixer_selem_get_playback_volume;
+            element[f].set_volume = snd_mixer_selem_set_playback_volume;
+            element[f].has_playback_switch = snd_mixer_selem_has_playback_switch(elem);
+        }
+        else if (element[f].has_capture) {
+            snd_mixer_selem_get_capture_volume_range(elem, &(element[f].min), &(element[f].max));
+            element[f].is_stereo = !snd_mixer_selem_is_capture_mono(element[f].element);
+            element[f].is_stereo &= snd_mixer_selem_has_capture_channel(elem,SND_MIXER_SCHN_FRONT_RIGHT);
+            element[f].get_volume = snd_mixer_selem_get_capture_volume;
+            element[f].set_volume = snd_mixer_selem_set_capture_volume;
+            element[f].has_capture_switch = snd_mixer_selem_has_capture_switch(elem);
+        } else {
+            elem = snd_mixer_elem_next(elem);
+            continue;
+        }
+
+        if (verbose) {
+            printf("  %d: '%s'%s%s%s%s %s (%ld - %ld)\n",
+                   f,
+                   element[f].name,
+                   element[f].has_playback? " pvolume" : "",
+                   element[f].has_playback_switch? " pswitch" : "",
+                   element[f].has_capture? " cvolume" : "",
+                   element[f].has_capture_switch? " cswitch" : "",
+                   element[f].is_stereo? "Stereo" : "Mono",
+                   element[f].min, element[f].max);
+        }
+
+        elem = snd_mixer_elem_next(elem);
+        f++;
+    }
+    n_elements = f;
+    get_mixer_state();
+}
+
+static bool element_is_muted(int e)
+{
+    if (!element[e].has_playback_switch)
+        return false;
+
+    snd_mixer_elem_t *elem = element[e].element;
+    int left_on;
+    snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, &left_on);
+    if (left_on)
+        return false;
+    if (element[e].is_stereo) {
+        int right_on;
+        snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, &right_on);
+        if (right_on)
+            return false;
+    }
+    return true;
+}
+
+static bool element_is_recording(int e)
+{
+    if (!element[e].has_capture_switch)
+        return false;
+
+    snd_mixer_elem_t *elem = element[e].element;
+    int left_on;
+    snd_mixer_selem_get_capture_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, &left_on);
+    if (left_on)
+        return true;
+    if (element[e].is_stereo) {
+        int right_on;
+        snd_mixer_selem_get_capture_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, &right_on);
+        if (right_on)
+            return true;
+    }
+    return false;
+}
+
+static bool get_mixer_state(void)
+{
+    if (!needs_update)
+        return false;
+    needs_update = false;
+
+    for (int e = 0; e < n_elements; e++) {
+        snd_mixer_elem_t *elem = element[e].element;
+        long left, right;
+
+        element[e].get_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &left);
+        float fleft = 1.0 * (left-element[e].min) / (element[e].max-element[e].min);
+        if (element[e].is_stereo) {
+            element[e].get_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &right);
+            float fright = 1.0 * (right-element[e].min) /
+                (element[e].max-element[e].min);
+            lr_to_vb(fleft, fright, &(element[e].volume), &(element[e].balance));
+        } else {
+            element[e].volume = fleft;
+            element[e].balance = 0.0;
+        }
+
+        element[e].is_muted = element_is_muted(e);
+        element[e].is_recording = element_is_recording(e);
+
+        //printf("Channel %s, has_vol: %d, stereo: %d, muted: %d, max: %ld, min: %ld, left: %ld, right: %ld, vol: %f, bal: %f\n", element[e].name, element[e].has_playback, element[e].is_stereo, element[e].is_muted, element[e].max, element[e].min, left, element[e].is_stereo?right:-1, element[e].volume, element[e].balance);            
+    }
+    return true;
+}
+
+static void set_mixer_state(void)
+{
+    float left, right;
+    long dev_left_volume, dev_right_volume;
+    if (!element[cur_element].has_playback && !element[cur_element].has_capture)
+        return;
+
+    bool muted = element_is_muted(cur_element);
+    if (muted != element[cur_element].is_muted) {
+        snd_mixer_selem_set_playback_switch_all(element[cur_element].element,
+                                                element[cur_element].is_muted?
+                                                0:1);
+    }
+
+    bool recording = element_is_recording(cur_element);
+    if (recording != element[cur_element].is_recording) {
+        snd_mixer_selem_set_capture_switch_all(element[cur_element].element,
+                                               element[cur_element].is_recording?
+                                               1:0);
+    }
+
+    if (element[cur_element].is_stereo)
+        vb_to_lr(element[cur_element].volume,
+                 element[cur_element].balance, &left, &right);
+    else
+        left = element[cur_element].volume;
+
+    long range = element[cur_element].max - element[cur_element].min;
+    dev_left_volume = element[cur_element].min + (long) (range * left);
+    element[cur_element].set_volume(element[cur_element].element,
+                                    SND_MIXER_SCHN_FRONT_LEFT,
+                                    dev_left_volume);
+    if (element[cur_element].is_stereo) {
+        dev_right_volume = element[cur_element].min + (long) (range * right);
+        element[cur_element].set_volume(element[cur_element].element,
+                                        SND_MIXER_SCHN_FRONT_RIGHT,
+                                        dev_right_volume);
+    }
+}
+
+bool mixer_alsa_is_changed(void)
+{
+    return get_mixer_state();
+}
+
+int mixer_alsa_get_channel_count(void)
+{
+    return n_elements;
+}
+
+int mixer_alsa_get_channel(void)
+{
+    return cur_element;
+}
+
+const char *mixer_alsa_get_channel_name(void)
+{
+    return element[cur_element].name;
+}
+
+const char *mixer_alsa_get_short_name(void)
+{
+    return element[cur_element].sname;
+}
+
+void mixer_alsa_set_channel(int element)
+{
+    assert((element >= 0) && (element < n_elements));
+
+    cur_element = element;
+    get_mixer_state();
+}
+
+void mixer_alsa_set_channel_rel(int delta_element)
+{
+    cur_element = (cur_element + delta_element) % n_elements;
+    if (cur_element < 0)
+	cur_element += n_elements;
+    get_mixer_state();
+}
+
+float mixer_alsa_get_volume(void)
+{
+    get_mixer_state();
+    return element[cur_element].volume;
+}
+
+void mixer_alsa_set_volume(float volume)
+{
+    assert((volume >= 0.0) && (volume <= 1.0));
+    element[cur_element].volume = volume;
+    set_mixer_state();
+}
+
+void mixer_alsa_set_volume_rel(float delta_volume)
+{
+    element[cur_element].volume += delta_volume;
+    element[cur_element].volume = CLAMP(element[cur_element].volume, 0.0, 1.0);
+    set_mixer_state();
+}
+
+float mixer_alsa_get_balance(void)
+{
+    get_mixer_state();
+    return element[cur_element].balance;
+}
+
+void mixer_alsa_set_balance(float balance)
+{
+    assert((balance >= -1.0) && (balance <= 1.0));
+    if (element[cur_element].is_stereo) {
+	element[cur_element].balance = balance;
+	set_mixer_state();
+    }
+}
+
+void mixer_alsa_set_balance_rel(float delta_balance)
+{
+    if (element[cur_element].is_stereo) {
+	element[cur_element].balance += delta_balance;
+	element[cur_element].balance =
+	    CLAMP(element[cur_element].balance, -1.0, 1.0);
+	set_mixer_state();
+    }
+}
+
+void mixer_alsa_toggle_mute(void)
+{
+    if (element[cur_element].has_playback_switch) {
+        element[cur_element].is_muted ^= 1;
+        set_mixer_state();
+    }
+}
+
+void mixer_alsa_toggle_rec(void)
+{
+    if (element[cur_element].has_capture_switch) {
+        element[cur_element].is_recording ^= 1;
+        set_mixer_state();
+    }
+}
+
+bool mixer_alsa_is_muted(void)
+{
+    return element[cur_element].is_muted;
+}
+
+bool mixer_alsa_is_stereo(void)
+{
+    return element[cur_element].is_stereo;
+}
+
+bool mixer_alsa_is_rec(void)
+{
+    return element[cur_element].is_recording;
+}
+
+bool mixer_alsa_can_rec(void)
+{
+    return element[cur_element].has_capture;
+}
+
+void mixer_alsa_tick(void)
+{
+    snd_mixer_handle_events(mixer);
+}
diff --git a/wmix/mixer-oss.c b/wmix/mixer-oss.c
index 3f0ca8f..0c0e03f 100644
--- a/wmix/mixer-oss.c
+++ b/wmix/mixer-oss.c
@@ -32,7 +32,7 @@
 
 #include "include/common.h"
 #include "include/misc.h"
-#include "include/mixer.h"
+#include "include/mixer-oss.h"
 
 #define WMVOLUME_CHANNEL_NAMES \
 	"Master volume", \
@@ -221,7 +221,43 @@ static void set_record_state(void)
     }
 }
 
-void mixer_init(const char *mixer_device, bool verbose, const char * exclude[])
+static bool is_exclude(const char *short_name, const char *exclude[])
+{
+    int count;
+    int len;
+
+    for (count = 0; exclude[count] != NULL; count++) {
+
+        /*
+         * Short names may be padded with spaces, because apparently there is a minimum
+         * length requirement of 6 characters for the name, and we do not want to
+         * include this padding in the match
+         */
+        len = strlen(short_name);
+        while (len > 0) {
+            if (short_name[len - 1] == ' ')
+                len--;
+            else
+                break;
+        }
+
+        if (strncmp(short_name, exclude[count], len) != 0)
+            continue;
+
+        if (exclude[count][len] != '\0')
+            continue;
+
+        /* Check the remaining in short name is only space */
+        while (short_name[len] == ' ')
+            len++;
+
+        if (short_name[len] == '\0')
+            return true;
+    }
+    return false;
+}
+
+void mixer_oss_init(const char *mixer_device, bool verbose, const char * exclude[])
 {
     int devmask, srcmask, recmask, stmask;
     struct mixer_info m_info;
@@ -288,32 +324,32 @@ void mixer_init(const char *mixer_device, bool verbose, const char * exclude[])
     get_mixer_state();
 }
 
-bool mixer_is_changed(void)
+bool mixer_oss_is_changed(void)
 {
     return get_mixer_state();
 }
 
-int mixer_get_channel_count(void)
+int mixer_oss_get_channel_count(void)
 {
     return n_channels;
 }
 
-int mixer_get_channel(void)
+int mixer_oss_get_channel(void)
 {
     return cur_channel;
 }
 
-const char *mixer_get_channel_name(void)
+const char *mixer_oss_get_channel_name(void)
 {
     return mixer[cur_channel].name;
 }
 
-const char *mixer_get_short_name(void)
+const char *mixer_oss_get_short_name(void)
 {
     return mixer[cur_channel].sname;
 }
 
-void mixer_set_channel(int channel)
+void mixer_oss_set_channel(int channel)
 {
     assert((channel >= 0) && (channel < n_channels));
 
@@ -321,7 +357,7 @@ void mixer_set_channel(int channel)
     get_record_state();
 }
 
-void mixer_set_channel_rel(int delta_channel)
+void mixer_oss_set_channel_rel(int delta_channel)
 {
     cur_channel = (cur_channel + delta_channel) % n_channels;
     if (cur_channel < 0)
@@ -329,13 +365,13 @@ void mixer_set_channel_rel(int delta_channel)
     get_record_state();
 }
 
-float mixer_get_volume(void)
+float mixer_oss_get_volume(void)
 {
     get_mixer_state();
     return mixer[cur_channel].volume;
 }
 
-void mixer_set_volume(float volume)
+void mixer_oss_set_volume(float volume)
 {
     assert((volume >= 0.0) && (volume <= 1.0));
 
@@ -343,20 +379,20 @@ void mixer_set_volume(float volume)
     set_mixer_state();
 }
 
-void mixer_set_volume_rel(float delta_volume)
+void mixer_oss_set_volume_rel(float delta_volume)
 {
     mixer[cur_channel].volume += delta_volume;
     mixer[cur_channel].volume = CLAMP(mixer[cur_channel].volume, 0.0, 1.0);
     set_mixer_state();
 }
 
-float mixer_get_balance(void)
+float mixer_oss_get_balance(void)
 {
     get_mixer_state();
     return mixer[cur_channel].balance;
 }
 
-void mixer_set_balance(float balance)
+void mixer_oss_set_balance(float balance)
 {
     assert((balance >= -1.0) && (balance <= 1.0));
 
@@ -366,7 +402,7 @@ void mixer_set_balance(float balance)
     }
 }
 
-void mixer_set_balance_rel(float delta_balance)
+void mixer_oss_set_balance_rel(float delta_balance)
 {
     if (mixer[cur_channel].is_stereo) {
 	mixer[cur_channel].balance += delta_balance;
@@ -376,14 +412,14 @@ void mixer_set_balance_rel(float delta_balance)
     }
 }
 
-void mixer_toggle_mute(void)
+void mixer_oss_toggle_mute(void)
 {
     mixer[cur_channel].is_muted = !mixer[cur_channel].is_muted;
 
     set_mixer_state();
 }
 
-void mixer_toggle_rec(void)
+void mixer_oss_toggle_rec(void)
 {
     if (mixer[cur_channel].can_record) {
 	mixer[cur_channel].is_recording = !mixer[cur_channel].is_recording;
@@ -392,60 +428,22 @@ void mixer_toggle_rec(void)
     }
 }
 
-bool mixer_is_muted(void)
+bool mixer_oss_is_muted(void)
 {
     return mixer[cur_channel].is_muted;
 }
 
-bool mixer_is_stereo(void)
+bool mixer_oss_is_stereo(void)
 {
     return mixer[cur_channel].is_stereo;
 }
 
-bool mixer_is_rec(void)
+bool mixer_oss_is_rec(void)
 {
     return mixer[cur_channel].is_recording;
 }
 
-bool mixer_can_rec(void)
+bool mixer_oss_can_rec(void)
 {
     return mixer[cur_channel].can_record;
 }
-
-bool is_exclude(const char *short_name, const char *exclude[])
-{
-	int count;
-	int len;
-
-	for (count = 0; count < SOUND_MIXER_NRDEVICES; count++) {
-		if (exclude[count] == NULL)
-			break;
-
-		/*
-		 * Short names may be padded with spaces, because apparently there is a minimum
-		 * length requirement of 6 characters for the name, and we do not want to
-		 * include this padding in the match
-		 */
-		len = strlen(short_name);
-		while (len > 0) {
-			if (short_name[len - 1] == ' ')
-				len--;
-			else
-				break;
-		}
-
-		if (strncmp(short_name, exclude[count], len) != 0)
-			continue;
-
-		if (exclude[count][len] != '\0')
-			continue;
-
-		/* Check the remaining in short name is only space */
-		while (short_name[len] == ' ')
-			len++;
-
-		if (short_name[len] == '\0')
-			return true;
-	}
-	return false;
-}
diff --git a/wmix/wmix.c b/wmix/wmix.c
index b2ff99f..13d1593 100644
--- a/wmix/wmix.c
+++ b/wmix/wmix.c
@@ -31,14 +31,14 @@
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 
-#include <sys/soundcard.h>
-
 #include "include/common.h"
 #include "include/mixer.h"
 #include "include/misc.h"
 #include "include/ui_x.h"
 #include "include/mmkeys.h"
 #include "include/config.h"
+#include "include/mixer-oss.h"
+#include "include/mixer-alsa.h"
 
 
 static Display *display;
@@ -58,6 +58,7 @@ static void button_press_event(XButtonEvent *event);
 static void button_release_event(XButtonEvent *event);
 static int  key_press_event(XKeyEvent *event);
 static void motion_event(XMotionEvent *event);
+static void choose_api(int api);
 
 
 int main(int argc, char **argv)
@@ -67,6 +68,7 @@ int main(int argc, char **argv)
     config_init();
     parse_cli_options(argc, argv);
     config_read();
+    choose_api(config.api);
 
     mixer_init(config.mixer_device, config.verbose, (const char **)config.exclude_channel);
     mixer_set_channel(0);
@@ -154,6 +156,8 @@ int main(int argc, char **argv)
 	    }
 	} else {
 	    usleep(100000);
+            if (mixer_tick)
+                mixer_tick();
 	    scroll_text(3, 4, 57, false);
 	    /* rescroll message after some delay */
 	    if (idle_loop++ > 256) {
@@ -196,6 +200,55 @@ static void signal_catch(int sig)
     }
 }
 
+static void choose_api(int api)
+{
+    if (api == 0) {
+        mixer_init = &mixer_alsa_init;
+        mixer_is_changed = &mixer_alsa_is_changed;
+        mixer_get_channel_count = mixer_alsa_get_channel_count;
+        mixer_get_channel = mixer_alsa_get_channel;
+        mixer_get_channel_name = mixer_alsa_get_channel_name;
+        mixer_get_short_name = mixer_alsa_get_short_name;
+        mixer_set_channel = mixer_alsa_set_channel;
+        mixer_set_channel_rel = mixer_alsa_set_channel_rel;
+        mixer_get_volume = mixer_alsa_get_volume;
+        mixer_set_volume = mixer_alsa_set_volume;
+        mixer_set_volume_rel = mixer_alsa_set_volume_rel;
+        mixer_get_balance = mixer_alsa_get_balance;
+        mixer_set_balance = mixer_alsa_set_balance;
+        mixer_set_balance_rel = mixer_alsa_set_balance_rel;
+        mixer_toggle_mute = mixer_alsa_toggle_mute;
+        mixer_toggle_rec = mixer_alsa_toggle_rec;
+        mixer_is_muted = mixer_alsa_is_muted;
+        mixer_is_stereo = mixer_alsa_is_stereo;
+        mixer_is_rec = mixer_alsa_is_rec;
+        mixer_can_rec = mixer_alsa_can_rec;
+        mixer_tick = mixer_alsa_tick;
+    } else if (api == 1) {
+        mixer_init = &mixer_oss_init;
+        mixer_is_changed = &mixer_oss_is_changed;
+        mixer_get_channel_count = mixer_oss_get_channel_count;
+        mixer_get_channel = mixer_oss_get_channel;
+        mixer_get_channel_name = mixer_oss_get_channel_name;
+        mixer_get_short_name = mixer_oss_get_short_name;
+        mixer_set_channel = mixer_oss_set_channel;
+        mixer_set_channel_rel = mixer_oss_set_channel_rel;
+        mixer_get_volume = mixer_oss_get_volume;
+        mixer_set_volume = mixer_oss_set_volume;
+        mixer_set_volume_rel = mixer_oss_set_volume_rel;
+        mixer_get_balance = mixer_oss_get_balance;
+        mixer_set_balance = mixer_oss_set_balance;
+        mixer_set_balance_rel = mixer_oss_set_balance_rel;
+        mixer_toggle_mute = mixer_oss_toggle_mute;
+        mixer_toggle_rec = mixer_oss_toggle_rec;
+        mixer_is_muted = mixer_oss_is_muted;
+        mixer_is_stereo = mixer_oss_is_stereo;
+        mixer_is_rec = mixer_oss_is_rec;
+        mixer_can_rec = mixer_oss_can_rec;
+        mixer_tick = NULL;
+    }
+}
+
 static void button_press_event(XButtonEvent *event)
 {
     double button_press_time = get_current_time();
-- 
2.1.4


-- 
To unsubscribe, send mail to [email protected].
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.