filter-chain: add the 2 stage convolver

So that the tail is processed with a larger fft size to make it
faster for larger convolutions.
This commit is contained in:
Wim Taymans 2021-08-16 20:31:43 +02:00
parent 6c9f342775
commit ee6d363481
3 changed files with 200 additions and 29 deletions

View file

@ -452,7 +452,7 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
const char *val; const char *val;
char key[256]; char key[256];
char filename[PATH_MAX] = ""; char filename[PATH_MAX] = "";
int blocksize = 0; int blocksize = 0, tailsize = 0;
int delay = 0; int delay = 0;
float gain = 1.0f; float gain = 1.0f;
@ -468,6 +468,10 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
if (spa_json_get_int(&it[1], &blocksize) <= 0) if (spa_json_get_int(&it[1], &blocksize) <= 0)
return NULL; return NULL;
} }
else if (spa_streq(key, "tailsize")) {
if (spa_json_get_int(&it[1], &tailsize) <= 0)
return NULL;
}
else if (spa_streq(key, "gain")) { else if (spa_streq(key, "gain")) {
if (spa_json_get_float(&it[1], &gain) <= 0) if (spa_json_get_float(&it[1], &gain) <= 0)
return NULL; return NULL;
@ -509,6 +513,10 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
if (blocksize <= 0) if (blocksize <= 0)
blocksize = SPA_CLAMP(n_samples, 64, 256); blocksize = SPA_CLAMP(n_samples, 64, 256);
if (tailsize <= 0)
tailsize = SPA_CLAMP(4096, blocksize, 4096);
pw_log_info("using %d:%d blocksize ir:%s", blocksize, tailsize, filename);
impl = calloc(1, sizeof(*impl)); impl = calloc(1, sizeof(*impl));
if (impl == NULL) if (impl == NULL)
@ -516,7 +524,7 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
impl->rate = SampleRate; impl->rate = SampleRate;
impl->conv = convolver_new(blocksize, samples, n_samples); impl->conv = convolver_new(blocksize, tailsize, samples, n_samples);
free(samples); free(samples);
return impl; return impl;

View file

@ -1,24 +1,29 @@
// ================================================================================== /* PipeWire
// Copyright (c) 2017 HiFi-LoFi *
// * Copyright (c) 2017 HiFi-LoFi
// Permission is hereby granted, free of charge, to any person obtaining a copy * Copyright © 2021 Wim Taymans
// of this software and associated documentation files (the "Software"), to deal *
// in the Software without restriction, including without limitation the rights * Permission is hereby granted, free of charge, to any person obtaining a
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copy of this software and associated documentation files (the "Software"),
// copies of the Software, and to permit persons to whom the Software is furnished * to deal in the Software without restriction, including without limitation
// to do so, subject to the following conditions: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
// * and/or sell copies of the Software, and to permit persons to whom the
// The above copyright notice and this permission notice shall be included in * Software is furnished to do so, subject to the following conditions:
// all copies or substantial portions of the Software. *
// * The above copyright notice and this permission notice (including the next
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * paragraph) shall be included in all copies or substantial portions of the
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * Software.
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR *
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// ================================================================================== * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Adapted from https://github.com/HiFi-LoFi/FFTConvolver
*/
#include "convolver.h" #include "convolver.h"
#include <spa/utils/defs.h> #include <spa/utils/defs.h>
@ -26,7 +31,7 @@
#include "kiss_fft_f32.h" #include "kiss_fft_f32.h"
#include "kiss_fftr_f32.h" #include "kiss_fftr_f32.h"
struct convolver { struct convolver1 {
int blockSize; int blockSize;
int segSize; int segSize;
int segCount; int segCount;
@ -58,9 +63,9 @@ static int next_power_of_two(int val)
return r; return r;
} }
struct convolver *convolver_new(int block, const float *ir, int irlen) static struct convolver1 *convolver1_new(int block, const float *ir, int irlen)
{ {
struct convolver *conv; struct convolver1 *conv;
int i; int i;
if (block == 0) if (block == 0)
@ -118,8 +123,22 @@ struct convolver *convolver_new(int block, const float *ir, int irlen)
return conv; return conv;
} }
void convolver_free(struct convolver *conv) static void convolver1_free(struct convolver1 *conv)
{ {
int i;
for (i = 0; i < conv->segCount; i++) {
free(conv->segments[i]);
free(conv->segmentsIr[i]);
}
free(conv->fft);
free(conv->ifft);
free(conv->fft_buffer);
free(conv->segments);
free(conv->segmentsIr);
free(conv->pre_mult);
free(conv->conv);
free(conv->overlap);
free(conv->inputBuffer);
free(conv); free(conv);
} }
@ -139,7 +158,7 @@ void ComplexMultiplyAccumulate(kiss_fft_f32_cpx *r, const kiss_fft_f32_cpx *a, c
} }
} }
int convolver_run(struct convolver *conv, const float *input, float *output, int len) static int convolver1_run(struct convolver1 *conv, const float *input, float *output, int len)
{ {
int i, processed = 0; int i, processed = 0;
@ -198,3 +217,147 @@ int convolver_run(struct convolver *conv, const float *input, float *output, int
} }
return len; return len;
} }
struct convolver
{
int headBlockSize;
int tailBlockSize;
struct convolver1 *headConvolver;
struct convolver1 *tailConvolver0;
float *tailOutput0;
float *tailPrecalculated0;
struct convolver1 *tailConvolver;
float *tailOutput;
float *tailPrecalculated;
float *tailInput;
int tailInputFill;
int precalculatedPos;
};
struct convolver *convolver_new(int head_block, int tail_block, const float *ir, int irlen)
{
struct convolver *conv;
int head_ir_len;
if (head_block == 0 || tail_block == 0)
return NULL;
head_block = SPA_MAX(1, head_block);
if (head_block > tail_block)
SPA_SWAP(head_block, tail_block);
while (irlen > 0 && fabs(ir[irlen-1]) < 0.000001f)
irlen--;
conv = calloc(1, sizeof(*conv));
if (conv == NULL)
return NULL;
if (irlen == 0)
return conv;
conv->headBlockSize = next_power_of_two(head_block);
conv->tailBlockSize = next_power_of_two(tail_block);
head_ir_len = SPA_MIN(irlen, conv->tailBlockSize);
conv->headConvolver = convolver1_new(conv->headBlockSize, ir, head_ir_len);
if (irlen > conv->tailBlockSize) {
int conv1IrLen = SPA_MIN(irlen - conv->tailBlockSize, conv->tailBlockSize);
conv->tailConvolver0 = convolver1_new(conv->headBlockSize, ir + conv->tailBlockSize, conv1IrLen);
conv->tailOutput0 = calloc(conv->tailBlockSize, sizeof(float));
conv->tailPrecalculated0 = calloc(conv->tailBlockSize, sizeof(float));
}
if (irlen > 2 * conv->tailBlockSize) {
int tailIrLen = irlen - (2 * conv->tailBlockSize);
conv->tailConvolver = convolver1_new(conv->tailBlockSize, ir + (2 * conv->tailBlockSize), tailIrLen);
conv->tailOutput = calloc(conv->tailBlockSize, sizeof(float));
conv->tailPrecalculated = calloc(conv->tailBlockSize, sizeof(float));
}
if (conv->tailConvolver0 || conv->tailConvolver)
conv->tailInput = calloc(conv->tailBlockSize, sizeof(float));
conv->tailInputFill = 0;
conv->precalculatedPos = 0;
return conv;
}
void convolver_free(struct convolver *conv)
{
if (conv->headConvolver)
convolver1_free(conv->headConvolver);
if (conv->tailConvolver0)
convolver1_free(conv->tailConvolver0);
if (conv->tailConvolver)
convolver1_free(conv->tailConvolver);
free(conv->tailOutput0);
free(conv->tailPrecalculated0);
free(conv->tailOutput);
free(conv->tailPrecalculated);
free(conv->tailInput);
free(conv);
}
int convolver_run(struct convolver *conv, const float *input, float *output, int length)
{
int i;
convolver1_run(conv->headConvolver, input, output, length);
if (conv->tailInput) {
int processed = 0;
while (processed < length) {
int remaining = length - processed;
int processing = SPA_MIN(remaining, conv->headBlockSize - (conv->tailInputFill % conv->headBlockSize));
const int sumBegin = processed;
const int sumEnd = processed + processing;
if (conv->tailPrecalculated0) {
int precalculatedPos = conv->precalculatedPos;
for (i = sumBegin; i < sumEnd; i++) {
output[i] += conv->tailPrecalculated0[precalculatedPos];
precalculatedPos++;
}
}
if (conv->tailPrecalculated) {
int precalculatedPos = conv->precalculatedPos;
for (i = sumBegin; i < sumEnd; i++) {
output[i] += conv->tailPrecalculated[precalculatedPos];
precalculatedPos++;
}
}
conv->precalculatedPos += processing;
memcpy(conv->tailInput + conv->tailInputFill, input + processed, processing * sizeof(float));
conv->tailInputFill += processing;
if (conv->tailPrecalculated0 && (conv->tailInputFill % conv->headBlockSize == 0)) {
int blockOffset = conv->tailInputFill - conv->headBlockSize;
convolver1_run(conv->tailConvolver0,
conv->tailInput + blockOffset,
conv->tailOutput0 + blockOffset,
conv->headBlockSize);
if (conv->tailInputFill == conv->tailBlockSize)
SPA_SWAP(conv->tailPrecalculated0, conv->tailOutput0);
}
if (conv->tailPrecalculated &&
conv->tailInputFill == conv->tailBlockSize) {
SPA_SWAP(conv->tailPrecalculated, conv->tailOutput);
convolver1_run(conv->tailConvolver, conv->tailInput, conv->tailOutput, conv->tailBlockSize);
}
if (conv->tailInputFill == conv->tailBlockSize) {
conv->tailInputFill = 0;
conv->precalculatedPos = 0;
}
processed += processing;
}
}
return 0;
}

View file

@ -25,7 +25,7 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
struct convolver *convolver_new(int block, const float *ir, int irlen); struct convolver *convolver_new(int block, int tail, const float *ir, int irlen);
void convolver_free(struct convolver *conv); void convolver_free(struct convolver *conv);
int convolver_run(struct convolver *conv, const float *input, float *output, int length); int convolver_run(struct convolver *conv, const float *input, float *output, int length);