1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 2 сент. 2016 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <core/alloc.h>
23 #include <core/types.h>
24 #include <dsp/dsp.h>
25 #include <core/filters/FilterBank.h>
26 
27 // Currently disable x8 banks on non-experimental stuff
28 namespace lsp
29 {
FilterBank()30     FilterBank::FilterBank()
31     {
32         construct();
33     }
34 
~FilterBank()35     FilterBank::~FilterBank()
36     {
37         destroy();
38     }
39 
construct()40     void FilterBank::construct()
41     {
42         vFilters    = NULL;
43         vChains     = NULL;
44         nItems      = 0;
45         nMaxItems   = 0;
46         nLastItems  = -1;
47         vData       = NULL;
48         vBackup     = NULL;
49     }
50 
destroy()51     void FilterBank::destroy()
52     {
53         if (vData != NULL)
54             lsp_free(vData);
55 
56         construct();
57     }
58 
init(size_t filters)59     bool FilterBank::init(size_t filters)
60     {
61         destroy();
62 
63         // Calculate data size
64         size_t n_banks      = (filters/8) + 3;
65         size_t bank_alloc   = ALIGN_SIZE(sizeof(biquad_t), BIQUAD_ALIGN) * n_banks;
66         size_t chain_alloc  = sizeof(biquad_x1_t) * filters;
67         size_t backup_alloc = sizeof(float) * BIQUAD_D_ITEMS * n_banks;
68 
69         // Allocate data
70         size_t allocate     = bank_alloc + chain_alloc + backup_alloc + BIQUAD_ALIGN;
71         vData               = lsp_tmalloc(uint8_t, allocate);
72         if (vData == NULL)
73             return false;
74 
75         // Align and initialize pointers
76         uint8_t *ptr        = ALIGN_PTR(vData, BIQUAD_ALIGN);
77         vFilters            = reinterpret_cast<biquad_t *>(ptr);
78         ptr                += bank_alloc;
79         vChains             = reinterpret_cast<biquad_x1_t *>(ptr);
80         ptr                += chain_alloc;
81         vBackup             = reinterpret_cast<float *>(ptr);
82         ptr                += backup_alloc;
83 
84         // Update parameters
85         nItems              = 0;
86         nMaxItems           = filters;
87         nLastItems          = -1;
88 
89         return true;
90     }
91 
add_chain()92     biquad_x1_t *FilterBank::add_chain()
93     {
94         if (nItems >= nMaxItems)
95             return (nItems <= 0) ? NULL : &vChains[nItems-1];
96         return &vChains[nItems++];
97     }
98 
end(bool clear)99     void FilterBank::end(bool clear)
100     {
101         size_t items    = nItems;
102         biquad_x1_t *c  = vChains;
103         biquad_t *b     = vFilters;
104 
105         // Add 8x filter bank
106         while (items >= 8)
107         {
108             biquad_x8_t *f = &b->x8;
109 
110             f->b0[0]    = c[0].b0;
111             f->b0[1]    = c[1].b0;
112             f->b0[2]    = c[2].b0;
113             f->b0[3]    = c[3].b0;
114             f->b0[4]    = c[4].b0;
115             f->b0[5]    = c[5].b0;
116             f->b0[6]    = c[6].b0;
117             f->b0[7]    = c[7].b0;
118 
119             f->b1[0]    = c[0].b1;
120             f->b1[1]    = c[1].b1;
121             f->b1[2]    = c[2].b1;
122             f->b1[3]    = c[3].b1;
123             f->b1[4]    = c[4].b1;
124             f->b1[5]    = c[5].b1;
125             f->b1[6]    = c[6].b1;
126             f->b1[7]    = c[7].b1;
127 
128             f->b2[0]    = c[0].b2;
129             f->b2[1]    = c[1].b2;
130             f->b2[2]    = c[2].b2;
131             f->b2[3]    = c[3].b2;
132             f->b2[4]    = c[4].b2;
133             f->b2[5]    = c[5].b2;
134             f->b2[6]    = c[6].b2;
135             f->b2[7]    = c[7].b2;
136 
137             f->a1[0]    = c[0].a1;
138             f->a1[1]    = c[1].a1;
139             f->a1[2]    = c[2].a1;
140             f->a1[3]    = c[3].a1;
141             f->a1[4]    = c[4].a1;
142             f->a1[5]    = c[5].a1;
143             f->a1[6]    = c[6].a1;
144             f->a1[7]    = c[7].a1;
145 
146             f->a2[0]    = c[0].a2;
147             f->a2[1]    = c[1].a2;
148             f->a2[2]    = c[2].a2;
149             f->a2[3]    = c[3].a2;
150             f->a2[4]    = c[4].a2;
151             f->a2[5]    = c[5].a2;
152             f->a2[6]    = c[6].a2;
153             f->a2[7]    = c[7].a2;
154 
155             c          += 8;
156             b          ++;
157             items      -= 8;
158         }
159 
160         // Add 4x filter bank
161         if (items & 4)
162         {
163             biquad_x4_t *f = &b->x4;
164 
165             f->b0[0]    = c[0].b0;
166             f->b0[1]    = c[1].b0;
167             f->b0[2]    = c[2].b0;
168             f->b0[3]    = c[3].b0;
169 
170             f->b1[0]    = c[0].b1;
171             f->b1[1]    = c[1].b1;
172             f->b1[2]    = c[2].b1;
173             f->b1[3]    = c[3].b1;
174 
175             f->b2[0]    = c[0].b2;
176             f->b2[1]    = c[1].b2;
177             f->b2[2]    = c[2].b2;
178             f->b2[3]    = c[3].b2;
179 
180             f->a1[0]    = c[0].a1;
181             f->a1[1]    = c[1].a1;
182             f->a1[2]    = c[2].a1;
183             f->a1[3]    = c[3].a1;
184 
185             f->a2[0]    = c[0].a2;
186             f->a2[1]    = c[1].a2;
187             f->a2[2]    = c[2].a2;
188             f->a2[3]    = c[3].a2;
189 
190             c          += 4;
191             b          ++;
192         }
193 
194         // Add 2x filter bank
195         if (items & 2)
196         {
197             biquad_x2_t *f = &b->x2;
198 
199             f->b0[0]    = c[0].b0;
200             f->b0[1]    = c[1].b0;
201             f->b1[0]    = c[0].b1;
202             f->b1[1]    = c[1].b1;
203             f->b2[0]    = c[0].b2;
204             f->b2[1]    = c[1].b2;
205 
206             f->a1[0]    = c[0].a1;
207             f->a1[1]    = c[1].a1;
208             f->a2[0]    = c[0].a2;
209             f->a2[1]    = c[1].a2;
210 
211             f->p[0]     = 0.0f;
212             f->p[1]     = 0.0f;
213 
214             c          += 2;
215             b          ++;
216         }
217 
218         // Add 1x filter
219         if (items & 1)
220         {
221             b->x1       = *(c++);
222             b          ++;
223         }
224 
225         // Clear delays if structure has changed
226         if ((clear) || (nItems != nLastItems))
227             reset();
228         nLastItems      = nItems;
229     }
230 
reset()231     void FilterBank::reset()
232     {
233         size_t items    = nItems >> 3;
234         if (nItems & 4)
235             items ++;
236         if (nItems & 2)
237             items ++;
238         if (nItems & 1)
239             items ++;
240 
241         biquad_t *b     = vFilters;
242         while (items--)
243         {
244             dsp::fill_zero(b->d, BIQUAD_D_ITEMS);
245             b++;
246         }
247     }
248 
process(float * out,const float * in,size_t samples)249     void FilterBank::process(float *out, const float *in, size_t samples)
250     {
251         size_t items        = nItems;
252         biquad_t *f         = vFilters;
253 
254         if (items == 0)
255         {
256             dsp::copy(out, in, samples);
257             return;
258         }
259 
260         while (items >= 8)
261         {
262             dsp::biquad_process_x8(out, in, samples, f);
263             in         = out;  // actual data for the next chain is in output buffer now
264             f         ++;
265             items     -= 8;
266         }
267 
268         if (items & 4)
269         {
270             dsp::biquad_process_x4(out, in, samples, f);
271             in         = out;  // actual data for the next chain is in output buffer now
272             f         ++;
273         }
274 
275         if (items & 2)
276         {
277             dsp::biquad_process_x2(out, in, samples, f);
278             in         = out;  // actual data for the next chain is in output buffer now
279             f         ++;
280         }
281 
282         if (items & 1)
283             dsp::biquad_process_x1(out, in, samples, f);
284     }
285 
impulse_response(float * out,size_t samples)286     void FilterBank::impulse_response(float *out, size_t samples)
287     {
288         // Backup and clean all delays
289         biquad_t *f         = vFilters;
290         float *dst          = vBackup;
291 
292         size_t items        = nItems >> 3;
293         if (nItems & 4)
294             items ++;
295         if (nItems & 2)
296             items ++;
297         if (nItems & 1)
298             items ++;
299 
300         for (size_t i=0; i < items; ++i)
301         {
302             dsp::copy(dst, f->d, BIQUAD_D_ITEMS);
303             dsp::fill_zero(f->d, BIQUAD_D_ITEMS);
304             dst                += BIQUAD_D_ITEMS;
305             f                  ++;
306         }
307 
308         // Generate impulse response
309         dsp::fill_zero(out, samples);
310         out[0]              = 1.0f;
311         process(out, out, samples);
312 
313         // Restore all delays
314         dst                 = vBackup;
315         f                   = vFilters;
316 
317         for (size_t i=0; i < items; ++i)
318         {
319             dsp::copy(f->d, dst, BIQUAD_D_ITEMS);
320             dst                += BIQUAD_D_ITEMS;
321             f                  ++;
322         }
323     }
324 
dump(IStateDumper * v) const325     void FilterBank::dump(IStateDumper *v) const
326     {
327         size_t ni       = nItems;
328         size_t nc       = (ni >> 3) + ((ni >> 2) & 1) + ((ni >> 1) & 1) + (ni & 1);
329         biquad_t *b     = vFilters;
330 
331         v->begin_array("vFilters", vFilters, nc);
332         while (ni >= 8)
333         {
334             v->begin_object(b, sizeof(biquad_t));
335             {
336                 v->writev("b0", b->x8.b0, 8);
337                 v->writev("b1", b->x8.b1, 8);
338                 v->writev("b2", b->x8.b2, 8);
339                 v->writev("a1", b->x8.a1, 8);
340                 v->writev("a2", b->x8.a2, 8);
341             }
342             v->end_object();
343             b       ++;
344             ni      -= 8;
345         }
346         if (ni & 4)
347         {
348             v->begin_object(b, sizeof(biquad_t));
349             {
350                 v->writev("b0", b->x4.b0, 4);
351                 v->writev("b1", b->x4.b1, 4);
352                 v->writev("b2", b->x4.b2, 4);
353                 v->writev("a1", b->x4.a1, 4);
354                 v->writev("a2", b->x4.a2, 4);
355             }
356             v->end_object();
357             b       ++;
358             ni      -= 8;
359         }
360         if (ni & 2)
361         {
362             v->begin_object(b, sizeof(biquad_t));
363             {
364                 v->writev("b0", b->x2.b0, 2);
365                 v->writev("b1", b->x2.b1, 2);
366                 v->writev("b2", b->x2.b2, 2);
367                 v->writev("a1", b->x2.a1, 2);
368                 v->writev("a2", b->x2.a2, 2);
369                 v->writev("p", b->x2.p, 2);
370             }
371             v->end_object();
372             b       ++;
373             ni      -= 8;
374         }
375         if (ni & 1)
376         {
377             v->begin_object(b, sizeof(biquad_t));
378             {
379                 v->write("b0", b->x1.b0);
380                 v->write("b1", b->x1.b1);
381                 v->write("b2", b->x1.b2);
382                 v->write("a1", b->x1.a1);
383                 v->write("a2", b->x1.a2);
384                 v->write("p0", b->x1.p0);
385                 v->write("p1", b->x1.p1);
386                 v->write("p2", b->x1.p2);
387             }
388             v->end_object();
389             b       ++;
390             ni      -= 8;
391         }
392         v->end_array();
393 
394         v->begin_array("vChains", vChains, nItems);
395         for (size_t i=0; i<nItems; ++i)
396         {
397             biquad_x1_t *bq = &vChains[i];
398             v->begin_object(bq, sizeof(biquad_x1_t));
399             {
400                 v->write("b0", bq->b0);
401                 v->write("b1", bq->b1);
402                 v->write("b2", bq->b2);
403                 v->write("a1", bq->a1);
404                 v->write("a2", bq->a2);
405                 v->write("p0", bq->p0);
406                 v->write("p1", bq->p1);
407                 v->write("p2", bq->p2);
408             }
409             v->end_object();
410         }
411         v->end_array();
412         v->write("nItems", nItems);
413         v->write("nMaxItems", nMaxItems);
414         v->write("nLastItems", nLastItems);
415         v->write("vBackup", vBackup);
416         v->write("vData", vData);
417     }
418 } /* namespace lsp */
419