• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
FM_Initialize(S_VOICE_MGR * pVoiceMgr)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  */
GetFMVoicePtr(S_VOICE_MGR * pVoiceMgr,EAS_INT voiceNum)131 EAS_INLINE S_FM_VOICE *GetFMVoicePtr (S_VOICE_MGR *pVoiceMgr, EAS_INT voiceNum)
132 {
133     return &pVoiceMgr->fmVoices[voiceNum];
134 }
GetChannelPtr(S_SYNTH * pSynth,S_SYNTH_VOICE * pVoice)135 EAS_INLINE S_SYNTH_CHANNEL *GetChannelPtr (S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice)
136 {
137     return &pSynth->channels[pVoice->channel & 15];
138 }
GetFMRegionPtr(S_SYNTH * pSynth,S_SYNTH_VOICE * pVoice)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 */
FM_SynthIsOutputOperator(const S_FM_REGION * pRegion,EAS_INT operIndex)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 
FM_CalcEGRate(EAS_U8 nKeyNumber,EAS_U8 nLogRate,EAS_U8 nEGScale)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 */
FM_ReleaseVoice(S_VOICE_MGR * pVoiceMgr,S_SYNTH * pSynth,S_SYNTH_VOICE * pVoice,EAS_I32 voiceNum)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 */
FM_MuteVoice(S_VOICE_MGR * pVoiceMgr,S_SYNTH * pSynth,S_SYNTH_VOICE * pVoice,EAS_I32 voiceNum)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 */
FM_SustainPedal(S_VOICE_MGR * pVoiceMgr,S_SYNTH * pSynth,S_SYNTH_VOICE * pVoice,S_SYNTH_CHANNEL * pChannel,EAS_I32 voiceNum)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 */
FM_StartVoice(S_VOICE_MGR * pVoiceMgr,S_SYNTH * pSynth,S_SYNTH_VOICE * pVoice,EAS_I32 voiceNum,EAS_U16 regionIndex)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 */
FM_UpdateChannel(S_VOICE_MGR * pVoiceMgr,S_SYNTH * pSynth,EAS_U8 channel)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 */
FM_UpdateLFO(S_FM_VOICE * pFMVoice,const S_FM_REGION * pRegion)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 */
FM_UpdateEG(S_SYNTH_VOICE * pVoice,S_OPERATOR * pOper,const S_FM_OPER * pOperData,EAS_BOOL oneShot)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 */
FM_UpdateDynamic(S_SYNTH_VOICE * pVoice,S_FM_VOICE * pFMVoice,const S_FM_REGION * pRegion,S_SYNTH_CHANNEL * pChannel)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 */
FM_UpdateVoice(S_VOICE_MGR * pVoiceMgr,S_SYNTH * pSynth,S_SYNTH_VOICE * pVoice,EAS_I32 voiceNum,EAS_I32 * pMixBuffer,EAS_I32 numSamples)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