//filter-slfi-calc.c: /* * Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2012-2015 * * 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 #include #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