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 
24 namespace dp_fx {
25 
26 using 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 
32 static constexpr float MIN_ENVELOPE = 1e-6f; //-120 dB
33 //helper functionS
isPowerOf2(unsigned long n)34 static inline bool isPowerOf2(unsigned long n) {
35     return (n & (n - 1)) == 0;
36 }
37 static constexpr float EPSILON = 0.0000001f;
38 
isZero(float f)39 static inline bool isZero(float f) {
40     return fabs(f) <= EPSILON;
41 }
42 
43 template <class T>
compareEquality(T a,T b)44 bool compareEquality(T a, T b) {
45     return (a == b);
46 }
47 
compareEquality(float a,float b)48 template <> 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
initBuffers(unsigned int blockSize,unsigned int overlapSize,unsigned int halfFftSize,unsigned int samplingRate,DPBase & dpBase)57 void 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 
computeBinStartStop(BandParams & bp,size_t binStart)99 void 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
reset()106 void LinkedLimiters::reset() {
107     mGroupsMap.clear();
108 }
109 
update(int32_t group,int index)110 void LinkedLimiters::update(int32_t group, int index) {
111     mGroupsMap[group].push_back(index);
112 }
113 
remove(int index)114 void 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
reset()134 void DPFrequency::reset() {
135 }
136 
getMinBockSize()137 size_t DPFrequency::getMinBockSize() {
138     return MIN_BLOCKSIZE;
139 }
140 
getMaxBockSize()141 size_t DPFrequency::getMaxBockSize() {
142     return MAX_BLOCKSIZE;
143 }
144 
configure(size_t blockSize,size_t overlapSize,size_t samplingRate)145 void 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 
updateParameters(ChannelBuffer & cb,int channelIndex)186 void 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 
processSamples(const float * in,float * out,size_t samples)376 size_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 
processChannelBuffers(CBufferVector & channelBuffers)428 size_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 }
processFirstStages(ChannelBuffer & cb)480 size_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 
processLinkedLimiters(CBufferVector & channelBuffers)630 void 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 
processLastStages(ChannelBuffer & cb)650 size_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