Home | History | Annotate | Download | only in lib_src
      1 /*----------------------------------------------------------------------------
      2  *
      3  * File:
      4  * fmsynth.c
      5  *
      6  * Contents and purpose:
      7  * Implements the high-level FM synthesizer functions.
      8  *
      9  * Copyright Sonic Network Inc. 2004
     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_host.h"
     32 #include "eas_report.h"
     33 
     34 #include "eas_data.h"
     35 #include "eas_synth_protos.h"
     36 #include "eas_audioconst.h"
     37 #include "eas_fmengine.h"
     38 #include "eas_math.h"
     39 
     40 /* option sanity check */
     41 #ifdef _REVERB
     42 #error "No reverb for FM synthesizer"
     43 #endif
     44 #ifdef _CHORUS
     45 #error "No chorus for FM synthesizer"
     46 #endif
     47 
     48 /*
     49  * WARNING: These macros can cause unwanted side effects. Use them
     50  * with care. For example, min(x++,y++) will cause either x or y to be
     51  * incremented twice.
     52  */
     53 #define min(a,b) ((a) < (b) ? (a) : (b))
     54 #define max(a,b) ((a) > (b) ? (a) : (b))
     55 
     56 /* pivot point for keyboard scalars */
     57 #define EG_SCALE_PIVOT_POINT 64
     58 #define KEY_SCALE_PIVOT_POINT 36
     59 
     60 /* This number is the negative of the frequency of the note (in cents) of
     61  * the sine wave played at unity. The number can be calculated as follows:
     62  *
     63  * MAGIC_NUMBER = 1200 * log(base2) (SINE_TABLE_SIZE * 8.175798916 / SAMPLE_RATE)
     64  *
     65  * 8.17578 is a reference to the frequency of MIDI note 0
     66  */
     67 #if defined (_SAMPLE_RATE_8000)
     68 #define MAGIC_NUMBER 1279
     69 #elif   defined (_SAMPLE_RATE_16000)
     70 #define MAGIC_NUMBER 79
     71 #elif   defined (_SAMPLE_RATE_20000)
     72 #define MAGIC_NUMBER -308
     73 #elif   defined (_SAMPLE_RATE_22050)
     74 #define MAGIC_NUMBER -477
     75 #elif   defined (_SAMPLE_RATE_24000)
     76 #define MAGIC_NUMBER -623
     77 #elif defined (_SAMPLE_RATE_32000)
     78 #define MAGIC_NUMBER -1121
     79 #elif defined (_SAMPLE_RATE_44100)
     80 #define MAGIC_NUMBER -1677
     81 #elif defined (_SAMPLE_RATE_48000)
     82 #define MAGIC_NUMBER -1823
     83 #endif
     84 
     85 /* externs */
     86 extern const EAS_I16 fmControlTable[128];
     87 extern const EAS_U16 fmRateTable[256];
     88 extern const EAS_U16 fmAttackTable[16];
     89 extern const EAS_U8 fmDecayTable[16];
     90 extern const EAS_U8 fmReleaseTable[16];
     91 extern const EAS_U8 fmScaleTable[16];
     92 
     93 /* local prototypes */
     94 /*lint -esym(715, pVoiceMgr) standard synthesizer interface - pVoiceMgr not used */
     95 static EAS_RESULT FM_Initialize (S_VOICE_MGR *pVoiceMgr) { return EAS_SUCCESS; }
     96 static EAS_RESULT FM_StartVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice, EAS_I32 voiceNum, EAS_U16 regionIndex);
     97 static EAS_BOOL FM_UpdateVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice, EAS_I32 voiceNum, EAS_I32 *pMixBuffer, EAS_I32 numSamples);
     98 static void FM_ReleaseVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice, EAS_I32 voiceNum);
     99 static void FM_MuteVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice, EAS_I32 voiceNum);
    100 static void FM_SustainPedal (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice, S_SYNTH_CHANNEL *pChannel, EAS_I32 voiceNum);
    101 static void FM_UpdateChannel (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel);
    102 
    103 
    104 /*----------------------------------------------------------------------------
    105  * Synthesizer interface
    106  *----------------------------------------------------------------------------
    107 */
    108 const S_SYNTH_INTERFACE fmSynth =
    109 {
    110     FM_Initialize,
    111     FM_StartVoice,
    112     FM_UpdateVoice,
    113     FM_ReleaseVoice,
    114     FM_MuteVoice,
    115     FM_SustainPedal,
    116     FM_UpdateChannel
    117 };
    118 
    119 #ifdef FM_OFFBOARD
    120 const S_FRAME_INTERFACE fmFrameInterface =
    121 {
    122     FM_StartFrame,
    123     FM_EndFrame
    124 };
    125 #endif
    126 
    127 /*----------------------------------------------------------------------------
    128  * inline functions
    129  *----------------------------------------------------------------------------
    130  */
    131 EAS_INLINE S_FM_VOICE *GetFMVoicePtr (S_VOICE_MGR *pVoiceMgr, EAS_INT voiceNum)
    132 {
    133     return &pVoiceMgr->fmVoices[voiceNum];
    134 }
    135 EAS_INLINE S_SYNTH_CHANNEL *GetChannelPtr (S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice)
    136 {
    137     return &pSynth->channels[pVoice->channel & 15];
    138 }
    139 EAS_INLINE const S_FM_REGION *GetFMRegionPtr (S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice)
    140 {
    141 #ifdef _SECONDARY_SYNTH
    142     return &pSynth->pEAS->pFMRegions[pVoice->regionIndex & REGION_INDEX_MASK];
    143 #else
    144     return &pSynth->pEAS->pFMRegions[pVoice->regionIndex];
    145 #endif
    146 }
    147 
    148 /*----------------------------------------------------------------------------
    149  * FM_SynthIsOutputOperator
    150  *----------------------------------------------------------------------------
    151  * Purpose:
    152  * Returns true if the operator is a direct output and not muted
    153  *
    154  * Inputs:
    155  *
    156  * Outputs:
    157  * Returns boolean
    158  *----------------------------------------------------------------------------
    159 */
    160 static EAS_BOOL FM_SynthIsOutputOperator (const S_FM_REGION *pRegion, EAS_INT operIndex)
    161 {
    162 
    163     /* see if voice is muted */
    164     if ((pRegion->oper[operIndex].gain & 0xfc) == 0)
    165         return 0;
    166 
    167     /* check based on mode */
    168     switch (pRegion->region.keyGroupAndFlags & 7)
    169     {
    170 
    171         /* mode 0 - all operators are external */
    172         case 0:
    173             return EAS_TRUE;
    174 
    175         /* mode 1 - operators 1-3 are external */
    176         case 1:
    177             if (operIndex != 0)
    178                 return EAS_TRUE;
    179         break;
    180 
    181         /* mode 2 - operators 1 & 3 are external */
    182         case 2:
    183             if ((operIndex == 1) || (operIndex == 3))
    184                 return EAS_TRUE;
    185             break;
    186 
    187         /* mode 2 - operators 1 & 2 are external */
    188         case 3:
    189             if ((operIndex == 1) || (operIndex == 2))
    190                 return EAS_TRUE;
    191             break;
    192 
    193         /* modes 4 & 5 - operator 1 is external */
    194         case 4:
    195         case 5:
    196             if (operIndex == 1)
    197                 return EAS_TRUE;
    198             break;
    199 
    200         default:
    201             { /* dpp: EAS_ReportEx(_EAS_SEVERITY_FATAL,"Invalid voice mode: %d",
    202                 pRegion->region.keyGroupAndFlags & 7); */ }
    203     }
    204 
    205     return EAS_FALSE;
    206 }
    207 
    208 /*----------------------------------------------------------------------------
    209  * FM_CalcEGRate()
    210  *----------------------------------------------------------------------------
    211  * Purpose:
    212  *
    213  * Inputs:
    214  * nKeyNumber - MIDI note
    215  * nLogRate - logarithmic scale rate from patch data
    216  * nKeyScale - key scaling factor for this EG
    217  *
    218  * Outputs:
    219  * 16-bit linear multiplier
    220  *----------------------------------------------------------------------------
    221 */
    222 
    223 static EAS_U16 FM_CalcEGRate (EAS_U8 nKeyNumber, EAS_U8 nLogRate, EAS_U8 nEGScale)
    224 {
    225     EAS_I32 temp;
    226 
    227     /* incorporate key scaling on release rate */
    228     temp = (EAS_I32) nLogRate << 7;
    229     temp += ((EAS_I32) nKeyNumber - EG_SCALE_PIVOT_POINT) * (EAS_I32) nEGScale;
    230 
    231     /* saturate */
    232     temp = max(temp, 0);
    233     temp = min(temp, 32767);
    234 
    235     /* look up in rate table */
    236     /*lint -e{704} use shift for performance */
    237     return fmRateTable[temp >> 8];
    238 }
    239 
    240 /*----------------------------------------------------------------------------
    241  * FM_ReleaseVoice()
    242  *----------------------------------------------------------------------------
    243  * Purpose:
    244  * The selected voice is being released.
    245  *
    246  * Inputs:
    247  * psEASData - pointer to S_EAS_DATA
    248  * pVoice - pointer to voice to release
    249  *
    250  * Outputs:
    251  * None
    252  *----------------------------------------------------------------------------
    253 */
    254 static void FM_ReleaseVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice, EAS_I32 voiceNum)
    255 {
    256     EAS_INT operIndex;
    257     const S_FM_REGION *pRegion;
    258     S_FM_VOICE *pFMVoice;
    259 
    260     /* check to see if voice responds to NOTE-OFF's */
    261     pRegion = GetFMRegionPtr(pSynth, pVoice);
    262     if (pRegion->region.keyGroupAndFlags & REGION_FLAG_ONE_SHOT)
    263         return;
    264 
    265     /* set all envelopes to release state */
    266     pFMVoice = GetFMVoicePtr(pVoiceMgr, voiceNum);
    267     for (operIndex = 0; operIndex < 4; operIndex++)
    268     {
    269         pFMVoice->oper[operIndex].envState = eFMEnvelopeStateRelease;
    270 
    271         /* incorporate key scaling on release rate */
    272         pFMVoice->oper[operIndex].envRate = FM_CalcEGRate(
    273                 pVoice->note,
    274                 fmReleaseTable[pRegion->oper[operIndex].velocityRelease & 0x0f],
    275                 fmScaleTable[pRegion->oper[operIndex].egKeyScale >> 4]);
    276     } /* end for (operIndex = 0; operIndex < 4; operIndex++) */
    277 }
    278 
    279 /*----------------------------------------------------------------------------
    280  * FM_MuteVoice()
    281  *----------------------------------------------------------------------------
    282  * Purpose:
    283  * The selected voice is being muted.
    284  *
    285  * Inputs:
    286  * pVoice - pointer to voice to release
    287  *
    288  * Outputs:
    289  * None
    290  *----------------------------------------------------------------------------
    291 */
    292 /*lint -esym(715, pSynth) standard interface, pVoiceMgr not used */
    293 static void FM_MuteVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice, EAS_I32 voiceNum)
    294 {
    295     S_FM_VOICE *pFMVoice;
    296 
    297     /* clear deferred action flags */
    298     pVoice->voiceFlags &=
    299         ~(VOICE_FLAG_DEFER_MIDI_NOTE_OFF |
    300         VOICE_FLAG_SUSTAIN_PEDAL_DEFER_NOTE_OFF |
    301         VOICE_FLAG_DEFER_MUTE);
    302 
    303     /* set all envelopes to muted state */
    304     pFMVoice = GetFMVoicePtr(pVoiceMgr, voiceNum);
    305     pFMVoice->oper[0].envState = eFMEnvelopeStateMuted;
    306     pFMVoice->oper[1].envState = eFMEnvelopeStateMuted;
    307     pFMVoice->oper[2].envState = eFMEnvelopeStateMuted;
    308     pFMVoice->oper[3].envState = eFMEnvelopeStateMuted;
    309 }
    310 
    311 /*----------------------------------------------------------------------------
    312  * FM_SustainPedal()
    313  *----------------------------------------------------------------------------
    314  * Purpose:
    315  * The selected voice is held due to sustain pedal
    316  *
    317  * Inputs:
    318  * pVoice - pointer to voice to sustain
    319  *
    320  * Outputs:
    321  * None
    322  *----------------------------------------------------------------------------
    323 */
    324 /*lint -esym(715, pChannel) standard interface, pVoiceMgr not used */
    325 static void FM_SustainPedal (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice, S_SYNTH_CHANNEL *pChannel, EAS_I32 voiceNum)
    326 {
    327     const S_FM_REGION *pRegion;
    328     S_FM_VOICE *pFMVoice;
    329     EAS_INT operIndex;
    330 
    331     pRegion = GetFMRegionPtr(pSynth, pVoice);
    332     pFMVoice = GetFMVoicePtr(pVoiceMgr, voiceNum);
    333 
    334     /* check to see if any envelopes are above the sustain level */
    335     for (operIndex = 0; operIndex < 4; operIndex++)
    336     {
    337 
    338         /* if level control or envelope gain is zero, skip this envelope */
    339         if (((pRegion->oper[operIndex].gain & 0xfc) == 0) ||
    340             (pFMVoice->oper[operIndex].envGain == 0))
    341         {
    342             continue;
    343         }
    344 
    345         /* if the envelope gain is above the sustain level, we need to catch this voice */
    346         if (pFMVoice->oper[operIndex].envGain >= ((EAS_U16) (pRegion->oper[operIndex].sustain & 0xfc) << 7))
    347         {
    348 
    349             /* reset envelope to decay state */
    350             pFMVoice->oper[operIndex].envState = eFMEnvelopeStateDecay;
    351 
    352             pFMVoice->oper[operIndex].envRate = FM_CalcEGRate(
    353                     pVoice->note,
    354                     fmDecayTable[pRegion->oper[operIndex].attackDecay & 0x0f],
    355                     fmScaleTable[pRegion->oper[operIndex].egKeyScale >> 4]);
    356 
    357             /* set voice to decay state */
    358             pVoice->voiceState = eVoiceStatePlay;
    359 
    360             /* set sustain flag */
    361             pVoice->voiceFlags |= VOICE_FLAG_SUSTAIN_PEDAL_DEFER_NOTE_OFF;
    362         }
    363     } /* end for (operIndex = 0; operIndex < 4; operIndex++) */
    364 }
    365 
    366 /*----------------------------------------------------------------------------
    367  * FM_StartVoice()
    368  *----------------------------------------------------------------------------
    369  * Purpose:
    370  * Assign the region for the given instrument using the midi key number
    371  * and the RPN2 (coarse tuning) value. By using RPN2 as part of the
    372  * region selection process, we reduce the amount a given sample has
    373  * to be transposed by selecting the closest recorded root instead.
    374  *
    375  * This routine is the second half of SynthAssignRegion().
    376  * If the region was successfully found by SynthFindRegionIndex(),
    377  * then assign the region's parameters to the voice.
    378  *
    379  * Setup and initialize the following voice parameters:
    380  * m_nRegionIndex
    381  *
    382  * Inputs:
    383  * pVoice - ptr to the voice we have assigned for this channel
    384  * nRegionIndex - index of the region
    385  * psEASData - pointer to overall EAS data structure
    386  *
    387  * Outputs:
    388  * success - could find and assign the region for this voice's note otherwise
    389  * failure - could not find nor assign the region for this voice's note
    390  *
    391  * Side Effects:
    392  * psSynthObject->m_sVoice[].m_nRegionIndex is assigned
    393  * psSynthObject->m_sVoice[] parameters are assigned
    394  *----------------------------------------------------------------------------
    395 */
    396 static EAS_RESULT FM_StartVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice, EAS_I32 voiceNum, EAS_U16 regionIndex)
    397 {
    398     S_FM_VOICE *pFMVoice;
    399     S_SYNTH_CHANNEL *pChannel;
    400     const S_FM_REGION *pRegion;
    401     EAS_I32 temp;
    402     EAS_INT operIndex;
    403 
    404     /* establish pointers to data */
    405     pVoice->regionIndex = regionIndex;
    406     pFMVoice = GetFMVoicePtr(pVoiceMgr, voiceNum);
    407     pChannel = GetChannelPtr(pSynth, pVoice);
    408     pRegion = GetFMRegionPtr(pSynth, pVoice);
    409 
    410     /* update static channel parameters */
    411     if (pChannel->channelFlags & CHANNEL_FLAG_UPDATE_CHANNEL_PARAMETERS)
    412         FM_UpdateChannel(pVoiceMgr, pSynth, pVoice->channel & 15);
    413 
    414     /* init the LFO */
    415     pFMVoice->lfoValue = 0;
    416     pFMVoice->lfoPhase = 0;
    417     pFMVoice->lfoDelay = (EAS_U16) (fmScaleTable[pRegion->lfoFreqDelay & 0x0f] >> 1);
    418 
    419 #if (NUM_OUTPUT_CHANNELS == 2)
    420     /* calculate pan gain values only if stereo output */
    421     /* set up panning only at note start */
    422     temp = (EAS_I32) pChannel->pan - 64;
    423     temp += (EAS_I32) pRegion->pan;
    424     if (temp < -64)
    425         temp = -64;
    426     if (temp > 64)
    427         temp = 64;
    428     pFMVoice->pan = (EAS_I8) temp;
    429 #endif /* #if (NUM_OUTPUT_CHANNELS == 2) */
    430 
    431     /* no samples have been synthesized for this note yet */
    432     pVoice->voiceFlags = VOICE_FLAG_NO_SAMPLES_SYNTHESIZED_YET;
    433 
    434     /* initialize gain value for anti-zipper filter */
    435     pFMVoice->voiceGain = (EAS_I16) EAS_LogToLinear16(pChannel->staticGain);
    436     pFMVoice->voiceGain = (EAS_I16) FMUL_15x15(pFMVoice->voiceGain, pSynth->masterVolume);
    437 
    438     /* initialize the operators */
    439     for (operIndex = 0; operIndex < 4; operIndex++)
    440     {
    441 
    442         /* establish operator output gain level */
    443         /*lint -e{701} <use shift for performance> */
    444         pFMVoice->oper[operIndex].outputGain = EAS_LogToLinear16(((EAS_I16) (pRegion->oper[operIndex].gain & 0xfc) - 0xfc) << 7);
    445 
    446         /* check for linear velocity flag */
    447         /*lint -e{703} <use shift for performance> */
    448         if (pRegion->oper[operIndex].flags & FM_OPER_FLAG_LINEAR_VELOCITY)
    449             temp = (EAS_I32) (pVoice->velocity - 127) << 5;
    450         else
    451             temp = (EAS_I32) fmControlTable[pVoice->velocity];
    452 
    453         /* scale velocity */
    454         /*lint -e{704} use shift for performance */
    455         temp = (temp * (EAS_I32)(pRegion->oper[operIndex].velocityRelease & 0xf0)) >> 7;
    456 
    457         /* include key scalar */
    458         temp -= ((EAS_I32) pVoice->note - KEY_SCALE_PIVOT_POINT) * (EAS_I32) fmScaleTable[pRegion->oper[operIndex].egKeyScale & 0x0f];
    459 
    460         /* saturate */
    461         temp = min(temp, 0);
    462         temp = max(temp, -32768);
    463 
    464         /* save static gain parameters */
    465         pFMVoice->oper[operIndex].baseGain = (EAS_I16) EAS_LogToLinear16(temp);
    466 
    467         /* incorporate key scaling on decay rate */
    468         pFMVoice->oper[operIndex].envRate = FM_CalcEGRate(
    469             pVoice->note,
    470             fmDecayTable[pRegion->oper[operIndex].attackDecay & 0x0f],
    471             fmScaleTable[pRegion->oper[operIndex].egKeyScale >> 4]);
    472 
    473         /* if zero attack time, max out envelope and jump to decay state */
    474         if ((pRegion->oper[operIndex].attackDecay & 0xf0) == 0xf0)
    475         {
    476 
    477             /* start out envelope at max */
    478             pFMVoice->oper[operIndex].envGain = 0x7fff;
    479 
    480             /* set envelope to decay state */
    481             pFMVoice->oper[operIndex].envState = eFMEnvelopeStateDecay;
    482         }
    483 
    484         /* start envelope at zero and start in attack state */
    485         else
    486         {
    487             pFMVoice->oper[operIndex].envGain = 0;
    488             pFMVoice->oper[operIndex].envState = eFMEnvelopeStateAttack;
    489         }
    490     }
    491 
    492     return EAS_SUCCESS;
    493 }
    494 
    495 /*----------------------------------------------------------------------------
    496  * FM_UpdateChannel()
    497  *----------------------------------------------------------------------------
    498  * Purpose:
    499  * Calculate and assign static channel parameters
    500  * These values only need to be updated if one of the controller values
    501  * for this channel changes.
    502  * Called when CHANNEL_FLAG_UPDATE_CHANNEL_PARAMETERS flag is set.
    503  *
    504  * Inputs:
    505  * nChannel - channel to update
    506  * psEASData - pointer to overall EAS data structure
    507  *
    508  * Outputs:
    509  *
    510  * Side Effects:
    511  * - the given channel's static gain and static pitch are updated
    512  *----------------------------------------------------------------------------
    513 */
    514 /*lint -esym(715, pVoiceMgr) standard interface, pVoiceMgr not used */
    515 static void FM_UpdateChannel (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel)
    516 {
    517     S_SYNTH_CHANNEL *pChannel;
    518     EAS_I32 temp;
    519 
    520     pChannel = &pSynth->channels[channel];
    521 
    522     /* convert CC7 volume controller to log scale */
    523     temp = fmControlTable[pChannel->volume];
    524 
    525     /* incorporate CC11 expression controller */
    526     temp += fmControlTable[pChannel->expression];
    527 
    528     /* saturate */
    529     pChannel->staticGain = (EAS_I16) max(temp,-32768);
    530 
    531     /* calculate pitch bend */
    532     /*lint -e{703} <avoid multiply for performance>*/
    533     temp = (((EAS_I32)(pChannel->pitchBend) << 2) - 32768);
    534 
    535     temp = FMUL_15x15(temp, pChannel->pitchBendSensitivity);
    536 
    537     /* include "magic number" compensation for sample rate and lookup table size */
    538     temp += MAGIC_NUMBER;
    539 
    540     /* if this is not a drum channel, then add in the per-channel tuning */
    541     if (!(pChannel->channelFlags & CHANNEL_FLAG_RHYTHM_CHANNEL))
    542         temp += (pChannel->finePitch + (pChannel->coarsePitch * 100));
    543 
    544     /* save static pitch */
    545     pChannel->staticPitch = temp;
    546 
    547     /* Calculate LFO modulation depth */
    548     /* mod wheel to LFO depth */
    549     temp = FMUL_15x15(DEFAULT_LFO_MOD_WHEEL_TO_PITCH_CENTS,
    550     pChannel->modWheel << (NUM_EG1_FRAC_BITS -7));
    551 
    552     /* channel pressure to LFO depth */
    553     pChannel->lfoAmt = (EAS_I16) (temp +
    554     FMUL_15x15(DEFAULT_LFO_CHANNEL_PRESSURE_TO_PITCH_CENTS,
    555     pChannel->channelPressure << (NUM_EG1_FRAC_BITS -7)));
    556 
    557     /* clear update flag */
    558     pChannel->channelFlags &= ~CHANNEL_FLAG_UPDATE_CHANNEL_PARAMETERS;
    559     return;
    560 }
    561 
    562 /*----------------------------------------------------------------------------
    563  * FM_UpdateLFO()
    564  *----------------------------------------------------------------------------
    565  * Purpose:
    566  * Calculate the LFO for the given voice
    567  *
    568  * Inputs:
    569  * pVoice - ptr to the voice whose LFO we want to update
    570  * psEASData - pointer to overall EAS data structure - used for debug only
    571  *
    572  * Outputs:
    573  *
    574  * Side Effects:
    575  * - updates LFO values for the given voice
    576  *----------------------------------------------------------------------------
    577 */
    578 static void FM_UpdateLFO (S_FM_VOICE *pFMVoice, const S_FM_REGION *pRegion)
    579 {
    580 
    581     /* increment the LFO phase if the delay time has elapsed */
    582     if (!pFMVoice->lfoDelay)
    583     {
    584         /*lint -e{701} <use shift for performance> */
    585         pFMVoice->lfoPhase = pFMVoice->lfoPhase + (EAS_U16) (-fmControlTable[((15 - (pRegion->lfoFreqDelay >> 4)) << 3) + 4]);
    586 
    587         /* square wave LFO? */
    588         if (pRegion->region.keyGroupAndFlags & REGION_FLAG_SQUARE_WAVE)
    589             pFMVoice->lfoValue = (EAS_I16)(pFMVoice->lfoPhase & 0x8000 ? -32767 : 32767);
    590 
    591         /* trick to get a triangle wave out of a sawtooth */
    592         else
    593         {
    594             pFMVoice->lfoValue = (EAS_I16) (pFMVoice->lfoPhase << 1);
    595             /*lint -e{502} <shortcut to turn sawtooth into sine wave> */
    596             if ((pFMVoice->lfoPhase > 0x3fff) && (pFMVoice->lfoPhase < 0xC000))
    597                 pFMVoice->lfoValue = ~pFMVoice->lfoValue;
    598         }
    599     }
    600 
    601     /* still in delay */
    602     else
    603         pFMVoice->lfoDelay--;
    604 
    605     return;
    606 }
    607 
    608 /*----------------------------------------------------------------------------
    609  * FM_UpdateEG()
    610  *----------------------------------------------------------------------------
    611  * Purpose:
    612  * Calculate the synthesis parameters for an operator to be used during
    613  * the next buffer
    614  *
    615  * Inputs:
    616  * pVoice - pointer to the voice being updated
    617  * psEASData - pointer to overall EAS data structure
    618  *
    619  * Outputs:
    620  *
    621  * Side Effects:
    622  *
    623  *----------------------------------------------------------------------------
    624 */
    625 static EAS_BOOL FM_UpdateEG (S_SYNTH_VOICE *pVoice, S_OPERATOR *pOper, const S_FM_OPER *pOperData, EAS_BOOL oneShot)
    626 {
    627     EAS_U32 temp;
    628     EAS_BOOL done;
    629 
    630     /* set flag assuming the envelope is not done */
    631     done = EAS_FALSE;
    632 
    633     /* take appropriate action based on state */
    634     switch (pOper->envState)
    635     {
    636 
    637         case eFMEnvelopeStateAttack:
    638 
    639             /* the envelope is linear during the attack, so add the value */
    640             temp = pOper->envGain + fmAttackTable[pOperData->attackDecay >> 4];
    641 
    642             /* check for end of attack */
    643             if (temp >= 0x7fff)
    644             {
    645                 pOper->envGain = 0x7fff;
    646                 pOper->envState = eFMEnvelopeStateDecay;
    647             }
    648             else
    649                 pOper->envGain = (EAS_U16) temp;
    650             break;
    651 
    652         case eFMEnvelopeStateDecay:
    653 
    654             /* decay is exponential, multiply by decay rate */
    655             pOper->envGain = (EAS_U16) FMUL_15x15(pOper->envGain, pOper->envRate);
    656 
    657             /* check for sustain level reached */
    658             temp = (EAS_U32) (pOperData->sustain & 0xfc) << 7;
    659             if (pOper->envGain <= (EAS_U16) temp)
    660             {
    661                 /* if this is a one-shot patch, go directly to release phase */
    662                 if (oneShot)
    663                 {
    664                     pOper->envRate = FM_CalcEGRate(
    665                     pVoice->note,
    666                     fmReleaseTable[pOperData->velocityRelease & 0x0f],
    667                     fmScaleTable[pOperData->egKeyScale >> 4]);
    668                     pOper->envState = eFMEnvelopeStateRelease;
    669                 }
    670 
    671                 /* normal sustaining type */
    672                 else
    673                 {
    674                     pOper->envGain = (EAS_U16) temp;
    675                     pOper->envState = eFMEnvelopeStateSustain;
    676                 }
    677             }
    678             break;
    679 
    680         case eFMEnvelopeStateSustain:
    681             pOper->envGain = (EAS_U16)((EAS_U16)(pOperData->sustain & 0xfc) << 7);
    682             break;
    683 
    684         case eFMEnvelopeStateRelease:
    685 
    686             /* release is exponential, multiply by release rate */
    687             pOper->envGain = (EAS_U16) FMUL_15x15(pOper->envGain, pOper->envRate);
    688 
    689             /* fully released */
    690             if (pOper->envGain == 0)
    691             {
    692                 pOper->envGain = 0;
    693                 pOper->envState = eFMEnvelopeStateMuted;
    694                 done = EAS_TRUE;
    695             }
    696             break;
    697 
    698         case eFMEnvelopeStateMuted:
    699             pOper->envGain = 0;
    700             done = EAS_TRUE;
    701             break;
    702         default:
    703             { /* dpp: EAS_ReportEx(_EAS_SEVERITY_FATAL,"Invalid operator state: %d", pOper->envState); */ }
    704     } /* end switch (pOper->m_eState) */
    705 
    706     return done;
    707 }
    708 
    709 /*----------------------------------------------------------------------------
    710  * FM_UpdateDynamic()
    711  *----------------------------------------------------------------------------
    712  * Purpose:
    713  * Calculate the synthesis parameters for this voice that will be used to
    714  * synthesize the next buffer
    715  *
    716  * Inputs:
    717  * pVoice - pointer to the voice being updated
    718  * psEASData - pointer to overall EAS data structure
    719  *
    720  * Outputs:
    721  * Returns EAS_TRUE if voice will be fully ramped to zero at the end of
    722  * the next synthesized buffer.
    723  *
    724  * Side Effects:
    725  *
    726  *----------------------------------------------------------------------------
    727 */
    728 static EAS_BOOL FM_UpdateDynamic (S_SYNTH_VOICE *pVoice, S_FM_VOICE *pFMVoice, const S_FM_REGION *pRegion, S_SYNTH_CHANNEL *pChannel)
    729 {
    730     EAS_I32 temp;
    731     EAS_I32 pitch;
    732     EAS_I32 lfoPitch;
    733     EAS_INT operIndex;
    734     EAS_BOOL done;
    735 
    736     /* increment LFO phase */
    737     FM_UpdateLFO(pFMVoice, pRegion);
    738 
    739     /* base pitch in cents */
    740     pitch = pVoice->note * 100;
    741 
    742     /* LFO amount includes LFO depth from programming + channel dynamics */
    743     temp = (fmScaleTable[pRegion->vibTrem >> 4] >> 1) + pChannel->lfoAmt;
    744 
    745     /* multiply by LFO output to get final pitch modulation */
    746     lfoPitch = FMUL_15x15(pFMVoice->lfoValue, temp);
    747 
    748     /* flag to indicate this voice is done */
    749     done = EAS_TRUE;
    750 
    751     /* iterate through operators to establish parameters */
    752     for (operIndex = 0; operIndex < 4; operIndex++)
    753     {
    754         EAS_BOOL bTemp;
    755 
    756         /* set base phase increment for each operator */
    757         temp = pRegion->oper[operIndex].tuning +
    758         pChannel->staticPitch;
    759 
    760         /* add vibrato effect unless it is disabled for this operator */
    761         if ((pRegion->oper[operIndex].flags & FM_OPER_FLAG_NO_VIBRATO) == 0)
    762             temp += lfoPitch;
    763 
    764         /* if note is monotonic, bias to MIDI note 60 */
    765         if (pRegion->oper[operIndex].flags & FM_OPER_FLAG_MONOTONE)
    766             temp += 6000;
    767         else
    768             temp += pitch;
    769         pFMVoice->oper[operIndex].pitch = (EAS_I16) temp;
    770 
    771         /* calculate envelope, returns true if done */
    772         bTemp = FM_UpdateEG(pVoice, &pFMVoice->oper[operIndex], &pRegion->oper[operIndex], pRegion->region.keyGroupAndFlags & REGION_FLAG_ONE_SHOT);
    773 
    774         /* check if all output envelopes have completed */
    775         if (FM_SynthIsOutputOperator(pRegion, operIndex))
    776             done = done && bTemp;
    777     }
    778 
    779     return done;
    780 }
    781 
    782 /*----------------------------------------------------------------------------
    783  * FM_UpdateVoice()
    784  *----------------------------------------------------------------------------
    785  * Purpose:
    786  * Synthesize a block of samples for the given voice.
    787  *
    788  * Inputs:
    789  * psEASData - pointer to overall EAS data structure
    790  *
    791  * Outputs:
    792  * number of samples actually written to buffer
    793  *
    794  * Side Effects:
    795  * - samples are added to the presently free buffer
    796  *
    797  *----------------------------------------------------------------------------
    798 */
    799 static EAS_BOOL FM_UpdateVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice, EAS_I32 voiceNum, EAS_I32 *pMixBuffer, EAS_I32 numSamples)
    800 {
    801     S_SYNTH_CHANNEL *pChannel;
    802     const S_FM_REGION *pRegion;
    803     S_FM_VOICE *pFMVoice;
    804     S_FM_VOICE_CONFIG vCfg;
    805     S_FM_VOICE_FRAME vFrame;
    806     EAS_I32 temp;
    807     EAS_INT oper;
    808     EAS_U16 voiceGainTarget;
    809     EAS_BOOL done;
    810 
    811     /* setup some pointers */
    812     pChannel = GetChannelPtr(pSynth, pVoice);
    813     pRegion = GetFMRegionPtr(pSynth, pVoice);
    814     pFMVoice = GetFMVoicePtr(pVoiceMgr, voiceNum);
    815 
    816     /* if the voice is just starting, get the voice configuration data */
    817     if (pVoice->voiceFlags & VOICE_FLAG_NO_SAMPLES_SYNTHESIZED_YET)
    818     {
    819 
    820         /* split architecture may limit the number of voice starts */
    821 #ifdef MAX_VOICE_STARTS
    822         if (pVoiceMgr->numVoiceStarts == MAX_VOICE_STARTS)
    823             return EAS_FALSE;
    824         pVoiceMgr->numVoiceStarts++;
    825 #endif
    826 
    827         /* get initial parameters */
    828         vCfg.feedback = pRegion->feedback;
    829         vCfg.voiceGain = (EAS_U16) pFMVoice->voiceGain;
    830 
    831 #if (NUM_OUTPUT_CHANNELS == 2)
    832         vCfg.pan = pFMVoice->pan;
    833 #endif
    834 
    835         /* get voice mode */
    836         vCfg.flags = pRegion->region.keyGroupAndFlags & 7;
    837 
    838         /* get operator parameters */
    839         for (oper = 0; oper < 4; oper++)
    840         {
    841             /* calculate initial gain */
    842             vCfg.gain[oper] = (EAS_U16) FMUL_15x15(pFMVoice->oper[oper].baseGain, pFMVoice->oper[oper].envGain);
    843             vCfg.outputGain[oper] = pFMVoice->oper[oper].outputGain;
    844 
    845             /* copy noise waveform flag */
    846             if (pRegion->oper[oper].flags & FM_OPER_FLAG_NOISE)
    847                 vCfg.flags |= (EAS_U8) (FLAG_FM_ENG_VOICE_OP1_NOISE << oper);
    848         }
    849 
    850 #ifdef FM_OFFBOARD
    851         FM_ConfigVoice(voiceNum, &vCfg, pVoiceMgr->pFrameBuffer);
    852 #else
    853         FM_ConfigVoice(voiceNum, &vCfg, NULL);
    854 #endif
    855 
    856         /* clear startup flag */
    857         pVoice->voiceFlags &= ~VOICE_FLAG_NO_SAMPLES_SYNTHESIZED_YET;
    858     }
    859 
    860     /* calculate new synthesis parameters */
    861     done = FM_UpdateDynamic(pVoice, pFMVoice, pRegion, pChannel);
    862 
    863     /* calculate LFO gain modulation */
    864     /*lint -e{702} <use shift for performance> */
    865     temp = ((fmScaleTable[pRegion->vibTrem & 0x0f] >> 1) * pFMVoice->lfoValue) >> FM_LFO_GAIN_SHIFT;
    866 
    867     /* include channel gain */
    868     temp += pChannel->staticGain;
    869 
    870     /* -32768 or lower is infinite attenuation */
    871     if (temp < -32767)
    872         voiceGainTarget = 0;
    873 
    874     /* translate to linear gain multiplier */
    875     else
    876         voiceGainTarget = EAS_LogToLinear16(temp);
    877 
    878     /* include synth master volume */
    879     voiceGainTarget = (EAS_U16) FMUL_15x15(voiceGainTarget, pSynth->masterVolume);
    880 
    881     /* save target values for this frame */
    882     vFrame.voiceGain = voiceGainTarget;
    883 
    884     /* assume voice output is zero */
    885     pVoice->gain = 0;
    886 
    887     /* save operator targets for this frame */
    888     for (oper = 0; oper < 4; oper++)
    889     {
    890         vFrame.gain[oper] = (EAS_U16) FMUL_15x15(pFMVoice->oper[oper].baseGain, pFMVoice->oper[oper].envGain);
    891         vFrame.pitch[oper] = pFMVoice->oper[oper].pitch;
    892 
    893         /* use the highest output envelope level as the gain for the voice stealing algorithm */
    894         if (FM_SynthIsOutputOperator(pRegion, oper))
    895             pVoice->gain = max(pVoice->gain, (EAS_I16) vFrame.gain[oper]);
    896     }
    897 
    898     /* consider voice gain multiplier in calculating gain for stealing algorithm */
    899     pVoice->gain = (EAS_I16) FMUL_15x15(voiceGainTarget, pVoice->gain);
    900 
    901     /* synthesize samples */
    902 #ifdef FM_OFFBOARD
    903     FM_ProcessVoice(voiceNum, &vFrame, numSamples, pVoiceMgr->operMixBuffer, pVoiceMgr->voiceBuffer, pMixBuffer, pVoiceMgr->pFrameBuffer);
    904 #else
    905     FM_ProcessVoice(voiceNum, &vFrame, numSamples, pVoiceMgr->operMixBuffer, pVoiceMgr->voiceBuffer, pMixBuffer, NULL);
    906 #endif
    907 
    908     return done;
    909 }
    910 
    911