formats: clarify setting of reverse_bytes
[sox.git] / src / synth.c
blob3eafda746fc69dffb59b0971693d5c361153a08f
1 /* libSoX synth - Synthesizer Effect.
3 * Copyright (c) 2001-2009 SoX contributors
4 * Copyright (c) Jan 2001 Carsten Borchardt
6 * This source code is freely redistributable and may be used for any purpose.
7 * This copyright notice must be maintained. The authors are not responsible
8 * for the consequences of using this software.
10 * Except for synth types: pluck, tpdf, pinknoise, & brownnoise, and
11 * sweep types: linear, square & exp, which are:
13 * Copyright (c) 2006-2013 robs@users.sourceforge.net
15 * This library is free software; you can redistribute it and/or modify it
16 * under the terms of the GNU Lesser General Public License as published by
17 * the Free Software Foundation; either version 2.1 of the License, or (at
18 * your option) any later version.
20 * This library is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
23 * General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public License
26 * along with this library; if not, write to the Free Software Foundation,
27 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30 #include "sox_i.h"
32 #include <string.h>
33 #include <ctype.h>
35 typedef enum {
36 synth_sine,
37 synth_square,
38 synth_sawtooth,
39 synth_triangle,
40 synth_trapezium,
41 synth_trapetz = synth_trapezium, /* Deprecated name for trapezium */
42 synth_exp,
43 /* Tones above, noises below */
44 synth_whitenoise,
45 synth_noise = synth_whitenoise, /* Just a handy alias */
46 synth_tpdfnoise,
47 synth_pinknoise,
48 synth_brownnoise,
49 synth_pluck
50 } type_t;
52 static lsx_enum_item const synth_type[] = {
53 LSX_ENUM_ITEM(synth_, sine)
54 LSX_ENUM_ITEM(synth_, square)
55 LSX_ENUM_ITEM(synth_, sawtooth)
56 LSX_ENUM_ITEM(synth_, triangle)
57 LSX_ENUM_ITEM(synth_, trapezium)
58 LSX_ENUM_ITEM(synth_, trapetz)
59 LSX_ENUM_ITEM(synth_, exp)
60 LSX_ENUM_ITEM(synth_, whitenoise)
61 LSX_ENUM_ITEM(synth_, noise)
62 LSX_ENUM_ITEM(synth_, tpdfnoise)
63 LSX_ENUM_ITEM(synth_, pinknoise)
64 LSX_ENUM_ITEM(synth_, brownnoise)
65 LSX_ENUM_ITEM(synth_, pluck)
66 {0, 0}
69 typedef enum {synth_create, synth_mix, synth_amod, synth_fmod} combine_t;
71 static lsx_enum_item const combine_type[] = {
72 LSX_ENUM_ITEM(synth_, create)
73 LSX_ENUM_ITEM(synth_, mix)
74 LSX_ENUM_ITEM(synth_, amod)
75 LSX_ENUM_ITEM(synth_, fmod)
76 {0, 0}
81 typedef enum {Linear, Square, Exp, Exp_cycle} sweep_t;
83 typedef struct {
84 /* options */
85 type_t type;
86 combine_t combine;
87 double freq, freq2, mult;
88 sweep_t sweep;
89 double offset, phase;
90 double p1, p2, p3; /* Use depends on synth type */
92 /* internal stuff */
93 double lp_last_out, hp_last_out, hp_last_in, ap_last_out, ap_last_in;
94 double cycle_start_time_s, c0, c1, c2, c3, c4, c5, c6;
96 double * buffer;
97 size_t buffer_len, pos;
98 } channel_t;
102 /* Private data for the synthesizer */
103 typedef struct {
104 char * length_str;
105 channel_t * getopts_channels;
106 size_t getopts_nchannels;
107 uint64_t samples_done;
108 uint64_t samples_to_do;
109 channel_t * channels;
110 size_t number_of_channels;
111 sox_bool no_headroom;
112 double gain;
113 } priv_t;
117 static void create_channel(channel_t * chan)
119 memset(chan, 0, sizeof(*chan));
120 chan->freq2 = chan->freq = 440;
121 chan->p3 = chan->p2 = chan->p1 = -1;
126 static void set_default_parameters(channel_t * chan)
128 switch (chan->type) {
129 case synth_square: /* p1 is pulse width */
130 if (chan->p1 < 0)
131 chan->p1 = 0.5; /* default to 50% duty cycle */
132 break;
134 case synth_triangle: /* p1 is position of maximum */
135 if (chan->p1 < 0)
136 chan->p1 = 0.5;
137 break;
139 case synth_trapezium:
140 /* p1 is length of rising slope,
141 * p2 position where falling slope begins
142 * p3 position of end of falling slope
144 if (chan->p1 < 0) {
145 chan->p1 = 0.1;
146 chan->p2 = 0.5;
147 chan->p3 = 0.6;
148 } else if (chan->p2 < 0) { /* try a symmetric waveform */
149 if (chan->p1 <= 0.5) {
150 chan->p2 = (1 - 2 * chan->p1) / 2;
151 chan->p3 = chan->p2 + chan->p1;
152 } else {
153 /* symetric is not possible, fall back to asymmetrical triangle */
154 chan->p2 = chan->p1;
155 chan->p3 = 1;
157 } else if (chan->p3 < 0)
158 chan->p3 = 1; /* simple falling slope to the end */
159 break;
161 case synth_exp:
162 if (chan->p1 < 0) /* p1 is position of maximum */
163 chan->p1 = 0.5;
164 if (chan->p2 < 0) /* p2 is amplitude */
165 chan->p2 = .5;
166 break;
168 case synth_pluck:
169 if (chan->p1 < 0)
170 chan->p1 = .4;
171 if (chan->p2 < 0)
172 chan->p2 = .2, chan->p3 = .9;
174 default: break;
180 #undef NUMERIC_PARAMETER
181 #define NUMERIC_PARAMETER(p, min, max) { \
182 char * end_ptr_np; \
183 double d_np = strtod(argv[argn], &end_ptr_np); \
184 if (end_ptr_np == argv[argn]) \
185 break; \
186 if (d_np < min || d_np > max || *end_ptr_np != '\0') { \
187 lsx_fail("parameter error"); \
188 return SOX_EOF; \
190 chan->p = d_np / 100; /* adjust so abs(parameter) <= 1 */\
191 if (++argn == argc) \
192 break; \
197 static int getopts(sox_effect_t * effp, int argc, char **argv)
199 priv_t * p = (priv_t *) effp->priv;
200 channel_t master, * chan = &master;
201 int key = INT_MAX, argn = 0;
202 char dummy, * end_ptr;
203 const char *n;
204 --argc, ++argv;
206 if (argc && !strcmp(*argv, "-n")) p->no_headroom = sox_true, ++argv, --argc;
208 if (argc > 1 && !strcmp(*argv, "-j") && (
209 sscanf(argv[1], "%i %c", &key, &dummy) == 1 || (
210 (key = lsx_parse_note(argv[1], &end_ptr)) != INT_MAX &&
211 !*end_ptr))) {
212 argc -= 2;
213 argv += 2;
216 /* Get duration if given (if first arg starts with digit) */
217 if (argc && (isdigit((int)argv[argn][0]) || argv[argn][0] == '.')) {
218 p->length_str = lsx_strdup(argv[argn]);
219 /* Do a dummy parse of to see if it will fail */
220 n = lsx_parsesamples(0., p->length_str, &p->samples_to_do, 't');
221 if (!n || *n)
222 return lsx_usage(effp);
223 argn++;
226 create_channel(chan);
227 if (argn < argc) { /* [off [ph [p1 [p2 [p3]]]]]] */
228 do { /* break-able block */
229 NUMERIC_PARAMETER(offset,-100, 100)
230 NUMERIC_PARAMETER(phase , 0, 100)
231 NUMERIC_PARAMETER(p1, 0, 100)
232 NUMERIC_PARAMETER(p2, 0, 100)
233 NUMERIC_PARAMETER(p3, 0, 100)
234 } while (0);
237 while (argn < argc) { /* type [combine] [f1[-f2] [off [ph [p1 [p2 [p3]]]]]] */
238 lsx_enum_item const * enum_p = lsx_find_enum_text(argv[argn], synth_type, lsx_find_enum_item_case_sensitive);
240 if (enum_p == NULL) {
241 lsx_fail("no type given");
242 return SOX_EOF;
244 p->getopts_channels = lsx_realloc(p->getopts_channels, sizeof(*p->getopts_channels) * (p->getopts_nchannels + 1));
245 chan = &p->getopts_channels[p->getopts_nchannels++];
246 memcpy(chan, &master, sizeof(*chan));
247 chan->type = enum_p->value;
248 if (++argn == argc)
249 break;
251 /* maybe there is a combine-type in next arg */
252 enum_p = lsx_find_enum_text(argv[argn], combine_type, lsx_find_enum_item_case_sensitive);
253 if (enum_p != NULL) {
254 chan->combine = enum_p->value;
255 if (++argn == argc)
256 break;
259 /* read frequencies if given */
260 if (!lsx_find_enum_text(argv[argn], synth_type, lsx_find_enum_item_case_sensitive) &&
261 argv[argn][0] != '-') {
262 static const char sweeps[] = ":+/-";
264 chan->freq2 = chan->freq = lsx_parse_frequency_k(argv[argn], &end_ptr, key);
265 if (chan->freq < (chan->type == synth_pluck? 27.5 : 0) ||
266 (chan->type == synth_pluck && chan->freq > 4220)) {
267 lsx_fail("invalid freq");
268 return SOX_EOF;
270 if (*end_ptr && strchr(sweeps, *end_ptr)) { /* freq2 given? */
271 if (chan->type >= synth_noise) {
272 lsx_fail("can't sweep this type");
273 return SOX_EOF;
275 chan->sweep = strchr(sweeps, *end_ptr) - sweeps;
276 chan->freq2 = lsx_parse_frequency_k(end_ptr + 1, &end_ptr, key);
277 if (chan->freq2 < 0) {
278 lsx_fail("invalid freq2");
279 return SOX_EOF;
281 if (p->length_str == NULL) {
282 lsx_fail("duration must be given when using freq2");
283 return SOX_EOF;
286 if (*end_ptr) {
287 lsx_fail("frequency: invalid trailing character");
288 return SOX_EOF;
290 if (chan->sweep >= Exp && chan->freq * chan->freq2 == 0) {
291 lsx_fail("invalid frequency for exponential sweep");
292 return SOX_EOF;
295 if (++argn == argc)
296 break;
299 /* read rest of parameters */
300 do { /* break-able block */
301 NUMERIC_PARAMETER(offset,-100, 100)
302 NUMERIC_PARAMETER(phase , 0, 100)
303 NUMERIC_PARAMETER(p1, 0, 100)
304 NUMERIC_PARAMETER(p2, 0, 100)
305 NUMERIC_PARAMETER(p3, 0, 100)
306 } while (0);
309 /* If no channel parameters were given, create one default channel: */
310 if (!p->getopts_nchannels) {
311 p->getopts_channels = lsx_malloc(sizeof(*p->getopts_channels));
312 memcpy(&p->getopts_channels[0], &master, sizeof(channel_t));
313 ++p->getopts_nchannels;
316 if (!effp->in_signal.channels)
317 effp->in_signal.channels = p->getopts_nchannels;
319 return SOX_SUCCESS;
324 static int start(sox_effect_t * effp)
326 priv_t * p = (priv_t *)effp->priv;
327 size_t i, j, k;
329 p->samples_done = 0;
331 if (p->length_str) {
332 if (lsx_parsesamples(effp->in_signal.rate, p->length_str, &p->samples_to_do, 't') == NULL)
333 return lsx_usage(effp);
334 } else
335 p->samples_to_do = effp->in_signal.length != SOX_UNKNOWN_LEN ?
336 effp->in_signal.length / effp->in_signal.channels : 0;
338 p->number_of_channels = effp->in_signal.channels;
339 p->channels = lsx_calloc(p->number_of_channels, sizeof(*p->channels));
340 for (i = 0; i < p->number_of_channels; ++i) {
341 channel_t * chan = &p->channels[i];
342 *chan = p->getopts_channels[i % p->getopts_nchannels];
343 set_default_parameters(chan);
344 if (chan->type == synth_pluck) {
345 double min, max, frac, p2;
347 /* Low pass: */
348 double const decay_rate = -2; /* dB / s */
349 double const decay_f = min(912, 266 + 106 * log(chan->freq));
350 double d = sqr(dB_to_linear(decay_rate / chan->freq));
351 d = (d * cos(2 * M_PI * decay_f / effp->in_signal.rate) - 1) / (d - 1);
352 chan->c0 = d - sqrt(d * d - 1);
353 chan->c1 = 1 - chan->c0;
355 /* Single-pole low pass is very rate-dependent: */
356 if (effp->in_signal.rate < 44100 || effp->in_signal.rate > 48000) {
357 lsx_fail(
358 "sample rate for pluck must be 44100-48000; use `rate' to resample");
359 return SOX_EOF;
361 /* Decay: */
362 chan->c1 *= exp(-2e4/ (.05+chan->p1)/ chan->freq/ effp->in_signal.rate);
364 /* High pass (DC-block): */
365 chan->c2 = exp(-2 * M_PI * 10 / effp->in_signal.rate);
366 chan->c3 = (1 + chan->c2) * .5;
368 /* All pass (for fractional delay): */
369 d = chan->c0 / (chan->c0 + chan->c1);
370 chan->buffer_len = effp->in_signal.rate / chan->freq - d;
371 frac = effp->in_signal.rate / chan->freq - d - chan->buffer_len;
372 chan->c4 = (1 - frac) / (1 + frac);
373 chan->pos = 0;
375 /* Exitation: */
376 chan->buffer = lsx_calloc(chan->buffer_len, sizeof(*chan->buffer));
377 for (k = 0, p2 = chan->p2; k < 2 && p2 >= 0; ++k, p2 = chan->p3) {
378 double d1 = 0, d2, colour = pow(2., 4 * (p2 - 1));
379 int32_t r = p2 * 100 + .5;
380 for (j = 0; j < chan->buffer_len; ++j) {
381 do d2 = d1 + (chan->phase? DRANQD1:dranqd1(r)) * colour;
382 while (fabs(d2) > 1);
383 chan->buffer[j] += d2 * (1 - .3 * k);
384 d1 = d2 * (colour != 1);
385 #ifdef TEST_PLUCK
386 chan->buffer[j] = sin(2 * M_PI * j / chan->buffer_len);
387 #endif
391 /* In-delay filter graduation: */
392 for (j = 0, min = max = 0; j < chan->buffer_len; ++j) {
393 double d2, t = (double)j / chan->buffer_len;
394 chan->lp_last_out = d2 =
395 chan->buffer[j] * chan->c1 + chan->lp_last_out * chan->c0;
397 chan->ap_last_out =
398 d2 * chan->c4 + chan->ap_last_in - chan->ap_last_out * chan->c4;
399 chan->ap_last_in = d2;
401 chan->buffer[j] = chan->buffer[j] * (1 - t) + chan->ap_last_out * t;
402 min = min(min, chan->buffer[j]);
403 max = max(max, chan->buffer[j]);
406 /* Normalise: */
407 for (j = 0, d = 0; j < chan->buffer_len; ++j) {
408 chan->buffer[j] = (2 * chan->buffer[j] - max - min) / (max - min);
409 d += sqr(chan->buffer[j]);
411 lsx_debug("rms=%f c0=%f c1=%f df=%f d3f=%f c2=%f c3=%f c4=%f frac=%f",
412 10 * log(d / chan->buffer_len), chan->c0, chan->c1, decay_f,
413 log(chan->c0)/ -2 / M_PI * effp->in_signal.rate,
414 chan->c2, chan->c3, chan->c4, frac);
416 switch (chan->sweep) {
417 case Linear: chan->mult = p->samples_to_do?
418 (chan->freq2 - chan->freq) / p->samples_to_do / 2 : 0;
419 break;
420 case Square: chan->mult = p->samples_to_do?
421 sqrt(fabs(chan->freq2 - chan->freq)) / p->samples_to_do / sqrt(3.) : 0;
422 if (chan->freq > chan->freq2)
423 chan->mult = -chan->mult;
424 break;
425 case Exp: chan->mult = p->samples_to_do?
426 log(chan->freq2 / chan->freq) / p->samples_to_do * effp->in_signal.rate : 1;
427 chan->freq /= chan->mult;
428 break;
429 case Exp_cycle: chan->mult = p->samples_to_do?
430 (log(chan->freq2) - log(chan->freq)) / p->samples_to_do : 1;
431 break;
433 lsx_debug("type=%s, combine=%s, samples_to_do=%" PRIu64 ", f1=%g, f2=%g, "
434 "offset=%g, phase=%g, p1=%g, p2=%g, p3=%g mult=%g",
435 lsx_find_enum_value(chan->type, synth_type)->text,
436 lsx_find_enum_value(chan->combine, combine_type)->text,
437 p->samples_to_do, chan->freq, chan->freq2,
438 chan->offset, chan->phase, chan->p1, chan->p2, chan->p3, chan->mult);
440 p->gain = 1;
441 effp->out_signal.mult = p->no_headroom? NULL : &p->gain;
442 effp->out_signal.length = p->samples_to_do ?
443 p->samples_to_do * effp->out_signal.channels : SOX_UNKNOWN_LEN;
444 return SOX_SUCCESS;
447 #define elapsed_time_s p->samples_done / effp->in_signal.rate
449 static int flow(sox_effect_t * effp, const sox_sample_t * ibuf, sox_sample_t * obuf,
450 size_t * isamp, size_t * osamp)
452 priv_t * p = (priv_t *) effp->priv;
453 unsigned len = min(*isamp, *osamp) / effp->in_signal.channels;
454 unsigned c, done;
455 int result = SOX_SUCCESS;
457 for (done = 0; done < len && result == SOX_SUCCESS; ++done) {
458 for (c = 0; c < effp->in_signal.channels; c++) {
459 sox_sample_t synth_input = *ibuf++;
460 channel_t * chan = &p->channels[c];
461 double synth_out; /* [-1, 1] */
463 if (chan->type < synth_noise) { /* Need to calculate phase: */
464 double phase; /* [0, 1) */
465 switch (chan->sweep) {
466 case Linear:
467 phase = (chan->freq + p->samples_done * chan->mult) *
468 elapsed_time_s;
469 break;
470 case Square:
471 phase = (chan->freq + sign(chan->mult) *
472 sqr(p->samples_done * chan->mult)) * elapsed_time_s;
473 break;
474 case Exp:
475 phase = chan->freq * exp(chan->mult * elapsed_time_s);
476 break;
477 case Exp_cycle: default: {
478 double f = chan->freq * exp(p->samples_done * chan->mult);
479 double cycle_elapsed_time_s = elapsed_time_s - chan->cycle_start_time_s;
480 if (f * cycle_elapsed_time_s >= 1) { /* move to next cycle */
481 chan->cycle_start_time_s += 1 / f;
482 cycle_elapsed_time_s = elapsed_time_s - chan->cycle_start_time_s;
484 phase = f * cycle_elapsed_time_s;
485 break;
488 phase = fmod(phase + chan->phase, 1.0);
490 switch (chan->type) {
491 case synth_sine:
492 synth_out = sin(2 * M_PI * phase);
493 break;
495 case synth_square:
496 /* |_______ | +1
497 * | | |
498 * |_______|__________| 0
499 * | | |
500 * | |__________| -1
501 * | |
502 * 0 p1 1
504 synth_out = -1 + 2 * (phase < chan->p1);
505 break;
507 case synth_sawtooth:
508 /* | __| +1
509 * | __/ |
510 * |_______/_____| 0
511 * | __/ |
512 * |_/ | -1
513 * | |
514 * 0 1
516 synth_out = -1 + 2 * phase;
517 break;
519 case synth_triangle:
520 /* | . | +1
521 * | / \ |
522 * |__/___\__| 0
523 * | / \ |
524 * |/ \| -1
525 * | |
526 * 0 p1 1
529 if (phase < chan->p1)
530 synth_out = -1 + 2 * phase / chan->p1; /* In rising part of period */
531 else
532 synth_out = 1 - 2 * (phase - chan->p1) / (1 - chan->p1); /* In falling part */
533 break;
535 case synth_trapezium:
536 /* | ______ |+1
537 * | / \ |
538 * |__/________\___________| 0
539 * | / \ |
540 * |/ \_________|-1
541 * | |
542 * 0 p1 p2 p3 1
544 if (phase < chan->p1) /* In rising part of period */
545 synth_out = -1 + 2 * phase / chan->p1;
546 else if (phase < chan->p2) /* In high part of period */
547 synth_out = 1;
548 else if (phase < chan->p3) /* In falling part */
549 synth_out = 1 - 2 * (phase - chan->p2) / (chan->p3 - chan->p2);
550 else /* In low part of period */
551 synth_out = -1;
552 break;
554 case synth_exp:
555 /* | | | +1
556 * | | | |
557 * | _| |_ | 0
558 * | __- -__ |
559 * |____--- ---____ | f(p2)
560 * | |
561 * 0 p1 1
563 synth_out = dB_to_linear(chan->p2 * -200); /* 0 .. 1 */
564 if (phase < chan->p1)
565 synth_out = synth_out * exp(phase * log(1 / synth_out) / chan->p1);
566 else
567 synth_out = synth_out * exp((1 - phase) * log(1 / synth_out) / (1 - chan->p1));
568 synth_out = synth_out * 2 - 1; /* map 0 .. 1 to -1 .. +1 */
569 break;
571 default: synth_out = 0;
573 } else switch (chan->type) {
574 case synth_whitenoise:
575 synth_out = DRANQD1;
576 break;
578 case synth_tpdfnoise:
579 synth_out = .5 * (DRANQD1 + DRANQD1);
580 break;
582 case synth_pinknoise: { /* "Paul Kellet's refined method" */
583 #define _ .125 / (65536. * 32768.)
584 double d = RANQD1;
585 chan->c0 = .99886 * chan->c0 + d * (.0555179*_);
586 chan->c1 = .99332 * chan->c1 + d * (.0750759*_);
587 chan->c2 = .96900 * chan->c2 + d * (.1538520*_);
588 chan->c3 = .86650 * chan->c3 + d * (.3104856*_);
589 chan->c4 = .55000 * chan->c4 + d * (.5329522*_);
590 chan->c5 = -.7616 * chan->c5 - d * (.0168980*_);
591 synth_out = chan->c0 + chan->c1 + chan->c2 + chan->c3
592 + chan->c4 + chan->c5 + chan->c6 + d * (.5362*_);
593 chan->c6 = d * (.115926*_);
594 break;
595 #undef _
598 case synth_brownnoise:
599 do synth_out = chan->lp_last_out + DRANQD1 * (1. / 16);
600 while (fabs(synth_out) > 1);
601 chan->lp_last_out = synth_out;
602 break;
604 case synth_pluck: {
605 double d = chan->buffer[chan->pos];
607 chan->hp_last_out =
608 (d - chan->hp_last_in) * chan->c3 + chan->hp_last_out * chan->c2;
609 chan->hp_last_in = d;
611 synth_out = range_limit(chan->hp_last_out, -1, 1);
613 chan->lp_last_out = d = d * chan->c1 + chan->lp_last_out * chan->c0;
615 chan->ap_last_out = chan->buffer[chan->pos] =
616 (d - chan->ap_last_out) * chan->c4 + chan->ap_last_in;
617 chan->ap_last_in = d;
619 chan->pos = chan->pos + 1 == chan->buffer_len? 0 : chan->pos + 1;
620 break;
623 default: synth_out = 0;
626 /* Add offset, but prevent clipping: */
627 synth_out = synth_out * (1 - fabs(chan->offset)) + chan->offset;
629 switch (chan->combine) {
630 case synth_create: synth_out *= SOX_SAMPLE_MAX; break;
631 case synth_mix : synth_out = (synth_out * SOX_SAMPLE_MAX + synth_input) * .5; break;
632 case synth_amod : synth_out = (synth_out + 1) * synth_input * .5; break;
633 case synth_fmod : synth_out *= synth_input; break;
635 *obuf++ = synth_out < 0? synth_out * p->gain - .5 : synth_out * p->gain + .5;
637 if (++p->samples_done == p->samples_to_do)
638 result = SOX_EOF;
640 *isamp = *osamp = done * effp->in_signal.channels;
641 return result;
646 static int stop(sox_effect_t * effp)
648 priv_t * p = (priv_t *) effp->priv;
649 size_t i;
651 for (i = 0; i < p->number_of_channels; ++i)
652 free(p->channels[i].buffer);
653 free(p->channels);
654 return SOX_SUCCESS;
659 static int lsx_kill(sox_effect_t * effp)
661 priv_t * p = (priv_t *) effp->priv;
662 free(p->getopts_channels);
663 free(p->length_str);
664 return SOX_SUCCESS;
669 const sox_effect_handler_t *lsx_synth_effect_fn(void)
671 static sox_effect_handler_t handler = {
672 "synth", "[-j KEY] [-n] [length [offset [phase [p1 [p2 [p3]]]]]]] {type [combine] [[%]freq[k][:|+|/|-[%]freq2[k]] [offset [phase [p1 [p2 [p3]]]]]]}",
673 SOX_EFF_MCHAN | SOX_EFF_LENGTH | SOX_EFF_GAIN,
674 getopts, start, flow, 0, stop, lsx_kill, sizeof(priv_t)
676 return &handler;