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: 4 нояб. 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/debug.h> 23 #include <core/colors.h> 24 #include <core/util/Color.h> 25 #include <plugins/expander.h> 26 27 #define EXP_BUF_SIZE 0x1000 28 #define TRACE_PORT(p) lsp_trace(" port id=%s", (p)->metadata()->id); 29 30 namespace lsp 31 { 32 //------------------------------------------------------------------------- 33 // Expander base class 34 expander_base(const plugin_metadata_t & metadata,bool sc,size_t mode)35 expander_base::expander_base(const plugin_metadata_t &metadata, bool sc, size_t mode): plugin_t(metadata) 36 { 37 nMode = mode; 38 bSidechain = sc; 39 vChannels = NULL; 40 vCurve = NULL; 41 vTime = NULL; 42 bPause = false; 43 bClear = false; 44 bMSListen = false; 45 fInGain = 1.0f; 46 bUISync = true; 47 48 pBypass = NULL; 49 pInGain = NULL; 50 pOutGain = NULL; 51 pPause = NULL; 52 pClear = NULL; 53 pMSListen = NULL; 54 55 pData = NULL; 56 pIDisplay = NULL; 57 } 58 ~expander_base()59 expander_base::~expander_base() 60 { 61 } 62 init(IWrapper * wrapper)63 void expander_base::init(IWrapper *wrapper) 64 { 65 plugin_t::init(wrapper); 66 size_t channels = (nMode == EM_MONO) ? 1 : 2; 67 68 // Allocate channels 69 vChannels = new channel_t[channels]; 70 if (vChannels == NULL) 71 return; 72 73 // Allocate temporary buffers 74 size_t buf_size = EXP_BUF_SIZE * sizeof(float); 75 size_t curve_size = (expander_base_metadata::CURVE_MESH_SIZE) * sizeof(float); 76 size_t history_size = (expander_base_metadata::TIME_MESH_SIZE) * sizeof(float); 77 size_t allocate = buf_size * channels * 5 + curve_size + history_size + DEFAULT_ALIGN; 78 uint8_t *ptr = new uint8_t[allocate]; 79 if (ptr == NULL) 80 return; 81 pData = ptr; 82 ptr = ALIGN_PTR(ptr, DEFAULT_ALIGN); 83 vCurve = reinterpret_cast<float *>(ptr); 84 ptr += curve_size; 85 vTime = reinterpret_cast<float *>(ptr); 86 ptr += history_size; 87 88 // Initialize channels 89 for (size_t i=0; i<channels; ++i) 90 { 91 channel_t *c = &vChannels[i]; 92 93 if (!c->sSC.init(channels, expander_base_metadata::REACTIVITY_MAX)) 94 return; 95 if (!c->sSCEq.init(2, 12)) 96 return; 97 c->sSCEq.set_mode(EQM_IIR); 98 c->sSC.set_pre_equalizer(&c->sSCEq); 99 100 c->vIn = reinterpret_cast<float *>(ptr); 101 ptr += buf_size; 102 c->vOut = reinterpret_cast<float *>(ptr); 103 ptr += buf_size; 104 c->vSc = reinterpret_cast<float *>(ptr); 105 ptr += buf_size; 106 c->vEnv = reinterpret_cast<float *>(ptr); 107 ptr += buf_size; 108 c->vGain = reinterpret_cast<float *>(ptr); 109 ptr += buf_size; 110 c->bScListen = false; 111 c->nSync = S_ALL; 112 c->nScType = SCT_INTERNAL; 113 c->fMakeup = 1.0f; 114 c->fDryGain = 1.0f; 115 c->fWetGain = 0.0f; 116 c->fDotIn = 0.0f; 117 c->fDotOut = 0.0f; 118 119 c->pIn = NULL; 120 c->pOut = NULL; 121 c->pSC = NULL; 122 123 for (size_t j=0; j<G_TOTAL; ++j) 124 c->pGraph[j] = NULL; 125 126 for (size_t j=0; j<M_TOTAL; ++j) 127 c->pMeter[j] = NULL; 128 129 c->pScType = NULL; 130 c->pScMode = NULL; 131 c->pScLookahead = NULL; 132 c->pScListen = NULL; 133 c->pScSource = NULL; 134 c->pScReactivity = NULL; 135 c->pScPreamp = NULL; 136 c->pScHpfMode = NULL; 137 c->pScHpfFreq = NULL; 138 c->pScLpfMode = NULL; 139 c->pScLpfFreq = NULL; 140 141 c->pMode = NULL; 142 c->pAttackLvl = NULL; 143 c->pReleaseLvl = NULL; 144 c->pAttackTime = NULL; 145 c->pReleaseTime = NULL; 146 c->pRatio = NULL; 147 c->pKnee = NULL; 148 c->pMakeup = NULL; 149 c->pDryGain = NULL; 150 c->pWetGain = NULL; 151 c->pCurve = NULL; 152 c->pReleaseOut = NULL; 153 } 154 155 lsp_assert(ptr < &pData[allocate]); 156 157 // Bind ports 158 size_t port_id = 0; 159 160 // Input ports 161 lsp_trace("Binding input ports"); 162 for (size_t i=0; i<channels; ++i) 163 { 164 TRACE_PORT(vPorts[port_id]); 165 vChannels[i].pIn = vPorts[port_id++]; 166 } 167 168 // Input ports 169 lsp_trace("Binding output ports"); 170 for (size_t i=0; i<channels; ++i) 171 { 172 TRACE_PORT(vPorts[port_id]); 173 vChannels[i].pOut = vPorts[port_id++]; 174 } 175 176 // Input ports 177 if (bSidechain) 178 { 179 lsp_trace("Binding sidechain ports"); 180 for (size_t i=0; i<channels; ++i) 181 { 182 TRACE_PORT(vPorts[port_id]); 183 vChannels[i].pSC = vPorts[port_id++]; 184 } 185 } 186 187 // Common ports 188 lsp_trace("Binding common ports"); 189 TRACE_PORT(vPorts[port_id]); 190 pBypass = vPorts[port_id++]; 191 TRACE_PORT(vPorts[port_id]); 192 pInGain = vPorts[port_id++]; 193 TRACE_PORT(vPorts[port_id]); 194 pOutGain = vPorts[port_id++]; 195 TRACE_PORT(vPorts[port_id]); 196 pPause = vPorts[port_id++]; 197 TRACE_PORT(vPorts[port_id]); 198 pClear = vPorts[port_id++]; 199 if (nMode == EM_MS) 200 { 201 TRACE_PORT(vPorts[port_id]); 202 pMSListen = vPorts[port_id++]; 203 } 204 205 // Sidechain ports 206 lsp_trace("Binding sidechain ports"); 207 for (size_t i=0; i<channels; ++i) 208 { 209 channel_t *c = &vChannels[i]; 210 211 if ((i > 0) && (nMode == EM_STEREO)) 212 { 213 channel_t *sc = &vChannels[0]; 214 c->pScType = sc->pScType; 215 c->pScSource = sc->pScSource; 216 c->pScMode = sc->pScMode; 217 c->pScLookahead = sc->pScLookahead; 218 c->pScListen = sc->pScListen; 219 c->pScReactivity = sc->pScReactivity; 220 c->pScPreamp = sc->pScPreamp; 221 c->pScHpfMode = sc->pScHpfMode; 222 c->pScHpfFreq = sc->pScHpfFreq; 223 c->pScLpfMode = sc->pScLpfMode; 224 c->pScLpfFreq = sc->pScLpfFreq; 225 } 226 else 227 { 228 if (bSidechain) 229 { 230 TRACE_PORT(vPorts[port_id]); 231 c->pScType = vPorts[port_id++]; 232 } 233 TRACE_PORT(vPorts[port_id]); 234 c->pScMode = vPorts[port_id++]; 235 TRACE_PORT(vPorts[port_id]); 236 c->pScLookahead = vPorts[port_id++]; 237 TRACE_PORT(vPorts[port_id]); 238 c->pScListen = vPorts[port_id++]; 239 if (nMode != EM_MONO) 240 { 241 TRACE_PORT(vPorts[port_id]); 242 c->pScSource = vPorts[port_id++]; 243 } 244 TRACE_PORT(vPorts[port_id]); 245 c->pScReactivity = vPorts[port_id++]; 246 TRACE_PORT(vPorts[port_id]); 247 c->pScPreamp = vPorts[port_id++]; 248 TRACE_PORT(vPorts[port_id]); 249 c->pScHpfMode = vPorts[port_id++]; 250 TRACE_PORT(vPorts[port_id]); 251 c->pScHpfFreq = vPorts[port_id++]; 252 TRACE_PORT(vPorts[port_id]); 253 c->pScLpfMode = vPorts[port_id++]; 254 TRACE_PORT(vPorts[port_id]); 255 c->pScLpfFreq = vPorts[port_id++]; 256 } 257 } 258 259 // Expander ports 260 lsp_trace("Binding expander ports"); 261 for (size_t i=0; i<channels; ++i) 262 { 263 channel_t *c = &vChannels[i]; 264 265 if ((i > 0) && (nMode == EM_STEREO)) 266 { 267 channel_t *sc = &vChannels[0]; 268 269 c->pMode = sc->pMode; 270 c->pAttackLvl = sc->pAttackLvl; 271 c->pAttackTime = sc->pAttackTime; 272 c->pReleaseLvl = sc->pReleaseLvl; 273 c->pReleaseTime = sc->pReleaseTime; 274 c->pRatio = sc->pRatio; 275 c->pKnee = sc->pKnee; 276 c->pMakeup = sc->pMakeup; 277 c->pDryGain = sc->pDryGain; 278 c->pWetGain = sc->pWetGain; 279 } 280 else 281 { 282 TRACE_PORT(vPorts[port_id]); 283 c->pMode = vPorts[port_id++]; 284 TRACE_PORT(vPorts[port_id]); 285 c->pAttackLvl = vPorts[port_id++]; 286 TRACE_PORT(vPorts[port_id]); 287 c->pAttackTime = vPorts[port_id++]; 288 TRACE_PORT(vPorts[port_id]); 289 c->pReleaseLvl = vPorts[port_id++]; 290 TRACE_PORT(vPorts[port_id]); 291 c->pReleaseTime = vPorts[port_id++]; 292 TRACE_PORT(vPorts[port_id]); 293 c->pRatio = vPorts[port_id++]; 294 TRACE_PORT(vPorts[port_id]); 295 c->pKnee = vPorts[port_id++]; 296 TRACE_PORT(vPorts[port_id]); 297 c->pMakeup = vPorts[port_id++]; 298 TRACE_PORT(vPorts[port_id]); 299 c->pDryGain = vPorts[port_id++]; 300 TRACE_PORT(vPorts[port_id]); 301 c->pWetGain = vPorts[port_id++]; 302 TRACE_PORT(vPorts[port_id]); 303 c->pReleaseOut = vPorts[port_id++]; 304 305 // Skip meters visibility controls 306 TRACE_PORT(vPorts[port_id]); 307 port_id++; 308 TRACE_PORT(vPorts[port_id]); 309 port_id++; 310 TRACE_PORT(vPorts[port_id]); 311 port_id++; 312 313 TRACE_PORT(vPorts[port_id]); 314 c->pCurve = vPorts[port_id++]; 315 TRACE_PORT(vPorts[port_id]); 316 c->pGraph[G_SC] = vPorts[port_id++]; 317 TRACE_PORT(vPorts[port_id]); 318 c->pGraph[G_ENV] = vPorts[port_id++]; 319 TRACE_PORT(vPorts[port_id]); 320 c->pGraph[G_GAIN] = vPorts[port_id++]; 321 TRACE_PORT(vPorts[port_id]); 322 c->pMeter[M_SC] = vPorts[port_id++]; 323 TRACE_PORT(vPorts[port_id]); 324 c->pMeter[M_CURVE] = vPorts[port_id++]; 325 TRACE_PORT(vPorts[port_id]); 326 c->pMeter[M_ENV] = vPorts[port_id++]; 327 TRACE_PORT(vPorts[port_id]); 328 c->pMeter[M_GAIN] = vPorts[port_id++]; 329 } 330 } 331 332 // Bind history 333 lsp_trace("Binding history ports"); 334 for (size_t i=0; i<channels; ++i) 335 { 336 channel_t *c = &vChannels[i]; 337 338 // Skip meters visibility controls 339 TRACE_PORT(vPorts[port_id]); 340 port_id++; 341 TRACE_PORT(vPorts[port_id]); 342 port_id++; 343 344 // Bind ports 345 TRACE_PORT(vPorts[port_id]); 346 c->pGraph[G_IN] = vPorts[port_id++]; 347 TRACE_PORT(vPorts[port_id]); 348 c->pGraph[G_OUT] = vPorts[port_id++]; 349 TRACE_PORT(vPorts[port_id]); 350 c->pMeter[M_IN] = vPorts[port_id++]; 351 TRACE_PORT(vPorts[port_id]); 352 c->pMeter[M_OUT] = vPorts[port_id++]; 353 } 354 355 // Initialize curve (logarithmic) in range of -72 .. +24 db 356 float delta = (expander_base_metadata::CURVE_DB_MAX - expander_base_metadata::CURVE_DB_MIN) / (expander_base_metadata::CURVE_MESH_SIZE-1); 357 for (size_t i=0; i<expander_base_metadata::CURVE_MESH_SIZE; ++i) 358 vCurve[i] = db_to_gain(expander_base_metadata::CURVE_DB_MIN + delta * i); 359 360 // Initialize time points 361 delta = expander_base_metadata::TIME_HISTORY_MAX / (expander_base_metadata::TIME_MESH_SIZE - 1); 362 for (size_t i=0; i<expander_base_metadata::TIME_MESH_SIZE; ++i) 363 vTime[i] = expander_base_metadata::TIME_HISTORY_MAX - i*delta; 364 } 365 destroy()366 void expander_base::destroy() 367 { 368 if (vChannels != NULL) 369 { 370 size_t channels = (nMode == EM_MONO) ? 1 : 2; 371 for (size_t i=0; i<channels; ++i) 372 { 373 vChannels[i].sSC.destroy(); 374 vChannels[i].sSCEq.destroy(); 375 vChannels[i].sDelay.destroy(); 376 vChannels[i].sCompDelay.destroy(); 377 vChannels[i].sDryDelay.destroy(); 378 } 379 380 delete [] vChannels; 381 vChannels = NULL; 382 } 383 384 if (pData != NULL) 385 { 386 delete [] pData; 387 pData = NULL; 388 } 389 390 if (pIDisplay != NULL) 391 { 392 pIDisplay->detroy(); 393 pIDisplay = NULL; 394 } 395 } 396 update_sample_rate(long sr)397 void expander_base::update_sample_rate(long sr) 398 { 399 size_t samples_per_dot = seconds_to_samples(sr, expander_base_metadata::TIME_HISTORY_MAX / expander_base_metadata::TIME_MESH_SIZE); 400 size_t channels = (nMode == EM_MONO) ? 1 : 2; 401 size_t max_delay = millis_to_samples(fSampleRate, compressor_base_metadata::LOOKAHEAD_MAX); 402 403 for (size_t i=0; i<channels; ++i) 404 { 405 channel_t *c = &vChannels[i]; 406 c->sBypass.init(sr); 407 c->sExp.set_sample_rate(sr); 408 c->sSC.set_sample_rate(sr); 409 c->sSCEq.set_sample_rate(sr); 410 c->sDelay.init(max_delay); 411 c->sCompDelay.init(max_delay); 412 c->sDryDelay.init(max_delay); 413 414 for (size_t j=0; j<G_TOTAL; ++j) 415 c->sGraph[j].init(expander_base_metadata::TIME_MESH_SIZE, samples_per_dot); 416 c->sGraph[G_GAIN].fill(1.0f); 417 } 418 } 419 update_settings()420 void expander_base::update_settings() 421 { 422 filter_params_t fp; 423 size_t channels = (nMode == EM_MONO) ? 1 : 2; 424 bool bypass = pBypass->getValue() >= 0.5f; 425 426 // Global parameters 427 bPause = pPause->getValue() >= 0.5f; 428 bClear = pClear->getValue() >= 0.5f; 429 bMSListen = (pMSListen != NULL) ? pMSListen->getValue() >= 0.5f : false; 430 fInGain = pInGain->getValue(); 431 float out_gain = pOutGain->getValue(); 432 size_t latency = 0; 433 434 for (size_t i=0; i<channels; ++i) 435 { 436 channel_t *c = &vChannels[i]; 437 438 // Update bypass settings 439 c->sBypass.set_bypass(bypass); 440 441 // Update sidechain settings 442 c->nScType = (c->pScType != NULL) ? c->pScType->getValue() : SCT_INTERNAL; 443 c->bScListen = c->pScListen->getValue() >= 0.5f; 444 445 c->sSC.set_gain(c->pScPreamp->getValue()); 446 c->sSC.set_mode((c->pScMode != NULL) ? c->pScMode->getValue() : SCM_RMS); 447 c->sSC.set_source((c->pScSource != NULL) ? c->pScSource->getValue() : SCS_MIDDLE); 448 c->sSC.set_reactivity(c->pScReactivity->getValue()); 449 c->sSC.set_stereo_mode(((nMode == EM_MS) && (c->nScType != SCT_EXTERNAL)) ? SCSM_MIDSIDE : SCSM_STEREO); 450 451 // Setup hi-pass filter for sidechain 452 size_t hp_slope = c->pScHpfMode->getValue() * 2; 453 fp.nType = (hp_slope > 0) ? FLT_BT_BWC_HIPASS : FLT_NONE; 454 fp.fFreq = c->pScHpfFreq->getValue(); 455 fp.fFreq2 = fp.fFreq; 456 fp.fGain = 1.0f; 457 fp.nSlope = hp_slope; 458 fp.fQuality = 0.0f; 459 c->sSCEq.set_params(0, &fp); 460 461 // Setup low-pass filter for sidechain 462 size_t lp_slope = c->pScLpfMode->getValue() * 2; 463 fp.nType = (lp_slope > 0) ? FLT_BT_BWC_LOPASS : FLT_NONE; 464 fp.fFreq = c->pScLpfFreq->getValue(); 465 fp.fFreq2 = fp.fFreq; 466 fp.fGain = 1.0f; 467 fp.nSlope = lp_slope; 468 fp.fQuality = 0.0f; 469 c->sSCEq.set_params(1, &fp); 470 471 // Update delay 472 size_t delay = millis_to_samples(fSampleRate, (c->pScLookahead != NULL) ? c->pScLookahead->getValue() : 0); 473 c->sDelay.set_delay(delay); 474 if (delay > latency) 475 latency = delay; 476 477 // Update expander settings 478 float attack = c->pAttackLvl->getValue(); 479 float release = c->pReleaseLvl->getValue() * attack; 480 float makeup = c->pMakeup->getValue(); 481 bool upward = c->pMode->getValue() >= 0.5f; 482 483 c->sExp.set_threshold(attack, release); 484 c->sExp.set_timings(c->pAttackTime->getValue(), c->pReleaseTime->getValue()); 485 c->sExp.set_ratio(c->pRatio->getValue()); 486 c->sExp.set_knee(c->pKnee->getValue()); 487 c->sExp.set_mode((upward) ? EM_UPWARD : EM_DOWNWARD); 488 if (c->pReleaseOut != NULL) 489 c->pReleaseOut->setValue(release); 490 c->sGraph[G_GAIN].set_method((upward) ? MM_MAXIMUM : MM_MINIMUM); 491 492 // Check modification flag 493 if (c->sExp.modified()) 494 { 495 c->sExp.update_settings(); 496 c->nSync |= S_CURVE; 497 } 498 499 // Update gains 500 c->fDryGain = c->pDryGain->getValue() * out_gain; 501 c->fWetGain = c->pWetGain->getValue() * out_gain; 502 if (c->fMakeup != makeup) 503 { 504 c->fMakeup = makeup; 505 c->nSync |= S_CURVE; 506 } 507 } 508 509 // Tune compensation delays 510 for (size_t i=0; i<channels; ++i) 511 { 512 channel_t *c = &vChannels[i]; 513 c->sCompDelay.set_delay(latency - c->sDelay.get_delay()); 514 c->sDryDelay.set_delay(latency); 515 } 516 517 // Report latency 518 set_latency(latency); 519 } 520 ui_activated()521 void expander_base::ui_activated() 522 { 523 size_t channels = (nMode == EM_MONO) ? 1 : 2; 524 for (size_t i=0; i<channels; ++i) 525 vChannels[i].nSync = S_CURVE; 526 bUISync = true; 527 } 528 process(size_t samples)529 void expander_base::process(size_t samples) 530 { 531 size_t channels = (nMode == EM_MONO) ? 1 : 2; 532 533 float *in_buf[2]; // Input buffer 534 float *out_buf[2]; // Output buffer 535 float *sc_buf[2]; // Sidechain source 536 const float *in[2]; // Buffet to pass to sidechain 537 538 // Prepare audio channels 539 for (size_t i=0; i<channels; ++i) 540 { 541 channel_t *c = &vChannels[i]; 542 543 // Initialize pointers 544 in_buf[i] = c->pIn->getBuffer<float>(); 545 out_buf[i] = c->pOut->getBuffer<float>(); 546 sc_buf[i] = (c->pSC != NULL) ? c->pSC->getBuffer<float>() : in_buf[i]; 547 } 548 549 // Perform expansion 550 size_t left = samples; 551 while (left > 0) 552 { 553 // Detemine number of samples to process 554 size_t to_process = (left > EXP_BUF_SIZE) ? EXP_BUF_SIZE : left; 555 556 // Prepare audio channels 557 if (nMode == EM_MONO) 558 dsp::mul_k3(vChannels[0].vIn, in_buf[0], fInGain, to_process); 559 else if (nMode == EM_MS) 560 { 561 dsp::lr_to_ms(vChannels[0].vIn, vChannels[1].vIn, in_buf[0], in_buf[1], to_process); 562 dsp::mul_k2(vChannels[0].vIn, fInGain, to_process); 563 dsp::mul_k2(vChannels[1].vIn, fInGain, to_process); 564 } 565 else 566 { 567 dsp::mul_k3(vChannels[0].vIn, in_buf[0], fInGain, to_process); 568 dsp::mul_k3(vChannels[1].vIn, in_buf[1], fInGain, to_process); 569 } 570 571 // Perform sidechain processing 572 for (size_t i=0; i<channels; ++i) 573 { 574 channel_t *c = &vChannels[i]; 575 576 // Update input graph 577 c->sGraph[G_IN].process(c->vIn, to_process); 578 c->pMeter[M_IN]->setValue(dsp::abs_max(c->vIn, to_process)); 579 580 // Do expansion 581 in[0] = (c->nScType == SCT_EXTERNAL) ? sc_buf[0] : vChannels[0].vIn; 582 if (channels > 1) 583 in[1] = (c->nScType == SCT_EXTERNAL) ? sc_buf[1] : vChannels[1].vIn; 584 c->sSC.process(c->vSc, in, to_process); 585 c->sExp.process(c->vGain, c->vEnv, c->vSc, to_process); 586 } 587 588 // Apply gain to each channel and process meters 589 for (size_t i=0; i<channels; ++i) 590 { 591 channel_t *c = &vChannels[i]; 592 593 // Add delay to original signal and apply gain 594 c->sDelay.process(c->vOut, c->vIn, c->vGain, to_process); 595 596 // Apply latency compensation delay 597 c->sCompDelay.process(c->vOut, c->vOut, to_process); 598 599 // Process graph outputs 600 if ((i == 0) || (nMode != EM_STEREO)) 601 { 602 c->sGraph[G_SC].process(c->vSc, to_process); // Sidechain signal 603 c->pMeter[M_SC]->setValue(dsp::abs_max(c->vSc, to_process)); 604 605 c->sGraph[G_GAIN].process(c->vGain, to_process); // Gain reduction signal 606 c->pMeter[M_GAIN]->setValue(dsp::abs_max(c->vGain, to_process)); 607 608 c->sGraph[G_ENV].process(c->vEnv, to_process); // Envelope signal 609 c->pMeter[M_ENV]->setValue(dsp::abs_max(c->vEnv, to_process)); 610 } 611 } 612 613 // Form output signal 614 if (nMode == EM_MS) 615 { 616 channel_t *cm = &vChannels[0]; 617 channel_t *cs = &vChannels[1]; 618 619 dsp::mix2(cm->vOut, cm->vIn, cm->fMakeup * cm->fWetGain, cm->fDryGain, to_process); 620 dsp::mix2(cs->vOut, cs->vIn, cs->fMakeup * cs->fWetGain, cs->fDryGain, to_process); 621 622 cm->sGraph[G_OUT].process(cm->vOut, to_process); 623 cm->pMeter[M_OUT]->setValue(dsp::abs_max(cm->vOut, to_process)); 624 cs->sGraph[G_OUT].process(cs->vOut, to_process); 625 cs->pMeter[M_OUT]->setValue(dsp::abs_max(cs->vOut, to_process)); 626 627 if (!bMSListen) 628 dsp::ms_to_lr(cm->vOut, cs->vOut, cm->vOut, cs->vOut, to_process); 629 if (cm->bScListen) 630 dsp::copy(cm->vOut, cm->vSc, to_process); 631 if (cs->bScListen) 632 dsp::copy(cs->vOut, cs->vSc, to_process); 633 } 634 else 635 { 636 for (size_t i=0; i<channels; ++i) 637 { 638 // Mix dry/wet signal or copy sidechain signal 639 channel_t *c = &vChannels[i]; 640 if (c->bScListen) 641 dsp::copy(c->vOut, c->vSc, to_process); 642 else 643 dsp::mix2(c->vOut, c->vIn, c->fMakeup * c->fWetGain, c->fDryGain, to_process); 644 645 c->sGraph[G_OUT].process(c->vOut, to_process); // Output signal 646 c->pMeter[M_OUT]->setValue(dsp::abs_max(c->vOut, to_process)); 647 } 648 } 649 650 // Final metering 651 for (size_t i=0; i<channels; ++i) 652 { 653 // Apply bypass 654 channel_t *c = &vChannels[i]; 655 c->sDryDelay.process(c->vIn, in_buf[i], to_process); // Dry compensation 656 c->sBypass.process(out_buf[i], c->vIn, c->vOut, to_process); 657 658 in_buf[i] += to_process; 659 out_buf[i] += to_process; 660 sc_buf[i] += to_process; 661 } 662 663 left -= to_process; 664 } 665 666 if ((!bPause) || (bClear) || (bUISync)) 667 { 668 // Process mesh requests 669 for (size_t i=0; i<channels; ++i) 670 { 671 // Get channel 672 channel_t *c = &vChannels[i]; 673 674 for (size_t j=0; j<G_TOTAL; ++j) 675 { 676 // Check that port is bound 677 if (c->pGraph[j] == NULL) 678 continue; 679 680 // Clear data if requested 681 if (bClear) 682 dsp::fill_zero(c->sGraph[j].data(), expander_base_metadata::TIME_MESH_SIZE); 683 684 // Get mesh 685 mesh_t *mesh = c->pGraph[j]->getBuffer<mesh_t>(); 686 if ((mesh != NULL) && (mesh->isEmpty())) 687 { 688 // Fill mesh with new values 689 dsp::copy(mesh->pvData[0], vTime, expander_base_metadata::TIME_MESH_SIZE); 690 dsp::copy(mesh->pvData[1], c->sGraph[j].data(), expander_base_metadata::TIME_MESH_SIZE); 691 mesh->data(2, expander_base_metadata::TIME_MESH_SIZE); 692 } 693 } // for j 694 } 695 696 bUISync = false; 697 } 698 699 // Output expander curves for each channel 700 for (size_t i=0; i<channels; ++i) 701 { 702 channel_t *c = &vChannels[i]; 703 704 // Output expansion curve 705 if (c->pCurve != NULL) 706 { 707 mesh_t *mesh = c->pCurve->getBuffer<mesh_t>(); 708 if ((c->nSync & S_CURVE) && (mesh != NULL) && (mesh->isEmpty())) 709 { 710 // Copy frequency points 711 dsp::copy(mesh->pvData[0], vCurve, expander_base_metadata::CURVE_MESH_SIZE); 712 c->sExp.curve(mesh->pvData[1], vCurve, expander_base_metadata::CURVE_MESH_SIZE); 713 if (c->fMakeup != 1.0f) 714 dsp::mul_k2(mesh->pvData[1], c->fMakeup, expander_base_metadata::CURVE_MESH_SIZE); 715 716 // Mark mesh containing data 717 mesh->data(2, expander_base_metadata::CURVE_MESH_SIZE); 718 c->nSync &= ~S_CURVE; 719 } 720 } 721 722 // Update meter 723 if ((c->pMeter[M_ENV] != NULL) && (c->pMeter[M_CURVE] != NULL)) 724 { 725 c->fDotIn = c->pMeter[M_ENV]->getValue(); 726 c->fDotOut = c->sExp.curve(c->fDotIn) * c->fMakeup; 727 c->pMeter[M_CURVE]->setValue(c->fDotOut); 728 } 729 } 730 731 // Request for redraw 732 if (pWrapper != NULL) 733 pWrapper->query_display_draw(); 734 } 735 inline_display(ICanvas * cv,size_t width,size_t height)736 bool expander_base::inline_display(ICanvas *cv, size_t width, size_t height) 737 { 738 // Check proportions 739 if (height > width) 740 height = width; 741 742 // Init canvas 743 if (!cv->init(width, height)) 744 return false; 745 width = cv->width(); 746 height = cv->height(); 747 748 // Clear background 749 bool bypassing = vChannels[0].sBypass.bypassing(); 750 cv->set_color_rgb((bypassing) ? CV_DISABLED : CV_BACKGROUND); 751 cv->paint(); 752 753 float zx = 1.0f/GAIN_AMP_M_72_DB; 754 float zy = 1.0f/GAIN_AMP_M_72_DB; 755 float dx = width/(logf(GAIN_AMP_P_24_DB)-logf(GAIN_AMP_M_72_DB)); 756 float dy = height/(logf(GAIN_AMP_M_72_DB)-logf(GAIN_AMP_P_24_DB)); 757 758 // Draw horizontal and vertical lines 759 cv->set_line_width(1.0); 760 cv->set_color_rgb((bypassing) ? CV_SILVER: CV_YELLOW, 0.5f); 761 for (float i=GAIN_AMP_M_72_DB; i<GAIN_AMP_P_24_DB; i *= GAIN_AMP_P_24_DB) 762 { 763 float ax = dx*(logf(i*zx)); 764 float ay = height + dy*(logf(i*zy)); 765 cv->line(ax, 0, ax, height); 766 cv->line(0, ay, width, ay); 767 } 768 769 // Draw 1:1 line 770 cv->set_line_width(2.0); 771 cv->set_color_rgb(CV_GRAY); 772 { 773 float ax1 = dx*(logf(GAIN_AMP_M_72_DB*zx)); 774 float ax2 = dx*(logf(GAIN_AMP_P_24_DB*zx)); 775 float ay1 = height + dy*(logf(GAIN_AMP_M_72_DB*zy)); 776 float ay2 = height + dy*(logf(GAIN_AMP_P_24_DB*zy)); 777 cv->line(ax1, ay1, ax2, ay2); 778 } 779 780 // Draw axis 781 cv->set_color_rgb((bypassing) ? CV_SILVER : CV_WHITE); 782 { 783 float ax = dx*(logf(GAIN_AMP_0_DB*zx)); 784 float ay = height + dy*(logf(GAIN_AMP_0_DB*zy)); 785 cv->line(ax, 0, ax, height); 786 cv->line(0, ay, width, ay); 787 } 788 789 // Reuse display 790 pIDisplay = float_buffer_t::reuse(pIDisplay, 4, width); 791 float_buffer_t *b = pIDisplay; 792 if (b == NULL) 793 return false; 794 795 size_t channels = ((nMode == EM_MONO) || (nMode == EM_STEREO)) ? 1 : 2; 796 static uint32_t c_colors[] = { 797 CV_MIDDLE_CHANNEL, CV_MIDDLE_CHANNEL, 798 CV_MIDDLE_CHANNEL, CV_MIDDLE_CHANNEL, 799 CV_LEFT_CHANNEL, CV_RIGHT_CHANNEL, 800 CV_MIDDLE_CHANNEL, CV_SIDE_CHANNEL 801 }; 802 803 bool aa = cv->set_anti_aliasing(true); 804 cv->set_line_width(2); 805 806 for (size_t i=0; i<channels; ++i) 807 { 808 channel_t *c = &vChannels[i]; 809 810 for (size_t j=0; j<width; ++j) 811 { 812 size_t k = (j*expander_base_metadata::CURVE_MESH_SIZE)/width; 813 b->v[0][j] = vCurve[k]; 814 } 815 c->sExp.curve(b->v[1], b->v[0], width); 816 if (c->fMakeup != 1.0f) 817 dsp::mul_k2(b->v[1], c->fMakeup, width); 818 819 dsp::fill(b->v[2], 0.0f, width); 820 dsp::fill(b->v[3], height, width); 821 dsp::axis_apply_log1(b->v[2], b->v[0], zx, dx, width); 822 dsp::axis_apply_log1(b->v[3], b->v[1], zy, dy, width); 823 824 // Draw mesh 825 uint32_t color = (bypassing || !(active())) ? CV_SILVER : c_colors[nMode*2 + i]; 826 cv->set_color_rgb(color); 827 cv->draw_lines(b->v[2], b->v[3], width); 828 } 829 830 // Draw dot 831 if (active()) 832 { 833 for (size_t i=0; i<channels; ++i) 834 { 835 channel_t *c = &vChannels[i]; 836 837 uint32_t color = (bypassing) ? CV_SILVER : c_colors[nMode*2 + i]; 838 Color c1(color), c2(color); 839 c2.alpha(0.9); 840 841 float ax = dx*(logf(c->fDotIn*zx)); 842 float ay = height + dy*(logf(c->fDotOut*zy)); 843 844 cv->radial_gradient(ax, ay, c1, c2, 12); 845 cv->set_color_rgb(0); 846 cv->circle(ax, ay, 4); 847 cv->set_color_rgb(color); 848 cv->circle(ax, ay, 3); 849 } 850 } 851 852 cv->set_anti_aliasing(aa); 853 854 return true; 855 } 856 857 //------------------------------------------------------------------------- 858 // Expander derivatives expander_mono()859 expander_mono::expander_mono() : expander_base(metadata, false, EM_MONO) 860 { 861 } 862 expander_stereo()863 expander_stereo::expander_stereo() : expander_base(metadata, false, EM_STEREO) 864 { 865 } 866 expander_lr()867 expander_lr::expander_lr() : expander_base(metadata, false, EM_LR) 868 { 869 } 870 expander_ms()871 expander_ms::expander_ms() : expander_base(metadata, false, EM_MS) 872 { 873 } 874 sc_expander_mono()875 sc_expander_mono::sc_expander_mono() : expander_base(metadata, true, EM_MONO) 876 { 877 } 878 sc_expander_stereo()879 sc_expander_stereo::sc_expander_stereo() : expander_base(metadata, true, EM_STEREO) 880 { 881 } 882 sc_expander_lr()883 sc_expander_lr::sc_expander_lr() : expander_base(metadata, true, EM_LR) 884 { 885 } 886 sc_expander_ms()887 sc_expander_ms::sc_expander_ms() : expander_base(metadata, true, EM_MS) 888 { 889 } 890 } 891 892 893 894 895 896