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 */ 86extern const EAS_I16 fmControlTable[128]; 87extern const EAS_U16 fmRateTable[256]; 88extern const EAS_U16 fmAttackTable[16]; 89extern const EAS_U8 fmDecayTable[16]; 90extern const EAS_U8 fmReleaseTable[16]; 91extern const EAS_U8 fmScaleTable[16]; 92 93/* local prototypes */ 94/*lint -esym(715, pVoiceMgr) standard synthesizer interface - pVoiceMgr not used */ 95static EAS_RESULT FM_Initialize (S_VOICE_MGR *pVoiceMgr) { return EAS_SUCCESS; } 96static EAS_RESULT FM_StartVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice, EAS_I32 voiceNum, EAS_U16 regionIndex); 97static EAS_BOOL FM_UpdateVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice, EAS_I32 voiceNum, EAS_I32 *pMixBuffer, EAS_I32 numSamples); 98static void FM_ReleaseVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice, EAS_I32 voiceNum); 99static void FM_MuteVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice, EAS_I32 voiceNum); 100static void FM_SustainPedal (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice, S_SYNTH_CHANNEL *pChannel, EAS_I32 voiceNum); 101static void FM_UpdateChannel (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel); 102 103 104/*---------------------------------------------------------------------------- 105 * Synthesizer interface 106 *---------------------------------------------------------------------------- 107*/ 108const 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 120const S_FRAME_INTERFACE fmFrameInterface = 121{ 122 FM_StartFrame, 123 FM_EndFrame 124}; 125#endif 126 127/*---------------------------------------------------------------------------- 128 * inline functions 129 *---------------------------------------------------------------------------- 130 */ 131EAS_INLINE S_FM_VOICE *GetFMVoicePtr (S_VOICE_MGR *pVoiceMgr, EAS_INT voiceNum) 132{ 133 return &pVoiceMgr->fmVoices[voiceNum]; 134} 135EAS_INLINE S_SYNTH_CHANNEL *GetChannelPtr (S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice) 136{ 137 return &pSynth->channels[pVoice->channel & 15]; 138} 139EAS_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*/ 160static 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 223static 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*/ 254static 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 */ 293static 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 */ 325static 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*/ 396static 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 */ 515static 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*/ 578static 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*/ 625static 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*/ 728static 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*/ 799static 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