blob: d06fd700e762c060bb23198763a002edffcfe08e [file] [log] [blame]
ragoff0a51f2018-03-22 09:55:50 -07001/*
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
ragod55d0f32018-05-15 18:57:31 -070032static constexpr float MIN_ENVELOPE = 1e-6f; //-120 dB
ragoff0a51f2018-03-22 09:55:50 -070033//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
ragoff0a51f2018-03-22 09:55:50 -070056//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...
ragod55d0f32018-05-15 18:57:31 -070069 for (unsigned int k = 0; k < mBlockSize/2; k++) {
ragoff0a51f2018-03-22 09:55:50 -070070 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);
ragod55d0f32018-05-15 18:57:31 -070089 if (pChannel != nullptr) {
ragoff0a51f2018-03-22 09:55:50 -070090 mPreEqInUse = pChannel->getPreEq()->isInUse();
91 mMbcInUse = pChannel->getMbc()->isInUse();
92 mPostEqInUse = pChannel->getPostEq()->isInUse();
93 mLimiterInUse = pChannel->getLimiter()->isInUse();
94 }
ragod55d0f32018-05-15 18:57:31 -070095
96 mLimiterParams.linkGroup = -1; //no group.
ragoff0a51f2018-03-22 09:55:50 -070097}
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
ragod55d0f32018-05-15 18:57:31 -0700105//== LinkedLimiters Helper
106void LinkedLimiters::reset() {
107 mGroupsMap.clear();
108}
ragoff0a51f2018-03-22 09:55:50 -0700109
ragod55d0f32018-05-15 18:57:31 -0700110void 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
ragoff0a51f2018-03-22 09:55:50 -0700134void 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
ragod55d0f32018-05-15 18:57:31 -0700171 //effective number of frames processed per second
172 mBlocksPerSecond = (float)mSamplingRate / (mBlockSize - mOverlapSize);
173
ragoff0a51f2018-03-22 09:55:50 -0700174 fill_window(mVWindow, RDSP_WINDOW_HANNING_FLAT_TOP, mBlockSize, mOverlapSize);
ragod55d0f32018-05-15 18:57:31 -0700175
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);
ragoff0a51f2018-03-22 09:55:50 -0700184}
185
186void DPFrequency::updateParameters(ChannelBuffer &cb, int channelIndex) {
187 DPChannel *pChannel = getChannel(channelIndex);
188
ragod55d0f32018-05-15 18:57:31 -0700189 if (pChannel == nullptr) {
ragoff0a51f2018-03-22 09:55:50 -0700190 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();
ragod55d0f32018-05-15 18:57:31 -0700201 if (pPreEq == nullptr) {
ragoff0a51f2018-03-22 09:55:50 -0700202 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);
ragod55d0f32018-05-15 18:57:31 -0700209 if (pEqBand == nullptr) {
ragoff0a51f2018-03-22 09:55:50 -0700210 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();
ragod55d0f32018-05-15 18:57:31 -0700257 if (pPostEq == nullptr) {
ragoff0a51f2018-03-22 09:55:50 -0700258 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);
ragod55d0f32018-05-15 18:57:31 -0700265 if (pEqBand == nullptr) {
ragoff0a51f2018-03-22 09:55:50 -0700266 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();
ragod55d0f32018-05-15 18:57:31 -0700300 if (pMbc == nullptr) {
ragoff0a51f2018-03-22 09:55:50 -0700301 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);
ragod55d0f32018-05-15 18:57:31 -0700309 if (pMbcBand == nullptr) {
ragoff0a51f2018-03-22 09:55:50 -0700310 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 }
ragoff0a51f2018-03-22 09:55:50 -0700342 }
ragod55d0f32018-05-15 18:57:31 -0700343 }
344 }
ragoff0a51f2018-03-22 09:55:50 -0700345
ragod55d0f32018-05-15 18:57:31 -0700346 //===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);
ragoff0a51f2018-03-22 09:55:50 -0700369 }
370 }
ragod24e9832018-05-18 17:20:16 -0700371
372 //=== Output Gain
373 cb.outputGainDb = pChannel->getOutputGain();
ragoff0a51f2018-03-22 09:55:50 -0700374}
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
ragod55d0f32018-05-15 18:57:31 -0700398 //**process all channelBuffers
399 processChannelBuffers(mChannelBuffers);
ragoff0a51f2018-03-22 09:55:50 -0700400
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
ragod55d0f32018-05-15 18:57:31 -0700428size_t DPFrequency::processChannelBuffers(CBufferVector &channelBuffers) {
429 const int channelCount = channelBuffers.size();
ragoff0a51f2018-03-22 09:55:50 -0700430 size_t processedSamples = 0;
ragod55d0f32018-05-15 18:57:31 -0700431 size_t processFrames = mBlockSize - mOverlapSize;
ragoff0a51f2018-03-22 09:55:50 -0700432
ragod55d0f32018-05-15 18:57:31 -0700433 size_t available = channelBuffers[0].cBInput.availableToRead();
434 for (int ch = 1; ch < channelCount; ch++) {
435 available = std::min(available, channelBuffers[ch].cBInput.availableToRead());
ragoff0a51f2018-03-22 09:55:50 -0700436 }
437
ragod55d0f32018-05-15 18:57:31 -0700438 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 }
ragoff0a51f2018-03-22 09:55:50 -0700478 return processedSamples;
479}
ragod55d0f32018-05-15 18:57:31 -0700480size_t DPFrequency::processFirstStages(ChannelBuffer &cb) {
ragoff0a51f2018-03-22 09:55:50 -0700481
482 //##apply window
483 Eigen::Map<Eigen::VectorXf> eWindow(&mVWindow[0], mVWindow.size());
ragod55d0f32018-05-15 18:57:31 -0700484 Eigen::Map<Eigen::VectorXf> eInput(&cb.input[0], cb.input.size());
ragoff0a51f2018-03-22 09:55:50 -0700485
486 Eigen::VectorXf eWin = eInput.cwiseProduct(eWindow); //apply window
487
ragod55d0f32018-05-15 18:57:31 -0700488 //##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);
ragoff0a51f2018-03-22 09:55:50 -0700493
ragod55d0f32018-05-15 18:57:31 -0700494 size_t cSize = cb.complexTemp.size();
ragoff0a51f2018-03-22 09:55:50 -0700495 size_t maxBin = std::min(cSize/2, mHalfFFTSize);
496
497 //== EqPre (always runs)
498 for (size_t k = 0; k < maxBin; k++) {
ragod55d0f32018-05-15 18:57:31 -0700499 cb.complexTemp[k] *= cb.mPreEqFactorVector[k];
ragoff0a51f2018-03-22 09:55:50 -0700500 }
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++) {
ragod55d0f32018-05-15 18:57:31 -0700513 fEnergySum += std::norm(cb.complexTemp[k]) * preGainSquared; //mag squared
ragoff0a51f2018-03-22 09:55:50 -0700514 }
515
ragod55d0f32018-05-15 18:57:31 -0700516 //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.
ragoff0a51f2018-03-22 09:55:50 -0700526 float fTheta = 0.0;
ragod55d0f32018-05-15 18:57:31 -0700527 float fFAttSec = pMbcBandParams->attackTimeMs / 1000; //in seconds
528 float fFRelSec = pMbcBandParams->releaseTimeMs / 1000; //in seconds
ragoff0a51f2018-03-22 09:55:50 -0700529
530 if (fEnergySum > pMbcBandParams->previousEnvelope) {
ragod55d0f32018-05-15 18:57:31 -0700531 fTheta = exp(-1.0 / (fFAttSec * mBlocksPerSecond));
ragoff0a51f2018-03-22 09:55:50 -0700532 } else {
ragod55d0f32018-05-15 18:57:31 -0700533 fTheta = exp(-1.0 / (fFRelSec * mBlocksPerSecond));
ragoff0a51f2018-03-22 09:55:50 -0700534 }
535
ragod55d0f32018-05-15 18:57:31 -0700536 float fEnv = (1.0 - fTheta) * fEnergySum + fTheta * pMbcBandParams->previousEnvelope;
ragoff0a51f2018-03-22 09:55:50 -0700537 //preserve for next iteration
538 pMbcBandParams->previousEnvelope = fEnv;
539
ragoce5f3422018-05-23 16:07:48 -0700540 if (fEnv < MIN_ENVELOPE) {
541 fEnv = MIN_ENVELOPE;
ragoff0a51f2018-03-22 09:55:50 -0700542 }
ragoce5f3422018-05-23 16:07:48 -0700543 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);
ragoff0a51f2018-03-22 09:55:50 -0700568
569 //apply post gain.
ragoce5f3422018-05-23 16:07:48 -0700570 newFactor *= dBtoLinear(pMbcBandParams->gainPostDb);
ragoff0a51f2018-03-22 09:55:50 -0700571
572 //apply to this band
573 for (size_t k = pMbcBandParams->binStart; k <= pMbcBandParams->binStop; k++) {
ragoce5f3422018-05-23 16:07:48 -0700574 cb.complexTemp[k] *= newFactor;
ragoff0a51f2018-03-22 09:55:50 -0700575 }
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++) {
ragod55d0f32018-05-15 18:57:31 -0700584 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
ragoce5f3422018-05-23 16:07:48 -0700611 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;
ragod55d0f32018-05-15 18:57:31 -0700616
ragoce5f3422018-05-23 16:07:48 -0700617 if (envDb > thresholdDb) {
618 //limiter segment
619 newFactorDb = ((1 / ratio) - 1) * (envDb - thresholdDb);
ragod55d0f32018-05-15 18:57:31 -0700620 }
621
ragoce5f3422018-05-23 16:07:48 -0700622 float newFactor = dBtoLinear(newFactorDb);
ragod55d0f32018-05-15 18:57:31 -0700623
ragoce5f3422018-05-23 16:07:48 -0700624 cb.mLimiterParams.newFactor = newFactor;
ragod55d0f32018-05-15 18:57:31 -0700625
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) {
ragod24e9832018-05-18 17:20:16 -0700651
652 float outputGainFactor = dBtoLinear(cb.outputGainDb);
ragod55d0f32018-05-15 18:57:31 -0700653 //== Limiter. last Pass
654 if (cb.mLimiterInUse && cb.mLimiterEnabled) {
ragod55d0f32018-05-15 18:57:31 -0700655 //compute factor, with post-gain
656 float factor = cb.mLimiterParams.linkFactor * dBtoLinear(cb.mLimiterParams.postGainDb);
ragod24e9832018-05-18 17:20:16 -0700657 outputGainFactor *= factor;
658 }
ragod55d0f32018-05-15 18:57:31 -0700659
ragod24e9832018-05-18 17:20:16 -0700660 //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);
ragod55d0f32018-05-15 18:57:31 -0700664 for (size_t k = 0; k < maxBin; k++) {
ragod24e9832018-05-18 17:20:16 -0700665 cb.complexTemp[k] *= outputGainFactor;
ragoff0a51f2018-03-22 09:55:50 -0700666 }
667 }
668
669 //##ifft directly to output.
ragod55d0f32018-05-15 18:57:31 -0700670 Eigen::Map<Eigen::VectorXf> eOutput(&cb.output[0], cb.output.size());
671 mFftServer.inv(eOutput, cb.complexTemp);
ragoff0a51f2018-03-22 09:55:50 -0700672 return mBlockSize;
673}
674
675} //namespace dp_fx