| /* |
| * 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 "LVCS.h" |
| #include "LVCS_Private.h" |
| #include "LVCS_ReverbGenerator.h" |
| #include "LVC_Mixer.h" |
| #include "VectorArithmetic.h" |
| #include "BIQUAD.h" |
| #include "LVCS_Tables.h" |
| |
| /************************************************************************************/ |
| /* */ |
| /* FUNCTION: LVCS_ReverbGeneratorInit */ |
| /* */ |
| /* DESCRIPTION: */ |
| /* Initialises the reverb module. The delay buffer size is configured for the */ |
| /* sample rate and the speaker type. */ |
| /* */ |
| /* The routine may also be called for re-initialisation, i.e. when one of the */ |
| /* control parameters has changed. In this case the delay and filters are only */ |
| /* re-initialised if one of the following two conditions is met: */ |
| /* - the sample rate has changed */ |
| /* - the speaker type changes to/from the mobile speaker */ |
| /* */ |
| /* */ |
| /* PARAMETERS: */ |
| /* hInstance Instance Handle */ |
| /* pParams Pointer to the inialisation parameters */ |
| /* */ |
| /* RETURNS: */ |
| /* LVCS_Success Always succeeds */ |
| /* */ |
| /* NOTES: */ |
| /* 1. In the delay settings 'Samples' is the number of samples to the end of the */ |
| /* buffer. */ |
| /* 2. The numerator coefficients of the filter are negated to cause an inversion. */ |
| /* */ |
| /************************************************************************************/ |
| |
| LVCS_ReturnStatus_en LVCS_ReverbGeneratorInit(LVCS_Handle_t hInstance, |
| LVCS_Params_t *pParams) |
| { |
| |
| LVM_UINT16 Delay; |
| LVM_UINT16 Offset; |
| LVCS_Instance_t *pInstance = (LVCS_Instance_t *)hInstance; |
| LVCS_ReverbGenerator_t *pConfig = (LVCS_ReverbGenerator_t *)&pInstance->Reverberation; |
| LVCS_Data_t *pData = (LVCS_Data_t *)pInstance->MemoryTable.Region[LVCS_MEMREGION_PERSISTENT_FAST_DATA].pBaseAddress; |
| LVCS_Coefficient_t *pCoefficients = (LVCS_Coefficient_t *)pInstance->MemoryTable.Region[LVCS_MEMREGION_PERSISTENT_FAST_COEF].pBaseAddress; |
| BQ_C16_Coefs_t Coeffs; |
| const BiquadA012B12CoefsSP_t *pReverbCoefTable; |
| |
| /* |
| * Initialise the delay and filters if: |
| * - the sample rate has changed |
| * - the speaker type has changed to or from the mobile speaker |
| */ |
| if(pInstance->Params.SampleRate != pParams->SampleRate ) /* Sample rate change test */ |
| |
| { |
| /* |
| * Setup the delay |
| */ |
| Delay = (LVM_UINT16)LVCS_StereoDelayCS[(LVM_UINT16)pParams->SampleRate]; |
| |
| |
| pConfig->DelaySize = (LVM_INT16)(2 * Delay); |
| pConfig->DelayOffset = 0; |
| LoadConst_16(0, /* Value */ |
| (LVM_INT16 *)&pConfig->StereoSamples[0], /* Destination */ |
| (LVM_UINT16)(sizeof(pConfig->StereoSamples)/sizeof(LVM_INT16))); /* Number of words */ |
| |
| /* |
| * Setup the filters |
| */ |
| Offset = (LVM_UINT16)pParams->SampleRate; |
| pReverbCoefTable = (BiquadA012B12CoefsSP_t*)&LVCS_ReverbCoefTable[0]; |
| |
| /* Convert incoming coefficients to the required format/ordering */ |
| Coeffs.A0 = (LVM_INT16)pReverbCoefTable[Offset].A0; |
| Coeffs.A1 = (LVM_INT16)pReverbCoefTable[Offset].A1; |
| Coeffs.A2 = (LVM_INT16)pReverbCoefTable[Offset].A2; |
| Coeffs.B1 = (LVM_INT16)-pReverbCoefTable[Offset].B1; |
| Coeffs.B2 = (LVM_INT16)-pReverbCoefTable[Offset].B2; |
| |
| LoadConst_16(0, /* Value */ |
| (void *)&pData->ReverbBiquadTaps, /* Destination Cast to void: no dereferencing in function*/ |
| (LVM_UINT16)(sizeof(pData->ReverbBiquadTaps)/sizeof(LVM_INT16))); /* Number of words */ |
| |
| BQ_2I_D16F16Css_TRC_WRA_01_Init(&pCoefficients->ReverbBiquadInstance, |
| &pData->ReverbBiquadTaps, |
| &Coeffs); |
| |
| /* Callbacks */ |
| switch(pReverbCoefTable[Offset].Scale) |
| { |
| case 14: |
| pConfig->pBiquadCallBack = BQ_2I_D16F16C14_TRC_WRA_01; |
| break; |
| case 15: |
| pConfig->pBiquadCallBack = BQ_2I_D16F16C15_TRC_WRA_01; |
| break; |
| } |
| |
| |
| /* |
| * Setup the mixer |
| */ |
| pConfig->ProcGain = (LVM_UINT16)(HEADPHONEGAINPROC); |
| pConfig->UnprocGain = (LVM_UINT16)(HEADPHONEGAINUNPROC); |
| } |
| |
| if(pInstance->Params.ReverbLevel != pParams->ReverbLevel) |
| { |
| LVM_INT32 ReverbPercentage=83886; // 1 Percent Reverb i.e 1/100 in Q 23 format |
| ReverbPercentage*=pParams->ReverbLevel; // Actual Reverb Level in Q 23 format |
| pConfig->ReverbLevel=(LVM_INT16)(ReverbPercentage>>8); // Reverb Level in Q 15 format |
| } |
| |
| return(LVCS_SUCCESS); |
| } |
| |
| /************************************************************************************/ |
| /* */ |
| /* FUNCTION: LVCS_Reverb */ |
| /* */ |
| /* DESCRIPTION: */ |
| /* Create reverb using the block of input samples based on the following block */ |
| /* diagram: */ |
| /* ________ ________ */ |
| /* | | | | */ |
| /* _____ _______ | |----------->| | ______ ___ */ |
| /* | | | | | Stereo | | L & R | | | | | */ |
| /* -->| LPF |-->| Delay |-->| to | ____ | to |-->| Gain |-->| + |--> */ |
| /* | |_____| |_______| | L & R | | | | Stereo | |______| |___| */ |
| /* | | |-->| -1 |-->| | | */ |
| /* | |________| |____| |________| | */ |
| /* | | */ |
| /* |-----------------------------------------------------------------------| */ |
| /* */ |
| /* The input buffer is broken in to sub-blocks of the size of the delay or less. */ |
| /* This allows the delay buffer to be treated as a circular buffer but processed */ |
| /* as a linear buffer. */ |
| /* */ |
| /* */ |
| /* PARAMETERS: */ |
| /* hInstance Instance Handle */ |
| /* pInData Pointer to the input buffer */ |
| /* pOutData Pointer to the output buffer */ |
| /* NumSamples Number of samples to process */ |
| /* */ |
| /* RETURNS: */ |
| /* LVCS_Success Always succeeds */ |
| /* */ |
| /* NOTES: */ |
| /* 1. Process in blocks of samples the size of the delay where possible, if not */ |
| /* the number of samples left over */ |
| /* 2. The Gain is combined with the LPF and incorporated in to the coefficients */ |
| /* */ |
| /************************************************************************************/ |
| |
| LVCS_ReturnStatus_en LVCS_ReverbGenerator(LVCS_Handle_t hInstance, |
| const LVM_INT16 *pInData, |
| LVM_INT16 *pOutData, |
| LVM_UINT16 NumSamples) |
| { |
| |
| LVCS_Instance_t *pInstance = (LVCS_Instance_t *)hInstance; |
| LVCS_ReverbGenerator_t *pConfig = (LVCS_ReverbGenerator_t *)&pInstance->Reverberation; |
| LVCS_Coefficient_t *pCoefficients = (LVCS_Coefficient_t *)pInstance->MemoryTable.Region[LVCS_MEMREGION_PERSISTENT_FAST_COEF].pBaseAddress; |
| LVM_INT16 *pScratch = (LVM_INT16 *)pInstance->MemoryTable.Region[LVCS_MEMREGION_TEMPORARY_FAST].pBaseAddress; |
| |
| |
| /* |
| * Copy the data to the output in outplace processing |
| */ |
| if (pInData != pOutData) |
| { |
| /* |
| * Reverb not required so just copy the data |
| */ |
| Copy_16((LVM_INT16 *)pInData, /* Source */ |
| (LVM_INT16 *)pOutData, /* Destination */ |
| (LVM_INT16)(2*NumSamples)); /* Left and right */ |
| } |
| |
| |
| /* |
| * Check if the reverb is required |
| */ |
| if (((pInstance->Params.SpeakerType == LVCS_HEADPHONE) || /* Disable when CS4MS in stereo mode */ |
| (pInstance->Params.SpeakerType == LVCS_EX_HEADPHONES) || |
| (pInstance->Params.SourceFormat != LVCS_STEREO)) && |
| ((pInstance->Params.OperatingMode & LVCS_REVERBSWITCH) !=0)) /* For validation testing */ |
| { |
| /********************************************************************************/ |
| /* */ |
| /* Copy the input data to scratch memory and filter it */ |
| /* */ |
| /********************************************************************************/ |
| |
| /* |
| * Copy the input data to the scratch memory |
| */ |
| Copy_16((LVM_INT16 *)pInData, /* Source */ |
| (LVM_INT16 *)pScratch, /* Destination */ |
| (LVM_INT16)(2*NumSamples)); /* Left and right */ |
| |
| |
| /* |
| * Filter the data |
| */ |
| (pConfig->pBiquadCallBack)((Biquad_Instance_t*)&pCoefficients->ReverbBiquadInstance, |
| (LVM_INT16 *)pScratch, |
| (LVM_INT16 *)pScratch, |
| (LVM_INT16)NumSamples); |
| |
| Mult3s_16x16( (LVM_INT16 *)pScratch, |
| pConfig->ReverbLevel, |
| (LVM_INT16 *)pScratch, |
| (LVM_INT16)(2*NumSamples)); |
| |
| |
| /* |
| * Apply the delay mix |
| */ |
| DelayMix_16x16((LVM_INT16 *)pScratch, |
| &pConfig->StereoSamples[0], |
| pConfig->DelaySize, |
| pOutData, |
| &pConfig->DelayOffset, |
| (LVM_INT16)NumSamples); |
| |
| |
| } |
| |
| return(LVCS_SUCCESS); |
| } |
| |
| |
| |
| |
| |