diff --git a/src/modules/module-filter-chain/builtin_plugin.c b/src/modules/module-filter-chain/builtin_plugin.c index dd0455e9a..8ccaff382 100644 --- a/src/modules/module-filter-chain/builtin_plugin.c +++ b/src/modules/module-filter-chain/builtin_plugin.c @@ -452,7 +452,7 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor, const char *val; char key[256]; char filename[PATH_MAX] = ""; - int blocksize = 0; + int blocksize = 0, tailsize = 0; int delay = 0; 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) 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")) { if (spa_json_get_float(&it[1], &gain) <= 0) return NULL; @@ -509,6 +513,10 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor, if (blocksize <= 0) 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)); if (impl == NULL) @@ -516,7 +524,7 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor, impl->rate = SampleRate; - impl->conv = convolver_new(blocksize, samples, n_samples); + impl->conv = convolver_new(blocksize, tailsize, samples, n_samples); free(samples); return impl; diff --git a/src/modules/module-filter-chain/convolver.c b/src/modules/module-filter-chain/convolver.c index 7a46cbbc1..29f5d2e44 100644 --- a/src/modules/module-filter-chain/convolver.c +++ b/src/modules/module-filter-chain/convolver.c @@ -1,24 +1,29 @@ -// ================================================================================== -// Copyright (c) 2017 HiFi-LoFi -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is furnished -// to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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. -// ================================================================================== - +/* PipeWire + * + * Copyright (c) 2017 HiFi-LoFi + * Copyright © 2021 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * 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 @@ -26,7 +31,7 @@ #include "kiss_fft_f32.h" #include "kiss_fftr_f32.h" -struct convolver { +struct convolver1 { int blockSize; int segSize; int segCount; @@ -58,9 +63,9 @@ static int next_power_of_two(int val) 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; if (block == 0) @@ -118,8 +123,22 @@ struct convolver *convolver_new(int block, const float *ir, int irlen) 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); } @@ -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; @@ -198,3 +217,147 @@ int convolver_run(struct convolver *conv, const float *input, float *output, int } 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; +} diff --git a/src/modules/module-filter-chain/convolver.h b/src/modules/module-filter-chain/convolver.h index fa8d71127..d8cea5faf 100644 --- a/src/modules/module-filter-chain/convolver.h +++ b/src/modules/module-filter-chain/convolver.h @@ -25,7 +25,7 @@ #include #include -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); int convolver_run(struct convolver *conv, const float *input, float *output, int length);