From 11c29cccb3409fb4d0fea5171a56230253583864 Mon Sep 17 00:00:00 2001
From: Viliam Mateicka <viliam.mateicka@gmail.com>
Date: Sat, 10 Jan 2009 17:53:33 +0100
Subject: [PATCH] Introducing mixer api

This patch tryes to introduce pluggable mixer (struct mixer_plugin) along with some basic infrastructure (mixer_* functions). Instance of mixer (struct mixer) is used in
alsa and oss output plugin
---
 src/Makefile.am          |  1 +
 src/mixer/alsa_mixer.c   | 60 ++++++++++++++++++++++--------------
 src/mixer/oss_mixer.c    | 65 +++++++++++++++++++++------------------
 src/mixer_api.c          | 45 +++++++++++++++++++++++++++
 src/mixer_api.h          | 66 ++++++++++++++++++++++++++++------------
 src/output/alsa_plugin.c | 15 ++++-----
 src/output/oss_plugin.c  | 16 +++++-----
 7 files changed, 182 insertions(+), 86 deletions(-)
 create mode 100644 src/mixer_api.c

diff --git a/src/Makefile.am b/src/Makefile.am
index e1977494a..65bfd45e4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -128,6 +128,7 @@ mpd_SOURCES = \
 	main.c \
 	event_pipe.c \
 	daemon.c \
+	mixer_api.c \
 	normalize.c \
 	compress.c \
 	pipe.c \
diff --git a/src/mixer/alsa_mixer.c b/src/mixer/alsa_mixer.c
index c1331993b..6930fa8a0 100644
--- a/src/mixer/alsa_mixer.c
+++ b/src/mixer/alsa_mixer.c
@@ -18,7 +18,7 @@ struct alsa_mixer {
 	int volume_set;
 };
 
-struct alsa_mixer *
+static struct mixer_data *
 alsa_mixer_init(void)
 {
 	struct alsa_mixer *am = g_malloc(sizeof(struct alsa_mixer));
@@ -29,18 +29,20 @@ alsa_mixer_init(void)
 	am->volume_min = 0;
 	am->volume_max = 0;
 	am->volume_set = -1;
-	return am;
+	return (struct mixer_data *)am;
 }
 
-void
-alsa_mixer_finish(struct alsa_mixer *am)
+static void
+alsa_mixer_finish(struct mixer_data *data)
 {
+	struct alsa_mixer *am = (struct alsa_mixer *)data;
 	g_free(am);
 }
 
-void
-alsa_mixer_configure(struct alsa_mixer *am, ConfigParam *param)
+static void
+alsa_mixer_configure(struct mixer_data *data, ConfigParam *param)
 {
+	struct alsa_mixer *am = (struct alsa_mixer *)data;
 	BlockParam *bp;
 
 	if ((bp = getBlockParam(param, "mixer_device")))
@@ -49,16 +51,18 @@ alsa_mixer_configure(struct alsa_mixer *am, ConfigParam *param)
 		am->control = bp->value;
 }
 
-void
-alsa_mixer_close(struct alsa_mixer *am)
+static void
+alsa_mixer_close(struct mixer_data *data)
 {
+	struct alsa_mixer *am = (struct alsa_mixer *)data;
 	if (am->handle) snd_mixer_close(am->handle);
 	am->handle = NULL;
 }
 
-bool
-alsa_mixer_open(struct alsa_mixer *am)
+static bool
+alsa_mixer_open(struct mixer_data *data)
 {
+	struct alsa_mixer *am = (struct alsa_mixer *)data;
 	int err;
 	snd_mixer_elem_t *elem;
 	const char *control_name = VOLUME_MIXER_ALSA_CONTROL_DEFAULT;
@@ -77,7 +81,7 @@ alsa_mixer_open(struct alsa_mixer *am)
 	if ((err = snd_mixer_attach(am->handle, device)) < 0) {
 		g_warning("problems attaching alsa mixer: %s\n",
 			snd_strerror(err));
-		alsa_mixer_close(am);
+		alsa_mixer_close(data);
 		return false;
 	}
 
@@ -85,14 +89,14 @@ alsa_mixer_open(struct alsa_mixer *am)
 		    NULL)) < 0) {
 		g_warning("problems snd_mixer_selem_register'ing: %s\n",
 			snd_strerror(err));
-		alsa_mixer_close(am);
+		alsa_mixer_close(data);
 		return false;
 	}
 
 	if ((err = snd_mixer_load(am->handle)) < 0) {
 		g_warning("problems snd_mixer_selem_register'ing: %s\n",
 			snd_strerror(err));
-		alsa_mixer_close(am);
+		alsa_mixer_close(data);
 		return false;
 	}
 
@@ -122,18 +126,19 @@ alsa_mixer_open(struct alsa_mixer *am)
 
 	g_warning("can't find alsa mixer control \"%s\"\n", control_name);
 
-	alsa_mixer_close(am);
+	alsa_mixer_close(data);
 	return false;
 }
 
-bool
-alsa_mixer_control(struct alsa_mixer *am, int cmd, void *arg)
+static bool
+alsa_mixer_control(struct mixer_data *data, int cmd, void *arg)
 {
+	struct alsa_mixer *am = (struct alsa_mixer *)data;
 	switch (cmd) {
 	case AC_MIXER_CONFIGURE:
-		alsa_mixer_configure(am, (ConfigParam *)arg);
+		alsa_mixer_configure(data, (ConfigParam *)arg);
 		if (am->handle)
-			alsa_mixer_close(am);
+			alsa_mixer_close(data);
 		return true;
 	case AC_MIXER_GETVOL:
 	{
@@ -141,20 +146,20 @@ alsa_mixer_control(struct alsa_mixer *am, int cmd, void *arg)
 		int ret, *volume = arg;
 		long level;
 
-		if (!am->handle && !alsa_mixer_open(am)) {
+		if (!am->handle && !alsa_mixer_open(data)) {
 			return false;
 		}
 		if ((err = snd_mixer_handle_events(am->handle)) < 0) {
 			g_warning("problems getting alsa volume: %s (snd_mixer_%s)\n",
 				snd_strerror(err), "handle_events");
-			alsa_mixer_close(am);
+			alsa_mixer_close(data);
 			return false;
 		}
 		if ((err = snd_mixer_selem_get_playback_volume(am->elem,
 			       SND_MIXER_SCHN_FRONT_LEFT, &level)) < 0) {
 			g_warning("problems getting alsa volume: %s (snd_mixer_%s)\n",
 				snd_strerror(err), "selem_get_playback_volume");
-			alsa_mixer_close(am);
+			alsa_mixer_close(data);
 			return false;
 		}
 		ret = ((am->volume_set / 100.0) * (am->volume_max - am->volume_min)
@@ -175,7 +180,7 @@ alsa_mixer_control(struct alsa_mixer *am, int cmd, void *arg)
 		int *volume = arg;
 		int err;
 
-		if (!am->handle && !alsa_mixer_open(am)) {
+		if (!am->handle && !alsa_mixer_open(data)) {
 			return false;
 		}
 		vol = *volume;
@@ -193,7 +198,7 @@ alsa_mixer_control(struct alsa_mixer *am, int cmd, void *arg)
 								level)) < 0) {
 			g_warning("problems setting alsa volume: %s\n",
 				snd_strerror(err));
-			alsa_mixer_close(am);
+			alsa_mixer_close(data);
 			return false;
 		}
 		return true;
@@ -204,3 +209,12 @@ alsa_mixer_control(struct alsa_mixer *am, int cmd, void *arg)
 	}
 	return false;
 }
+
+struct mixer_plugin alsa_mixer = {
+	.init = alsa_mixer_init,
+	.finish = alsa_mixer_finish,
+	.configure = alsa_mixer_configure,
+	.open = alsa_mixer_open,
+	.control = alsa_mixer_control,
+	.close = alsa_mixer_close
+};
diff --git a/src/mixer/oss_mixer.c b/src/mixer/oss_mixer.c
index b4b2ed7c6..db8431ab9 100644
--- a/src/mixer/oss_mixer.c
+++ b/src/mixer/oss_mixer.c
@@ -25,14 +25,7 @@ struct oss_mixer {
 	int volume_control;
 };
 
-struct oss_mixer *oss_mixer_init(void);
-void oss_mixer_finish(struct oss_mixer *am);
-void oss_mixer_configure(struct oss_mixer *am, ConfigParam *param);
-bool oss_mixer_open(struct oss_mixer *am);
-bool oss_mixer_control(struct oss_mixer *am, int cmd, void *arg);
-void oss_mixer_close(struct oss_mixer *am);
-
-struct oss_mixer *
+static struct mixer_data *
 oss_mixer_init(void)
 {
 	struct oss_mixer *om = g_malloc(sizeof(struct oss_mixer));
@@ -40,18 +33,20 @@ oss_mixer_init(void)
 	om->control = NULL;
 	om->device_fd = -1;
 	om->volume_control = SOUND_MIXER_PCM;
-	return om;
+	return (struct mixer_data *)om;
 }
 
-void
-oss_mixer_finish(struct oss_mixer *om)
+static void
+oss_mixer_finish(struct mixer_data *data)
 {
+	struct oss_mixer *om = (struct oss_mixer *) data;
 	g_free(om);
 }
 
-void
-oss_mixer_configure(struct oss_mixer *om, ConfigParam *param)
+static void
+oss_mixer_configure(struct mixer_data *data, ConfigParam *param)
 {
+	struct oss_mixer *om = (struct oss_mixer *) data;
 	BlockParam *bp;
 	bp = getBlockParam(param, "mixer_device");
 	if (bp) {
@@ -63,9 +58,10 @@ oss_mixer_configure(struct oss_mixer *om, ConfigParam *param)
 	}
 }
 
-void
-oss_mixer_close(struct oss_mixer *om)
+static void
+oss_mixer_close(struct mixer_data *data)
 {
+	struct oss_mixer *om = (struct oss_mixer *) data;
 	if (om->device_fd != -1)
 		while (close(om->device_fd) && errno == EINTR) ;
 	om->device_fd = -1;
@@ -86,9 +82,10 @@ oss_find_mixer(const char *name)
 	return -1;
 }
 
-bool
-oss_mixer_open(struct oss_mixer *om)
+static bool
+oss_mixer_open(struct mixer_data *data)
 {
+	struct oss_mixer *om = (struct oss_mixer *) data;
 	const char *device = VOLUME_MIXER_OSS_DEFAULT;
 
 	if (om->device) {
@@ -106,7 +103,7 @@ oss_mixer_open(struct oss_mixer *om)
 
 		if (ioctl(om->device_fd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) {
 			g_warning("errors getting read_devmask for oss mixer\n");
-			oss_mixer_close(om);
+			oss_mixer_close(data);
 			return false;
 		}
 		i = oss_find_mixer(om->control);
@@ -114,12 +111,12 @@ oss_mixer_open(struct oss_mixer *om)
 		if (i < 0) {
 			g_warning("mixer control \"%s\" not found\n",
 				om->control);
-			oss_mixer_close(om);
+			oss_mixer_close(data);
 			return false;
 		} else if (!((1 << i) & devmask)) {
 			g_warning("mixer control \"%s\" not usable\n",
 				om->control);
-			oss_mixer_close(om);
+			oss_mixer_close(data);
 			return false;
 		}
 		om->volume_control = i;
@@ -127,14 +124,15 @@ oss_mixer_open(struct oss_mixer *om)
 	return true;
 }
 
-bool
-oss_mixer_control(struct oss_mixer *om, int cmd, void *arg)
+static bool
+oss_mixer_control(struct mixer_data *data, int cmd, void *arg)
 {
+	struct oss_mixer *om = (struct oss_mixer *) data;
 	switch (cmd) {
 	case AC_MIXER_CONFIGURE:
-		oss_mixer_configure(om, (ConfigParam *)arg);
-		//if (om->device_fd >= 0)
-			oss_mixer_close(om);
+		oss_mixer_configure(data, (ConfigParam *)arg);
+		if (om->device_fd >= 0)
+			oss_mixer_close(data);
 		return true;
 		break;
 	case AC_MIXER_GETVOL:
@@ -142,12 +140,12 @@ oss_mixer_control(struct oss_mixer *om, int cmd, void *arg)
 		int left, right, level;
 		int *ret;
 
-		if (om->device_fd < 0 && !oss_mixer_open(om)) {
+		if (om->device_fd < 0 && !oss_mixer_open(data)) {
 			return false;
 		}
 
 		if (ioctl(om->device_fd, MIXER_READ(om->volume_control), &level) < 0) {
-			oss_mixer_close(om);
+			oss_mixer_close(data);
 			g_warning("unable to read oss volume\n");
 			return false;
 		}
@@ -169,7 +167,7 @@ oss_mixer_control(struct oss_mixer *om, int cmd, void *arg)
 		int level;
 		int *value = arg;
 
-		if (om->device_fd < 0 && !oss_mixer_open(om)) {
+		if (om->device_fd < 0 && !oss_mixer_open(data)) {
 			return false;
 		}
 
@@ -184,7 +182,7 @@ oss_mixer_control(struct oss_mixer *om, int cmd, void *arg)
 
 		if (ioctl(om->device_fd, MIXER_WRITE(om->volume_control), &level) < 0) {
 			g_warning("unable to set oss volume\n");
-			oss_mixer_close(om);
+			oss_mixer_close(data);
 			return false;
 		}
 		return true;
@@ -195,3 +193,12 @@ oss_mixer_control(struct oss_mixer *om, int cmd, void *arg)
 	}
 	return false;
 }
+
+struct mixer_plugin oss_mixer = {
+	.init = oss_mixer_init,
+	.finish = oss_mixer_finish,
+	.configure = oss_mixer_configure,
+	.open = oss_mixer_open,
+	.control = oss_mixer_control,
+	.close = oss_mixer_close
+};
diff --git a/src/mixer_api.c b/src/mixer_api.c
new file mode 100644
index 000000000..f77f764cf
--- /dev/null
+++ b/src/mixer_api.c
@@ -0,0 +1,45 @@
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "mixer_api.h"
+
+void mixer_init(struct mixer *mixer, struct mixer_plugin *plugin)
+{
+	assert(plugin != NULL);
+	assert(mixer != NULL);
+	mixer->plugin = plugin;
+	mixer->data = mixer->plugin->init();
+}
+
+void mixer_finish(struct mixer *mixer)
+{
+	assert(mixer != NULL && mixer->plugin != NULL);
+	mixer->plugin->finish(mixer->data);
+	mixer->data = NULL;
+	mixer->plugin = NULL;
+}
+
+void mixer_configure(struct mixer *mixer, ConfigParam *param)
+{
+	assert(mixer != NULL && mixer->plugin != NULL);
+	mixer->plugin->configure(mixer->data, param);
+}
+
+bool mixer_open(struct mixer *mixer)
+{
+	assert(mixer != NULL && mixer->plugin != NULL);
+	return mixer->plugin->open(mixer->data);
+}
+
+bool mixer_control(struct mixer *mixer, int cmd, void *arg)
+{
+	assert(mixer != NULL && mixer->plugin != NULL);
+	return mixer->plugin->control(mixer->data, cmd, arg);
+}
+
+void mixer_close(struct mixer *mixer)
+{
+	assert(mixer != NULL && mixer->plugin != NULL);
+	mixer->plugin->close(mixer->data);
+}
diff --git a/src/mixer_api.h b/src/mixer_api.h
index 43dc3299d..67d08427c 100644
--- a/src/mixer_api.h
+++ b/src/mixer_api.h
@@ -4,30 +4,58 @@
 
 #include "conf.h"
 
-/**
- * alsa mixer
+/*
+ * list of currently implemented mixers
  */
 
-struct alsa_mixer;
+extern struct mixer_plugin alsa_mixer;
+extern struct mixer_plugin oss_mixer;
 
-struct alsa_mixer *alsa_mixer_init(void);
-void alsa_mixer_finish(struct alsa_mixer *am);
-void alsa_mixer_configure(struct alsa_mixer *am, ConfigParam *param);
-bool alsa_mixer_open(struct alsa_mixer *am);
-bool alsa_mixer_control(struct alsa_mixer *am, int cmd, void *arg);
-void alsa_mixer_close(struct alsa_mixer *am);
+struct mixer_data;
 
-/**
- * oss mixer
- */
+struct mixer_plugin {
 
-struct oss_mixer;
+        /**
+         * Allocate and initialize mixer data
+	 */
+        struct mixer_data *(*init)(void);
 
-struct oss_mixer *oss_mixer_init(void);
-void oss_mixer_finish(struct oss_mixer *am);
-void oss_mixer_configure(struct oss_mixer *am, ConfigParam *param);
-bool oss_mixer_open(struct oss_mixer *am);
-bool oss_mixer_control(struct oss_mixer *am, int cmd, void *arg);
-void oss_mixer_close(struct oss_mixer *am);
+        /**
+	 * Finish and free mixer data
+         */
+        void (*finish)(struct mixer_data *data);
+
+        /**
+	 * Setup and configure mixer
+         */
+	void (*configure)(struct mixer_data *data, ConfigParam *param);
+
+        /**
+    	 * Open mixer device
+	 */
+	bool (*open)(struct mixer_data *data);
+
+        /**
+	 * Control mixer device.
+         */
+	bool (*control)(struct mixer_data *data, int cmd, void *arg);
+
+        /**
+    	 * Close mixer device
+	 */
+	void (*close)(struct mixer_data *data);
+};
+
+struct mixer {
+	struct mixer_plugin *plugin;
+	struct mixer_data *data;
+};
+
+void mixer_init(struct mixer *mixer, struct mixer_plugin *plugin);
+void mixer_finish(struct mixer *mixer);
+void mixer_configure(struct mixer *mixer, ConfigParam *param);
+bool mixer_open(struct mixer *mixer);
+bool mixer_control(struct mixer *mixer, int cmd, void *arg);
+void mixer_close(struct mixer *mixer);
 
 #endif
diff --git a/src/output/alsa_plugin.c b/src/output/alsa_plugin.c
index fc5c56c21..58f549800 100644
--- a/src/output/alsa_plugin.c
+++ b/src/output/alsa_plugin.c
@@ -52,7 +52,7 @@ typedef struct _AlsaData {
 	int sampleSize;
 	int useMmap;
 
-	struct alsa_mixer *mixer;
+	struct mixer mixer;
 
 } AlsaData;
 
@@ -74,7 +74,8 @@ static AlsaData *newAlsaData(void)
 	ret->buffer_time = MPD_ALSA_BUFFER_TIME_US;
 	ret->period_time = 0;
 
-	ret->mixer = alsa_mixer_init();
+	//use alsa mixer by default
+	mixer_init(&ret->mixer, &alsa_mixer);
 
 	return ret;
 }
@@ -82,7 +83,7 @@ static AlsaData *newAlsaData(void)
 static void freeAlsaData(AlsaData * ad)
 {
 	g_free(ad->device);
-	alsa_mixer_finish(ad->mixer);
+	mixer_finish(&ad->mixer);
 	free(ad);
 }
 
@@ -133,7 +134,7 @@ static void *alsa_initDriver(G_GNUC_UNUSED struct audio_output *ao,
 
 	if (param) {
 		alsa_configure(ad, param);
-		alsa_mixer_configure(ad->mixer, param);
+		mixer_configure(&ad->mixer, param);
 	}
 
 	return ad;
@@ -189,7 +190,7 @@ static bool alsa_openDevice(void *data, struct audio_format *audioFormat)
 	unsigned int period_time, period_time_ro;
 	unsigned int buffer_time;
 
-	alsa_mixer_open(ad->mixer);
+	mixer_open(&ad->mixer);
 
 	if ((bitformat = get_bitformat(audioFormat)) == SND_PCM_FORMAT_UNKNOWN)
 		g_warning("ALSA device \"%s\" doesn't support %u bit audio\n",
@@ -413,7 +414,7 @@ static void alsa_closeDevice(void *data)
 		snd_pcm_close(ad->pcmHandle);
 		ad->pcmHandle = NULL;
 	}
-	alsa_mixer_close(ad->mixer);
+	mixer_close(&ad->mixer);
 }
 
 static bool
@@ -451,7 +452,7 @@ static bool
 alsa_control(void *data, int cmd, void *arg)
 {
 	AlsaData *ad = data;
-	return alsa_mixer_control(ad->mixer, cmd, arg);
+	return mixer_control(&ad->mixer, cmd, arg);
 }
 
 const struct audio_output_plugin alsaPlugin = {
diff --git a/src/output/oss_plugin.c b/src/output/oss_plugin.c
index f46377254..175aace1a 100644
--- a/src/output/oss_plugin.c
+++ b/src/output/oss_plugin.c
@@ -55,7 +55,7 @@ typedef struct _OssData {
 	int numSupported[3];
 	int *unsupported[3];
 	int numUnsupported[3];
-	struct oss_mixer *mixer;
+	struct mixer mixer;
 } OssData;
 
 enum oss_support {
@@ -276,7 +276,7 @@ static OssData *newOssData(void)
 	supportParam(ret, SNDCTL_DSP_CHANNELS, 2);
 	supportParam(ret, SNDCTL_DSP_SAMPLESIZE, 16);
 
-	ret->mixer = oss_mixer_init();
+	mixer_init( &ret->mixer, &oss_mixer);
 
 	return ret;
 }
@@ -290,7 +290,7 @@ static void freeOssData(OssData * od)
 	g_free(od->unsupported[OSS_CHANNELS]);
 	g_free(od->unsupported[OSS_BITS]);
 
-	oss_mixer_finish(od->mixer);
+	mixer_finish(&od->mixer);
 
 	free(od);
 }
@@ -355,7 +355,7 @@ static void *oss_open_default(ConfigParam *param)
 		if (ret[i] == 0) {
 			OssData *od = newOssData();
 			od->device = default_devices[i];
-			oss_mixer_configure(od->mixer, param);
+			mixer_configure(&od->mixer, param);
 			return od;
 		}
 	}
@@ -396,7 +396,7 @@ static void *oss_initDriver(G_GNUC_UNUSED struct audio_output *audioOutput,
 		if (bp) {
 			OssData *od = newOssData();
 			od->device = bp->value;
-			oss_mixer_configure(od->mixer, param);
+			mixer_configure(&od->mixer, param);
 			return od;
 		}
 	}
@@ -522,7 +522,7 @@ oss_openDevice(void *data, struct audio_format *audioFormat)
 		od->audio_format.bits, od->audio_format.channels,
 		od->audio_format.sample_rate);
 
-	oss_mixer_open(od->mixer);
+	mixer_open(&od->mixer);
 
 	return ret;
 }
@@ -532,7 +532,7 @@ static void oss_closeDevice(void *data)
 	OssData *od = data;
 
 	oss_close(od);
-	oss_mixer_close(od->mixer);
+	mixer_close(&od->mixer);
 }
 
 static void oss_dropBufferedAudio(void *data)
@@ -575,7 +575,7 @@ static bool
 oss_control(void *data, int cmd, void *arg)
 {
 	OssData *od = data;
-	return oss_mixer_control(od->mixer, cmd, arg);
+	return mixer_control(&od->mixer, cmd, arg);
 }
 
 const struct audio_output_plugin ossPlugin = {