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: 14 сент. 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 <dsp/dsp.h> 23 #include <core/util/Sidechain.h> 24 25 #define REFRESH_RATE 0x1000 26 #define MIN_GAP_ITEMS 0x200 27 28 namespace lsp 29 { Sidechain()30 Sidechain::Sidechain() 31 { 32 nReactivity = 0; 33 fReactivity = 0.0f; 34 fTau = 0.0f; 35 fRmsValue = 0.0f; 36 nSource = SCS_MIDDLE; 37 nMode = SCM_RMS; 38 nSampleRate = 0; 39 nRefresh = 0; 40 nChannels = 0; 41 fMaxReactivity = 0.0f; 42 fGain = 1.0f; 43 bUpdate = true; 44 bMidSide = false; 45 pPreEq = NULL; 46 } 47 ~Sidechain()48 Sidechain::~Sidechain() 49 { 50 destroy(); 51 } 52 destroy()53 void Sidechain::destroy() 54 { 55 sBuffer.destroy(); 56 } 57 init(size_t channels,float max_reactivity)58 bool Sidechain::init(size_t channels, float max_reactivity) 59 { 60 if ((channels != 1) && (channels != 2)) 61 return false; 62 63 nReactivity = 0; 64 fReactivity = 0.0f; 65 fTau = 0.0f; 66 fRmsValue = 0.0f; 67 nSource = SCS_MIDDLE; 68 nMode = SCM_RMS; 69 nSampleRate = 0; 70 nRefresh = 0; 71 nChannels = channels; 72 fMaxReactivity = max_reactivity; 73 fGain = 1.0f; 74 bUpdate = true; 75 76 return true; 77 } 78 set_sample_rate(size_t sr)79 void Sidechain::set_sample_rate(size_t sr) 80 { 81 nSampleRate = sr; 82 bUpdate = true; 83 size_t gap = millis_to_samples(sr, fMaxReactivity); 84 size_t buf_size = (gap < MIN_GAP_ITEMS) ? MIN_GAP_ITEMS : gap; 85 sBuffer.init(buf_size * 4, gap); 86 } 87 update_settings()88 void Sidechain::update_settings() 89 { 90 if (!bUpdate) 91 return; 92 93 ssize_t react = millis_to_samples(nSampleRate, fReactivity); 94 nReactivity = (react > 1) ? react : 1; 95 fTau = 1.0f - expf(logf(1.0f - M_SQRT1_2) / (nReactivity)); // Tau is based on seconds 96 nRefresh = REFRESH_RATE; // Force the function to be refreshed 97 bUpdate = false; 98 } 99 refresh_processing()100 void Sidechain::refresh_processing() 101 { 102 switch (nMode) 103 { 104 case SCM_PEAK: 105 fRmsValue = 0.0f; 106 break; 107 108 case SCM_UNIFORM: 109 fRmsValue = dsp::h_abs_sum(sBuffer.tail(nReactivity), nReactivity); 110 break; 111 112 case SCM_RMS: 113 fRmsValue = dsp::h_sqr_sum(sBuffer.tail(nReactivity), nReactivity); 114 break; 115 116 default: 117 break; 118 } 119 } 120 preprocess(float * out,const float ** in,size_t samples)121 bool Sidechain::preprocess(float *out, const float **in, size_t samples) 122 { 123 if (nChannels == 2) 124 { 125 if (bMidSide) 126 { 127 switch (nSource) 128 { 129 case SCS_LEFT: 130 dsp::ms_to_left(out, in[0], in[1], samples); 131 if (pPreEq != NULL) 132 pPreEq->process(out, out, samples); 133 dsp::abs1(out, samples); 134 break; 135 case SCS_RIGHT: 136 dsp::ms_to_right(out, in[0], in[1], samples); 137 if (pPreEq != NULL) 138 pPreEq->process(out, out, samples); 139 dsp::abs1(out, samples); 140 break; 141 case SCS_MIDDLE: 142 if (pPreEq != NULL) 143 { 144 pPreEq->process(out, in[0], samples); 145 dsp::abs1(out, samples); 146 } 147 else 148 dsp::abs2(out, in[0], samples); 149 break; 150 case SCS_SIDE: 151 if (pPreEq != NULL) 152 { 153 pPreEq->process(out, in[1], samples); 154 dsp::abs1(out, samples); 155 } 156 else 157 dsp::abs2(out, in[1], samples); 158 break; 159 default: 160 break; 161 } 162 } 163 else 164 { 165 switch (nSource) 166 { 167 case SCS_LEFT: 168 if (pPreEq != NULL) 169 { 170 pPreEq->process(out, in[0], samples); 171 dsp::abs1(out, samples); 172 } 173 else 174 dsp::abs2(out, in[0], samples); 175 break; 176 case SCS_RIGHT: 177 if (pPreEq != NULL) 178 { 179 pPreEq->process(out, in[1], samples); 180 dsp::abs1(out, samples); 181 } 182 else 183 dsp::abs2(out, in[1], samples); 184 break; 185 case SCS_MIDDLE: 186 dsp::lr_to_mid(out, in[0], in[1], samples); 187 if (pPreEq != NULL) 188 pPreEq->process(out, out, samples); 189 dsp::abs1(out, samples); 190 break; 191 case SCS_SIDE: 192 dsp::lr_to_side(out, in[0], in[1], samples); 193 if (pPreEq != NULL) 194 pPreEq->process(out, out, samples); 195 dsp::abs1(out, samples); 196 break; 197 default: 198 break; 199 } 200 } 201 } 202 else if (nChannels == 1) 203 { 204 if (pPreEq != NULL) 205 { 206 pPreEq->process(out, in[0], samples); 207 dsp::abs1(out, samples); 208 } 209 else 210 dsp::abs2(out, in[0], samples); 211 } 212 else 213 { 214 dsp::fill_zero(out, samples); 215 if (pPreEq != NULL) 216 { 217 pPreEq->process(out, out, samples); 218 dsp::abs1(out, samples); 219 } 220 return false; 221 } 222 223 return true; 224 } 225 preprocess(float * out,const float * in)226 bool Sidechain::preprocess(float *out, const float *in) 227 { 228 float s; 229 230 if (nChannels == 2) 231 { 232 if (bMidSide) 233 { 234 switch (nSource) 235 { 236 case SCS_LEFT: 237 s = in[0] + in[1]; 238 if (pPreEq != NULL) 239 pPreEq->process(&s, &s, 1); 240 break; 241 case SCS_RIGHT: 242 s = in[0] - in[1]; 243 if (pPreEq != NULL) 244 pPreEq->process(&s, &s, 1); 245 break; 246 case SCS_MIDDLE: 247 s = in[0]; 248 if (pPreEq != NULL) 249 pPreEq->process(&s, &s, 1); 250 break; 251 case SCS_SIDE: 252 s = in[1]; 253 if (pPreEq != NULL) 254 pPreEq->process(&s, &s, 1); 255 break; 256 default: 257 s = in[0]; 258 break; 259 } 260 } 261 else 262 { 263 switch (nSource) 264 { 265 case SCS_LEFT: 266 s = in[0]; 267 break; 268 case SCS_RIGHT: 269 s = in[1]; 270 break; 271 case SCS_MIDDLE: 272 s = (in[0] + in[1])*0.5f; 273 if (pPreEq != NULL) 274 pPreEq->process(&s, &s, 1); 275 break; 276 case SCS_SIDE: 277 s = (in[0] - in[1])*0.5f; 278 if (pPreEq != NULL) 279 pPreEq->process(&s, &s, 1); 280 break; 281 default: 282 s = (in[0] + in[1])*0.5f; 283 break; 284 } 285 } 286 } 287 else if (nChannels == 1) 288 { 289 s = in[0]; 290 if (pPreEq != NULL) 291 pPreEq->process(&s, &s, 1); 292 } 293 else 294 { 295 s = 0.0f; 296 if (pPreEq != NULL) 297 pPreEq->process(&s, &s, 1); 298 *out = s; 299 return false; 300 } 301 302 *out = (s < 0.0f) ? -s : s; 303 return true; 304 } 305 process(float * out,const float ** in,size_t samples)306 void Sidechain::process(float *out, const float **in, size_t samples) 307 { 308 // Check if need update settings 309 update_settings(); 310 311 // Determine what source to use 312 if (!preprocess(out, in, samples)) 313 return; 314 315 // Adjust pre-amplification 316 if (fGain != 1.0f) 317 dsp::mul_k2(out, fGain, samples); 318 319 // Update refresh counter 320 nRefresh += samples; 321 if (nRefresh >= REFRESH_RATE) 322 { 323 refresh_processing(); 324 nRefresh %= REFRESH_RATE; 325 } 326 327 // Calculate sidechain function 328 switch (nMode) 329 { 330 // Peak processing 331 case SCM_PEAK: 332 { 333 while (samples > 0) 334 { 335 size_t n = sBuffer.append(out, samples); 336 sBuffer.shift(n); 337 out += n; 338 samples -= n; 339 } 340 break; 341 } 342 343 // Lo-pass filter processing 344 case SCM_LPF: 345 { 346 while (samples > 0) 347 { 348 size_t n = sBuffer.append(out, samples); 349 sBuffer.shift(n); 350 samples -= n; 351 352 while (n--) 353 { 354 fRmsValue += fTau * ((*out) - fRmsValue); 355 *(out++) = (fRmsValue < 0.0f) ? 0.0f : fRmsValue; 356 } 357 } 358 break; 359 } 360 361 // Uniform processing 362 case SCM_UNIFORM: 363 { 364 if (nReactivity <= 0) 365 break; 366 float interval = nReactivity; 367 368 while (samples > 0) 369 { 370 size_t n = sBuffer.append(out, samples); 371 float *p = sBuffer.tail(nReactivity + n); 372 samples -= n; 373 374 for (size_t i=0; i<n; ++i) 375 { 376 fRmsValue += *(out) - *(p++); 377 *(out++) = (fRmsValue < 0.0f) ? 0.0f : fRmsValue / interval; 378 } 379 380 // Remove old sample 381 sBuffer.shift(n); 382 } 383 break; 384 } 385 386 // RMS processing 387 case SCM_RMS: 388 { 389 if (nReactivity <= 0) 390 break; 391 float interval = nReactivity; 392 393 while (samples > 0) 394 { 395 size_t n = sBuffer.append(out, samples); 396 float *p = sBuffer.tail(nReactivity + n); 397 samples -= n; 398 399 for (size_t i=0; i<n; ++i) 400 { 401 float sample = *out; 402 float last = *(p++); 403 fRmsValue += sample*sample - last*last; 404 *(out++) = (fRmsValue < 0.0f) ? 0.0f : sqrtf(fRmsValue / interval); 405 } 406 sBuffer.shift(n); 407 } 408 break; 409 } 410 411 default: 412 break; 413 } 414 } 415 process(const float * in)416 float Sidechain::process(const float *in) 417 { 418 // Check if need update settings 419 update_settings(); 420 421 float out = 0.0f; 422 if (!preprocess(&out, in)) 423 return out; 424 425 // Adjust pre-amplification 426 out *= fGain; 427 428 // Update refresh counter 429 nRefresh ++; 430 if (nRefresh >= REFRESH_RATE) 431 { 432 refresh_processing(); 433 nRefresh %= REFRESH_RATE; 434 } 435 436 // Calculate sidechain function 437 switch (nMode) 438 { 439 // Peak processing 440 case SCM_PEAK: 441 { 442 sBuffer.append(out); 443 sBuffer.shift(); 444 break; 445 } 446 447 // Lo-pass filter processing 448 case SCM_LPF: 449 { 450 sBuffer.append(out); 451 sBuffer.shift(); 452 fRmsValue += fTau * (out - fRmsValue); 453 out = (fRmsValue < 0.0f) ? 0.0f : fRmsValue; 454 break; 455 } 456 457 // Uniform processing 458 case SCM_UNIFORM: 459 { 460 if (nReactivity <= 0) 461 break; 462 sBuffer.append(out); 463 fRmsValue += out - sBuffer.last(nReactivity + 1); 464 out = (fRmsValue < 0.0f) ? 0.0f : fRmsValue / float(nReactivity); 465 sBuffer.shift(); 466 break; 467 } 468 469 // RMS processing 470 case SCM_RMS: 471 { 472 if (nReactivity <= 0) 473 break; 474 sBuffer.append(out); 475 float last = sBuffer.last(nReactivity + 1); 476 fRmsValue += out*out - last*last; 477 out = (fRmsValue < 0.0f) ? 0.0f : sqrtf(fRmsValue / float(nReactivity)); 478 sBuffer.shift(); 479 break; 480 } 481 482 default: 483 break; 484 } 485 486 return out; 487 } 488 dump(IStateDumper * v) const489 void Sidechain::dump(IStateDumper *v) const 490 { 491 v->write_object("sBuffer", &sBuffer); 492 v->write("nReactivity", nReactivity); 493 v->write("fReactivity", fReactivity); 494 v->write("fTau", fTau); 495 v->write("fRmsValue", fRmsValue); 496 v->write("nSource", nSource); 497 v->write("nMode", nMode); 498 v->write("nSampleRate", nSampleRate); 499 v->write("nRefresh", nRefresh); 500 v->write("nChannels", nChannels); 501 v->write("fMaxReactivity", fMaxReactivity); 502 v->write("fGain", fGain); 503 v->write("bUpdate", bUpdate); 504 v->write("bMidSide", bMidSide); 505 v->write("pPreEq", pPreEq); 506 } 507 508 } /* namespace lsp */ 509