| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 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 |  | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 32 | static constexpr float MIN_ENVELOPE = 1e-6f; //-120 dB | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 33 | //helper functionS | 
 | 34 | static inline bool isPowerOf2(unsigned long n) { | 
 | 35 |     return (n & (n - 1)) == 0; | 
 | 36 | } | 
 | 37 | static constexpr float EPSILON = 0.0000001f; | 
 | 38 |  | 
 | 39 | static inline bool isZero(float f) { | 
 | 40 |     return fabs(f) <= EPSILON; | 
 | 41 | } | 
 | 42 |  | 
 | 43 | template <class T> | 
 | 44 | bool compareEquality(T a, T b) { | 
 | 45 |     return (a == b); | 
 | 46 | } | 
 | 47 |  | 
 | 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 |  | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 56 | //ChannelBuffers helper | 
 | 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... | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 69 |     for (unsigned int k = 0; k < mBlockSize/2; k++) { | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 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); | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 89 |     if (pChannel != nullptr) { | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 90 |         mPreEqInUse = pChannel->getPreEq()->isInUse(); | 
 | 91 |         mMbcInUse = pChannel->getMbc()->isInUse(); | 
 | 92 |         mPostEqInUse = pChannel->getPostEq()->isInUse(); | 
 | 93 |         mLimiterInUse = pChannel->getLimiter()->isInUse(); | 
 | 94 |     } | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 95 |  | 
 | 96 |     mLimiterParams.linkGroup = -1; //no group. | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 97 | } | 
 | 98 |  | 
 | 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 |  | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 105 | //== LinkedLimiters Helper | 
 | 106 | void LinkedLimiters::reset() { | 
 | 107 |     mGroupsMap.clear(); | 
 | 108 | } | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 109 |  | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 110 | void LinkedLimiters::update(int32_t group, int index) { | 
 | 111 |     mGroupsMap[group].push_back(index); | 
 | 112 | } | 
 | 113 |  | 
 | 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 | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 134 | void DPFrequency::reset() { | 
 | 135 | } | 
 | 136 |  | 
 | 137 | size_t DPFrequency::getMinBockSize() { | 
 | 138 |     return MIN_BLOCKSIZE; | 
 | 139 | } | 
 | 140 |  | 
 | 141 | size_t DPFrequency::getMaxBockSize() { | 
 | 142 |     return MAX_BLOCKSIZE; | 
 | 143 | } | 
 | 144 |  | 
 | 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 |  | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 171 |     //effective number of frames processed per second | 
 | 172 |     mBlocksPerSecond = (float)mSamplingRate / (mBlockSize - mOverlapSize); | 
 | 173 |  | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 174 |     fill_window(mVWindow, RDSP_WINDOW_HANNING_FLAT_TOP, mBlockSize, mOverlapSize); | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 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); | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 184 | } | 
 | 185 |  | 
 | 186 | void DPFrequency::updateParameters(ChannelBuffer &cb, int channelIndex) { | 
 | 187 |     DPChannel *pChannel = getChannel(channelIndex); | 
 | 188 |  | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 189 |     if (pChannel == nullptr) { | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 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(); | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 201 |             if (pPreEq == nullptr) { | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 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); | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 209 |                     if (pEqBand == nullptr) { | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 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(); | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 257 |         if (pPostEq == nullptr) { | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 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); | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 265 |                 if (pEqBand == nullptr) { | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 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(); | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 300 |         if (pMbc == nullptr) { | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 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); | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 309 |                 if (pMbcBand == nullptr) { | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 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 |                 } | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 342 |             } | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 343 |         } | 
 | 344 |     } | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 345 |  | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 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); | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 369 |         } | 
 | 370 |     } | 
| rago | d24e983 | 2018-05-18 17:20:16 -0700 | [diff] [blame] | 371 |  | 
 | 372 |     //=== Output Gain | 
 | 373 |     cb.outputGainDb = pChannel->getOutputGain(); | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 374 | } | 
 | 375 |  | 
 | 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 |  | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 398 |        //**process all channelBuffers | 
 | 399 |        processChannelBuffers(mChannelBuffers); | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 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 |  | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 428 | size_t DPFrequency::processChannelBuffers(CBufferVector &channelBuffers) { | 
 | 429 |     const int channelCount = channelBuffers.size(); | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 430 |     size_t processedSamples = 0; | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 431 |     size_t processFrames = mBlockSize - mOverlapSize; | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 432 |  | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 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()); | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 436 |     } | 
 | 437 |  | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 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 |     } | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 478 |     return processedSamples; | 
 | 479 | } | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 480 | size_t DPFrequency::processFirstStages(ChannelBuffer &cb) { | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 481 |  | 
 | 482 |     //##apply window | 
 | 483 |     Eigen::Map<Eigen::VectorXf> eWindow(&mVWindow[0], mVWindow.size()); | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 484 |     Eigen::Map<Eigen::VectorXf> eInput(&cb.input[0], cb.input.size()); | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 485 |  | 
 | 486 |     Eigen::VectorXf eWin = eInput.cwiseProduct(eWindow); //apply window | 
 | 487 |  | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 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); | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 493 |  | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 494 |     size_t cSize = cb.complexTemp.size(); | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 495 |     size_t maxBin = std::min(cSize/2, mHalfFFTSize); | 
 | 496 |  | 
 | 497 |     //== EqPre (always runs) | 
 | 498 |     for (size_t k = 0; k < maxBin; k++) { | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 499 |         cb.complexTemp[k] *= cb.mPreEqFactorVector[k]; | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 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++) { | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 513 |                 fEnergySum += std::norm(cb.complexTemp[k]) * preGainSquared; //mag squared | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 514 |             } | 
 | 515 |  | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 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. | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 526 |             float fTheta = 0.0; | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 527 |             float fFAttSec = pMbcBandParams->attackTimeMs / 1000; //in seconds | 
 | 528 |             float fFRelSec = pMbcBandParams->releaseTimeMs / 1000; //in seconds | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 529 |  | 
 | 530 |             if (fEnergySum > pMbcBandParams->previousEnvelope) { | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 531 |                 fTheta = exp(-1.0 / (fFAttSec * mBlocksPerSecond)); | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 532 |             } else { | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 533 |                 fTheta = exp(-1.0 / (fFRelSec * mBlocksPerSecond)); | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 534 |             } | 
 | 535 |  | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 536 |             float fEnv = (1.0 - fTheta) * fEnergySum + fTheta * pMbcBandParams->previousEnvelope; | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 537 |             //preserve for next iteration | 
 | 538 |             pMbcBandParams->previousEnvelope = fEnv; | 
 | 539 |  | 
| rago | ce5f342 | 2018-05-23 16:07:48 -0700 | [diff] [blame] | 540 |             if (fEnv < MIN_ENVELOPE) { | 
 | 541 |                 fEnv = MIN_ENVELOPE; | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 542 |             } | 
| rago | ce5f342 | 2018-05-23 16:07:48 -0700 | [diff] [blame] | 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); | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 568 |  | 
 | 569 |             //apply post gain. | 
| rago | ce5f342 | 2018-05-23 16:07:48 -0700 | [diff] [blame] | 570 |             newFactor *= dBtoLinear(pMbcBandParams->gainPostDb); | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 571 |  | 
 | 572 |             //apply to this band | 
 | 573 |             for (size_t k = pMbcBandParams->binStart; k <= pMbcBandParams->binStop; k++) { | 
| rago | ce5f342 | 2018-05-23 16:07:48 -0700 | [diff] [blame] | 574 |                 cb.complexTemp[k] *= newFactor; | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 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++) { | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 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 |  | 
| rago | ce5f342 | 2018-05-23 16:07:48 -0700 | [diff] [blame] | 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; | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 616 |  | 
| rago | ce5f342 | 2018-05-23 16:07:48 -0700 | [diff] [blame] | 617 |         if (envDb > thresholdDb) { | 
 | 618 |             //limiter segment | 
 | 619 |             newFactorDb = ((1 / ratio) - 1) * (envDb - thresholdDb); | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 620 |         } | 
 | 621 |  | 
| rago | ce5f342 | 2018-05-23 16:07:48 -0700 | [diff] [blame] | 622 |         float newFactor = dBtoLinear(newFactorDb); | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 623 |  | 
| rago | ce5f342 | 2018-05-23 16:07:48 -0700 | [diff] [blame] | 624 |         cb.mLimiterParams.newFactor = newFactor; | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 625 |  | 
 | 626 |     } //end Limiter | 
 | 627 |     return mBlockSize; | 
 | 628 | } | 
 | 629 |  | 
 | 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 |  | 
 | 650 | size_t DPFrequency::processLastStages(ChannelBuffer &cb) { | 
| rago | d24e983 | 2018-05-18 17:20:16 -0700 | [diff] [blame] | 651 |  | 
 | 652 |     float outputGainFactor = dBtoLinear(cb.outputGainDb); | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 653 |     //== Limiter. last Pass | 
 | 654 |     if (cb.mLimiterInUse && cb.mLimiterEnabled) { | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 655 |         //compute factor, with post-gain | 
 | 656 |         float factor = cb.mLimiterParams.linkFactor * dBtoLinear(cb.mLimiterParams.postGainDb); | 
| rago | d24e983 | 2018-05-18 17:20:16 -0700 | [diff] [blame] | 657 |         outputGainFactor *= factor; | 
 | 658 |     } | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 659 |  | 
| rago | d24e983 | 2018-05-18 17:20:16 -0700 | [diff] [blame] | 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); | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 664 |         for (size_t k = 0; k < maxBin; k++) { | 
| rago | d24e983 | 2018-05-18 17:20:16 -0700 | [diff] [blame] | 665 |             cb.complexTemp[k] *= outputGainFactor; | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 666 |         } | 
 | 667 |     } | 
 | 668 |  | 
 | 669 |     //##ifft directly to output. | 
| rago | d55d0f3 | 2018-05-15 18:57:31 -0700 | [diff] [blame] | 670 |     Eigen::Map<Eigen::VectorXf> eOutput(&cb.output[0], cb.output.size()); | 
 | 671 |     mFftServer.inv(eOutput, cb.complexTemp); | 
| rago | ff0a51f | 2018-03-22 09:55:50 -0700 | [diff] [blame] | 672 |     return mBlockSize; | 
 | 673 | } | 
 | 674 |  | 
 | 675 | } //namespace dp_fx |