source: roaraudio/plugins/universal/filter-slfi-fade.c @ 6014:4f1cd59b5026

Last change on this file since 6014:4f1cd59b5026 was 6014:4f1cd59b5026, checked in by phi, 5 years ago

avoid segfaul when number of channels is multiple of 8

File size: 10.2 KB
Line 
1//filter-slfi-fade.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2012-2014
5 *
6 *  This file is part of roard a part of RoarAudio,
7 *  a cross-platform sound system for both, home and professional use.
8 *  See README for details.
9 *
10 *  This file is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 3
12 *  as published by the Free Software Foundation.
13 *
14 *  RoarAudio is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this software; see the file COPYING.  If not, write to
21 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
22 *  Boston, MA 02110-1301, USA.
23 *
24 */
25
26#include <roaraudio.h>
27#include <libroarlight/libroarlight.h>
28#include <math.h>
29
30enum slfi_function {
31 FUNC_STEP = 0,
32 FUNC_LIN,
33 FUNC_COS
34// FUNC_LOWPASS
35};
36
37struct slfi_channel {
38 // config:
39 ssize_t index;
40 enum    slfi_function func;
41 double  exponent;
42 int32_t time_base;
43 ssize_t time_channel; // if not -1: time_end = time_base * valueof(time_channel)/255.
44 ssize_t time_channelblack; // if not -1: time_black = time_end * valueof(time_channelblack)/255.
45 double  time_black_point;
46 int32_t time_end;
47 int32_t time_black;
48 ssize_t trigger_channel;
49 uint8_t trigger_event;
50 // state:
51 int32_t t;
52 uint8_t value_old;
53 uint8_t value_trigger;
54};
55
56static enum slfi_function __str2func (const char * str) {
57 if ( !strcasecmp(str, "step") ) {
58  return FUNC_STEP;
59 } else if ( !strcasecmp(str, "lin") ) {
60  return FUNC_LIN;
61 } else if ( !strcasecmp(str, "cos") ) {
62  return FUNC_COS;
63/*
64 } else if ( !strcasecmp(str, "lowpass") ) {
65  return FUNC_LOWPASS;
66*/
67 } else {
68  ROAR_WARN("__str2func(str='%s'): Unknown function, defaulting to linear fade", str);
69  return FUNC_LIN;
70 }
71}
72
73static void __init_chan(struct slfi_channel * chan) {
74 memset(chan, 0, sizeof(*chan));
75 chan->index = -1;
76 chan->func = FUNC_LIN;
77 chan->exponent = 1.;
78 chan->time_base = 1000000L;
79 chan->time_channel = -1;
80 chan->time_channelblack = -1;
81 chan->time_black_point = -1;
82 chan->time_end = chan->time_base;
83 chan->time_black = -1;
84 chan->trigger_channel = -1;
85 chan->trigger_event = ROAR_ROARDMX_EVENT_STEP;
86 chan->t = -1;
87 chan->value_old = 0;
88 chan->value_trigger = 0;
89}
90
91static int __push_chan(struct slfi_channel * chan, struct slfi_channel ** array, size_t * arraylen, size_t * arrayptr) {
92 struct slfi_channel * new;
93 int err;
94 size_t i;
95
96 if ( (*arrayptr + 1) >= *arraylen ) {
97  new = roar_mm_realloc(*array, sizeof(struct slfi_channel)*(*arraylen + 8));
98  err = roar_error;
99  if ( new == NULL ) {
100   roar_mm_free(array);
101   roar_err_set(err);
102   *array = NULL;
103   *arraylen = 0;
104   *arrayptr = 0;
105   return -1;
106  }
107  for (i = *arrayptr + 1; i < (*arrayptr + 8); i++)
108   __init_chan(&(new[i]));
109  *array = new;
110  *arraylen += 8;
111 }
112
113 (*array)[*arrayptr] = *chan;
114 (*arrayptr)++;
115 return 0;
116}
117
118static int __init(struct roar_slfi_inst * inst, const struct roar_keyval * para, ssize_t paralen) {
119 const struct roar_keyval * kv;
120 ssize_t i;
121 struct slfi_channel chan;
122 struct slfi_channel * array = NULL;
123 size_t arraylen = 0;
124 size_t arrayptr = 0;
125
126 __init_chan(&chan);
127
128 for (i = 0; i < paralen; i++) {
129  kv = &(para[i]);
130  if ( kv->key == NULL || kv->value == NULL )
131   continue;
132
133  if ( !strcmp(kv->key, "channel") ) {
134   if ( chan.index != -1 ) {
135    if ( __push_chan(&chan, &array, &arraylen, &arrayptr) == -1 ) {
136     ROAR_ERR("__init(*): Can not add more channels: %s", roar_errorstring);
137     return -1;
138    }
139   }
140   chan.index = atoi(kv->value);
141  } else if ( !strcmp(kv->key, "exponent") ) {
142   chan.exponent = atof(kv->value);
143  } else if ( !strcmp(kv->key, "timebase") ) {
144   chan.time_base = roar_str2usec(kv->value);
145  } else if ( !strcmp(kv->key, "time") ) {
146   chan.time_end = roar_str2usec(kv->value);
147  } else if ( !strcmp(kv->key, "time_black_point") ) {
148   if ( !strcmp(kv->value, "none") ) {
149    chan.time_black_point = -1;
150   } else {
151    chan.time_black_point = atof(kv->value);
152    if ( chan.time_black_point <= 0. ) chan.time_black_point = -1.;
153    if ( chan.time_black_point >= 1. ) chan.time_black_point = -1.;
154   }
155  } else if ( !strcmp(kv->key, "timechannel") ) {
156   chan.time_channel = atoi(kv->value);
157  } else if ( !strcmp(kv->key, "timechannelblack") ) {
158   chan.time_channelblack = atoi(kv->value);
159  } else if ( !strcmp(kv->key, "triggerchannel") ) {
160   chan.trigger_channel = atoi(kv->value);
161  } else if ( !strcmp(kv->key, "triggerevent") ) {
162   chan.trigger_event = roar_roardmx_str2event(kv->value);
163  } else if ( !strcmp(kv->key, "function") ) {
164   chan.func = __str2func(kv->value);
165  } else {
166   ROAR_WARN("__init(*): Unknown parameter: %s", kv->key);
167  }
168 }
169
170 if ( chan.index != -1 ) {
171  if ( __push_chan(&chan, &array, &arraylen, &arrayptr) == -1 ) {
172   ROAR_ERR("__init(*): Can not add more channels: %s", roar_errorstring);
173   return -1;
174  }
175 }
176
177 inst->userdata = array;
178 return 0;
179}
180
181static double __func_calc(enum slfi_function func, double a) {
182 switch (func) {
183  case FUNC_STEP:
184     return a > 0.999 ? 1. : 0.; // allow some error here. Error should be 1/255.
185    break;
186  case FUNC_LIN:
187    return a;
188   break;
189  case FUNC_COS:
190    return (1.-cos(M_PI*a))/2.;
191   break;
192 }
193
194 // error.
195 return 1.;
196}
197
198static double __time_back_to_t1(int32_t t) {
199 if ( t > 0 )
200  return (double)t;
201 return 0;
202}
203
204static 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) {
205 int have_trigger = 0;
206 size_t i;
207 double value_old, value_new;
208 double a;
209 double t1;
210
211 if ( chan->trigger_channel >= 0 ) {
212  if ( chan->trigger_channel >= size_of_universe ) {
213   ROAR_WARN("__channel_calc(*): Universe too small for filter.");
214   have_trigger = 0;
215  } else {
216   if ( universe[chan->trigger_channel] != chan->value_trigger ) {
217    chan->value_trigger = universe[chan->trigger_channel];
218    have_trigger = 1;
219   }
220  }
221 } else {
222  for (i = 0; i < eventlen; i++) {
223   if ( event[i] == chan->trigger_event || event[i] == (chan->trigger_event | ROAR_ROARDMX_ETYPE_ON) ) {
224    have_trigger = 1;
225    break;
226   }
227  }
228 }
229
230 ROAR_DBG("__channel_calc(*): chan={.time=%li, .time_end=%li, ...}, have_trigger=%i", (long int)chan->t, (long int)chan->time_end, have_trigger);
231
232 // are we currently fading?
233 if ( chan->t < 0 && !have_trigger )
234  return chan->value_old; // no, just return current value.
235
236 // yes, keep calcing:
237 if ( have_trigger && chan->t < 0 )
238  chan->t = 0;
239
240 // recaculate timings.
241 if ( chan->time_channel >= 0 ) {
242  if ( chan->time_channel >= size_of_universe ) {
243   ROAR_WARN("__channel_calc(*): Universe too small for filter.");
244   chan->time_end = 0;
245  } else {
246   chan->time_end = (chan->time_base * (int32_t)universe[chan->time_channel]) / 255;
247  }
248 }
249
250 if ( chan->time_channelblack != -1 ) {
251  if ( chan->time_channelblack >= size_of_universe ) {
252   ROAR_WARN("__channel_calc(*): Universe too small for filter.");
253   chan->time_black = -1;
254  } else {
255   chan->time_black = (chan->time_end * (int32_t)universe[chan->time_channelblack]) / 255;
256  }
257 } else {
258  chan->time_black = chan->time_end * chan->time_black_point;
259 }
260 ROAR_DBG("__channel_calc(*): chan->time_black=%li", (long int)chan->time_black);
261
262 // calculate value.
263 if ( chan->t >= chan->time_end ) {
264  chan->t = -1;
265  a = 1.;
266  value_old = value_new = chan->value_old = value;
267 } else if ( chan->t < chan->time_black ) {
268  a = (double)chan->t / (double)chan->time_black;
269  value_old = chan->value_old;
270  value_new = 0.;
271 } else {
272  t1 = __time_back_to_t1(chan->time_black);
273  a = ((double)chan->t - t1) / ((double)chan->time_end - t1);
274  value_old = chan->time_black > 0 ? 0 : chan->value_old;
275  value_new = value;
276  if ( a > 1. )
277   a = 1.;
278 }
279
280 if ( a > 1. ) {
281  a = 1.;
282 }
283
284 if ( 0 ) { // inverse mode
285  a = 2.*a - __func_calc(chan->func, a);
286 } else { // normal mode
287  a = __func_calc(chan->func, a);
288 }
289
290 a = pow(a, chan->exponent);
291 value = value_new = (double)(value_old * (1. - a) + value_new * a);
292 if ( value_new > 255. ) value = 255;
293 if ( value_new < 0. )   value = 0;
294
295 if ( have_trigger ) {
296  chan->value_old = value;
297  chan->t = 0;
298 }
299
300 if ( chan->t != -1 )
301  chan->t += usecspassed;
302
303 return value;
304}
305
306static 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) {
307 struct slfi_channel * chan = inst->userdata;
308
309 if ( chan == NULL )
310  return 0;
311
312 while (chan->index != -1) {
313  ROAR_DBG("__update(*): chan(%p)->index=%i", chan, (int)chan->index);
314  if ( chan->index < 0 && chan->index >= size_of_universe ) {
315   ROAR_WARN("__update(*): Universe too small for filter.");
316   continue;
317  }
318  universe[chan->index] = __channel_calc(chan, universe[chan->index], usecspassed, event, eventlen, universe, size_of_universe);
319  chan++;
320 }
321
322 return 0;
323}
324
325static const struct roar_slfi_filter filter[1] = {
326 {
327  .name = "fade",
328  .description = "Fade SLFI filter",
329  .flags = ROAR_SLFI_FLAG_ON_UPDATE,
330  .init = __init,
331  .uninit = NULL,
332  .update = __update,
333  .ctl = NULL
334 }
335};
336
337ROAR_DL_PLUGIN_REG_SLFI(filter);
338
339// This is the plugin control block.
340ROAR_DL_PLUGIN_START(filter_slfi_fade) {
341 // Here we set the name and vendor of our plugin.
342 // If you have no Vendor ID you need to use ROAR_DL_PLUGIN_META_PRODUCT_NV().
343 ROAR_DL_PLUGIN_META_PRODUCT_NIV("filter-slfi-fade", ROAR_VID_ROARAUDIO, ROAR_VNAME_ROARAUDIO);
344
345 // This sets the version of your plugin.
346 ROAR_DL_PLUGIN_META_VERSION(ROAR_VERSION_STRING);
347
348 // This sets the license of your plugin.
349 // If there is no tag for the license you use you can just
350 // use ROAR_DL_PLUGIN_META_LICENSE().
351 ROAR_DL_PLUGIN_META_LICENSE_TAG(GPLv3_0);
352
353 // This sets the author and contact infos.
354 // There are several other macros to do this with other parameters.
355 // See ROAR_DL_PLUGIN_META_CONTACT*() in the header or documentation.
356 ROAR_DL_PLUGIN_META_CONTACT_FLNE("Philipp", "Schafft", "ph3-der-loewe", "lion@lion.leolix.org");
357
358 // This sets the description for your plugin.
359 ROAR_DL_PLUGIN_META_DESC("This plugin allows to fade between settings");
360
361 // Load filters.
362 ROAR_DL_PLUGIN_REG_FNFUNC(ROAR_DL_FN_FILTER);
363
364// This is the end of the control block.
365} ROAR_DL_PLUGIN_END
366
367//ll
Note: See TracBrowser for help on using the repository browser.