filter-graph: add feedback and feedforward to delay

Add feedback and feedforward controls to the delay. This makes it
possible to make comb and allpass filters with the delay to build
custom reverb effects.
This commit is contained in:
Wim Taymans 2025-11-28 17:10:55 +01:00
parent 933ac4be43
commit 52ec847cbd
6 changed files with 104 additions and 37 deletions

View file

@ -205,9 +205,10 @@ void dsp_linear_c(void *obj, float * dst,
void dsp_delay_c(void *obj, float *buffer, uint32_t *pos, uint32_t n_buffer,
uint32_t delay, float *dst, const float *src, uint32_t n_samples)
uint32_t delay, float *dst, const float *src, uint32_t n_samples,
float fb, float ff)
{
if (delay == 0) {
if (delay == 0 && fb == 0.0f && ff == 0.0f) {
dsp_copy_c(obj, dst, src, n_samples);
} else {
uint32_t w, o, i;
@ -215,10 +216,20 @@ void dsp_delay_c(void *obj, float *buffer, uint32_t *pos, uint32_t n_buffer,
w = *pos;
o = n_buffer - delay;
for (i = 0; i < n_samples; i++) {
buffer[w] = buffer[w + n_buffer] = src[i];
dst[i] = buffer[w + o];
w = w + 1 >= n_buffer ? 0 : w + 1;
if (fb == 0.0f && ff == 0.0f) {
for (i = 0; i < n_samples; i++) {
buffer[w] = buffer[w + n_buffer] = src[i];
dst[i] = buffer[w + o];
w = w + 1 >= n_buffer ? 0 : w + 1;
}
} else {
for (i = 0; i < n_samples; i++) {
float d = buffer[w + o];
float s = src[i];
buffer[w] = buffer[w + n_buffer] = s + d * fb;
dst[i] = ff * s + d;
w = w + 1 >= n_buffer ? 0 : w + 1;
}
}
*pos = w;
}

View file

@ -32,7 +32,7 @@ void dsp_biquad_run_##arch (void *obj, struct biquad *bq, uint32_t n_bq, uint32_
float * SPA_RESTRICT out[], const float * SPA_RESTRICT in[], uint32_t n_src, uint32_t n_samples)
#define MAKE_DELAY_FUNC(arch) \
void dsp_delay_##arch (void *obj, float *buffer, uint32_t *pos, uint32_t n_buffer, \
uint32_t delay, float *dst, const float *src, uint32_t n_samples)
uint32_t delay, float *dst, const float *src, uint32_t n_samples, float fb, float ff)
#define MAKE_FFT_NEW_FUNC(arch) \
void *dsp_fft_new_##arch(void *obj, uint32_t size, bool real)

View file

@ -614,34 +614,70 @@ void dsp_biquad_run_sse(void *obj, struct biquad *bq, uint32_t n_bq, uint32_t bq
}
void dsp_delay_sse(void *obj, float *buffer, uint32_t *pos, uint32_t n_buffer, uint32_t delay,
float *dst, const float *src, uint32_t n_samples)
float *dst, const float *src, uint32_t n_samples, float fb, float ff)
{
__m128 t[1];
__m128 t[4];
uint32_t w = *pos;
uint32_t o = n_buffer - delay;
uint32_t n, unrolled;
if (SPA_IS_ALIGNED(src, 16) &&
SPA_IS_ALIGNED(dst, 16))
unrolled = n_samples & ~3;
else
unrolled = 0;
if (fb == 0.0f && ff == 0.0f) {
if (SPA_IS_ALIGNED(src, 16) &&
SPA_IS_ALIGNED(dst, 16) && delay >= 4)
unrolled = n_samples & ~3;
else
unrolled = 0;
for(n = 0; n < unrolled; n += 4) {
t[0] = _mm_load_ps(&src[n]);
_mm_storeu_ps(&buffer[w], t[0]);
_mm_storeu_ps(&buffer[w+n_buffer], t[0]);
t[0] = _mm_loadu_ps(&buffer[w+o]);
_mm_store_ps(&dst[n], t[0]);
w = w + 4 >= n_buffer ? 0 : w + 4;
}
for(; n < n_samples; n++) {
t[0] = _mm_load_ss(&src[n]);
_mm_store_ss(&buffer[w], t[0]);
_mm_store_ss(&buffer[w+n_buffer], t[0]);
t[0] = _mm_load_ss(&buffer[w+o]);
_mm_store_ss(&dst[n], t[0]);
w = w + 1 >= n_buffer ? 0 : w + 1;
for(n = 0; n < unrolled; n += 4) {
t[0] = _mm_load_ps(&src[n]);
_mm_storeu_ps(&buffer[w], t[0]);
_mm_storeu_ps(&buffer[w+n_buffer], t[0]);
t[0] = _mm_loadu_ps(&buffer[w+o]);
_mm_store_ps(&dst[n], t[0]);
w = w + 4 >= n_buffer ? 0 : w + 4;
}
for(; n < n_samples; n++) {
t[0] = _mm_load_ss(&src[n]);
_mm_store_ss(&buffer[w], t[0]);
_mm_store_ss(&buffer[w+n_buffer], t[0]);
t[0] = _mm_load_ss(&buffer[w+o]);
_mm_store_ss(&dst[n], t[0]);
w = w + 1 >= n_buffer ? 0 : w + 1;
}
} else {
__m128 fb0 = _mm_set1_ps(fb);
__m128 ff0 = _mm_set1_ps(ff);
if (SPA_IS_ALIGNED(src, 16) &&
SPA_IS_ALIGNED(dst, 16) && delay >= 4)
unrolled = n_samples & ~3;
else
unrolled = 0;
for(n = 0; n < unrolled; n += 4) {
t[0] = _mm_loadu_ps(&buffer[w+o]);
t[1] = _mm_load_ps(&src[n]);
t[2] = _mm_mul_ps(t[0], fb0);
t[2] = _mm_add_ps(t[2], t[1]);
_mm_storeu_ps(&buffer[w], t[2]);
_mm_storeu_ps(&buffer[w+n_buffer], t[2]);
t[2] = _mm_mul_ps(t[1], ff0);
t[2] = _mm_add_ps(t[2], t[0]);
_mm_store_ps(&dst[n], t[2]);
w = w + 4 >= n_buffer ? 0 : w + 4;
}
for(; n < n_samples; n++) {
t[0] = _mm_load_ss(&buffer[w+o]);
t[1] = _mm_load_ss(&src[n]);
t[2] = _mm_mul_ss(t[0], fb0);
t[2] = _mm_add_ss(t[2], t[1]);
_mm_store_ss(&buffer[w], t[2]);
_mm_store_ss(&buffer[w+n_buffer], t[2]);
t[2] = _mm_mul_ps(t[1], ff0);
t[2] = _mm_add_ps(t[2], t[0]);
_mm_store_ss(&dst[n], t[2]);
w = w + 1 >= n_buffer ? 0 : w + 1;
}
}
*pos = w;
}

View file

@ -58,7 +58,8 @@ struct spa_fga_dsp_methods {
float * SPA_RESTRICT out[], const float * SPA_RESTRICT in[],
uint32_t n_src, uint32_t n_samples);
void (*delay) (void *obj, float *buffer, uint32_t *pos, uint32_t n_buffer, uint32_t delay,
float *dst, const float *src, uint32_t n_samples);
float *dst, const float *src, uint32_t n_samples,
float fb, float ff);
};
static inline void spa_fga_dsp_clear(struct spa_fga_dsp *obj, float * SPA_RESTRICT dst, uint32_t n_samples)
@ -159,10 +160,11 @@ static inline void spa_fga_dsp_biquad_run(struct spa_fga_dsp *obj,
}
static inline void spa_fga_dsp_delay(struct spa_fga_dsp *obj,
float *buffer, uint32_t *pos, uint32_t n_buffer, uint32_t delay,
float *dst, const float *src, uint32_t n_samples)
float *dst, const float *src, uint32_t n_samples,
float fb, float ff)
{
spa_api_method_v(spa_fga_dsp, &obj->iface, delay, 0,
buffer, pos, n_buffer, delay, dst, src, n_samples);
buffer, pos, n_buffer, delay, dst, src, n_samples, fb, ff);
}
#endif /* SPA_FGA_DSP_H */

View file

@ -1228,7 +1228,7 @@ struct delay_impl {
struct spa_log *log;
unsigned long rate;
float *port[4];
float *port[6];
float delay;
uint32_t delay_samples;
@ -1334,7 +1334,8 @@ static void delay_run(void * Instance, unsigned long SampleCount)
}
if (in != NULL && out != NULL) {
spa_fga_dsp_delay(impl->dsp, impl->buffer, &impl->ptr, impl->buffer_samples,
impl->delay_samples, out, in, SampleCount);
impl->delay_samples, out, in, SampleCount,
impl->port[4][0], impl->port[5][0]);
}
if (impl->port[3] != NULL)
impl->port[3][0] = impl->latency;
@ -1359,6 +1360,16 @@ static struct spa_fga_port delay_ports[] = {
.hint = SPA_FGA_HINT_LATENCY,
.flags = SPA_FGA_PORT_OUTPUT | SPA_FGA_PORT_CONTROL,
},
{ .index = 4,
.name = "Feedback",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
.def = 0.0f, .min = -10.0f, .max = 10.0f
},
{ .index = 5,
.name = "Feedforward",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
.def = 0.0f, .min = -10.0f, .max = 10.0f
},
};
static const struct spa_fga_descriptor delay_desc = {