blob: 5d9946181843ba9fdc8d143a34ad59cf19385515 [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.
*/
/************************************************************************************/
/* */
/* 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);
}