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