Index: plugins/universal/Makefile
===================================================================
--- plugins/universal/Makefile	(revision 6015)
+++ plugins/universal/Makefile	(revision 6019)
@@ -3,5 +3,5 @@
 
 TARGETS_PROTO=echo daytime discard http
-TARGETS_SLFI=helloworld chain alternative static file2filter channel2event trigger map random strobe
+TARGETS_SLFI=helloworld chain alternative static file2filter channel2event trigger map random strobe calc
 TARGETS_SLFI_LIBM=fade
 TARGETS_SERVICE=about
Index: plugins/universal/filter-slfi-calc.c
===================================================================
--- plugins/universal/filter-slfi-calc.c	(revision 6019)
+++ plugins/universal/filter-slfi-calc.c	(revision 6019)
@@ -0,0 +1,420 @@
+//filter-slfi-calc.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>
+
+#define MAX_CHANNELS_IN 8
+
+enum slfi_calctype {
+ CT_NULL = 0,
+ CT_ADD,
+ CT_SUB,
+ CT_MUL,
+ CT_DIV,
+ CT_MOD,
+ CT_LOG,
+ CT_EXP,
+ CT_SQRT,
+ CT_POW,
+ CT_SUM,  // sum of all samples
+ CT_DXDT  // dx/dt.
+};
+
+enum slfi_numbersystem {
+ NS_AUTO = 0,
+ NS_CLIP,
+ NS_SCALE
+};
+
+// flags:
+#define CF_NONE              0x00000000U
+#define CF_I                 0x00000001U
+#define CF_ABS               0x00000002U
+
+struct slfi_calc {
+ enum slfi_calctype ct;
+ enum slfi_numbersystem ns;
+ uint32_t flags;
+ int_least32_t i; // i-flag register
+ int_least32_t c; // register to store inter-interval values.
+ ssize_t channel_out;
+ ssize_t channel_in[MAX_CHANNELS_IN];
+ ssize_t channel_in_num;
+};
+
+static inline void __init_calc(struct slfi_calc * calc) {
+ size_t i;
+
+ memset(calc, 0, sizeof(*calc));
+
+ calc->ct = CT_NULL;
+ calc->ns = NS_AUTO;
+ calc->flags = CF_NONE;
+ calc->i = 0;
+ calc->c = 0;
+ calc->channel_out = -1;
+ calc->channel_in_num = 0;
+
+ for (i = 0; i < MAX_CHANNELS_IN; i++)
+  calc->channel_in[i] = -1;
+}
+
+static int __push_calc(const struct slfi_calc * calc, struct slfi_calc ** array, size_t * arraylen, size_t * arrayptr) {
+ struct slfi_calc * new;
+ int err;
+ size_t i;
+
+ if ( *arrayptr == *arraylen ) {
+  new = roar_mm_realloc(*array, sizeof(struct slfi_calc)*(*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_calc(&(new[i]));
+  *array = new;
+  *arraylen += 8;
+ }
+
+ (*array)[*arrayptr] = *calc;
+ (*arrayptr)++;
+ return 0;
+}
+
+static inline enum slfi_calctype __parse_calc_ct(const char * str, const char ** flags) {
+ const struct {
+  const char * name;
+  const enum slfi_calctype ct;
+ } *p, __table[] = {
+  {"NULL", CT_NULL},
+  {"add", CT_ADD},
+  {"sub", CT_SUB},
+  {"mul", CT_MUL},
+  {"div", CT_DIV},
+  {"mod", CT_MOD},
+  {"log", CT_LOG},
+  {"exp", CT_EXP},
+  {"sqrt", CT_SQRT},
+  {"pow", CT_POW},
+  {"sum", CT_SUM},
+  {"dxdt", CT_DXDT},
+  {NULL, CT_NULL} // list terminator.
+ };
+ ssize_t len;
+
+ for (p = __table; p->name != NULL; p++) {
+  len = roar_mm_strlen(p->name);
+  if ( !strncasecmp(str, p->name, len) ) {
+   *flags = str+len;
+   return p->ct;
+  }
+ }
+ *flags = NULL;
+ return CT_NULL;
+}
+
+static inline const struct slfi_calc * __parse_calc(const char * key, const char * value) {
+ static struct slfi_calc calc;
+ const char * flags;
+ char * valbuf, * cur, * saveptr;
+ size_t i;
+ int found_error = 0;
+
+ if ( !*value )
+  return NULL;
+
+ __init_calc(&calc);
+
+/*
+ calc->i = 0;
+ calc->channel_out = -1;
+*/
+
+ calc.ct = __parse_calc_ct(key, &flags);
+ if ( calc.ct == CT_NULL )
+  return NULL;
+
+ if ( flags != NULL ) {
+  for (; *flags; flags++) {
+   switch (*flags) {
+    // normal flags:
+    case 'i': calc.flags |= CF_I; break;
+    case 'a': calc.flags |= CF_ABS; break;
+
+    // system:
+    case 's': calc.ns = NS_SCALE; break;
+    case 'c': calc.ns = NS_CLIP; break;
+    default:
+     ROAR_ERR("__parse_calc(key='%s', value='%s'): Unknown flag: %c", key, value, *flags);
+     return NULL;
+   }
+  }
+ }
+
+ valbuf = roar_mm_strdup(value);
+ if ( valbuf == NULL )
+  return NULL;
+ 
+ cur = roar_mm_strtok_r(valbuf, ":", &saveptr);
+
+ calc.channel_out = atoi(cur);
+
+ i = 0;
+ while ((cur = roar_mm_strtok_r(NULL, ":", &saveptr)) != NULL) {
+  calc.channel_in[i] = atoi(cur);
+  calc.channel_in_num++;
+  if ( calc.channel_in[i] < 0 || (i+1) == MAX_CHANNELS_IN ) {
+   found_error = 1;
+   break;
+  }
+  i++;
+ }
+
+ roar_mm_free(valbuf);
+
+ if ( found_error )
+  return NULL;
+
+ if ( calc.flags & CF_I ) {
+  for (i = 1; i < MAX_CHANNELS_IN; i++) {
+   if ( calc.channel_in[i] == -1 ) {
+    calc.i = calc.channel_in[i-1];
+    calc.channel_in[i-1] = -1;
+    calc.channel_in_num--;
+    break;
+   }
+  }
+ }
+
+ if ( calc.ct == CT_NULL || calc.ns == NS_AUTO || calc.channel_out < 0 )
+  return NULL;
+ return &calc;
+}
+
+static int __init(struct roar_slfi_inst * inst, const struct roar_keyval * para, ssize_t paralen) {
+ const struct slfi_calc * calc;
+ struct slfi_calc * array = NULL;
+ size_t arraylen = 0;
+ size_t arrayptr = 0;
+ const struct roar_keyval * kv;
+ ssize_t i;
+
+ for (i = 0; i < paralen; i++) {
+  kv = &(para[i]);
+  if ( kv->key == NULL || kv->value == NULL )
+   continue;
+
+  if ( (calc = __parse_calc(kv->key, kv->value)) != NULL ) {
+   if ( __push_calc(calc, &array, &arraylen, &arrayptr) == -1 ) {
+    ROAR_ERR("__init(*): Can not add more calculations: %s", roar_errorstring);
+    return -1;
+   }
+//  } else if ( !strcmp(kv->key, "") ) {
+  } else {
+   ROAR_WARN("__init(*): Unknown parameter: %s", kv->key);
+  }
+ }
+
+ inst->userdata = array;
+ return 0;
+}
+
+static inline int_least32_t __calc_step(int_least32_t val, uint8_t in, enum slfi_calctype ct, enum slfi_numbersystem ns) {
+ switch (ct) {
+  case CT_SUM:
+  case CT_ADD: val += in; break;
+  case CT_SUB: val -= in; break;
+  case CT_MUL: val *= in; break;
+  case CT_DIV: val /= in; break;
+  case CT_MOD: val %= in; break;
+  default:
+    roar_panic(ROAR_FATAL_ERROR_CPU_FAILURE, NULL); // we should never reach this point at all.
+   break;
+ }
+
+ switch (ns) {
+  case NS_AUTO: break;
+  case NS_CLIP:
+    if ( val > 255 ) {
+     val = 255;
+    } else if ( val < 0 ) {
+     val = 0;
+    }
+   break;
+  case NS_SCALE:
+    switch (ct) {
+     case CT_MUL:
+       val /= 255;
+      break;
+     default: /* NOOPs */; break;
+    }
+   break;
+ }
+
+ return val;
+}
+
+static inline void __calc(struct slfi_calc * calc, uint8_t * universe, ssize_t size_of_universe, int32_t usecspassed) {
+ int_least32_t val = 0;
+ ssize_t i;
+
+ (void)usecspassed; // needed for CT_DXDT.
+
+ if ( calc->channel_out >= size_of_universe ) {
+  ROAR_WARN("__calc(*): Output channel outside universe!");
+  return;
+ }
+
+ if ( calc->channel_in_num ) {
+  if ( calc->channel_in[0] >= size_of_universe ) {
+   ROAR_WARN("__calc(*): Initial Input channel outside universe!");
+   return;
+  }
+  val = universe[calc->channel_in[0]];
+ } else if ( calc->flags & CF_I ) {
+  val = calc->i;
+ }
+
+ switch (calc->ct) {
+  case CT_NULL: return; break;
+  case CT_LOG:
+  case CT_SQRT:
+  case CT_POW:
+  case CT_DXDT:
+    ROAR_ERR("__calc(*): Operation not supported");
+    return;
+   break;
+  case CT_SUM:
+    val += calc->c;
+  case CT_ADD:
+  case CT_SUB:
+  case CT_MUL:
+  case CT_DIV:
+  case CT_MOD:
+  case CT_EXP:
+    for (i = 1; i < calc->channel_in_num; i++) {
+     if ( calc->channel_in[i] >= size_of_universe ) {
+      ROAR_WARN("__calc(*): Input channel outside universe!");
+      return;
+     }
+     val = __calc_step(val, universe[calc->channel_in[i]], calc->ct, calc->ns);
+    }
+
+    if ( calc->channel_in_num && calc->flags & CF_I ) {
+     ROAR_DBG("__calc(*): calc->channel_in_num=%i, calc->i=%i", (int)calc->channel_in_num, (int)calc->i);
+     val = __calc_step(val, calc->i, calc->ct, calc->ns);
+    }
+   break;
+ }
+
+ switch (calc->ct) {
+  case CT_NULL: break;
+  case CT_SUM:
+    calc->c = val;
+   break;
+  case CT_ADD:
+  case CT_SUB:
+    if ( calc->ns == NS_SCALE )
+     val /= calc->channel_in_num + (calc->flags & CF_I ? 1 : 0);
+   break;
+  // NOOPs:
+  case CT_MUL:
+  case CT_DIV:
+  case CT_MOD:
+  case CT_EXP:
+   break;
+  default:
+    ROAR_ERR("__calc(*): Operation not supported");
+   break;
+ }
+
+ if ( calc->flags & CF_ABS )
+  val = abs(val);
+
+ universe[calc->channel_out] = val;
+}
+
+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_calc * self = inst->userdata;
+
+ (void)event, (void)eventlen;
+
+ while (self->channel_out != -1) {
+  __calc(self, universe, size_of_universe, usecspassed);
+  self++;
+ }
+
+ return 0;
+}
+
+static const struct roar_slfi_filter filter[1] = {
+ {
+  .name = "calc",
+  .description = "Calc 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_calc) {
+ // 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-calc", 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 calculates values of channels.");
+
+ // Load filters.
+ ROAR_DL_PLUGIN_REG_FNFUNC(ROAR_DL_FN_FILTER);
+
+// This is the end of the control block.
+} ROAR_DL_PLUGIN_END
+
+//ll
