Home | History | Annotate | Download | only in lib_src
      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 */
     41 extern const EAS_I16 sineTable[];
     42 extern 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 
     48 static S_FM_ENG_VOICE voices[NUM_FM_VOICES];
     49 
     50 /* local prototypes */
     51 void 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)
     55 static 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 */
     74 EAS_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 */
     96 EAS_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 */
    129 EAS_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 */
    152 static 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 */
    208 static 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 */
    272 void 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 */
    384 void 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 */
    489 void 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 */
    546 void 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 */
    737 void 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