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