| /*---------------------------------------------------------------------------- |
| * |
| * 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. |
| * 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. |
| * |
| *---------------------------------------------------------------------------- |
| * 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; |
| } |
| |