/* * 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 "VectorArithmetic.h" #include "CompLim.h" /************************************************************************************/ /* */ /* FUNCTION: LVCS_Process_CS */ /* */ /* DESCRIPTION: */ /* Process function for the Concert Sound module based on the following block */ /* diagram: */ /* _________ ________ _____ _______ ___ ______ */ /* | | | | | | | | | | | | */ /* ----->| Stereo |->| Reverb |->| Equ |->| Alpha |-->| + |-| Gain |----> */ /* | | Enhance | |________| |_____| |_______| |___| |______| */ /* | |_________| | */ /* | ___________ | */ /* | | | | */ /* |------------------------------->| 1 - Alpha |-----| */ /* |___________| */ /* */ /* The Stereo Enhancer, Reverb and Equaliser blocks are each configured to have */ /* their gain to give a near peak to peak output (-0.1dBFS) with a worst case */ /* input signal. The gains of these blocks are re-combined in the Alpha mixer and */ /* the gain block folloing the sum. */ /* */ /* The processing uses the output buffer for data storage after each processing */ /* block. When processing is inplace a copy of the input signal is made in scratch */ /* memory for the 1-Alpha path. */ /* */ /* */ /* PARAMETERS: */ /* hInstance Instance handle */ /* pInData Pointer to the input data */ /* pOutData Pointer to the output data */ /* NumSamples Number of samples in the input buffer */ /* */ /* RETURNS: */ /* LVCS_Success Succeeded */ /* */ /* NOTES: */ /* */ /************************************************************************************/ LVCS_ReturnStatus_en LVCS_Process_CS(LVCS_Handle_t hInstance, const LVM_INT16 *pInData, LVM_INT16 *pOutData, LVM_UINT16 NumSamples) { const LVM_INT16 *pInput; LVCS_Instance_t *pInstance = (LVCS_Instance_t *)hInstance; LVM_INT16 *pScratch = (LVM_INT16 *)pInstance->MemoryTable.Region[LVCS_MEMREGION_TEMPORARY_FAST].pBaseAddress; LVCS_ReturnStatus_en err; /* * Check if the processing is inplace */ if (pInData == pOutData) { /* Processing inplace */ pInput = pScratch + (2*NumSamples); Copy_16((LVM_INT16 *)pInData, /* Source */ (LVM_INT16 *)pInput, /* Destination */ (LVM_INT16)(2*NumSamples)); /* Left and right */ } else { /* Processing outplace */ pInput = pInData; } /* * Call the stereo enhancer */ err=LVCS_StereoEnhancer(hInstance, /* Instance handle */ pInData, /* Pointer to the input data */ pOutData, /* Pointer to the output data */ NumSamples); /* Number of samples to process */ /* * Call the reverb generator */ err=LVCS_ReverbGenerator(hInstance, /* Instance handle */ pOutData, /* Pointer to the input data */ pOutData, /* Pointer to the output data */ NumSamples); /* Number of samples to process */ /* * Call the equaliser */ err=LVCS_Equaliser(hInstance, /* Instance handle */ pOutData, /* Pointer to the input data */ NumSamples); /* Number of samples to process */ /* * Call the bypass mixer */ err=LVCS_BypassMixer(hInstance, /* Instance handle */ pOutData, /* Pointer to the processed data */ pInput, /* Pointer to the input (unprocessed) data */ pOutData, /* Pointer to the output data */ NumSamples); /* Number of samples to process */ if(err !=LVCS_SUCCESS) { return err; } return(LVCS_SUCCESS); } /************************************************************************************/ /* */ /* FUNCTION: LVCS_Process */ /* */ /* DESCRIPTION: */ /* Process function for the Concert Sound module. The implementation supports two */ /* variants of the algorithm, one for headphones and one for mobile speakers. */ /* */ /* Data can be processed in two formats, stereo or mono-in-stereo. Data in mono */ /* format is not supported, the calling routine must convert the mono stream to */ /* mono-in-stereo. */ /* */ /* */ /* PARAMETERS: */ /* hInstance Instance handle */ /* pInData Pointer to the input data */ /* pOutData Pointer to the output data */ /* NumSamples Number of samples in the input buffer */ /* */ /* RETURNS: */ /* LVCS_Success Succeeded */ /* LVCS_TooManySamples NumSamples was larger than the maximum block size */ /* */ /* NOTES: */ /* */ /************************************************************************************/ LVCS_ReturnStatus_en LVCS_Process(LVCS_Handle_t hInstance, const LVM_INT16 *pInData, LVM_INT16 *pOutData, LVM_UINT16 NumSamples) { LVCS_Instance_t *pInstance =(LVCS_Instance_t *)hInstance; LVCS_ReturnStatus_en err; /* * Check the number of samples is not too large */ if (NumSamples > pInstance->Capabilities.MaxBlockSize) { return(LVCS_TOOMANYSAMPLES); } /* * Check if the algorithm is enabled */ if (pInstance->Params.OperatingMode != LVCS_OFF) { /* * Call CS process function */ err=LVCS_Process_CS(hInstance, pInData, pOutData, NumSamples); /* * Compress to reduce expansion effect of Concert Sound and correct volume * differences for difference settings. Not applied in test modes */ if ((pInstance->Params.OperatingMode == LVCS_ON)&&(pInstance->Params.CompressorMode == LVM_MODE_ON)) { LVM_INT16 Gain = pInstance->VolCorrect.CompMin; LVM_INT32 Current1; Current1 = LVC_Mixer_GetCurrent(&pInstance->BypassMix.Mixer_Instance.MixerStream[0]); Gain = (LVM_INT16)( pInstance->VolCorrect.CompMin - (((LVM_INT32)pInstance->VolCorrect.CompMin * (Current1)) >> 15) + (((LVM_INT32)pInstance->VolCorrect.CompFull * (Current1)) >> 15) ); if(NumSamples < LVCS_COMPGAINFRAME) { NonLinComp_D16(Gain, /* Compressor gain setting */ pOutData, pOutData, (LVM_INT32)(2*NumSamples)); } else { LVM_INT16 GainStep; LVM_INT16 FinalGain; LVM_INT16 SampleToProcess = NumSamples; LVM_INT16 *pOutPtr; /* Large changes in Gain can cause clicks in output Split data into small blocks and use interpolated gain values */ GainStep = (LVM_INT16)(((Gain-pInstance->CompressGain) * LVCS_COMPGAINFRAME)/NumSamples); if((GainStep ==0)&&(pInstance->CompressGain < Gain)) { GainStep=1; } else { if((GainStep ==0)&&(pInstance->CompressGain > Gain)) { GainStep=-1; } } FinalGain = Gain; Gain = pInstance->CompressGain; pOutPtr = pOutData; while(SampleToProcess > 0) { Gain = (LVM_INT16)(Gain + GainStep); if((GainStep > 0)&& (FinalGain <= Gain)) { Gain = FinalGain; GainStep =0; } if((GainStep < 0)&& (FinalGain > Gain)) { Gain = FinalGain; GainStep =0; } if(SampleToProcess > LVCS_COMPGAINFRAME) { NonLinComp_D16(Gain, /* Compressor gain setting */ pOutPtr, pOutPtr, (LVM_INT32)(2*LVCS_COMPGAINFRAME)); pOutPtr +=(2*LVCS_COMPGAINFRAME); SampleToProcess = (LVM_INT16)(SampleToProcess-LVCS_COMPGAINFRAME); } else { NonLinComp_D16(Gain, /* Compressor gain setting */ pOutPtr, pOutPtr, (LVM_INT32)(2*SampleToProcess)); SampleToProcess = 0; } } } /* Store gain value*/ pInstance->CompressGain = Gain; } if(pInstance->bInOperatingModeTransition == LVM_TRUE){ /* * Re-init bypass mix when timer has completed */ if ((pInstance->bTimerDone == LVM_TRUE) && (pInstance->BypassMix.Mixer_Instance.MixerStream[1].CallbackSet == 0)) { err=LVCS_BypassMixInit(hInstance, &pInstance->Params); if(err != LVCS_SUCCESS) { return err; } } else{ LVM_Timer ( &pInstance->TimerInstance, (LVM_INT16)NumSamples); } } } else { if (pInData != pOutData) { /* * The algorithm is disabled so just copy the data */ Copy_16((LVM_INT16 *)pInData, /* Source */ (LVM_INT16 *)pOutData, /* Destination */ (LVM_INT16)(2*NumSamples)); /* Left and right */ } } return(LVCS_SUCCESS); }