Index: plugins/alsavs/Makefile
===================================================================
--- plugins/alsavs/Makefile	(revision 5196)
+++ plugins/alsavs/Makefile	(revision 5263)
@@ -1,7 +1,10 @@
-BASENAME=libasound_module_pcm_roar
-SLIB=$(BASENAME)$(SHARED_SUFFIX)
-TARGET=$(SLIB)
+BASENAME0=libasound_module_pcm_roar
+BASENAME1=libasound_module_ctl_roar
+SLIB0=$(BASENAME0)$(SHARED_SUFFIX)
+SLIB1=$(BASENAME1)$(SHARED_SUFFIX)
+TARGET=$(SLIB0) $(SLIB1)
 INSTALL_DIR=$(PREFIX_LIB)/alsa-lib/
-OBJS=pcm_roar.o roar.o
+OBJS0=pcm_roar.o roar.o
+OBJS1=ctl_roar.o roar.o
 
 include ../../Makefile.conf
@@ -25,6 +28,9 @@
 semi-install: ${TARGET}
 	mkdir -p ${DESTDIR}${INSTALL_DIR}
-	ln -fs `pwd`/$(SLIB) ${DESTDIR}${INSTALL_DIR}
+	ln -fs `pwd`/$(SLIB0) ${DESTDIR}${INSTALL_DIR}
+	ln -fs `pwd`/$(SLIB1) ${DESTDIR}${INSTALL_DIR}
 
-$(SLIB): $(OBJS)
-	$(CC) $(LDFLAGS) -Wl,-soname -Wl,$(SLIB) -o $(SLIB) $(OBJS) $(LIBS)
+$(SLIB0): $(OBJS0)
+	$(CC) $(LDFLAGS) -Wl,-soname -Wl,$(SLIB0) -o $(SLIB0) $(OBJS0) $(LIBS)
+$(SLIB1): $(OBJS1)
+	$(CC) $(LDFLAGS) -Wl,-soname -Wl,$(SLIB1) -o $(SLIB1) $(OBJS1) $(LIBS)
Index: plugins/alsavs/ctl_roar.c
===================================================================
--- plugins/alsavs/ctl_roar.c	(revision 5263)
+++ plugins/alsavs/ctl_roar.c	(revision 5263)
@@ -0,0 +1,293 @@
+//ctl_roar.c:
+
+/*
+ *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2011
+ *
+ *  This file is part of libroar a part of RoarAudio,
+ *  a cross-platform sound system for both, home and professional use.
+ *  See README for details.
+ *
+ *  This file is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 3
+ *  as published by the Free Software Foundation.
+ *
+ *  libroar 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 software; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ *  Boston, MA 02110-1301, USA.
+ *
+ *  NOTE for everyone want's to change something and send patches:
+ *  read README and HACKING! There a addition information on
+ *  the license of this document you need to read before you send
+ *  any patches.
+ *
+ *  NOTE for uses of non-GPL (LGPL,...) software using libesd, libartsc
+ *  or libpulse*:
+ *  The libs libroaresd, libroararts and libroarpulse link this lib
+ *  and are therefore GPL. Because of this it may be illigal to use
+ *  them with any software that uses libesd, libartsc or libpulse*.
+ */
+
+//#define DEBUG
+#include "roar.h"
+#include <string.h>
+
+static void roar_plugin_close(snd_ctl_ext_t * ext) {
+ struct roar_alsa_ctl * self = ext->private_data;
+
+ roar_disconnect(&(self->roar.con));
+ free(self);
+}
+
+static int roar_plugin_elem_count(snd_ctl_ext_t *ext) {
+ struct roar_alsa_ctl * self = ext->private_data;
+ int ret = 0;
+
+ for (; self->streams[ret].id != -1; ret++);
+
+ return ret;
+}
+
+static int roar_plugin_elem_list(snd_ctl_ext_t *ext, unsigned int offset, snd_ctl_elem_id_t *id) {
+ struct roar_alsa_ctl * self = ext->private_data;
+
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+ snd_ctl_elem_id_set_name(id, self->streams[offset].name);
+
+ return 0; 
+}
+
+static snd_ctl_ext_key_t roar_plugin_find_elem(snd_ctl_ext_t *ext,
+                                               const snd_ctl_elem_id_t *id) {
+ struct roar_alsa_ctl * self = ext->private_data;
+ int numid = snd_ctl_elem_id_get_numid(id);
+
+ return self->streams[numid].id;
+}
+
+static int roar_plugin_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
+                                     int *type, unsigned int *acc, unsigned int *count) {
+ struct roar_alsa_ctl * self = ext->private_data;
+ int i;
+
+ *type = SND_CTL_ELEM_TYPE_INTEGER;
+ *acc = SND_CTL_EXT_ACCESS_READWRITE;
+
+ for (i = 0; i < MAX_STREAMS && self->streams[i].id != (int)key; i++)
+
+ if ( i == MAX_STREAMS )
+  return -EIO;
+
+ *count = self->streams[i].channels;
+
+ return 0;
+}
+
+static int roar_plugin_get_integer_info(snd_ctl_ext_t *ext ATTRIBUTE_UNUSED,
+                                        snd_ctl_ext_key_t key ATTRIBUTE_UNUSED,
+                                        long *imin, long *imax, long *istep) {
+ *istep = 0;
+ *imin  = 0;
+ *imax  = 65535;
+ return 0;
+}
+
+static int roar_plugin_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) {
+ struct roar_alsa_ctl * self = ext->private_data;
+ struct roar_mixer_settings mixer;
+ int channels;
+ int i;
+
+ for (i = 0; i < MAX_STREAMS && self->streams[i].id != (int)key; i++)
+
+ if ( i == MAX_STREAMS )
+  return -EIO;
+
+ roar_err_clear_all();
+ if ( roar_get_vol(&(self->roar.con), key, &mixer, &channels) == -1 ) {
+  roar_err_update();
+  return -errno;
+ }
+
+ if ( channels != (int)self->streams[i].channels )
+  return -EIO;
+
+ for (i = 0; i < channels; i++)
+  value[i] = ((unsigned long int)mixer.mixer[i] * 65535LU) / mixer.scale;
+
+ return 0;
+}
+
+static int roar_plugin_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) {
+ struct roar_alsa_ctl * self = ext->private_data;
+ struct roar_mixer_settings mixer;
+ int sid;
+ int i;
+
+ for (sid = 0; i < MAX_STREAMS && self->streams[sid].id != (int)key; sid++)
+
+ if ( sid == MAX_STREAMS )
+  return -EIO;
+
+ mixer.scale = 65535;
+ mixer.rpg_mul = 1;
+ mixer.rpg_div = 1;
+
+ for (i = 0; i < (int)self->streams[i].channels; i++)
+  mixer.mixer[i] = value[i];
+
+ roar_err_clear_all();
+ if ( roar_set_vol(&(self->roar.con), key, &mixer, self->streams[sid].channels, ROAR_SET_VOL_ALL) == -1 ) {
+  roar_err_update();
+  return -errno;
+ }
+
+ return 0;
+}
+
+static int roar_plugin_read_event(snd_ctl_ext_t *ext ATTRIBUTE_UNUSED,
+                                  snd_ctl_elem_id_t *id ATTRIBUTE_UNUSED,
+                                  unsigned int *event_mask ATTRIBUTE_UNUSED) {
+ return -EAGAIN;
+}
+
+static void roar_plugin__handle_stream(struct roar_alsa_ctl * self, int id) {
+ struct roar_mixerstream * cs = NULL;
+ struct roar_stream s;
+ char name[1024];
+ int i;
+
+ if ( roar_get_stream(&(self->roar.con), &s, id) == -1 )
+  return;
+
+ switch (s.dir) {
+  case ROAR_DIR_OUTPUT:
+  case ROAR_DIR_MIXING:
+  case ROAR_DIR_BRIDGE:
+    if ( roar_stream_get_name(&(self->roar.con), &s, name, sizeof(name)) != 0 )
+     snprintf(name, sizeof(name), "Stream %i", id);
+   break;
+  default:
+    return;
+   break;
+ }
+
+ for (i = 0; cs == NULL && i < (int)(sizeof(self->streams)/sizeof(*self->streams)); i++)
+  if (self->streams[i].id == -1 )
+   cs = &(self->streams[i]);
+
+ if ( cs == NULL )
+  return;
+
+ cs->id = id;
+ cs->channels = s.info.channels;
+ strncpy(cs->name, name, sizeof(cs->name) - 1); 
+}
+
+static int roar_plugin__find_streams(struct roar_alsa_ctl * self) {
+ int id[ROAR_STREAMS_MAX];
+ int num;
+ int i;
+
+ for (i = 0; i < (int)(sizeof(self->streams)/sizeof(*self->streams)); i++)
+  self->streams[i].id = -1;
+
+ roar_err_clear_all();
+
+ if ( (num = roar_list_streams(&(self->roar.con), id, ROAR_STREAMS_MAX)) == -1 ) {
+  roar_err_update();
+  return -errno;
+ }
+
+ for (i = 0; i < num; i++)
+  roar_plugin__handle_stream(self, i);
+
+ return 0;
+}
+
+static snd_ctl_ext_callback_t roar_ext_callback = {
+ .close            = roar_plugin_close,
+ .elem_count       = roar_plugin_elem_count,
+ .elem_list        = roar_plugin_elem_list,
+ .find_elem        = roar_plugin_find_elem,
+ .get_attribute    = roar_plugin_get_attribute,
+ .get_integer_info = roar_plugin_get_integer_info,
+ .read_integer     = roar_plugin_read_integer,
+ .write_integer    = roar_plugin_write_integer,
+ .read_event       = roar_plugin_read_event
+};
+
+SND_CTL_PLUGIN_DEFINE_FUNC(roar) {
+ struct roar_alsa_ctl * self;
+ snd_config_iterator_t i, next;
+ snd_config_t * n;
+ const char   * para;
+ const char   * server = NULL;
+ int error;
+
+ const char   * ext_id   = "ROAR";
+ const char   * ext_name = "RoarAudio";
+
+ (void)root;
+
+ snd_config_for_each(i, next, conf) {
+  n = snd_config_iterator_entry(i);
+  if ( snd_config_get_id(n, &para) < 0 )
+   continue;
+
+  if ( !strcmp(para, "type") || !strcmp(para, "comment") || !strcmp(para, "hint") )
+   continue;
+
+  if ( !strcmp(para, "server") ) {
+   if (snd_config_get_string(n, &server) < 0) {
+    return -EINVAL;
+   }
+  } else {
+   return -EINVAL;
+  }
+ }
+
+ if ( (self = malloc(sizeof(struct roar_alsa_ctl))) == NULL )
+  return -errno;
+
+ memset(self, 0, sizeof(struct roar_alsa_ctl));
+
+ errno = ENOSYS;
+ if ( roar_simple_connect(&(self->roar.con), server, "ALSA Plugin") == -1 ) {
+  free(self);
+  return -errno;
+ }
+
+ if ( (error = roar_plugin__find_streams(self)) < 0 ) {
+  roar_disconnect(&(self->roar.con));
+  free(self);
+  return error;
+ }
+
+ self->ext.version = SND_CTL_EXT_VERSION;
+ self->ext.card_idx = 0; /* FIXME */
+ strncpy(self->ext.id, ext_id, sizeof(self->ext.id) - 1);
+ strcpy(self->ext.driver, "OSS-Emulation");
+ strncpy(self->ext.name, ext_name, sizeof(self->ext.name) - 1);
+ strncpy(self->ext.longname, ext_name, sizeof(self->ext.longname) - 1);
+ strncpy(self->ext.mixername, ext_name, sizeof(self->ext.mixername) - 1);
+ self->ext.poll_fd = -1;
+ self->ext.callback = &roar_ext_callback; 
+ self->ext.private_data = self;
+
+ if ( (error = snd_ctl_ext_create(&(self->ext), name, mode)) < 0 ) {
+  roar_disconnect(&(self->roar.con));
+  free(self);
+  return error;
+ }
+
+ *handlep = self->ext.handle;
+ return 0;
+}
+
+SND_CTL_PLUGIN_SYMBOL(roar);
Index: plugins/alsavs/pcm_roar.c
===================================================================
--- plugins/alsavs/pcm_roar.c	(revision 5239)
+++ plugins/alsavs/pcm_roar.c	(revision 5263)
@@ -479,5 +479,5 @@
 
  errno = ENOSYS;
- if ( roar_simple_connect(&(self->roar.con), (char*)server, "ALSA Plugin") == -1 ) {
+ if ( roar_simple_connect(&(self->roar.con), server, "ALSA Plugin") == -1 ) {
   free(self);
   return -errno;
Index: plugins/alsavs/roar.h
===================================================================
--- plugins/alsavs/roar.h	(revision 5196)
+++ plugins/alsavs/roar.h	(revision 5263)
@@ -64,4 +64,18 @@
 };
 
+struct roar_mixerstream {
+ int id;
+ char name[32];
+ unsigned int channels;
+};
+
+#define MAX_STREAMS      16
+
+struct roar_alsa_ctl {
+ snd_ctl_ext_t          ext;
+ struct roar_alsa       roar;
+ struct roar_mixerstream streams[MAX_STREAMS];
+};
+
 void roar_plugin_reset(struct roar_alsa_pcm * self);
 void* roar_plugin_thread (void * thread_data);
