blob: cd5f69cd996258dadfc5e5726de4422fdad7393e [file] [log] [blame]
/*
* 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.
*/
#include "LVPSA.h"
#include "LVPSA_Private.h"
#include "VectorArithmetic.h"
#define LOW_FREQ 298 /* 32768/110 for low test frequency */
#define HIGH_FREQ 386 /* 32768/85 for high test frequency */
LVPSA_RETURN LVPSA_SetBPFiltersType ( LVPSA_InstancePr_t *pInst,
LVPSA_ControlParams_t *pParams );
LVPSA_RETURN LVPSA_SetQPFCoefficients( LVPSA_InstancePr_t *pInst,
LVPSA_ControlParams_t *pParams );
LVPSA_RETURN LVPSA_BPSinglePrecCoefs( LVM_UINT16 Fs,
LVPSA_FilterParam_t *pFilterParams,
BP_C16_Coefs_t *pCoefficients);
LVPSA_RETURN LVPSA_BPDoublePrecCoefs( LVM_UINT16 Fs,
LVPSA_FilterParam_t *pFilterParams,
BP_C32_Coefs_t *pCoefficients);
LVPSA_RETURN LVPSA_BPDoublePrecCoefs( LVM_UINT16 Fs,
LVPSA_FilterParam_t *pFilterParams,
BP_C32_Coefs_t *pCoefficients);
LVPSA_RETURN LVPSA_SetBPFCoefficients( LVPSA_InstancePr_t *pInst,
LVPSA_ControlParams_t *pParams );
LVPSA_RETURN LVPSA_ClearFilterHistory( LVPSA_InstancePr_t *pInst);
/************************************************************************************/
/* */
/* FUNCTION: LVPSA_Control */
/* */
/* DESCRIPTION: */
/* Give some new control parameters to the module. */
/* */
/* PARAMETERS: */
/* hInstance Pointer to the instance */
/* NewParams Structure that contains the new parameters */
/* */
/* RETURNS: */
/* LVPSA_OK Succeeds */
/* otherwise Error due to bad parameters */
/* */
/************************************************************************************/
LVPSA_RETURN LVPSA_Control ( pLVPSA_Handle_t hInstance,
LVPSA_ControlParams_t *pNewParams )
{
LVPSA_InstancePr_t *pLVPSA_Inst = (LVPSA_InstancePr_t*)hInstance;
if((hInstance == LVM_NULL) || (pNewParams == LVM_NULL))
{
return(LVPSA_ERROR_NULLADDRESS);
}
if(pNewParams->Fs >= LVPSA_NR_SUPPORTED_RATE)
{
return(LVPSA_ERROR_INVALIDPARAM);
}
if(pNewParams->LevelDetectionSpeed >= LVPSA_NR_SUPPORTED_SPEED)
{
return(LVPSA_ERROR_INVALIDPARAM);
}
pLVPSA_Inst->NewParams = *pNewParams;
pLVPSA_Inst->bControlPending = LVM_TRUE;
return(LVPSA_OK);
}
/************************************************************************************/
/* */
/* FUNCTION: LVPSA_GetControlParams */
/* */
/* DESCRIPTION: */
/* Get the current control parameters of the module */
/* */
/* PARAMETERS: */
/* hInstance Pointer to the instance */
/* pParams Pointer to an empty control structure */
/* RETURNS: */
/* LVPSA_OK Succeeds */
/* otherwise Error due to bad parameters */
/* */
/************************************************************************************/
LVPSA_RETURN LVPSA_GetControlParams ( pLVPSA_Handle_t hInstance,
LVPSA_ControlParams_t *pParams )
{
LVPSA_InstancePr_t *pLVPSA_Inst = (LVPSA_InstancePr_t*)hInstance;
if((hInstance == LVM_NULL) || (pParams == LVM_NULL))
{
return(LVPSA_ERROR_NULLADDRESS);
}
pParams->Fs = pLVPSA_Inst->CurrentParams.Fs;
pParams->LevelDetectionSpeed = pLVPSA_Inst->CurrentParams.LevelDetectionSpeed;
return(LVPSA_OK);
}
/************************************************************************************/
/* */
/* FUNCTION: LVPSA_GetInitParams */
/* */
/* DESCRIPTION: */
/* Get the initialization parameters of the module */
/* */
/* PARAMETERS: */
/* hInstance Pointer to the instance */
/* pParams Pointer to an empty control structure */
/* RETURNS: */
/* LVPSA_OK Succeeds */
/* otherwise Error due to bad parameters */
/* */
/************************************************************************************/
LVPSA_RETURN LVPSA_GetInitParams ( pLVPSA_Handle_t hInstance,
LVPSA_InitParams_t *pParams )
{
LVPSA_InstancePr_t *pLVPSA_Inst = (LVPSA_InstancePr_t*)hInstance;
if((hInstance == LVM_NULL) || (pParams == LVM_NULL))
{
return(LVPSA_ERROR_NULLADDRESS);
}
pParams->SpectralDataBufferDuration = pLVPSA_Inst->SpectralDataBufferDuration;
pParams->MaxInputBlockSize = pLVPSA_Inst->MaxInputBlockSize;
pParams->nBands = pLVPSA_Inst->nBands;
pParams->pFiltersParams = pLVPSA_Inst->pFiltersParams;
return(LVPSA_OK);
}
/************************************************************************************/
/* */
/* FUNCTION: LVPSA_ApplyNewSettings */
/* */
/* DESCRIPTION: */
/* Reinitialize some parameters and changes filters' coefficients if */
/* some control parameters have changed. */
/* */
/* PARAMETERS: */
/* pInst Pointer to the instance */
/* */
/* RETURNS: */
/* LVPSA_OK Succeeds */
/* otherwise Error due to bad parameters */
/* */
/* NOTES: */
/* */
/************************************************************************************/
LVPSA_RETURN LVPSA_ApplyNewSettings (LVPSA_InstancePr_t *pInst)
{
LVM_UINT16 ii;
LVM_UINT16 Freq;
LVPSA_ControlParams_t Params;
extern LVM_INT16 LVPSA_nSamplesBufferUpdate[];
extern LVM_UINT16 LVPSA_SampleRateTab[];
extern LVM_UINT16 LVPSA_DownSamplingFactor[];
if(pInst == 0)
{
return(LVPSA_ERROR_NULLADDRESS);
}
Params = pInst->NewParams;
/* Modifies filters types and coefficients, clear the taps and
re-initializes parameters if sample frequency has changed */
if(Params.Fs != pInst->CurrentParams.Fs)
{
pInst->CurrentParams.Fs = Params.Fs;
/* Initialize the center freqeuncies as a function of the sample rate */
Freq = (LVM_UINT16) ((LVPSA_SampleRateTab[pInst->CurrentParams.Fs]>>1) / (pInst->nBands + 1));
for(ii = pInst->nBands; ii > 0; ii--)
{
pInst->pFiltersParams[ii-1].CenterFrequency = (LVM_UINT16) (Freq * ii);
}
/* Count the number of relevant filters. If the center frequency of the filter is
bigger than the nyquist frequency, then the filter is not relevant and doesn't
need to be used */
for(ii = pInst->nBands; ii > 0; ii--)
{
if(pInst->pFiltersParams[ii-1].CenterFrequency < (LVPSA_SampleRateTab[pInst->CurrentParams.Fs]>>1))
{
pInst->nRelevantFilters = ii;
break;
}
}
LVPSA_SetBPFiltersType(pInst, &Params);
LVPSA_SetBPFCoefficients(pInst, &Params);
LVPSA_SetQPFCoefficients(pInst, &Params);
LVPSA_ClearFilterHistory(pInst);
pInst->nSamplesBufferUpdate = (LVM_UINT16)LVPSA_nSamplesBufferUpdate[Params.Fs];
pInst->BufferUpdateSamplesCount = 0;
pInst->DownSamplingFactor = LVPSA_DownSamplingFactor[Params.Fs];
pInst->DownSamplingCount = 0;
for(ii = 0; ii < (pInst->nBands * pInst->SpectralDataBufferLength); ii++)
{
pInst->pSpectralDataBufferStart[ii] = 0;
}
for(ii = 0; ii < pInst->nBands; ii++)
{
pInst->pPreviousPeaks[ii] = 0;
}
}
else
{
if(Params.LevelDetectionSpeed != pInst->CurrentParams.LevelDetectionSpeed)
{
LVPSA_SetQPFCoefficients(pInst, &Params);
}
}
pInst->CurrentParams = pInst->NewParams;
return (LVPSA_OK);
}
/************************************************************************************/
/* */
/* FUNCTION: LVPSA_SetBPFiltersType */
/* */
/* DESCRIPTION: */
/* Sets the filter type based on the BPFilterType. */
/* */
/* PARAMETERS: */
/* pInst Pointer to the instance */
/* pParams Poniter to conrol parameters */
/* */
/* RETURNS: */
/* LVPSA_OK Always succeeds */
/* */
/* NOTES: */
/* 1. To select the biquad type the follow rules are applied: */
/* Double precision if (fc <= fs/110) */
/* Double precision if (fs/110 < fc < fs/85) & (Q>3) */
/* Single precision otherwise */
/* */
/************************************************************************************/
LVPSA_RETURN LVPSA_SetBPFiltersType ( LVPSA_InstancePr_t *pInst,
LVPSA_ControlParams_t *pParams )
{
extern LVM_UINT16 LVPSA_SampleRateTab[]; /* Sample rate table */
LVM_UINT16 ii; /* Filter band index */
LVM_UINT32 fs = (LVM_UINT32)LVPSA_SampleRateTab[(LVM_UINT16)pParams->Fs]; /* Sample rate */
LVM_UINT32 fc; /* Filter centre frequency */
LVM_INT16 QFactor; /* Filter Q factor */
for (ii = 0; ii < pInst->nRelevantFilters; ii++)
{
/*
* Get the filter settings
*/
fc = (LVM_UINT32)pInst->pFiltersParams[ii].CenterFrequency; /* Get the band centre frequency */
QFactor =(LVM_INT16) pInst->pFiltersParams[ii].QFactor; /* Get the band Q factor */
/*
* For each filter set the type of biquad required
*/
pInst->pBPFiltersPrecision[ii] = LVPSA_SimplePrecisionFilter; /* Default to single precision */
if ((LOW_FREQ * fs) >= (fc << 15))
{
/*
* fc <= fs/110
*/
pInst->pBPFiltersPrecision[ii] = LVPSA_DoublePrecisionFilter;
}
else
{
if (((LOW_FREQ * fs) < (fc << 15)) && ((fc << 15) < (HIGH_FREQ * fs)) && (QFactor > 300))
{
/*
* (fs/110 < fc < fs/85) & (Q>3)
*/
pInst->pBPFiltersPrecision[ii] = LVPSA_DoublePrecisionFilter;
}
}
}
return(LVPSA_OK);
}
/************************************************************************************/
/* */
/* FUNCTION: LVPSA_SetBPFCoefficients */
/* */
/* DESCRIPTION: */
/* Sets the band pass filter coefficients. This uses the type to select */
/* single or double precision coefficients. */
/* */
/* PARAMETERS: */
/* pInst Pointer to the instance */
/* Params Initialisation parameters */
/* */
/* RETURNS: */
/* LVPSA_OK Always succeeds */
/* */
/* NOTES: */
/* */
/************************************************************************************/
LVPSA_RETURN LVPSA_SetBPFCoefficients( LVPSA_InstancePr_t *pInst,
LVPSA_ControlParams_t *pParams)
{
LVM_UINT16 ii;
/*
* Set the coefficients for each band by the init function
*/
for (ii = 0; ii < pInst->nRelevantFilters; ii++)
{
switch (pInst->pBPFiltersPrecision[ii])
{
case LVPSA_DoublePrecisionFilter:
{
BP_C32_Coefs_t Coefficients;
/*
* Calculate the double precision coefficients
*/
LVPSA_BPDoublePrecCoefs((LVM_UINT16)pParams->Fs,
&pInst->pFiltersParams[ii],
&Coefficients);
/*
* Set the coefficients
*/
BP_1I_D16F32Cll_TRC_WRA_01_Init ( &pInst->pBP_Instances[ii],
&pInst->pBP_Taps[ii],
&Coefficients);
break;
}
case LVPSA_SimplePrecisionFilter:
{
BP_C16_Coefs_t Coefficients;
/*
* Calculate the single precision coefficients
*/
LVPSA_BPSinglePrecCoefs((LVM_UINT16)pParams->Fs,
&pInst->pFiltersParams[ii],
&Coefficients);
/*
* Set the coefficients
*/
BP_1I_D16F16Css_TRC_WRA_01_Init ( &pInst->pBP_Instances[ii],
&pInst->pBP_Taps[ii],
&Coefficients);
break;
}
}
}
return(LVPSA_OK);
}
/************************************************************************************/
/* */
/* FUNCTION: LVPSA_SetQPFCoefficients */
/* */
/* DESCRIPTION: */
/* Sets the quasi peak filters coefficients. This uses the chosen */
/* LevelDetectionSpeed from the control parameters. */
/* */
/* PARAMETERS: */
/* pInst Pointer to the instance */
/* Params Control parameters */
/* */
/* RETURNS: */
/* LVPSA_OK Always succeeds */
/* */
/* NOTES: */
/* */
/************************************************************************************/
LVPSA_RETURN LVPSA_SetQPFCoefficients( LVPSA_InstancePr_t *pInst,
LVPSA_ControlParams_t *pParams )
{
LVM_UINT16 ii;
LVM_Fs_en Fs = pParams->Fs;
QPD_C32_Coefs *pCoefficients;
extern QPD_C32_Coefs LVPSA_QPD_Coefs[];
pCoefficients = &LVPSA_QPD_Coefs[(pParams->LevelDetectionSpeed * LVPSA_NR_SUPPORTED_RATE) + Fs];
for (ii = 0; ii < pInst->nRelevantFilters; ii++)
{
LVPSA_QPD_Init (&pInst->pQPD_States[ii],
&pInst->pQPD_Taps[ii],
pCoefficients );
}
return(LVPSA_OK);
}
/****************************************************************************************/
/* */
/* FUNCTION: LVPSA_BPSinglePrecCoefs */
/* */
/* DESCRIPTION: */
/* Calculate single precision coefficients for a band pass filter */
/* */
/* PARAMETERS: */
/* Fs Sampling frequency index */
/* pFilterParams Pointer to the filter definition */
/* pCoefficients Pointer to the coefficients */
/* */
/* RETURNS: */
/* LVPSA_OK Always succeeds */
/* */
/* NOTES: */
/* 1. The equations used are as follows: */
/* */
/* t0 = 2 * Pi * Fc / Fs */
/* */
/* b2 = -0.5 * (2Q - t0) / (2Q + t0) */
/* b1 = (0.5 - b2) * cos(t0) */
/* a0 = (0.5 + b2) / 2 */
/* */
/* Where: */
/* Fc is the centre frequency, DC to Nyquist */
/* Fs is the sample frequency, 8000 to 48000 in descrete steps */
/* Q is the Q factor, 0.25 to 12 */
/* */
/* 2. This function is entirely based on the LVEQNB_SinglePrecCoefs function */
/* of the n bands equalizer (LVEQNB */
/* */
/****************************************************************************************/
LVPSA_RETURN LVPSA_BPSinglePrecCoefs( LVM_UINT16 Fs,
LVPSA_FilterParam_t *pFilterParams,
BP_C16_Coefs_t *pCoefficients)
{
extern LVM_INT16 LVPSA_TwoPiOnFsTable[];
extern LVM_INT16 LVPSA_CosCoef[];
/*
* Intermediate variables and temporary values
*/
LVM_INT32 T0;
LVM_INT16 D;
LVM_INT32 A0;
LVM_INT32 B1;
LVM_INT32 B2;
LVM_INT32 Dt0;
LVM_INT32 B2_Den;
LVM_INT32 B2_Num;
LVM_INT32 COS_T0;
LVM_INT16 coef;
LVM_INT32 factor;
LVM_INT16 t0;
LVM_INT16 i;
/*
* Get the filter definition
*/
LVM_UINT16 Frequency = pFilterParams->CenterFrequency;
LVM_UINT16 QFactor = pFilterParams->QFactor;
/*
* Calculating the intermediate values
*/
T0 = (LVM_INT32)Frequency * LVPSA_TwoPiOnFsTable[Fs]; /* T0 = 2 * Pi * Fc / Fs */
D = 3200; /* Floating point value 1.000000 (1*100*2^5) */
/* Force D = 1 : the function was originally used for a peaking filter.
The D parameter do not exist for a BandPass filter coefficients */
/*
* Calculate the B2 coefficient
*/
Dt0 = D * (T0 >> 10);
B2_Den = (LVM_INT32)(((LVM_UINT32)QFactor << 19) + (LVM_UINT32)(Dt0 >> 2));
B2_Num = (LVM_INT32)((LVM_UINT32)(Dt0 >> 3) - ((LVM_UINT32)QFactor << 18));
B2 = (B2_Num / (B2_Den >> 16)) << 15;
/*
* Calculate the cosine by a polynomial expansion using the equation:
*
* Cos += coef(n) * t0^n For n = 0 to 6
*/
T0 = (T0 >> 10) * 20859; /* Scale to 1.0 in 16-bit for range 0 to fs/2 */
t0 = (LVM_INT16)(T0 >> 16);
factor = 0x7fff; /* Initialise to 1.0 for the a0 coefficient */
COS_T0 = 0; /* Initialise the error to zero */
for (i=1; i<7; i++)
{
coef = LVPSA_CosCoef[i]; /* Get the nth coefficient */
COS_T0 += (factor * coef) >> 5; /* The nth partial sum */
factor = (factor * t0) >> 15; /* Calculate t0^n */
}
COS_T0 = COS_T0 << (LVPSA_CosCoef[0]+6); /* Correct the scaling */
B1 = ((0x40000000 - B2) >> 16) * (COS_T0 >> 16); /* B1 = (0.5 - b2) * cos(t0) */
A0 = (0x40000000 + B2) >> 1; /* A0 = (0.5 + b2) / 2 */
/*
* Write coeff into the data structure
*/
pCoefficients->A0 = (LVM_INT16)(A0>>16);
pCoefficients->B1 = (LVM_INT16)(B1>>15);
pCoefficients->B2 = (LVM_INT16)(B2>>16);
return(LVPSA_OK);
}
/****************************************************************************************/
/* */
/* FUNCTION: LVPSA_BPDoublePrecCoefs */
/* */
/* DESCRIPTION: */
/* Calculate double precision coefficients for a band pass filter */
/* */
/* PARAMETERS: */
/* Fs Sampling frequency index */
/* pFilterParams Pointer to the filter definition */
/* pCoefficients Pointer to the coefficients */
/* */
/* RETURNS: */
/* LVPSA_OK Always succeeds */
/* */
/* NOTES: */
/* 1. The equations used are as follows: */
/* */
/* t0 = 2 * Pi * Fc / Fs */
/* */
/* b2 = -0.5 * (2Q - t0) / (2Q + t0) */
/* b1 = (0.5 - b2) * (1 - coserr(t0)) */
/* a0 = (0.5 + b2) / 2 */
/* */
/* Where: */
/* Fc is the centre frequency, DC to Fs/50 */
/* Fs is the sample frequency, 8000 to 48000 in descrete steps */
/* Q is the Q factor, 0.25 to 12 (represented by 25 to 1200) */
/* */
/* 2. The double precision coefficients are only used when fc is less than fs/85, so */
/* the cosine of t0 is always close to 1.0. Instead of calculating the cosine */
/* itself the difference from the value 1.0 is calculated, this can be done with */
/* lower precision maths. */
/* */
/* 3. The value of the B2 coefficient is only calculated as a single precision value, */
/* small errors in this value have a combined effect on the Q and Gain but not the */
/* the frequency of the filter. */
/* */
/* 4. This function is entirely based on the LVEQNB_DoublePrecCoefs function */
/* of the n bands equalizer (LVEQNB */
/* */
/****************************************************************************************/
LVPSA_RETURN LVPSA_BPDoublePrecCoefs( LVM_UINT16 Fs,
LVPSA_FilterParam_t *pFilterParams,
BP_C32_Coefs_t *pCoefficients)
{
extern LVM_INT16 LVPSA_TwoPiOnFsTable[];
extern LVM_INT16 LVPSA_DPCosCoef[];
/*
* Intermediate variables and temporary values
*/
LVM_INT32 T0;
LVM_INT16 D;
LVM_INT32 A0;
LVM_INT32 B1;
LVM_INT32 B2;
LVM_INT32 Dt0;
LVM_INT32 B2_Den;
LVM_INT32 B2_Num;
LVM_INT32 CosErr;
LVM_INT16 coef;
LVM_INT32 factor;
LVM_INT16 t0;
LVM_INT16 i;
/*
* Get the filter definition
*/
LVM_UINT16 Frequency = pFilterParams->CenterFrequency;
LVM_UINT16 QFactor = pFilterParams->QFactor;
/*
* Calculating the intermediate values
*/
T0 = (LVM_INT32)Frequency * LVPSA_TwoPiOnFsTable[Fs]; /* T0 = 2 * Pi * Fc / Fs */
D = 3200; /* Floating point value 1.000000 (1*100*2^5) */
/* Force D = 1 : the function was originally used for a peaking filter.
The D parameter do not exist for a BandPass filter coefficients */
/*
* Calculate the B2 coefficient
*/
Dt0 = D * (T0 >> 10);
B2_Den = (LVM_INT32)(((LVM_UINT32)QFactor << 19) + (LVM_UINT32)(Dt0 >> 2));
B2_Num = (LVM_INT32)((LVM_UINT32)(Dt0 >> 3) - ((LVM_UINT32)QFactor << 18));
B2 = (B2_Num / (B2_Den >> 16)) << 15;
/*
* Calculate the cosine error by a polynomial expansion using the equation:
*
* CosErr += coef(n) * t0^n For n = 0 to 4
*/
T0 = (T0 >> 6) * 0x7f53; /* Scale to 1.0 in 16-bit for range 0 to fs/50 */
t0 = (LVM_INT16)(T0 >> 16);
factor = 0x7fff; /* Initialise to 1.0 for the a0 coefficient */
CosErr = 0; /* Initialise the error to zero */
for (i=1; i<5; i++)
{
coef = LVPSA_DPCosCoef[i]; /* Get the nth coefficient */
CosErr += (factor * coef) >> 5; /* The nth partial sum */
factor = (factor * t0) >> 15; /* Calculate t0^n */
}
CosErr = CosErr << (LVPSA_DPCosCoef[0]); /* Correct the scaling */
/*
* Calculate the B1 and A0 coefficients
*/
B1 = (0x40000000 - B2); /* B1 = (0.5 - b2) */
A0 = ((B1 >> 16) * (CosErr >> 10)) >> 6; /* Temporary storage for (0.5 - b2) * coserr(t0) */
B1 -= A0; /* B1 = (0.5 - b2) * (1 - coserr(t0)) */
A0 = (0x40000000 + B2) >> 1; /* A0 = (0.5 + b2) / 2 */
/*
* Write coeff into the data structure
*/
pCoefficients->A0 = A0;
pCoefficients->B1 = B1;
pCoefficients->B2 = B2;
return(LVPSA_OK);
}
/************************************************************************************/
/* */
/* FUNCTION: LVPSA_ClearFilterHistory */
/* */
/* DESCRIPTION: */
/* Clears the filters' data history */
/* */
/* PARAMETERS: */
/* pInst Pointer to the instance */
/* */
/* RETURNS: */
/* LVPSA_OK Always succeeds */
/* */
/* NOTES: */
/* */
/************************************************************************************/
LVPSA_RETURN LVPSA_ClearFilterHistory(LVPSA_InstancePr_t *pInst)
{
LVM_INT8 *pTapAddress;
LVM_UINT32 i;
/* Band Pass filters taps */
pTapAddress = (LVM_INT8 *)pInst->pBP_Taps;
for(i = 0; i < pInst->nBands * sizeof(Biquad_1I_Order2_Taps_t); i++)
{
pTapAddress[i] = 0;
}
/* Quasi-peak filters taps */
pTapAddress = (LVM_INT8 *)pInst->pQPD_Taps;
for(i = 0; i < pInst->nBands * sizeof(QPD_Taps_t); i++)
{
pTapAddress[i] = 0;
}
return(LVPSA_OK);
}