ref: 8de54df09bdcce676c59651ba9e6661ad2ad5d85
parent: 880f442273d4bf1c510bae8b4abb2befbd687bd6
author: robs <robs>
date: Sat Dec 9 17:45:33 EST 2006
New flanger & rationalise sine tables generation
--- a/AUTHORS
+++ b/AUTHORS
@@ -89,5 +89,5 @@
Much cleaning up, Secret Rabbit Code resampling.
Rob Sykes robs@users.sourceforge.net
Bass & treble effects, FLAC support, initial 24bit
- support, Octave plotting for filters,
+ support, Octave plotting for filters, new flanger,
various small fixes, enchancements and clean-ups.
--- a/sox.1
+++ b/sox.1
@@ -1074,13 +1074,38 @@
For more discussion of beta, look under the \fBresample\fR effect.
.TP 10
-flanger \fIgain-in gain-out delay decay speed\fR < -s | -t >
-Add a flanger to a sound sample. Each triple
-delay/decay/speed gives the delay in milliseconds
-and the decay (relative to gain-in) with a modulation
-speed in Hz.
-The modulation is either sinusoidal (-s) or triangular
-(-t). Gain-out is the volume of the output.
+flanger [\fIdelay depth regen width speed shape phase interp\fR]
+Apply a flanging effect to the signal.
+All parameters are optional (right to left).
+
+ RANGE DEFAULT DESCRIPTION
+.RS
+.TP 21
+\fIdelay\fR 0 10 0
+Base delay in milliseconds.
+.TP 21
+\fIdepth\fR 0 10 2
+Added swept delay in milliseconds.
+.TP 21
+\fIregen\fR -95 +95 0
+Percentage regeneration (delayed signal feedback).
+.TP 21
+\fIwidth\fR 0 100 71
+Percentage of delayed signal mixed with original.
+.TP 21
+\fIspeed\fR 0.1 10 0.5
+Sweeps per second (Hz).
+.TP 21
+\fIshape\fR -- sin
+Swept wave shape: sine | triangle.
+.TP 21
+\fIphase\fR 0 100 25
+Swept wave percentage phase-shift for multi-channel
+(e.g. stereo) flange; 0 = 100 = same phase on each channel.
+.TP 21
+\fIinterp\fR -- lin
+Delay-line interpolation: linear | quadratic.
+.RE
.TP 10
highp|lowp \fIfrequency\fR
Apply a single-pole recursive high-pass or low-pass filter with
--- a/soxexam.1
+++ b/soxexam.1
@@ -340,28 +340,17 @@
The flanger effect is widely used in funk and soul music, where the guitar
sound varies frequently slow or a bit faster.
.P
-The typical delay is around 3ms to 5ms, the speed of the modulation is best
-near 0.5Hz.
+Enter \fIsox --help-effect flanger\fR to see the default settings.
.P
Now, let's groove the sample:
.P
.BR
- play file.xxx flanger 0.6 0.87 3.0 0.9 0.5 -s
+ play file.xxx flanger
.P
listen carefully between the difference of sinusoidal and triangular modulation:
.P
.BR
- play file.xxx flanger 0.6 0.87 3.0 0.9 0.5 -t
-.P
-If the decay is a bit lower, than the effect sounds more popular:
-.P
-.BR
- play file.xxx flanger 0.8 0.88 3.0 0.4 0.5 -t
-.P
-The drunken loudspeaker system:
-.P
-.BR
- play file.xxx flanger 0.9 0.9 4.0 0.23 1.3 -s
+ play file.xxx flanger triangle
.P
.B Reverb
.P
--- a/src/chorus.c
+++ b/src/chorus.c
@@ -213,14 +213,14 @@
sizeof(int) * chorus->length[i]);
return (ST_EOF);
}
- if ( chorus->modulation[i] == MOD_SINE )
- st_sine(chorus->lookup_tab[i], chorus->length[i],
- chorus->depth_samples[i] - 1,
- chorus->depth_samples[i]);
- else
- st_triangle(chorus->lookup_tab[i], chorus->length[i],
- chorus->samples[i] - 1,
- chorus->depth_samples[i]);
+ if (chorus->modulation[i] == MOD_SINE)
+ st_generate_wave_table(ST_WAVE_SINE, ST_INT, chorus->lookup_tab[i],
+ chorus->length[i], 0, chorus->depth_samples[i], 0);
+ else
+ st_generate_wave_table(ST_WAVE_TRIANGLE, ST_INT, chorus->lookup_tab[i],
+ chorus->length[i],
+ chorus->samples[i] - 1 - 2 * chorus->depth_samples[i],
+ chorus->samples[i] - 1, 3 * M_PI_2);
chorus->phase[i] = 0;
if ( chorus->samples[i] > chorus->maxsamples )
--- a/src/flanger.c
+++ b/src/flanger.c
@@ -1,306 +1,326 @@
-/*
- * August 24, 1998
- * Copyright (C) 1998 Juergen Mueller And Sundry Contributors
- * This source code is freely redistributable and may be used for
- * any purpose. This copyright notice must be maintained.
- * Juergen Mueller And Sundry Contributors are not responsible for
- * the consequences of using this software.
- */
-
-/*
- * Flanger effect.
- *
- * Flow diagram scheme:
+/* Sound Tools Effect: Stereo Flanger
*
- * * gain-in ___
- * ibuff -----+--------------------------------------------->| |
- * | _______ | |
- * | | | * decay | |
- * +---->| delay |------------------------------->| + |
- * |_______| | |
- * /|\ | |
- * | |___|
- * | |
- * +---------------+ +------------------+ | * gain-out
- * | Delay control |<-----| modulation speed | |
- * +---------------+ +------------------+ +----->obuff
+ * (c) 2006 robs@users.sourceforge.net
*
- *
- * The delay is controled by a sine or triangle modulation.
- *
- * Usage:
- * flanger gain-in gain-out delay decay speed [ -s | -t ]
- *
- * Where:
- * gain-in, decay : 0.0 ... 1.0 volume
- * gain-out : 0.0 ... volume
- * delay : 0.0 ... 5.0 msec
- * speed : 0.1 ... 2.0 Hz modulation
- * -s : modulation by sine (default)
- * -t : modulation by triangle
- *
- * Note:
- * when decay is close to 1.0, the samples may begin clipping or the output
- * can saturate!
- *
- * Hint:
- * 1 / out-gain > gain-in * ( 1 + decay )
- *
-*/
-
-/*
- * Sound Tools flanger effect file.
+ * See LICENSE file for further copyright information.
*/
-#include <stdlib.h> /* Harmless, and prototypes atof() etc. --dgc */
+static char const st_flanger_usage[] =
+ "Usage: \n"
+ " .\n"
+ " /|regen\n"
+ " / |\n"
+ " +--( |------------+\n"
+ " | \\ | | .\n"
+ " _V_ \\| _______ | |\\ width ___\n"
+ " | | ' | | | | \\ | |\n"
+ " +-->| + |---->| DELAY |--+-->| )----->| |\n"
+ " | |___| |_______| | / | |\n"
+ " | delay : depth |/ | |\n"
+ " In | : interp ' | | Out\n"
+ " --->+ __:__ | + |--->\n"
+ " | | |speed | |\n"
+ " | | ~ |shape | |\n"
+ " | |_____|phase | |\n"
+ " +------------------------------------->| |\n"
+ " |___|\n"
+ "\n"
+ "Usage: flanger [delay depth regen width speed shape phase interp]\n"
+ "\n"
+ " RANGE DEFAULT DESCRIPTION\n"
+ "delay 0 10 0 base delay in milliseconds\n"
+ "depth 0 10 2 added swept delay in milliseconds\n"
+ "regen -95 +95 0 percentage regeneration (delayed signal feedback)\n"
+ "width 0 100 71 percentage of delayed signal mixed with original\n"
+ "speed 0.1 10 0.5 sweeps per second (Hz) \n"
+ "shape -- sin swept wave shape: sine|triangle\n"
+ "phase 0 100 25 swept wave percentage phase-shift for multi-channel\n"
+ " (e.g. stereo) flange; 0 = 100 = same phase on each channel\n"
+ "interp -- lin delay-line interpolation: linear|quadratic";
+
+/* TODO: Slide in the delay at the start? */
+
+
+
+#include "st_i.h"
#include <math.h>
#include <string.h>
-#include "st_i.h"
-static st_effect_t st_flanger_effect;
-#define MOD_SINE 0
-#define MOD_TRIANGLE 1
-/* Private data for SKEL file */
-typedef struct flangerstuff {
- int modulation;
- int counter;
- int phase;
- double *flangerbuf;
- float in_gain, out_gain;
- float delay, decay;
- float speed;
- st_size_t length;
- int *lookup_tab;
- st_size_t maxsamples, fade_out;
-} *flanger_t;
+typedef enum {INTERP_LINEAR, INTERP_QUADRATIC} interp_t;
-/* Private data for SKEL file */
+#define MAX_CHANNELS 4
-/*
- * Process options
- */
-int st_flanger_getopts(eff_t effp, int n, char **argv)
-{
- flanger_t flanger = (flanger_t) effp->priv;
- if (!((n == 5) || (n == 6)))
- {
- st_fail(st_flanger_effect.usage);
- return (ST_EOF);
- }
- sscanf(argv[0], "%f", &flanger->in_gain);
- sscanf(argv[1], "%f", &flanger->out_gain);
- sscanf(argv[2], "%f", &flanger->delay);
- sscanf(argv[3], "%f", &flanger->decay);
- sscanf(argv[4], "%f", &flanger->speed);
- flanger->modulation = MOD_SINE;
- if ( n == 6 ) {
- if ( !strcmp(argv[5], "-s"))
- flanger->modulation = MOD_SINE;
- else if ( ! strcmp(argv[5], "-t"))
- flanger->modulation = MOD_TRIANGLE;
- else
- {
- st_fail(st_flanger_effect.usage);
- return (ST_EOF);
- }
- }
- return (ST_SUCCESS);
+typedef struct flanger {
+ /* Parameters */
+ double delay_min;
+ double delay_depth;
+ double feedback_gain;
+ double delay_gain;
+ double speed;
+ st_wave_t wave_shape;
+ double channel_phase;
+ interp_t interpolation;
+
+ /* Delay buffers */
+ double * delay_bufs[MAX_CHANNELS];
+ st_size_t delay_buf_length;
+ st_size_t delay_buf_pos;
+ double delay_last[MAX_CHANNELS];
+
+ /* Low Frequency Oscillator */
+ float * lfo;
+ st_size_t lfo_length;
+ st_size_t lfo_pos;
+
+ /* Balancing */
+ double in_gain;
+} * flanger_t;
+
+assert_static(sizeof(struct flanger) <= ST_MAX_EFFECT_PRIVSIZE,
+ /* else */ flanger_PRIVSIZE_too_big);
+
+
+
+static enum_item const interp_enum[] = {
+ ENUM_ITEM(INTERP_,LINEAR)
+ ENUM_ITEM(INTERP_,QUADRATIC)
+ {0}};
+
+
+
+#define NUMERIC_PARAMETER(p, min, max) { \
+ char * end_ptr; \
+ double d; \
+ if (argc == 0) break; \
+ d = strtod(*argv, &end_ptr); \
+ if (end_ptr != *argv) { \
+ if (d < min || d > max || *end_ptr != '\0') { \
+ st_fail(effp->h->usage); \
+ return ST_EOF; \
+ } \
+ f->p = d; \
+ --argc, ++argv; \
+ } \
+}
+
+
+
+#define TEXTUAL_PARAMETER(p, enum_table) { \
+ enum_item const * e; \
+ if (argc == 0) break; \
+ e = find_enum_text(*argv, enum_table); \
+ if (e != NULL) { \
+ f->p = e->value; \
+ --argc, ++argv; \
+ } \
}
-/*
- * Prepare for processing.
- */
-int st_flanger_start(eff_t effp)
+
+
+static int st_flanger_getopts(eff_t effp, int argc, char const * const * argv)
{
- flanger_t flanger = (flanger_t) effp->priv;
- unsigned int i;
+ flanger_t f = (flanger_t) effp->priv;
- flanger->maxsamples = flanger->delay * effp->ininfo.rate / 1000.0;
+ /* Set non-zero defaults: */
+ f->delay_depth = 2;
+ f->delay_gain = 71;
+ f->speed = 0.5;
+ f->channel_phase= 25;
- if ( flanger->in_gain < 0.0 )
- {
- st_fail("flanger: gain-in must be positive!");
- return (ST_EOF);
- }
- if ( flanger->in_gain > 1.0 )
- {
- st_fail("flanger: gain-in must be less than 1.0!");
- return (ST_EOF);
- }
- if ( flanger->out_gain < 0.0 )
- {
- st_fail("flanger: gain-out must be positive!");
- return (ST_EOF);
- }
- if ( flanger->delay < 0.0 )
- {
- st_fail("flanger: delay must be positive!");
- return (ST_EOF);
- }
- if ( flanger->delay > 5.0 )
- {
- st_fail("flanger: delay must be less than 5.0 msec!");
- return (ST_EOF);
- }
- if ( flanger->speed < 0.1 )
- {
- st_fail("flanger: speed must be more than 0.1 Hz!");
- return (ST_EOF);
- }
- if ( flanger->speed > 2.0 )
- {
- st_fail("flanger: speed must be less than 2.0 Hz!");
- return (ST_EOF);
- }
- if ( flanger->decay < 0.0 )
- {
- st_fail("flanger: decay must be positive!" );
- return (ST_EOF);
- }
- if ( flanger->decay > 1.0 )
- {
- st_fail("flanger: decay must be less that 1.0!" );
- return (ST_EOF);
- }
- /* Be nice and check the hint with warning, if... */
- if ( flanger->in_gain * ( 1.0 + flanger->decay ) > 1.0 / flanger->out_gain )
- st_warn("flanger: warning >>> gain-out can cause saturation or clipping of output <<<");
+ do { /* break-able block */
+ NUMERIC_PARAMETER(delay_min , 0 , 10 )
+ NUMERIC_PARAMETER(delay_depth , 0 , 10 )
+ NUMERIC_PARAMETER(feedback_gain,-95 , 95 )
+ NUMERIC_PARAMETER(delay_gain , 0 , 100)
+ NUMERIC_PARAMETER(speed , 0.1, 10 )
+ TEXTUAL_PARAMETER(wave_shape, st_wave_enum)
+ NUMERIC_PARAMETER(channel_phase, 0 , 100)
+ TEXTUAL_PARAMETER(interpolation, interp_enum)
+ } while (0);
- flanger->length = effp->ininfo.rate / flanger->speed;
+ if (argc != 0) {
+ st_fail(effp->h->usage);
+ return ST_EOF;
+ }
- if (! (flanger->flangerbuf =
- (double *) malloc(sizeof (double) * flanger->maxsamples)))
- {
- st_fail("flanger: Cannot malloc %d bytes!",
- sizeof(double) * flanger->maxsamples);
- return (ST_EOF);
- }
- for ( i = 0; i < flanger->maxsamples; i++ )
- flanger->flangerbuf[i] = 0.0;
- if (! (flanger->lookup_tab =
- (int *) malloc(sizeof (int) * flanger->length)))
- {
- st_fail("flanger: Cannot malloc %d bytes!",
- sizeof(int) * flanger->length);
- return(ST_EOF);
- }
+ st_report("parameters:\n"
+ "delay = %gms\n"
+ "depth = %gms\n"
+ "regen = %g%%\n"
+ "width = %g%%\n"
+ "speed = %gHz\n"
+ "shape = %s\n"
+ "phase = %g%%\n"
+ "interp= %s",
+ f->delay_min,
+ f->delay_depth,
+ f->feedback_gain,
+ f->delay_gain,
+ f->speed,
+ st_wave_enum[f->wave_shape].text,
+ f->channel_phase,
+ interp_enum[f->interpolation].text);
- if ( flanger->modulation == MOD_SINE )
- st_sine(flanger->lookup_tab, flanger->length,
- flanger->maxsamples - 1,
- flanger->maxsamples - 1);
- else
- st_triangle(flanger->lookup_tab, flanger->length,
- (flanger->maxsamples - 1) * 2,
- flanger->maxsamples - 1);
- flanger->counter = 0;
- flanger->phase = 0;
- flanger->fade_out = flanger->maxsamples;
- return (ST_SUCCESS);
+ return ST_SUCCESS;
}
-/*
- * Processed signed long samples from ibuf to obuf.
- * Return number of samples processed.
- */
-int st_flanger_flow(eff_t effp, const st_sample_t *ibuf, st_sample_t *obuf,
- st_size_t *isamp, st_size_t *osamp)
+
+
+static int st_flanger_start(eff_t effp)
{
- flanger_t flanger = (flanger_t) effp->priv;
- int len, done;
-
- double d_in, d_out;
- st_sample_t out;
+ flanger_t f = (flanger_t) effp->priv;
+ int c, channels = effp->ininfo.channels;
- len = ((*isamp > *osamp) ? *osamp : *isamp);
- for(done = 0; done < len; done++) {
- /* Store delays as 24-bit signed longs */
- d_in = (double) *ibuf++ / 256;
- /* Compute output first */
- d_out = d_in * flanger->in_gain;
- d_out += flanger->flangerbuf[(flanger->maxsamples +
- flanger->counter - flanger->lookup_tab[flanger->phase]) %
- flanger->maxsamples] * flanger->decay;
- /* Adjust the output volume and size to 24 bit */
- d_out = d_out * flanger->out_gain;
- out = ST_24BIT_CLIP_COUNT((st_sample_t) d_out, effp->clippedCount);
- *obuf++ = out * 256;
- /* Mix decay of delay and input */
- flanger->flangerbuf[flanger->counter] = d_in;
- flanger->counter =
- ( flanger->counter + 1 ) % flanger->maxsamples;
- flanger->phase = ( flanger->phase + 1 ) % flanger->length;
- }
- /* processed all samples */
- return (ST_SUCCESS);
+ if (channels > MAX_CHANNELS) {
+ st_fail("Can not operate with more than %i channels", MAX_CHANNELS);
+ return ST_EOF;
+ }
+
+ /* Scale percentages to unity: */
+ f->feedback_gain /= 100;
+ f->delay_gain /= 100;
+ f->channel_phase /= 100;
+
+ /* Balance output: */
+ f->in_gain = 1 / (1 + f->delay_gain);
+ f->delay_gain /= 1 + f->delay_gain;
+
+ /* Balance feedback loop: */
+ f->delay_gain *= 1 - fabs(f->feedback_gain);
+
+ st_debug("in_gain=%g feedback_gain=%g delay_gain=%g\n",
+ f->in_gain, f->feedback_gain, f->delay_gain);
+
+ /* Create the delay buffers, one for each channel: */
+ f->delay_buf_length =
+ (f->delay_min + f->delay_depth) / 1000 * effp->ininfo.rate + 0.5;
+ ++f->delay_buf_length; /* Need 0 to n, i.e. n + 1. */
+ ++f->delay_buf_length; /* Quadratic interpolator needs one more. */
+ for (c = 0; c < channels; ++c) {
+ f->delay_bufs[c] = calloc(f->delay_buf_length, sizeof(*f->delay_bufs[0]));
+ if (f->delay_bufs[c] == NULL)
+ {
+ st_fail("Cannot allocate memory for delay_bufs");
+ return ST_EOF;
+ }
+ }
+
+ /* Create the LFO lookup table: */
+ f->lfo_length = effp->ininfo.rate / f->speed;
+ f->lfo = calloc(f->lfo_length, sizeof(*f->lfo));
+ if (f->lfo == NULL) {
+ st_fail("Cannot allocate memory for lfo");
+ return ST_EOF;
+ }
+ st_generate_wave_table(
+ f->wave_shape,
+ ST_FLOAT,
+ f->lfo,
+ f->lfo_length,
+ (st_size_t)(f->delay_min / 1000 * effp->ininfo.rate + .5),
+ f->delay_buf_length - 2,
+ 3 * M_PI_2); /* Start the sweep at minimum delay (for mono at least) */
+
+ st_debug("delay_buf_length=%u lfo_length=%u\n",
+ f->delay_buf_length, f->lfo_length);
+
+ return ST_SUCCESS;
}
-/*
- * Drain out reverb lines.
- */
-int st_flanger_drain(eff_t effp, st_sample_t *obuf, st_size_t *osamp)
+
+
+static int st_flanger_flow(eff_t effp, st_sample_t const * ibuf,
+ st_sample_t * obuf, st_size_t * isamp, st_size_t * osamp)
{
- flanger_t flanger = (flanger_t) effp->priv;
- st_size_t done;
-
- double d_in, d_out;
- st_sample_t out;
+ flanger_t f = (flanger_t) effp->priv;
+ int c, channels = effp->ininfo.channels;
+ st_size_t len = (*isamp > *osamp ? *osamp : *isamp) / channels;
- done = 0;
- while ( ( done < *osamp ) && ( done < flanger->fade_out ) ) {
- d_in = 0;
- d_out = 0;
- /* Compute output first */
- d_out += flanger->flangerbuf[(flanger->maxsamples +
- flanger->counter - flanger->lookup_tab[flanger->phase]) %
- flanger->maxsamples] * flanger->decay;
- /* Adjust the output volume and size to 24 bit */
- d_out = d_out * flanger->out_gain;
- out = ST_24BIT_CLIP_COUNT((st_sample_t) d_out, effp->clippedCount);
- *obuf++ = out * 256;
- /* Mix decay of delay and input */
- flanger->flangerbuf[flanger->counter] = d_in;
- flanger->counter =
- ( flanger->counter + 1 ) % flanger->maxsamples;
- flanger->phase = ( flanger->phase + 1 ) % flanger->length;
- done++;
- flanger->fade_out--;
- }
- /* samples playd, it remains */
- *osamp = done;
- return (ST_SUCCESS);
+ *isamp = *osamp = len * channels;
+
+ while (len--) {
+ f->delay_buf_pos =
+ (f->delay_buf_pos + f->delay_buf_length - 1) % f->delay_buf_length;
+ for (c = 0; c < channels; ++c) {
+ double delayed_0, delayed_1;
+ double delayed;
+ double in, out;
+ st_size_t channel_phase = c * f->lfo_length * f->channel_phase + .5;
+ double delay = f->lfo[(f->lfo_pos + channel_phase) % f->lfo_length];
+ double frac_delay = modf(delay, &delay);
+ st_size_t int_delay = (size_t)delay;
+
+ in = *ibuf++;
+ f->delay_bufs[c][f->delay_buf_pos] = in + f->delay_last[c] * f->feedback_gain;
+
+ delayed_0 = f->delay_bufs[c]
+ [(f->delay_buf_pos + int_delay++) % f->delay_buf_length];
+ delayed_1 = f->delay_bufs[c]
+ [(f->delay_buf_pos + int_delay++) % f->delay_buf_length];
+
+ if (f->interpolation == INTERP_LINEAR)
+ delayed = delayed_0 + (delayed_1 - delayed_0) * frac_delay;
+ else /* if (f->interpolation == INTERP_QUADRATIC) */
+ {
+ double a, b;
+ double delayed_2 = f->delay_bufs[c]
+ [(f->delay_buf_pos + int_delay++) % f->delay_buf_length];
+ delayed_2 -= delayed_0;
+ delayed_1 -= delayed_0;
+ a = delayed_2 *.5 - delayed_1;
+ b = delayed_1 * 2 - delayed_2 *.5;
+ delayed = delayed_0 + (a * frac_delay + b) * frac_delay;
+ }
+
+ f->delay_last[c] = delayed;
+ out = in * f->in_gain + delayed * f->delay_gain;
+ *obuf++ = ST_ROUND_CLIP_COUNT(out, effp->clippedCount);
+ }
+ f->lfo_pos = (f->lfo_pos + 1) % f->lfo_length;
+ }
+
+ return ST_SUCCESS;
}
-/*
- * Clean up flanger effect.
- */
-int st_flanger_stop(eff_t effp)
+
+
+static int st_flanger_stop(eff_t effp)
{
- flanger_t flanger = (flanger_t) effp->priv;
+ flanger_t f = (flanger_t) effp->priv;
+ int c, channels = effp->ininfo.channels;
- free((char *) flanger->flangerbuf);
- flanger->flangerbuf = (double *) -1; /* guaranteed core dump */
- free((char *) flanger->lookup_tab);
- flanger->lookup_tab = (int *) -1; /* guaranteed core dump */
- return (ST_SUCCESS);
+ for (c = 0; c < channels; ++c)
+ if (f->delay_bufs[c] != NULL)
+ free(f->delay_bufs[c]);
+
+ if (f->lfo != NULL)
+ free(f->lfo);
+
+ memset(f, 0, sizeof(*f));
+
+ return ST_SUCCESS;
}
+
+
static st_effect_t st_flanger_effect = {
"flanger",
- "Usage: flanger gain-in gain-out delay decay speed [ -s | -t ]",
- 0,
+ st_flanger_usage,
+ ST_EFF_MCHAN,
st_flanger_getopts,
st_flanger_start,
st_flanger_flow,
- st_flanger_drain,
+ st_effect_nothing_drain,
st_flanger_stop
};
-const st_effect_t *st_flanger_effect_fn(void)
+
+
+st_effect_t const * st_flanger_effect_fn(void)
{
- return &st_flanger_effect;
+ return &st_flanger_effect;
}
--- a/src/misc.c
+++ b/src/misc.c
@@ -424,38 +424,59 @@
srand(t);
}
-/* This was very painful. We need a sine library. */
-void st_sine(int *buf, st_size_t len, int max, int depth)
-{
- st_ssize_t i;
- int offset;
- double val;
- offset = max - depth;
- for (i = 0; i < len; i++) {
- val = sin((double)i/(double)len * 2.0 * M_PI);
- buf[i] = (int) ((1.0 + val) * depth / 2.0);
- }
-}
-
-void st_triangle(int *buf, st_size_t len, int max, int depth)
+void st_generate_wave_table(
+ st_wave_t wave_type,
+ st_data_t data_type,
+ void * table,
+ uint32_t table_size,
+ double min,
+ double max,
+ double phase)
{
- st_ssize_t i;
- int offset;
- double val;
+ uint32_t t;
+ uint32_t phase_offset = phase / M_PI / 2 * table_size + 0.5;
- offset = max - 2 * depth;
- for (i = 0; i < len / 2; i++) {
- val = i * 2.0 / len;
- buf[i] = offset + (int) (val * 2.0 * (double)depth);
+ for (t = 0; t < table_size; t++)
+ {
+ uint32_t point = (t + phase_offset) % table_size;
+ double d;
+ switch (wave_type)
+ {
+ case ST_WAVE_SINE:
+ d = (sin((double)point / table_size * 2 * M_PI) + 1) / 2;
+ break;
+
+ case ST_WAVE_TRIANGLE:
+ d = (double)point * 2 / table_size;
+ switch (4 * point / table_size)
+ {
+ case 0: d = d + 0.5; break;
+ case 1: case 2: d = 1.5 - d; break;
+ case 3: d = d - 1.5; break;
+ }
+ break;
}
- for (i = len / 2; i < len ; i++) {
- val = (len - i) * 2.0 / len;
- buf[i] = offset + (int) (val * 2.0 * (double)depth);
+ d = d * (max - min) + min;
+ switch (data_type)
+ {
+ case ST_FLOAT : *(float *)table = d; table += sizeof(float ); continue;
+ case ST_DOUBLE: *(double *)table = d; table += sizeof(double); continue;
+ default: break;
}
+ d += d < 0? -0.5 : +0.5;
+ switch (data_type)
+ {
+ case ST_SHORT : *(short *)table = d; table += sizeof(short ); continue;
+ case ST_INT : *(int *)table = d; table += sizeof(int ); continue;
+ default: break;
+ }
+ }
}
+
+
const char *st_version(void)
{
return PACKAGE_VERSION;
@@ -500,3 +521,26 @@
return ft->st_errno;
}
+
+enum_item const * find_enum_text(char const * text, enum_item const * enum_items)
+{
+ enum_item const * result = NULL; /* Assume not found */
+
+ while (enum_items->text)
+ {
+ if (strncasecmp(text, enum_items->text, strlen(text)) == 0)
+ {
+ if (result != NULL && result->value != enum_items->value)
+ return NULL; /* Found ambiguity */
+ result = enum_items; /* Found match */
+ }
+ ++enum_items;
+ }
+ return result;
+}
+
+enum_item const st_wave_enum[] = {
+ ENUM_ITEM(ST_WAVE_,SINE)
+ ENUM_ITEM(ST_WAVE_,TRIANGLE)
+ {0}};
+
--- a/src/phaser.c
+++ b/src/phaser.c
@@ -176,14 +176,12 @@
return (ST_EOF);
}
- if ( phaser->modulation == MOD_SINE )
- st_sine(phaser->lookup_tab, phaser->length,
- phaser->maxsamples - 1,
- phaser->maxsamples - 1);
+ if (phaser->modulation == MOD_SINE)
+ st_generate_wave_table(ST_WAVE_SINE, ST_INT, phaser->lookup_tab,
+ phaser->length, 0, phaser->maxsamples - 1, 0);
else
- st_triangle(phaser->lookup_tab, phaser->length,
- (phaser->maxsamples - 1) * 2,
- phaser->maxsamples - 1);
+ st_generate_wave_table(ST_WAVE_TRIANGLE, ST_INT, phaser->lookup_tab,
+ phaser->length, 0, 2 * (phaser->maxsamples - 1), 3 * M_PI_2);
phaser->counter = 0;
phaser->phase = 0;
phaser->fade_out = phaser->maxsamples;
--- a/src/skeleff.c
+++ b/src/skeleff.c
@@ -3,7 +3,7 @@
*
* Written by Chris Bagwell (cbagwell@sprynet.com) - March 16, 1999
*
- * Copyright 1999 Chris Bagwell And Sundry Contributors
+ * Copyright 1999 Chris Bagwell And Sundry Contributors
* This source code is freely redistributable and may be used for
* any purpose. This copyright notice must be maintained.
* Chris Bagwell And Sundry Contributors are not responsible for
@@ -13,10 +13,13 @@
#include "st_i.h"
-/* Private data for SKEL file */
+/* Private data for effect */
typedef struct skelleffstuff {
int localdata;
} *skeleff_t;
+
+assert_static(sizeof(struct skelleffstuff) <= ST_MAX_EFFECT_PRIVSIZE,
+ /* else */ skelleff_PRIVSIZE_too_big);
/*
* Process options
--- a/src/st_i.h
+++ b/src/st_i.h
@@ -34,8 +34,22 @@
#endif
/* declared in misc.c */
-void st_sine(int *buf, st_size_t len, int max, int depth);
-void st_triangle(int *buf, st_size_t len, int max, int depth);
+typedef struct {char const *text; int value;} enum_item;
+#define ENUM_ITEM(prefix, item) {#item, prefix##item},
+enum_item const * find_enum_text(
+ char const * text, enum_item const * enum_items);
+
+typedef enum {ST_SHORT, ST_INT, ST_FLOAT, ST_DOUBLE} st_data_t;
+typedef enum {ST_WAVE_SINE, ST_WAVE_TRIANGLE} st_wave_t;
+extern enum_item const st_wave_enum[];
+void st_generate_wave_table(
+ st_wave_t wave_type,
+ st_data_t data_type,
+ void * table,
+ uint32_t table_size,
+ double min,
+ double max,
+ double phase);
st_sample_t st_gcd(st_sample_t a, st_sample_t b) REGPARM(2);
st_sample_t st_lcm(st_sample_t a, st_sample_t b) REGPARM(2);
--- a/src/synth.c
+++ b/src/synth.c
@@ -16,26 +16,6 @@
#include <ctype.h>
#include "st_i.h"
-typedef struct {char const *text; int value;} enum_item;
-#define ENUM_ITEM(prefix, item) {#item, prefix##item},
-
-static enum_item const * find(char const * text, enum_item const * enum_items)
-{
- enum_item const * result = NULL; /* Assume not found */
-
- while (enum_items->text)
- {
- if (strncasecmp(text, enum_items->text, strlen(text)) == 0)
- {
- if (result != NULL && result->value != enum_items->value)
- return NULL; /* Found ambiguity */
- result = enum_items; /* Found match */
- }
- ++enum_items;
- }
- return result;
-}
-
static st_effect_t st_synth_effect;
#define PCOUNT 5
@@ -315,7 +295,7 @@
/* for one or more channel */
/* type [combine] [f1[-f2]] [p0] [p1] [p2] [p3] [p4] */
for (c = 0; c < MAXCHAN && n > argn; c++) {
- enum_item const * p = find(argv[argn], synth_type);
+ enum_item const * p = find_enum_text(argv[argn], synth_type);
if (p == NULL) {
st_fail("no type given");
return ST_EOF;
@@ -324,7 +304,7 @@
if (++argn == n) break;
/* maybe there is a combine-type in next arg */
- p = find(argv[argn], combine_type);
+ p = find_enum_text(argv[argn], combine_type);
if (p != NULL) {
synth->mix[c] = p->value;
if (++argn == n) break;
--- a/src/vibro.c
+++ b/src/vibro.c
@@ -63,22 +63,6 @@
return (ST_SUCCESS);
}
-/* This was very painful. We need a sine library. */
-/* SJB: this is somewhat different than st_sine() */
-/* FIXME: move to misc.c */
-static void sine(short *buf, int len, float depth)
-{
- int i;
- int scale = depth * 128;
- int base = (1.0 - depth) * 128;
- double val;
-
- for (i = 0; i < len; i++) {
- val = sin((float)i/(float)len * 2.0 * M_PI);
- buf[i] = (val + 1.0) * scale + base * 2;
- }
-}
-
/*
* Prepare processing.
*/
@@ -94,7 +78,8 @@
return (ST_EOF);
}
- sine(vibro->sinetab, vibro->length, vibro->depth);
+ st_generate_wave_table(ST_WAVE_SINE, ST_SHORT,
+ vibro->sinetab, vibro->length, (1 - vibro->depth) * 256, 256, 0);
vibro->counter = 0;
return (ST_SUCCESS);
}