Frequency Domain implementation of Dynamics Processing Effect

Ported the frequency domain implementation of the effect from the
development sandbox.

Bug: 64161702
Bug: 38266419

Test: manual with Triton app. Cts tests. Listening tests.
Change-Id: I9417beba2f98f2a677f0857c7976bf76a0e0d8e0
diff --git a/media/libeffects/dynamicsproc/Android.mk b/media/libeffects/dynamicsproc/Android.mk
index a27a2e7..7be0c49 100644
--- a/media/libeffects/dynamicsproc/Android.mk
+++ b/media/libeffects/dynamicsproc/Android.mk
@@ -18,9 +18,14 @@
 include $(CLEAR_VARS)
 
 LOCAL_VENDOR_MODULE := true
+
+EIGEN_PATH := external/eigen
+LOCAL_C_INCLUDES += $(EIGEN_PATH)
+
 LOCAL_SRC_FILES:= \
     EffectDynamicsProcessing.cpp \
-    dsp/DPBase.cpp
+    dsp/DPBase.cpp \
+    dsp/DPFrequency.cpp
 
 LOCAL_CFLAGS+= -O2 -fvisibility=hidden
 LOCAL_CFLAGS += -Wall -Werror
diff --git a/media/libeffects/dynamicsproc/EffectDynamicsProcessing.cpp b/media/libeffects/dynamicsproc/EffectDynamicsProcessing.cpp
index 56cd247..55383eb 100644
--- a/media/libeffects/dynamicsproc/EffectDynamicsProcessing.cpp
+++ b/media/libeffects/dynamicsproc/EffectDynamicsProcessing.cpp
@@ -28,6 +28,7 @@
 
 #include <audio_effects/effect_dynamicsprocessing.h>
 #include <dsp/DPBase.h>
+#include <dsp/DPFrequency.h>
 
 //#define VERY_VERY_VERBOSE_LOGGING
 #ifdef VERY_VERY_VERBOSE_LOGGING
@@ -186,7 +187,7 @@
     pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
     pContext->mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
     pContext->mConfig.inputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
-    pContext->mConfig.inputCfg.samplingRate = 44100;
+    pContext->mConfig.inputCfg.samplingRate = 48000;
     pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL;
     pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL;
     pContext->mConfig.inputCfg.bufferProvider.cookie = NULL;
@@ -194,7 +195,7 @@
     pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
     pContext->mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
     pContext->mConfig.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
-    pContext->mConfig.outputCfg.samplingRate = 44100;
+    pContext->mConfig.outputCfg.samplingRate = 48000;
     pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL;
     pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
     pContext->mConfig.outputCfg.bufferProvider.cookie = NULL;
@@ -209,17 +210,52 @@
 }
 
 void DP_changeVariant(DynamicsProcessingContext *pContext, int newVariant) {
-    if (pContext->mPDynamics != NULL) {
-        delete pContext->mPDynamics;
-        pContext->mPDynamics = NULL;
-    }
+    ALOGV("DP_changeVariant from %d to %d", pContext->mCurrentVariant, newVariant);
     switch(newVariant) {
-    //TODO: actually instantiate one of the variants. For now all instantiate the base;
-    default:
-        pContext->mCurrentVariant = newVariant;
-        pContext->mPDynamics = new dp_fx::DPBase();
+    case VARIANT_FAVOR_FREQUENCY_RESOLUTION: {
+        pContext->mCurrentVariant = VARIANT_FAVOR_FREQUENCY_RESOLUTION;
+        delete pContext->mPDynamics;
+        pContext->mPDynamics = new dp_fx::DPFrequency();
         break;
     }
+    default: {
+        ALOGW("DynamicsProcessing variant %d not available for creation", newVariant);
+        break;
+    }
+    } //switch
+}
+
+static inline bool isPowerOf2(unsigned long n) {
+    return (n & (n - 1)) == 0;
+}
+
+void DP_configureVariant(DynamicsProcessingContext *pContext, int newVariant) {
+    ALOGV("DP_configureVariant %d", newVariant);
+    switch(newVariant) {
+    case VARIANT_FAVOR_FREQUENCY_RESOLUTION: {
+        int32_t minBlockSize = (int32_t)dp_fx::DPFrequency::getMinBockSize();
+        int32_t desiredBlock = pContext->mPreferredFrameDuration *
+                pContext->mConfig.inputCfg.samplingRate / 1000.0f;
+        int32_t currentBlock = desiredBlock;
+        ALOGV(" sampling rate: %d, desiredBlock size %0.2f (%d) samples",
+                pContext->mConfig.inputCfg.samplingRate, pContext->mPreferredFrameDuration,
+                desiredBlock);
+        if (desiredBlock < minBlockSize) {
+            currentBlock = minBlockSize;
+        } else if (!isPowerOf2(desiredBlock)) {
+            //find next highest power of 2.
+            currentBlock = 1 << (32 - __builtin_clz(desiredBlock));
+        }
+        ((dp_fx::DPFrequency*)pContext->mPDynamics)->configure(currentBlock,
+                currentBlock/2,
+                pContext->mConfig.inputCfg.samplingRate);
+        break;
+    }
+    default: {
+        ALOGE("DynamicsProcessing variant %d not available to configure", newVariant);
+        break;
+    }
+    }
 }
 
 //
@@ -312,6 +348,7 @@
                         pContext->mConfig.inputCfg.channels);
         pContext->mPDynamics->processSamples(inBuffer->f32, inBuffer->f32,
                 inBuffer->frameCount * channelCount);
+
         if (inBuffer->raw != outBuffer->raw) {
             if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
                 for (size_t i = 0; i < outBuffer->frameCount * channelCount; i++) {
@@ -518,7 +555,8 @@
     if (pChannel == NULL) {
         return NULL;
     }
-    dp_fx::DPEq *pEq = eqType == DP_PARAM_PRE_EQ ? pChannel->getPreEq() : pChannel->getPostEq();
+    dp_fx::DPEq *pEq = (eqType == DP_PARAM_PRE_EQ ? pChannel->getPreEq() :
+            (eqType == DP_PARAM_POST_EQ ? pChannel->getPostEq() : NULL));
     ALOGE_IF(pEq == NULL,"DPEq NULL invalid eq");
     return pEq;
 }
@@ -699,8 +737,10 @@
 //              eqBand.getGain()};
         const int32_t channel = params[1];
         const int32_t band = params[2];
+        int eqCommand = (command == DP_PARAM_PRE_EQ_BAND ? DP_PARAM_PRE_EQ :
+                (command == DP_PARAM_POST_EQ_BAND ? DP_PARAM_POST_EQ : -1));
 
-        dp_fx::DPEqBand *pEqBand = DP_getEqBand(pContext, channel, command, band);
+        dp_fx::DPEqBand *pEqBand = DP_getEqBand(pContext, channel, eqCommand, band);
         if (pEqBand == NULL) {
             ALOGE("%s get PARAM_*_EQ_BAND invalid channel %d or band %d", __func__, channel, band);
             status = -EINVAL;
@@ -923,6 +963,8 @@
                 mbcInUse != 0, (uint32_t)mbcBandCount,
                 postEqInUse != 0, (uint32_t)postEqBandCount,
                 limiterInUse != 0);
+
+        DP_configureVariant(pContext, variant);
         break;
     }
     case DP_PARAM_INPUT_GAIN: {
@@ -1015,7 +1057,9 @@
                 (command == DP_PARAM_PRE_EQ_BAND ? "preEqBand" : "postEqBand"), channel, band,
                 enabled, cutoffFrequency, gain);
 
-        dp_fx::DPEq *pEq = DP_getEq(pContext, channel, command);
+        int eqCommand = (command == DP_PARAM_PRE_EQ_BAND ? DP_PARAM_PRE_EQ :
+                (command == DP_PARAM_POST_EQ_BAND ? DP_PARAM_POST_EQ : -1));
+        dp_fx::DPEq *pEq = DP_getEq(pContext, channel, eqCommand);
         if (pEq == NULL) {
             ALOGE("%s set PARAM_*_EQ_BAND invalid channel %d or command %d", __func__, channel,
                     command);
diff --git a/media/libeffects/dynamicsproc/dsp/DPBase.cpp b/media/libeffects/dynamicsproc/dsp/DPBase.cpp
index 30c2c36..8b79991 100644
--- a/media/libeffects/dynamicsproc/dsp/DPBase.cpp
+++ b/media/libeffects/dynamicsproc/dsp/DPBase.cpp
@@ -14,8 +14,12 @@
  * limitations under the License.
  */
 
-#include <android/log.h>
+#define LOG_TAG "DPBase"
+//#define LOG_NDEBUG 0
+
+#include <log/log.h>
 #include "DPBase.h"
+#include "DPFrequency.h"
 
 namespace dp_fx {
 
@@ -234,6 +238,7 @@
 void DPBase::init(uint32_t channelCount, bool preEqInUse, uint32_t preEqBandCount,
         bool mbcInUse, uint32_t mbcBandCount, bool postEqInUse, uint32_t postEqBandCount,
         bool limiterInUse) {
+    ALOGV("DPBase::init");
     mChannelCount = channelCount;
     mPreEqInUse = preEqInUse;
     mPreEqBandCount = preEqBandCount;
@@ -250,31 +255,6 @@
     mInitialized = true;
 }
 
-void DPBase::reset() {
-    //perform reset operations with current architecture.
-}
-
-size_t DPBase::processSamples(float *in, float *out, size_t samples) {
-    //actually do something with samples, for now, just apply level.
-    uint32_t channelCount = getChannelCount();
-    std::vector<float> level(channelCount);
-    for (uint32_t ch = 0; ch < channelCount; ch++) {
-        DPChannel *pChannel = getChannel(ch);
-        if (pChannel != NULL) {
-            level[ch] = pow(10, pChannel->getInputGain() / 20.0);
-        }
-    }
-    size_t processedSamples = 0;
-    float *pInput = in;
-    float *pOutput = out;
-    for (size_t k = 0; k < samples; k++) {
-            float value = *pInput++;
-            *pOutput++ = value * level[k % channelCount];
-            processedSamples++;
-    }
-    return processedSamples;
-}
-
 DPChannel* DPBase::getChannel(uint32_t channelIndex) {
     if (!mInitialized || channelIndex < 0 || channelIndex >= mChannel.size()) {
         return NULL;
diff --git a/media/libeffects/dynamicsproc/dsp/DPBase.h b/media/libeffects/dynamicsproc/dsp/DPBase.h
index 52593ef..355f64b 100644
--- a/media/libeffects/dynamicsproc/dsp/DPBase.h
+++ b/media/libeffects/dynamicsproc/dsp/DPBase.h
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#ifndef DPBASE_H_
+#define DPBASE_H_
+
+
 #include <stdint.h>
 #include <cmath>
 #include <vector>
@@ -297,8 +301,8 @@
     void init(uint32_t channelCount, bool preEqInUse, uint32_t preEqBandCount,
             bool mbcInUse, uint32_t mbcBandCount, bool postEqInUse, uint32_t postEqBandCount,
             bool limiterInUse);
-    virtual size_t processSamples(float *in, float *out, size_t samples);
-    virtual void reset();
+    virtual size_t processSamples(const float *in, float *out, size_t samples) = 0;
+    virtual void reset() = 0;
 
     DPChannel* getChannel(uint32_t channelIndex);
     uint32_t getChannelCount() const {
@@ -342,3 +346,6 @@
 };
 
 } //namespace dp_fx
+
+
+#endif  // DPBASE_H_
diff --git a/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp b/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp
new file mode 100644
index 0000000..59195fc
--- /dev/null
+++ b/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DPFrequency"
+//#define LOG_NDEBUG 0
+
+#include <log/log.h>
+#include "DPFrequency.h"
+#include <algorithm>
+
+namespace dp_fx {
+
+using Eigen::MatrixXd;
+#define MAX_BLOCKSIZE 16384 //For this implementation
+#define MIN_BLOCKSIZE 8
+
+#define CIRCULAR_BUFFER_UPSAMPLE 4  //4 times buffer size
+
+static constexpr float MIN_ENVELOPE = 0.000001f;
+//helper functionS
+static inline bool isPowerOf2(unsigned long n) {
+    return (n & (n - 1)) == 0;
+}
+static constexpr float EPSILON = 0.0000001f;
+
+static inline bool isZero(float f) {
+    return fabs(f) <= EPSILON;
+}
+
+template <class T>
+bool compareEquality(T a, T b) {
+    return (a == b);
+}
+
+template <> bool compareEquality<float>(float a, float b) {
+    return isZero(a - b);
+}
+
+//TODO: avoid using macro for estimating change and assignment.
+#define IS_CHANGED(c, a, b) { c |= !compareEquality(a,b); \
+    (a) = (b); }
+
+float dBtoLinear(float valueDb) {
+    return  pow (10, valueDb / 20.0);
+}
+
+float linearToDb(float value) {
+    return 20 * log10(value);
+}
+
+//ChannelBuffers helper
+void ChannelBuffer::initBuffers(unsigned int blockSize, unsigned int overlapSize,
+        unsigned int halfFftSize, unsigned int samplingRate, DPBase &dpBase) {
+    ALOGV("ChannelBuffer::initBuffers blockSize %d, overlap %d, halfFft %d",
+            blockSize, overlapSize, halfFftSize);
+
+    mSamplingRate = samplingRate;
+    mBlockSize = blockSize;
+
+    cBInput.resize(mBlockSize * CIRCULAR_BUFFER_UPSAMPLE);
+    cBOutput.resize(mBlockSize * CIRCULAR_BUFFER_UPSAMPLE);
+
+    //fill input with half block size...
+    for (unsigned int k = 0;  k < mBlockSize/2; k++) {
+        cBInput.write(0);
+    }
+
+    //temp vectors
+    input.resize(mBlockSize);
+    output.resize(mBlockSize);
+    outTail.resize(overlapSize);
+
+    //module vectors
+    mPreEqFactorVector.resize(halfFftSize, 1.0);
+    mPostEqFactorVector.resize(halfFftSize, 1.0);
+
+    mPreEqBands.resize(dpBase.getPreEqBandCount());
+    mMbcBands.resize(dpBase.getMbcBandCount());
+    mPostEqBands.resize(dpBase.getPostEqBandCount());
+    ALOGV("mPreEqBands %zu, mMbcBands %zu, mPostEqBands %zu",mPreEqBands.size(),
+            mMbcBands.size(), mPostEqBands.size());
+
+    DPChannel *pChannel = dpBase.getChannel(0);
+    if (pChannel != NULL) {
+        mPreEqInUse = pChannel->getPreEq()->isInUse();
+        mMbcInUse = pChannel->getMbc()->isInUse();
+        mPostEqInUse = pChannel->getPostEq()->isInUse();
+        mLimiterInUse = pChannel->getLimiter()->isInUse();
+    }
+}
+
+void ChannelBuffer::computeBinStartStop(BandParams &bp, size_t binStart) {
+
+    bp.binStart = binStart;
+    bp.binStop = (int)(0.5 + bp.freqCutoffHz * mBlockSize / mSamplingRate);
+}
+
+//== DPFrequency
+
+void DPFrequency::reset() {
+}
+
+size_t DPFrequency::getMinBockSize() {
+    return MIN_BLOCKSIZE;
+}
+
+size_t DPFrequency::getMaxBockSize() {
+    return MAX_BLOCKSIZE;
+}
+
+void DPFrequency::configure(size_t blockSize, size_t overlapSize,
+        size_t samplingRate) {
+    ALOGV("configure");
+    mBlockSize = blockSize;
+    if (mBlockSize > MAX_BLOCKSIZE) {
+        mBlockSize = MAX_BLOCKSIZE;
+    } else if (mBlockSize < MIN_BLOCKSIZE) {
+        mBlockSize = MIN_BLOCKSIZE;
+    } else {
+        if (!isPowerOf2(blockSize)) {
+            //find next highest power of 2.
+            mBlockSize = 1 << (32 - __builtin_clz(blockSize));
+        }
+    }
+
+    mHalfFFTSize = 1 + mBlockSize / 2; //including Nyquist bin
+    mOverlapSize = std::min(overlapSize, mBlockSize/2);
+
+    int channelcount = getChannelCount();
+    mSamplingRate = samplingRate;
+    mChannelBuffers.resize(channelcount);
+    for (int ch = 0; ch < channelcount; ch++) {
+        mChannelBuffers[ch].initBuffers(mBlockSize, mOverlapSize, mHalfFFTSize,
+                mSamplingRate, *this);
+    }
+
+    //dsp
+    fill_window(mVWindow, RDSP_WINDOW_HANNING_FLAT_TOP, mBlockSize, mOverlapSize);
+}
+
+void DPFrequency::updateParameters(ChannelBuffer &cb, int channelIndex) {
+    DPChannel *pChannel = getChannel(channelIndex);
+
+    if (pChannel == NULL) {
+        ALOGE("Error: updateParameters null DPChannel %d", channelIndex);
+        return;
+    }
+
+    //===Input Gain and preEq
+    {
+        bool changed = false;
+        IS_CHANGED(changed, cb.inputGainDb, pChannel->getInputGain());
+        //===EqPre
+        if (cb.mPreEqInUse) {
+            DPEq *pPreEq = pChannel->getPreEq();
+            if (pPreEq == NULL) {
+                ALOGE("Error: updateParameters null PreEq for channel: %d", channelIndex);
+                return;
+            }
+            IS_CHANGED(changed, cb.mPreEqEnabled, pPreEq->isEnabled());
+            if (cb.mPreEqEnabled) {
+                for (unsigned int b = 0; b < getPreEqBandCount(); b++) {
+                    DPEqBand *pEqBand = pPreEq->getBand(b);
+                    if (pEqBand == NULL) {
+                        ALOGE("Error: updateParameters null PreEqBand for band %d", b);
+                        return; //failed.
+                    }
+                    ChannelBuffer::EqBandParams *pEqBandParams = &cb.mPreEqBands[b];
+                    IS_CHANGED(changed, pEqBandParams->enabled, pEqBand->isEnabled());
+                    IS_CHANGED(changed, pEqBandParams->freqCutoffHz,
+                            pEqBand->getCutoffFrequency());
+                    IS_CHANGED(changed, pEqBandParams->gainDb, pEqBand->getGain());
+                }
+            }
+        }
+
+        if (changed) {
+            float inputGainFactor = dBtoLinear(cb.inputGainDb);
+            if (cb.mPreEqInUse && cb.mPreEqEnabled) {
+                ALOGV("preEq changed, recomputing! channel %d", channelIndex);
+                size_t binNext = 0;
+                for (unsigned int b = 0; b < getPreEqBandCount(); b++) {
+                    ChannelBuffer::EqBandParams *pEqBandParams = &cb.mPreEqBands[b];
+
+                    //frequency translation
+                    cb.computeBinStartStop(*pEqBandParams, binNext);
+                    binNext = pEqBandParams->binStop + 1;
+                    float factor = dBtoLinear(pEqBandParams->gainDb);
+                    if (!pEqBandParams->enabled) {
+                        factor = inputGainFactor;
+                    }
+                    for (size_t k = pEqBandParams->binStart;
+                            k <= pEqBandParams->binStop && k < mHalfFFTSize; k++) {
+                        cb.mPreEqFactorVector[k] = factor * inputGainFactor;
+                    }
+                }
+            } else {
+                ALOGV("only input gain changed, recomputing!");
+                //populate PreEq factor with input gain factor.
+                for (size_t k = 0; k < mHalfFFTSize; k++) {
+                    cb.mPreEqFactorVector[k] = inputGainFactor;
+                }
+            }
+        }
+    } //inputGain and preEq
+
+    //===EqPost
+    if (cb.mPostEqInUse) {
+        bool changed = false;
+
+        DPEq *pPostEq = pChannel->getPostEq();
+        if (pPostEq == NULL) {
+            ALOGE("Error: updateParameters null postEq for channel: %d", channelIndex);
+            return; //failed.
+        }
+        IS_CHANGED(changed, cb.mPostEqEnabled, pPostEq->isEnabled());
+        if (cb.mPostEqEnabled) {
+            for (unsigned int b = 0; b < getPostEqBandCount(); b++) {
+                DPEqBand *pEqBand = pPostEq->getBand(b);
+                if (pEqBand == NULL) {
+                    ALOGE("Error: updateParameters PostEqBand NULL for band %d", b);
+                    return; //failed.
+                }
+                ChannelBuffer::EqBandParams *pEqBandParams = &cb.mPostEqBands[b];
+                IS_CHANGED(changed, pEqBandParams->enabled, pEqBand->isEnabled());
+                IS_CHANGED(changed, pEqBandParams->freqCutoffHz,
+                        pEqBand->getCutoffFrequency());
+                IS_CHANGED(changed, pEqBandParams->gainDb, pEqBand->getGain());
+            }
+            if (changed) {
+                ALOGV("postEq changed, recomputing! channel %d", channelIndex);
+                size_t binNext = 0;
+                for (unsigned int b = 0; b < getPostEqBandCount(); b++) {
+                    ChannelBuffer::EqBandParams *pEqBandParams = &cb.mPostEqBands[b];
+
+                    //frequency translation
+                    cb.computeBinStartStop(*pEqBandParams, binNext);
+                    binNext = pEqBandParams->binStop + 1;
+                    float factor = dBtoLinear(pEqBandParams->gainDb);
+                    if (!pEqBandParams->enabled) {
+                        factor = 1.0;
+                    }
+                    for (size_t k = pEqBandParams->binStart;
+                            k <= pEqBandParams->binStop && k < mHalfFFTSize; k++) {
+                        cb.mPostEqFactorVector[k] = factor;
+                    }
+                }
+            }
+        } //enabled
+    }
+
+    //===MBC
+    if (cb.mMbcInUse) {
+        DPMbc *pMbc = pChannel->getMbc();
+        if (pMbc == NULL) {
+            ALOGE("Error: updateParameters Mbc NULL for channel: %d", channelIndex);
+            return;
+        }
+        cb.mMbcEnabled = pMbc->isEnabled();
+        if (cb.mMbcEnabled) {
+            bool changed = false;
+            for (unsigned int b = 0; b < getMbcBandCount(); b++) {
+                DPMbcBand *pMbcBand = pMbc->getBand(b);
+                if (pMbcBand == NULL) {
+                    ALOGE("Error: updateParameters MbcBand NULL for band %d", b);
+                    return; //failed.
+                }
+                ChannelBuffer::MbcBandParams *pMbcBandParams = &cb.mMbcBands[b];
+                pMbcBandParams->enabled = pMbcBand->isEnabled();
+                IS_CHANGED(changed, pMbcBandParams->freqCutoffHz,
+                        pMbcBand->getCutoffFrequency());
+
+                pMbcBandParams->gainPreDb = pMbcBand->getPreGain();
+                pMbcBandParams->gainPostDb = pMbcBand->getPostGain();
+                pMbcBandParams->attackTimeMs = pMbcBand->getAttackTime();
+                pMbcBandParams->releaseTimeMs = pMbcBand->getReleaseTime();
+                pMbcBandParams->ratio = pMbcBand->getRatio();
+                pMbcBandParams->thresholdDb = pMbcBand->getThreshold();
+                pMbcBandParams->kneeWidthDb = pMbcBand->getKneeWidth();
+                pMbcBandParams->noiseGateThresholdDb = pMbcBand->getNoiseGateThreshold();
+                pMbcBandParams->expanderRatio = pMbcBand->getExpanderRatio();
+
+            }
+
+            if (changed) {
+                ALOGV("mbc changed, recomputing! channel %d", channelIndex);
+                size_t binNext= 0;
+                for (unsigned int b = 0; b < getMbcBandCount(); b++) {
+                    ChannelBuffer::MbcBandParams *pMbcBandParams = &cb.mMbcBands[b];
+
+                    pMbcBandParams->previousEnvelope = 0;
+
+                    //frequency translation
+                    cb.computeBinStartStop(*pMbcBandParams, binNext);
+                    binNext = pMbcBandParams->binStop + 1;
+                }
+
+            }
+
+        }
+    }
+}
+
+size_t DPFrequency::processSamples(const float *in, float *out, size_t samples) {
+       const float *pIn = in;
+       float *pOut = out;
+
+       int channelCount = mChannelBuffers.size();
+       if (channelCount < 1) {
+           ALOGW("warning: no Channels ready for processing");
+           return 0;
+       }
+
+       //**Check if parameters have changed and update
+       for (int ch = 0; ch < channelCount; ch++) {
+           updateParameters(mChannelBuffers[ch], ch);
+       }
+
+       //**separate into channels
+       for (size_t k = 0; k < samples; k += channelCount) {
+           for (int ch = 0; ch < channelCount; ch++) {
+               mChannelBuffers[ch].cBInput.write(*pIn++);
+           }
+       }
+
+       //TODO: lookahead limiters
+       //TODO: apply linked limiters to all channels.
+       //**Process each Channel
+       for (int ch = 0; ch < channelCount; ch++) {
+           processMono(mChannelBuffers[ch]);
+       }
+
+       //** estimate how much data is available in ALL channels
+       size_t available = mChannelBuffers[0].cBOutput.availableToRead();
+       for (int ch = 1; ch < channelCount; ch++) {
+           available = std::min(available, mChannelBuffers[ch].cBOutput.availableToRead());
+       }
+
+       //** make sure to output just what the buffer can handle
+       if (available > samples/channelCount) {
+           available = samples/channelCount;
+       }
+
+       //**Prepend zeroes if necessary
+       size_t fill = samples - (channelCount * available);
+       for (size_t k = 0; k < fill; k++) {
+           *pOut++ = 0;
+       }
+
+       //**interleave channels
+       for (size_t k = 0; k < available; k++) {
+           for (int ch = 0; ch < channelCount; ch++) {
+               *pOut++ = mChannelBuffers[ch].cBOutput.read();
+           }
+       }
+
+       return samples;
+}
+
+size_t DPFrequency::processMono(ChannelBuffer &cb) {
+
+    size_t processedSamples = 0;
+
+    size_t available = cb.cBInput.availableToRead();
+    while (available >= mBlockSize - mOverlapSize) {
+
+        //move tail of previous
+        for (unsigned int k = 0; k < mOverlapSize; ++k) {
+            cb.input[k] = cb.input[mBlockSize - mOverlapSize + k];
+        }
+
+        //read new available data
+        for (unsigned int k = 0; k < mBlockSize - mOverlapSize; k++) {
+            cb.input[mOverlapSize + k] = cb.cBInput.read();
+        }
+
+        //## Actual process
+        processOneVector(cb.output, cb.input, cb);
+        //##End of Process
+
+        //mix tail (and capture new tail
+        for (unsigned int k = 0; k < mOverlapSize; k++) {
+            cb.output[k] += cb.outTail[k];
+            cb.outTail[k] = cb.output[mBlockSize - mOverlapSize + k]; //new tail
+        }
+
+        //output data
+        for (unsigned int k = 0; k < mBlockSize - mOverlapSize; k++) {
+            cb.cBOutput.write(cb.output[k]);
+        }
+
+        available = cb.cBInput.availableToRead();
+    }
+
+    return processedSamples;
+}
+
+size_t DPFrequency::processOneVector(FloatVec & output, FloatVec & input,
+        ChannelBuffer &cb) {
+
+    //##apply window
+    Eigen::Map<Eigen::VectorXf> eWindow(&mVWindow[0], mVWindow.size());
+    Eigen::Map<Eigen::VectorXf> eInput(&input[0], input.size());
+
+    Eigen::VectorXf eWin = eInput.cwiseProduct(eWindow); //apply window
+
+    //##fft //TODO: refactor frequency transformations away from other stages.
+    mFftServer.fwd(mComplexTemp, eWin);
+
+    size_t cSize = mComplexTemp.size();
+    size_t maxBin = std::min(cSize/2, mHalfFFTSize);
+
+    //== EqPre (always runs)
+    for (size_t k = 0; k < maxBin; k++) {
+        mComplexTemp[k] *= cb.mPreEqFactorVector[k];
+    }
+
+    //== MBC
+    if (cb.mMbcInUse && cb.mMbcEnabled) {
+        for (size_t band = 0; band < cb.mMbcBands.size(); band++) {
+            ChannelBuffer::MbcBandParams *pMbcBandParams = &cb.mMbcBands[band];
+            float fEnergySum = 0;
+
+            //apply pre gain.
+            float preGainFactor = dBtoLinear(pMbcBandParams->gainPreDb);
+            float preGainSquared = preGainFactor * preGainFactor;
+
+            for (size_t k = pMbcBandParams->binStart; k <= pMbcBandParams->binStop; k++) {
+                float fReal = mComplexTemp[k].real();
+                float fImag = mComplexTemp[k].imag();
+                float fSquare = (fReal * fReal + fImag * fImag) * preGainSquared;
+
+                fEnergySum += fSquare;
+            }
+
+            fEnergySum = sqrt(fEnergySum /2.0);
+            float fTheta = 0.0;
+            float fFAtt = pMbcBandParams->attackTimeMs;
+            float fFRel = pMbcBandParams->releaseTimeMs;
+
+            float fUpdatesPerSecond = 10; //TODO: compute from framerate
+
+
+            if (fEnergySum > pMbcBandParams->previousEnvelope) {
+                fTheta = exp(-1.0 / (fFAtt * fUpdatesPerSecond));
+            } else {
+                fTheta = exp(-1.0 / (fFRel * fUpdatesPerSecond));
+            }
+
+            float fEnv = (1.0 - fTheta) * fEnergySum + fTheta * pMbcBandParams->previousEnvelope;
+
+            //preserve for next iteration
+            pMbcBandParams->previousEnvelope = fEnv;
+
+            float fThreshold = dBtoLinear(pMbcBandParams->thresholdDb);
+            float fNoiseGateThreshold = dBtoLinear(pMbcBandParams->noiseGateThresholdDb);
+
+            float fNewFactor = 1.0;
+
+            if (fEnv > fThreshold) {
+                float fDbAbove = linearToDb(fThreshold / fEnv);
+                float fDbTarget = fDbAbove / pMbcBandParams->ratio;
+                float fDbChange = fDbAbove - fDbTarget;
+                fNewFactor = dBtoLinear(fDbChange);
+            } else if (fEnv < fNoiseGateThreshold) {
+                if (fEnv < MIN_ENVELOPE) {
+                    fEnv = MIN_ENVELOPE;
+                }
+                float fDbBelow = linearToDb(fNoiseGateThreshold / fEnv);
+                float fDbTarget = fDbBelow / pMbcBandParams->expanderRatio;
+                float fDbChange = fDbBelow - fDbTarget;
+                fNewFactor = dBtoLinear(fDbChange);
+            }
+
+            //apply post gain.
+            fNewFactor *= dBtoLinear(pMbcBandParams->gainPostDb);
+
+            if (fNewFactor < 0) {
+                fNewFactor = 0;
+            }
+
+            //apply to this band
+            for (size_t k = pMbcBandParams->binStart; k <= pMbcBandParams->binStop; k++) {
+                mComplexTemp[k] *= fNewFactor;
+            }
+
+        } //end per band process
+
+    } //end MBC
+
+    //== EqPost
+    if (cb.mPostEqInUse && cb.mPostEqEnabled) {
+        for (size_t k = 0; k < maxBin; k++) {
+            mComplexTemp[k] *= cb.mPostEqFactorVector[k];
+        }
+    }
+
+    //##ifft directly to output.
+    Eigen::Map<Eigen::VectorXf> eOutput(&output[0], output.size());
+    mFftServer.inv(eOutput, mComplexTemp);
+
+    return mBlockSize;
+}
+
+} //namespace dp_fx
diff --git a/media/libeffects/dynamicsproc/dsp/DPFrequency.h b/media/libeffects/dynamicsproc/dsp/DPFrequency.h
new file mode 100644
index 0000000..9919142
--- /dev/null
+++ b/media/libeffects/dynamicsproc/dsp/DPFrequency.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef DPFREQUENCY_H_
+#define DPFREQUENCY_H_
+
+#include <Eigen/Dense>
+#include <unsupported/Eigen/FFT>
+
+#include "RDsp.h"
+#include "SHCircularBuffer.h"
+
+#include "DPBase.h"
+
+
+namespace dp_fx {
+
+using FXBuffer = SHCircularBuffer<float>;
+
+class ChannelBuffer {
+public:
+    FXBuffer cBInput;   // Circular Buffer input
+    FXBuffer cBOutput;  // Circular Buffer output
+    FloatVec input;     // time domain temp vector for input
+    FloatVec output;    // time domain temp vector for output
+    FloatVec outTail;   // time domain temp vector for output tail (for overlap-add method)
+
+    //Current parameters
+    float inputGainDb;
+    struct BandParams {
+        bool enabled;
+        float freqCutoffHz;
+        size_t binStart;
+        size_t binStop;
+    };
+    struct EqBandParams : public BandParams {
+        float gainDb;
+    };
+    struct MbcBandParams : public BandParams {
+        float gainPreDb;
+        float gainPostDb;
+        float attackTimeMs;
+        float releaseTimeMs;
+        float ratio;
+        float thresholdDb;
+        float kneeWidthDb;
+        float noiseGateThresholdDb;
+        float expanderRatio;
+
+        //Historic values
+        float previousEnvelope;
+    };
+
+    bool mPreEqInUse;
+    bool mPreEqEnabled;
+    std::vector<EqBandParams> mPreEqBands;
+
+    bool mMbcInUse;
+    bool mMbcEnabled;
+    std::vector<MbcBandParams> mMbcBands;
+
+    bool mPostEqInUse;
+    bool mPostEqEnabled;
+    std::vector<EqBandParams> mPostEqBands;
+
+    bool mLimiterInUse;
+    bool mLimiterEnabled;
+    FloatVec mPreEqFactorVector; // temp pre-computed vector to shape spectrum at preEQ stage
+    FloatVec mPostEqFactorVector; // temp pre-computed vector to shape spectrum at postEQ stage
+
+    void initBuffers(unsigned int blockSize, unsigned int overlapSize, unsigned int halfFftSize,
+            unsigned int samplingRate, DPBase &dpBase);
+    void computeBinStartStop(BandParams &bp, size_t binStart);
+private:
+    unsigned int mSamplingRate;
+    unsigned int mBlockSize;
+
+};
+
+class DPFrequency : public DPBase {
+public:
+    virtual size_t processSamples(const float *in, float *out, size_t samples);
+    virtual void reset();
+    void configure(size_t blockSize, size_t overlapSize, size_t samplingRate);
+    static size_t getMinBockSize();
+    static size_t getMaxBockSize();
+
+private:
+    void updateParameters(ChannelBuffer &cb, int channelIndex);
+    size_t processMono(ChannelBuffer &cb);
+    size_t processOneVector(FloatVec &output, FloatVec &input, ChannelBuffer &cb);
+
+    size_t mBlockSize;
+    size_t mHalfFFTSize;
+    size_t mOverlapSize;
+    size_t mSamplingRate;
+
+    std::vector<ChannelBuffer> mChannelBuffers;
+
+    //dsp
+    FloatVec mVWindow;  //window class.
+    Eigen::VectorXcf mComplexTemp;
+    Eigen::FFT<float> mFftServer;
+};
+
+} //namespace dp_fx
+
+#endif  // DPFREQUENCY_H_
diff --git a/media/libeffects/dynamicsproc/dsp/RDsp.h b/media/libeffects/dynamicsproc/dsp/RDsp.h
new file mode 100644
index 0000000..1048442
--- /dev/null
+++ b/media/libeffects/dynamicsproc/dsp/RDsp.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RDSP_H
+#define RDSP_H
+
+#include <complex>
+#include <log/log.h>
+#include <vector>
+using FloatVec = std::vector<float>;
+using ComplexVec  = std::vector<std::complex<float>>;
+
+// =======
+// DSP window creation
+// =======
+
+#define TWOPI (M_PI * 2)
+
+enum rdsp_window_type {
+    RDSP_WINDOW_RECTANGULAR,
+    RDSP_WINDOW_TRIANGULAR,
+    RDSP_WINDOW_TRIANGULAR_FLAT_TOP,
+    RDSP_WINDOW_HAMMING,
+    RDSP_WINDOW_HAMMING_FLAT_TOP,
+    RDSP_WINDOW_HANNING,
+    RDSP_WINDOW_HANNING_FLAT_TOP,
+};
+
+template <typename T>
+static void fillRectangular(T &v) {
+    const size_t size = v.size();
+    for (size_t i = 0; i < size; i++) {
+        v[i] = 1.0;
+    }
+} //rectangular
+
+template <typename T>
+static void fillTriangular(T &v, size_t overlap) {
+    const size_t size = v.size();
+    //ramp up
+    size_t i = 0;
+    if (overlap > 0) {
+        for (; i < overlap; i++) {
+            v[i] = (2.0 * i + 1) / (2 * overlap);
+        }
+    }
+
+    //flat top
+    for (; i < size - overlap; i++) {
+        v[i] = 1.0;
+    }
+
+    //ramp down
+    if (overlap > 0) {
+        for (; i < size; i++) {
+            v[i] = (2.0 * (size - i) - 1) / (2 * overlap);
+        }
+    }
+} //triangular
+
+template <typename T>
+static void fillHamming(T &v, size_t overlap) {
+    const size_t size = v.size();
+    const size_t twoOverlap = 2 * overlap;
+    size_t i = 0;
+    if (overlap > 0) {
+        for (; i < overlap; i++) {
+            v[i] = 0.54 - 0.46 * cos(TWOPI * i /(twoOverlap - 1));
+        }
+    }
+
+    //flat top
+    for (; i < size - overlap; i++) {
+        v[i] = 1.0;
+    }
+
+    //ramp down
+    if (overlap > 0) {
+        for (; i < size; i++) {
+            int k = i - ((int)size - 2 * overlap);
+            v[i] = 0.54 - 0.46 * cos(TWOPI * k / (twoOverlap - 1));
+        }
+    }
+} //hamming
+
+template <typename T>
+static void fillHanning(T &v, size_t overlap) {
+    const size_t size = v.size();
+    const size_t twoOverlap = 2 * overlap;
+    //ramp up
+    size_t i = 0;
+    if (overlap > 0) {
+        for (; i < overlap; i++) {
+            v[i] = 0.5 * (1.0 - cos(TWOPI * i / (twoOverlap - 1)));
+        }
+    }
+
+    //flat top
+    for (; i < size - overlap; i++) {
+        v[i] = 1.0;
+    }
+
+    //ramp down
+    if (overlap > 0) {
+        for (; i < size; i++) {
+            int k = i - ((int)size - 2 * overlap);
+            v[i] = 0.5 * (1.0 - cos(TWOPI * k / (twoOverlap - 1)));
+        }
+    }
+}
+
+template <typename T>
+static void fill_window(T &v, int type, size_t size, size_t overlap) {
+    if (overlap > size / 2) {
+        overlap = size / 2;
+    }
+    v.resize(size);
+
+    switch (type) {
+    case RDSP_WINDOW_RECTANGULAR:
+        fillRectangular(v);
+        break;
+    case RDSP_WINDOW_TRIANGULAR:
+        fillTriangular(v, size / 2);
+        break;
+    case RDSP_WINDOW_TRIANGULAR_FLAT_TOP:
+        fillTriangular(v, overlap);
+        break;
+    case RDSP_WINDOW_HAMMING:
+        fillHamming(v, size / 2);
+        break;
+    case RDSP_WINDOW_HAMMING_FLAT_TOP:
+        fillHamming(v, overlap);
+        break;
+    case RDSP_WINDOW_HANNING:
+        fillHanning(v, size / 2);
+        break;
+    case RDSP_WINDOW_HANNING_FLAT_TOP:
+        fillHanning(v, overlap);
+        break;
+    default:
+        ALOGE("Error: unknown window type %d", type);
+    }
+}
+
+//};
+#endif //RDSP_H
diff --git a/media/libeffects/dynamicsproc/dsp/SHCircularBuffer.h b/media/libeffects/dynamicsproc/dsp/SHCircularBuffer.h
new file mode 100644
index 0000000..c139cd8
--- /dev/null
+++ b/media/libeffects/dynamicsproc/dsp/SHCircularBuffer.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SHCIRCULARBUFFER_H
+#define SHCIRCULARBUFFER_H
+
+#include <log/log.h>
+#include <vector>
+
+template <class T>
+class SHCircularBuffer {
+
+public:
+    SHCircularBuffer() : mReadIndex(0), mWriteIndex(0), mReadAvailable(0) {
+    }
+
+    explicit SHCircularBuffer(size_t maxSize) {
+        resize(maxSize);
+    }
+    void resize(size_t maxSize) {
+        mBuffer.resize(maxSize);
+        mReadIndex = 0;
+        mWriteIndex = 0;
+        mReadAvailable = 0;
+    }
+    inline void write(T value) {
+        if (availableToWrite()) {
+            mBuffer[mWriteIndex++] = value;
+            if (mWriteIndex >= getSize()) {
+                mWriteIndex = 0;
+            }
+            mReadAvailable++;
+        } else {
+            ALOGE("Error: SHCircularBuffer no space to write. allocated size %zu ", getSize());
+        }
+    }
+    inline T read() {
+        T value = T();
+        if (availableToRead()) {
+            value = mBuffer[mReadIndex++];
+            if (mReadIndex >= getSize()) {
+                mReadIndex = 0;
+            }
+            mReadAvailable--;
+        } else {
+            ALOGW("Warning: SHCircularBuffer no data available to read. Default value returned");
+        }
+        return value;
+    }
+    inline size_t availableToRead() const {
+        return mReadAvailable;
+    }
+    inline size_t availableToWrite() const {
+        return getSize() - mReadAvailable;
+    }
+    inline size_t getSize() const {
+        return mBuffer.size();
+    }
+
+private:
+    std::vector<T> mBuffer;
+    size_t mReadIndex;
+    size_t mWriteIndex;
+    size_t mReadAvailable;
+};
+
+
+#endif //SHCIRCULARBUFFER_H