blob: 5c8655fc6a8549e0f9f50dfe6af19c0079a05fd1 [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 "AGC.h"
#include "ScalarArithmetic.h"
/****************************************************************************************/
/* */
/* Defines */
/* */
/****************************************************************************************/
#define VOL_TC_SHIFT 21 /* As a power of 2 */
#define DECAY_SHIFT 10 /* As a power of 2 */
#ifdef BUILD_FLOAT
#define VOL_TC_FLOAT 2.0f /* As a power of 2 */
#define DECAY_FAC_FLOAT 64.0f /* As a power of 2 */
#endif
/****************************************************************************************/
/* */
/* FUNCTION: AGC_MIX_VOL_2St1Mon_D32_WRA */
/* */
/* DESCRIPTION: */
/* Apply AGC and mix signals */
/* */
/* */
/* StSrc ------------------| */
/* | */
/* ______ _|_ ________ */
/* | | | | | | */
/* MonoSrc -->| AGC |---->| + |----->| Volume |------------------------------+---> */
/* | Gain | |___| | Gain | | */
/* |______| |________| | */
/* /|\ __________ ________ | */
/* | | | | | | */
/* |-------------------------------| AGC Gain |<--| Peak |<--| */
/* | Update | | Detect | */
/* |__________| |________| */
/* */
/* */
/* PARAMETERS: */
/* pInstance Instance pointer */
/* pStereoIn Stereo source */
/* pMonoIn Mono band pass source */
/* pStereoOut Stereo destination */
/* */
/* RETURNS: */
/* Void */
/* */
/* NOTES: */
/* */
/****************************************************************************************/
#ifndef BUILD_FLOAT
void AGC_MIX_VOL_2St1Mon_D32_WRA(AGC_MIX_VOL_2St1Mon_D32_t *pInstance, /* Instance pointer */
const LVM_INT32 *pStSrc, /* Stereo source */
const LVM_INT32 *pMonoSrc, /* Mono source */
LVM_INT32 *pDst, /* Stereo destination */
LVM_UINT16 NumSamples) /* Number of samples */
{
/*
* General variables
*/
LVM_UINT16 i; /* Sample index */
LVM_INT32 Left; /* Left sample */
LVM_INT32 Right; /* Right sample */
LVM_INT32 Mono; /* Mono sample */
LVM_INT32 AbsPeak; /* Absolute peak signal */
LVM_INT32 HighWord; /* High word in intermediate calculations */
LVM_INT32 LowWord; /* Low word in intermediate calculations */
LVM_INT16 AGC_Mult; /* Short AGC gain */
LVM_INT16 Vol_Mult; /* Short volume */
/*
* Instance control variables
*/
LVM_INT32 AGC_Gain = pInstance->AGC_Gain; /* Get the current AGC gain */
LVM_INT32 AGC_MaxGain = pInstance->AGC_MaxGain; /* Get maximum AGC gain */
LVM_INT16 AGC_GainShift = pInstance->AGC_GainShift; /* Get the AGC shift */
LVM_INT16 AGC_Attack = pInstance->AGC_Attack; /* Attack scaler */
LVM_INT16 AGC_Decay = pInstance->AGC_Decay; /* Decay scaler */
LVM_INT32 AGC_Target = pInstance->AGC_Target; /* Get the target level */
LVM_INT32 Vol_Current = pInstance->Volume; /* Actual volume setting */
LVM_INT32 Vol_Target = pInstance->Target; /* Target volume setting */
LVM_INT16 Vol_Shift = pInstance->VolumeShift; /* Volume shift scaling */
LVM_INT16 Vol_TC = pInstance->VolumeTC; /* Time constant */
/*
* Process on a sample by sample basis
*/
for (i=0;i<NumSamples;i++) /* For each sample */
{
/*
* Get the short scalers
*/
AGC_Mult = (LVM_INT16)(AGC_Gain >> 16); /* Get the short AGC gain */
Vol_Mult = (LVM_INT16)(Vol_Current >> 16); /* Get the short volume gain */
/*
* Get the input samples
*/
Left = *pStSrc++; /* Get the left sample */
Right = *pStSrc++; /* Get the right sample */
Mono = *pMonoSrc++; /* Get the mono sample */
/*
* Apply the AGC gain to the mono input and mix with the stereo signal
*/
HighWord = (AGC_Mult * (Mono >> 16)); /* signed long (Mono) by unsigned short (AGC_Mult) multiply */
LowWord = (AGC_Mult * (Mono & 0xffff));
Mono = (HighWord + (LowWord >> 16)) << (AGC_GainShift);
Left += Mono; /* Mix in the mono signal */
Right += Mono;
/*
* Apply the volume and write to the output stream
*/
HighWord = (Vol_Mult * (Left >> 16)); /* signed long (Left) by unsigned short (Vol_Mult) multiply */
LowWord = (Vol_Mult * (Left & 0xffff));
Left = (HighWord + (LowWord >> 16)) << (Vol_Shift);
HighWord = (Vol_Mult * (Right >> 16)); /* signed long (Right) by unsigned short (Vol_Mult) multiply */
LowWord = (Vol_Mult * (Right & 0xffff));
Right = (HighWord + (LowWord >> 16)) << (Vol_Shift);
*pDst++ = Left; /* Save the results */
*pDst++ = Right;
/*
* Update the AGC gain
*/
AbsPeak = (Abs_32(Left)>Abs_32(Right)) ? Abs_32(Left) : Abs_32(Right); /* Get the absolute peak */
if (AbsPeak > AGC_Target)
{
/*
* The signal is too large so decrease the gain
*/
HighWord = (AGC_Attack * (AGC_Gain >> 16)); /* signed long (AGC_Gain) by unsigned short (AGC_Attack) multiply */
LowWord = (AGC_Attack * (AGC_Gain & 0xffff));
AGC_Gain = (HighWord + (LowWord >> 16)) << 1;
}
else
{
/*
* The signal is too small so increase the gain
*/
if (AGC_Gain > AGC_MaxGain)
{
AGC_Gain -= (AGC_Decay << DECAY_SHIFT);
}
else
{
AGC_Gain += (AGC_Decay << DECAY_SHIFT);
}
}
/*
* Update the gain
*/
Vol_Current += Vol_TC * ((Vol_Target - Vol_Current) >> VOL_TC_SHIFT);
}
/*
* Update the parameters
*/
pInstance->Volume = Vol_Current; /* Actual volume setting */
pInstance->AGC_Gain = AGC_Gain;
return;
}
#else
void AGC_MIX_VOL_2St1Mon_D32_WRA(AGC_MIX_VOL_2St1Mon_FLOAT_t *pInstance, /* Instance pointer */
const LVM_FLOAT *pStSrc, /* Stereo source */
const LVM_FLOAT *pMonoSrc, /* Mono source */
LVM_FLOAT *pDst, /* Stereo destination */
LVM_UINT16 NumSamples) /* Number of samples */
{
/*
* General variables
*/
LVM_UINT16 i; /* Sample index */
LVM_FLOAT Left; /* Left sample */
LVM_FLOAT Right; /* Right sample */
LVM_FLOAT Mono; /* Mono sample */
LVM_FLOAT AbsPeak; /* Absolute peak signal */
LVM_FLOAT AGC_Mult; /* Short AGC gain */
LVM_FLOAT Vol_Mult; /* Short volume */
/*
* Instance control variables
*/
LVM_FLOAT AGC_Gain = pInstance->AGC_Gain; /* Get the current AGC gain */
LVM_FLOAT AGC_MaxGain = pInstance->AGC_MaxGain; /* Get maximum AGC gain */
LVM_FLOAT AGC_Attack = pInstance->AGC_Attack; /* Attack scaler */
LVM_FLOAT AGC_Decay = (pInstance->AGC_Decay * (1 << (DECAY_SHIFT)));/* Decay scaler */
LVM_FLOAT AGC_Target = pInstance->AGC_Target; /* Get the target level */
LVM_FLOAT Vol_Current = pInstance->Volume; /* Actual volume setting */
LVM_FLOAT Vol_Target = pInstance->Target; /* Target volume setting */
LVM_FLOAT Vol_TC = pInstance->VolumeTC; /* Time constant */
/*
* Process on a sample by sample basis
*/
for (i = 0; i < NumSamples; i++) /* For each sample */
{
/*
* Get the short scalers
*/
AGC_Mult = (LVM_FLOAT)(AGC_Gain); /* Get the short AGC gain */
Vol_Mult = (LVM_FLOAT)(Vol_Current); /* Get the short volume gain */
/*
* Get the input samples
*/
Left = *pStSrc++; /* Get the left sample */
Right = *pStSrc++; /* Get the right sample */
Mono = *pMonoSrc++; /* Get the mono sample */
/*
* Apply the AGC gain to the mono input and mix with the stereo signal
*/
Left += (Mono * AGC_Mult); /* Mix in the mono signal */
Right += (Mono * AGC_Mult);
/*
* Apply the volume and write to the output stream
*/
Left = Left * Vol_Mult;
Right = Right * Vol_Mult;
*pDst++ = Left; /* Save the results */
*pDst++ = Right;
/*
* Update the AGC gain
*/
AbsPeak = Abs_Float(Left) > Abs_Float(Right) ? Abs_Float(Left) : Abs_Float(Right);
if (AbsPeak > AGC_Target)
{
/*
* The signal is too large so decrease the gain
*/
AGC_Gain = AGC_Gain * AGC_Attack;
}
else
{
/*
* The signal is too small so increase the gain
*/
if (AGC_Gain > AGC_MaxGain)
{
AGC_Gain -= (AGC_Decay);
}
else
{
AGC_Gain += (AGC_Decay);
}
}
/*
* Update the gain
*/
Vol_Current += (Vol_Target - Vol_Current) * ((LVM_FLOAT)Vol_TC / VOL_TC_FLOAT);
}
/*
* Update the parameters
*/
pInstance->Volume = Vol_Current; /* Actual volume setting */
pInstance->AGC_Gain = AGC_Gain;
return;
}
#ifdef SUPPORT_MC
/****************************************************************************************/
/* */
/* FUNCTION: AGC_MIX_VOL_Mc1Mon_D32_WRA */
/* */
/* DESCRIPTION: */
/* Apply AGC and mix signals */
/* */
/* */
/* McSrc ------------------| */
/* | */
/* ______ _|_ ________ */
/* | | | | | | */
/* MonoSrc -->| AGC |---->| + |----->| Volume |------------------------------+---> */
/* | Gain | |___| | Gain | | */
/* |______| |________| | */
/* /|\ __________ ________ | */
/* | | | | | | */
/* |-------------------------------| AGC Gain |<--| Peak |<--| */
/* | Update | | Detect | */
/* |__________| |________| */
/* */
/* */
/* PARAMETERS: */
/* pInstance Instance pointer */
/* pMcSrc Multichannel source */
/* pMonoSrc Mono band pass source */
/* pDst Multichannel destination */
/* NrFrames Number of frames */
/* NrChannels Number of channels */
/* */
/* RETURNS: */
/* Void */
/* */
/* NOTES: */
/* */
/****************************************************************************************/
void AGC_MIX_VOL_Mc1Mon_D32_WRA(AGC_MIX_VOL_2St1Mon_FLOAT_t *pInstance,
const LVM_FLOAT *pMcSrc,
const LVM_FLOAT *pMonoSrc,
LVM_FLOAT *pDst,
LVM_UINT16 NrFrames,
LVM_UINT16 NrChannels)
{
/*
* General variables
*/
LVM_UINT16 i, jj; /* Sample index */
LVM_FLOAT SampleVal; /* Sample value */
LVM_FLOAT Mono; /* Mono sample */
LVM_FLOAT AbsPeak; /* Absolute peak signal */
LVM_FLOAT AGC_Mult; /* Short AGC gain */
LVM_FLOAT Vol_Mult; /* Short volume */
/*
* Instance control variables
*/
LVM_FLOAT AGC_Gain = pInstance->AGC_Gain; /* Get the current AGC gain */
LVM_FLOAT AGC_MaxGain = pInstance->AGC_MaxGain; /* Get maximum AGC gain */
LVM_FLOAT AGC_Attack = pInstance->AGC_Attack; /* Attack scaler */
/* Decay scaler */
LVM_FLOAT AGC_Decay = (pInstance->AGC_Decay * (1 << (DECAY_SHIFT)));
LVM_FLOAT AGC_Target = pInstance->AGC_Target; /* Get the target level */
LVM_FLOAT Vol_Current = pInstance->Volume; /* Actual volume setting */
LVM_FLOAT Vol_Target = pInstance->Target; /* Target volume setting */
LVM_FLOAT Vol_TC = pInstance->VolumeTC; /* Time constant */
/*
* Process on a sample by sample basis
*/
for (i = 0; i < NrFrames; i++) /* For each frame */
{
/*
* Get the scalers
*/
AGC_Mult = (LVM_FLOAT)(AGC_Gain); /* Get the AGC gain */
Vol_Mult = (LVM_FLOAT)(Vol_Current); /* Get the volume gain */
AbsPeak = 0.0f;
/*
* Get the input samples
*/
for (jj = 0; jj < NrChannels; jj++)
{
SampleVal = *pMcSrc++; /* Get the sample value of jj Channel*/
Mono = *pMonoSrc; /* Get the mono sample */
/*
* Apply the AGC gain to the mono input and mix with the input signal
*/
SampleVal += (Mono * AGC_Mult); /* Mix in the mono signal */
/*
* Apply the volume and write to the output stream
*/
SampleVal = SampleVal * Vol_Mult;
*pDst++ = SampleVal; /* Save the results */
/*
* Update the AGC gain
*/
AbsPeak = Abs_Float(SampleVal) > AbsPeak ? Abs_Float(SampleVal) : AbsPeak;
}
if (AbsPeak > AGC_Target)
{
/*
* The signal is too large so decrease the gain
*/
AGC_Gain = AGC_Gain * AGC_Attack;
}
else
{
/*
* The signal is too small so increase the gain
*/
if (AGC_Gain > AGC_MaxGain)
{
AGC_Gain -= (AGC_Decay);
}
else
{
AGC_Gain += (AGC_Decay);
}
}
pMonoSrc++;
/*
* Update the gain
*/
Vol_Current += (Vol_Target - Vol_Current) * ((LVM_FLOAT)Vol_TC / VOL_TC_FLOAT);
}
/*
* Update the parameters
*/
pInstance->Volume = Vol_Current; /* Actual volume setting */
pInstance->AGC_Gain = AGC_Gain;
return;
}
#endif /*SUPPORT_MC*/
#endif /*BUILD_FLOAT*/