1/* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "DPFrequency" 18//#define LOG_NDEBUG 0 19 20#include <log/log.h> 21#include "DPFrequency.h" 22#include <algorithm> 23 24namespace dp_fx { 25 26using Eigen::MatrixXd; 27#define MAX_BLOCKSIZE 16384 //For this implementation 28#define MIN_BLOCKSIZE 8 29 30#define CIRCULAR_BUFFER_UPSAMPLE 4 //4 times buffer size 31 32static constexpr float MIN_ENVELOPE = 1e-6f; //-120 dB 33//helper functionS 34static inline bool isPowerOf2(unsigned long n) { 35 return (n & (n - 1)) == 0; 36} 37static constexpr float EPSILON = 0.0000001f; 38 39static inline bool isZero(float f) { 40 return fabs(f) <= EPSILON; 41} 42 43template <class T> 44bool compareEquality(T a, T b) { 45 return (a == b); 46} 47 48template <> bool compareEquality<float>(float a, float b) { 49 return isZero(a - b); 50} 51 52//TODO: avoid using macro for estimating change and assignment. 53#define IS_CHANGED(c, a, b) { c |= !compareEquality(a,b); \ 54 (a) = (b); } 55 56//ChannelBuffers helper 57void ChannelBuffer::initBuffers(unsigned int blockSize, unsigned int overlapSize, 58 unsigned int halfFftSize, unsigned int samplingRate, DPBase &dpBase) { 59 ALOGV("ChannelBuffer::initBuffers blockSize %d, overlap %d, halfFft %d", 60 blockSize, overlapSize, halfFftSize); 61 62 mSamplingRate = samplingRate; 63 mBlockSize = blockSize; 64 65 cBInput.resize(mBlockSize * CIRCULAR_BUFFER_UPSAMPLE); 66 cBOutput.resize(mBlockSize * CIRCULAR_BUFFER_UPSAMPLE); 67 68 //fill input with half block size... 69 for (unsigned int k = 0; k < mBlockSize/2; k++) { 70 cBInput.write(0); 71 } 72 73 //temp vectors 74 input.resize(mBlockSize); 75 output.resize(mBlockSize); 76 outTail.resize(overlapSize); 77 78 //module vectors 79 mPreEqFactorVector.resize(halfFftSize, 1.0); 80 mPostEqFactorVector.resize(halfFftSize, 1.0); 81 82 mPreEqBands.resize(dpBase.getPreEqBandCount()); 83 mMbcBands.resize(dpBase.getMbcBandCount()); 84 mPostEqBands.resize(dpBase.getPostEqBandCount()); 85 ALOGV("mPreEqBands %zu, mMbcBands %zu, mPostEqBands %zu",mPreEqBands.size(), 86 mMbcBands.size(), mPostEqBands.size()); 87 88 DPChannel *pChannel = dpBase.getChannel(0); 89 if (pChannel != nullptr) { 90 mPreEqInUse = pChannel->getPreEq()->isInUse(); 91 mMbcInUse = pChannel->getMbc()->isInUse(); 92 mPostEqInUse = pChannel->getPostEq()->isInUse(); 93 mLimiterInUse = pChannel->getLimiter()->isInUse(); 94 } 95 96 mLimiterParams.linkGroup = -1; //no group. 97} 98 99void ChannelBuffer::computeBinStartStop(BandParams &bp, size_t binStart) { 100 101 bp.binStart = binStart; 102 bp.binStop = (int)(0.5 + bp.freqCutoffHz * mBlockSize / mSamplingRate); 103} 104 105//== LinkedLimiters Helper 106void LinkedLimiters::reset() { 107 mGroupsMap.clear(); 108} 109 110void LinkedLimiters::update(int32_t group, int index) { 111 mGroupsMap[group].push_back(index); 112} 113 114void LinkedLimiters::remove(int index) { 115 //check all groups and if index is found, remove it. 116 //if group is empty afterwards, remove it. 117 for (auto it = mGroupsMap.begin(); it != mGroupsMap.end(); ) { 118 for (auto itIndex = it->second.begin(); itIndex != it->second.end(); ) { 119 if (*itIndex == index) { 120 itIndex = it->second.erase(itIndex); 121 } else { 122 ++itIndex; 123 } 124 } 125 if (it->second.size() == 0) { 126 it = mGroupsMap.erase(it); 127 } else { 128 ++it; 129 } 130 } 131} 132 133//== DPFrequency 134void DPFrequency::reset() { 135} 136 137size_t DPFrequency::getMinBockSize() { 138 return MIN_BLOCKSIZE; 139} 140 141size_t DPFrequency::getMaxBockSize() { 142 return MAX_BLOCKSIZE; 143} 144 145void DPFrequency::configure(size_t blockSize, size_t overlapSize, 146 size_t samplingRate) { 147 ALOGV("configure"); 148 mBlockSize = blockSize; 149 if (mBlockSize > MAX_BLOCKSIZE) { 150 mBlockSize = MAX_BLOCKSIZE; 151 } else if (mBlockSize < MIN_BLOCKSIZE) { 152 mBlockSize = MIN_BLOCKSIZE; 153 } else { 154 if (!isPowerOf2(blockSize)) { 155 //find next highest power of 2. 156 mBlockSize = 1 << (32 - __builtin_clz(blockSize)); 157 } 158 } 159 160 mHalfFFTSize = 1 + mBlockSize / 2; //including Nyquist bin 161 mOverlapSize = std::min(overlapSize, mBlockSize/2); 162 163 int channelcount = getChannelCount(); 164 mSamplingRate = samplingRate; 165 mChannelBuffers.resize(channelcount); 166 for (int ch = 0; ch < channelcount; ch++) { 167 mChannelBuffers[ch].initBuffers(mBlockSize, mOverlapSize, mHalfFFTSize, 168 mSamplingRate, *this); 169 } 170 171 //effective number of frames processed per second 172 mBlocksPerSecond = (float)mSamplingRate / (mBlockSize - mOverlapSize); 173 174 fill_window(mVWindow, RDSP_WINDOW_HANNING_FLAT_TOP, mBlockSize, mOverlapSize); 175 176 //compute window rms for energy compensation 177 mWindowRms = 0; 178 for (size_t i = 0; i < mVWindow.size(); i++) { 179 mWindowRms += mVWindow[i] * mVWindow[i]; 180 } 181 182 //Making sure window rms is not zero. 183 mWindowRms = std::max(sqrt(mWindowRms / mVWindow.size()), MIN_ENVELOPE); 184} 185 186void DPFrequency::updateParameters(ChannelBuffer &cb, int channelIndex) { 187 DPChannel *pChannel = getChannel(channelIndex); 188 189 if (pChannel == nullptr) { 190 ALOGE("Error: updateParameters null DPChannel %d", channelIndex); 191 return; 192 } 193 194 //===Input Gain and preEq 195 { 196 bool changed = false; 197 IS_CHANGED(changed, cb.inputGainDb, pChannel->getInputGain()); 198 //===EqPre 199 if (cb.mPreEqInUse) { 200 DPEq *pPreEq = pChannel->getPreEq(); 201 if (pPreEq == nullptr) { 202 ALOGE("Error: updateParameters null PreEq for channel: %d", channelIndex); 203 return; 204 } 205 IS_CHANGED(changed, cb.mPreEqEnabled, pPreEq->isEnabled()); 206 if (cb.mPreEqEnabled) { 207 for (unsigned int b = 0; b < getPreEqBandCount(); b++) { 208 DPEqBand *pEqBand = pPreEq->getBand(b); 209 if (pEqBand == nullptr) { 210 ALOGE("Error: updateParameters null PreEqBand for band %d", b); 211 return; //failed. 212 } 213 ChannelBuffer::EqBandParams *pEqBandParams = &cb.mPreEqBands[b]; 214 IS_CHANGED(changed, pEqBandParams->enabled, pEqBand->isEnabled()); 215 IS_CHANGED(changed, pEqBandParams->freqCutoffHz, 216 pEqBand->getCutoffFrequency()); 217 IS_CHANGED(changed, pEqBandParams->gainDb, pEqBand->getGain()); 218 } 219 } 220 } 221 222 if (changed) { 223 float inputGainFactor = dBtoLinear(cb.inputGainDb); 224 if (cb.mPreEqInUse && cb.mPreEqEnabled) { 225 ALOGV("preEq changed, recomputing! channel %d", channelIndex); 226 size_t binNext = 0; 227 for (unsigned int b = 0; b < getPreEqBandCount(); b++) { 228 ChannelBuffer::EqBandParams *pEqBandParams = &cb.mPreEqBands[b]; 229 230 //frequency translation 231 cb.computeBinStartStop(*pEqBandParams, binNext); 232 binNext = pEqBandParams->binStop + 1; 233 float factor = dBtoLinear(pEqBandParams->gainDb); 234 if (!pEqBandParams->enabled) { 235 factor = inputGainFactor; 236 } 237 for (size_t k = pEqBandParams->binStart; 238 k <= pEqBandParams->binStop && k < mHalfFFTSize; k++) { 239 cb.mPreEqFactorVector[k] = factor * inputGainFactor; 240 } 241 } 242 } else { 243 ALOGV("only input gain changed, recomputing!"); 244 //populate PreEq factor with input gain factor. 245 for (size_t k = 0; k < mHalfFFTSize; k++) { 246 cb.mPreEqFactorVector[k] = inputGainFactor; 247 } 248 } 249 } 250 } //inputGain and preEq 251 252 //===EqPost 253 if (cb.mPostEqInUse) { 254 bool changed = false; 255 256 DPEq *pPostEq = pChannel->getPostEq(); 257 if (pPostEq == nullptr) { 258 ALOGE("Error: updateParameters null postEq for channel: %d", channelIndex); 259 return; //failed. 260 } 261 IS_CHANGED(changed, cb.mPostEqEnabled, pPostEq->isEnabled()); 262 if (cb.mPostEqEnabled) { 263 for (unsigned int b = 0; b < getPostEqBandCount(); b++) { 264 DPEqBand *pEqBand = pPostEq->getBand(b); 265 if (pEqBand == nullptr) { 266 ALOGE("Error: updateParameters PostEqBand NULL for band %d", b); 267 return; //failed. 268 } 269 ChannelBuffer::EqBandParams *pEqBandParams = &cb.mPostEqBands[b]; 270 IS_CHANGED(changed, pEqBandParams->enabled, pEqBand->isEnabled()); 271 IS_CHANGED(changed, pEqBandParams->freqCutoffHz, 272 pEqBand->getCutoffFrequency()); 273 IS_CHANGED(changed, pEqBandParams->gainDb, pEqBand->getGain()); 274 } 275 if (changed) { 276 ALOGV("postEq changed, recomputing! channel %d", channelIndex); 277 size_t binNext = 0; 278 for (unsigned int b = 0; b < getPostEqBandCount(); b++) { 279 ChannelBuffer::EqBandParams *pEqBandParams = &cb.mPostEqBands[b]; 280 281 //frequency translation 282 cb.computeBinStartStop(*pEqBandParams, binNext); 283 binNext = pEqBandParams->binStop + 1; 284 float factor = dBtoLinear(pEqBandParams->gainDb); 285 if (!pEqBandParams->enabled) { 286 factor = 1.0; 287 } 288 for (size_t k = pEqBandParams->binStart; 289 k <= pEqBandParams->binStop && k < mHalfFFTSize; k++) { 290 cb.mPostEqFactorVector[k] = factor; 291 } 292 } 293 } 294 } //enabled 295 } 296 297 //===MBC 298 if (cb.mMbcInUse) { 299 DPMbc *pMbc = pChannel->getMbc(); 300 if (pMbc == nullptr) { 301 ALOGE("Error: updateParameters Mbc NULL for channel: %d", channelIndex); 302 return; 303 } 304 cb.mMbcEnabled = pMbc->isEnabled(); 305 if (cb.mMbcEnabled) { 306 bool changed = false; 307 for (unsigned int b = 0; b < getMbcBandCount(); b++) { 308 DPMbcBand *pMbcBand = pMbc->getBand(b); 309 if (pMbcBand == nullptr) { 310 ALOGE("Error: updateParameters MbcBand NULL for band %d", b); 311 return; //failed. 312 } 313 ChannelBuffer::MbcBandParams *pMbcBandParams = &cb.mMbcBands[b]; 314 pMbcBandParams->enabled = pMbcBand->isEnabled(); 315 IS_CHANGED(changed, pMbcBandParams->freqCutoffHz, 316 pMbcBand->getCutoffFrequency()); 317 318 pMbcBandParams->gainPreDb = pMbcBand->getPreGain(); 319 pMbcBandParams->gainPostDb = pMbcBand->getPostGain(); 320 pMbcBandParams->attackTimeMs = pMbcBand->getAttackTime(); 321 pMbcBandParams->releaseTimeMs = pMbcBand->getReleaseTime(); 322 pMbcBandParams->ratio = pMbcBand->getRatio(); 323 pMbcBandParams->thresholdDb = pMbcBand->getThreshold(); 324 pMbcBandParams->kneeWidthDb = pMbcBand->getKneeWidth(); 325 pMbcBandParams->noiseGateThresholdDb = pMbcBand->getNoiseGateThreshold(); 326 pMbcBandParams->expanderRatio = pMbcBand->getExpanderRatio(); 327 328 } 329 330 if (changed) { 331 ALOGV("mbc changed, recomputing! channel %d", channelIndex); 332 size_t binNext= 0; 333 for (unsigned int b = 0; b < getMbcBandCount(); b++) { 334 ChannelBuffer::MbcBandParams *pMbcBandParams = &cb.mMbcBands[b]; 335 336 pMbcBandParams->previousEnvelope = 0; 337 338 //frequency translation 339 cb.computeBinStartStop(*pMbcBandParams, binNext); 340 binNext = pMbcBandParams->binStop + 1; 341 } 342 } 343 } 344 } 345 346 //===Limiter 347 if (cb.mLimiterInUse) { 348 bool changed = false; 349 DPLimiter *pLimiter = pChannel->getLimiter(); 350 if (pLimiter == nullptr) { 351 ALOGE("Error: updateParameters Limiter NULL for channel: %d", channelIndex); 352 return; 353 } 354 cb.mLimiterEnabled = pLimiter->isEnabled(); 355 if (cb.mLimiterEnabled) { 356 IS_CHANGED(changed, cb.mLimiterParams.linkGroup , 357 (int32_t)pLimiter->getLinkGroup()); 358 cb.mLimiterParams.attackTimeMs = pLimiter->getAttackTime(); 359 cb.mLimiterParams.releaseTimeMs = pLimiter->getReleaseTime(); 360 cb.mLimiterParams.ratio = pLimiter->getRatio(); 361 cb.mLimiterParams.thresholdDb = pLimiter->getThreshold(); 362 cb.mLimiterParams.postGainDb = pLimiter->getPostGain(); 363 } 364 365 if (changed) { 366 ALOGV("limiter changed, recomputing linkGroups for %d", channelIndex); 367 mLinkedLimiters.remove(channelIndex); //in case it was already there. 368 mLinkedLimiters.update(cb.mLimiterParams.linkGroup, channelIndex); 369 } 370 } 371 372 //=== Output Gain 373 cb.outputGainDb = pChannel->getOutputGain(); 374} 375 376size_t DPFrequency::processSamples(const float *in, float *out, size_t samples) { 377 const float *pIn = in; 378 float *pOut = out; 379 380 int channelCount = mChannelBuffers.size(); 381 if (channelCount < 1) { 382 ALOGW("warning: no Channels ready for processing"); 383 return 0; 384 } 385 386 //**Check if parameters have changed and update 387 for (int ch = 0; ch < channelCount; ch++) { 388 updateParameters(mChannelBuffers[ch], ch); 389 } 390 391 //**separate into channels 392 for (size_t k = 0; k < samples; k += channelCount) { 393 for (int ch = 0; ch < channelCount; ch++) { 394 mChannelBuffers[ch].cBInput.write(*pIn++); 395 } 396 } 397 398 //**process all channelBuffers 399 processChannelBuffers(mChannelBuffers); 400 401 //** estimate how much data is available in ALL channels 402 size_t available = mChannelBuffers[0].cBOutput.availableToRead(); 403 for (int ch = 1; ch < channelCount; ch++) { 404 available = std::min(available, mChannelBuffers[ch].cBOutput.availableToRead()); 405 } 406 407 //** make sure to output just what the buffer can handle 408 if (available > samples/channelCount) { 409 available = samples/channelCount; 410 } 411 412 //**Prepend zeroes if necessary 413 size_t fill = samples - (channelCount * available); 414 for (size_t k = 0; k < fill; k++) { 415 *pOut++ = 0; 416 } 417 418 //**interleave channels 419 for (size_t k = 0; k < available; k++) { 420 for (int ch = 0; ch < channelCount; ch++) { 421 *pOut++ = mChannelBuffers[ch].cBOutput.read(); 422 } 423 } 424 425 return samples; 426} 427 428size_t DPFrequency::processChannelBuffers(CBufferVector &channelBuffers) { 429 const int channelCount = channelBuffers.size(); 430 size_t processedSamples = 0; 431 size_t processFrames = mBlockSize - mOverlapSize; 432 433 size_t available = channelBuffers[0].cBInput.availableToRead(); 434 for (int ch = 1; ch < channelCount; ch++) { 435 available = std::min(available, channelBuffers[ch].cBInput.availableToRead()); 436 } 437 438 while (available >= processFrames) { 439 //First pass 440 for (int ch = 0; ch < channelCount; ch++) { 441 ChannelBuffer * pCb = &channelBuffers[ch]; 442 //move tail of previous 443 std::copy(pCb->input.begin() + processFrames, 444 pCb->input.end(), 445 pCb->input.begin()); 446 447 //read new available data 448 for (unsigned int k = 0; k < processFrames; k++) { 449 pCb->input[mOverlapSize + k] = pCb->cBInput.read(); 450 } 451 //first stages: fft, preEq, mbc, postEq and start of Limiter 452 processedSamples += processFirstStages(*pCb); 453 } 454 455 //**compute linked limiters and update levels if needed 456 processLinkedLimiters(channelBuffers); 457 458 //final pass. 459 for (int ch = 0; ch < channelCount; ch++) { 460 ChannelBuffer * pCb = &channelBuffers[ch]; 461 462 //linked limiter and ifft 463 processLastStages(*pCb); 464 465 //mix tail (and capture new tail 466 for (unsigned int k = 0; k < mOverlapSize; k++) { 467 pCb->output[k] += pCb->outTail[k]; 468 pCb->outTail[k] = pCb->output[processFrames + k]; //new tail 469 } 470 471 //output data 472 for (unsigned int k = 0; k < processFrames; k++) { 473 pCb->cBOutput.write(pCb->output[k]); 474 } 475 } 476 available -= processFrames; 477 } 478 return processedSamples; 479} 480size_t DPFrequency::processFirstStages(ChannelBuffer &cb) { 481 482 //##apply window 483 Eigen::Map<Eigen::VectorXf> eWindow(&mVWindow[0], mVWindow.size()); 484 Eigen::Map<Eigen::VectorXf> eInput(&cb.input[0], cb.input.size()); 485 486 Eigen::VectorXf eWin = eInput.cwiseProduct(eWindow); //apply window 487 488 //##fft 489 //Note: we are using eigen with the default scaling, which ensures that 490 // IFFT( FFT(x) ) = x. 491 // TODO: optimize by using the noscale option, and compensate with dB scale offsets 492 mFftServer.fwd(cb.complexTemp, eWin); 493 494 size_t cSize = cb.complexTemp.size(); 495 size_t maxBin = std::min(cSize/2, mHalfFFTSize); 496 497 //== EqPre (always runs) 498 for (size_t k = 0; k < maxBin; k++) { 499 cb.complexTemp[k] *= cb.mPreEqFactorVector[k]; 500 } 501 502 //== MBC 503 if (cb.mMbcInUse && cb.mMbcEnabled) { 504 for (size_t band = 0; band < cb.mMbcBands.size(); band++) { 505 ChannelBuffer::MbcBandParams *pMbcBandParams = &cb.mMbcBands[band]; 506 float fEnergySum = 0; 507 508 //apply pre gain. 509 float preGainFactor = dBtoLinear(pMbcBandParams->gainPreDb); 510 float preGainSquared = preGainFactor * preGainFactor; 511 512 for (size_t k = pMbcBandParams->binStart; k <= pMbcBandParams->binStop; k++) { 513 fEnergySum += std::norm(cb.complexTemp[k]) * preGainSquared; //mag squared 514 } 515 516 //Eigen FFT is full spectrum, even if the source was real data. 517 // Each half spectrum has half the energy. This is taken into account with the * 2 518 // factor in the energy computations. 519 // energy = sqrt(sum_components_squared) number_points 520 // in here, the fEnergySum is duplicated to account for the second half spectrum, 521 // and the windowRms is used to normalize by the expected energy reduction 522 // caused by the window used (expected for steady state signals) 523 fEnergySum = sqrt(fEnergySum * 2) / (mBlockSize * mWindowRms); 524 525 // updates computed per frame advance. 526 float fTheta = 0.0; 527 float fFAttSec = pMbcBandParams->attackTimeMs / 1000; //in seconds 528 float fFRelSec = pMbcBandParams->releaseTimeMs / 1000; //in seconds 529 530 if (fEnergySum > pMbcBandParams->previousEnvelope) { 531 fTheta = exp(-1.0 / (fFAttSec * mBlocksPerSecond)); 532 } else { 533 fTheta = exp(-1.0 / (fFRelSec * mBlocksPerSecond)); 534 } 535 536 float fEnv = (1.0 - fTheta) * fEnergySum + fTheta * pMbcBandParams->previousEnvelope; 537 //preserve for next iteration 538 pMbcBandParams->previousEnvelope = fEnv; 539 540 if (fEnv < MIN_ENVELOPE) { 541 fEnv = MIN_ENVELOPE; 542 } 543 const float envDb = linearToDb(fEnv); 544 float newLevelDb = envDb; 545 //using shorter variables for code clarity 546 const float thresholdDb = pMbcBandParams->thresholdDb; 547 const float ratio = pMbcBandParams->ratio; 548 const float kneeWidthDbHalf = pMbcBandParams->kneeWidthDb / 2; 549 const float noiseGateThresholdDb = pMbcBandParams->noiseGateThresholdDb; 550 const float expanderRatio = pMbcBandParams->expanderRatio; 551 552 //find segment 553 if (envDb > thresholdDb + kneeWidthDbHalf) { 554 //compression segment 555 newLevelDb = envDb + ((1 / ratio) - 1) * (envDb - thresholdDb); 556 } else if (envDb > thresholdDb - kneeWidthDbHalf) { 557 //knee-compression segment 558 float temp = (envDb - thresholdDb + kneeWidthDbHalf); 559 newLevelDb = envDb + ((1 / ratio) - 1) * 560 temp * temp / (kneeWidthDbHalf * 4); 561 } else if (envDb < noiseGateThresholdDb) { 562 //expander segment 563 newLevelDb = noiseGateThresholdDb - 564 expanderRatio * (noiseGateThresholdDb - envDb); 565 } 566 567 float newFactor = dBtoLinear(newLevelDb - envDb); 568 569 //apply post gain. 570 newFactor *= dBtoLinear(pMbcBandParams->gainPostDb); 571 572 //apply to this band 573 for (size_t k = pMbcBandParams->binStart; k <= pMbcBandParams->binStop; k++) { 574 cb.complexTemp[k] *= newFactor; 575 } 576 577 } //end per band process 578 579 } //end MBC 580 581 //== EqPost 582 if (cb.mPostEqInUse && cb.mPostEqEnabled) { 583 for (size_t k = 0; k < maxBin; k++) { 584 cb.complexTemp[k] *= cb.mPostEqFactorVector[k]; 585 } 586 } 587 588 //== Limiter. First Pass 589 if (cb.mLimiterInUse && cb.mLimiterEnabled) { 590 float fEnergySum = 0; 591 for (size_t k = 0; k < maxBin; k++) { 592 fEnergySum += std::norm(cb.complexTemp[k]); 593 } 594 595 //see explanation above for energy computation logic 596 fEnergySum = sqrt(fEnergySum * 2) / (mBlockSize * mWindowRms); 597 float fTheta = 0.0; 598 float fFAttSec = cb.mLimiterParams.attackTimeMs / 1000; //in seconds 599 float fFRelSec = cb.mLimiterParams.releaseTimeMs / 1000; //in seconds 600 601 if (fEnergySum > cb.mLimiterParams.previousEnvelope) { 602 fTheta = exp(-1.0 / (fFAttSec * mBlocksPerSecond)); 603 } else { 604 fTheta = exp(-1.0 / (fFRelSec * mBlocksPerSecond)); 605 } 606 607 float fEnv = (1.0 - fTheta) * fEnergySum + fTheta * cb.mLimiterParams.previousEnvelope; 608 //preserve for next iteration 609 cb.mLimiterParams.previousEnvelope = fEnv; 610 611 const float envDb = linearToDb(fEnv); 612 float newFactorDb = 0; 613 //using shorter variables for code clarity 614 const float thresholdDb = cb.mLimiterParams.thresholdDb; 615 const float ratio = cb.mLimiterParams.ratio; 616 617 if (envDb > thresholdDb) { 618 //limiter segment 619 newFactorDb = ((1 / ratio) - 1) * (envDb - thresholdDb); 620 } 621 622 float newFactor = dBtoLinear(newFactorDb); 623 624 cb.mLimiterParams.newFactor = newFactor; 625 626 } //end Limiter 627 return mBlockSize; 628} 629 630void DPFrequency::processLinkedLimiters(CBufferVector &channelBuffers) { 631 632 const int channelCount = channelBuffers.size(); 633 for (auto &groupPair : mLinkedLimiters.mGroupsMap) { 634 float minFactor = 1.0; 635 //estimate minfactor for all linked 636 for(int index : groupPair.second) { 637 if (index >= 0 && index < channelCount) { 638 minFactor = std::min(channelBuffers[index].mLimiterParams.newFactor, minFactor); 639 } 640 } 641 //apply minFactor 642 for(int index : groupPair.second) { 643 if (index >= 0 && index < channelCount) { 644 channelBuffers[index].mLimiterParams.linkFactor = minFactor; 645 } 646 } 647 } 648} 649 650size_t DPFrequency::processLastStages(ChannelBuffer &cb) { 651 652 float outputGainFactor = dBtoLinear(cb.outputGainDb); 653 //== Limiter. last Pass 654 if (cb.mLimiterInUse && cb.mLimiterEnabled) { 655 //compute factor, with post-gain 656 float factor = cb.mLimiterParams.linkFactor * dBtoLinear(cb.mLimiterParams.postGainDb); 657 outputGainFactor *= factor; 658 } 659 660 //apply to all if != 1.0 661 if (!compareEquality(outputGainFactor, 1.0f)) { 662 size_t cSize = cb.complexTemp.size(); 663 size_t maxBin = std::min(cSize/2, mHalfFFTSize); 664 for (size_t k = 0; k < maxBin; k++) { 665 cb.complexTemp[k] *= outputGainFactor; 666 } 667 } 668 669 //##ifft directly to output. 670 Eigen::Map<Eigen::VectorXf> eOutput(&cb.output[0], cb.output.size()); 671 mFftServer.inv(eOutput, cb.complexTemp); 672 return mBlockSize; 673} 674 675} //namespace dp_fx 676