mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-02-25 01:40:36 -05:00
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:
parent
933ac4be43
commit
52ec847cbd
6 changed files with 104 additions and 37 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue