eas_fmengine.c revision e442bb7cd6a085b33a4dd52c0e20a157ada7feb1
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