kobodl/sound/a_delay.c
Ville Lindholm dbc223eb84
Initial commit
Import existing source tree; original VCS history is no longer available.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-28 16:35:31 +03:00

595 lines
12 KiB
C

/*(LGPL)
---------------------------------------------------------------------------
a_delay.c - Feedback delay w/ LP filter
---------------------------------------------------------------------------
* Copyright (C) 2001, David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdlib.h>
#include "a_globals.h"
#include "a_delay.h"
#include "a_tools.h"
#define DELAY_BUFSIZE 65536
#define DELAY_MAX_TAPS 16
#define DELAY_MAX_TAIL_TAPS 8
typedef struct dtap_t
{
unsigned delay;
int shift;
} dtap_t;
typedef struct delay_t
{
int *delaybuf;
int inspos;
int cl, cr, lpf; /* LPF */
int level;
unsigned taps, tailtaps;
int ttimer;
float tlevel;
dtap_t tap[DELAY_MAX_TAPS];
dtap_t tailtap[DELAY_MAX_TAIL_TAPS];
} delay_t;
#define DELAY_MASK (DELAY_BUFSIZE-1)
#define INTERNAL_BITS 8
static int _init(delay_t *d)
{
d->inspos = 0;
d->cl = 0;
d->cr = 0;
d->delaybuf = calloc(1, sizeof(int)*DELAY_BUFSIZE);
if(!d->delaybuf)
return -2;
return 0;
}
#define DB(x) delaybuf[(inspos-(x)) & DELAY_MASK]
#define TAP(n) (DB(d->tap[n].delay) >> d->tap[n].shift)
#define TTAP(n) (DB(d->tailtap[n].delay) >> d->tailtap[n].shift)
#if 0
/* Stereo in, stereo out */
static void o_delay_process_mix_s(delay_t *d, int *in, int *out, unsigned frames)
{
int *delaybuf = d->delaybuf;
int inspos = d->inspos;
int cl = d->cl;
int cr = d->cr;
unsigned s = frames;
int outl, outr;
int vm = d->level >> 8;
int i;
while(s--)
{
/* Feedback with LP filters */
cl += (TTAP(0) + TTAP(3) + TTAP(4) - cl) >> 3;
cr += (TTAP(1) + TTAP(2) + TTAP(5) - cr) >> 3;
DB(0) = cl;
DB(1) = cr;
/* "Tap 0" */
outl = cl;
outr = cr;
/* Taps */
for(i = 0; i < d->taps; i += 2)
{
outl += TAP(i);
outr += TAP(i+1);
}
/* Input */
DB(0) += in[0];
DB(1) += in[1];
/* Output */
out[0] += (outl*vm)>>8;
out[1] += (outr*vm)>>8;
inspos += 2;
in += 2;
out += 2;
}
d->cl = cl;
d->cr = cr;
d->inspos = inspos;
}
#endif
/*
* Stereo, replacing, without level control.
*/
static void o_delay_process_r_s(delay_t *d, int *in, int *out, unsigned frames)
{
int *delaybuf = d->delaybuf;
int inspos = d->inspos;
int cl = d->cl;
int cr = d->cr;
unsigned s = frames;
int outl, outr;
unsigned i;
while(s--)
{
/* Tail taps */
outl = outr = 0;
for(i = 0; i < d->tailtaps; i += 2)
{
outl += TTAP(i);
outr += TTAP(i+1);
}
/* LPF */
// cl += (outl - (cl>>INTERNAL_BITS)) * d->lpf;
// cr += (outr - (cr>>INTERNAL_BITS)) * d->lpf;
cl += (outl - cl) * d->lpf >> INTERNAL_BITS;
cr += (outr - cr) * d->lpf >> INTERNAL_BITS;
/* Feedback */
DB(0) = cl;// >> INTERNAL_BITS;
DB(1) = cr;// >> INTERNAL_BITS;
/* "Tap 0" */
outl = cl;
outr = cr;
/* Taps */
for(i = 0; i < d->taps; i += 2)
{
outl += TAP(i);
outr += TAP(i+1);
}
/* Input */
DB(0) += in[0] << INTERNAL_BITS;
DB(1) += in[1] << INTERNAL_BITS;
/* Output */
out[0] = outl >> INTERNAL_BITS;
out[1] = outr >> INTERNAL_BITS;
inspos += 2;
in += 2;
out += 2;
}
d->cl = cl;
d->cr = cr;
d->inspos = inspos;
}
/*
* Stereo, replacing in-place, without output level control
* (for inserts)
*/
static void o_delay_process_s(delay_t *d, int *buf, unsigned frames)
{
int *delaybuf = d->delaybuf;
int inspos = d->inspos;
int cl = d->cl;
int cr = d->cr;
int outl, outr;
unsigned i, s;
frames <<= 1;
for(s = 0; s < frames; s += 2)
{
/* Tail taps */
outl = outr = 0;
for(i = 0; i < d->tailtaps; i += 2)
{
outl += TTAP(i);
outr += TTAP(i+1);
}
/* LP filters */
// cl += (outl - (cl>>INTERNAL_BITS)) * d->lpf;
// cr += (outr - (cr>>INTERNAL_BITS)) * d->lpf;
cl += (outl - cl) * d->lpf >> INTERNAL_BITS;
cr += (outr - cr) * d->lpf >> INTERNAL_BITS;
/* Input + Feedback */
DB(0) = cl + (buf[s] << INTERNAL_BITS);
DB(1) = cr + (buf[s+1] << INTERNAL_BITS);
/* "Tap 0" - the Feedback Signal */
outl = cl;
outr = cr;
/* Early Reflection Taps */
for(i = 0; i < d->taps; i += 2)
{
outl += TAP(i);
outr += TAP(i+1);
}
/* Output */
buf[s] = outl >> INTERNAL_BITS;
buf[s+1] = outr >> INTERNAL_BITS;
inspos += 2;
}
d->cl = cl;
d->cr = cr;
d->inspos = inspos;
}
/*
* Stereo, replacing, with silent input and no level control.
* Returns the approximate peak level of the generated output.
*/
static int o_delay_process_tail_s(delay_t *d, int *out, unsigned frames)
{
int level = 0;
int *delaybuf = d->delaybuf;
int inspos = d->inspos;
int cl = d->cl;
int cr = d->cr;
int outl, outr;
unsigned i, s;
frames <<= 1;
for(s = 0; s < frames; s += 2)
{
/* Tail taps */
outl = outr = 0;
for(i = 0; i < d->tailtaps; i += 2)
{
outl += TTAP(i);
outr += TTAP(i+1);
}
/* LP filters */
// cl += (outl - (cl>>INTERNAL_BITS)) * d->lpf;
// cr += (outr - (cr>>INTERNAL_BITS)) * d->lpf;
cl += (outl - cl) * d->lpf >> INTERNAL_BITS;
cr += (outr - cr) * d->lpf >> INTERNAL_BITS;
/* Feedback (No input!) */
DB(0) = cl;
DB(1) = cr;
/* "Tap 0" - the Feedback Signal */
outl = cl;
outr = cr;
/* Early Reflection Taps */
for(i = 0; i < d->taps; i += 2)
{
outl += TAP(i);
outr += TAP(i+1);
}
/* Output */
out[s] = outl >> INTERNAL_BITS;
out[s+1] = outr >> INTERNAL_BITS;
/* Level meter */
level |= labs(outl) | labs(outr);
inspos += 2;
}
d->cl = cl;
d->cr = cr;
d->inspos = inspos;
return level >> INTERNAL_BITS;
}
#if 0
/* UNTESTED: Mono in, stereo out. Obviously not in-place capable! */
void o_delay_process_mix_ms(delay_t *d, int *in, int *out, unsigned frames)
{
int *delaybuf = d->delaybuf;
int inspos = d->inspos;
int cl = d->cl;
int cr = d->cr;
int s = frames;
int outl, outr;
int vm = d->level >> 8;
int i;
while(s--)
{
/* Feedback with LP filters */
cl += (TTAP(0) + TTAP(3) + TTAP(4) - cl) >> 3;
cr += (TTAP(1) + TTAP(2) + TTAP(5) - cr) >> 3;
DB(0) = cl;
DB(1) = cr;
/* "Tap 0" */
outl = cl;
outr = cr;
/* Taps */
for(i = 0; i < d->taps; i += 2)
{
outl += TAP(i);
outr += TAP(i+1);
}
/* Input */
DB(0) += in[0];
DB(1) += in[0];
/* Output */
out[0] += (outl*vm)>>8;
out[1] += (outr*vm)>>8;
inspos += 2;
in += 1;
out += 2;
}
d->cl = cl;
d->cr = cr;
d->inspos = inspos;
}
#endif
#undef DB
#undef TAP
/*
* New API
======================================================================
*/
static void _load_taps(struct audio_plugin_t *p, const float *taps)
{
int i;
p->ctl[DC_EARLY_TIME] = (int)(1.0 * 65536.0);
p->ctl[DC_TAIL_TIME] = (int)(1.0 * 65536.0);
p->ctl[DC_EARLY_LEVEL] = (int)(1.0 * 65536.0);
p->ctl[DC_FEEDBACK] = (int)(1.0 * 65536.0);
p->ctl[DC_LP_FILTER] = (int)(6500.0 * 65536.0);
for(i = 0; i < DELAY_MAX_TAPS + DELAY_MAX_TAIL_TAPS; ++i)
{
p->ctl[DC_EARLY_TAP_1_TIME + i*2] = (int)(*taps++ * 65536.0);
p->ctl[DC_EARLY_TAP_1_LEVEL + i*2] = (int)(*taps++ * 65536.0);
}
}
static void _scale_early_taps(struct audio_plugin_t *p)
{
delay_t *d = (delay_t *)p->user;
unsigned i, t, scale;
/* samples per ms */
float s_ms = (float)p->ctl[FXC_SAMPLERATE] * 0.001;
s_ms *= (float)p->ctl[DC_EARLY_TIME]; /* scale */
s_ms *= 1.0/1024.0; /* Throw away 10 of the 16 fraction bits */
scale = (unsigned)s_ms;
for(t = 0, i = 0; i < DELAY_MAX_TAPS; ++i)
{
unsigned delay;
int shift;
if(!p->ctl[DC_EARLY_TAP_1_LEVEL + i*2])
continue;
shift = fixp2shift(p->ctl[DC_EARLY_TAP_1_LEVEL + i*2]);
delay = (unsigned)p->ctl[DC_EARLY_TAP_1_TIME + i*2] >> 8;
delay *= scale;
delay >>= 14;
if(delay > DELAY_BUFSIZE-2)
delay = DELAY_BUFSIZE-2;
else if(delay < 0)
delay = 0;
delay &= (DELAY_MASK-1);
delay |= t & 1; /* L->L, R->R, L->L, R->R,... */
d->tap[t].delay = delay;
d->tap[t].shift = shift;
++t;
}
d->taps = t;
}
static void _scale_tail_taps(struct audio_plugin_t *p)
{
delay_t *d = (delay_t *)p->user;
unsigned i, t, scale;
/* samples per ms */
float s_ms = (float)p->ctl[FXC_SAMPLERATE] * 0.001;
s_ms *= (float)p->ctl[DC_TAIL_TIME]; /* scale */
s_ms *= 1.0/1024.0; /* Throw away 10 of the 16 fraction bits */
scale = (unsigned)s_ms;
for(t = 0, i = 0; i < DELAY_MAX_TAIL_TAPS; ++i)
{
unsigned delay;
int shift;
if(!p->ctl[DC_TAIL_TAP_1_LEVEL + i*2])
continue;
shift = fixp2shift(p->ctl[DC_TAIL_TAP_1_LEVEL + i*2]);
delay = (unsigned)p->ctl[DC_TAIL_TAP_1_TIME + i*2] >> 8;
delay *= scale;
delay >>= 14;
if(delay > DELAY_BUFSIZE-2)
delay = DELAY_BUFSIZE-2;
delay &= (DELAY_MASK-1);
delay |= (t>>1) & 1; /* L->L, L->R, R->L, R->R,... */
d->tailtap[t].delay = delay;
d->tailtap[t].shift = shift;
++t;
}
d->tailtaps = t;
}
static inline void __calc_filters(audio_plugin_t *p)
{
delay_t *d = (delay_t *)p->user;
d->lpf = p->ctl[DC_LP_FILTER] /
p->ctl[FXC_SAMPLERATE] >> (16-INTERNAL_BITS);
if(d->lpf > (1<<(16-INTERNAL_BITS)))
d->lpf = (1<<(16-INTERNAL_BITS));
}
static const float default_taps[(DELAY_MAX_TAPS + DELAY_MAX_TAIL_TAPS)*2] = {
/* Early Reflection Taps */
17, 1.0/4, 31, 1.0/4,
87, 1.0/4, 179, 1.0/4,
379, 1.0/16, 246, 1.0/16,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
/* Tail Feedback Taps */
552, 1.0/4, 642, 1.0/4,
883, 1.0/4, 851, 1.0/4,
1204, 1.0/4, 1176, 1.0/4,
0, 0, 0, 0
};
static int delay_state(struct audio_plugin_t *p, audio_fxstates_t new_state)
{
delay_t *d;
if(new_state > p->current_state)
switch(new_state)
{
case FX_STATE_CLOSED: /* Can't happen. */
case FX_STATE_OPEN:
if(!audio_plugin_alloc_ctls(p, DC_COUNT))
return -1;
d = calloc(1, sizeof(delay_t));
if(!d)
return -2;
p->user = d;
/*_load_taps(d, default_taps, p->ctl[FXC_SAMPLERATE]);*/
_load_taps(p, default_taps);
break;
case FX_STATE_READY:
d = (delay_t *)p->user;
if(_init(d) < 0)
return -2;
_scale_early_taps(p);
_scale_tail_taps(p);
__calc_filters(p);
break;
case FX_STATE_PAUSED:
case FX_STATE_RUNNING:
case FX_STATE_SILENT:
case FX_STATE_RESTING:
p->current_state = FX_STATE_RESTING;
break;
}
else
switch(new_state)
{
case FX_STATE_CLOSED:
free(p->user);
p->user = NULL;
break;
case FX_STATE_OPEN:
d = (delay_t *)p->user;
free(d->delaybuf);
d->delaybuf = NULL;
break;
case FX_STATE_READY:
break;
case FX_STATE_PAUSED:
case FX_STATE_RUNNING: /* Can't happen. */
case FX_STATE_SILENT: /* Can't happen. */
case FX_STATE_RESTING: /* Can't happen. */
break;
}
return 0;
}
static void delay_control(struct audio_plugin_t *p, unsigned ctl, int arg)
{
/* 1: Early reflections duration
* 2: Tail duration
* 3: Early reflections level
* 4: Feedback Level
* 5: Feedback LPF Cutoff
* 6: (Unused)
*/
p->ctl[ctl] = arg;
switch(ctl)
{
case DC_EARLY_TIME:
_scale_early_taps(p);
break;
case DC_TAIL_TIME:
_scale_tail_taps(p);
break;
case DC_EARLY_LEVEL:
case DC_FEEDBACK:
break;
case DC_LP_FILTER:
__calc_filters(p);
break;
}
}
static void delay_process(struct audio_plugin_t *p, int *buf, unsigned frames)
{
delay_t *d = (delay_t *)p->user;
d->tlevel = 1000;
d->ttimer = 0;
p->current_state = FX_STATE_RUNNING;
o_delay_process_s(d, buf, frames);
}
static void delay_process_r(struct audio_plugin_t *p, int *in, int *out, unsigned frames)
{
delay_t *d = (delay_t *)p->user;
if(in)
{
d->tlevel = 1000;
d->ttimer = 0;
p->current_state = FX_STATE_RUNNING;
o_delay_process_r_s(d, in, out, frames);
}
else
{
int level;
if(FX_STATE_RESTING == p->current_state)
return;
level = o_delay_process_tail_s(d, out, frames);
d->tlevel += (float)((level - d->tlevel) * frames) /
(float)(p->ctl[FXC_SAMPLERATE] * 0.1);
d->ttimer += frames;
if(d->ttimer < p->ctl[FXC_SAMPLERATE] * 2)
return;
if(d->tlevel < 5.0)
{
d->tlevel = 1000;
p->current_state = FX_STATE_RESTING;
return;
}
}
}
void delay_init(struct audio_plugin_t *p)
{
p->state = delay_state;
p->control = delay_control;
p->process = delay_process;
p->process_r = delay_process_r;
}