1/*----------------------------------------------------------------------------
2 *
3 * File:
4 * eas_fmengine.c
5 *
6 * Contents and purpose:
7 * Implements the low-level FM synthesizer functions.
8 *
9 * Copyright Sonic Network Inc. 2004, 2005
10
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
14 *
15 *      http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 *
23 *----------------------------------------------------------------------------
24 * Revision Control:
25 *   $Revision: 795 $
26 *   $Date: 2007-08-01 00:14:45 -0700 (Wed, 01 Aug 2007) $
27 *----------------------------------------------------------------------------
28*/
29
30/* includes */
31#include "eas_types.h"
32#include "eas_math.h"
33#include "eas_audioconst.h"
34#include "eas_fmengine.h"
35
36#if defined(EAS_FM_SYNTH) || defined(EAS_HYBRID_SYNTH) || defined(EAS_SPLIT_HYBRID_SYNTH) || defined(EAS_SPLIT_FM_SYNTH)
37#include "eas_data.h"
38#endif
39
40/* externals */
41extern const EAS_I16 sineTable[];
42extern const EAS_U8 fmScaleTable[16];
43
44// saturation constants for 32-bit to 16-bit conversion
45#define _EAS_MAX_OUTPUT 32767
46#define _EAS_MIN_OUTPUT -32767
47
48static S_FM_ENG_VOICE voices[NUM_FM_VOICES];
49
50/* local prototypes */
51void FM_SynthMixVoice (S_FM_ENG_VOICE *p,  EAS_U16 gainTarget, EAS_I32 numSamplesToAdd, EAS_PCM *pInputBuffer, EAS_I32 *pBuffer);
52
53/* used in development environment */
54#if defined(_SATURATION_MONITOR)
55static EAS_BOOL bSaturated = EAS_FALSE;
56
57/*----------------------------------------------------------------------------
58 * FM_CheckSaturation()
59 *----------------------------------------------------------------------------
60 * Purpose:
61 * Allows the sound development tool to check for saturation at the voice
62 * level. Useful for tuning the level controls.
63 *
64 * Inputs:
65 *
66 * Outputs:
67 * Returns true if saturation has occurred since the last time the function
68 * was called.
69 *
70 * Side Effects:
71 * Resets the saturation flag
72 *----------------------------------------------------------------------------
73*/
74EAS_BOOL FM_CheckSaturation ()
75{
76    EAS_BOOL bTemp;
77    bTemp = bSaturated;
78    bSaturated = EAS_FALSE;
79    return bTemp;
80}
81#endif
82
83/*----------------------------------------------------------------------------
84 * FM_Saturate()
85 *----------------------------------------------------------------------------
86 * Purpose:
87 * This inline function saturates a 32-bit number to 16-bits
88 *
89 * Inputs:
90 * psEASData - pointer to overall EAS data structure
91 *
92 * Outputs:
93 * Returns a 16-bit integer
94 *----------------------------------------------------------------------------
95*/
96EAS_INLINE EAS_I16 FM_Saturate (EAS_I32 nValue)
97{
98    if (nValue > _EAS_MAX_OUTPUT)
99    {
100#if defined(_SATURATION_MONITOR)
101        bSaturated = EAS_TRUE;
102#endif
103        return _EAS_MAX_OUTPUT;
104    }
105    if (nValue < _EAS_MIN_OUTPUT)
106    {
107#if defined(_SATURATION_MONITOR)
108        bSaturated = EAS_TRUE;
109#endif
110        return _EAS_MIN_OUTPUT;
111    }
112    return (EAS_I16) nValue;
113}
114
115/*----------------------------------------------------------------------------
116 * FM_Noise()
117 *----------------------------------------------------------------------------
118 * Purpose:
119 * A 31-bit low-cost linear congruential PRNG algorithm used to
120 * generate noise.
121 *
122 * Inputs:
123 * pnSeed - pointer to 32-bit PRNG seed
124 *
125 * Outputs:
126 * Returns a 16-bit integer
127 *----------------------------------------------------------------------------
128*/
129EAS_INLINE EAS_I16 FM_Noise (EAS_U32 *pnSeed)
130{
131    *pnSeed = *pnSeed * 214013L + 2531011L;
132    return (EAS_I16) ((*pnSeed >> 15) & 0xffff);
133}
134
135/*----------------------------------------------------------------------------
136 * FM_PhaseInc()
137 *----------------------------------------------------------------------------
138 * Purpose:
139 * Transform pitch cents to linear phase increment
140 *
141 * Inputs:
142 * nCents -     measured in cents
143 * psEASData - pointer to overall EAS data structure
144 *
145 * Outputs:
146 * nResult - int.frac result (where frac has NUM_DENTS_FRAC_BITS)
147 *
148 * Side Effects:
149 *
150 *----------------------------------------------------------------------------
151*/
152static EAS_I32 FM_PhaseInc (EAS_I32 nCents)
153{
154    EAS_I32 nDents;
155    EAS_I32 nExponentInt, nExponentFrac;
156    EAS_I32 nTemp1, nTemp2;
157    EAS_I32 nResult;
158
159    /* convert cents to dents */
160    nDents = FMUL_15x15(nCents, CENTS_TO_DENTS);
161    nExponentInt = GET_DENTS_INT_PART(nDents) + (32 - SINE_TABLE_SIZE_IN_BITS - NUM_EG1_FRAC_BITS);
162    nExponentFrac = GET_DENTS_FRAC_PART(nDents);
163
164    /* implement 2^(fracPart) as a power series */
165    nTemp1 = GN2_TO_X2 + MULT_DENTS_COEF(nExponentFrac, GN2_TO_X3);
166    nTemp2 = GN2_TO_X1 + MULT_DENTS_COEF(nExponentFrac, nTemp1);
167    nTemp1 = GN2_TO_X0 + MULT_DENTS_COEF(nExponentFrac, nTemp2);
168
169    /*
170    implement 2^(intPart) as
171    a left shift for intPart >= 0 or
172    a left shift for intPart <  0
173    */
174    if (nExponentInt >= 0)
175    {
176        /* left shift for positive exponents */
177        /*lint -e{703} <avoid multiply for performance>*/
178        nResult = nTemp1 << nExponentInt;
179    }
180    else
181    {
182        /* right shift for negative exponents */
183        nExponentInt = -nExponentInt;
184        nResult = nTemp1 >> nExponentInt;
185    }
186
187    return nResult;
188}
189
190#if (NUM_OUTPUT_CHANNELS == 2)
191/*----------------------------------------------------------------------------
192 * FM_CalculatePan()
193 *----------------------------------------------------------------------------
194 * Purpose:
195 * Assign the left and right gain values corresponding to the given pan value.
196 *
197 * Inputs:
198 * psVoice - ptr to the voice we have assigned for this channel
199 * psArticulation - ptr to this voice's articulation
200 * psEASData - pointer to overall EAS data structure
201 *
202 * Outputs:
203 *
204 * Side Effects:
205 * the given voice's m_nGainLeft and m_nGainRight are assigned
206 *----------------------------------------------------------------------------
207*/
208static void FM_CalculatePan (EAS_I16 pan, EAS_U16 *pGainLeft, EAS_U16 *pGainRight)
209{
210    EAS_I32 nTemp;
211    EAS_INT nNetAngle;
212
213    /*
214    Implement the following
215    sin(x) = (2-4*c)*x^2 + c + x
216    cos(x) = (2-4*c)*x^2 + c - x
217
218      where  c = 1/sqrt(2)
219    using the a0 + x*(a1 + x*a2) approach
220    */
221
222    /*
223    Get the Midi CC10 pan value for this voice's channel
224    convert the pan value to an "angle" representation suitable for
225    our sin, cos calculator. This representation is NOT necessarily the same
226    as the transform in the GM manuals because of our sin, cos calculator.
227    "angle" = (CC10 - 64)/128
228    */
229    /*lint -e{703} <avoid multiply for performance reasons>*/
230    nNetAngle = ((EAS_I32) pan) << (NUM_EG1_FRAC_BITS -7);
231
232    /* calculate sin */
233    nTemp = EG1_ONE + FMUL_15x15(COEFF_PAN_G2, nNetAngle);
234    nTemp = COEFF_PAN_G0 + FMUL_15x15(nTemp, nNetAngle);
235
236    if (nTemp > SYNTH_FULL_SCALE_EG1_GAIN)
237        nTemp = SYNTH_FULL_SCALE_EG1_GAIN;
238    else if (nTemp < 0)
239        nTemp = 0;
240
241    *pGainRight = (EAS_U16) nTemp;
242
243    /* calculate cos */
244    nTemp = -EG1_ONE + FMUL_15x15(COEFF_PAN_G2, nNetAngle);
245    nTemp = COEFF_PAN_G0 + FMUL_15x15(nTemp, nNetAngle);
246
247    if (nTemp > SYNTH_FULL_SCALE_EG1_GAIN)
248        nTemp = SYNTH_FULL_SCALE_EG1_GAIN;
249    else if (nTemp < 0)
250        nTemp = 0;
251
252    *pGainLeft = (EAS_U16) nTemp;
253}
254#endif /* #if (NUM_OUTPUT_CHANNELS == 2) */
255
256/*----------------------------------------------------------------------------
257 * FM_Operator()
258 *----------------------------------------------------------------------------
259 * Purpose:
260 * Synthesizes a buffer of samples based on passed parameters.
261 *
262 * Inputs:
263 * nNumSamplesToAdd - number of samples to synthesize
264 * psEASData - pointer to overall EAS data structure
265 *
266 * Outputs:
267 *
268 * Side Effects:
269 *
270 *----------------------------------------------------------------------------
271*/
272void FM_Operator (
273        S_FM_ENG_OPER *p,
274        EAS_I32 numSamplesToAdd,
275        EAS_PCM *pBuffer,
276        EAS_PCM *pModBuffer,
277        EAS_BOOL mix,
278        EAS_U16 gainTarget,
279        EAS_I16 pitch,
280        EAS_U8 feedback,
281        EAS_I16 *pLastOutput)
282{
283    EAS_I32 gain;
284    EAS_I32 gainInc;
285    EAS_U32 phase;
286    EAS_U32 phaseInc;
287    EAS_U32 phaseTemp;
288    EAS_I32 temp;
289    EAS_I32 temp2;
290
291    /* establish local gain variable */
292    gain = (EAS_I32) p->gain << 16;
293
294    /* calculate gain increment */
295    /*lint -e{703} use shift for performance */
296    gainInc = ((EAS_I32) gainTarget - (EAS_I32) p->gain) << (16 - SYNTH_UPDATE_PERIOD_IN_BITS);
297
298    /* establish local phase variables */
299    phase = p->phase;
300
301    /* calculate the new phase increment */
302    phaseInc = (EAS_U32) FM_PhaseInc(pitch);
303
304    /* restore final output from previous frame for feedback loop */
305    if (pLastOutput)
306        temp = *pLastOutput;
307    else
308        temp = 0;
309
310    /* generate a buffer of samples */
311    while (numSamplesToAdd--)
312    {
313
314        /* incorporate modulation */
315        if (pModBuffer)
316        {
317            /*lint -e{701} use shift for performance */
318            temp = *pModBuffer++ << FM_MODULATOR_INPUT_SHIFT;
319        }
320
321        /* incorporate feedback */
322        else
323        {
324            /*lint -e{703} use shift for performance */
325            temp = (temp * (EAS_I32) feedback) << FM_FEEDBACK_SHIFT;
326        }
327
328        /*lint -e{737} <use this behavior to avoid extra mask step> */
329        phaseTemp = phase + temp;
330
331        /* fetch sample from wavetable */
332        temp = sineTable[phaseTemp >> (32 - SINE_TABLE_SIZE_IN_BITS)];
333
334        /* increment operator phase */
335        phase += phaseInc;
336
337        /* internal gain for modulation effects */
338        temp = FMUL_15x15(temp, (gain >> 16));
339
340        /* output gain calculation */
341        temp2 = FMUL_15x15(temp, p->outputGain);
342
343        /* saturating add to buffer */
344        if (mix)
345        {
346            temp2 += *pBuffer;
347            *pBuffer++ = FM_Saturate(temp2);
348        }
349
350        /* output to buffer */
351        else
352            *pBuffer++ = (EAS_I16) temp2;
353
354        /* increment gain */
355        gain += gainInc;
356
357    }
358
359    /* save phase and gain */
360    p->phase = phase;
361    p->gain = gainTarget;
362
363    /* save last output for feedback in next frame */
364    if (pLastOutput)
365        *pLastOutput = (EAS_I16) temp;
366}
367
368/*----------------------------------------------------------------------------
369 * FM_NoiseOperator()
370 *----------------------------------------------------------------------------
371 * Purpose:
372 * Synthesizes a buffer of samples based on passed parameters.
373 *
374 * Inputs:
375 * nNumSamplesToAdd - number of samples to synthesize
376 * psEASData - pointer to overall EAS data structure
377 *
378 * Outputs:
379 *
380 * Side Effects:
381 *
382 *----------------------------------------------------------------------------
383*/
384void FM_NoiseOperator (
385        S_FM_ENG_OPER *p,
386        EAS_I32 numSamplesToAdd,
387        EAS_PCM *pBuffer,
388        EAS_BOOL mix,
389        EAS_U16 gainTarget,
390        EAS_U8 feedback,
391        EAS_I16 *pLastOutput)
392{
393    EAS_I32 gain;
394    EAS_I32 gainInc;
395    EAS_U32 phase;
396    EAS_I32 temp;
397    EAS_I32 temp2;
398
399    /* establish local gain variable */
400    gain = (EAS_I32) p->gain << 16;
401
402    /* calculate gain increment */
403    /*lint -e{703} use shift for performance */
404    gainInc = ((EAS_I32) gainTarget - (EAS_I32) p->gain) << (16 - SYNTH_UPDATE_PERIOD_IN_BITS);
405
406    /* establish local phase variables */
407    phase = p->phase;
408
409    /* establish local phase variables */
410    phase = p->phase;
411
412    /* recall last sample for filter Z-1 term */
413    temp = 0;
414    if (pLastOutput)
415        temp = *pLastOutput;
416
417    /* generate a buffer of samples */
418    while (numSamplesToAdd--)
419    {
420
421        /* if using filter */
422        if (pLastOutput)
423        {
424            /* use PRNG for noise */
425            temp2 = FM_Noise(&phase);
426
427            /*lint -e{704} use shift for performance */
428            temp += ((temp2 -temp) * feedback) >> 8;
429        }
430        else
431        {
432            temp = FM_Noise(&phase);
433        }
434
435        /* internal gain for modulation effects */
436        temp2 = FMUL_15x15(temp, (gain >> 16));
437
438        /* output gain calculation */
439        temp2 = FMUL_15x15(temp2, p->outputGain);
440
441        /* saturating add to buffer */
442        if (mix)
443        {
444            temp2 += *pBuffer;
445            *pBuffer++ = FM_Saturate(temp2);
446        }
447
448        /* output to buffer */
449        else
450            *pBuffer++ = (EAS_I16) temp2;
451
452        /* increment gain */
453        gain += gainInc;
454
455    }
456
457    /* save phase and gain */
458    p->phase = phase;
459    p->gain = gainTarget;
460
461    /* save last output for feedback in next frame */
462    if (pLastOutput)
463        *pLastOutput = (EAS_I16) temp;
464}
465
466/*----------------------------------------------------------------------------
467 * FM_ConfigVoice()
468 *----------------------------------------------------------------------------
469 * Purpose:
470 * Receives parameters to start a new voice.
471 *
472 * Inputs:
473 * voiceNum     - voice number to start
474 * vCfg         - configuration data
475 * pMixBuffer   - pointer to host supplied buffer
476 *
477 * Outputs:
478 *
479 * Side Effects:
480 *
481 * Notes:
482 * pFrameBuffer is not used in the test version, but is passed as a
483 * courtesy to split architecture implementations. It can be used as
484 * as pointer to the interprocessor communications buffer when the
485 * synthesis parameters are passed off to a DSP for synthesis.
486 *----------------------------------------------------------------------------
487*/
488/*lint -esym(715, pFrameBuffer) pFrameBuffer not used in test version - see above */
489void FM_ConfigVoice (EAS_I32 voiceNum, S_FM_VOICE_CONFIG *vCfg, EAS_FRAME_BUFFER_HANDLE pFrameBuffer)
490{
491    S_FM_ENG_VOICE *pVoice;
492    EAS_INT i;
493
494    /* establish pointer to voice data */
495    pVoice = &voices[voiceNum];
496
497    /* save data */
498    pVoice->feedback = vCfg->feedback;
499    pVoice->flags = vCfg->flags;
500    pVoice->voiceGain = vCfg->voiceGain;
501
502    /* initialize Z-1 terms */
503    pVoice->op1Out = 0;
504    pVoice->op3Out = 0;
505
506    /* initialize operators */
507    for (i = 0; i < 4; i++)
508    {
509        /* save operator data */
510        pVoice->oper[i].gain = vCfg->gain[i];
511        pVoice->oper[i].outputGain = vCfg->outputGain[i];
512        pVoice->oper[i].outputGain = vCfg->outputGain[i];
513
514        /* initalize operator */
515        pVoice->oper[i].phase = 0;
516    }
517
518    /* calculate pan */
519#if NUM_OUTPUT_CHANNELS == 2
520    FM_CalculatePan(vCfg->pan, &pVoice->gainLeft, &pVoice->gainRight);
521#endif
522}
523
524/*----------------------------------------------------------------------------
525 * FM_ProcessVoice()
526 *----------------------------------------------------------------------------
527 * Purpose:
528 * Synthesizes a buffer of samples based on calculated parameters.
529 *
530 * Inputs:
531 * nNumSamplesToAdd - number of samples to synthesize
532 * psEASData - pointer to overall EAS data structure
533 *
534 * Outputs:
535 *
536 * Side Effects:
537 *
538 * Notes:
539 * pOut is not used in the test version, but is passed as a
540 * courtesy to split architecture implementations. It can be used as
541 * as pointer to the interprocessor communications buffer when the
542 * synthesis parameters are passed off to a DSP for synthesis.
543 *----------------------------------------------------------------------------
544*/
545/*lint -esym(715, pOut) pOut not used in test version - see above */
546void FM_ProcessVoice (
547        EAS_I32 voiceNum,
548        S_FM_VOICE_FRAME *pFrame,
549        EAS_I32 numSamplesToAdd,
550        EAS_PCM *pTempBuffer,
551        EAS_PCM *pBuffer,
552        EAS_I32 *pMixBuffer,
553        EAS_FRAME_BUFFER_HANDLE pFrameBuffer)
554{
555    S_FM_ENG_VOICE *p;
556    EAS_PCM *pOutBuf;
557    EAS_PCM *pMod;
558    EAS_BOOL mix;
559    EAS_U8 feedback1;
560    EAS_U8 feedback3;
561    EAS_U8 mode;
562
563    /* establish pointer to voice data */
564    p = &voices[voiceNum];
565    mode = p->flags & 0x07;
566
567    /* lookup feedback values */
568    feedback1 = fmScaleTable[p->feedback >> 4];
569    feedback3 = fmScaleTable[p->feedback & 0x0f];
570
571    /* operator 3 is on output bus in modes 0, 1, and 3 */
572    if ((mode == 0) || (mode == 1) || (mode == 3))
573        pOutBuf = pBuffer;
574    else
575        pOutBuf = pTempBuffer;
576
577    if (p->flags & FLAG_FM_ENG_VOICE_OP3_NOISE)
578    {
579        FM_NoiseOperator(
580                p->oper + 2,
581                numSamplesToAdd,
582                pOutBuf,
583                EAS_FALSE,
584                pFrame->gain[2],
585                feedback3,
586                &p->op3Out);
587    }
588    else
589    {
590        FM_Operator(
591                p->oper + 2,
592                numSamplesToAdd,
593                pOutBuf,
594                0,
595                EAS_FALSE,
596                pFrame->gain[2],
597                pFrame->pitch[2],
598                feedback3,
599                &p->op3Out);
600    }
601
602    /* operator 4 is on output bus in modes 0, 1, and 2 */
603    if (mode < 3)
604        pOutBuf = pBuffer;
605    else
606        pOutBuf = pTempBuffer;
607
608    /* operator 4 is modulated in modes 2, 4, and 5 */
609    if ((mode == 2) || (mode == 4) || (mode == 5))
610        pMod = pTempBuffer;
611    else
612        pMod = 0;
613
614    /* operator 4 is in mix mode in modes 0 and 1 */
615    mix = (mode < 2);
616
617    if (p->flags & FLAG_FM_ENG_VOICE_OP4_NOISE)
618    {
619        FM_NoiseOperator(
620                p->oper + 3,
621                numSamplesToAdd,
622                pOutBuf,
623                mix,
624                pFrame->gain[3],
625                0,
626                0);
627    }
628    else
629    {
630        FM_Operator(
631                p->oper + 3,
632                numSamplesToAdd,
633                pOutBuf,
634                pMod,
635                mix,
636                pFrame->gain[3],
637                pFrame->pitch[3],
638                0,
639                0);
640    }
641
642    /* operator 1 is on output bus in mode 0 */
643    if (mode == 0)
644        pOutBuf = pBuffer;
645    else
646        pOutBuf = pTempBuffer;
647
648    /* operator 1 is modulated in modes 3 and 4 */
649    if ((mode == 3) || (mode == 4))
650        pMod = pTempBuffer;
651    else
652        pMod = 0;
653
654    /* operator 1 is in mix mode in modes 0 and 5 */
655    mix = ((mode == 0) || (mode == 5));
656
657    if (p->flags & FLAG_FM_ENG_VOICE_OP1_NOISE)
658    {
659        FM_NoiseOperator(
660                p->oper,
661                numSamplesToAdd,
662                pOutBuf,
663                mix,
664                pFrame->gain[0],
665                feedback1,
666                &p->op1Out);
667    }
668    else
669    {
670        FM_Operator(
671                p->oper,
672                numSamplesToAdd,
673                pOutBuf,
674                pMod,
675                mix,
676                pFrame->gain[0],
677                pFrame->pitch[0],
678                feedback1,
679                &p->op1Out);
680    }
681
682    /* operator 2 is modulated in all modes except 0 */
683    if (mode != 0)
684        pMod = pTempBuffer;
685    else
686        pMod = 0;
687
688    /* operator 1 is in mix mode in modes 0 -3 */
689    mix = (mode < 4);
690
691    if (p->flags & FLAG_FM_ENG_VOICE_OP2_NOISE)
692    {
693        FM_NoiseOperator(
694                p->oper + 1,
695                numSamplesToAdd,
696                pBuffer,
697                mix,
698                pFrame->gain[1],
699                0,
700                0);
701    }
702    else
703    {
704        FM_Operator(
705                p->oper + 1,
706                numSamplesToAdd,
707                pBuffer,
708                pMod,
709                mix,
710                pFrame->gain[1],
711                pFrame->pitch[1],
712                0,
713                0);
714    }
715
716    /* mix voice output to synthesizer output buffer */
717    FM_SynthMixVoice(p, pFrame->voiceGain, numSamplesToAdd, pBuffer, pMixBuffer);
718}
719
720/*----------------------------------------------------------------------------
721 * FM_SynthMixVoice()
722 *----------------------------------------------------------------------------
723 * Purpose:
724 * Mixes the voice output buffer into the final mix using an anti-zipper
725 * filter.
726 *
727 * Inputs:
728 * nNumSamplesToAdd - number of samples to synthesize
729 * psEASData - pointer to overall EAS data structure
730 *
731 * Outputs:
732 *
733 * Side Effects:
734 *
735 *----------------------------------------------------------------------------
736*/
737void FM_SynthMixVoice(S_FM_ENG_VOICE *p,  EAS_U16 nGainTarget, EAS_I32 numSamplesToAdd, EAS_PCM *pInputBuffer, EAS_I32 *pBuffer)
738{
739    EAS_I32 nGain;
740    EAS_I32 nGainInc;
741    EAS_I32 nTemp;
742
743    /* restore previous gain */
744    /*lint -e{703} <use shift for performance> */
745    nGain = (EAS_I32) p->voiceGain << 16;
746
747    /* calculate gain increment */
748    /*lint -e{703} <use shift for performance> */
749    nGainInc = ((EAS_I32) nGainTarget - (EAS_I32) p->voiceGain) << (16 - SYNTH_UPDATE_PERIOD_IN_BITS);
750
751    /* mix the output buffer */
752    while (numSamplesToAdd--)
753    {
754        /* output gain calculation */
755        nTemp = *pInputBuffer++;
756
757        /* sum to output buffer */
758#if (NUM_OUTPUT_CHANNELS == 2)
759
760        /*lint -e{704} <use shift for performance> */
761        nTemp = ((EAS_I32) nTemp * (nGain >> 16)) >> FM_GAIN_SHIFT;
762
763        /*lint -e{704} <use shift for performance> */
764        {
765            EAS_I32 nTemp2;
766            nTemp = nTemp >> FM_STEREO_PRE_GAIN_SHIFT;
767            nTemp2 = (nTemp * p->gainLeft) >> FM_STEREO_POST_GAIN_SHIFT;
768            *pBuffer++ += nTemp2;
769            nTemp2 = (nTemp * p->gainRight) >> FM_STEREO_POST_GAIN_SHIFT;
770            *pBuffer++ += nTemp2;
771        }
772#else
773        /*lint -e{704} <use shift for performance> */
774        nTemp = ((EAS_I32) nTemp * (nGain >> 16)) >> FM_MONO_GAIN_SHIFT;
775        *pBuffer++ += nTemp;
776#endif
777
778        /* increment gain for anti-zipper filter */
779        nGain += nGainInc;
780    }
781
782    /* save gain */
783    p->voiceGain = nGainTarget;
784}
785
786