The attached patches supply two things:

* Configuration support for the already in-place filter system
* A new filter which reroutes audio between channels

The configuration of filters is done by setting up an arbitrary
number of names filter templates in mpd.conf, like so:

filter {
        name    "lefty"
        plugin  "null"

        # Any plugin-specific configuration
        # goes here
}

filter {
        name    "lucy"
        plugin  "route"

        routes  "0>0, 1>1, 0>2, 1>3"
}

Once this is done, any output section accepts the "filters"
string formatted as a comma-separated list of template names

audio_output {
        type    "oss"
        name    "oss_out"
        filters "lefty, lucy"
}


The second new feature, the "route" filter, simply copies
channel data from one to one or more channels in its output.
In the example above, a stereo stream is upmixed to quadrophonic
by duplicating the front left (0) and right (1) to rear left (2)
and right (3).


The patches have been tested on an OSSv4 system, if anyone can
test it with different output types, it would be appreciated!

Best regards
Albin Eldstål-Damlin

From 39bbd26243d8e5363ac0c690fda5d3627b136851 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albin=20Eldst=C3=A5l-Damlin?= <laeder.k...@gmail.com>
Date: Sun, 13 Dec 2009 21:23:57 +0100
Subject: [PATCH 1/7] Initial (statically configured) route filter plugin

---
 src/filter/route_filter_plugin.c |  240 ++++++++++++++++++++++++++++++++++++++
 src/filter/route_filter_plugin.h |   24 ++++
 src/filter_registry.c            |    1 +
 src/filter_registry.h            |    1 +
 4 files changed, 266 insertions(+), 0 deletions(-)
 create mode 100644 src/filter/route_filter_plugin.c
 create mode 100644 src/filter/route_filter_plugin.h

diff --git a/src/filter/route_filter_plugin.c b/src/filter/route_filter_plugin.c
new file mode 100644
index 0000000..df0fa26
--- /dev/null
+++ b/src/filter/route_filter_plugin.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2003-2009 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * 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.
+ */
+
+/** \file
+ *
+ * This filter copies audio data between channels. Useful for
+ * upmixing mono/stereo audio to surround speaker configurations.
+ */
+
+#include "config.h"
+#include "audio_format.h"
+#include "filter_plugin.h"
+#include "filter_internal.h"
+#include "filter_registry.h"
+
+#include <assert.h>
+#include <string.h>
+
+
+struct route_filter {
+
+       /**
+        * Inherit (and support cast to/from) filter
+        */
+       struct filter base;
+
+       /**
+        * The number of copy operations done on each sample
+        * For example, an upmix from stereo to quadrophonic
+        * would have number_of_copies=2; one for left->rear_left
+        * and one for right->rear_right
+        */
+       int number_of_copies;
+
+       /**
+        * The minimum number of channels we need for output
+        * to be able to perform all the copies the user has specified
+        */
+       int min_output_channels;
+
+       /**
+        * The minimum number of input channels we need to
+        * copy all the data the user has requested. If fewer
+        * than this many are supplied by the input, undefined
+        * copy operations are given zeroed sources in stead.
+        */
+       int min_input_channels;
+
+       /**
+        * The set of copy operations to perform on each sample
+        * The index is an output channel to use, the value is
+        * a corresponding input channel from which to take the
+        * data. A -1 means "no source"
+        */
+       signed char* sources;
+
+       /**
+        * The actual input format of our signal, once opened
+        */
+       struct audio_format input_format;
+
+       /**
+        * The decided upon output format, once opened
+        */
+       struct audio_format output_format;
+
+       /**
+        * The size, in bytes, of each multichannel sample in the
+c
+        * input buffer
+        */
+       size_t input_sample_size;
+
+       /**
+        * The size, in bytes, of each multichannel sample in the
+        * output buffer
+        */
+       size_t output_sample_size;
+
+       /**
+        * The output buffer used last time around, can be reused if the size 
doesn't differ.
+        */
+       void *output_buffer;
+
+       /**
+        * The size in bytes of the currently allocated output buffer
+        */
+       size_t output_buffer_size;
+
+};
+
+static struct filter *
+route_filter_init(G_GNUC_UNUSED const struct config_param *param,
+                G_GNUC_UNUSED GError **error_r)
+{
+       struct route_filter *filter = g_new(struct route_filter, 1);
+
+       /**
+        * Hard-coded configuration for now. This will eventually be
+        * equivalent to a configuration string:
+        * routes "0-0, 1-1, 0-2, 1-3"
+        * And should result in 4-channel output
+        */
+       filter->number_of_copies    = 4;
+       filter->min_output_channels = 3+1;
+       filter->min_input_channels  = 1+1;
+       filter->sources = g_malloc(filter->min_output_channels * sizeof(signed 
char));
+
+       filter->sources[0] = 0; 
+       filter->sources[1] = 1; 
+       filter->sources[2] = 0; 
+       filter->sources[3] = 1; 
+
+       filter_init(&filter->base, &route_filter_plugin);
+       return &filter->base;
+}
+
+static void
+route_filter_finish(struct filter *_filter)
+{
+       struct route_filter *filter = (struct route_filter *)_filter;
+
+       g_free(filter->sources);
+       g_free(filter);
+}
+
+static const struct audio_format *
+route_filter_open(struct filter *_filter,
+                const struct audio_format *audio_format,
+                G_GNUC_UNUSED GError **error_r)
+{
+       struct route_filter *filter = (struct route_filter *)_filter;
+
+       // WTF is this?
+       (void)filter;
+
+       // Copy the input format for later reference
+       filter->input_format = *audio_format;
+       filter->input_sample_size = 
audio_format_sample_size(&filter->input_format) * filter->input_format.channels;
+
+       // Decide on an output format which has enough channels, and is 
otherwise identical
+       filter->output_format = *audio_format;
+       filter->output_format.channels = filter->min_output_channels;
+
+       // Precalculate this simple value, to speed up allocation later
+       filter->output_sample_size = 
audio_format_sample_size(&filter->output_format) * 
filter->output_format.channels;
+
+       // This buffer grows as needed
+       filter->output_buffer_size = filter->output_sample_size;
+       filter->output_buffer = g_malloc0(filter->output_buffer_size);
+
+       return &filter->output_format;
+}
+
+static void
+route_filter_close(struct filter *_filter)
+{
+       struct route_filter *filter = (struct route_filter *)_filter;
+
+       // Again, WTF?
+       (void)filter;
+
+       filter->output_buffer_size = 0;
+       g_free(filter->output_buffer);
+}
+
+static const void *
+route_filter_filter(struct filter *_filter,
+                  const void *src, size_t src_size,
+                  size_t *dest_size_r, G_GNUC_UNUSED GError **error_r)
+{
+       struct route_filter *filter = (struct route_filter *)_filter;
+       (void)filter;
+
+       size_t number_of_samples = src_size / filter->input_sample_size;
+       *dest_size_r = number_of_samples * filter->output_sample_size;
+
+       size_t bytes_per_sample_per_channel = 
audio_format_sample_size(&filter->input_format);
+
+       // Grow our reusable buffer, if needed
+       if (*dest_size_r > filter->output_buffer_size) {
+               filter->output_buffer_size = *dest_size_r;
+               filter->output_buffer = g_realloc(filter->output_buffer, 
filter->output_buffer_size);
+       }
+
+       // A moving pointer that always refers to channel 0 in the input, at 
the currently handled sample
+       const void *base_source = src;
+
+       // A moving pointer that always refers to the currently filled channel 
of the currently handled sample, in the output
+       void *chan_destination = filter->output_buffer;
+
+       // Perform our copy operations, with N input channels and M output 
channels
+       for (int s=0; s<number_of_samples; ++s) {
+
+               // Need to perform one copy per output channel
+               for (int c=0; c<filter->min_output_channels; ++c) {
+                       if (filter->sources[c] == -1) {
+                               // No source for this destination output, give 
it zeroes as input
+                               memset(chan_destination, 0x00, 
bytes_per_sample_per_channel);
+                       } else {
+                               // Get the data from channel sources[c] and 
copy it to the output
+                               void *data = base_source + (filter->sources[c] 
* bytes_per_sample_per_channel);
+                               g_memmove(chan_destination, data, 
bytes_per_sample_per_channel);
+                       }
+                       // Move on to the next output channel
+                       chan_destination += bytes_per_sample_per_channel;
+               }
+
+
+               // Go on to the next N input samples
+               base_source += filter->input_sample_size;
+       }
+
+       return filter->output_buffer;
+}
+
+const struct filter_plugin route_filter_plugin = {
+       .name = "route",
+       .init = route_filter_init,
+       .finish = route_filter_finish,
+       .open = route_filter_open,
+       .close = route_filter_close,
+       .filter = route_filter_filter,
+};
diff --git a/src/filter/route_filter_plugin.h b/src/filter/route_filter_plugin.h
new file mode 100644
index 0000000..61a0498
--- /dev/null
+++ b/src/filter/route_filter_plugin.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2003-2009 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * 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.
+ */
+
+#ifndef ROUTE_FILTER_PLUGIN_H
+#define ROUTE_FILTER_PLUGIN_H
+
+
+#endif
diff --git a/src/filter_registry.c b/src/filter_registry.c
index a6aaa2b..2685b32 100644
--- a/src/filter_registry.c
+++ b/src/filter_registry.c
@@ -27,6 +27,7 @@
 const struct filter_plugin *const filter_plugins[] = {
        &null_filter_plugin,
        &chain_filter_plugin,
+       &route_filter_plugin,
        &volume_filter_plugin,
        NULL,
 };
diff --git a/src/filter_registry.h b/src/filter_registry.h
index 7eb7f70..aab4105 100644
--- a/src/filter_registry.h
+++ b/src/filter_registry.h
@@ -29,6 +29,7 @@
 extern const struct filter_plugin null_filter_plugin;
 extern const struct filter_plugin chain_filter_plugin;
 extern const struct filter_plugin convert_filter_plugin;
+extern const struct filter_plugin route_filter_plugin;
 extern const struct filter_plugin volume_filter_plugin;
 
 const struct filter_plugin *
-- 
1.6.4.4

From f25568af4409d5fceb1999027f69ce2096dbbfde Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albin=20Eldst=C3=A5l-Damlin?= <laeder.k...@gmail.com>
Date: Sun, 13 Dec 2009 21:24:31 +0100
Subject: [PATCH 2/7] Minor documentation fix

---
 src/filter_plugin.h |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/filter_plugin.h b/src/filter_plugin.h
index 0043246..dc5903b 100644
--- a/src/filter_plugin.h
+++ b/src/filter_plugin.h
@@ -108,10 +108,10 @@ filter_free(struct filter *filter);
  * Opens the filter, preparing it for filter_filter().
  *
  * @param filter the filter object
- * @param audio_format the audio format of incoming and outgoing data
+ * @param audio_format the audio format of incoming data
  * @param error location to store the error occuring, or NULL to
  * ignore errors.
- * @return true on success, false on error
+ * @return the format of outgoing data
  */
 const struct audio_format *
 filter_open(struct filter *filter, const struct audio_format *audio_format,
-- 
1.6.4.4

From 002adc95264a55c6c69dea73eade18e0fd7c04e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albin=20Eldst=C3=A5l-Damlin?= <laeder.k...@gmail.com>
Date: Sun, 13 Dec 2009 21:24:44 +0100
Subject: [PATCH 3/7] Added route filter plugin to build

---
 Makefile.am |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 760e077..bd9878e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -51,6 +51,7 @@ mpd_headers = \
        src/filter_registry.h \
        src/filter/chain_filter_plugin.h \
        src/filter/convert_filter_plugin.h \
+       src/filter/route_filter_plugin.h \
        src/filter/volume_filter_plugin.h \
        src/command.h \
        src/idle.h \
@@ -703,6 +704,7 @@ FILTER_SRC = \
        src/filter/null_filter_plugin.c \
        src/filter/chain_filter_plugin.c \
        src/filter/convert_filter_plugin.c \
+       src/filter/route_filter_plugin.c \
        src/filter/volume_filter_plugin.c
 
 
-- 
1.6.4.4

From 77a4e3473c5b1dc52fe26389125c905f6a8686f7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albin=20Eldst=C3=A5l-Damlin?= <laeder.k...@gmail.com>
Date: Sun, 13 Dec 2009 22:49:39 +0100
Subject: [PATCH 4/7] Initial filter chain and filter configuration for outputs.

---
 src/conf.h                       |    3 +
 src/filter/chain_filter_plugin.c |   91 ++++++++++++++++++++++++++++++++++++++
 src/filter/chain_filter_plugin.h |   12 +++++
 src/output_init.c                |    4 ++
 4 files changed, 110 insertions(+), 0 deletions(-)

diff --git a/src/conf.h b/src/conf.h
index 7dd12af..049c980 100644
--- a/src/conf.h
+++ b/src/conf.h
@@ -42,6 +42,7 @@
 #define CONF_PASSWORD                   "password"
 #define CONF_DEFAULT_PERMS              "default_permissions"
 #define CONF_AUDIO_OUTPUT               "audio_output"
+#define CONF_AUDIO_FILTER               "filter"
 #define CONF_AUDIO_OUTPUT_FORMAT        "audio_output_format"
 #define CONF_MIXER_TYPE                 "mixer_type"
 #define CONF_REPLAYGAIN                 "replaygain"
@@ -73,6 +74,8 @@
 #define DEFAULT_PLAYLIST_MAX_LENGTH (1024*16)
 #define DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS false
 
+#define MAX_FILTER_CHAIN_LENGTH 255
+
 struct block_param {
        char *name;
        char *value;
diff --git a/src/filter/chain_filter_plugin.c b/src/filter/chain_filter_plugin.c
index 5cd8aa0..a0c430c 100644
--- a/src/filter/chain_filter_plugin.c
+++ b/src/filter/chain_filter_plugin.c
@@ -18,12 +18,14 @@
  */
 
 #include "config.h"
+#include "conf.h"
 #include "filter/chain_filter_plugin.h"
 #include "filter_plugin.h"
 #include "filter_internal.h"
 #include "filter_registry.h"
 
 #include <assert.h>
+#include <string.h>
 
 struct filter_chain {
        /** the base class */
@@ -176,3 +178,92 @@ filter_chain_append(struct filter *_chain, struct filter 
*filter)
 
        chain->children = g_slist_append(chain->children, filter);
 }
+
+/**
+ * Find the "filter" configuration block for the specified name.
+ *
+ * @param filter_template_name the name of the filter template
+ * @return the configuration block, or NULL if none was configured
+ */
+static const struct config_param *
+filter_plugin_config(const char *filter_template_name)
+{
+       const struct config_param *param = NULL;
+
+       while ((param = config_get_next_param(CONF_AUDIO_FILTER, param)) != 
NULL) {
+               const char *name =
+                       config_get_block_string(param, "name", NULL);
+               if (name == NULL)
+                       g_error("filter configuration without 'name' name in 
line %d",
+                               param->line);
+
+               if (strcmp(name, filter_template_name) == 0)
+                       return param;
+       }
+
+       return NULL;
+}
+
+/**
+ * Builds a filter chain from a configuration string on the form
+ * "name1, name2, name3, ..." by looking up each name among the
+ * configured filter sections.
+ * @param chain the chain to append filters on
+ * @param spec the filter chain specification
+ * @return the number of filters which were successfully added
+ */
+unsigned int
+filter_chain_parse(struct filter *chain, const char *spec) {
+       
+       // Split on comma and/or whitespace
+       gchar** tokens = g_strsplit_set(spec, ", \t", 255);
+
+       int added_filters = 0;
+
+       // Add each name to the filter chain by instantiating an actual filter
+       char **template_names = tokens;
+       while (*template_names != NULL) {
+               const struct config_param *cfg = 
filter_plugin_config(*template_names);
+               if (cfg == NULL) {
+                       g_error("Unable to locate filter template %s",
+                               *template_names);
+                       ++template_names;
+                       continue;
+               }
+
+               // Figure out what kind of a plugin that is
+               const char *plugin_name = config_get_block_string(cfg, 
"plugin", NULL);
+               if (plugin_name == NULL) {
+                       g_error("filter configuration without 'plugin' at line 
%d",
+                               cfg->line);
+                       ++template_names;
+                       continue;
+               }
+
+               // Instantiate one of those filter plugins with the template 
name as a hint
+               const struct filter_plugin *fp = 
filter_plugin_by_name(plugin_name);
+               if (fp == NULL) {
+                       g_error("filter plugin not found: %s",
+                               plugin_name);
+                       ++template_names;
+                       continue;
+               }
+
+               struct filter *f = filter_new(fp, cfg, NULL);
+               if (f == NULL) {
+                       g_error("filter plugin initialization failed: %s",
+                               plugin_name);
+                       ++template_names;
+                       continue;
+               }
+
+               filter_chain_append(chain, f);
+               ++added_filters;
+
+               ++template_names;
+       }
+
+       g_strfreev(tokens);
+
+       return added_filters;
+}
diff --git a/src/filter/chain_filter_plugin.h b/src/filter/chain_filter_plugin.h
index f8462b2..54e0708 100644
--- a/src/filter/chain_filter_plugin.h
+++ b/src/filter/chain_filter_plugin.h
@@ -45,4 +45,16 @@ filter_chain_new(void);
 void
 filter_chain_append(struct filter *chain, struct filter *filter);
 
+/**
+ * Builds a filter chain from a configuration string on the form
+ * "name1, name2, name3, ..." by looking up each name among the
+ * configured filter sections. If no filters are specified, a
+ * null filter is automatically appended.
+ * @param chain the chain to append filters on
+ * @param spec the filter chain specification
+ * @return the number of filters which were successfully added
+ */
+unsigned int
+filter_chain_parse(struct filter *chain, const char *spec);
+
 #endif
diff --git a/src/output_init.c b/src/output_init.c
index a1b79c9..b107daa 100644
--- a/src/output_init.c
+++ b/src/output_init.c
@@ -41,6 +41,7 @@
 #define AUDIO_OUTPUT_TYPE      "type"
 #define AUDIO_OUTPUT_NAME      "name"
 #define AUDIO_OUTPUT_FORMAT    "format"
+#define AUDIO_FILTERS          "filters"
 
 static const struct audio_output_plugin *
 audio_output_detect(GError **error)
@@ -187,9 +188,12 @@ audio_output_init(struct audio_output *ao, const struct 
config_param *param,
        ao->fail_timer = NULL;
 
        /* set up the filter chain */
+       const char *filters_spec = config_get_block_string(param, 
AUDIO_FILTERS, "");
 
        ao->filter = filter_chain_new();
        assert(ao->filter != NULL);
+       filter_chain_parse(ao->filter, filters_spec);
+       
 
        ao->thread = NULL;
        ao->command = AO_COMMAND_NONE;
-- 
1.6.4.4

From 47f80190fcd6e93915f339e6e6b6599605227050 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albin=20Eldst=C3=A5l-Damlin?= <laeder.k...@gmail.com>
Date: Sun, 13 Dec 2009 23:59:44 +0100
Subject: [PATCH 5/7] Route filter configuration, compilation warning fixes

---
 src/filter/chain_filter_plugin.c |   10 ++-
 src/filter/route_filter_plugin.c |  160 +++++++++++++++++++++++++++-----------
 src/output_init.c                |    5 +-
 3 files changed, 126 insertions(+), 49 deletions(-)

diff --git a/src/filter/chain_filter_plugin.c b/src/filter/chain_filter_plugin.c
index a0c430c..6055399 100644
--- a/src/filter/chain_filter_plugin.c
+++ b/src/filter/chain_filter_plugin.c
@@ -223,6 +223,10 @@ filter_chain_parse(struct filter *chain, const char *spec) 
{
        // Add each name to the filter chain by instantiating an actual filter
        char **template_names = tokens;
        while (*template_names != NULL) {
+               const char *plugin_name;
+               const struct filter_plugin *fp;
+               struct filter *f;
+
                const struct config_param *cfg = 
filter_plugin_config(*template_names);
                if (cfg == NULL) {
                        g_error("Unable to locate filter template %s",
@@ -232,7 +236,7 @@ filter_chain_parse(struct filter *chain, const char *spec) {
                }
 
                // Figure out what kind of a plugin that is
-               const char *plugin_name = config_get_block_string(cfg, 
"plugin", NULL);
+               plugin_name = config_get_block_string(cfg, "plugin", NULL);
                if (plugin_name == NULL) {
                        g_error("filter configuration without 'plugin' at line 
%d",
                                cfg->line);
@@ -241,7 +245,7 @@ filter_chain_parse(struct filter *chain, const char *spec) {
                }
 
                // Instantiate one of those filter plugins with the template 
name as a hint
-               const struct filter_plugin *fp = 
filter_plugin_by_name(plugin_name);
+               fp = filter_plugin_by_name(plugin_name);
                if (fp == NULL) {
                        g_error("filter plugin not found: %s",
                                plugin_name);
@@ -249,7 +253,7 @@ filter_chain_parse(struct filter *chain, const char *spec) {
                        continue;
                }
 
-               struct filter *f = filter_new(fp, cfg, NULL);
+               f = filter_new(fp, cfg, NULL);
                if (f == NULL) {
                        g_error("filter plugin initialization failed: %s",
                                plugin_name);
diff --git a/src/filter/route_filter_plugin.c b/src/filter/route_filter_plugin.c
index df0fa26..bb07e36 100644
--- a/src/filter/route_filter_plugin.c
+++ b/src/filter/route_filter_plugin.c
@@ -21,9 +21,26 @@
  *
  * This filter copies audio data between channels. Useful for
  * upmixing mono/stereo audio to surround speaker configurations.
+ *
+ * Its configuration consists of a "filter" section with a single
+ * "routes" entry, formatted as: \\
+ * routes "0>1, 1>0, 2>2, 3>3, 3>4" \\
+ * where each pair of numbers signifies a set of channels.
+ * Each source>dest pair leads to the data from channel #source
+ * being copied to channel #dest in the output.
+ * 
+ * Example: \\
+ * routes "0>0, 1>1, 0>2, 1>3"\\
+ * upmixes stereo audio to a 4-speaker system, copying the front-left
+ * (0) to front left (0) and rear left (2), copying front-right (1) to
+ * front-right (1) and rear-right (3).
+ *
+ * If multiple sources are copied to the same destination channel, only
+ * one of them takes effect.
  */
 
 #include "config.h"
+#include "conf.h"
 #include "audio_format.h"
 #include "filter_plugin.h"
 #include "filter_internal.h"
@@ -31,6 +48,7 @@
 
 #include <assert.h>
 #include <string.h>
+#include <stdlib.h>
 
 
 struct route_filter {
@@ -41,18 +59,10 @@ struct route_filter {
        struct filter base;
 
        /**
-        * The number of copy operations done on each sample
-        * For example, an upmix from stereo to quadrophonic
-        * would have number_of_copies=2; one for left->rear_left
-        * and one for right->rear_right
-        */
-       int number_of_copies;
-
-       /**
         * The minimum number of channels we need for output
         * to be able to perform all the copies the user has specified
         */
-       int min_output_channels;
+       unsigned char min_output_channels;
 
        /**
         * The minimum number of input channels we need to
@@ -60,7 +70,7 @@ struct route_filter {
         * than this many are supplied by the input, undefined
         * copy operations are given zeroed sources in stead.
         */
-       int min_input_channels;
+       unsigned char min_input_channels;
 
        /**
         * The set of copy operations to perform on each sample
@@ -82,7 +92,6 @@ struct route_filter {
 
        /**
         * The size, in bytes, of each multichannel sample in the
-c
         * input buffer
         */
        size_t input_sample_size;
@@ -105,29 +114,95 @@ c
 
 };
 
-static struct filter *
-route_filter_init(G_GNUC_UNUSED const struct config_param *param,
-                G_GNUC_UNUSED GError **error_r)
-{
-       struct route_filter *filter = g_new(struct route_filter, 1);
+/**
+ * Parse the "routes" section, a string on the form
+ *  a>b, c>d, e>f, ...
+ * where a... are non-unique, non-negative integers
+ * and input channel a gets copied to output channel b, etc.
+ * @param param the configuration block to read
+ * @param filter a route_filter whose min_channels and sources[] values to 
allocate and set up
+ */
+void
+route_filter_parse(const struct config_param *param, struct route_filter 
*filter) {
 
-       /**
-        * Hard-coded configuration for now. This will eventually be
-        * equivalent to a configuration string:
-        * routes "0-0, 1-1, 0-2, 1-3"
-        * And should result in 4-channel output
+       /* TODO:
+        * With a more clever way of marking "don't copy to output N",
+        * This could easily be merged into a single loop with some
+        * dynamic g_realloc() instead of one count run and one g_malloc().
         */
-       filter->number_of_copies    = 4;
-       filter->min_output_channels = 3+1;
-       filter->min_input_channels  = 1+1;
+
+       gchar **tokens;
+       int number_of_copies;
+
+       // A cowardly default, just passthrough stereo
+       const char *routes = config_get_block_string(param, "routes", "0>0, 
1>1");
+
+       filter->min_input_channels = 0;
+       filter->min_output_channels = 0;
+
+       tokens = g_strsplit_set(routes, ", \t", 255);
+       number_of_copies = g_strv_length(tokens);
+
+       // Start by figuring out a few basic things about the routing set
+       for (int c=0; c<number_of_copies; ++c) {
+               int source, dest;
+
+               // Split the a>b string into source and destination
+               gchar **sd = g_strsplit(tokens[c], ">", 2);
+               if (g_strv_length(sd) != 2) {
+                       g_error("Invalid copy operation in routes spec at line 
%d",
+                                       param->line);
+                       continue;
+               }
+
+               source = strtol(sd[0], NULL, 10);
+               dest = strtol(sd[1], NULL, 10);
+
+               // Keep track of the highest channel numbers seen as either in- 
or outputs
+               if (source >= filter->min_input_channels) 
filter->min_input_channels = source + 1;
+               if (dest   >= filter->min_output_channels) 
filter->min_output_channels = dest + 1;
+
+               g_strfreev(sd);
+       }
+       
+       // Allocate a map of "copy nothing to me"
        filter->sources = g_malloc(filter->min_output_channels * sizeof(signed 
char));
+       for (int i=0; i<filter->min_output_channels; ++i)
+               filter->sources[i] = -1;
+
+       // Run through the spec again, and save the actual mapping output <- 
input
+       for (int c=0; c<number_of_copies; ++c) {
+               int source, dest;
+
+               // Split the a>b string into source and destination
+               gchar **sd = g_strsplit(tokens[c], ">", 2);
+               if (g_strv_length(sd) != 2) {
+                       g_error("Invalid copy operation in routes spec at line 
%d",
+                                       param->line);
+                       continue;
+               }
+
+               source = strtol(sd[0], NULL, 10);
+               dest = strtol(sd[1], NULL, 10);
+
+               filter->sources[dest] = source;
 
-       filter->sources[0] = 0; 
-       filter->sources[1] = 1; 
-       filter->sources[2] = 0; 
-       filter->sources[3] = 1; 
+               g_strfreev(sd);
+       }
+
+       g_strfreev(tokens);
+}
 
+static struct filter *
+route_filter_init(const struct config_param *param,
+                G_GNUC_UNUSED GError **error_r)
+{
+       struct route_filter *filter = g_new(struct route_filter, 1);
        filter_init(&filter->base, &route_filter_plugin);
+
+       // Allocate and set the filter->sources[] array
+       route_filter_parse(param, filter);
+
        return &filter->base;
 }
 
@@ -147,9 +222,6 @@ route_filter_open(struct filter *_filter,
 {
        struct route_filter *filter = (struct route_filter *)_filter;
 
-       // WTF is this?
-       (void)filter;
-
        // Copy the input format for later reference
        filter->input_format = *audio_format;
        filter->input_sample_size = 
audio_format_sample_size(&filter->input_format) * filter->input_format.channels;
@@ -173,9 +245,6 @@ route_filter_close(struct filter *_filter)
 {
        struct route_filter *filter = (struct route_filter *)_filter;
 
-       // Again, WTF?
-       (void)filter;
-
        filter->output_buffer_size = 0;
        g_free(filter->output_buffer);
 }
@@ -186,36 +255,38 @@ route_filter_filter(struct filter *_filter,
                   size_t *dest_size_r, G_GNUC_UNUSED GError **error_r)
 {
        struct route_filter *filter = (struct route_filter *)_filter;
-       (void)filter;
 
        size_t number_of_samples = src_size / filter->input_sample_size;
-       *dest_size_r = number_of_samples * filter->output_sample_size;
 
        size_t bytes_per_sample_per_channel = 
audio_format_sample_size(&filter->input_format);
 
+       // A moving pointer that always refers to channel 0 in the input, at 
the currently handled sample
+       const void *base_source = src;
+
+       // A moving pointer that always refers to the currently filled channel 
of the currently handled sample, in the output
+       void *chan_destination;
+
        // Grow our reusable buffer, if needed
+       *dest_size_r = number_of_samples * filter->output_sample_size;
        if (*dest_size_r > filter->output_buffer_size) {
                filter->output_buffer_size = *dest_size_r;
                filter->output_buffer = g_realloc(filter->output_buffer, 
filter->output_buffer_size);
        }
 
-       // A moving pointer that always refers to channel 0 in the input, at 
the currently handled sample
-       const void *base_source = src;
-
        // A moving pointer that always refers to the currently filled channel 
of the currently handled sample, in the output
-       void *chan_destination = filter->output_buffer;
+       chan_destination = filter->output_buffer;
 
        // Perform our copy operations, with N input channels and M output 
channels
-       for (int s=0; s<number_of_samples; ++s) {
+       for (unsigned int s=0; s<number_of_samples; ++s) {
 
                // Need to perform one copy per output channel
-               for (int c=0; c<filter->min_output_channels; ++c) {
-                       if (filter->sources[c] == -1) {
+               for (unsigned int c=0; c<filter->min_output_channels; ++c) {
+                       if (filter->sources[c] == -1 || filter->sources[c] >= 
filter->input_format.channels) {
                                // No source for this destination output, give 
it zeroes as input
                                memset(chan_destination, 0x00, 
bytes_per_sample_per_channel);
                        } else {
                                // Get the data from channel sources[c] and 
copy it to the output
-                               void *data = base_source + (filter->sources[c] 
* bytes_per_sample_per_channel);
+                               const void *data = base_source + 
(filter->sources[c] * bytes_per_sample_per_channel);
                                g_memmove(chan_destination, data, 
bytes_per_sample_per_channel);
                        }
                        // Move on to the next output channel
@@ -227,6 +298,7 @@ route_filter_filter(struct filter *_filter,
                base_source += filter->input_sample_size;
        }
 
+       // Here it is, ladies and gentlemen! Rerouted data!
        return filter->output_buffer;
 }
 
diff --git a/src/output_init.c b/src/output_init.c
index b107daa..1691d3e 100644
--- a/src/output_init.c
+++ b/src/output_init.c
@@ -188,11 +188,12 @@ audio_output_init(struct audio_output *ao, const struct 
config_param *param,
        ao->fail_timer = NULL;
 
        /* set up the filter chain */
-       const char *filters_spec = config_get_block_string(param, 
AUDIO_FILTERS, "");
 
        ao->filter = filter_chain_new();
        assert(ao->filter != NULL);
-       filter_chain_parse(ao->filter, filters_spec);
+       filter_chain_parse(ao->filter,
+                          config_get_block_string(param, AUDIO_FILTERS, "")
+       );
        
 
        ao->thread = NULL;
-- 
1.6.4.4

From 40c75b81860bc5f5f37d2bc36c86f50d9c4458e4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albin=20Eldst=C3=A5l-Damlin?= <laeder.k...@gmail.com>
Date: Mon, 14 Dec 2009 00:10:45 +0100
Subject: [PATCH 6/7] Whitespace handling now works as expected

---
 src/filter/chain_filter_plugin.c |    7 +++++--
 src/filter/route_filter_plugin.c |   13 ++++++++-----
 2 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/src/filter/chain_filter_plugin.c b/src/filter/chain_filter_plugin.c
index 6055399..ce88457 100644
--- a/src/filter/chain_filter_plugin.c
+++ b/src/filter/chain_filter_plugin.c
@@ -215,8 +215,8 @@ filter_plugin_config(const char *filter_template_name)
 unsigned int
 filter_chain_parse(struct filter *chain, const char *spec) {
        
-       // Split on comma and/or whitespace
-       gchar** tokens = g_strsplit_set(spec, ", \t", 255);
+       // Split on comma
+       gchar** tokens = g_strsplit_set(spec, ",", 255);
 
        int added_filters = 0;
 
@@ -227,6 +227,9 @@ filter_chain_parse(struct filter *chain, const char *spec) {
                const struct filter_plugin *fp;
                struct filter *f;
 
+               // Squeeze whitespace
+               g_strstrip(*template_names);
+
                const struct config_param *cfg = 
filter_plugin_config(*template_names);
                if (cfg == NULL) {
                        g_error("Unable to locate filter template %s",
diff --git a/src/filter/route_filter_plugin.c b/src/filter/route_filter_plugin.c
index bb07e36..8c51274 100644
--- a/src/filter/route_filter_plugin.c
+++ b/src/filter/route_filter_plugin.c
@@ -140,18 +140,21 @@ route_filter_parse(const struct config_param *param, 
struct route_filter *filter
        filter->min_input_channels = 0;
        filter->min_output_channels = 0;
 
-       tokens = g_strsplit_set(routes, ", \t", 255);
+       tokens = g_strsplit(routes, ",", 255);
        number_of_copies = g_strv_length(tokens);
 
        // Start by figuring out a few basic things about the routing set
        for (int c=0; c<number_of_copies; ++c) {
                int source, dest;
 
+               // Squeeze whitespace
+               g_strstrip(tokens[c]);
+
                // Split the a>b string into source and destination
                gchar **sd = g_strsplit(tokens[c], ">", 2);
                if (g_strv_length(sd) != 2) {
-                       g_error("Invalid copy operation in routes spec at line 
%d",
-                                       param->line);
+                       g_error("Invalid copy operation around line %d in 
routes spec: %s", 
+                                       param->line, tokens[c]);
                        continue;
                }
 
@@ -177,8 +180,8 @@ route_filter_parse(const struct config_param *param, struct 
route_filter *filter
                // Split the a>b string into source and destination
                gchar **sd = g_strsplit(tokens[c], ">", 2);
                if (g_strv_length(sd) != 2) {
-                       g_error("Invalid copy operation in routes spec at line 
%d",
-                                       param->line);
+                       g_error("Invalid copy operation around line %d in 
routes spec: %s", 
+                                       param->line, tokens[c]);
                        continue;
                }
 
-- 
1.6.4.4

From 5004b02bd74cf09235107478b96cd67fb1ff910b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albin=20Eldst=C3=A5l-Damlin?= <laeder.k...@gmail.com>
Date: Mon, 14 Dec 2009 12:46:59 +0100
Subject: [PATCH 7/7] Removed useless header file of route_filter_plugin

---
 Makefile.am                      |    1 -
 src/filter/route_filter_plugin.h |   24 ------------------------
 2 files changed, 0 insertions(+), 25 deletions(-)
 delete mode 100644 src/filter/route_filter_plugin.h

diff --git a/Makefile.am b/Makefile.am
index bd9878e..a55a5e1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -51,7 +51,6 @@ mpd_headers = \
        src/filter_registry.h \
        src/filter/chain_filter_plugin.h \
        src/filter/convert_filter_plugin.h \
-       src/filter/route_filter_plugin.h \
        src/filter/volume_filter_plugin.h \
        src/command.h \
        src/idle.h \
diff --git a/src/filter/route_filter_plugin.h b/src/filter/route_filter_plugin.h
deleted file mode 100644
index 61a0498..0000000
--- a/src/filter/route_filter_plugin.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2003-2009 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * 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.
- */
-
-#ifndef ROUTE_FILTER_PLUGIN_H
-#define ROUTE_FILTER_PLUGIN_H
-
-
-#endif
-- 
1.6.4.4

Attachment: pgpLBc5VqMEqr.pgp
Description: PGP signature

------------------------------------------------------------------------------
Return on Information:
Google Enterprise Search pays you back
Get the facts.
http://p.sf.net/sfu/google-dev2dev
_______________________________________________
Musicpd-dev-team mailing list
Musicpd-dev-team@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/musicpd-dev-team

Reply via email to