/*
 * Copyright (C) 2004-2010 NXP Software
 * Copyright (C) 2010 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.
 */

/****************************************************************************************/
/*                                                                                      */
/*    Includes                                                                          */
/*                                                                                      */
/****************************************************************************************/

#include "LVDBE.h"
#include "LVDBE_Private.h"
#include "VectorArithmetic.h"
#include "LVDBE_Coeffs.h"
#include "LVDBE_Tables.h"

/****************************************************************************************/
/*                                                                                      */
/* FUNCTION:                  LVDBE_GetParameters                                       */
/*                                                                                      */
/* DESCRIPTION:                                                                         */
/*    Request the Dynamic Bass Enhancement parameters. The current parameter set is     */
/*  returned via the parameter pointer.                                                 */
/*                                                                                      */
/* PARAMETERS:                                                                          */
/*  hInstance                   Instance handle                                         */
/*  pParams                  Pointer to an empty parameter structure                    */
/*                                                                                      */
/* RETURNS:                                                                             */
/*  LVDBE_SUCCESS            Always succeeds                                            */
/*                                                                                      */
/* NOTES:                                                                               */
/*  1.    This function may be interrupted by the LVDBE_Process function                */
/*                                                                                      */
/****************************************************************************************/

LVDBE_ReturnStatus_en LVDBE_GetParameters(LVDBE_Handle_t        hInstance,
                                            LVDBE_Params_t        *pParams)
{

    LVDBE_Instance_t    *pInstance =(LVDBE_Instance_t  *)hInstance;

    *pParams = pInstance->Params;

    return(LVDBE_SUCCESS);
}


/************************************************************************************/
/*                                                                                  */
/* FUNCTION:                  LVDBE_GetCapabilities                                 */
/*                                                                                  */
/* DESCRIPTION: Dynamic Bass Enhnacement capabilities. The current capabilities are */
/* returned via the pointer.                                                        */
/*                                                                                  */
/* PARAMETERS:                                                                      */
/*  hInstance                   Instance handle                                     */
/*  pCapabilities              Pointer to an empty capability structure             */
/*                                                                                  */
/* RETURNS:                                                                         */
/*  LVDBE_Success             Always succeeds                                       */
/*                                                                                  */
/* NOTES:                                                                           */
/*  1.    This function may be interrupted by the LVDBE_Process function            */
/*                                                                                  */
/************************************************************************************/

LVDBE_ReturnStatus_en LVDBE_GetCapabilities(LVDBE_Handle_t            hInstance,
                                              LVDBE_Capabilities_t    *pCapabilities)
{

    LVDBE_Instance_t    *pInstance =(LVDBE_Instance_t  *)hInstance;

    *pCapabilities = pInstance->Capabilities;

    return(LVDBE_SUCCESS);
}


/************************************************************************************/
/*                                                                                  */
/* FUNCTION:            LVDBE_SetFilters                                            */
/*                                                                                  */
/* DESCRIPTION:                                                                     */
/*  Sets the filter coefficients and clears the data history                        */
/*                                                                                  */
/* PARAMETERS:                                                                      */
/*  pInstance           Pointer to the instance                                     */
/*  pParams             Initialisation parameters                                   */
/*                                                                                  */
/************************************************************************************/

void    LVDBE_SetFilters(LVDBE_Instance_t     *pInstance,
                         LVDBE_Params_t       *pParams)
{

#if defined(BUILD_FLOAT) && defined(HIGHER_FS)
    /*
     * Calculate the table offsets
     */
    LVM_UINT16 Offset = (LVM_UINT16)((LVM_UINT16)pParams->SampleRate + \
                                    (LVM_UINT16)(pParams->CentreFrequency * (1+LVDBE_FS_192000)));
#else
    /*
     * Calculate the table offsets
     */
    LVM_UINT16 Offset = (LVM_UINT16)((LVM_UINT16)pParams->SampleRate + \
                                    (LVM_UINT16)(pParams->CentreFrequency * (1+LVDBE_FS_48000)));    
#endif

    /*
     * Setup the high pass filter
     */
#ifndef BUILD_FLOAT
    LoadConst_16(0,                                              /* Clear the history, value 0 */
                 (void *)&pInstance->pData->HPFTaps,             /* Destination Cast to void: \
                                                                    no dereferencing in function*/
                 sizeof(pInstance->pData->HPFTaps)/sizeof(LVM_INT16));   /* Number of words */
#else
    LoadConst_Float(0,                                          /* Clear the history, value 0 */
                   (void *)&pInstance->pData->HPFTaps,          /* Destination Cast to void: \
                                                                  no dereferencing in function*/
                    sizeof(pInstance->pData->HPFTaps) / sizeof(LVM_FLOAT)); /* Number of words */
#endif
#ifndef BUILD_FLOAT
    BQ_2I_D32F32Cll_TRC_WRA_01_Init(&pInstance->pCoef->HPFInstance,    /* Initialise the filter */
                                    &pInstance->pData->HPFTaps,
                                    (BQ_C32_Coefs_t *)&LVDBE_HPF_Table[Offset]);
#else
    BQ_2I_D32F32Cll_TRC_WRA_01_Init(&pInstance->pCoef->HPFInstance,    /* Initialise the filter */
                                    &pInstance->pData->HPFTaps,
                                    (BQ_FLOAT_Coefs_t *)&LVDBE_HPF_Table[Offset]);
#endif


    /*
     * Setup the band pass filter
     */
#ifndef BUILD_FLOAT
    LoadConst_16(0,                                                 /* Clear the history, value 0 */
                 (void *)&pInstance->pData->BPFTaps,                /* Destination Cast to void: \
                                                                     no dereferencing in function*/
                 sizeof(pInstance->pData->BPFTaps)/sizeof(LVM_INT16));   /* Number of words */
#else
    LoadConst_Float(0,                                           /* Clear the history, value 0 */
                 (void *)&pInstance->pData->BPFTaps,             /* Destination Cast to void: \
                                                                    no dereferencing in function*/
                 sizeof(pInstance->pData->BPFTaps) / sizeof(LVM_FLOAT));   /* Number of words */
#endif
#ifndef BUILD_FLOAT
    BP_1I_D32F32Cll_TRC_WRA_02_Init(&pInstance->pCoef->BPFInstance,         /* Initialise the filter */
                                    &pInstance->pData->BPFTaps,
                                    (BP_C32_Coefs_t *)&LVDBE_BPF_Table[Offset]);
#else
    BP_1I_D32F32Cll_TRC_WRA_02_Init(&pInstance->pCoef->BPFInstance,    /* Initialise the filter */
                                    &pInstance->pData->BPFTaps,
                                    (BP_FLOAT_Coefs_t *)&LVDBE_BPF_Table[Offset]);
#endif
}



/************************************************************************************/
/*                                                                                  */
/* FUNCTION:            LVDBE_SetAGC                                                */
/*                                                                                  */
/* DESCRIPTION:                                                                     */
/*  Sets the AGC gain level and attack and decay times constants.                   */
/*                                                                                  */
/* PARAMETERS:                                                                      */
/*  pInstance           Pointer to the instance                                     */
/*  pParams             Initialisation parameters                                   */
/*                                                                                  */
/************************************************************************************/

void    LVDBE_SetAGC(LVDBE_Instance_t     *pInstance,
                     LVDBE_Params_t       *pParams)
{

    /*
     * Get the attack and decay time constants
     */
    pInstance->pData->AGCInstance.AGC_Attack = LVDBE_AGC_ATTACK_Table[(LVM_UINT16)pParams->SampleRate];  /* Attack multiplier */
    pInstance->pData->AGCInstance.AGC_Decay  = LVDBE_AGC_DECAY_Table[(LVM_UINT16)pParams->SampleRate];   /* Decay multipler */


    /*
     * Get the boost gain
     */
    if (pParams->HPFSelect == LVDBE_HPF_ON)
    {
        pInstance->pData->AGCInstance.AGC_MaxGain   = LVDBE_AGC_HPFGAIN_Table[(LVM_UINT16)pParams->EffectLevel];  /* High pass filter on */
    }
    else
    {
        pInstance->pData->AGCInstance.AGC_MaxGain   = LVDBE_AGC_GAIN_Table[(LVM_UINT16)pParams->EffectLevel];     /* High pass filter off */
    }
#ifndef BUILD_FLOAT
    pInstance->pData->AGCInstance.AGC_GainShift = AGC_GAIN_SHIFT;
#endif
    pInstance->pData->AGCInstance.AGC_Target = AGC_TARGETLEVEL;

}


/************************************************************************************/
/*                                                                                  */
/* FUNCTION:            LVDBE_SetVolume                                             */
/*                                                                                  */
/* DESCRIPTION:                                                                     */
/*  Converts the input volume demand from dBs to linear.                            */
/*                                                                                  */
/* PARAMETERS:                                                                      */
/*  pInstance           Pointer to the instance                                     */
/*  pParams             Initialisation parameters                                   */
/*                                                                                  */
/* NOTES:                                                                           */
/*  1. The volume should have the following settings:                               */
/*                                                                                  */
/*          DBE         Vol Control           Volume setting                        */
/*          ===         ===========         ===================                     */
/*          Off             Off                 HeadroomdB                          */
/*          Off             On              VolumedB+HeadroomdB                     */
/*          On              Off                 HeadroomdB                          */
/*          On              On              VolumedB+HeadroomdB                     */
/*                                                                                  */
/************************************************************************************/

void    LVDBE_SetVolume(LVDBE_Instance_t     *pInstance,
                        LVDBE_Params_t       *pParams)
{

    LVM_UINT16      dBShifts;                                   /* 6dB shifts */
    LVM_UINT16      dBOffset;                                   /* Table offset */
    LVM_INT16       Volume = 0;                                 /* Required volume in dBs */

#ifdef BUILD_FLOAT
    LVM_FLOAT        dBShifts_fac;
#endif
    /*
     * Apply the volume if enabled
     */
    if (pParams->VolumeControl == LVDBE_VOLUME_ON)
    {
        /*
         * Limit the gain to the maximum allowed
         */
        if  (pParams->VolumedB > VOLUME_MAX)
        {
            Volume = VOLUME_MAX;
        }
        else
        {
            Volume = pParams->VolumedB;
        }
    }


    /*
     * Calculate the required gain and shifts
     */
    dBOffset = (LVM_UINT16)(6 + Volume % 6);                    /* Get the dBs 0-5 */
    dBShifts = (LVM_UINT16)(Volume / -6);                       /* Get the 6dB shifts */

#ifdef BUILD_FLOAT
    dBShifts_fac = (LVM_FLOAT)(1 << dBShifts);
#endif
    /*
     * When DBE is enabled use AGC volume
     */
#ifndef BUILD_FLOAT
    pInstance->pData->AGCInstance.Target = ((LVM_INT32)LVDBE_VolumeTable[dBOffset] << 16);
    pInstance->pData->AGCInstance.Target = pInstance->pData->AGCInstance.Target >> dBShifts;
#else
    pInstance->pData->AGCInstance.Target = (LVDBE_VolumeTable[dBOffset]);
    pInstance->pData->AGCInstance.Target = pInstance->pData->AGCInstance.Target / dBShifts_fac;
#endif
    pInstance->pData->AGCInstance.VolumeTC    = LVDBE_VolumeTCTable[(LVM_UINT16)pParams->SampleRate];   /* Volume update time constant */
#ifndef BUILD_FLOAT
    pInstance->pData->AGCInstance.VolumeShift = VOLUME_SHIFT+1;
#endif

    /*
     * When DBE is disabled use the bypass volume control
     */
    if(dBShifts > 0)
    {
#ifndef BUILD_FLOAT
        LVC_Mixer_SetTarget(&pInstance->pData->BypassVolume.MixerStream[0],(((LVM_INT32)LVDBE_VolumeTable[dBOffset]) >> dBShifts));
#else
        LVC_Mixer_SetTarget(&pInstance->pData->BypassVolume.MixerStream[0],
                            LVDBE_VolumeTable[dBOffset] / dBShifts_fac);
#endif
    }
    else
    {
#ifndef BUILD_FLOAT
        LVC_Mixer_SetTarget(&pInstance->pData->BypassVolume.MixerStream[0],(LVM_INT32)LVDBE_VolumeTable[dBOffset]);
#else
        LVC_Mixer_SetTarget(&pInstance->pData->BypassVolume.MixerStream[0],
                            LVDBE_VolumeTable[dBOffset]);
#endif
    }

    pInstance->pData->BypassVolume.MixerStream[0].CallbackSet = 1;
#ifndef BUILD_FLOAT
    LVC_Mixer_VarSlope_SetTimeConstant(&pInstance->pData->BypassVolume.MixerStream[0],
                                LVDBE_MIXER_TC,
                                (LVM_Fs_en)pInstance->Params.SampleRate,
                                2);
#else
    LVC_Mixer_VarSlope_SetTimeConstant(&pInstance->pData->BypassVolume.MixerStream[0],
                                LVDBE_MIXER_TC,
                                (LVM_Fs_en)pInstance->Params.SampleRate,
                                2);
#endif
}


/****************************************************************************************/
/*                                                                                      */
/* FUNCTION:                LVDBE_Control                                               */
/*                                                                                      */
/* DESCRIPTION:                                                                         */
/*  Sets or changes the Bass Enhancement parameters. Changing the parameters while the  */
/*  module is processing signals may have the following side effects:                   */
/*                                                                                      */
/*  General parameters:                                                                 */
/*  ===================                                                                 */
/*  OperatingMode:      Changing the mode of operation may cause a change in volume     */
/*                      level or cause pops and clicks.                                 */
/*                                                                                      */
/*  SampleRate:         Changing the sample rate may cause pops and clicks.             */
/*                                                                                      */
/*  EffectLevel:        Changing the effect level may cause pops and clicks             */
/*                                                                                      */
/*  CentreFrequency:    Changing the centre frequency may cause pops and clicks         */
/*                                                                                      */
/*  HPFSelect:          Selecting/de-selecting the high pass filter may cause pops and  */
/*                      clicks                                                          */
/*                                                                                      */
/*  VolumedB            Changing the volume setting will have no side effects           */
/*                                                                                      */
/*                                                                                      */
/* PARAMETERS:                                                                          */
/*  hInstance               Instance handle                                             */
/*  pParams                 Pointer to a parameter structure                            */
/*                                                                                      */
/* RETURNS:                                                                             */
/*  LVDBE_SUCCESS           Always succeeds                                             */
/*                                                                                      */
/* NOTES:                                                                               */
/*  1.  This function must not be interrupted by the LVDBE_Process function             */
/*                                                                                      */
/****************************************************************************************/

LVDBE_ReturnStatus_en LVDBE_Control(LVDBE_Handle_t         hInstance,
                                      LVDBE_Params_t         *pParams)
{

    LVDBE_Instance_t    *pInstance =(LVDBE_Instance_t  *)hInstance;
#ifndef BUILD_FLOAT
    LVMixer3_2St_st     *pBypassMixer_Instance = &pInstance->pData->BypassMixer;
#else
    LVMixer3_2St_FLOAT_st     *pBypassMixer_Instance = &pInstance->pData->BypassMixer;
#endif


    /*
     * Update the filters
     */
    if ((pInstance->Params.SampleRate != pParams->SampleRate) ||
        (pInstance->Params.CentreFrequency != pParams->CentreFrequency))
    {
        LVDBE_SetFilters(pInstance,                     /* Instance pointer */
                         pParams);                      /* New parameters */
    }


    /*
     * Update the AGC is the effect level has changed
     */
    if ((pInstance->Params.SampleRate != pParams->SampleRate) ||
        (pInstance->Params.EffectLevel != pParams->EffectLevel) ||
        (pInstance->Params.HPFSelect != pParams->HPFSelect))
    {
        LVDBE_SetAGC(pInstance,                         /* Instance pointer */
                     pParams);                          /* New parameters */
#ifndef BUILD_FLOAT
        LVC_Mixer_SetTimeConstant(&pBypassMixer_Instance->MixerStream[0],
            LVDBE_BYPASS_MIXER_TC,(LVM_Fs_en)pParams->SampleRate,2);

        LVC_Mixer_SetTimeConstant(&pBypassMixer_Instance->MixerStream[1],
            LVDBE_BYPASS_MIXER_TC,(LVM_Fs_en)pParams->SampleRate,2);
#else
        LVC_Mixer_SetTimeConstant(&pBypassMixer_Instance->MixerStream[0],
            LVDBE_BYPASS_MIXER_TC,(LVM_Fs_en)pParams->SampleRate, 2);

        LVC_Mixer_SetTimeConstant(&pBypassMixer_Instance->MixerStream[1],
            LVDBE_BYPASS_MIXER_TC,(LVM_Fs_en)pParams->SampleRate, 2);
#endif


    }


    /*
     * Update the Volume if the volume demand has changed
     */
    if ((pInstance->Params.VolumedB != pParams->VolumedB) ||
        (pInstance->Params.SampleRate != pParams->SampleRate) ||
        (pInstance->Params.HeadroomdB != pParams->HeadroomdB) ||
        (pInstance->Params.VolumeControl != pParams->VolumeControl))
    {
        LVDBE_SetVolume(pInstance,                      /* Instance pointer */
                       pParams);                        /* New parameters */
    }

    if (pInstance->Params.OperatingMode==LVDBE_ON && pParams->OperatingMode==LVDBE_OFF)
    {
#ifndef BUILD_FLOAT
        LVC_Mixer_SetTarget(&pInstance->pData->BypassMixer.MixerStream[0],0);
        LVC_Mixer_SetTarget(&pInstance->pData->BypassMixer.MixerStream[1],0x00007FFF);
#else
        LVC_Mixer_SetTarget(&pInstance->pData->BypassMixer.MixerStream[0], 0);
        LVC_Mixer_SetTarget(&pInstance->pData->BypassMixer.MixerStream[1], 1.0f);
#endif
    }
    if (pInstance->Params.OperatingMode==LVDBE_OFF && pParams->OperatingMode==LVDBE_ON)
    {
#ifndef BUILD_FLOAT
        LVC_Mixer_SetTarget(&pInstance->pData->BypassMixer.MixerStream[0],0x00007FFF);
        LVC_Mixer_SetTarget(&pInstance->pData->BypassMixer.MixerStream[1],0);
#else
        LVC_Mixer_SetTarget(&pInstance->pData->BypassMixer.MixerStream[0], 1.0f);
        LVC_Mixer_SetTarget(&pInstance->pData->BypassMixer.MixerStream[1], 0);
#endif
    }

    /*
     * Update the instance parameters
     */
    pInstance->Params = *pParams;


    return(LVDBE_SUCCESS);
}
