Index: plugins/universal/Makefile
===================================================================
--- plugins/universal/Makefile	(revision 6011)
+++ plugins/universal/Makefile	(revision 6013)
@@ -4,8 +4,12 @@
 TARGETS_PROTO=echo daytime discard http
 TARGETS_SLFI=helloworld chain alternative static file2filter channel2event trigger map random
+TARGETS_SLFI_LIBM=fade
 TARGETS_SERVICE=about
 TARGETS_MISC=plugin-info helloworld debug-notify tic-tac-toe
 TARGETS_IO=piface
 TARGETS__NAMES=$(TARGETS_MISC) $(TARGETS_IO) $(foreach c,$(TARGETS_PROTO),protocol-$c) $(foreach c,$(TARGETS_SERVICE),service-$c) $(foreach c,$(TARGETS_SLFI),filter-slfi-$c)
+ifneq ($(lib_m),)
+TARGETS__NAMES+=$(foreach c,$(TARGETS_SLFI_LIBM),filter-slfi-$c)
+endif
 TARGETS       = $(foreach c,$(TARGETS__NAMES),$c$(SHARED_SUFFIX))
 
@@ -36,2 +40,4 @@
 $(foreach c,$(TARGETS_SLFI),filter-slfi-$c$(SHARED_SUFFIX)): $(foreach c,$(TARGETS_SLFI),filter-slfi-$c.o)
 	$(CC) $(LDFLAGS) -o $@ $(shell basename $@ $(SHARED_SUFFIX)).o $(LIBS) $(LIBROARLIGHT)
+$(foreach c,$(TARGETS_SLFI_LIBM),filter-slfi-$c$(SHARED_SUFFIX)): $(foreach c,$(TARGETS_SLFI_LIBM),filter-slfi-$c.o)
+	$(CC) $(LDFLAGS) -o $@ $(shell basename $@ $(SHARED_SUFFIX)).o $(LIBS) $(LIBROARLIGHT) $(lib_m)
Index: plugins/universal/filter-slfi-fade.c
===================================================================
--- plugins/universal/filter-slfi-fade.c	(revision 6013)
+++ plugins/universal/filter-slfi-fade.c	(revision 6013)
@@ -0,0 +1,367 @@
+//filter-slfi-fade.c:
+
+/*
+ *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2012-2014
+ *
+ *  This file is part of roard 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.
+ *
+ *  RoarAudio 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.
+ *
+ */
+
+#include <roaraudio.h>
+#include <libroarlight/libroarlight.h>
+#include <math.h>
+
+enum slfi_function {
+ FUNC_STEP = 0,
+ FUNC_LIN,
+ FUNC_COS
+// FUNC_LOWPASS
+};
+
+struct slfi_channel {
+ // config:
+ ssize_t index;
+ enum    slfi_function func;
+ double  exponent;
+ int32_t time_base;
+ ssize_t time_channel; // if not -1: time_end = time_base * valueof(time_channel)/255.
+ ssize_t time_channelblack; // if not -1: time_black = time_end * valueof(time_channelblack)/255.
+ double  time_black_point;
+ int32_t time_end;
+ int32_t time_black;
+ ssize_t trigger_channel;
+ uint8_t trigger_event;
+ // state:
+ int32_t t;
+ uint8_t value_old;
+ uint8_t value_trigger;
+};
+
+static enum slfi_function __str2func (const char * str) {
+ if ( !strcasecmp(str, "step") ) {
+  return FUNC_STEP;
+ } else if ( !strcasecmp(str, "lin") ) {
+  return FUNC_LIN;
+ } else if ( !strcasecmp(str, "cos") ) {
+  return FUNC_COS;
+/*
+ } else if ( !strcasecmp(str, "lowpass") ) {
+  return FUNC_LOWPASS;
+*/
+ } else {
+  ROAR_WARN("__str2func(str='%s'): Unknown function, defaulting to linear fade", str);
+  return FUNC_LIN;
+ }
+}
+
+static void __init_chan(struct slfi_channel * chan) {
+ memset(chan, 0, sizeof(*chan));
+ chan->index = -1;
+ chan->func = FUNC_LIN;
+ chan->exponent = 1.;
+ chan->time_base = 1000000L;
+ chan->time_channel = -1;
+ chan->time_channelblack = -1;
+ chan->time_black_point = -1;
+ chan->time_end = chan->time_base;
+ chan->time_black = -1;
+ chan->trigger_channel = -1;
+ chan->trigger_event = ROAR_ROARDMX_EVENT_STEP;
+ chan->t = -1;
+ chan->value_old = 0;
+ chan->value_trigger = 0;
+}
+
+static int __push_chan(struct slfi_channel * chan, struct slfi_channel ** array, size_t * arraylen, size_t * arrayptr) {
+ struct slfi_channel * new;
+ int err;
+ size_t i;
+
+ if ( *arrayptr == *arraylen ) {
+  new = roar_mm_realloc(*array, sizeof(struct slfi_channel)*(*arraylen + 8));
+  err = roar_error;
+  if ( new == NULL ) {
+   roar_mm_free(array);
+   roar_err_set(err);
+   *array = NULL;
+   *arraylen = 0;
+   *arrayptr = 0;
+   return -1;
+  }
+  for (i = *arrayptr + 1; i < (*arrayptr + 8); i++)
+   __init_chan(&(new[i]));
+  *array = new;
+  *arraylen += 8;
+ }
+
+ (*array)[*arrayptr] = *chan;
+ (*arrayptr)++;
+ return 0;
+}
+
+static int __init(struct roar_slfi_inst * inst, const struct roar_keyval * para, ssize_t paralen) {
+ const struct roar_keyval * kv;
+ ssize_t i;
+ struct slfi_channel chan;
+ struct slfi_channel * array = NULL;
+ size_t arraylen = 0;
+ size_t arrayptr = 0;
+
+ __init_chan(&chan);
+
+ for (i = 0; i < paralen; i++) {
+  kv = &(para[i]);
+  if ( kv->key == NULL || kv->value == NULL )
+   continue;
+
+  if ( !strcmp(kv->key, "channel") ) {
+   if ( chan.index != -1 ) {
+    if ( __push_chan(&chan, &array, &arraylen, &arrayptr) == -1 ) {
+     ROAR_ERR("__init(*): Can not add more channels: %s", roar_errorstring);
+     return -1;
+    }
+   }
+   chan.index = atoi(kv->value);
+  } else if ( !strcmp(kv->key, "exponent") ) {
+   chan.exponent = atof(kv->value);
+  } else if ( !strcmp(kv->key, "timebase") ) {
+   chan.time_base = roar_str2usec(kv->value);
+  } else if ( !strcmp(kv->key, "time") ) {
+   chan.time_end = roar_str2usec(kv->value);
+  } else if ( !strcmp(kv->key, "time_black_point") ) {
+   if ( !strcmp(kv->value, "none") ) {
+    chan.time_black_point = -1;
+   } else {
+    chan.time_black_point = atof(kv->value);
+    if ( chan.time_black_point <= 0. ) chan.time_black_point = -1.;
+    if ( chan.time_black_point >= 1. ) chan.time_black_point = -1.;
+   }
+  } else if ( !strcmp(kv->key, "timechannel") ) {
+   chan.time_channel = atoi(kv->value);
+  } else if ( !strcmp(kv->key, "timechannelblack") ) {
+   chan.time_channelblack = atoi(kv->value);
+  } else if ( !strcmp(kv->key, "triggerchannel") ) {
+   chan.trigger_channel = atoi(kv->value);
+  } else if ( !strcmp(kv->key, "triggerevent") ) {
+   chan.trigger_event = roar_roardmx_str2event(kv->value);
+  } else if ( !strcmp(kv->key, "function") ) {
+   chan.func = __str2func(kv->value);
+  } else {
+   ROAR_WARN("__init(*): Unknown parameter: %s", kv->key);
+  }
+ }
+
+ if ( chan.index != -1 ) {
+  if ( __push_chan(&chan, &array, &arraylen, &arrayptr) == -1 ) {
+   ROAR_ERR("__init(*): Can not add more channels: %s", roar_errorstring);
+   return -1;
+  }
+ }
+
+ inst->userdata = array;
+ return 0;
+}
+
+static double __func_calc(enum slfi_function func, double a) {
+ switch (func) {
+  case FUNC_STEP:
+     return a > 0.999 ? 1. : 0.; // allow some error here. Error should be 1/255.
+    break;
+  case FUNC_LIN:
+    return a;
+   break;
+  case FUNC_COS:
+    return (1.-cos(M_PI*a))/2.;
+   break;
+ }
+
+ // error.
+ return 1.;
+}
+
+static double __time_back_to_t1(int32_t t) {
+ if ( t > 0 )
+  return (double)t;
+ return 0;
+}
+
+static uint8_t __channel_calc(struct slfi_channel * chan, uint8_t value, int32_t usecspassed, const uint8_t * event, size_t eventlen, uint8_t * universe, ssize_t size_of_universe) {
+ int have_trigger = 0;
+ size_t i;
+ double value_old, value_new;
+ double a;
+ double t1;
+
+ if ( chan->trigger_channel >= 0 ) {
+  if ( chan->trigger_channel >= size_of_universe ) {
+   ROAR_WARN("__channel_calc(*): Universe too small for filter.");
+   have_trigger = 0;
+  } else {
+   if ( universe[chan->trigger_channel] != chan->value_trigger ) {
+    chan->value_trigger = universe[chan->trigger_channel];
+    have_trigger = 1;
+   }
+  }
+ } else {
+  for (i = 0; i < eventlen; i++) {
+   if ( event[i] == chan->trigger_event || event[i] == (chan->trigger_event | ROAR_ROARDMX_ETYPE_ON) ) {
+    have_trigger = 1;
+    break;
+   }
+  }
+ }
+
+ ROAR_DBG("__channel_calc(*): chan={.time=%li, .time_end=%li, ...}, have_trigger=%i", (long int)chan->t, (long int)chan->time_end, have_trigger);
+
+ // are we currently fading?
+ if ( chan->t < 0 && !have_trigger )
+  return chan->value_old; // no, just return current value.
+
+ // yes, keep calcing:
+ if ( have_trigger && chan->t < 0 )
+  chan->t = 0;
+
+ // recaculate timings.
+ if ( chan->time_channel >= 0 ) {
+  if ( chan->time_channel >= size_of_universe ) {
+   ROAR_WARN("__channel_calc(*): Universe too small for filter.");
+   chan->time_end = 0;
+  } else {
+   chan->time_end = (chan->time_base * (int32_t)universe[chan->time_channel]) / 255;
+  }
+ }
+
+ if ( chan->time_channelblack != -1 ) {
+  if ( chan->time_channelblack >= size_of_universe ) {
+   ROAR_WARN("__channel_calc(*): Universe too small for filter.");
+   chan->time_black = -1;
+  } else {
+   chan->time_black = (chan->time_end * (int32_t)universe[chan->time_channelblack]) / 255;
+  }
+ } else {
+  chan->time_black = chan->time_end * chan->time_black_point;
+ }
+ ROAR_DBG("__channel_calc(*): chan->time_black=%li", (long int)chan->time_black);
+
+ // calculate value.
+ if ( chan->t >= chan->time_end ) {
+  chan->t = -1;
+  a = 1.;
+  value_old = value_new = chan->value_old = value;
+ } else if ( chan->t < chan->time_black ) {
+  a = (double)chan->t / (double)chan->time_black;
+  value_old = chan->value_old;
+  value_new = 0.;
+ } else {
+  t1 = __time_back_to_t1(chan->time_black);
+  a = ((double)chan->t - t1) / ((double)chan->time_end - t1);
+  value_old = chan->time_black > 0 ? 0 : chan->value_old;
+  value_new = value;
+  if ( a > 1. )
+   a = 1.;
+ }
+
+ if ( a > 1. ) {
+  a = 1.;
+ }
+
+ if ( 0 ) { // inverse mode
+  a = 2.*a - __func_calc(chan->func, a);
+ } else { // normal mode
+  a = __func_calc(chan->func, a);
+ }
+
+ a = pow(a, chan->exponent);
+ value = value_new = (double)(value_old * (1. - a) + value_new * a);
+ if ( value_new > 255. ) value = 255;
+ if ( value_new < 0. )   value = 0;
+
+ if ( have_trigger ) {
+  chan->value_old = value;
+  chan->t = 0;
+ }
+
+ if ( chan->t != -1 )
+  chan->t += usecspassed;
+
+ return value;
+}
+
+static int __update(struct roar_slfi_inst * inst, uint8_t * universe, ssize_t size_of_universe, int32_t usecspassed, const uint8_t * event, size_t eventlen) {
+ struct slfi_channel * chan = inst->userdata;
+
+ if ( chan == NULL )
+  return 0;
+
+ while (chan->index != -1) {
+  ROAR_DBG("__update(*): chan(%p)->index=%i", chan, (int)chan->index);
+  if ( chan->index < 0 && chan->index >= size_of_universe ) {
+   ROAR_WARN("__update(*): Universe too small for filter.");
+   continue;
+  }
+  universe[chan->index] = __channel_calc(chan, universe[chan->index], usecspassed, event, eventlen, universe, size_of_universe);
+  chan++;
+ }
+
+ return 0;
+}
+
+static const struct roar_slfi_filter filter[1] = {
+ {
+  .name = "fade",
+  .description = "Fade SLFI filter",
+  .flags = ROAR_SLFI_FLAG_ON_UPDATE,
+  .init = __init,
+  .uninit = NULL,
+  .update = __update,
+  .ctl = NULL
+ }
+};
+
+ROAR_DL_PLUGIN_REG_SLFI(filter);
+
+// This is the plugin control block.
+ROAR_DL_PLUGIN_START(filter_slfi_fade) {
+ // Here we set the name and vendor of our plugin.
+ // If you have no Vendor ID you need to use ROAR_DL_PLUGIN_META_PRODUCT_NV().
+ ROAR_DL_PLUGIN_META_PRODUCT_NIV("filter-slfi-fade", ROAR_VID_ROARAUDIO, ROAR_VNAME_ROARAUDIO);
+
+ // This sets the version of your plugin.
+ ROAR_DL_PLUGIN_META_VERSION(ROAR_VERSION_STRING);
+
+ // This sets the license of your plugin.
+ // If there is no tag for the license you use you can just
+ // use ROAR_DL_PLUGIN_META_LICENSE().
+ ROAR_DL_PLUGIN_META_LICENSE_TAG(GPLv3_0);
+
+ // This sets the author and contact infos.
+ // There are several other macros to do this with other parameters.
+ // See ROAR_DL_PLUGIN_META_CONTACT*() in the header or documentation.
+ ROAR_DL_PLUGIN_META_CONTACT_FLNE("Philipp", "Schafft", "ph3-der-loewe", "lion@lion.leolix.org");
+
+ // This sets the description for your plugin.
+ ROAR_DL_PLUGIN_META_DESC("This plugin allows to fade between settings");
+
+ // Load filters.
+ ROAR_DL_PLUGIN_REG_FNFUNC(ROAR_DL_FN_FILTER);
+
+// This is the end of the control block.
+} ROAR_DL_PLUGIN_END
+
+//ll
