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