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