summaryrefslogtreecommitdiffstats
path: root/arm-hybrid-22k/lib_src/eas_fmengine.c
diff options
context:
space:
mode:
Diffstat (limited to 'arm-hybrid-22k/lib_src/eas_fmengine.c')
-rw-r--r--arm-hybrid-22k/lib_src/eas_fmengine.c1546
1 files changed, 773 insertions, 773 deletions
diff --git a/arm-hybrid-22k/lib_src/eas_fmengine.c b/arm-hybrid-22k/lib_src/eas_fmengine.c
index 9c3da66..ea7f69c 100644
--- a/arm-hybrid-22k/lib_src/eas_fmengine.c
+++ b/arm-hybrid-22k/lib_src/eas_fmengine.c
@@ -1,12 +1,12 @@
-/*----------------------------------------------------------------------------
- *
- * File:
- * eas_fmengine.c
- *
- * Contents and purpose:
- * Implements the low-level FM synthesizer functions.
- *
- * Copyright Sonic Network Inc. 2004, 2005
+/*----------------------------------------------------------------------------
+ *
+ * File:
+ * eas_fmengine.c
+ *
+ * Contents and purpose:
+ * Implements the low-level FM synthesizer functions.
+ *
+ * Copyright Sonic Network Inc. 2004, 2005
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,767 +19,767 @@
* 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.
- *
- *----------------------------------------------------------------------------
- * Revision Control:
- * $Revision: 795 $
- * $Date: 2007-08-01 00:14:45 -0700 (Wed, 01 Aug 2007) $
- *----------------------------------------------------------------------------
-*/
-
-/* includes */
-#include "eas_types.h"
-#include "eas_math.h"
-#include "eas_audioconst.h"
-#include "eas_fmengine.h"
-
-#if defined(EAS_FM_SYNTH) || defined(EAS_HYBRID_SYNTH) || defined(EAS_SPLIT_HYBRID_SYNTH) || defined(EAS_SPLIT_FM_SYNTH)
-#include "eas_data.h"
-#endif
-
-/* externals */
-extern const EAS_I16 sineTable[];
-extern const EAS_U8 fmScaleTable[16];
-
-// saturation constants for 32-bit to 16-bit conversion
-#define _EAS_MAX_OUTPUT 32767
-#define _EAS_MIN_OUTPUT -32767
-
-static S_FM_ENG_VOICE voices[NUM_FM_VOICES];
-
-/* local prototypes */
-void FM_SynthMixVoice (S_FM_ENG_VOICE *p, EAS_U16 gainTarget, EAS_I32 numSamplesToAdd, EAS_PCM *pInputBuffer, EAS_I32 *pBuffer);
-
-/* used in development environment */
-#if defined(_SATURATION_MONITOR)
-static EAS_BOOL bSaturated = EAS_FALSE;
-
-/*----------------------------------------------------------------------------
- * FM_CheckSaturation()
- *----------------------------------------------------------------------------
- * Purpose:
- * Allows the sound development tool to check for saturation at the voice
- * level. Useful for tuning the level controls.
- *
- * Inputs:
- *
- * Outputs:
- * Returns true if saturation has occurred since the last time the function
- * was called.
- *
- * Side Effects:
- * Resets the saturation flag
- *----------------------------------------------------------------------------
-*/
-EAS_BOOL FM_CheckSaturation ()
-{
- EAS_BOOL bTemp;
- bTemp = bSaturated;
- bSaturated = EAS_FALSE;
- return bTemp;
-}
-#endif
-
-/*----------------------------------------------------------------------------
- * FM_Saturate()
- *----------------------------------------------------------------------------
- * Purpose:
- * This inline function saturates a 32-bit number to 16-bits
- *
- * Inputs:
- * psEASData - pointer to overall EAS data structure
- *
- * Outputs:
- * Returns a 16-bit integer
- *----------------------------------------------------------------------------
-*/
-EAS_INLINE EAS_I16 FM_Saturate (EAS_I32 nValue)
-{
- if (nValue > _EAS_MAX_OUTPUT)
- {
-#if defined(_SATURATION_MONITOR)
- bSaturated = EAS_TRUE;
-#endif
- return _EAS_MAX_OUTPUT;
- }
- if (nValue < _EAS_MIN_OUTPUT)
- {
-#if defined(_SATURATION_MONITOR)
- bSaturated = EAS_TRUE;
-#endif
- return _EAS_MIN_OUTPUT;
- }
- return (EAS_I16) nValue;
-}
-
-/*----------------------------------------------------------------------------
- * FM_Noise()
- *----------------------------------------------------------------------------
- * Purpose:
- * A 31-bit low-cost linear congruential PRNG algorithm used to
- * generate noise.
- *
- * Inputs:
- * pnSeed - pointer to 32-bit PRNG seed
- *
- * Outputs:
- * Returns a 16-bit integer
- *----------------------------------------------------------------------------
-*/
-EAS_INLINE EAS_I16 FM_Noise (EAS_U32 *pnSeed)
-{
- *pnSeed = *pnSeed * 214013L + 2531011L;
- return (EAS_I16) ((*pnSeed >> 15) & 0xffff);
-}
-
-/*----------------------------------------------------------------------------
- * FM_PhaseInc()
- *----------------------------------------------------------------------------
- * Purpose:
- * Transform pitch cents to linear phase increment
- *
- * Inputs:
- * nCents - measured in cents
- * psEASData - pointer to overall EAS data structure
- *
- * Outputs:
- * nResult - int.frac result (where frac has NUM_DENTS_FRAC_BITS)
- *
- * Side Effects:
- *
- *----------------------------------------------------------------------------
-*/
-static EAS_I32 FM_PhaseInc (EAS_I32 nCents)
-{
- EAS_I32 nDents;
- EAS_I32 nExponentInt, nExponentFrac;
- EAS_I32 nTemp1, nTemp2;
- EAS_I32 nResult;
-
- /* convert cents to dents */
- nDents = FMUL_15x15(nCents, CENTS_TO_DENTS);
- nExponentInt = GET_DENTS_INT_PART(nDents) + (32 - SINE_TABLE_SIZE_IN_BITS - NUM_EG1_FRAC_BITS);
- nExponentFrac = GET_DENTS_FRAC_PART(nDents);
-
- /* implement 2^(fracPart) as a power series */
- nTemp1 = GN2_TO_X2 + MULT_DENTS_COEF(nExponentFrac, GN2_TO_X3);
- nTemp2 = GN2_TO_X1 + MULT_DENTS_COEF(nExponentFrac, nTemp1);
- nTemp1 = GN2_TO_X0 + MULT_DENTS_COEF(nExponentFrac, nTemp2);
-
- /*
- implement 2^(intPart) as
- a left shift for intPart >= 0 or
- a left shift for intPart < 0
- */
- if (nExponentInt >= 0)
- {
- /* left shift for positive exponents */
- /*lint -e{703} <avoid multiply for performance>*/
- nResult = nTemp1 << nExponentInt;
- }
- else
- {
- /* right shift for negative exponents */
- nExponentInt = -nExponentInt;
- nResult = nTemp1 >> nExponentInt;
- }
-
- return nResult;
-}
-
-#if (NUM_OUTPUT_CHANNELS == 2)
-/*----------------------------------------------------------------------------
- * FM_CalculatePan()
- *----------------------------------------------------------------------------
- * Purpose:
- * Assign the left and right gain values corresponding to the given pan value.
- *
- * Inputs:
- * psVoice - ptr to the voice we have assigned for this channel
- * psArticulation - ptr to this voice's articulation
- * psEASData - pointer to overall EAS data structure
- *
- * Outputs:
- *
- * Side Effects:
- * the given voice's m_nGainLeft and m_nGainRight are assigned
- *----------------------------------------------------------------------------
-*/
-static void FM_CalculatePan (EAS_I16 pan, EAS_U16 *pGainLeft, EAS_U16 *pGainRight)
-{
- EAS_I32 nTemp;
- EAS_INT nNetAngle;
-
- /*
- Implement the following
- sin(x) = (2-4*c)*x^2 + c + x
- cos(x) = (2-4*c)*x^2 + c - x
-
- where c = 1/sqrt(2)
- using the a0 + x*(a1 + x*a2) approach
- */
-
- /*
- Get the Midi CC10 pan value for this voice's channel
- convert the pan value to an "angle" representation suitable for
- our sin, cos calculator. This representation is NOT necessarily the same
- as the transform in the GM manuals because of our sin, cos calculator.
- "angle" = (CC10 - 64)/128
- */
- /*lint -e{703} <avoid multiply for performance reasons>*/
- nNetAngle = ((EAS_I32) pan) << (NUM_EG1_FRAC_BITS -7);
-
- /* calculate sin */
- nTemp = EG1_ONE + FMUL_15x15(COEFF_PAN_G2, nNetAngle);
- nTemp = COEFF_PAN_G0 + FMUL_15x15(nTemp, nNetAngle);
-
- if (nTemp > SYNTH_FULL_SCALE_EG1_GAIN)
- nTemp = SYNTH_FULL_SCALE_EG1_GAIN;
- else if (nTemp < 0)
- nTemp = 0;
-
- *pGainRight = (EAS_U16) nTemp;
-
- /* calculate cos */
- nTemp = -EG1_ONE + FMUL_15x15(COEFF_PAN_G2, nNetAngle);
- nTemp = COEFF_PAN_G0 + FMUL_15x15(nTemp, nNetAngle);
-
- if (nTemp > SYNTH_FULL_SCALE_EG1_GAIN)
- nTemp = SYNTH_FULL_SCALE_EG1_GAIN;
- else if (nTemp < 0)
- nTemp = 0;
-
- *pGainLeft = (EAS_U16) nTemp;
-}
-#endif /* #if (NUM_OUTPUT_CHANNELS == 2) */
-
-/*----------------------------------------------------------------------------
- * FM_Operator()
- *----------------------------------------------------------------------------
- * Purpose:
- * Synthesizes a buffer of samples based on passed parameters.
- *
- * Inputs:
- * nNumSamplesToAdd - number of samples to synthesize
- * psEASData - pointer to overall EAS data structure
- *
- * Outputs:
- *
- * Side Effects:
- *
- *----------------------------------------------------------------------------
-*/
-void FM_Operator (
- S_FM_ENG_OPER *p,
- EAS_I32 numSamplesToAdd,
- EAS_PCM *pBuffer,
- EAS_PCM *pModBuffer,
- EAS_BOOL mix,
- EAS_U16 gainTarget,
- EAS_I16 pitch,
- EAS_U8 feedback,
- EAS_I16 *pLastOutput)
-{
- EAS_I32 gain;
- EAS_I32 gainInc;
- EAS_U32 phase;
- EAS_U32 phaseInc;
- EAS_U32 phaseTemp;
- EAS_I32 temp;
- EAS_I32 temp2;
-
- /* establish local gain variable */
- gain = (EAS_I32) p->gain << 16;
-
- /* calculate gain increment */
- /*lint -e{703} use shift for performance */
- gainInc = ((EAS_I32) gainTarget - (EAS_I32) p->gain) << (16 - SYNTH_UPDATE_PERIOD_IN_BITS);
-
- /* establish local phase variables */
- phase = p->phase;
-
- /* calculate the new phase increment */
- phaseInc = (EAS_U32) FM_PhaseInc(pitch);
-
- /* restore final output from previous frame for feedback loop */
- if (pLastOutput)
- temp = *pLastOutput;
- else
- temp = 0;
-
- /* generate a buffer of samples */
- while (numSamplesToAdd--)
- {
-
- /* incorporate modulation */
- if (pModBuffer)
- {
- /*lint -e{701} use shift for performance */
- temp = *pModBuffer++ << FM_MODULATOR_INPUT_SHIFT;
- }
-
- /* incorporate feedback */
- else
- {
- /*lint -e{703} use shift for performance */
- temp = (temp * (EAS_I32) feedback) << FM_FEEDBACK_SHIFT;
- }
-
- /*lint -e{737} <use this behavior to avoid extra mask step> */
- phaseTemp = phase + temp;
-
- /* fetch sample from wavetable */
- temp = sineTable[phaseTemp >> (32 - SINE_TABLE_SIZE_IN_BITS)];
-
- /* increment operator phase */
- phase += phaseInc;
-
- /* internal gain for modulation effects */
- temp = FMUL_15x15(temp, (gain >> 16));
-
- /* output gain calculation */
- temp2 = FMUL_15x15(temp, p->outputGain);
-
- /* saturating add to buffer */
- if (mix)
- {
- temp2 += *pBuffer;
- *pBuffer++ = FM_Saturate(temp2);
- }
-
- /* output to buffer */
- else
- *pBuffer++ = (EAS_I16) temp2;
-
- /* increment gain */
- gain += gainInc;
-
- }
-
- /* save phase and gain */
- p->phase = phase;
- p->gain = gainTarget;
-
- /* save last output for feedback in next frame */
- if (pLastOutput)
- *pLastOutput = (EAS_I16) temp;
-}
-
-/*----------------------------------------------------------------------------
- * FM_NoiseOperator()
- *----------------------------------------------------------------------------
- * Purpose:
- * Synthesizes a buffer of samples based on passed parameters.
- *
- * Inputs:
- * nNumSamplesToAdd - number of samples to synthesize
- * psEASData - pointer to overall EAS data structure
- *
- * Outputs:
- *
- * Side Effects:
- *
- *----------------------------------------------------------------------------
-*/
-void FM_NoiseOperator (
- S_FM_ENG_OPER *p,
- EAS_I32 numSamplesToAdd,
- EAS_PCM *pBuffer,
- EAS_BOOL mix,
- EAS_U16 gainTarget,
- EAS_U8 feedback,
- EAS_I16 *pLastOutput)
-{
- EAS_I32 gain;
- EAS_I32 gainInc;
- EAS_U32 phase;
- EAS_I32 temp;
- EAS_I32 temp2;
-
- /* establish local gain variable */
- gain = (EAS_I32) p->gain << 16;
-
- /* calculate gain increment */
- /*lint -e{703} use shift for performance */
- gainInc = ((EAS_I32) gainTarget - (EAS_I32) p->gain) << (16 - SYNTH_UPDATE_PERIOD_IN_BITS);
-
- /* establish local phase variables */
- phase = p->phase;
-
- /* establish local phase variables */
- phase = p->phase;
-
- /* recall last sample for filter Z-1 term */
- temp = 0;
- if (pLastOutput)
- temp = *pLastOutput;
-
- /* generate a buffer of samples */
- while (numSamplesToAdd--)
- {
-
- /* if using filter */
- if (pLastOutput)
- {
- /* use PRNG for noise */
- temp2 = FM_Noise(&phase);
-
- /*lint -e{704} use shift for performance */
- temp += ((temp2 -temp) * feedback) >> 8;
- }
- else
- {
- temp = FM_Noise(&phase);
- }
-
- /* internal gain for modulation effects */
- temp2 = FMUL_15x15(temp, (gain >> 16));
-
- /* output gain calculation */
- temp2 = FMUL_15x15(temp2, p->outputGain);
-
- /* saturating add to buffer */
- if (mix)
- {
- temp2 += *pBuffer;
- *pBuffer++ = FM_Saturate(temp2);
- }
-
- /* output to buffer */
- else
- *pBuffer++ = (EAS_I16) temp2;
-
- /* increment gain */
- gain += gainInc;
-
- }
-
- /* save phase and gain */
- p->phase = phase;
- p->gain = gainTarget;
-
- /* save last output for feedback in next frame */
- if (pLastOutput)
- *pLastOutput = (EAS_I16) temp;
-}
-
-/*----------------------------------------------------------------------------
- * FM_ConfigVoice()
- *----------------------------------------------------------------------------
- * Purpose:
- * Receives parameters to start a new voice.
- *
- * Inputs:
- * voiceNum - voice number to start
- * vCfg - configuration data
- * pMixBuffer - pointer to host supplied buffer
- *
- * Outputs:
- *
- * Side Effects:
- *
- * Notes:
- * pFrameBuffer is not used in the test version, but is passed as a
- * courtesy to split architecture implementations. It can be used as
- * as pointer to the interprocessor communications buffer when the
- * synthesis parameters are passed off to a DSP for synthesis.
- *----------------------------------------------------------------------------
-*/
-/*lint -esym(715, pFrameBuffer) pFrameBuffer not used in test version - see above */
-void FM_ConfigVoice (EAS_I32 voiceNum, S_FM_VOICE_CONFIG *vCfg, EAS_FRAME_BUFFER_HANDLE pFrameBuffer)
-{
- S_FM_ENG_VOICE *pVoice;
- EAS_INT i;
-
- /* establish pointer to voice data */
- pVoice = &voices[voiceNum];
-
- /* save data */
- pVoice->feedback = vCfg->feedback;
- pVoice->flags = vCfg->flags;
- pVoice->voiceGain = vCfg->voiceGain;
-
- /* initialize Z-1 terms */
- pVoice->op1Out = 0;
- pVoice->op3Out = 0;
-
- /* initialize operators */
- for (i = 0; i < 4; i++)
- {
- /* save operator data */
- pVoice->oper[i].gain = vCfg->gain[i];
- pVoice->oper[i].outputGain = vCfg->outputGain[i];
- pVoice->oper[i].outputGain = vCfg->outputGain[i];
-
- /* initalize operator */
- pVoice->oper[i].phase = 0;
- }
-
- /* calculate pan */
-#if NUM_OUTPUT_CHANNELS == 2
- FM_CalculatePan(vCfg->pan, &pVoice->gainLeft, &pVoice->gainRight);
-#endif
-}
-
-/*----------------------------------------------------------------------------
- * FM_ProcessVoice()
- *----------------------------------------------------------------------------
- * Purpose:
- * Synthesizes a buffer of samples based on calculated parameters.
- *
- * Inputs:
- * nNumSamplesToAdd - number of samples to synthesize
- * psEASData - pointer to overall EAS data structure
- *
- * Outputs:
- *
- * Side Effects:
- *
- * Notes:
- * pOut is not used in the test version, but is passed as a
- * courtesy to split architecture implementations. It can be used as
- * as pointer to the interprocessor communications buffer when the
- * synthesis parameters are passed off to a DSP for synthesis.
- *----------------------------------------------------------------------------
-*/
-/*lint -esym(715, pOut) pOut not used in test version - see above */
-void FM_ProcessVoice (
- EAS_I32 voiceNum,
- S_FM_VOICE_FRAME *pFrame,
- EAS_I32 numSamplesToAdd,
- EAS_PCM *pTempBuffer,
- EAS_PCM *pBuffer,
- EAS_I32 *pMixBuffer,
- EAS_FRAME_BUFFER_HANDLE pFrameBuffer)
-{
- S_FM_ENG_VOICE *p;
- EAS_PCM *pOutBuf;
- EAS_PCM *pMod;
- EAS_BOOL mix;
- EAS_U8 feedback1;
- EAS_U8 feedback3;
- EAS_U8 mode;
-
- /* establish pointer to voice data */
- p = &voices[voiceNum];
- mode = p->flags & 0x07;
-
- /* lookup feedback values */
- feedback1 = fmScaleTable[p->feedback >> 4];
- feedback3 = fmScaleTable[p->feedback & 0x0f];
-
- /* operator 3 is on output bus in modes 0, 1, and 3 */
- if ((mode == 0) || (mode == 1) || (mode == 3))
- pOutBuf = pBuffer;
- else
- pOutBuf = pTempBuffer;
-
- if (p->flags & FLAG_FM_ENG_VOICE_OP3_NOISE)
- {
- FM_NoiseOperator(
- p->oper + 2,
- numSamplesToAdd,
- pOutBuf,
- EAS_FALSE,
- pFrame->gain[2],
- feedback3,
- &p->op3Out);
- }
- else
- {
- FM_Operator(
- p->oper + 2,
- numSamplesToAdd,
- pOutBuf,
- 0,
- EAS_FALSE,
- pFrame->gain[2],
- pFrame->pitch[2],
- feedback3,
- &p->op3Out);
- }
-
- /* operator 4 is on output bus in modes 0, 1, and 2 */
- if (mode < 3)
- pOutBuf = pBuffer;
- else
- pOutBuf = pTempBuffer;
-
- /* operator 4 is modulated in modes 2, 4, and 5 */
- if ((mode == 2) || (mode == 4) || (mode == 5))
- pMod = pTempBuffer;
- else
- pMod = 0;
-
- /* operator 4 is in mix mode in modes 0 and 1 */
- mix = (mode < 2);
-
- if (p->flags & FLAG_FM_ENG_VOICE_OP4_NOISE)
- {
- FM_NoiseOperator(
- p->oper + 3,
- numSamplesToAdd,
- pOutBuf,
- mix,
- pFrame->gain[3],
- 0,
- 0);
- }
- else
- {
- FM_Operator(
- p->oper + 3,
- numSamplesToAdd,
- pOutBuf,
- pMod,
- mix,
- pFrame->gain[3],
- pFrame->pitch[3],
- 0,
- 0);
- }
-
- /* operator 1 is on output bus in mode 0 */
- if (mode == 0)
- pOutBuf = pBuffer;
- else
- pOutBuf = pTempBuffer;
-
- /* operator 1 is modulated in modes 3 and 4 */
- if ((mode == 3) || (mode == 4))
- pMod = pTempBuffer;
- else
- pMod = 0;
-
- /* operator 1 is in mix mode in modes 0 and 5 */
- mix = ((mode == 0) || (mode == 5));
-
- if (p->flags & FLAG_FM_ENG_VOICE_OP1_NOISE)
- {
- FM_NoiseOperator(
- p->oper,
- numSamplesToAdd,
- pOutBuf,
- mix,
- pFrame->gain[0],
- feedback1,
- &p->op1Out);
- }
- else
- {
- FM_Operator(
- p->oper,
- numSamplesToAdd,
- pOutBuf,
- pMod,
- mix,
- pFrame->gain[0],
- pFrame->pitch[0],
- feedback1,
- &p->op1Out);
- }
-
- /* operator 2 is modulated in all modes except 0 */
- if (mode != 0)
- pMod = pTempBuffer;
- else
- pMod = 0;
-
- /* operator 1 is in mix mode in modes 0 -3 */
- mix = (mode < 4);
-
- if (p->flags & FLAG_FM_ENG_VOICE_OP2_NOISE)
- {
- FM_NoiseOperator(
- p->oper + 1,
- numSamplesToAdd,
- pBuffer,
- mix,
- pFrame->gain[1],
- 0,
- 0);
- }
- else
- {
- FM_Operator(
- p->oper + 1,
- numSamplesToAdd,
- pBuffer,
- pMod,
- mix,
- pFrame->gain[1],
- pFrame->pitch[1],
- 0,
- 0);
- }
-
- /* mix voice output to synthesizer output buffer */
- FM_SynthMixVoice(p, pFrame->voiceGain, numSamplesToAdd, pBuffer, pMixBuffer);
-}
-
-/*----------------------------------------------------------------------------
- * FM_SynthMixVoice()
- *----------------------------------------------------------------------------
- * Purpose:
- * Mixes the voice output buffer into the final mix using an anti-zipper
- * filter.
- *
- * Inputs:
- * nNumSamplesToAdd - number of samples to synthesize
- * psEASData - pointer to overall EAS data structure
- *
- * Outputs:
- *
- * Side Effects:
- *
- *----------------------------------------------------------------------------
-*/
-void FM_SynthMixVoice(S_FM_ENG_VOICE *p, EAS_U16 nGainTarget, EAS_I32 numSamplesToAdd, EAS_PCM *pInputBuffer, EAS_I32 *pBuffer)
-{
- EAS_I32 nGain;
- EAS_I32 nGainInc;
- EAS_I32 nTemp;
-
- /* restore previous gain */
- /*lint -e{703} <use shift for performance> */
- nGain = (EAS_I32) p->voiceGain << 16;
-
- /* calculate gain increment */
- /*lint -e{703} <use shift for performance> */
- nGainInc = ((EAS_I32) nGainTarget - (EAS_I32) p->voiceGain) << (16 - SYNTH_UPDATE_PERIOD_IN_BITS);
-
- /* mix the output buffer */
- while (numSamplesToAdd--)
- {
- /* output gain calculation */
- nTemp = *pInputBuffer++;
-
- /* sum to output buffer */
-#if (NUM_OUTPUT_CHANNELS == 2)
-
- /*lint -e{704} <use shift for performance> */
- nTemp = ((EAS_I32) nTemp * (nGain >> 16)) >> FM_GAIN_SHIFT;
-
- /*lint -e{704} <use shift for performance> */
- {
- EAS_I32 nTemp2;
- nTemp = nTemp >> FM_STEREO_PRE_GAIN_SHIFT;
- nTemp2 = (nTemp * p->gainLeft) >> FM_STEREO_POST_GAIN_SHIFT;
- *pBuffer++ += nTemp2;
- nTemp2 = (nTemp * p->gainRight) >> FM_STEREO_POST_GAIN_SHIFT;
- *pBuffer++ += nTemp2;
- }
-#else
- /*lint -e{704} <use shift for performance> */
- nTemp = ((EAS_I32) nTemp * (nGain >> 16)) >> FM_MONO_GAIN_SHIFT;
- *pBuffer++ += nTemp;
-#endif
-
- /* increment gain for anti-zipper filter */
- nGain += nGainInc;
- }
-
- /* save gain */
- p->voiceGain = nGainTarget;
-}
-
+ *
+ *----------------------------------------------------------------------------
+ * Revision Control:
+ * $Revision: 795 $
+ * $Date: 2007-08-01 00:14:45 -0700 (Wed, 01 Aug 2007) $
+ *----------------------------------------------------------------------------
+*/
+
+/* includes */
+#include "eas_types.h"
+#include "eas_math.h"
+#include "eas_audioconst.h"
+#include "eas_fmengine.h"
+
+#if defined(EAS_FM_SYNTH) || defined(EAS_HYBRID_SYNTH) || defined(EAS_SPLIT_HYBRID_SYNTH) || defined(EAS_SPLIT_FM_SYNTH)
+#include "eas_data.h"
+#endif
+
+/* externals */
+extern const EAS_I16 sineTable[];
+extern const EAS_U8 fmScaleTable[16];
+
+// saturation constants for 32-bit to 16-bit conversion
+#define _EAS_MAX_OUTPUT 32767
+#define _EAS_MIN_OUTPUT -32767
+
+static S_FM_ENG_VOICE voices[NUM_FM_VOICES];
+
+/* local prototypes */
+void FM_SynthMixVoice (S_FM_ENG_VOICE *p, EAS_U16 gainTarget, EAS_I32 numSamplesToAdd, EAS_PCM *pInputBuffer, EAS_I32 *pBuffer);
+
+/* used in development environment */
+#if defined(_SATURATION_MONITOR)
+static EAS_BOOL bSaturated = EAS_FALSE;
+
+/*----------------------------------------------------------------------------
+ * FM_CheckSaturation()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Allows the sound development tool to check for saturation at the voice
+ * level. Useful for tuning the level controls.
+ *
+ * Inputs:
+ *
+ * Outputs:
+ * Returns true if saturation has occurred since the last time the function
+ * was called.
+ *
+ * Side Effects:
+ * Resets the saturation flag
+ *----------------------------------------------------------------------------
+*/
+EAS_BOOL FM_CheckSaturation ()
+{
+ EAS_BOOL bTemp;
+ bTemp = bSaturated;
+ bSaturated = EAS_FALSE;
+ return bTemp;
+}
+#endif
+
+/*----------------------------------------------------------------------------
+ * FM_Saturate()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * This inline function saturates a 32-bit number to 16-bits
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ * Returns a 16-bit integer
+ *----------------------------------------------------------------------------
+*/
+EAS_INLINE EAS_I16 FM_Saturate (EAS_I32 nValue)
+{
+ if (nValue > _EAS_MAX_OUTPUT)
+ {
+#if defined(_SATURATION_MONITOR)
+ bSaturated = EAS_TRUE;
+#endif
+ return _EAS_MAX_OUTPUT;
+ }
+ if (nValue < _EAS_MIN_OUTPUT)
+ {
+#if defined(_SATURATION_MONITOR)
+ bSaturated = EAS_TRUE;
+#endif
+ return _EAS_MIN_OUTPUT;
+ }
+ return (EAS_I16) nValue;
+}
+
+/*----------------------------------------------------------------------------
+ * FM_Noise()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * A 31-bit low-cost linear congruential PRNG algorithm used to
+ * generate noise.
+ *
+ * Inputs:
+ * pnSeed - pointer to 32-bit PRNG seed
+ *
+ * Outputs:
+ * Returns a 16-bit integer
+ *----------------------------------------------------------------------------
+*/
+EAS_INLINE EAS_I16 FM_Noise (EAS_U32 *pnSeed)
+{
+ *pnSeed = *pnSeed * 214013L + 2531011L;
+ return (EAS_I16) ((*pnSeed >> 15) & 0xffff);
+}
+
+/*----------------------------------------------------------------------------
+ * FM_PhaseInc()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Transform pitch cents to linear phase increment
+ *
+ * Inputs:
+ * nCents - measured in cents
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ * nResult - int.frac result (where frac has NUM_DENTS_FRAC_BITS)
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+static EAS_I32 FM_PhaseInc (EAS_I32 nCents)
+{
+ EAS_I32 nDents;
+ EAS_I32 nExponentInt, nExponentFrac;
+ EAS_I32 nTemp1, nTemp2;
+ EAS_I32 nResult;
+
+ /* convert cents to dents */
+ nDents = FMUL_15x15(nCents, CENTS_TO_DENTS);
+ nExponentInt = GET_DENTS_INT_PART(nDents) + (32 - SINE_TABLE_SIZE_IN_BITS - NUM_EG1_FRAC_BITS);
+ nExponentFrac = GET_DENTS_FRAC_PART(nDents);
+
+ /* implement 2^(fracPart) as a power series */
+ nTemp1 = GN2_TO_X2 + MULT_DENTS_COEF(nExponentFrac, GN2_TO_X3);
+ nTemp2 = GN2_TO_X1 + MULT_DENTS_COEF(nExponentFrac, nTemp1);
+ nTemp1 = GN2_TO_X0 + MULT_DENTS_COEF(nExponentFrac, nTemp2);
+
+ /*
+ implement 2^(intPart) as
+ a left shift for intPart >= 0 or
+ a left shift for intPart < 0
+ */
+ if (nExponentInt >= 0)
+ {
+ /* left shift for positive exponents */
+ /*lint -e{703} <avoid multiply for performance>*/
+ nResult = nTemp1 << nExponentInt;
+ }
+ else
+ {
+ /* right shift for negative exponents */
+ nExponentInt = -nExponentInt;
+ nResult = nTemp1 >> nExponentInt;
+ }
+
+ return nResult;
+}
+
+#if (NUM_OUTPUT_CHANNELS == 2)
+/*----------------------------------------------------------------------------
+ * FM_CalculatePan()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Assign the left and right gain values corresponding to the given pan value.
+ *
+ * Inputs:
+ * psVoice - ptr to the voice we have assigned for this channel
+ * psArticulation - ptr to this voice's articulation
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ * Side Effects:
+ * the given voice's m_nGainLeft and m_nGainRight are assigned
+ *----------------------------------------------------------------------------
+*/
+static void FM_CalculatePan (EAS_I16 pan, EAS_U16 *pGainLeft, EAS_U16 *pGainRight)
+{
+ EAS_I32 nTemp;
+ EAS_INT nNetAngle;
+
+ /*
+ Implement the following
+ sin(x) = (2-4*c)*x^2 + c + x
+ cos(x) = (2-4*c)*x^2 + c - x
+
+ where c = 1/sqrt(2)
+ using the a0 + x*(a1 + x*a2) approach
+ */
+
+ /*
+ Get the Midi CC10 pan value for this voice's channel
+ convert the pan value to an "angle" representation suitable for
+ our sin, cos calculator. This representation is NOT necessarily the same
+ as the transform in the GM manuals because of our sin, cos calculator.
+ "angle" = (CC10 - 64)/128
+ */
+ /*lint -e{703} <avoid multiply for performance reasons>*/
+ nNetAngle = ((EAS_I32) pan) << (NUM_EG1_FRAC_BITS -7);
+
+ /* calculate sin */
+ nTemp = EG1_ONE + FMUL_15x15(COEFF_PAN_G2, nNetAngle);
+ nTemp = COEFF_PAN_G0 + FMUL_15x15(nTemp, nNetAngle);
+
+ if (nTemp > SYNTH_FULL_SCALE_EG1_GAIN)
+ nTemp = SYNTH_FULL_SCALE_EG1_GAIN;
+ else if (nTemp < 0)
+ nTemp = 0;
+
+ *pGainRight = (EAS_U16) nTemp;
+
+ /* calculate cos */
+ nTemp = -EG1_ONE + FMUL_15x15(COEFF_PAN_G2, nNetAngle);
+ nTemp = COEFF_PAN_G0 + FMUL_15x15(nTemp, nNetAngle);
+
+ if (nTemp > SYNTH_FULL_SCALE_EG1_GAIN)
+ nTemp = SYNTH_FULL_SCALE_EG1_GAIN;
+ else if (nTemp < 0)
+ nTemp = 0;
+
+ *pGainLeft = (EAS_U16) nTemp;
+}
+#endif /* #if (NUM_OUTPUT_CHANNELS == 2) */
+
+/*----------------------------------------------------------------------------
+ * FM_Operator()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Synthesizes a buffer of samples based on passed parameters.
+ *
+ * Inputs:
+ * nNumSamplesToAdd - number of samples to synthesize
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+void FM_Operator (
+ S_FM_ENG_OPER *p,
+ EAS_I32 numSamplesToAdd,
+ EAS_PCM *pBuffer,
+ EAS_PCM *pModBuffer,
+ EAS_BOOL mix,
+ EAS_U16 gainTarget,
+ EAS_I16 pitch,
+ EAS_U8 feedback,
+ EAS_I16 *pLastOutput)
+{
+ EAS_I32 gain;
+ EAS_I32 gainInc;
+ EAS_U32 phase;
+ EAS_U32 phaseInc;
+ EAS_U32 phaseTemp;
+ EAS_I32 temp;
+ EAS_I32 temp2;
+
+ /* establish local gain variable */
+ gain = (EAS_I32) p->gain << 16;
+
+ /* calculate gain increment */
+ /*lint -e{703} use shift for performance */
+ gainInc = ((EAS_I32) gainTarget - (EAS_I32) p->gain) << (16 - SYNTH_UPDATE_PERIOD_IN_BITS);
+
+ /* establish local phase variables */
+ phase = p->phase;
+
+ /* calculate the new phase increment */
+ phaseInc = (EAS_U32) FM_PhaseInc(pitch);
+
+ /* restore final output from previous frame for feedback loop */
+ if (pLastOutput)
+ temp = *pLastOutput;
+ else
+ temp = 0;
+
+ /* generate a buffer of samples */
+ while (numSamplesToAdd--)
+ {
+
+ /* incorporate modulation */
+ if (pModBuffer)
+ {
+ /*lint -e{701} use shift for performance */
+ temp = *pModBuffer++ << FM_MODULATOR_INPUT_SHIFT;
+ }
+
+ /* incorporate feedback */
+ else
+ {
+ /*lint -e{703} use shift for performance */
+ temp = (temp * (EAS_I32) feedback) << FM_FEEDBACK_SHIFT;
+ }
+
+ /*lint -e{737} <use this behavior to avoid extra mask step> */
+ phaseTemp = phase + temp;
+
+ /* fetch sample from wavetable */
+ temp = sineTable[phaseTemp >> (32 - SINE_TABLE_SIZE_IN_BITS)];
+
+ /* increment operator phase */
+ phase += phaseInc;
+
+ /* internal gain for modulation effects */
+ temp = FMUL_15x15(temp, (gain >> 16));
+
+ /* output gain calculation */
+ temp2 = FMUL_15x15(temp, p->outputGain);
+
+ /* saturating add to buffer */
+ if (mix)
+ {
+ temp2 += *pBuffer;
+ *pBuffer++ = FM_Saturate(temp2);
+ }
+
+ /* output to buffer */
+ else
+ *pBuffer++ = (EAS_I16) temp2;
+
+ /* increment gain */
+ gain += gainInc;
+
+ }
+
+ /* save phase and gain */
+ p->phase = phase;
+ p->gain = gainTarget;
+
+ /* save last output for feedback in next frame */
+ if (pLastOutput)
+ *pLastOutput = (EAS_I16) temp;
+}
+
+/*----------------------------------------------------------------------------
+ * FM_NoiseOperator()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Synthesizes a buffer of samples based on passed parameters.
+ *
+ * Inputs:
+ * nNumSamplesToAdd - number of samples to synthesize
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+void FM_NoiseOperator (
+ S_FM_ENG_OPER *p,
+ EAS_I32 numSamplesToAdd,
+ EAS_PCM *pBuffer,
+ EAS_BOOL mix,
+ EAS_U16 gainTarget,
+ EAS_U8 feedback,
+ EAS_I16 *pLastOutput)
+{
+ EAS_I32 gain;
+ EAS_I32 gainInc;
+ EAS_U32 phase;
+ EAS_I32 temp;
+ EAS_I32 temp2;
+
+ /* establish local gain variable */
+ gain = (EAS_I32) p->gain << 16;
+
+ /* calculate gain increment */
+ /*lint -e{703} use shift for performance */
+ gainInc = ((EAS_I32) gainTarget - (EAS_I32) p->gain) << (16 - SYNTH_UPDATE_PERIOD_IN_BITS);
+
+ /* establish local phase variables */
+ phase = p->phase;
+
+ /* establish local phase variables */
+ phase = p->phase;
+
+ /* recall last sample for filter Z-1 term */
+ temp = 0;
+ if (pLastOutput)
+ temp = *pLastOutput;
+
+ /* generate a buffer of samples */
+ while (numSamplesToAdd--)
+ {
+
+ /* if using filter */
+ if (pLastOutput)
+ {
+ /* use PRNG for noise */
+ temp2 = FM_Noise(&phase);
+
+ /*lint -e{704} use shift for performance */
+ temp += ((temp2 -temp) * feedback) >> 8;
+ }
+ else
+ {
+ temp = FM_Noise(&phase);
+ }
+
+ /* internal gain for modulation effects */
+ temp2 = FMUL_15x15(temp, (gain >> 16));
+
+ /* output gain calculation */
+ temp2 = FMUL_15x15(temp2, p->outputGain);
+
+ /* saturating add to buffer */
+ if (mix)
+ {
+ temp2 += *pBuffer;
+ *pBuffer++ = FM_Saturate(temp2);
+ }
+
+ /* output to buffer */
+ else
+ *pBuffer++ = (EAS_I16) temp2;
+
+ /* increment gain */
+ gain += gainInc;
+
+ }
+
+ /* save phase and gain */
+ p->phase = phase;
+ p->gain = gainTarget;
+
+ /* save last output for feedback in next frame */
+ if (pLastOutput)
+ *pLastOutput = (EAS_I16) temp;
+}
+
+/*----------------------------------------------------------------------------
+ * FM_ConfigVoice()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Receives parameters to start a new voice.
+ *
+ * Inputs:
+ * voiceNum - voice number to start
+ * vCfg - configuration data
+ * pMixBuffer - pointer to host supplied buffer
+ *
+ * Outputs:
+ *
+ * Side Effects:
+ *
+ * Notes:
+ * pFrameBuffer is not used in the test version, but is passed as a
+ * courtesy to split architecture implementations. It can be used as
+ * as pointer to the interprocessor communications buffer when the
+ * synthesis parameters are passed off to a DSP for synthesis.
+ *----------------------------------------------------------------------------
+*/
+/*lint -esym(715, pFrameBuffer) pFrameBuffer not used in test version - see above */
+void FM_ConfigVoice (EAS_I32 voiceNum, S_FM_VOICE_CONFIG *vCfg, EAS_FRAME_BUFFER_HANDLE pFrameBuffer)
+{
+ S_FM_ENG_VOICE *pVoice;
+ EAS_INT i;
+
+ /* establish pointer to voice data */
+ pVoice = &voices[voiceNum];
+
+ /* save data */
+ pVoice->feedback = vCfg->feedback;
+ pVoice->flags = vCfg->flags;
+ pVoice->voiceGain = vCfg->voiceGain;
+
+ /* initialize Z-1 terms */
+ pVoice->op1Out = 0;
+ pVoice->op3Out = 0;
+
+ /* initialize operators */
+ for (i = 0; i < 4; i++)
+ {
+ /* save operator data */
+ pVoice->oper[i].gain = vCfg->gain[i];
+ pVoice->oper[i].outputGain = vCfg->outputGain[i];
+ pVoice->oper[i].outputGain = vCfg->outputGain[i];
+
+ /* initalize operator */
+ pVoice->oper[i].phase = 0;
+ }
+
+ /* calculate pan */
+#if NUM_OUTPUT_CHANNELS == 2
+ FM_CalculatePan(vCfg->pan, &pVoice->gainLeft, &pVoice->gainRight);
+#endif
+}
+
+/*----------------------------------------------------------------------------
+ * FM_ProcessVoice()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Synthesizes a buffer of samples based on calculated parameters.
+ *
+ * Inputs:
+ * nNumSamplesToAdd - number of samples to synthesize
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ * Side Effects:
+ *
+ * Notes:
+ * pOut is not used in the test version, but is passed as a
+ * courtesy to split architecture implementations. It can be used as
+ * as pointer to the interprocessor communications buffer when the
+ * synthesis parameters are passed off to a DSP for synthesis.
+ *----------------------------------------------------------------------------
+*/
+/*lint -esym(715, pOut) pOut not used in test version - see above */
+void FM_ProcessVoice (
+ EAS_I32 voiceNum,
+ S_FM_VOICE_FRAME *pFrame,
+ EAS_I32 numSamplesToAdd,
+ EAS_PCM *pTempBuffer,
+ EAS_PCM *pBuffer,
+ EAS_I32 *pMixBuffer,
+ EAS_FRAME_BUFFER_HANDLE pFrameBuffer)
+{
+ S_FM_ENG_VOICE *p;
+ EAS_PCM *pOutBuf;
+ EAS_PCM *pMod;
+ EAS_BOOL mix;
+ EAS_U8 feedback1;
+ EAS_U8 feedback3;
+ EAS_U8 mode;
+
+ /* establish pointer to voice data */
+ p = &voices[voiceNum];
+ mode = p->flags & 0x07;
+
+ /* lookup feedback values */
+ feedback1 = fmScaleTable[p->feedback >> 4];
+ feedback3 = fmScaleTable[p->feedback & 0x0f];
+
+ /* operator 3 is on output bus in modes 0, 1, and 3 */
+ if ((mode == 0) || (mode == 1) || (mode == 3))
+ pOutBuf = pBuffer;
+ else
+ pOutBuf = pTempBuffer;
+
+ if (p->flags & FLAG_FM_ENG_VOICE_OP3_NOISE)
+ {
+ FM_NoiseOperator(
+ p->oper + 2,
+ numSamplesToAdd,
+ pOutBuf,
+ EAS_FALSE,
+ pFrame->gain[2],
+ feedback3,
+ &p->op3Out);
+ }
+ else
+ {
+ FM_Operator(
+ p->oper + 2,
+ numSamplesToAdd,
+ pOutBuf,
+ 0,
+ EAS_FALSE,
+ pFrame->gain[2],
+ pFrame->pitch[2],
+ feedback3,
+ &p->op3Out);
+ }
+
+ /* operator 4 is on output bus in modes 0, 1, and 2 */
+ if (mode < 3)
+ pOutBuf = pBuffer;
+ else
+ pOutBuf = pTempBuffer;
+
+ /* operator 4 is modulated in modes 2, 4, and 5 */
+ if ((mode == 2) || (mode == 4) || (mode == 5))
+ pMod = pTempBuffer;
+ else
+ pMod = 0;
+
+ /* operator 4 is in mix mode in modes 0 and 1 */
+ mix = (mode < 2);
+
+ if (p->flags & FLAG_FM_ENG_VOICE_OP4_NOISE)
+ {
+ FM_NoiseOperator(
+ p->oper + 3,
+ numSamplesToAdd,
+ pOutBuf,
+ mix,
+ pFrame->gain[3],
+ 0,
+ 0);
+ }
+ else
+ {
+ FM_Operator(
+ p->oper + 3,
+ numSamplesToAdd,
+ pOutBuf,
+ pMod,
+ mix,
+ pFrame->gain[3],
+ pFrame->pitch[3],
+ 0,
+ 0);
+ }
+
+ /* operator 1 is on output bus in mode 0 */
+ if (mode == 0)
+ pOutBuf = pBuffer;
+ else
+ pOutBuf = pTempBuffer;
+
+ /* operator 1 is modulated in modes 3 and 4 */
+ if ((mode == 3) || (mode == 4))
+ pMod = pTempBuffer;
+ else
+ pMod = 0;
+
+ /* operator 1 is in mix mode in modes 0 and 5 */
+ mix = ((mode == 0) || (mode == 5));
+
+ if (p->flags & FLAG_FM_ENG_VOICE_OP1_NOISE)
+ {
+ FM_NoiseOperator(
+ p->oper,
+ numSamplesToAdd,
+ pOutBuf,
+ mix,
+ pFrame->gain[0],
+ feedback1,
+ &p->op1Out);
+ }
+ else
+ {
+ FM_Operator(
+ p->oper,
+ numSamplesToAdd,
+ pOutBuf,
+ pMod,
+ mix,
+ pFrame->gain[0],
+ pFrame->pitch[0],
+ feedback1,
+ &p->op1Out);
+ }
+
+ /* operator 2 is modulated in all modes except 0 */
+ if (mode != 0)
+ pMod = pTempBuffer;
+ else
+ pMod = 0;
+
+ /* operator 1 is in mix mode in modes 0 -3 */
+ mix = (mode < 4);
+
+ if (p->flags & FLAG_FM_ENG_VOICE_OP2_NOISE)
+ {
+ FM_NoiseOperator(
+ p->oper + 1,
+ numSamplesToAdd,
+ pBuffer,
+ mix,
+ pFrame->gain[1],
+ 0,
+ 0);
+ }
+ else
+ {
+ FM_Operator(
+ p->oper + 1,
+ numSamplesToAdd,
+ pBuffer,
+ pMod,
+ mix,
+ pFrame->gain[1],
+ pFrame->pitch[1],
+ 0,
+ 0);
+ }
+
+ /* mix voice output to synthesizer output buffer */
+ FM_SynthMixVoice(p, pFrame->voiceGain, numSamplesToAdd, pBuffer, pMixBuffer);
+}
+
+/*----------------------------------------------------------------------------
+ * FM_SynthMixVoice()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Mixes the voice output buffer into the final mix using an anti-zipper
+ * filter.
+ *
+ * Inputs:
+ * nNumSamplesToAdd - number of samples to synthesize
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+void FM_SynthMixVoice(S_FM_ENG_VOICE *p, EAS_U16 nGainTarget, EAS_I32 numSamplesToAdd, EAS_PCM *pInputBuffer, EAS_I32 *pBuffer)
+{
+ EAS_I32 nGain;
+ EAS_I32 nGainInc;
+ EAS_I32 nTemp;
+
+ /* restore previous gain */
+ /*lint -e{703} <use shift for performance> */
+ nGain = (EAS_I32) p->voiceGain << 16;
+
+ /* calculate gain increment */
+ /*lint -e{703} <use shift for performance> */
+ nGainInc = ((EAS_I32) nGainTarget - (EAS_I32) p->voiceGain) << (16 - SYNTH_UPDATE_PERIOD_IN_BITS);
+
+ /* mix the output buffer */
+ while (numSamplesToAdd--)
+ {
+ /* output gain calculation */
+ nTemp = *pInputBuffer++;
+
+ /* sum to output buffer */
+#if (NUM_OUTPUT_CHANNELS == 2)
+
+ /*lint -e{704} <use shift for performance> */
+ nTemp = ((EAS_I32) nTemp * (nGain >> 16)) >> FM_GAIN_SHIFT;
+
+ /*lint -e{704} <use shift for performance> */
+ {
+ EAS_I32 nTemp2;
+ nTemp = nTemp >> FM_STEREO_PRE_GAIN_SHIFT;
+ nTemp2 = (nTemp * p->gainLeft) >> FM_STEREO_POST_GAIN_SHIFT;
+ *pBuffer++ += nTemp2;
+ nTemp2 = (nTemp * p->gainRight) >> FM_STEREO_POST_GAIN_SHIFT;
+ *pBuffer++ += nTemp2;
+ }
+#else
+ /*lint -e{704} <use shift for performance> */
+ nTemp = ((EAS_I32) nTemp * (nGain >> 16)) >> FM_MONO_GAIN_SHIFT;
+ *pBuffer++ += nTemp;
+#endif
+
+ /* increment gain for anti-zipper filter */
+ nGain += nGainInc;
+ }
+
+ /* save gain */
+ p->voiceGain = nGainTarget;
+}
+