Skip to content

Commit a0548da

Browse files
committed
zita_convolver: Use fir_parse_opts() and fir_read_filter().
Also provide zita_convolver_init_with_filter() and make use of Convproc.impdata_link().
1 parent 6953fc8 commit a0548da

File tree

5 files changed

+78
-73
lines changed

5 files changed

+78
-73
lines changed

README.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,14 @@ Example:
307307
one filter channel. Missing values are filled with zeros.
308308

309309
See the `fir` effect description an explanation of the `input_options`.
310-
* `zita_convolver [min_part_len [max_part_len]] [~/]filter_path`
310+
* `zita_convolver [input_options] [min_part_len [max_part_len]] [file:][~/]filter_path|coefs:list[/list...]`
311311
Partitioned 32-bit FFT convolution using the zita-convolver library.
312312
Latency is equal to `min_part_len` (64 samples by default).
313-
`{min,max}_part_len` must be powers of 2 between 64 and 8192.
313+
`{min,max}_part_len` must be powers of 2 between 64 and 8192. Each `list`
314+
is a comma-separated list of coefficients for one filter channel. Missing
315+
values are filled with zeros.
316+
317+
See the `fir` effect description an explanation of the `input_options`.
314318
* `hilbert [-p] taps`
315319
Simple FIR approximation of a Hilbert transform. The number of taps must be
316320
odd. Bandwidth is controlled by the number of taps. If `-p` is given, the

dsp.1

+6-2
Original file line numberDiff line numberDiff line change
@@ -327,10 +327,14 @@ one filter channel. Missing values are filled with zeros.
327327
.sp 0.5
328328
See the \fBfir\fR effect description for an explanation of the \fIinput_options\fR.
329329
.TP
330-
\fBzita_convolver\fR [\fImin_part_len\fR [\fImax_part_len\fR]] [~/]\fIfilter_path\fR
330+
\fBzita_convolver\fR [\fIinput_options\fR] [\fImin_part_len\fR [\fImax_part_len\fR]] [file:][~/]\fIfilter_path\fR|coefs:\fIlist\fR[/\fIlist\fR...]
331331
Partitioned 32-bit FFT convolution using the zita-convolver library.
332332
Latency is equal to \fImin_part_len\fR (64 samples by default).
333-
\fI{min,max}_part_len\fR must be powers of 2 between 64 and 8192.
333+
\fI{min,max}_part_len\fR must be powers of 2 between 64 and 8192. Each \fIlist\fR
334+
is a comma-separated list of coefficients for one filter channel. Missing
335+
values are filled with zeros.
336+
.sp 0.5
337+
See the \fBfir\fR effect description for an explanation of the \fIinput_options\fR.
334338
.TP
335339
\fBhilbert\fR [\fI\-p\fR] \fItaps\fR
336340
Simple FIR approximation of a Hilbert transform. The number of taps must be

effect.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ static const struct effect_info effects[] = {
8585
{ "fir_p", "fir_p [input_options] [max_part_len] [file:][~/]filter_path|coefs:list[/list...]", fir_p_effect_init, 0 },
8686
#endif
8787
#ifdef HAVE_ZITA_CONVOLVER
88-
{ "zita_convolver", "zita_convolver [min_part_len [max_part_len]] [~/]filter_path", zita_convolver_effect_init, 0 },
88+
{ "zita_convolver", "zita_convolver [input_options] [min_part_len [max_part_len]] [file:][~/]filter_path|coefs:list[/list...]", zita_convolver_effect_init, 0 },
8989
#endif
9090
#ifdef HAVE_FFTW3
9191
{ "hilbert", "hilbert [-p] taps", hilbert_effect_init, 0 },

zita_convolver.cpp

+63-67
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ extern "C" {
2525
#include "util.h"
2626
#include "codec.h"
2727
#include "sampleconv.h"
28+
#include "fir.h"
2829
}
2930

3031
struct zita_convolver_state {
@@ -149,82 +150,47 @@ static void write_buf_floatp(sample_t *in, float **out, int channels, ssize_t s)
149150
}
150151
}
151152

152-
struct effect * zita_convolver_effect_init(const struct effect_info *ei, const struct stream_info *istream, const char *channel_selector, const char *dir, int argc, const char *const *argv)
153+
struct effect * zita_convolver_effect_init_with_filter(const struct effect_info *ei, const struct stream_info *istream, const char *channel_selector, sample_t *filter_data, int filter_channels, ssize_t filter_frames, int min_part_len, int max_part_len)
153154
{
154-
int i, k;
155-
unsigned int min_part_len = 0, max_part_len = 0;
156155
struct effect *e;
157156
struct zita_convolver_state *state;
158-
struct codec *c_filter;
159-
Convproc *cproc;
160-
sample_t *buf_interleaved;
161-
float **buf_planar;
162-
char *endptr, *fp;
163157

164-
if (argc > 4 || argc < 2) {
165-
LOG_FMT(LL_ERROR, "%s: usage: %s", argv[0], ei->usage);
158+
const int n_channels = num_bits_set(channel_selector, istream->channels);
159+
if (n_channels > MINIMUM(Convproc::MAXINP, Convproc::MAXOUT)) {
160+
LOG_FMT(LL_ERROR, "%s: error: number of channels must not exceed %d", ei->name, MINIMUM(Convproc::MAXINP, Convproc::MAXOUT));
166161
return NULL;
167162
}
168-
if (argc > 2) {
169-
min_part_len = strtol(argv[1], &endptr, 10);
170-
CHECK_ENDPTR(argv[1], endptr, "min_part_len", return NULL);
163+
if (filter_channels != 1 && filter_channels != n_channels) {
164+
LOG_FMT(LL_ERROR, "%s: error: channel mismatch: channels=%d filter_channels=%d", ei->name, n_channels, filter_channels);
165+
return NULL;
171166
}
172-
if (argc > 3) {
173-
max_part_len = strtol(argv[2], &endptr, 10);
174-
CHECK_ENDPTR(argv[2], endptr, "max_part_len", return NULL);
167+
if (filter_frames < 1) {
168+
LOG_FMT(LL_ERROR, "%s: error: filter length must be >= 1", ei->name);
169+
return NULL;
175170
}
171+
176172
min_part_len = (min_part_len == 0) ? Convproc::MINPART : min_part_len;
177173
max_part_len = (max_part_len == 0) ? Convproc::MAXPART : max_part_len;
178174
if (min_part_len < Convproc::MINPART || min_part_len > Convproc::MAXPART || max_part_len < Convproc::MINPART || max_part_len > Convproc::MAXPART) {
179-
LOG_FMT(LL_ERROR, "%s: error: partition lengths must be within [%d,%d] or 0 for default", argv[0], Convproc::MINPART, Convproc::MAXPART);
175+
LOG_FMT(LL_ERROR, "%s: error: partition lengths must be within [%d,%d] or 0 for default", ei->name, Convproc::MINPART, Convproc::MAXPART);
180176
return NULL;
181177
}
182178
if (max_part_len < min_part_len) {
183-
LOG_FMT(LL_ERROR, "%s: warning: max_part_len < min_part_len", argv[0]);
179+
LOG_FMT(LL_ERROR, "%s: warning: max_part_len < min_part_len", ei->name);
184180
max_part_len = min_part_len;
185181
}
186182

187-
const int n_channels = num_bits_set(channel_selector, istream->channels);
188-
if (n_channels > MINIMUM(Convproc::MAXINP, Convproc::MAXOUT)) {
189-
LOG_FMT(LL_ERROR, "%s: error: number of channels must not exceed %d", argv[0], MINIMUM(Convproc::MAXINP, Convproc::MAXOUT));
190-
return NULL;
191-
}
192-
fp = construct_full_path(dir, argv[argc - 1]);
193-
struct codec_params c_params = CODEC_PARAMS_AUTO(fp, CODEC_MODE_READ);
194-
c_filter = init_codec(&c_params);
195-
if (c_filter == NULL) {
196-
LOG_FMT(LL_ERROR, "%s: error: failed to open filter file: %s", argv[0], fp);
197-
free(fp);
198-
return NULL;
199-
}
200-
free(fp);
201-
if (c_filter->channels != 1 && c_filter->channels != n_channels) {
202-
LOG_FMT(LL_ERROR, "%s: error: channel mismatch: channels=%d filter_channels=%d", argv[0], n_channels, c_filter->channels);
203-
destroy_codec(c_filter);
204-
return NULL;
205-
}
206-
if (c_filter->fs != istream->fs) {
207-
LOG_FMT(LL_ERROR, "%s: error: sample rate mismatch: fs=%d filter_fs=%d", argv[0], istream->fs, c_filter->fs);
208-
destroy_codec(c_filter);
209-
return NULL;
210-
}
211-
if (c_filter->frames < 1) {
212-
LOG_FMT(LL_ERROR, "%s: error: filter length must be >= 1", argv[0]);
213-
destroy_codec(c_filter);
214-
return NULL;
215-
}
216-
cproc = new Convproc;
183+
Convproc *cproc = new Convproc;
217184
#if ZITA_CONVOLVER_MAJOR_VERSION >= 4
218-
if (cproc->configure(n_channels, n_channels, c_filter->frames, min_part_len, min_part_len, max_part_len, 0.0f)) {
185+
if (cproc->configure(n_channels, n_channels, filter_frames, min_part_len, min_part_len, max_part_len, 0.0f)) {
219186
#else
220-
if (cproc->configure(n_channels, n_channels, c_filter->frames, min_part_len, min_part_len, max_part_len)) {
187+
if (cproc->configure(n_channels, n_channels, filter_frames, min_part_len, min_part_len, max_part_len)) {
221188
#endif
222-
LOG_FMT(LL_ERROR, "%s: error: failed to configure convolution engine", argv[0]);
223-
destroy_codec(c_filter);
189+
LOG_FMT(LL_ERROR, "%s: error: failed to configure convolution engine", ei->name);
224190
delete cproc;
225191
return NULL;
226192
}
227-
LOG_FMT(LL_VERBOSE, "%s: info: filter_frames=%zd min_part_len=%d max_part_len=%d", argv[0], c_filter->frames, min_part_len, max_part_len);
193+
LOG_FMT(LL_VERBOSE, "%s: info: filter_frames=%zd min_part_len=%d max_part_len=%d", ei->name, filter_frames, min_part_len, max_part_len);
228194

229195
e = (struct effect *) calloc(1, sizeof(struct effect));
230196
e->name = ei->name;
@@ -240,33 +206,63 @@ struct effect * zita_convolver_effect_init(const struct effect_info *ei, const s
240206
e->destroy = zita_convolver_effect_destroy;
241207

242208
state = (struct zita_convolver_state *) calloc(1, sizeof(struct zita_convolver_state));
243-
state->filter_frames = c_filter->frames;
209+
state->filter_frames = filter_frames;
244210
state->len = min_part_len;
245211
state->cproc = cproc;
246212
state->output = (sample_t **) calloc(istream->channels, sizeof(sample_t *));
247-
for (i = 0; i < istream->channels; ++i)
213+
for (int i = 0; i < istream->channels; ++i)
248214
state->output[i] = (sample_t *) calloc(state->len, sizeof(sample_t));
249215
e->data = (void *) state;
250216

251-
buf_interleaved = (sample_t *) calloc(c_filter->frames * c_filter->channels, sizeof(sample_t));
252-
if (c_filter->read(c_filter, buf_interleaved, c_filter->frames) != c_filter->frames)
253-
LOG_FMT(LL_ERROR, "%s: warning: short read", argv[0]);
254-
buf_planar = (float **) calloc(c_filter->channels, sizeof(float *));
255-
for (i = 0; i < c_filter->channels; ++i)
256-
buf_planar[i] = (float *) calloc(c_filter->frames, sizeof(float));
257-
write_buf_floatp(buf_interleaved, buf_planar, c_filter->channels, c_filter->frames);
258-
free(buf_interleaved);
259-
for (i = k = 0; i < istream->channels; ++i) {
217+
float **buf_planar = (float **) calloc(filter_channels, sizeof(float *));
218+
for (int i = 0; i < filter_channels; ++i)
219+
buf_planar[i] = (float *) calloc(filter_frames, sizeof(float));
220+
write_buf_floatp(filter_data, buf_planar, filter_channels, filter_frames);
221+
for (int i = 0, k = 0; i < istream->channels; ++i) {
260222
if (GET_BIT(channel_selector, i)) {
261-
cproc->impdata_create(k, k, 1, buf_planar[(c_filter->channels == 1) ? 0 : k], 0, c_filter->frames);
223+
if (filter_channels == 1 && k > 0) cproc->impdata_link(0, 0, k, k);
224+
else cproc->impdata_create(k, k, 1, buf_planar[k], 0, filter_frames);
262225
++k;
263226
}
264227
}
265-
for (i = 0; i < c_filter->channels; ++i)
228+
for (int i = 0; i < filter_channels; ++i)
266229
free(buf_planar[i]);
267230
free(buf_planar);
268-
destroy_codec(c_filter);
231+
269232
cproc->start_process(0, 0);
233+
return e;
234+
}
235+
236+
struct effect * zita_convolver_effect_init(const struct effect_info *ei, const struct stream_info *istream, const char *channel_selector, const char *dir, int argc, const char *const *argv)
237+
{
238+
int filter_channels, min_part_len = 0, max_part_len = 0;
239+
ssize_t filter_frames;
240+
struct effect *e;
241+
sample_t *filter_data;
242+
struct codec_params c_params;
243+
struct dsp_getopt_state g = DSP_GETOPT_STATE_INITIALIZER;
244+
char *endptr;
270245

246+
int err = fir_parse_opts(ei, istream, &c_params, &g, argc, argv, NULL, NULL);
247+
if (err || g.ind < argc-3 || g.ind > argc-1) {
248+
LOG_FMT(LL_ERROR, "%s: usage: %s", argv[0], ei->usage);
249+
return NULL;
250+
}
251+
if (g.ind <= argc-2) {
252+
min_part_len = strtol(argv[g.ind], &endptr, 10);
253+
CHECK_ENDPTR(argv[g.ind], endptr, "min_part_len", return NULL);
254+
++g.ind;
255+
}
256+
if (g.ind <= argc-2) {
257+
max_part_len = strtol(argv[g.ind], &endptr, 10);
258+
CHECK_ENDPTR(argv[g.ind], endptr, "max_part_len", return NULL);
259+
++g.ind;
260+
}
261+
c_params.path = argv[g.ind];
262+
filter_data = fir_read_filter(ei, istream, dir, &c_params, &filter_channels, &filter_frames);
263+
if (filter_data == NULL)
264+
return NULL;
265+
e = zita_convolver_effect_init_with_filter(ei, istream, channel_selector, filter_data, filter_channels, filter_frames, min_part_len, max_part_len);
266+
free(filter_data);
271267
return e;
272268
}

zita_convolver.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* This file is part of dsp.
33
*
4-
* Copyright (c) 2016-2024 Michael Barbour <[email protected]>
4+
* Copyright (c) 2016-2025 Michael Barbour <[email protected]>
55
*
66
* Permission to use, copy, modify, and distribute this software for any
77
* purpose with or without fee is hereby granted, provided that the above
@@ -26,6 +26,7 @@ extern "C" {
2626
#include "dsp.h"
2727
#include "effect.h"
2828

29+
struct effect * zita_convolver_effect_init_with_filter(const struct effect_info *, const struct stream_info *, const char *, sample_t *, int, ssize_t, int, int);
2930
struct effect * zita_convolver_effect_init(const struct effect_info *, const struct stream_info *, const char *, const char *, int, const char *const *);
3031

3132
#ifdef __cplusplus

0 commit comments

Comments
 (0)