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