convolver: convolver1 -> partition

This commit is contained in:
Wim Taymans 2026-04-16 16:12:33 +02:00
parent d4b472d2e5
commit 37b648a3e0

View file

@ -10,7 +10,7 @@
#include <math.h> #include <math.h>
struct convolver1 { struct partition {
int blockSize; int blockSize;
int segSize; int segSize;
int segCount; int segCount;
@ -42,48 +42,48 @@ static int next_power_of_two(int val)
return r; return r;
} }
static void convolver1_reset(struct spa_fga_dsp *dsp, struct convolver1 *conv) static void partition_reset(struct spa_fga_dsp *dsp, struct partition *part)
{ {
int i; int i;
for (i = 0; i < conv->segCount; i++) for (i = 0; i < part->segCount; i++)
spa_fga_dsp_fft_memclear(dsp, conv->segments[i], conv->fftComplexSize, false); spa_fga_dsp_fft_memclear(dsp, part->segments[i], part->fftComplexSize, false);
spa_fga_dsp_fft_memclear(dsp, conv->fft_buffer[0], conv->segSize, true); spa_fga_dsp_fft_memclear(dsp, part->fft_buffer[0], part->segSize, true);
spa_fga_dsp_fft_memclear(dsp, conv->fft_buffer[1], conv->segSize, true); spa_fga_dsp_fft_memclear(dsp, part->fft_buffer[1], part->segSize, true);
spa_fga_dsp_fft_memclear(dsp, conv->inputBuffer, conv->segSize, true); spa_fga_dsp_fft_memclear(dsp, part->inputBuffer, part->segSize, true);
spa_fga_dsp_fft_memclear(dsp, conv->pre_mult, conv->fftComplexSize, false); spa_fga_dsp_fft_memclear(dsp, part->pre_mult, part->fftComplexSize, false);
spa_fga_dsp_fft_memclear(dsp, conv->conv, conv->fftComplexSize, false); spa_fga_dsp_fft_memclear(dsp, part->conv, part->fftComplexSize, false);
conv->inputBufferFill = 0; part->inputBufferFill = 0;
conv->current = 0; part->current = 0;
} }
static void convolver1_free(struct spa_fga_dsp *dsp, struct convolver1 *conv) static void partition_free(struct spa_fga_dsp *dsp, struct partition *part)
{ {
int i; int i;
for (i = 0; i < conv->segCount; i++) { for (i = 0; i < part->segCount; i++) {
if (conv->segments) if (part->segments)
spa_fga_dsp_fft_memfree(dsp, conv->segments[i]); spa_fga_dsp_fft_memfree(dsp, part->segments[i]);
if (conv->segmentsIr) if (part->segmentsIr)
spa_fga_dsp_fft_memfree(dsp, conv->segmentsIr[i]); spa_fga_dsp_fft_memfree(dsp, part->segmentsIr[i]);
} }
if (conv->fft) if (part->fft)
spa_fga_dsp_fft_free(dsp, conv->fft); spa_fga_dsp_fft_free(dsp, part->fft);
if (conv->ifft) if (part->ifft)
spa_fga_dsp_fft_free(dsp, conv->ifft); spa_fga_dsp_fft_free(dsp, part->ifft);
if (conv->fft_buffer[0]) if (part->fft_buffer[0])
spa_fga_dsp_fft_memfree(dsp, conv->fft_buffer[0]); spa_fga_dsp_fft_memfree(dsp, part->fft_buffer[0]);
if (conv->fft_buffer[1]) if (part->fft_buffer[1])
spa_fga_dsp_fft_memfree(dsp, conv->fft_buffer[1]); spa_fga_dsp_fft_memfree(dsp, part->fft_buffer[1]);
free(conv->segments); free(part->segments);
free(conv->segmentsIr); free(part->segmentsIr);
spa_fga_dsp_fft_memfree(dsp, conv->pre_mult); spa_fga_dsp_fft_memfree(dsp, part->pre_mult);
spa_fga_dsp_fft_memfree(dsp, conv->conv); spa_fga_dsp_fft_memfree(dsp, part->conv);
spa_fga_dsp_fft_memfree(dsp, conv->inputBuffer); spa_fga_dsp_fft_memfree(dsp, part->inputBuffer);
free(conv); free(part);
} }
static struct convolver1 *convolver1_new(struct spa_fga_dsp *dsp, int block, const float *ir, int irlen) static struct partition *partition_new(struct spa_fga_dsp *dsp, int block, const float *ir, int irlen)
{ {
struct convolver1 *conv; struct partition *part;
int i; int i;
if (block == 0) if (block == 0)
@ -92,139 +92,139 @@ static struct convolver1 *convolver1_new(struct spa_fga_dsp *dsp, int block, con
while (irlen > 0 && fabs(ir[irlen-1]) < 0.000001f) while (irlen > 0 && fabs(ir[irlen-1]) < 0.000001f)
irlen--; irlen--;
conv = calloc(1, sizeof(*conv)); part = calloc(1, sizeof(*part));
if (conv == NULL) if (part == NULL)
return NULL; return NULL;
if (irlen == 0) if (irlen == 0)
return conv; return part;
conv->blockSize = next_power_of_two(block); part->blockSize = next_power_of_two(block);
conv->segSize = 2 * conv->blockSize; part->segSize = 2 * part->blockSize;
conv->segCount = (irlen + conv->blockSize-1) / conv->blockSize; part->segCount = (irlen + part->blockSize-1) / part->blockSize;
conv->fftComplexSize = (conv->segSize / 2) + 1; part->fftComplexSize = (part->segSize / 2) + 1;
conv->fft = spa_fga_dsp_fft_new(dsp, conv->segSize, true); part->fft = spa_fga_dsp_fft_new(dsp, part->segSize, true);
if (conv->fft == NULL) if (part->fft == NULL)
goto error; goto error;
conv->ifft = spa_fga_dsp_fft_new(dsp, conv->segSize, true); part->ifft = spa_fga_dsp_fft_new(dsp, part->segSize, true);
if (conv->ifft == NULL) if (part->ifft == NULL)
goto error; goto error;
conv->fft_buffer[0] = spa_fga_dsp_fft_memalloc(dsp, conv->segSize, true); part->fft_buffer[0] = spa_fga_dsp_fft_memalloc(dsp, part->segSize, true);
conv->fft_buffer[1] = spa_fga_dsp_fft_memalloc(dsp, conv->segSize, true); part->fft_buffer[1] = spa_fga_dsp_fft_memalloc(dsp, part->segSize, true);
if (conv->fft_buffer[0] == NULL || conv->fft_buffer[1] == NULL) if (part->fft_buffer[0] == NULL || part->fft_buffer[1] == NULL)
goto error; goto error;
conv->segments = calloc(conv->segCount, sizeof(float*)); part->segments = calloc(part->segCount, sizeof(float*));
conv->segmentsIr = calloc(conv->segCount, sizeof(float*)); part->segmentsIr = calloc(part->segCount, sizeof(float*));
if (conv->segments == NULL || conv->segmentsIr == NULL) if (part->segments == NULL || part->segmentsIr == NULL)
goto error; goto error;
for (i = 0; i < conv->segCount; i++) { for (i = 0; i < part->segCount; i++) {
int left = irlen - (i * conv->blockSize); int left = irlen - (i * part->blockSize);
int copy = SPA_MIN(conv->blockSize, left); int copy = SPA_MIN(part->blockSize, left);
conv->segments[i] = spa_fga_dsp_fft_memalloc(dsp, conv->fftComplexSize, false); part->segments[i] = spa_fga_dsp_fft_memalloc(dsp, part->fftComplexSize, false);
conv->segmentsIr[i] = spa_fga_dsp_fft_memalloc(dsp, conv->fftComplexSize, false); part->segmentsIr[i] = spa_fga_dsp_fft_memalloc(dsp, part->fftComplexSize, false);
if (conv->segments[i] == NULL || conv->segmentsIr[i] == NULL) if (part->segments[i] == NULL || part->segmentsIr[i] == NULL)
goto error; goto error;
spa_fga_dsp_copy(dsp, conv->fft_buffer[0], &ir[i * conv->blockSize], copy); spa_fga_dsp_copy(dsp, part->fft_buffer[0], &ir[i * part->blockSize], copy);
if (copy < conv->segSize) if (copy < part->segSize)
spa_fga_dsp_fft_memclear(dsp, conv->fft_buffer[0] + copy, conv->segSize - copy, true); spa_fga_dsp_fft_memclear(dsp, part->fft_buffer[0] + copy, part->segSize - copy, true);
spa_fga_dsp_fft_run(dsp, conv->fft, 1, conv->fft_buffer[0], conv->segmentsIr[i]); spa_fga_dsp_fft_run(dsp, part->fft, 1, part->fft_buffer[0], part->segmentsIr[i]);
} }
conv->pre_mult = spa_fga_dsp_fft_memalloc(dsp, conv->fftComplexSize, false); part->pre_mult = spa_fga_dsp_fft_memalloc(dsp, part->fftComplexSize, false);
conv->conv = spa_fga_dsp_fft_memalloc(dsp, conv->fftComplexSize, false); part->conv = spa_fga_dsp_fft_memalloc(dsp, part->fftComplexSize, false);
conv->inputBuffer = spa_fga_dsp_fft_memalloc(dsp, conv->segSize, true); part->inputBuffer = spa_fga_dsp_fft_memalloc(dsp, part->segSize, true);
if (conv->pre_mult == NULL || conv->conv == NULL || conv->inputBuffer == NULL) if (part->pre_mult == NULL || part->conv == NULL || part->inputBuffer == NULL)
goto error; goto error;
conv->scale = 1.0f / conv->segSize; part->scale = 1.0f / part->segSize;
convolver1_reset(dsp, conv); partition_reset(dsp, part);
return conv; return part;
error: error:
convolver1_free(dsp, conv); partition_free(dsp, part);
return NULL; return NULL;
} }
static int convolver1_run(struct spa_fga_dsp *dsp, struct convolver1 *conv, const float *input, float *output, int len) static int partition_run(struct spa_fga_dsp *dsp, struct partition *part, const float *input, float *output, int len)
{ {
int i, processed = 0; int i, processed = 0;
if (conv == NULL || conv->segCount == 0) { if (part == NULL || part->segCount == 0) {
spa_fga_dsp_fft_memclear(dsp, output, len, true); spa_fga_dsp_fft_memclear(dsp, output, len, true);
return len; return len;
} }
int inputBufferFill = conv->inputBufferFill; int inputBufferFill = part->inputBufferFill;
while (processed < len) { while (processed < len) {
const int processing = SPA_MIN(len - processed, conv->blockSize - inputBufferFill); const int processing = SPA_MIN(len - processed, part->blockSize - inputBufferFill);
spa_fga_dsp_copy(dsp, conv->inputBuffer + inputBufferFill, input + processed, processing); spa_fga_dsp_copy(dsp, part->inputBuffer + inputBufferFill, input + processed, processing);
if (inputBufferFill == 0 && processing < conv->blockSize) if (inputBufferFill == 0 && processing < part->blockSize)
spa_fga_dsp_fft_memclear(dsp, conv->inputBuffer + processing, spa_fga_dsp_fft_memclear(dsp, part->inputBuffer + processing,
conv->blockSize - processing, true); part->blockSize - processing, true);
spa_fga_dsp_fft_run(dsp, conv->fft, 1, conv->inputBuffer, conv->segments[conv->current]); spa_fga_dsp_fft_run(dsp, part->fft, 1, part->inputBuffer, part->segments[part->current]);
if (conv->segCount > 1) { if (part->segCount > 1) {
if (inputBufferFill == 0) { if (inputBufferFill == 0) {
int indexAudio = conv->current; int indexAudio = part->current;
if (++indexAudio == conv->segCount) if (++indexAudio == part->segCount)
indexAudio = 0; indexAudio = 0;
spa_fga_dsp_fft_cmul(dsp, conv->fft, conv->pre_mult, spa_fga_dsp_fft_cmul(dsp, part->fft, part->pre_mult,
conv->segmentsIr[1], part->segmentsIr[1],
conv->segments[indexAudio], part->segments[indexAudio],
conv->fftComplexSize, conv->scale); part->fftComplexSize, part->scale);
for (i = 2; i < conv->segCount; i++) { for (i = 2; i < part->segCount; i++) {
if (++indexAudio == conv->segCount) if (++indexAudio == part->segCount)
indexAudio = 0; indexAudio = 0;
spa_fga_dsp_fft_cmuladd(dsp, conv->fft, spa_fga_dsp_fft_cmuladd(dsp, part->fft,
conv->pre_mult, part->pre_mult,
conv->pre_mult, part->pre_mult,
conv->segmentsIr[i], part->segmentsIr[i],
conv->segments[indexAudio], part->segments[indexAudio],
conv->fftComplexSize, conv->scale); part->fftComplexSize, part->scale);
} }
} }
spa_fga_dsp_fft_cmuladd(dsp, conv->fft, spa_fga_dsp_fft_cmuladd(dsp, part->fft,
conv->conv, part->conv,
conv->pre_mult, part->pre_mult,
conv->segments[conv->current], part->segments[part->current],
conv->segmentsIr[0], part->segmentsIr[0],
conv->fftComplexSize, conv->scale); part->fftComplexSize, part->scale);
} else { } else {
spa_fga_dsp_fft_cmul(dsp, conv->fft, spa_fga_dsp_fft_cmul(dsp, part->fft,
conv->conv, part->conv,
conv->segments[conv->current], part->segments[part->current],
conv->segmentsIr[0], part->segmentsIr[0],
conv->fftComplexSize, conv->scale); part->fftComplexSize, part->scale);
} }
spa_fga_dsp_fft_run(dsp, conv->ifft, -1, conv->conv, conv->fft_buffer[0]); spa_fga_dsp_fft_run(dsp, part->ifft, -1, part->conv, part->fft_buffer[0]);
spa_fga_dsp_sum(dsp, output + processed, conv->fft_buffer[0] + inputBufferFill, spa_fga_dsp_sum(dsp, output + processed, part->fft_buffer[0] + inputBufferFill,
conv->fft_buffer[1] + conv->blockSize + inputBufferFill, processing); part->fft_buffer[1] + part->blockSize + inputBufferFill, processing);
inputBufferFill += processing; inputBufferFill += processing;
if (inputBufferFill == conv->blockSize) { if (inputBufferFill == part->blockSize) {
inputBufferFill = 0; inputBufferFill = 0;
SPA_SWAP(conv->fft_buffer[0], conv->fft_buffer[1]); SPA_SWAP(part->fft_buffer[0], part->fft_buffer[1]);
if (conv->current == 0) if (part->current == 0)
conv->current = conv->segCount; part->current = part->segCount;
conv->current--; part->current--;
} }
processed += processing; processed += processing;
} }
conv->inputBufferFill = inputBufferFill; part->inputBufferFill = inputBufferFill;
return len; return len;
} }
@ -233,11 +233,11 @@ struct convolver
struct spa_fga_dsp *dsp; struct spa_fga_dsp *dsp;
int headBlockSize; int headBlockSize;
int tailBlockSize; int tailBlockSize;
struct convolver1 *headConvolver; struct partition *headPartition;
struct convolver1 *tailConvolver0; struct partition *tailPartition0;
float *tailOutput0; float *tailOutput0;
float *tailPrecalculated0; float *tailPrecalculated0;
struct convolver1 *tailConvolver; struct partition *tailPartition;
float *tailOutput; float *tailOutput;
float *tailPrecalculated; float *tailPrecalculated;
float *tailInput; float *tailInput;
@ -248,15 +248,15 @@ void convolver_reset(struct convolver *conv)
{ {
struct spa_fga_dsp *dsp = conv->dsp; struct spa_fga_dsp *dsp = conv->dsp;
if (conv->headConvolver) if (conv->headPartition)
convolver1_reset(dsp, conv->headConvolver); partition_reset(dsp, conv->headPartition);
if (conv->tailConvolver0) { if (conv->tailPartition0) {
convolver1_reset(dsp, conv->tailConvolver0); partition_reset(dsp, conv->tailPartition0);
spa_fga_dsp_fft_memclear(dsp, conv->tailOutput0, conv->tailBlockSize, true); spa_fga_dsp_fft_memclear(dsp, conv->tailOutput0, conv->tailBlockSize, true);
spa_fga_dsp_fft_memclear(dsp, conv->tailPrecalculated0, conv->tailBlockSize, true); spa_fga_dsp_fft_memclear(dsp, conv->tailPrecalculated0, conv->tailBlockSize, true);
} }
if (conv->tailConvolver) { if (conv->tailPartition) {
convolver1_reset(dsp, conv->tailConvolver); partition_reset(dsp, conv->tailPartition);
spa_fga_dsp_fft_memclear(dsp, conv->tailOutput, conv->tailBlockSize, true); spa_fga_dsp_fft_memclear(dsp, conv->tailOutput, conv->tailBlockSize, true);
spa_fga_dsp_fft_memclear(dsp, conv->tailPrecalculated, conv->tailBlockSize, true); spa_fga_dsp_fft_memclear(dsp, conv->tailPrecalculated, conv->tailBlockSize, true);
} }
@ -291,31 +291,31 @@ struct convolver *convolver_new(struct spa_fga_dsp *dsp, int head_block, int tai
conv->tailBlockSize = next_power_of_two(tail_block); conv->tailBlockSize = next_power_of_two(tail_block);
head_ir_len = SPA_MIN(irlen, conv->tailBlockSize); head_ir_len = SPA_MIN(irlen, conv->tailBlockSize);
conv->headConvolver = convolver1_new(dsp, conv->headBlockSize, ir, head_ir_len); conv->headPartition = partition_new(dsp, conv->headBlockSize, ir, head_ir_len);
if (conv->headConvolver == NULL) if (conv->headPartition == NULL)
goto error; goto error;
if (irlen > conv->tailBlockSize) { if (irlen > conv->tailBlockSize) {
int conv1IrLen = SPA_MIN(irlen - conv->tailBlockSize, conv->tailBlockSize); int conv1IrLen = SPA_MIN(irlen - conv->tailBlockSize, conv->tailBlockSize);
conv->tailConvolver0 = convolver1_new(dsp, conv->headBlockSize, ir + conv->tailBlockSize, conv1IrLen); conv->tailPartition0 = partition_new(dsp, conv->headBlockSize, ir + conv->tailBlockSize, conv1IrLen);
conv->tailOutput0 = spa_fga_dsp_fft_memalloc(dsp, conv->tailBlockSize, true); conv->tailOutput0 = spa_fga_dsp_fft_memalloc(dsp, conv->tailBlockSize, true);
conv->tailPrecalculated0 = spa_fga_dsp_fft_memalloc(dsp, conv->tailBlockSize, true); conv->tailPrecalculated0 = spa_fga_dsp_fft_memalloc(dsp, conv->tailBlockSize, true);
if (conv->tailConvolver0 == NULL || conv->tailOutput0 == NULL || if (conv->tailPartition0 == NULL || conv->tailOutput0 == NULL ||
conv->tailPrecalculated0 == NULL) conv->tailPrecalculated0 == NULL)
goto error; goto error;
} }
if (irlen > 2 * conv->tailBlockSize) { if (irlen > 2 * conv->tailBlockSize) {
int tailIrLen = irlen - (2 * conv->tailBlockSize); int tailIrLen = irlen - (2 * conv->tailBlockSize);
conv->tailConvolver = convolver1_new(dsp, conv->tailBlockSize, ir + (2 * conv->tailBlockSize), tailIrLen); conv->tailPartition = partition_new(dsp, conv->tailBlockSize, ir + (2 * conv->tailBlockSize), tailIrLen);
conv->tailOutput = spa_fga_dsp_fft_memalloc(dsp, conv->tailBlockSize, true); conv->tailOutput = spa_fga_dsp_fft_memalloc(dsp, conv->tailBlockSize, true);
conv->tailPrecalculated = spa_fga_dsp_fft_memalloc(dsp, conv->tailBlockSize, true); conv->tailPrecalculated = spa_fga_dsp_fft_memalloc(dsp, conv->tailBlockSize, true);
if (conv->tailConvolver == NULL || conv->tailOutput == NULL || if (conv->tailPartition == NULL || conv->tailOutput == NULL ||
conv->tailPrecalculated == NULL) conv->tailPrecalculated == NULL)
goto error; goto error;
} }
if (conv->tailConvolver0 || conv->tailConvolver) { if (conv->tailPartition0 || conv->tailPartition) {
conv->tailInput = spa_fga_dsp_fft_memalloc(dsp, conv->tailBlockSize, true); conv->tailInput = spa_fga_dsp_fft_memalloc(dsp, conv->tailBlockSize, true);
if (conv->tailInput == NULL) if (conv->tailInput == NULL)
goto error; goto error;
@ -333,12 +333,12 @@ void convolver_free(struct convolver *conv)
{ {
struct spa_fga_dsp *dsp = conv->dsp; struct spa_fga_dsp *dsp = conv->dsp;
if (conv->headConvolver) if (conv->headPartition)
convolver1_free(dsp, conv->headConvolver); partition_free(dsp, conv->headPartition);
if (conv->tailConvolver0) if (conv->tailPartition0)
convolver1_free(dsp, conv->tailConvolver0); partition_free(dsp, conv->tailPartition0);
if (conv->tailConvolver) if (conv->tailPartition)
convolver1_free(dsp, conv->tailConvolver); partition_free(dsp, conv->tailPartition);
spa_fga_dsp_fft_memfree(dsp, conv->tailOutput0); spa_fga_dsp_fft_memfree(dsp, conv->tailOutput0);
spa_fga_dsp_fft_memfree(dsp, conv->tailPrecalculated0); spa_fga_dsp_fft_memfree(dsp, conv->tailPrecalculated0);
spa_fga_dsp_fft_memfree(dsp, conv->tailOutput); spa_fga_dsp_fft_memfree(dsp, conv->tailOutput);
@ -351,7 +351,7 @@ int convolver_run(struct convolver *conv, const float *input, float *output, int
{ {
struct spa_fga_dsp *dsp = conv->dsp; struct spa_fga_dsp *dsp = conv->dsp;
convolver1_run(dsp, conv->headConvolver, input, output, length); partition_run(dsp, conv->headPartition, input, output, length);
if (conv->tailInput) { if (conv->tailInput) {
int processed = 0; int processed = 0;
@ -374,7 +374,7 @@ int convolver_run(struct convolver *conv, const float *input, float *output, int
if (conv->tailPrecalculated0 && (conv->tailInputFill % conv->headBlockSize == 0)) { if (conv->tailPrecalculated0 && (conv->tailInputFill % conv->headBlockSize == 0)) {
int blockOffset = conv->tailInputFill - conv->headBlockSize; int blockOffset = conv->tailInputFill - conv->headBlockSize;
convolver1_run(dsp, conv->tailConvolver0, partition_run(dsp, conv->tailPartition0,
conv->tailInput + blockOffset, conv->tailInput + blockOffset,
conv->tailOutput0 + blockOffset, conv->tailOutput0 + blockOffset,
conv->headBlockSize); conv->headBlockSize);
@ -385,7 +385,7 @@ int convolver_run(struct convolver *conv, const float *input, float *output, int
if (conv->tailPrecalculated && if (conv->tailPrecalculated &&
conv->tailInputFill == conv->tailBlockSize) { conv->tailInputFill == conv->tailBlockSize) {
SPA_SWAP(conv->tailPrecalculated, conv->tailOutput); SPA_SWAP(conv->tailPrecalculated, conv->tailOutput);
convolver1_run(dsp, conv->tailConvolver, conv->tailInput, partition_run(dsp, conv->tailPartition, conv->tailInput,
conv->tailOutput, conv->tailBlockSize); conv->tailOutput, conv->tailBlockSize);
} }
if (conv->tailInputFill == conv->tailBlockSize) if (conv->tailInputFill == conv->tailBlockSize)