1/*----------------------------------------------------------------------------
2 *
3 * File:
4 * eas_voicemgt.c
5 *
6 * Contents and purpose:
7 * Implements the 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: 794 $
26 *   $Date: 2007-08-01 00:08:48 -0700 (Wed, 01 Aug 2007) $
27 *----------------------------------------------------------------------------
28*/
29
30/* includes */
31#include "eas.h"
32#include "eas_data.h"
33#include "eas_config.h"
34#include "eas_report.h"
35#include "eas_midictrl.h"
36#include "eas_host.h"
37#include "eas_synth_protos.h"
38#include "eas_vm_protos.h"
39
40#ifdef DLS_SYNTHESIZER
41#include "eas_mdls.h"
42#endif
43
44// #define _DEBUG_VM
45
46/* some defines for workload */
47#define WORKLOAD_AMOUNT_SMALL_INCREMENT     5
48#define WORKLOAD_AMOUNT_START_NOTE          10
49#define WORKLOAD_AMOUNT_STOP_NOTE           10
50#define WORKLOAD_AMOUNT_KEY_GROUP           10
51#define WORKLOAD_AMOUNT_POLY_LIMIT          10
52
53/* pointer to base sound library */
54extern S_EAS easSoundLib;
55
56#ifdef TEST_HARNESS
57extern S_EAS easTestLib;
58EAS_SNDLIB_HANDLE VMGetLibHandle(EAS_INT libNum)
59{
60    switch (libNum)
61    {
62        case 0:
63            return &easSoundLib;
64#ifdef _WT_SYNTH
65        case 1:
66            return &easTestLib;
67#endif
68        default:
69            return NULL;
70    }
71}
72#endif
73
74/* pointer to synthesizer interface(s) */
75#ifdef _WT_SYNTH
76extern const S_SYNTH_INTERFACE wtSynth;
77#endif
78
79#ifdef _FM_SYNTH
80extern const S_SYNTH_INTERFACE fmSynth;
81#endif
82
83typedef S_SYNTH_INTERFACE *S_SYNTH_INTERFACE_HANDLE;
84
85/* wavetable on MCU */
86#if defined(EAS_WT_SYNTH)
87const S_SYNTH_INTERFACE *const pPrimarySynth = &wtSynth;
88
89/* FM on MCU */
90#elif defined(EAS_FM_SYNTH)
91const S_SYNTH_INTERFACE *const pPrimarySynth = &fmSynth;
92
93/* wavetable drums on MCU, FM melodic on DSP */
94#elif defined(EAS_HYBRID_SYNTH)
95const S_SYNTH_INTERFACE *const pPrimarySynth = &wtSynth;
96const S_SYNTH_INTERFACE *const pSecondarySynth = &fmSynth;
97
98/* wavetable drums on MCU, wavetable melodic on DSP */
99#elif defined(EAS_SPLIT_WT_SYNTH)
100const S_SYNTH_INTERFACE *const pPrimarySynth = &wtSynth;
101extern const S_FRAME_INTERFACE wtFrameInterface;
102const S_FRAME_INTERFACE *const pFrameInterface = &wtFrameInterface;
103
104/* wavetable drums on MCU, FM melodic on DSP */
105#elif defined(EAS_SPLIT_HYBRID_SYNTH)
106const S_SYNTH_INTERFACE *const pPrimarySynth = &wtSynth;
107const S_SYNTH_INTERFACE *const pSecondarySynth = &fmSynth;
108extern const S_FRAME_INTERFACE fmFrameInterface;
109const S_FRAME_INTERFACE *const pFrameInterface = &fmFrameInterface;
110
111/* FM on DSP */
112#elif defined(EAS_SPLIT_FM_SYNTH)
113const S_SYNTH_INTERFACE *const pPrimarySynth = &fmSynth;
114extern const S_FRAME_INTERFACE fmFrameInterface;
115const S_FRAME_INTERFACE *const pFrameInterface = &fmFrameInterface;
116
117#else
118#error "Undefined architecture option"
119#endif
120
121/*----------------------------------------------------------------------------
122 * inline functions
123 *----------------------------------------------------------------------------
124*/
125EAS_INLINE const S_REGION* GetRegionPtr (S_SYNTH *pSynth, EAS_U16 regionIndex)
126{
127#if defined(DLS_SYNTHESIZER)
128    if (regionIndex & FLAG_RGN_IDX_DLS_SYNTH)
129        return &pSynth->pDLS->pDLSRegions[regionIndex & REGION_INDEX_MASK].wtRegion.region;
130#endif
131#if defined(_HYBRID_SYNTH)
132    if (regionIndex & FLAG_RGN_IDX_FM_SYNTH)
133        return &pSynth->pEAS->pFMRegions[regionIndex & REGION_INDEX_MASK].region;
134    else
135        return &pSynth->pEAS->pWTRegions[regionIndex].region;
136#elif defined(_WT_SYNTH)
137    return &pSynth->pEAS->pWTRegions[regionIndex].region;
138#elif defined(_FM_SYNTH)
139    return &pSynth->pEAS->pFMRegions[regionIndex].region;
140#endif
141}
142
143/*lint -esym(715, voiceNum) used in some implementation */
144EAS_INLINE const S_SYNTH_INTERFACE* GetSynthPtr (EAS_INT voiceNum)
145{
146#if defined(_HYBRID_SYNTH)
147    if (voiceNum < NUM_PRIMARY_VOICES)
148        return pPrimarySynth;
149    else
150        return pSecondarySynth;
151#else
152    return pPrimarySynth;
153#endif
154}
155
156EAS_INLINE EAS_INT GetAdjustedVoiceNum (EAS_INT voiceNum)
157{
158#if defined(_HYBRID_SYNTH)
159    if (voiceNum >= NUM_PRIMARY_VOICES)
160        return voiceNum - NUM_PRIMARY_VOICES;
161#endif
162    return voiceNum;
163}
164
165EAS_INLINE EAS_U8 VSynthToChannel (S_SYNTH *pSynth, EAS_U8 channel)
166{
167    /*lint -e{734} synthNum is always 0-15 */
168    return channel | (pSynth->vSynthNum << 4);
169}
170
171/*----------------------------------------------------------------------------
172 * InitVoice()
173 *----------------------------------------------------------------------------
174 * Initialize a synthesizer voice
175 *----------------------------------------------------------------------------
176*/
177void InitVoice (S_SYNTH_VOICE *pVoice)
178{
179    pVoice->channel = UNASSIGNED_SYNTH_CHANNEL;
180    pVoice->nextChannel = UNASSIGNED_SYNTH_CHANNEL;
181    pVoice->note = pVoice->nextNote = DEFAULT_KEY_NUMBER;
182    pVoice->velocity = pVoice->nextVelocity = DEFAULT_VELOCITY;
183    pVoice->regionIndex = DEFAULT_REGION_INDEX;
184    pVoice->age = DEFAULT_AGE;
185    pVoice->voiceFlags = DEFAULT_VOICE_FLAGS;
186    pVoice->voiceState = DEFAULT_VOICE_STATE;
187}
188
189/*----------------------------------------------------------------------------
190 * IncVoicePoolCount()
191 *----------------------------------------------------------------------------
192 * Updates the voice pool count when a voice changes state
193 *----------------------------------------------------------------------------
194*/
195static void IncVoicePoolCount (S_VOICE_MGR *pVoiceMgr, S_SYNTH_VOICE *pVoice)
196{
197    S_SYNTH *pSynth;
198    EAS_INT pool;
199
200    /* ignore muting voices */
201    if (pVoice->voiceState == eVoiceStateMuting)
202        return;
203
204    if (pVoice->voiceState == eVoiceStateStolen)
205    {
206        pSynth = pVoiceMgr->pSynth[GET_VSYNTH(pVoice->nextChannel)];
207        pool = pSynth->channels[GET_CHANNEL(pVoice->nextChannel)].pool;
208    }
209    else
210    {
211        pSynth = pVoiceMgr->pSynth[GET_VSYNTH(pVoice->channel)];
212        pool = pSynth->channels[GET_CHANNEL(pVoice->channel)].pool;
213    }
214
215    pSynth->poolCount[pool]++;
216
217#ifdef _DEBUG_VM
218    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IncVoicePoolCount: Synth=%d pool=%d\n", pSynth->vSynthNum, pool); */ }
219#endif
220}
221
222/*----------------------------------------------------------------------------
223 * DecVoicePoolCount()
224 *----------------------------------------------------------------------------
225 * Updates the voice pool count when a voice changes state
226 *----------------------------------------------------------------------------
227*/
228static void DecVoicePoolCount (S_VOICE_MGR *pVoiceMgr, S_SYNTH_VOICE *pVoice)
229{
230    S_SYNTH *pSynth;
231    EAS_INT pool;
232
233    /* ignore muting voices */
234    if (pVoice->voiceState == eVoiceStateMuting)
235        return;
236
237    if (pVoice->voiceState == eVoiceStateStolen)
238    {
239        pSynth = pVoiceMgr->pSynth[GET_VSYNTH(pVoice->nextChannel)];
240        pool = pSynth->channels[GET_CHANNEL(pVoice->nextChannel)].pool;
241    }
242    else
243    {
244        pSynth = pVoiceMgr->pSynth[GET_VSYNTH(pVoice->channel)];
245        pool = pSynth->channels[GET_CHANNEL(pVoice->channel)].pool;
246    }
247
248    pSynth->poolCount[pool]--;
249
250#ifdef _DEBUG_VM
251    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "DecVoicePoolCount: Synth=%d pool=%d\n", pSynth->vSynthNum, pool); */ }
252#endif
253}
254
255/*----------------------------------------------------------------------------
256 * VMInitialize()
257 *----------------------------------------------------------------------------
258 * Purpose:
259 *
260 * Inputs:
261 * psEASData - pointer to overall EAS data structure
262 *
263 * Outputs:
264 *
265 *----------------------------------------------------------------------------
266*/
267EAS_RESULT VMInitialize (S_EAS_DATA *pEASData)
268{
269    S_VOICE_MGR *pVoiceMgr;
270    EAS_INT i;
271
272    /* check Configuration Module for data allocation */
273    if (pEASData->staticMemoryModel)
274        pVoiceMgr = EAS_CMEnumData(EAS_CM_SYNTH_DATA);
275    else
276        pVoiceMgr = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_VOICE_MGR));
277    if (!pVoiceMgr)
278    {
279        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitialize: Failed to allocate synthesizer memory\n"); */ }
280        return EAS_ERROR_MALLOC_FAILED;
281    }
282    EAS_HWMemSet(pVoiceMgr, 0, sizeof(S_VOICE_MGR));
283
284    /* initialize non-zero variables */
285    pVoiceMgr->pGlobalEAS = (S_EAS*) &easSoundLib;
286    pVoiceMgr->maxPolyphony = (EAS_U16) MAX_SYNTH_VOICES;
287
288#if defined(_SECONDARY_SYNTH) || defined(EAS_SPLIT_WT_SYNTH)
289    pVoiceMgr->maxPolyphonyPrimary = NUM_PRIMARY_VOICES;
290    pVoiceMgr->maxPolyphonySecondary = NUM_SECONDARY_VOICES;
291#endif
292
293    /* set max workload to zero */
294    pVoiceMgr->maxWorkLoad = 0;
295
296    /* initialize the voice manager parameters */
297    for (i = 0; i < MAX_SYNTH_VOICES; i++)
298        InitVoice(&pVoiceMgr->voices[i]);
299
300    /* initialize the synth */
301    /*lint -e{522} return unused at this time */
302    pPrimarySynth->pfInitialize(pVoiceMgr);
303
304    /* initialize the off-chip synth */
305#ifdef _HYBRID_SYNTH
306    /*lint -e{522} return unused at this time */
307    pSecondarySynth->pfInitialize(pVoiceMgr);
308#endif
309
310    pEASData->pVoiceMgr = pVoiceMgr;
311    return EAS_SUCCESS;
312}
313
314/*----------------------------------------------------------------------------
315 * VMInitMIDI()
316 *----------------------------------------------------------------------------
317 * Purpose:
318 *
319 * Inputs:
320 * psEASData - pointer to overall EAS data structure
321 *
322 * Outputs:
323 *
324 *----------------------------------------------------------------------------
325*/
326EAS_RESULT VMInitMIDI (S_EAS_DATA *pEASData, S_SYNTH **ppSynth)
327{
328    EAS_RESULT result;
329    S_SYNTH *pSynth;
330    EAS_INT virtualSynthNum;
331
332    *ppSynth = NULL;
333
334    /* static memory model only allows one synth */
335    if (pEASData->staticMemoryModel)
336    {
337        if (pEASData->pVoiceMgr->pSynth[0] != NULL)
338        {
339            { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI: No virtual synthesizer support for static memory model\n"); */ }
340            return EAS_ERROR_NO_VIRTUAL_SYNTHESIZER;
341        }
342
343        /* check Configuration Module for data allocation */
344        pSynth = EAS_CMEnumData(EAS_CM_MIDI_DATA);
345        virtualSynthNum = 0;
346    }
347
348    /* dynamic memory model */
349    else
350    {
351        for (virtualSynthNum = 0; virtualSynthNum < MAX_VIRTUAL_SYNTHESIZERS; virtualSynthNum++)
352            if (pEASData->pVoiceMgr->pSynth[virtualSynthNum] == NULL)
353                break;
354        if (virtualSynthNum == MAX_VIRTUAL_SYNTHESIZERS)
355        {
356            { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI: Exceeded number of active virtual synthesizers"); */ }
357            return EAS_ERROR_NO_VIRTUAL_SYNTHESIZER;
358        }
359        pSynth = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_SYNTH));
360    }
361
362    /* make sure we have a valid memory pointer */
363    if (pSynth == NULL)
364    {
365        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI: Failed to allocate synthesizer memory\n"); */ }
366        return EAS_ERROR_MALLOC_FAILED;
367    }
368    EAS_HWMemSet(pSynth, 0, sizeof(S_SYNTH));
369
370    /* set the sound library pointer */
371    if ((result = VMSetEASLib(pSynth, pEASData->pVoiceMgr->pGlobalEAS)) != EAS_SUCCESS)
372    {
373        VMMIDIShutdown(pEASData, pSynth);
374        return result;
375    }
376
377    /* link in DLS bank if downloaded */
378#ifdef DLS_SYNTHESIZER
379    if (pEASData->pVoiceMgr->pGlobalDLS)
380    {
381        pSynth->pDLS = pEASData->pVoiceMgr->pGlobalDLS;
382        DLSAddRef(pSynth->pDLS);
383    }
384#endif
385
386    /* initialize MIDI state variables */
387    pSynth->synthFlags = DEFAULT_SYNTH_FLAGS;
388    pSynth->masterVolume = DEFAULT_SYNTH_MASTER_VOLUME;
389    pSynth->refCount = 1;
390    pSynth->priority = DEFAULT_SYNTH_PRIORITY;
391    pSynth->poolAlloc[0] = (EAS_U8) pEASData->pVoiceMgr->maxPolyphony;
392
393    VMInitializeAllChannels(pEASData->pVoiceMgr, pSynth);
394
395    pSynth->vSynthNum = (EAS_U8) virtualSynthNum;
396    pEASData->pVoiceMgr->pSynth[virtualSynthNum] = pSynth;
397
398    *ppSynth = pSynth;
399    return EAS_SUCCESS;
400}
401
402/*----------------------------------------------------------------------------
403 * VMIncRefCount()
404 *----------------------------------------------------------------------------
405 * Increment reference count for virtual synth
406 *----------------------------------------------------------------------------
407*/
408void VMIncRefCount (S_SYNTH *pSynth)
409{
410    pSynth->refCount++;
411}
412
413/*----------------------------------------------------------------------------
414 * VMReset()
415 *----------------------------------------------------------------------------
416 * Purpose:
417 * We call this routine to start the process of reseting the synth.
418 * This routine sets a flag for the entire synth indicating that we want
419 * to reset.
420 * We also force all voices to mute quickly.
421 * However, we do not actually perform any synthesis in this routine. That
422 * is, we do not ramp the voices down from this routine, but instead, we
423 * let the "regular" synth processing steps take care of adding the ramp
424 * down samples to the output buffer. After we are sure that all voices
425 * have completed ramping down, we continue the process of resetting the
426 * synth (from another routine).
427 *
428 * Inputs:
429 * psEASData - pointer to overall EAS data structure
430 * force - force reset even if voices are active
431 *
432 * Outputs:
433 *
434 * Side Effects:
435 * - set a flag (in psSynthObject->m_nFlags) indicating synth reset requested.
436 * - force all voices to update their envelope states to mute
437 *
438 *----------------------------------------------------------------------------
439*/
440void VMReset (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_BOOL force)
441{
442
443#ifdef _DEBUG_VM
444    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMReset: request to reset synth. Force = %d\n", force); */ }
445#endif
446
447    /* force voices to off state - may cause audio artifacts */
448    if (force)
449    {
450        pVoiceMgr->activeVoices -= pSynth->numActiveVoices;
451        pSynth->numActiveVoices = 0;
452        VMInitializeAllVoices(pVoiceMgr, pSynth->vSynthNum);
453    }
454    else
455        VMMuteAllVoices(pVoiceMgr, pSynth);
456
457    /* don't reset if voices are still playing */
458    if (pSynth->numActiveVoices == 0)
459    {
460        EAS_INT i;
461
462#ifdef _DEBUG_VM
463        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMReset: complete the reset process\n"); */ }
464#endif
465
466        VMInitializeAllChannels(pVoiceMgr, pSynth);
467        for (i = 0; i < NUM_SYNTH_CHANNELS; i++)
468            pSynth->poolCount[i] = 0;
469
470        /* set polyphony */
471        if (pSynth->maxPolyphony < pVoiceMgr->maxPolyphony)
472            pSynth->poolAlloc[0] = (EAS_U8) pVoiceMgr->maxPolyphony;
473        else
474            pSynth->poolAlloc[0] = (EAS_U8) pSynth->maxPolyphony;
475
476        /* clear reset flag */
477        pSynth->synthFlags &= ~SYNTH_FLAG_RESET_IS_REQUESTED;
478    }
479
480    /* handle reset after voices are muted */
481    else
482        pSynth->synthFlags |= SYNTH_FLAG_RESET_IS_REQUESTED;
483}
484
485/*----------------------------------------------------------------------------
486 * VMInitializeAllChannels()
487 *----------------------------------------------------------------------------
488 * Purpose:
489 *
490 * Inputs:
491 * psEASData - pointer to overall EAS data structure
492 *
493 * Outputs:
494 *
495 *----------------------------------------------------------------------------
496*/
497void VMInitializeAllChannels (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth)
498{
499    S_SYNTH_CHANNEL *pChannel;
500    EAS_INT i;
501
502    VMResetControllers(pSynth);
503
504    /* init each channel */
505    pChannel = pSynth->channels;
506
507    for (i = 0; i < NUM_SYNTH_CHANNELS; i++, pChannel++)
508    {
509        pChannel->channelFlags = DEFAULT_CHANNEL_FLAGS;
510        pChannel->staticGain = DEFAULT_CHANNEL_STATIC_GAIN;
511        pChannel->staticPitch = DEFAULT_CHANNEL_STATIC_PITCH;
512        pChannel->pool = 0;
513
514        /* the drum channel needs a different init */
515        if (i == DEFAULT_DRUM_CHANNEL)
516        {
517            pChannel->bankNum = DEFAULT_RHYTHM_BANK_NUMBER;
518            pChannel->channelFlags |= CHANNEL_FLAG_RHYTHM_CHANNEL;
519        }
520        else
521            pChannel->bankNum = DEFAULT_MELODY_BANK_NUMBER;
522
523        VMProgramChange(pVoiceMgr, pSynth, (EAS_U8) i, DEFAULT_SYNTH_PROGRAM_NUMBER);
524    }
525
526}
527
528/*----------------------------------------------------------------------------
529 * VMResetControllers()
530 *----------------------------------------------------------------------------
531 * Purpose:
532 *
533 * Inputs:
534 * psEASData - pointer to overall EAS data structure
535 *
536 * Outputs:
537 *
538 *----------------------------------------------------------------------------
539*/
540void VMResetControllers (S_SYNTH *pSynth)
541{
542    S_SYNTH_CHANNEL *pChannel;
543    EAS_INT i;
544
545    pChannel = pSynth->channels;
546
547    for (i = 0; i < NUM_SYNTH_CHANNELS; i++, pChannel++)
548    {
549        pChannel->pitchBend = DEFAULT_PITCH_BEND;
550        pChannel->modWheel = DEFAULT_MOD_WHEEL;
551        pChannel->volume = DEFAULT_CHANNEL_VOLUME;
552        pChannel->pan = DEFAULT_PAN;
553        pChannel->expression = DEFAULT_EXPRESSION;
554
555#ifdef  _REVERB
556        pSynth->channels[i].reverbSend = DEFAULT_REVERB_SEND;
557#endif
558
559#ifdef  _CHORUS
560        pSynth->channels[i].chorusSend = DEFAULT_CHORUS_SEND;
561#endif
562
563        pChannel->channelPressure = DEFAULT_CHANNEL_PRESSURE;
564        pChannel->registeredParam = DEFAULT_REGISTERED_PARAM;
565        pChannel->pitchBendSensitivity = DEFAULT_PITCH_BEND_SENSITIVITY;
566        pChannel->finePitch = DEFAULT_FINE_PITCH;
567        pChannel->coarsePitch = DEFAULT_COARSE_PITCH;
568
569        /* update all voices on this channel */
570        pChannel->channelFlags |= CHANNEL_FLAG_UPDATE_CHANNEL_PARAMETERS;
571    }
572}
573
574/*----------------------------------------------------------------------------
575 * VMInitializeAllVoices()
576 *----------------------------------------------------------------------------
577 * Purpose:
578 *
579 * Inputs:
580 * psEASData - pointer to overall EAS data structure
581 *
582 * Outputs:
583 *
584 *----------------------------------------------------------------------------
585*/
586void VMInitializeAllVoices (S_VOICE_MGR *pVoiceMgr, EAS_INT vSynthNum)
587{
588    EAS_INT i;
589
590    /* initialize the voice manager parameters */
591    for (i = 0; i < MAX_SYNTH_VOICES; i++)
592    {
593        if (pVoiceMgr->voices[i].voiceState != eVoiceStateStolen)
594        {
595            if (GET_VSYNTH(pVoiceMgr->voices[i].channel) == vSynthNum)
596                InitVoice(&pVoiceMgr->voices[i]);
597        }
598        else
599        {
600            if (GET_VSYNTH(pVoiceMgr->voices[i].nextChannel) == vSynthNum)
601                InitVoice(&pVoiceMgr->voices[i]);
602        }
603    }
604}
605
606/*----------------------------------------------------------------------------
607 * VMMuteVoice()
608 *----------------------------------------------------------------------------
609 * Mute the selected voice
610 *----------------------------------------------------------------------------
611*/
612void VMMuteVoice (S_VOICE_MGR *pVoiceMgr, EAS_I32 voiceNum)
613{
614    S_SYNTH *pSynth;
615    S_SYNTH_VOICE *pVoice;
616
617    /* take no action if voice is already muted */
618    pVoice = &pVoiceMgr->voices[voiceNum];
619    if ((pVoice->voiceState == eVoiceStateMuting) || (pVoice->voiceState == eVoiceStateFree))
620        return;
621
622    /* one less voice in pool */
623    DecVoicePoolCount(pVoiceMgr, pVoice);
624
625    pSynth = pVoiceMgr->pSynth[GET_VSYNTH(pVoice->channel)];
626    GetSynthPtr(voiceNum)->pfMuteVoice(pVoiceMgr, pSynth, pVoice, GetAdjustedVoiceNum(voiceNum));
627    pVoice->voiceState = eVoiceStateMuting;
628
629}
630
631/*----------------------------------------------------------------------------
632 * VMReleaseVoice()
633 *----------------------------------------------------------------------------
634 * Release the selected voice
635 *----------------------------------------------------------------------------
636*/
637void VMReleaseVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_I32 voiceNum)
638{
639    S_SYNTH_VOICE *pVoice = &pVoiceMgr->voices[voiceNum];
640
641    /* take no action if voice is already free, muting, or releasing */
642    if (( pVoice->voiceState == eVoiceStateMuting) ||
643        (pVoice->voiceState == eVoiceStateFree) ||
644        (pVoice->voiceState == eVoiceStateRelease))
645            return;
646
647    /* stolen voices should just be muted */
648    if (pVoice->voiceState == eVoiceStateStolen)
649        VMMuteVoice(pVoiceMgr, voiceNum);
650
651    /* release this voice */
652    GetSynthPtr(voiceNum)->pfReleaseVoice(pVoiceMgr, pSynth, &pVoiceMgr->voices[voiceNum], GetAdjustedVoiceNum(voiceNum));
653    pVoice->voiceState = eVoiceStateRelease;
654}
655
656/*----------------------------------------------------------------------------
657 * VMInitMIPTable()
658 *----------------------------------------------------------------------------
659 * Initialize the SP-MIDI MIP table in preparation for receiving MIP message
660 *----------------------------------------------------------------------------
661*/
662void VMInitMIPTable (S_SYNTH *pSynth)
663{
664    EAS_INT i;
665
666#ifdef _DEBUG_VM
667    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMInitMIPTable\n"); */ }
668#endif
669
670    /* clear SP-MIDI flag */
671    pSynth->synthFlags &= ~SYNTH_FLAG_SP_MIDI_ON;
672    for (i = 0; i < NUM_SYNTH_CHANNELS; i++)
673    {
674        pSynth->channels[i].pool = 0;
675        pSynth->channels[i].mip = 0;
676    }
677}
678
679/*----------------------------------------------------------------------------
680 * VMSetMIPEntry()
681 *----------------------------------------------------------------------------
682 * Sets the priority and MIP level for a MIDI channel
683 *----------------------------------------------------------------------------
684*/
685/*lint -esym(715, pVoiceMgr) reserved for future use */
686void VMSetMIPEntry (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 priority, EAS_U8 mip)
687{
688
689#ifdef _DEBUG_VM
690    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMSetMIPEntry: channel=%d, priority=%d, MIP=%d\n", channel, priority, mip); */ }
691#endif
692
693    /* save data for use by MIP message processing */
694    if (priority < NUM_SYNTH_CHANNELS)
695    {
696        pSynth->channels[channel].pool = priority;
697        pSynth->channels[channel].mip = mip;
698    }
699}
700
701/*----------------------------------------------------------------------------
702 * VMMIPUpdateChannelMuting()
703 *----------------------------------------------------------------------------
704 * This routine is called after an SP-MIDI message is received and
705 * any time the allocated polyphony changes. It mutes or unmutes
706 * channels based on polyphony.
707 *----------------------------------------------------------------------------
708*/
709void VMMIPUpdateChannelMuting (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth)
710{
711    EAS_INT i;
712    EAS_INT maxPolyphony;
713    EAS_INT channel;
714    EAS_INT vSynthNum;
715    EAS_INT pool;
716
717#ifdef _DEBUG_VM
718    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMUpdateMIPTable\n"); */ }
719#endif
720
721    /* determine max polyphony */
722    if (pSynth->maxPolyphony)
723        maxPolyphony = pSynth->maxPolyphony;
724    else
725        maxPolyphony = pVoiceMgr->maxPolyphony;
726
727    /* process channels */
728    for (i = 0; i < NUM_SYNTH_CHANNELS; i++)
729    {
730
731        /* channel must be in MIP message and must meet allocation target */
732        if ((pSynth->channels[i].mip != 0) && (pSynth->channels[i].mip <= maxPolyphony))
733            pSynth->channels[i].channelFlags &= ~CHANNEL_FLAG_MUTE;
734        else
735            pSynth->channels[i].channelFlags |= CHANNEL_FLAG_MUTE;
736
737        /* reset voice pool count */
738        pSynth->poolCount[i] = 0;
739    }
740
741    /* mute any voices on muted channels, and count unmuted voices */
742    for (i = 0; i < MAX_SYNTH_VOICES; i++)
743    {
744
745        /* ignore free voices */
746        if (pVoiceMgr->voices[i].voiceState == eVoiceStateFree)
747            continue;
748
749        /* get channel and virtual synth */
750        if (pVoiceMgr->voices[i].voiceState != eVoiceStateStolen)
751        {
752            vSynthNum = GET_VSYNTH(pVoiceMgr->voices[i].channel);
753            channel = GET_CHANNEL(pVoiceMgr->voices[i].channel);
754        }
755        else
756        {
757            vSynthNum = GET_VSYNTH(pVoiceMgr->voices[i].nextChannel);
758            channel = GET_CHANNEL(pVoiceMgr->voices[i].nextChannel);
759        }
760
761        /* ignore voices on other synths */
762        if (vSynthNum != pSynth->vSynthNum)
763            continue;
764
765        /* count voices */
766        pool = pSynth->channels[channel].pool;
767
768        /* deal with muted channels */
769        if (pSynth->channels[channel].channelFlags & CHANNEL_FLAG_MUTE)
770        {
771            /* mute stolen voices scheduled to play on this channel */
772            if (pVoiceMgr->voices[i].voiceState == eVoiceStateStolen)
773                pVoiceMgr->voices[i].voiceState = eVoiceStateMuting;
774
775            /* release voices that aren't already muting */
776            else if (pVoiceMgr->voices[i].voiceState != eVoiceStateMuting)
777            {
778                VMReleaseVoice(pVoiceMgr, pSynth, i);
779                pSynth->poolCount[pool]++;
780            }
781        }
782
783        /* not muted, count this voice */
784        else
785            pSynth->poolCount[pool]++;
786    }
787}
788
789/*----------------------------------------------------------------------------
790 * VMUpdateMIPTable()
791 *----------------------------------------------------------------------------
792 * This routine is called at the end of the SysEx message to allow
793 * the Voice Manager to complete the initialization of the MIP
794 * table. It assigns channels to the appropriate voice pool based
795 * on the MIP setting and calculates the voices allocated for each
796 * pool.
797 *----------------------------------------------------------------------------
798*/
799/*lint -esym(715, pVoiceMgr) reserved for future use */
800void VMUpdateMIPTable (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth)
801{
802    S_SYNTH_CHANNEL *pChannel;
803    EAS_INT i;
804    EAS_INT currentMIP;
805    EAS_INT currentPool;
806    EAS_INT priority[NUM_SYNTH_CHANNELS];
807
808#ifdef _DEBUG_VM
809    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMUpdateMIPTable\n"); */ }
810#endif
811
812    /* set SP-MIDI flag */
813    pSynth->synthFlags |= SYNTH_FLAG_SP_MIDI_ON;
814
815    /* sort channels into priority order */
816    for (i = 0; i < NUM_SYNTH_CHANNELS; i++)
817        priority[i] = -1;
818    for (i = 0; i < NUM_SYNTH_CHANNELS; i++)
819    {
820        if (pSynth->channels[i].pool != DEFAULT_SP_MIDI_PRIORITY)
821            priority[pSynth->channels[i].pool] = i;
822    }
823
824    /* process channels in priority order */
825    currentMIP = 0;
826    currentPool = -1;
827    for (i = 0; i < NUM_SYNTH_CHANNELS; i++)
828    {
829        /* stop when we run out of channels */
830        if (priority[i] == -1)
831            break;
832
833        pChannel = &pSynth->channels[priority[i]];
834
835        /* when 2 or more channels have the same MIP setting, they
836         * share a common voice pool
837         */
838        if (pChannel->mip == currentMIP)
839            pChannel->pool = (EAS_U8) currentPool;
840
841        /* new voice pool */
842        else
843        {
844            currentPool++;
845            pSynth->poolAlloc[currentPool] = (EAS_U8) (pChannel->mip - currentMIP);
846            currentMIP = pChannel->mip;
847        }
848    }
849
850    /* set SP-MIDI flag */
851    pSynth->synthFlags |= SYNTH_FLAG_SP_MIDI_ON;
852
853    /* update channel muting */
854    VMMIPUpdateChannelMuting (pVoiceMgr, pSynth);
855}
856
857/*----------------------------------------------------------------------------
858 * VMMuteAllVoices()
859 *----------------------------------------------------------------------------
860 * Purpose:
861 * We call this in an emergency reset situation.
862 * This forces all voices to mute quickly.
863 *
864 * Inputs:
865 * psEASData - pointer to overall EAS data structure
866 *
867 * Outputs:
868 *
869 * Side Effects:
870 * - forces all voices to update their envelope states to mute
871 *
872 *----------------------------------------------------------------------------
873*/
874void VMMuteAllVoices (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth)
875{
876    EAS_INT i;
877
878#ifdef _DEBUG_VM
879    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMMuteAllVoices: about to mute all voices!!\n"); */ }
880#endif
881
882    for (i = 0; i < MAX_SYNTH_VOICES; i++)
883    {
884        /* for stolen voices, check new channel */
885        if (pVoiceMgr->voices[i].voiceState == eVoiceStateStolen)
886        {
887            if (GET_VSYNTH(pVoiceMgr->voices[i].nextChannel) == pSynth->vSynthNum)
888                VMMuteVoice(pVoiceMgr, i);
889        }
890
891        else if (pSynth->vSynthNum == GET_VSYNTH(pVoiceMgr->voices[i].channel))
892            VMMuteVoice(pVoiceMgr, i);
893    }
894}
895
896/*----------------------------------------------------------------------------
897 * VMReleaseAllVoices()
898 *----------------------------------------------------------------------------
899 * Purpose:
900 * We call this after we've encountered the end of the Midi file.
901 * This ensures all voices are either in release (because we received their
902 * note off already) or forces them to mute quickly.
903 * We use this as a safety to prevent bad midi files from playing forever.
904 *
905 * Inputs:
906 * psEASData - pointer to overall EAS data structure
907 *
908 * Outputs:
909 *
910 * Side Effects:
911 * - forces all voices to update their envelope states to release or mute
912 *
913 *----------------------------------------------------------------------------
914*/
915void VMReleaseAllVoices (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth)
916{
917    EAS_INT i;
918
919    /* release sustain pedal on all channels */
920    for (i = 0; i < NUM_SYNTH_CHANNELS; i++)
921    {
922        if (pSynth->channels[ i ].channelFlags & CHANNEL_FLAG_SUSTAIN_PEDAL)
923        {
924            VMReleaseAllDeferredNoteOffs(pVoiceMgr, pSynth, (EAS_U8) i);
925            pSynth->channels[i].channelFlags &= ~CHANNEL_FLAG_SUSTAIN_PEDAL;
926        }
927    }
928
929    /* release all voices */
930    for (i = 0; i < MAX_SYNTH_VOICES; i++)
931    {
932
933        switch (pVoiceMgr->voices[i].voiceState)
934        {
935            case eVoiceStateStart:
936            case eVoiceStatePlay:
937                /* only release voices on this synth */
938                if (GET_VSYNTH(pVoiceMgr->voices[i].channel) == pSynth->vSynthNum)
939                    VMReleaseVoice(pVoiceMgr, pSynth, i);
940                break;
941
942            case eVoiceStateStolen:
943                if (GET_VSYNTH(pVoiceMgr->voices[i].nextChannel) == pSynth->vSynthNum)
944                    VMMuteVoice(pVoiceMgr, i);
945                break;
946
947            case eVoiceStateFree:
948            case eVoiceStateRelease:
949            case eVoiceStateMuting:
950                break;
951
952            case eVoiceStateInvalid:
953            default:
954#ifdef _DEBUG_VM
955                { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMReleaseAllVoices: error, %d is an unrecognized state\n",
956                    pVoiceMgr->voices[i].voiceState); */ }
957#endif
958                break;
959        }
960    }
961}
962
963/*----------------------------------------------------------------------------
964 * VMAllNotesOff()
965 *----------------------------------------------------------------------------
966 * Purpose:
967 * Quickly mute all notes on the given channel.
968 *
969 * Inputs:
970 * nChannel - quickly turn off all notes on this channel
971 * psEASData - pointer to overall EAS data structure
972 *
973 * Outputs:
974 *
975 * Side Effects:
976 * - forces all voices on this channel to update their envelope states to mute
977 *
978 *----------------------------------------------------------------------------
979*/
980void VMAllNotesOff (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel)
981{
982    EAS_INT voiceNum;
983    S_SYNTH_VOICE *pVoice;
984
985#ifdef _DEBUG_VM
986    if (channel >= NUM_SYNTH_CHANNELS)
987    {
988        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMAllNotesOff: error, %d invalid channel number\n",
989            channel); */ }
990        return;
991    }
992#endif
993
994    /* increment workload */
995    pVoiceMgr->workload += WORKLOAD_AMOUNT_SMALL_INCREMENT;
996
997    /* check each voice */
998    channel = VSynthToChannel(pSynth, channel);
999    for (voiceNum = 0; voiceNum < MAX_SYNTH_VOICES; voiceNum++)
1000    {
1001        pVoice = &pVoiceMgr->voices[voiceNum];
1002        if (pVoice->voiceState != eVoiceStateFree)
1003        {
1004            if (((pVoice->voiceState != eVoiceStateStolen) && (channel == pVoice->channel)) ||
1005                ((pVoice->voiceState == eVoiceStateStolen) && (channel == pVoice->nextChannel)))
1006            {
1007                /* this voice is assigned to the requested channel */
1008                GetSynthPtr(voiceNum)->pfMuteVoice(pVoiceMgr, pSynth, pVoice, GetAdjustedVoiceNum(voiceNum));
1009                pVoice->voiceState = eVoiceStateMuting;
1010            }
1011        }
1012    }
1013}
1014
1015/*----------------------------------------------------------------------------
1016 * VMDeferredStopNote()
1017 *----------------------------------------------------------------------------
1018 * Purpose:
1019 * Stop the notes that had deferred note-off requests.
1020 *
1021 * Inputs:
1022 * psEASData - pointer to overall EAS data structure
1023 *
1024 * Outputs:
1025 * None.
1026 *
1027 * Side Effects:
1028 * voices that have had deferred note-off requests are now put into release
1029 * psSynthObject->m_sVoice[i].m_nFlags has the VOICE_FLAG_DEFER_MIDI_NOTE_OFF
1030 *  cleared
1031 *----------------------------------------------------------------------------
1032*/
1033void VMDeferredStopNote (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth)
1034{
1035    EAS_INT voiceNum;
1036    EAS_INT channel;
1037    EAS_BOOL deferredNoteOff;
1038
1039    deferredNoteOff = EAS_FALSE;
1040
1041    /* check each voice to see if it requires a deferred note off */
1042    for (voiceNum=0; voiceNum < MAX_SYNTH_VOICES; voiceNum++)
1043    {
1044        if (pVoiceMgr->voices[voiceNum].voiceFlags & VOICE_FLAG_DEFER_MIDI_NOTE_OFF)
1045        {
1046            /* check if this voice was stolen */
1047            if (pVoiceMgr->voices[voiceNum].voiceState == eVoiceStateStolen)
1048            {
1049                /*
1050                This voice was stolen, AND it also has a deferred note-off.
1051                The stolen note must be completely ramped down at this point.
1052                The note that caused the stealing to occur, however, must
1053                have received a note-off request before the note that caused
1054                stealing ever had a chance to even start. We want to give
1055                the note that caused the stealing a chance to play, so we
1056                start it on the next update interval, and we defer sending
1057                the note-off request until the subsequent update interval.
1058                So do not send the note-off request for this voice because
1059                this voice was stolen and should have completed ramping down,
1060                Also, do not clear the global flag nor this voice's flag
1061                because we must indicate that the subsequent update interval,
1062                after the note that caused stealing has started, should
1063                then send the deferred note-off request.
1064                */
1065                deferredNoteOff = EAS_TRUE;
1066
1067#ifdef _DEBUG_VM
1068                { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMDeferredStopNote: defer request to stop voice %d (channel=%d note=%d) - voice not started\n",
1069                    voiceNum,
1070                    pVoiceMgr->voices[voiceNum].nextChannel,
1071                    pVoiceMgr->voices[voiceNum].note); */ }
1072
1073                /* sanity check: this stolen voice better be ramped to zero */
1074                if (0 != pVoiceMgr->voices[voiceNum].gain)
1075                {
1076                    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMDeferredStopNote: warning, this voice did not complete its ramp to zero\n"); */ }
1077                }
1078#endif  // #ifdef _DEBUG_VM
1079
1080            }
1081            else
1082            {
1083                /* clear the flag using exor */
1084                pVoiceMgr->voices[voiceNum].voiceFlags ^=
1085                    VOICE_FLAG_DEFER_MIDI_NOTE_OFF;
1086
1087#ifdef _DEBUG_VM
1088                { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMDeferredStopNote: Stop voice %d (channel=%d note=%d)\n",
1089                    voiceNum,
1090                    pVoiceMgr->voices[voiceNum].nextChannel,
1091                    pVoiceMgr->voices[voiceNum].note); */ }
1092#endif
1093
1094                channel = pVoiceMgr->voices[voiceNum].channel & 15;
1095
1096                /* check if sustain pedal is on */
1097                if (pSynth->channels[channel].channelFlags & CHANNEL_FLAG_SUSTAIN_PEDAL)
1098                {
1099                    GetSynthPtr(voiceNum)->pfSustainPedal(pVoiceMgr, pSynth, &pVoiceMgr->voices[voiceNum], &pSynth->channels[channel], GetAdjustedVoiceNum(voiceNum));
1100                }
1101
1102                /* release this voice */
1103                else
1104                    VMReleaseVoice(pVoiceMgr, pSynth, voiceNum);
1105
1106            }
1107
1108        }
1109
1110    }
1111
1112    /* clear the deferred note-off flag, unless there's another one pending */
1113    if (deferredNoteOff == EAS_FALSE)
1114        pSynth->synthFlags ^= SYNTH_FLAG_DEFERRED_MIDI_NOTE_OFF_PENDING;
1115}
1116
1117/*----------------------------------------------------------------------------
1118 * VMReleaseAllDeferredNoteOffs()
1119 *----------------------------------------------------------------------------
1120 * Purpose:
1121 * Call this functin when the sustain flag is presently set but
1122 * we are now transitioning from damper pedal on to
1123 * damper pedal off. This means all notes in this channel
1124 * that received a note off while the damper pedal was on, and
1125 * had their note-off requests deferred, should now proceed to
1126 * the release state.
1127 *
1128 * Inputs:
1129 * nChannel - this channel has its sustain pedal transitioning from on to off
1130 * psEASData - pointer to overall EAS data structure
1131 *
1132 * Outputs:
1133 * Side Effects:
1134 * any voice with deferred note offs on this channel are updated such that
1135 * pVoice->m_sEG1.m_eState = eEnvelopeStateRelease
1136 * pVoice->m_sEG1.m_nIncrement = release increment
1137 * pVoice->m_nFlags = clear the deferred note off flag
1138 *----------------------------------------------------------------------------
1139*/
1140void VMReleaseAllDeferredNoteOffs (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel)
1141{
1142    S_SYNTH_VOICE *pVoice;
1143    EAS_INT voiceNum;
1144
1145#ifdef _DEBUG_VM
1146    if (channel >= NUM_SYNTH_CHANNELS)
1147    {
1148        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMReleaseAllDeferredNoteOffs: error, %d invalid channel number\n",
1149            channel); */ }
1150        return;
1151    }
1152#endif  /* #ifdef _DEBUG_VM */
1153
1154    /* increment workload */
1155    pVoiceMgr->workload += WORKLOAD_AMOUNT_SMALL_INCREMENT;
1156
1157    /* find all the voices assigned to this channel */
1158    channel = VSynthToChannel(pSynth, channel);
1159    for (voiceNum = 0; voiceNum < MAX_SYNTH_VOICES; voiceNum++)
1160    {
1161
1162        pVoice = &pVoiceMgr->voices[voiceNum];
1163        if (channel == pVoice->channel)
1164        {
1165
1166            /* does this voice have a deferred note off? */
1167            if (pVoice->voiceFlags & VOICE_FLAG_SUSTAIN_PEDAL_DEFER_NOTE_OFF)
1168            {
1169                /* release voice */
1170                VMReleaseVoice(pVoiceMgr, pSynth, voiceNum);
1171
1172                /* use exor to flip bit, clear the flag */
1173                pVoice->voiceFlags &= ~VOICE_FLAG_SUSTAIN_PEDAL_DEFER_NOTE_OFF;
1174
1175            }
1176
1177        }
1178    }
1179
1180    return;
1181}
1182
1183/*----------------------------------------------------------------------------
1184 * VMCatchNotesForSustainPedal()
1185 *----------------------------------------------------------------------------
1186 * Purpose:
1187 * Call this function when the sustain flag is presently clear and
1188 * the damper pedal is off and we are transitioning from damper pedal OFF to
1189 * damper pedal ON. Currently sounding notes should be left
1190 * unchanged. However, we should try to "catch" notes if possible.
1191 * If any notes are in release and have levels >= sustain level, catch them,
1192 * otherwise, let them continue to release.
1193 *
1194 * Inputs:
1195 * nChannel - this channel has its sustain pedal transitioning from on to off
1196 * psEASData - pointer to overall EAS data structure
1197 *
1198 * Outputs:
1199 *----------------------------------------------------------------------------
1200*/
1201void VMCatchNotesForSustainPedal (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel)
1202{
1203    EAS_INT voiceNum;
1204
1205#ifdef _DEBUG_VM
1206    if (channel >= NUM_SYNTH_CHANNELS)
1207    {
1208        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMCatchNotesForSustainPedal: error, %d invalid channel number\n",
1209            channel); */ }
1210        return;
1211    }
1212#endif
1213
1214    pVoiceMgr->workload += WORKLOAD_AMOUNT_SMALL_INCREMENT;
1215    channel = VSynthToChannel(pSynth, channel);
1216
1217    /* find all the voices assigned to this channel */
1218    for (voiceNum = 0; voiceNum < MAX_SYNTH_VOICES; voiceNum++)
1219    {
1220        if (channel == pVoiceMgr->voices[voiceNum].channel)
1221        {
1222            if (eVoiceStateRelease == pVoiceMgr->voices[voiceNum].voiceState)
1223                GetSynthPtr(voiceNum)->pfSustainPedal(pVoiceMgr, pSynth, &pVoiceMgr->voices[voiceNum], &pSynth->channels[channel], GetAdjustedVoiceNum(voiceNum));
1224        }
1225    }
1226}
1227
1228/*----------------------------------------------------------------------------
1229 * VMUpdateAllNotesAge()
1230 *----------------------------------------------------------------------------
1231 * Purpose:
1232 * Increment the note age for all of the active voices.
1233 *
1234 * Inputs:
1235 * psEASData - pointer to overall EAS data structure
1236 *
1237 * Outputs:
1238 *
1239 * Side Effects:
1240 * m_nAge for all voices is incremented
1241 *----------------------------------------------------------------------------
1242*/
1243void VMUpdateAllNotesAge (S_VOICE_MGR *pVoiceMgr, EAS_U16 age)
1244{
1245    EAS_INT i;
1246
1247    for (i = 0; i < MAX_SYNTH_VOICES; i++)
1248    {
1249        if (age - pVoiceMgr->voices[i].age > 0)
1250            pVoiceMgr->voices[i].age++;
1251     }
1252}
1253
1254/*----------------------------------------------------------------------------
1255 * VMStolenVoice()
1256 *----------------------------------------------------------------------------
1257 * Purpose:
1258 * The selected voice is being stolen. Sets the parameters so that the
1259 * voice will begin playing the new sound on the next buffer.
1260 *
1261 * Inputs:
1262 * pVoice - pointer to voice to steal
1263 * nChannel - the channel to start a note on
1264 * nKeyNumber - the key number to start a note for
1265 * nNoteVelocity - the key velocity from this note
1266 *
1267 * Outputs:
1268 * None
1269 *----------------------------------------------------------------------------
1270*/
1271static void VMStolenVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_I32 voiceNum, EAS_U8 channel, EAS_U8 note, EAS_U8 velocity, EAS_U16 regionIndex)
1272{
1273    S_SYNTH_VOICE *pVoice = &pVoiceMgr->voices[voiceNum];
1274
1275    /* one less voice in old pool */
1276    DecVoicePoolCount(pVoiceMgr, pVoice);
1277
1278    /* mute the sound that is currently playing */
1279    GetSynthPtr(voiceNum)->pfMuteVoice(pVoiceMgr, pVoiceMgr->pSynth[GET_VSYNTH(pVoice->channel)], &pVoiceMgr->voices[voiceNum], GetAdjustedVoiceNum(voiceNum));
1280    pVoice->voiceState = eVoiceStateStolen;
1281
1282    /* set new note data */
1283    pVoice->nextChannel = VSynthToChannel(pSynth, channel);
1284    pVoice->nextNote = note;
1285    pVoice->nextVelocity = velocity;
1286    pVoice->nextRegionIndex = regionIndex;
1287
1288    /* one more voice in new pool */
1289    IncVoicePoolCount(pVoiceMgr, pVoice);
1290
1291    /* clear the deferred flags */
1292    pVoice->voiceFlags &=
1293        ~(VOICE_FLAG_DEFER_MIDI_NOTE_OFF |
1294        VOICE_FLAG_DEFER_MUTE |
1295        VOICE_FLAG_SUSTAIN_PEDAL_DEFER_NOTE_OFF);
1296
1297    /* all notes older than this one get "younger" */
1298    VMUpdateAllNotesAge(pVoiceMgr, pVoice->age);
1299
1300    /* assign current age to this note and increment for the next note */
1301    pVoice->age = pVoiceMgr->age++;
1302}
1303
1304/*----------------------------------------------------------------------------
1305 * VMFreeVoice()
1306 *----------------------------------------------------------------------------
1307 * Purpose:
1308 * The selected voice is done playing and being returned to the
1309 * pool of free voices
1310 *
1311 * Inputs:
1312 * pVoice - pointer to voice to free
1313 *
1314 * Outputs:
1315 * None
1316 *----------------------------------------------------------------------------
1317*/
1318static void VMFreeVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice)
1319{
1320
1321    /* do nothing if voice is already free */
1322    if (pVoice->voiceState == eVoiceStateFree)
1323    {
1324        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "VMFreeVoice: Attempt to free voice that is already free\n"); */ }
1325        return;
1326    }
1327
1328    /* if we jump directly to free without passing muting stage,
1329     * we need to adjust the voice count */
1330    DecVoicePoolCount(pVoiceMgr, pVoice);
1331
1332
1333#ifdef _DEBUG_VM
1334    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "VMFreeVoice: Synth=%d\n", pSynth->vSynthNum); */ }
1335#endif
1336
1337    /* return to free voice pool */
1338    pVoiceMgr->activeVoices--;
1339    pSynth->numActiveVoices--;
1340    InitVoice(pVoice);
1341
1342#ifdef _DEBUG_VM
1343    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMFreeVoice: free voice %d\n", pVoice - pVoiceMgr->voices); */ }
1344#endif
1345
1346    /* all notes older than this one get "younger" */
1347    VMUpdateAllNotesAge(pVoiceMgr, pVoice->age);
1348 }
1349
1350/*----------------------------------------------------------------------------
1351 * VMRetargetStolenVoice()
1352 *----------------------------------------------------------------------------
1353 * Purpose:
1354 * The selected voice has been stolen and needs to be initalized with
1355 * the paramters of its new note.
1356 *
1357 * Inputs:
1358 * pVoice - pointer to voice to retarget
1359 *
1360 * Outputs:
1361 * None
1362 *----------------------------------------------------------------------------
1363*/
1364static EAS_BOOL VMRetargetStolenVoice (S_VOICE_MGR *pVoiceMgr, EAS_I32 voiceNum)
1365{
1366    EAS_U8 flags;
1367    S_SYNTH_CHANNEL *pMIDIChannel;
1368    S_SYNTH_VOICE *pVoice;
1369    S_SYNTH *pSynth;
1370    S_SYNTH *pNextSynth;
1371
1372    /* establish some pointers */
1373    pVoice = &pVoiceMgr->voices[voiceNum];
1374    pSynth = pVoiceMgr->pSynth[GET_VSYNTH(pVoice->channel)];
1375    pMIDIChannel = &pSynth->channels[pVoice->channel & 15];
1376    pNextSynth = pVoiceMgr->pSynth[GET_VSYNTH(pVoice->nextChannel)];
1377
1378#ifdef _DEBUG_VM
1379{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMRetargetStolenVoice: retargeting stolen voice %d on channel %d\n",
1380        voiceNum, pVoice->channel); */ }
1381
1382    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "\to channel %d note: %d velocity: %d\n",
1383        pVoice->nextChannel, pVoice->nextNote, pVoice->nextVelocity); */ }
1384#endif
1385
1386    /* make sure new channel hasn't been muted by SP-MIDI since the voice was stolen */
1387    if ((pSynth->synthFlags & SYNTH_FLAG_SP_MIDI_ON) &&
1388        (pMIDIChannel->channelFlags & CHANNEL_FLAG_MUTE))
1389    {
1390        VMFreeVoice(pVoiceMgr, pSynth, &pVoiceMgr->voices[voiceNum]);
1391        return EAS_FALSE;
1392    }
1393
1394    /* if assigned to a new synth, correct the active voice count */
1395    if (pVoice->channel != pVoice->nextChannel)
1396    {
1397#ifdef _DEBUG_VM
1398        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMRetargetStolenVoice: Note assigned to different virtual synth, adjusting numActiveVoices\n"); */ }
1399#endif
1400        pSynth->numActiveVoices--;
1401        pNextSynth->numActiveVoices++;
1402    }
1403
1404    /* assign new channel number, and increase channel voice count */
1405    pVoice->channel = pVoice->nextChannel;
1406    pMIDIChannel = &pNextSynth->channels[pVoice->channel & 15];
1407
1408    /* assign other data */
1409    pVoice->note = pVoice->nextNote;
1410    pVoice->velocity = pVoice->nextVelocity;
1411    pVoice->nextChannel = UNASSIGNED_SYNTH_CHANNEL;
1412    pVoice->regionIndex = pVoice->nextRegionIndex;
1413
1414    /* save the flags, pfStartVoice() will clear them */
1415    flags = pVoice->voiceFlags;
1416
1417    /* keep track of the note-start related workload */
1418    pVoiceMgr->workload += WORKLOAD_AMOUNT_START_NOTE;
1419
1420    /* setup the voice parameters */
1421    pVoice->voiceState = eVoiceStateStart;
1422
1423    /*lint -e{522} return not used at this time */
1424    GetSynthPtr(voiceNum)->pfStartVoice(pVoiceMgr, pNextSynth, &pVoiceMgr->voices[voiceNum], GetAdjustedVoiceNum(voiceNum), pVoice->regionIndex);
1425
1426    /* did the new note already receive a MIDI note-off request? */
1427    if (flags & VOICE_FLAG_DEFER_MIDI_NOTE_OFF)
1428    {
1429#ifdef _DEBUG_VM
1430        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMRetargetVoice: stolen note note-off request deferred\n"); */ }
1431#endif
1432        pVoice->voiceFlags |= VOICE_FLAG_DEFER_MIDI_NOTE_OFF;
1433        pNextSynth->synthFlags |= SYNTH_FLAG_DEFERRED_MIDI_NOTE_OFF_PENDING;
1434    }
1435
1436    return EAS_TRUE;
1437}
1438
1439/*----------------------------------------------------------------------------
1440 * VMCheckKeyGroup()
1441 *----------------------------------------------------------------------------
1442 * If the note that we've been asked to start is in the same key group as
1443 * any currently playing notes, then we must shut down the currently playing
1444 * note in the same key group
1445 *----------------------------------------------------------------------------
1446*/
1447void VMCheckKeyGroup (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U16 keyGroup, EAS_U8 channel)
1448{
1449    const S_REGION *pRegion;
1450    EAS_INT voiceNum;
1451
1452    /* increment frame workload */
1453    pVoiceMgr->workload += WORKLOAD_AMOUNT_KEY_GROUP;
1454
1455    /* need to check all voices in case this is a layered sound */
1456    channel = VSynthToChannel(pSynth, channel);
1457    for (voiceNum = 0; voiceNum < MAX_SYNTH_VOICES; voiceNum++)
1458    {
1459        if (pVoiceMgr->voices[voiceNum].voiceState != eVoiceStateStolen)
1460        {
1461            /* voice must be on the same channel */
1462            if (channel == pVoiceMgr->voices[voiceNum].channel)
1463            {
1464                /* check key group */
1465                pRegion = GetRegionPtr(pSynth, pVoiceMgr->voices[voiceNum].regionIndex);
1466                if (keyGroup == (pRegion->keyGroupAndFlags & 0x0f00))
1467                {
1468#ifdef _DEBUG_VM
1469                    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMCheckKeyGroup: voice %d matches key group %d\n", voiceNum, keyGroup >> 8); */ }
1470#endif
1471
1472                    /* if this voice was just started, set it to mute on the next buffer */
1473                    if (pVoiceMgr->voices[voiceNum].voiceFlags & VOICE_FLAG_NO_SAMPLES_SYNTHESIZED_YET)
1474                        pVoiceMgr->voices[voiceNum].voiceFlags |= VOICE_FLAG_DEFER_MUTE;
1475
1476                    /* mute immediately */
1477                    else
1478                        VMMuteVoice(pVoiceMgr, voiceNum);
1479                }
1480            }
1481        }
1482
1483        /* for stolen voice, check new values */
1484        else
1485        {
1486            /* voice must be on the same channel */
1487            if (channel == pVoiceMgr->voices[voiceNum].nextChannel)
1488            {
1489                /* check key group */
1490                pRegion = GetRegionPtr(pSynth, pVoiceMgr->voices[voiceNum].nextRegionIndex);
1491                if (keyGroup == (pRegion->keyGroupAndFlags & 0x0f00))
1492                {
1493#ifdef _DEBUG_VM
1494                    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMCheckKeyGroup: voice %d matches key group %d\n", voiceNum, keyGroup >> 8); */ }
1495#endif
1496
1497                    /* if this voice was just started, set it to mute on the next buffer */
1498                    if (pVoiceMgr->voices[voiceNum].voiceFlags & VOICE_FLAG_NO_SAMPLES_SYNTHESIZED_YET)
1499                        pVoiceMgr->voices[voiceNum].voiceFlags |= VOICE_FLAG_DEFER_MUTE;
1500
1501                    /* mute immediately */
1502                    else
1503                        VMMuteVoice(pVoiceMgr, voiceNum);
1504                }
1505            }
1506
1507        }
1508    }
1509}
1510
1511/*----------------------------------------------------------------------------
1512 * VMCheckPolyphonyLimiting()
1513 *----------------------------------------------------------------------------
1514 * Purpose:
1515 * We only play at most 2 of the same note on a MIDI channel.
1516 * E.g., if we are asked to start note 36, and there are already two voices
1517 * that are playing note 36, then we must steal the voice playing
1518 * the oldest note 36 and use that stolen voice to play the new note 36.
1519 *
1520 * Inputs:
1521 * nChannel - synth channel that wants to start a new note
1522 * nKeyNumber - new note's midi note number
1523 * nNoteVelocity - new note's velocity
1524 * psEASData - pointer to overall EAS data structure
1525 *
1526 * Outputs:
1527 * pbVoiceStealingRequired - flag: this routine sets true if we needed to
1528 *                                 steal a voice
1529 * *
1530 * Side Effects:
1531 * psSynthObject->m_sVoice[free voice num].m_nKeyNumber may be assigned
1532 * psSynthObject->m_sVoice[free voice num].m_nVelocity may be assigned
1533 *----------------------------------------------------------------------------
1534*/
1535EAS_BOOL VMCheckPolyphonyLimiting (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 note, EAS_U8 velocity, EAS_U16 regionIndex, EAS_I32 lowVoice, EAS_I32 highVoice)
1536{
1537    EAS_INT voiceNum;
1538    EAS_INT oldestVoiceNum;
1539    EAS_INT numVoicesPlayingNote;
1540    EAS_U16 age;
1541    EAS_U16 oldestNoteAge;
1542
1543    pVoiceMgr->workload += WORKLOAD_AMOUNT_POLY_LIMIT;
1544
1545    numVoicesPlayingNote = 0;
1546    oldestVoiceNum = MAX_SYNTH_VOICES;
1547    oldestNoteAge = 0;
1548    channel = VSynthToChannel(pSynth, channel);
1549
1550    /* examine each voice on this channel playing this note */
1551    for (voiceNum = lowVoice; voiceNum <= highVoice; voiceNum++)
1552    {
1553        /* check stolen notes separately */
1554        if (pVoiceMgr->voices[voiceNum].voiceState != eVoiceStateStolen)
1555        {
1556
1557            /* same channel and note ? */
1558            if ((channel == pVoiceMgr->voices[voiceNum].channel) && (note == pVoiceMgr->voices[voiceNum].note))
1559            {
1560                numVoicesPlayingNote++;
1561                age = pVoiceMgr->age - pVoiceMgr->voices[voiceNum].age;
1562
1563                /* is this the oldest voice for this note? */
1564                if (age >= oldestNoteAge)
1565                {
1566                    oldestNoteAge = age;
1567                    oldestVoiceNum = voiceNum;
1568                }
1569            }
1570        }
1571
1572        /* handle stolen voices */
1573        else
1574        {
1575            /* same channel and note ? */
1576            if ((channel == pVoiceMgr->voices[voiceNum].nextChannel) && (note == pVoiceMgr->voices[voiceNum].nextNote))
1577            {
1578                numVoicesPlayingNote++;
1579            }
1580        }
1581    }
1582
1583    /* check to see if we exceeded poly limit */
1584    if (numVoicesPlayingNote < DEFAULT_CHANNEL_POLYPHONY_LIMIT)
1585        return EAS_FALSE;
1586
1587    /* make sure we have a voice to steal */
1588    if (oldestVoiceNum != MAX_SYNTH_VOICES)
1589    {
1590#ifdef _DEBUG_VM
1591        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMCheckPolyphonyLimiting: voice %d has the oldest note\n", oldestVoiceNum); */ }
1592        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "VMCheckPolyphonyLimiting: polyphony limiting requires shutting down note %d \n", pVoiceMgr->voices[oldestVoiceNum].note); */ }
1593#endif
1594        VMStolenVoice(pVoiceMgr, pSynth, oldestVoiceNum, channel, note, velocity, regionIndex);
1595        return EAS_TRUE;
1596    }
1597
1598#ifdef _DEBUG_VM
1599        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "VMCheckPolyphonyLimiting: No oldest voice to steal\n"); */ }
1600#endif
1601    return EAS_FALSE;
1602}
1603
1604/*----------------------------------------------------------------------------
1605 * VMStartVoice()
1606 *----------------------------------------------------------------------------
1607 * Starts a voice given a region index
1608 *----------------------------------------------------------------------------
1609*/
1610void VMStartVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 note, EAS_U8 velocity, EAS_U16 regionIndex)
1611{
1612    const S_REGION *pRegion;
1613    S_SYNTH_CHANNEL *pChannel;
1614    EAS_INT voiceNum;
1615    EAS_INT maxSynthPoly;
1616    EAS_I32 lowVoice, highVoice;
1617    EAS_U16 keyGroup;
1618
1619    pChannel = &pSynth->channels[channel];
1620    pRegion = GetRegionPtr(pSynth, regionIndex);
1621
1622    /* select correct synth */
1623#if defined(_SECONDARY_SYNTH) || defined(EAS_SPLIT_WT_SYNTH)
1624    {
1625#ifdef EAS_SPLIT_WT_SYNTH
1626        if ((pRegion->keyGroupAndFlags & REGION_FLAG_OFF_CHIP) == 0)
1627#else
1628        if ((regionIndex & FLAG_RGN_IDX_FM_SYNTH) == 0)
1629#endif
1630        {
1631            lowVoice = 0;
1632            highVoice = NUM_PRIMARY_VOICES - 1;
1633        }
1634        else
1635        {
1636            lowVoice = NUM_PRIMARY_VOICES;
1637            highVoice = MAX_SYNTH_VOICES - 1;
1638        }
1639    }
1640#else
1641    lowVoice = 0;
1642    highVoice = MAX_SYNTH_VOICES - 1;
1643#endif
1644
1645    /* keep track of the note-start related workload */
1646    pVoiceMgr->workload+= WORKLOAD_AMOUNT_START_NOTE;
1647
1648    /* other voices in pool, check for key group and poly limiting */
1649    if (pSynth->poolCount[pChannel->pool] != 0)
1650    {
1651
1652        /* check for key group exclusivity */
1653        keyGroup = pRegion->keyGroupAndFlags & 0x0f00;
1654        if (keyGroup!= 0)
1655            VMCheckKeyGroup(pVoiceMgr, pSynth, keyGroup, channel);
1656
1657        /* check polyphony limit and steal a voice if necessary */
1658        if ((pRegion->keyGroupAndFlags & REGION_FLAG_NON_SELF_EXCLUSIVE) == 0)
1659        {
1660            if (VMCheckPolyphonyLimiting(pVoiceMgr, pSynth, channel, note, velocity, regionIndex, lowVoice, highVoice) == EAS_TRUE)
1661                return;
1662        }
1663    }
1664
1665    /* check max poly allocation */
1666    if ((pSynth->maxPolyphony == 0) || (pVoiceMgr->maxPolyphony < pSynth->maxPolyphony))
1667        maxSynthPoly = pVoiceMgr->maxPolyphony;
1668    else
1669        maxSynthPoly = pSynth->maxPolyphony;
1670
1671    /* any free voices? */
1672    if ((pVoiceMgr->activeVoices < pVoiceMgr->maxPolyphony) &&
1673        (pSynth->numActiveVoices < maxSynthPoly) &&
1674        (EAS_SUCCESS == VMFindAvailableVoice(pVoiceMgr, &voiceNum, lowVoice, highVoice)))
1675    {
1676        S_SYNTH_VOICE *pVoice = &pVoiceMgr->voices[voiceNum];
1677
1678#ifdef _DEBUG_VM
1679    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "VMStartVoice: Synth=%d\n", pSynth->vSynthNum); */ }
1680#endif
1681
1682        /* bump voice counts */
1683        pVoiceMgr->activeVoices++;
1684        pSynth->numActiveVoices++;
1685
1686#ifdef _DEBUG_VM
1687        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMStartVoice: voice %d assigned to channel %d note %d velocity %d\n",
1688            voiceNum, channel, note, velocity); */ }
1689#endif
1690
1691        /* save parameters */
1692        pVoiceMgr->voices[voiceNum].channel = VSynthToChannel(pSynth, channel);
1693        pVoiceMgr->voices[voiceNum].note = note;
1694        pVoiceMgr->voices[voiceNum].velocity = velocity;
1695
1696        /* establish note age for voice stealing */
1697        pVoiceMgr->voices[voiceNum].age = pVoiceMgr->age++;
1698
1699        /* setup the synthesis parameters */
1700        pVoiceMgr->voices[voiceNum].voiceState = eVoiceStateStart;
1701
1702        /* increment voice pool count */
1703        IncVoicePoolCount(pVoiceMgr, pVoice);
1704
1705        /* start voice on correct synth */
1706        /*lint -e{522} return not used at this time */
1707        GetSynthPtr(voiceNum)->pfStartVoice(pVoiceMgr, pSynth, &pVoiceMgr->voices[voiceNum], GetAdjustedVoiceNum(voiceNum), regionIndex);
1708        return;
1709    }
1710
1711    /* no free voices, we have to steal one using appropriate algorithm */
1712    if (VMStealVoice(pVoiceMgr, pSynth, &voiceNum, channel, note, lowVoice, highVoice) == EAS_SUCCESS)
1713        VMStolenVoice(pVoiceMgr, pSynth, voiceNum, channel, note, velocity, regionIndex);
1714
1715#ifdef _DEBUG_VM
1716    else
1717    {
1718        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMStartVoice: Could not steal a voice for channel %d note %d velocity %d\n",
1719            channel, note, velocity); */ }
1720    }
1721#endif
1722
1723    return;
1724}
1725
1726/*----------------------------------------------------------------------------
1727 * VMStartNote()
1728 *----------------------------------------------------------------------------
1729 * Purpose:
1730 * Update the synth's state to play the requested note on the requested
1731 * channel if possible.
1732 *
1733 * Inputs:
1734 * nChannel - the channel to start a note on
1735 * nKeyNumber - the key number to start a note for
1736 * nNoteVelocity - the key velocity from this note
1737 * psEASData - pointer to overall EAS data structure
1738 *
1739 * Outputs:
1740 * Side Effects:
1741 * psSynthObject->m_nNumActiveVoices may be incremented
1742 * psSynthObject->m_sVoice[free voice num].m_nSynthChannel may be assigned
1743 * psSynthObject->m_sVoice[free voice num].m_nKeyNumber is assigned
1744 * psSynthObject->m_sVoice[free voice num].m_nVelocity is assigned
1745 *----------------------------------------------------------------------------
1746*/
1747void VMStartNote (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 note, EAS_U8 velocity)
1748{
1749    S_SYNTH_CHANNEL *pChannel;
1750    EAS_U16 regionIndex;
1751    EAS_I16 adjustedNote;
1752
1753    /* bump note count */
1754    pSynth->totalNoteCount++;
1755
1756    pChannel = &pSynth->channels[channel];
1757
1758    /* check channel mute */
1759    if (pChannel->channelFlags & CHANNEL_FLAG_MUTE)
1760        return;
1761
1762#ifdef EXTERNAL_AUDIO
1763    /* pass event to external audio when requested */
1764    if ((pChannel->channelFlags & CHANNEL_FLAG_EXTERNAL_AUDIO) && (pSynth->cbEventFunc != NULL))
1765    {
1766        S_EXT_AUDIO_EVENT event;
1767        event.channel = channel;
1768        event.note = note;
1769        event.velocity = velocity;
1770        event.noteOn = EAS_TRUE;
1771        if (pSynth->cbEventFunc(pSynth->pExtAudioInstData, &event))
1772            return;
1773    }
1774#endif
1775
1776    /* start search at first region */
1777    regionIndex = pChannel->regionIndex;
1778
1779    /* handle transposition */
1780    adjustedNote = note;
1781    if (pChannel->channelFlags & CHANNEL_FLAG_RHYTHM_CHANNEL)
1782        adjustedNote += pChannel->coarsePitch;
1783    else
1784        adjustedNote += pChannel->coarsePitch + pSynth->globalTranspose;
1785
1786    /* limit adjusted key number so it does not wraparound, over/underflow */
1787    if (adjustedNote < 0)
1788    {
1789        adjustedNote = 0;
1790    }
1791    else if (adjustedNote > 127)
1792    {
1793        adjustedNote = 127;
1794    }
1795
1796#if defined(DLS_SYNTHESIZER)
1797    if (regionIndex & FLAG_RGN_IDX_DLS_SYNTH)
1798    {
1799        /* DLS voice */
1800        for (;;)
1801        {
1802            /*lint -e{740,826} cast OK, we know this is actually a DLS region */
1803            const S_DLS_REGION *pDLSRegion = (S_DLS_REGION*) GetRegionPtr(pSynth, regionIndex);
1804
1805            /* check key against this region's key and velocity range */
1806            if (((adjustedNote >= pDLSRegion->wtRegion.region.rangeLow) && (adjustedNote <= pDLSRegion->wtRegion.region.rangeHigh)) &&
1807                ((velocity >= pDLSRegion->velLow) && (velocity <= pDLSRegion->velHigh)))
1808            {
1809                VMStartVoice(pVoiceMgr, pSynth, channel, note, velocity, regionIndex);
1810            }
1811
1812            /* last region in program? */
1813            if (pDLSRegion->wtRegion.region.keyGroupAndFlags & REGION_FLAG_LAST_REGION)
1814                break;
1815
1816            /* advance to next region */
1817            regionIndex++;
1818        }
1819    }
1820    else
1821#endif
1822
1823    /* braces here for #if clause */
1824    {
1825        /* EAS voice */
1826        for (;;)
1827        {
1828            const S_REGION *pRegion = GetRegionPtr(pSynth, regionIndex);
1829
1830            /* check key against this region's keyrange */
1831            if ((adjustedNote >= pRegion->rangeLow) && (adjustedNote <= pRegion->rangeHigh))
1832            {
1833                VMStartVoice(pVoiceMgr, pSynth, channel, note, velocity, regionIndex);
1834                break;
1835            }
1836
1837            /* last region in program? */
1838            if (pRegion->keyGroupAndFlags & REGION_FLAG_LAST_REGION)
1839                break;
1840
1841            /* advance to next region */
1842            regionIndex++;
1843        }
1844    }
1845}
1846
1847/*----------------------------------------------------------------------------
1848 * VMStopNote()
1849 *----------------------------------------------------------------------------
1850 * Purpose:
1851 * Update the synth's state to end the requested note on the requested
1852 * channel.
1853 *
1854 * Inputs:
1855 * nChannel - the channel to stop a note on
1856 * nKeyNumber - the key number for this note off
1857 * nNoteVelocity - the note-off velocity
1858 * psEASData - pointer to overall EAS data structure
1859 *
1860 * Outputs:
1861 * Side Effects:
1862 * psSynthObject->m_sVoice[free voice num].m_nSynthChannel may be assigned
1863 * psSynthObject->m_sVoice[free voice num].m_nKeyNumber is assigned
1864 * psSynthObject->m_sVoice[free voice num].m_nVelocity is assigned
1865 *----------------------------------------------------------------------------
1866*/
1867/*lint -esym(715, velocity) reserved for future use */
1868void VMStopNote (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 note, EAS_U8 velocity)
1869{
1870    S_SYNTH_CHANNEL *pChannel;
1871    EAS_INT voiceNum;
1872
1873    pChannel = &(pSynth->channels[channel]);
1874
1875#ifdef EXTERNAL_AUDIO
1876    if ((pChannel->channelFlags & CHANNEL_FLAG_EXTERNAL_AUDIO) && (pSynth->cbEventFunc != NULL))
1877    {
1878        S_EXT_AUDIO_EVENT event;
1879        event.channel = channel;
1880        event.note = note;
1881        event.velocity = velocity;
1882        event.noteOn = EAS_FALSE;
1883        if (pSynth->cbEventFunc(pSynth->pExtAudioInstData, &event))
1884            return;
1885    }
1886#endif
1887
1888    /* keep track of the note-start workload */
1889    pVoiceMgr->workload += WORKLOAD_AMOUNT_STOP_NOTE;
1890
1891    channel = VSynthToChannel(pSynth, channel);
1892
1893    for (voiceNum=0; voiceNum < MAX_SYNTH_VOICES; voiceNum++)
1894    {
1895
1896        /* stolen notes are handled separately */
1897        if (eVoiceStateStolen != pVoiceMgr->voices[voiceNum].voiceState)
1898        {
1899
1900            /* channel and key number must match */
1901            if ((channel == pVoiceMgr->voices[voiceNum].channel) && (note == pVoiceMgr->voices[voiceNum].note))
1902            {
1903#ifdef _DEBUG_VM
1904                { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMStopNote: voice %d channel %d note %d\n",
1905                    voiceNum, channel, note); */ }
1906#endif
1907
1908                /* if sustain pedal is down, set deferred note-off flag */
1909                if (pChannel->channelFlags & CHANNEL_FLAG_SUSTAIN_PEDAL)
1910                {
1911                    pVoiceMgr->voices[voiceNum].voiceFlags |= VOICE_FLAG_SUSTAIN_PEDAL_DEFER_NOTE_OFF;
1912                    continue;
1913                }
1914
1915                /* if this note just started, wait before we stop it */
1916                if (pVoiceMgr->voices[voiceNum].voiceFlags & VOICE_FLAG_NO_SAMPLES_SYNTHESIZED_YET)
1917                {
1918#ifdef _DEBUG_VM
1919                    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "\tDeferred: Not started yet\n"); */ }
1920#endif
1921                    pVoiceMgr->voices[voiceNum].voiceFlags |= VOICE_FLAG_DEFER_MIDI_NOTE_OFF;
1922                    pSynth->synthFlags |= SYNTH_FLAG_DEFERRED_MIDI_NOTE_OFF_PENDING;
1923                }
1924
1925                /* release voice */
1926                else
1927                    VMReleaseVoice(pVoiceMgr, pSynth, voiceNum);
1928
1929            }
1930        }
1931
1932        /* process stolen notes, new channel and key number must match */
1933        else if ((channel == pVoiceMgr->voices[voiceNum].nextChannel) && (note == pVoiceMgr->voices[voiceNum].nextNote))
1934        {
1935
1936#ifdef _DEBUG_VM
1937            { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMStopNote: voice %d channel %d note %d\n\tDeferred: Stolen voice\n",
1938                voiceNum, channel, note); */ }
1939#endif
1940            pVoiceMgr->voices[voiceNum].voiceFlags |= VOICE_FLAG_DEFER_MIDI_NOTE_OFF;
1941        }
1942    }
1943}
1944
1945/*----------------------------------------------------------------------------
1946 * VMFindAvailableVoice()
1947 *----------------------------------------------------------------------------
1948 * Purpose:
1949 * Find an available voice and return the voice number if available.
1950 *
1951 * Inputs:
1952 * pnVoiceNumber - really an output, returns the voice number found
1953 * psEASData - pointer to overall EAS data structure
1954 *
1955 * Outputs:
1956 * success - if there is an available voice
1957 * failure - otherwise
1958 *----------------------------------------------------------------------------
1959*/
1960EAS_RESULT VMFindAvailableVoice (S_VOICE_MGR *pVoiceMgr, EAS_INT *pVoiceNumber, EAS_I32 lowVoice, EAS_I32 highVoice)
1961{
1962    EAS_INT voiceNum;
1963
1964    /* Check each voice to see if it has been assigned to a synth channel */
1965    for (voiceNum = lowVoice; voiceNum <= highVoice; voiceNum++)
1966    {
1967        /* check if this voice has been assigned to a synth channel */
1968        if ( pVoiceMgr->voices[voiceNum].voiceState == eVoiceStateFree)
1969        {
1970            *pVoiceNumber = voiceNum;       /* this voice is available */
1971            return EAS_SUCCESS;
1972        }
1973    }
1974
1975    /* if we reach here, we have not found a free voice */
1976    *pVoiceNumber = UNASSIGNED_SYNTH_VOICE;
1977
1978#ifdef _DEBUG_VM
1979    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMFindAvailableVoice: error, could not find an available voice\n"); */ }
1980#endif
1981    return EAS_FAILURE;
1982}
1983
1984/*----------------------------------------------------------------------------
1985 * VMStealVoice()
1986 *----------------------------------------------------------------------------
1987 * Purpose:
1988 * Steal a voice and return the voice number
1989 *
1990 * Stealing algorithm: steal the best choice with minimal work, taking into
1991 * account SP-Midi channel priorities and polyphony allocation.
1992 *
1993 * In one pass through all the voices, figure out which voice to steal
1994 * taking into account a number of different factors:
1995 * Priority of the voice's MIDI channel
1996 * Number of voices over the polyphony allocation for voice's MIDI channel
1997 * Amplitude of the voice
1998 * Note age
1999 * Key velocity (for voices that haven't been started yet)
2000 * If any matching notes are found
2001 *
2002 * Inputs:
2003 * pnVoiceNumber - really an output, see below
2004 * nChannel - the channel that this voice wants to be started on
2005 * nKeyNumber - the key number for this new voice
2006 * psEASData - pointer to overall EAS data structure
2007 *
2008 * Outputs:
2009 * pnVoiceNumber - voice number of the voice that was stolen
2010 * EAS_RESULT EAS_SUCCESS - always successful
2011 *----------------------------------------------------------------------------
2012*/
2013EAS_RESULT VMStealVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_INT *pVoiceNumber, EAS_U8 channel, EAS_U8 note, EAS_I32 lowVoice, EAS_I32 highVoice)
2014{
2015    S_SYNTH_VOICE *pCurrVoice;
2016    S_SYNTH *pCurrSynth;
2017    EAS_INT voiceNum;
2018    EAS_INT bestCandidate;
2019    EAS_U8 currChannel;
2020    EAS_U8 currNote;
2021    EAS_I32 bestPriority;
2022    EAS_I32 currentPriority;
2023
2024    /* determine which voice to steal */
2025    bestPriority = 0;
2026    bestCandidate = MAX_SYNTH_VOICES;
2027
2028    for (voiceNum = lowVoice; voiceNum <= highVoice; voiceNum++)
2029    {
2030        pCurrVoice = &pVoiceMgr->voices[voiceNum];
2031
2032        /* ignore free voices */
2033        if (pCurrVoice->voiceState == eVoiceStateFree)
2034            continue;
2035
2036        /* for stolen voices, use the new parameters, not the old */
2037        if (pCurrVoice->voiceState == eVoiceStateStolen)
2038        {
2039            pCurrSynth = pVoiceMgr->pSynth[GET_VSYNTH(pCurrVoice->nextChannel)];
2040            currChannel = pCurrVoice->nextChannel;
2041            currNote = pCurrVoice->nextNote;
2042        }
2043        else
2044        {
2045            pCurrSynth = pVoiceMgr->pSynth[GET_VSYNTH(pCurrVoice->channel)];
2046            currChannel = pCurrVoice->channel;
2047            currNote = pCurrVoice->note;
2048        }
2049
2050        /* ignore voices that are higher priority */
2051        if (pSynth->priority > pCurrSynth->priority)
2052            continue;
2053#ifdef _DEBUG_VM
2054//      { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMStealVoice: New priority = %d exceeds old priority = %d\n", pSynth->priority, pCurrSynth->priority); */ }
2055#endif
2056
2057        /* if voice is stolen or just started, reduce the likelihood it will be stolen */
2058        if (( pCurrVoice->voiceState == eVoiceStateStolen) || (pCurrVoice->voiceFlags & VOICE_FLAG_NO_SAMPLES_SYNTHESIZED_YET))
2059        {
2060            currentPriority = 128 - pCurrVoice->nextVelocity;
2061        }
2062        else
2063        {
2064            /* compute the priority of this voice, higher means better for stealing */
2065            /* use not age */
2066            currentPriority = (EAS_I32) pCurrVoice->age << NOTE_AGE_STEAL_WEIGHT;
2067
2068            /* include note gain -higher gain is lower steal value */
2069            /*lint -e{704} use shift for performance */
2070            currentPriority += ((32768 >> (12 - NOTE_GAIN_STEAL_WEIGHT)) + 256) -
2071                ((EAS_I32) pCurrVoice->gain >> (12 - NOTE_GAIN_STEAL_WEIGHT));
2072        }
2073
2074        /* in SP-MIDI mode, include over poly allocation and channel priority */
2075        if (pSynth->synthFlags & SYNTH_FLAG_SP_MIDI_ON)
2076        {
2077            S_SYNTH_CHANNEL *pChannel = &pCurrSynth->channels[GET_CHANNEL(currChannel)];
2078            /*lint -e{701} use shift for performance */
2079            if (pSynth->poolCount[pChannel->pool] >= pSynth->poolAlloc[pChannel->pool])
2080                currentPriority += (pSynth->poolCount[pChannel->pool] -pSynth->poolAlloc[pChannel->pool] + 1) << CHANNEL_POLY_STEAL_WEIGHT;
2081
2082            /* include channel priority */
2083            currentPriority += (EAS_I32)(pChannel->pool << CHANNEL_PRIORITY_STEAL_WEIGHT);
2084        }
2085
2086        /* if a note is already playing that matches this note, consider stealing it more readily */
2087        if ((note == currNote) && (channel == currChannel))
2088            currentPriority += NOTE_MATCH_PENALTY;
2089
2090        /* is this the best choice so far? */
2091        if (currentPriority >= bestPriority)
2092        {
2093            bestPriority = currentPriority;
2094            bestCandidate = voiceNum;
2095        }
2096    }
2097
2098    /* may happen if all voices are allocated to a higher priority virtual synth */
2099    if (bestCandidate == MAX_SYNTH_VOICES)
2100    {
2101        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMStealVoice: Unable to allocate a voice\n"); */ }
2102        return EAS_ERROR_NO_VOICE_ALLOCATED;
2103    }
2104
2105#ifdef _DEBUG_VM
2106    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMStealVoice: Voice %d stolen\n", bestCandidate); */ }
2107
2108    /* are we stealing a stolen voice? */
2109    if (pVoiceMgr->voices[bestCandidate].voiceState == eVoiceStateStolen)
2110    {
2111        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "VMStealVoice: Voice %d is already marked as stolen and was scheduled to play ch: %d note: %d vel: %d\n",
2112            bestCandidate,
2113            pVoiceMgr->voices[bestCandidate].nextChannel,
2114            pVoiceMgr->voices[bestCandidate].nextNote,
2115            pVoiceMgr->voices[bestCandidate].nextVelocity); */ }
2116    }
2117#endif
2118
2119    *pVoiceNumber = (EAS_U16) bestCandidate;
2120    return EAS_SUCCESS;
2121}
2122
2123/*----------------------------------------------------------------------------
2124 * VMChannelPressure()
2125 *----------------------------------------------------------------------------
2126 * Purpose:
2127 * Change the channel pressure for the given channel
2128 *
2129 * Inputs:
2130 * nChannel - the MIDI channel
2131 * nVelocity - the channel pressure value
2132 * psEASData - pointer to overall EAS data structure
2133 *
2134 * Outputs:
2135 * Side Effects:
2136 * psSynthObject->m_sChannel[nChannel].m_nChannelPressure is updated
2137 *----------------------------------------------------------------------------
2138*/
2139void VMChannelPressure (S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 value)
2140{
2141    S_SYNTH_CHANNEL *pChannel;
2142
2143    pChannel = &(pSynth->channels[channel]);
2144    pChannel->channelPressure = value;
2145
2146    /*
2147    set a channel flag to request parameter updates
2148    for all the voices associated with this channel
2149    */
2150    pChannel->channelFlags |= CHANNEL_FLAG_UPDATE_CHANNEL_PARAMETERS;
2151}
2152
2153/*----------------------------------------------------------------------------
2154 * VMPitchBend()
2155 *----------------------------------------------------------------------------
2156 * Purpose:
2157 * Change the pitch wheel value for the given channel.
2158 * This routine constructs the proper 14-bit argument when the calling routine
2159 * passes the pitch LSB and MSB.
2160 *
2161 * Note: some midi disassemblers display a bipolar pitch bend value.
2162 * We can display the bipolar value using
2163 * if m_nPitchBend >= 0x2000
2164 *      bipolar pitch bend = postive (m_nPitchBend - 0x2000)
2165 * else
2166 *      bipolar pitch bend = negative (0x2000 - m_nPitchBend)
2167 *
2168 * Inputs:
2169 * nChannel - the MIDI channel
2170 * nPitchLSB - the LSB byte of the pitch bend message
2171 * nPitchMSB - the MSB byte of the pitch bend message
2172 * psEASData - pointer to overall EAS data structure
2173 *
2174 * Outputs:
2175 *
2176 * Side Effects:
2177 * psSynthObject->m_sChannel[nChannel].m_nPitchBend is changed
2178 *
2179 *----------------------------------------------------------------------------
2180*/
2181void VMPitchBend (S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 nPitchLSB, EAS_U8 nPitchMSB)
2182{
2183    S_SYNTH_CHANNEL *pChannel;
2184
2185    pChannel = &(pSynth->channels[channel]);
2186    pChannel->pitchBend = (EAS_I16) ((nPitchMSB << 7) | nPitchLSB);
2187
2188    /*
2189    set a channel flag to request parameter updates
2190    for all the voices associated with this channel
2191    */
2192    pChannel->channelFlags |= CHANNEL_FLAG_UPDATE_CHANNEL_PARAMETERS;
2193}
2194
2195/*----------------------------------------------------------------------------
2196 * VMControlChange()
2197 *----------------------------------------------------------------------------
2198 * Purpose:
2199 * Change the controller (or mode) for the given channel.
2200 *
2201 * Inputs:
2202 * nChannel - the MIDI channel
2203 * nControllerNumber - the MIDI controller number
2204 * nControlValue - the value for this controller message
2205 * psEASData - pointer to overall EAS data structure
2206 *
2207 * Outputs:
2208 * Side Effects:
2209 * psSynthObject->m_sChannel[nChannel] controller is changed
2210 *
2211 *----------------------------------------------------------------------------
2212*/
2213void VMControlChange (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 controller, EAS_U8 value)
2214{
2215    S_SYNTH_CHANNEL *pChannel;
2216
2217    pChannel = &(pSynth->channels[channel]);
2218
2219    /*
2220    set a channel flag to request parameter updates
2221    for all the voices associated with this channel
2222    */
2223    pChannel->channelFlags |= CHANNEL_FLAG_UPDATE_CHANNEL_PARAMETERS;
2224
2225    switch ( controller )
2226    {
2227    case MIDI_CONTROLLER_BANK_SELECT_MSB:
2228#ifdef _DEBUG_VM
2229        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMControlChange: Bank Select MSB: msb 0x%X\n", value); */ }
2230#endif
2231        /* use this MSB with a zero LSB, until we get an LSB message */
2232        pChannel->bankNum = value << 8;
2233        break;
2234
2235    case MIDI_CONTROLLER_MOD_WHEEL:
2236        /* we treat mod wheel as a 7-bit controller and only use the MSB */
2237        pChannel->modWheel = value;
2238        break;
2239
2240    case MIDI_CONTROLLER_VOLUME:
2241        /* we treat volume as a 7-bit controller and only use the MSB */
2242        pChannel->volume = value;
2243        break;
2244
2245    case MIDI_CONTROLLER_PAN:
2246        /* we treat pan as a 7-bit controller and only use the MSB */
2247        pChannel->pan = value;
2248        break;
2249
2250    case MIDI_CONTROLLER_EXPRESSION:
2251        /* we treat expression as a 7-bit controller and only use the MSB */
2252        pChannel->expression = value;
2253        break;
2254
2255    case MIDI_CONTROLLER_BANK_SELECT_LSB:
2256#ifdef _DEBUG_VM
2257        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMControlChange: Bank Select LSB: lsb 0x%X\n", value); */ }
2258#endif
2259        /*
2260        construct bank number as 7-bits (stored as 8) of existing MSB
2261        and 7-bits of new LSB (also stored as 8(
2262        */
2263        pChannel->bankNum =
2264            (pChannel->bankNum & 0xFF00) | value;
2265
2266        break;
2267
2268    case MIDI_CONTROLLER_SUSTAIN_PEDAL:
2269        /* we treat sustain pedal as a boolean on/off bit flag */
2270        if (value < 64)
2271        {
2272            /*
2273            we are requested to turn the pedal off, but first check
2274            if the pedal is already on
2275            */
2276            if (0 !=
2277                (pChannel->channelFlags & CHANNEL_FLAG_SUSTAIN_PEDAL)
2278               )
2279            {
2280                /*
2281                The sustain flag is presently set and the damper pedal is on.
2282                We are therefore transitioning from damper pedal ON to
2283                damper pedal OFF. This means all notes in this channel
2284                that received a note off while the damper pedal was on, and
2285                had their note-off requests deferred, should now proceed to
2286                the release state.
2287                */
2288                VMReleaseAllDeferredNoteOffs(pVoiceMgr, pSynth, channel);
2289            }   /* end if sustain pedal is already on */
2290
2291            /* turn the sustain pedal off */
2292            pChannel->channelFlags &= ~CHANNEL_FLAG_SUSTAIN_PEDAL;
2293        }
2294        else
2295        {
2296            /*
2297            we are requested to turn the pedal on, but first check
2298            if the pedal is already off
2299            */
2300            if (0 ==
2301                (pChannel->channelFlags & CHANNEL_FLAG_SUSTAIN_PEDAL)
2302               )
2303            {
2304                /*
2305                The sustain flag is presently clear and the damper pedal is off.
2306                We are therefore transitioning from damper pedal OFF to
2307                damper pedal ON. Currently sounding notes should be left
2308                unchanged. However, we should try to "catch" notes if possible.
2309                If any notes have levels >= sustain level, catch them,
2310                otherwise, let them continue to release.
2311                */
2312                VMCatchNotesForSustainPedal(pVoiceMgr, pSynth, channel);
2313            }
2314
2315            /* turn the sustain pedal on */
2316            pChannel->channelFlags |= CHANNEL_FLAG_SUSTAIN_PEDAL;
2317        }
2318
2319        break;
2320#ifdef _REVERB
2321    case MIDI_CONTROLLER_REVERB_SEND:
2322        /* we treat send as a 7-bit controller and only use the MSB */
2323        pSynth->channels[channel].reverbSend = value;
2324        break;
2325#endif
2326#ifdef _CHORUS
2327    case MIDI_CONTROLLER_CHORUS_SEND:
2328        /* we treat send as a 7-bit controller and only use the MSB */
2329        pSynth->channels[channel].chorusSend = value;
2330        break;
2331#endif
2332    case MIDI_CONTROLLER_RESET_CONTROLLERS:
2333        /* despite the Midi message name, not ALL controllers are reset */
2334        pChannel->modWheel = DEFAULT_MOD_WHEEL;
2335        pChannel->expression = DEFAULT_EXPRESSION;
2336
2337        /* turn the sustain pedal off as default/reset */
2338        pChannel->channelFlags &= ~CHANNEL_FLAG_SUSTAIN_PEDAL;
2339        pChannel->pitchBend = DEFAULT_PITCH_BEND;
2340
2341        /* reset channel pressure */
2342        pChannel->channelPressure = DEFAULT_CHANNEL_PRESSURE;
2343
2344        /* reset RPN values */
2345        pChannel->registeredParam = DEFAULT_REGISTERED_PARAM;
2346        pChannel->pitchBendSensitivity = DEFAULT_PITCH_BEND_SENSITIVITY;
2347        pChannel->finePitch = DEFAULT_FINE_PITCH;
2348        pChannel->coarsePitch = DEFAULT_COARSE_PITCH;
2349
2350        /*
2351        program change, bank select, channel volume CC7, pan CC10
2352        are NOT reset
2353        */
2354        break;
2355
2356    /*
2357    For logical reasons, the RPN data entry are grouped together.
2358    However, keep in mind that these cases are not necessarily in
2359    ascending order.
2360    e.g., MIDI_CONTROLLER_DATA_ENTRY_MSB == 6,
2361    whereas MIDI_CONTROLLER_SUSTAIN_PEDAL == 64.
2362    So arrange these case statements in whatever manner is more efficient for
2363    the processor / compiler.
2364    */
2365    case MIDI_CONTROLLER_ENTER_DATA_MSB:
2366    case MIDI_CONTROLLER_ENTER_DATA_LSB:
2367    case MIDI_CONTROLLER_SELECT_RPN_LSB:
2368    case MIDI_CONTROLLER_SELECT_RPN_MSB:
2369    case MIDI_CONTROLLER_SELECT_NRPN_MSB:
2370    case MIDI_CONTROLLER_SELECT_NRPN_LSB:
2371        VMUpdateRPNStateMachine(pSynth, channel, controller, value);
2372        break;
2373
2374    case MIDI_CONTROLLER_ALL_SOUND_OFF:
2375    case MIDI_CONTROLLER_ALL_NOTES_OFF:
2376    case MIDI_CONTROLLER_OMNI_OFF:
2377    case MIDI_CONTROLLER_OMNI_ON:
2378    case MIDI_CONTROLLER_MONO_ON_POLY_OFF:
2379    case MIDI_CONTROLLER_POLY_ON_MONO_OFF:
2380        /* NOTE: we treat all sounds off the same as all notes off */
2381        VMAllNotesOff(pVoiceMgr, pSynth, channel);
2382        break;
2383
2384    default:
2385#ifdef _DEBUG_VM
2386        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMControlChange: controller %d not yet implemented\n", controller); */ }
2387#endif
2388        break;
2389
2390    }
2391
2392    return;
2393}
2394
2395/*----------------------------------------------------------------------------
2396 * VMUpdateRPNStateMachine()
2397 *----------------------------------------------------------------------------
2398 * Purpose:
2399 * Call this function when we want to parse RPN related controller messages.
2400 * We only support RPN0 (pitch bend sensitivity), RPN1 (fine tuning) and
2401 * RPN2 (coarse tuning). Any other RPNs or NRPNs are ignored for now.
2402 *.
2403 * Supports any order, so not a state machine anymore. This function was
2404 * rewritten to work correctly regardless of order.
2405 *
2406 * Inputs:
2407 * nChannel - the channel this controller message is coming from
2408 * nControllerNumber - which RPN related controller
2409 * nControlValue - the value of the RPN related controller
2410 * psEASData - pointer to overall EAS data structure
2411 *
2412 * Outputs:
2413 * returns EAS_RESULT, which is typically EAS_SUCCESS, since there are
2414 * few possible errors
2415 *
2416 * Side Effects:
2417 * gsSynthObject.m_sChannel[nChannel].m_nPitchBendSensitivity
2418 * (or m_nFinePitch or m_nCoarsePitch)
2419 * will be updated if the proper RPN message is received.
2420 *----------------------------------------------------------------------------
2421*/
2422EAS_RESULT VMUpdateRPNStateMachine (S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 controller, EAS_U8 value)
2423{
2424    S_SYNTH_CHANNEL *pChannel;
2425
2426#ifdef _DEBUG_VM
2427    if (channel >= NUM_SYNTH_CHANNELS)
2428    {
2429        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMUpdateRPNStateMachines: error, %d invalid channel number\n",
2430            channel); */ }
2431        return EAS_FAILURE;
2432    }
2433#endif
2434
2435    pChannel = &(pSynth->channels[channel]);
2436
2437    switch (controller)
2438    {
2439    case MIDI_CONTROLLER_SELECT_NRPN_MSB:
2440    case MIDI_CONTROLLER_SELECT_NRPN_LSB:
2441        pChannel->registeredParam = DEFAULT_REGISTERED_PARAM;
2442        break;
2443    case MIDI_CONTROLLER_SELECT_RPN_MSB:
2444        pChannel->registeredParam =
2445            (pChannel->registeredParam & 0x7F) | (value<<7);
2446        break;
2447    case MIDI_CONTROLLER_SELECT_RPN_LSB:
2448        pChannel->registeredParam =
2449            (pChannel->registeredParam & 0x7F00) | value;
2450        break;
2451    case MIDI_CONTROLLER_ENTER_DATA_MSB:
2452        switch (pChannel->registeredParam)
2453        {
2454        case 0:
2455            pChannel->pitchBendSensitivity = value * 100;
2456            break;
2457        case 1:
2458            /*lint -e{702} <avoid division for performance reasons>*/
2459            pChannel->finePitch = (EAS_I8)((((value << 7) - 8192) * 100) >> 13);
2460            break;
2461        case 2:
2462            pChannel->coarsePitch = (EAS_I8)(value - 64);
2463            break;
2464        default:
2465            break;
2466        }
2467        break;
2468    case MIDI_CONTROLLER_ENTER_DATA_LSB:
2469        switch (pChannel->registeredParam)
2470        {
2471        case 0:
2472            //ignore lsb
2473            break;
2474        case 1:
2475            //ignore lsb
2476            break;
2477        case 2:
2478            //ignore lsb
2479            break;
2480        default:
2481            break;
2482        }
2483        break;
2484    default:
2485        return EAS_FAILURE; //not a RPN related controller
2486    }
2487
2488    return EAS_SUCCESS;
2489}
2490
2491/*----------------------------------------------------------------------------
2492 * VMUpdateStaticChannelParameters()
2493 *----------------------------------------------------------------------------
2494 * Purpose:
2495 * Update all of the static channel parameters for channels that have had
2496 * a controller change values
2497 * Or if the synth has signalled that all channels must forcibly
2498 * be updated
2499 *
2500 * Inputs:
2501 * psEASData - pointer to overall EAS data structure
2502 *
2503 * Outputs:
2504 * none
2505 *
2506 * Side Effects:
2507 * - psSynthObject->m_sChannel[].m_nStaticGain and m_nStaticPitch
2508 * are updated for channels whose controller values have changed
2509 * or if the synth has signalled that all channels must forcibly
2510 * be updated
2511 *----------------------------------------------------------------------------
2512*/
2513void VMUpdateStaticChannelParameters (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth)
2514{
2515    EAS_INT channel;
2516
2517    if (pSynth->synthFlags & SYNTH_FLAG_UPDATE_ALL_CHANNEL_PARAMETERS)
2518    {
2519        /*
2520        the synth wants us to forcibly update all channel
2521        parameters. This event occurs when we are about to
2522        finish resetting the synth
2523        */
2524        for (channel = 0; channel < NUM_SYNTH_CHANNELS; channel++)
2525        {
2526#ifdef _HYBRID_SYNTH
2527            if (pSynth->channels[channel].regionIndex & FLAG_RGN_IDX_FM_SYNTH)
2528                pSecondarySynth->pfUpdateChannel(pVoiceMgr, pSynth, (EAS_U8) channel);
2529            else
2530                pPrimarySynth->pfUpdateChannel(pVoiceMgr, pSynth, (EAS_U8) channel);
2531#else
2532            pPrimarySynth->pfUpdateChannel(pVoiceMgr, pSynth, (EAS_U8) channel);
2533#endif
2534        }
2535
2536        /*
2537        clear the flag to indicates we have now forcibly
2538        updated all channel parameters
2539        */
2540        pSynth->synthFlags &= ~SYNTH_FLAG_UPDATE_ALL_CHANNEL_PARAMETERS;
2541    }
2542    else
2543    {
2544
2545        /* only update channel params if signalled by a channel flag */
2546        for (channel = 0; channel < NUM_SYNTH_CHANNELS; channel++)
2547        {
2548            if ( 0 != (pSynth->channels[channel].channelFlags & CHANNEL_FLAG_UPDATE_CHANNEL_PARAMETERS))
2549            {
2550#ifdef _HYBRID_SYNTH
2551                if (pSynth->channels[channel].regionIndex & FLAG_RGN_IDX_FM_SYNTH)
2552                    pSecondarySynth->pfUpdateChannel(pVoiceMgr, pSynth, (EAS_U8) channel);
2553                else
2554                    pPrimarySynth->pfUpdateChannel(pVoiceMgr, pSynth, (EAS_U8) channel);
2555#else
2556                pPrimarySynth->pfUpdateChannel(pVoiceMgr, pSynth, (EAS_U8) channel);
2557#endif
2558            }
2559        }
2560
2561    }
2562
2563    return;
2564}
2565
2566/*----------------------------------------------------------------------------
2567 * VMFindProgram()
2568 *----------------------------------------------------------------------------
2569 * Purpose:
2570 * Look up an individual program in sound library. This function
2571 * searches the bank list for a program, then the individual program
2572 * list.
2573 *
2574 * Inputs:
2575 *
2576 * Outputs:
2577 *----------------------------------------------------------------------------
2578*/
2579static EAS_RESULT VMFindProgram (const S_EAS *pEAS, EAS_U32 bank, EAS_U8 programNum, EAS_U16 *pRegionIndex)
2580{
2581    EAS_U32 locale;
2582    const S_PROGRAM *p;
2583    EAS_U16 i;
2584    EAS_U16 regionIndex;
2585
2586    /* make sure we have a valid sound library */
2587    if (pEAS == NULL)
2588        return EAS_FAILURE;
2589
2590    /* search the banks */
2591    for (i = 0; i <  pEAS->numBanks; i++)
2592    {
2593        if (bank == (EAS_U32) pEAS->pBanks[i].locale)
2594        {
2595            regionIndex = pEAS->pBanks[i].regionIndex[programNum];
2596            if (regionIndex != INVALID_REGION_INDEX)
2597            {
2598                *pRegionIndex = regionIndex;
2599                return EAS_SUCCESS;
2600            }
2601            break;
2602        }
2603    }
2604
2605    /* establish locale */
2606    locale = ( bank << 8) | programNum;
2607
2608    /* search for program */
2609    for (i = 0, p = pEAS->pPrograms; i < pEAS->numPrograms; i++, p++)
2610    {
2611        if (p->locale == locale)
2612        {
2613            *pRegionIndex = p->regionIndex;
2614            return EAS_SUCCESS;
2615        }
2616    }
2617
2618    return EAS_FAILURE;
2619}
2620
2621#ifdef DLS_SYNTHESIZER
2622/*----------------------------------------------------------------------------
2623 * VMFindDLSProgram()
2624 *----------------------------------------------------------------------------
2625 * Purpose:
2626 * Look up an individual program in sound library. This function
2627 * searches the bank list for a program, then the individual program
2628 * list.
2629 *
2630 * Inputs:
2631 *
2632 * Outputs:
2633 *----------------------------------------------------------------------------
2634*/
2635static EAS_RESULT VMFindDLSProgram (const S_DLS *pDLS, EAS_U32 bank, EAS_U8 programNum, EAS_U16 *pRegionIndex)
2636{
2637    EAS_U32 locale;
2638    const S_PROGRAM *p;
2639    EAS_U16 i;
2640
2641    /* make sure we have a valid sound library */
2642    if (pDLS == NULL)
2643        return EAS_FAILURE;
2644
2645    /* establish locale */
2646    locale = (bank << 8) | programNum;
2647
2648    /* search for program */
2649    for (i = 0, p = pDLS->pDLSPrograms; i < pDLS->numDLSPrograms; i++, p++)
2650    {
2651        if (p->locale == locale)
2652        {
2653            *pRegionIndex = p->regionIndex;
2654            return EAS_SUCCESS;
2655        }
2656    }
2657
2658    return EAS_FAILURE;
2659}
2660#endif
2661
2662/*----------------------------------------------------------------------------
2663 * VMProgramChange()
2664 *----------------------------------------------------------------------------
2665 * Purpose:
2666 * Change the instrument (program) for the given channel.
2667 *
2668 * Depending on the program number, and the bank selected for this channel, the
2669 * program may be in ROM, RAM (from SMAF or CMX related RAM wavetable), or
2670 * Alternate wavetable (from mobile DLS or other DLS file)
2671 *
2672 * This function figures out what wavetable should be used, and sets it up as the
2673 * wavetable to use for this channel. Also the channel may switch from a melodic
2674 * channel to a rhythm channel, or vice versa.
2675 *
2676 * Inputs:
2677 *
2678 * Outputs:
2679 * Side Effects:
2680 * gsSynthObject.m_sChannel[nChannel].m_nProgramNumber is likely changed
2681 * gsSynthObject.m_sChannel[nChannel].m_psEAS may be changed
2682 * gsSynthObject.m_sChannel[nChannel].m_bRhythmChannel may be changed
2683 *
2684 *----------------------------------------------------------------------------
2685*/
2686/*lint -esym(715, pVoiceMgr) reserved for future use */
2687void VMProgramChange (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 program)
2688{
2689    S_SYNTH_CHANNEL *pChannel;
2690    EAS_U32 bank;
2691    EAS_U16 regionIndex;
2692
2693#ifdef _DEBUG_VM
2694    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "VMProgramChange: vSynthNum=%d, channel=%d, program=%d\n", pSynth->vSynthNum, channel, program); */ }
2695#endif
2696
2697    /* setup pointer to MIDI channel data */
2698    pChannel = &pSynth->channels[channel];
2699    bank = pChannel->bankNum;
2700
2701    /* allow channels to switch between being melodic or rhythm channels, using GM2 CC values */
2702    if ((bank & 0xFF00) == DEFAULT_RHYTHM_BANK_NUMBER)
2703    {
2704        /* make it a rhythm channel */
2705        pChannel->channelFlags |= CHANNEL_FLAG_RHYTHM_CHANNEL;
2706    }
2707    else if ((bank & 0xFF00) == DEFAULT_MELODY_BANK_NUMBER)
2708    {
2709        /* make it a melody channel */
2710        pChannel->channelFlags &= ~CHANNEL_FLAG_RHYTHM_CHANNEL;
2711    }
2712
2713    regionIndex = DEFAULT_REGION_INDEX;
2714
2715#ifdef EXTERNAL_AUDIO
2716    /* give the external audio interface a chance to handle it */
2717    if (pSynth->cbProgChgFunc != NULL)
2718    {
2719        S_EXT_AUDIO_PRG_CHG prgChg;
2720        prgChg.channel = channel;
2721        prgChg.bank = (EAS_U16) bank;
2722        prgChg.program = program;
2723        if (pSynth->cbProgChgFunc(pSynth->pExtAudioInstData, &prgChg))
2724            pChannel->channelFlags |= CHANNEL_FLAG_EXTERNAL_AUDIO;
2725    }
2726
2727#endif
2728
2729
2730#ifdef DLS_SYNTHESIZER
2731    /* first check for DLS program that may overlay the internal instrument */
2732    if (VMFindDLSProgram(pSynth->pDLS, bank, program, &regionIndex) != EAS_SUCCESS)
2733#endif
2734
2735    /* braces to support 'if' clause above */
2736    {
2737
2738        /* look in the internal banks */
2739        if (VMFindProgram(pSynth->pEAS, bank, program, &regionIndex) != EAS_SUCCESS)
2740
2741        /* fall back to default bank */
2742        {
2743            if (pSynth->channels[channel].channelFlags & CHANNEL_FLAG_RHYTHM_CHANNEL)
2744                bank = DEFAULT_RHYTHM_BANK_NUMBER;
2745            else
2746                bank = DEFAULT_MELODY_BANK_NUMBER;
2747
2748            if (VMFindProgram(pSynth->pEAS, bank, program, &regionIndex) != EAS_SUCCESS)
2749
2750            /* switch to program 0 in the default bank */
2751            {
2752                if (VMFindProgram(pSynth->pEAS, bank, 0, &regionIndex) != EAS_SUCCESS)
2753                    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "VMProgramChange: No program @ %03d:%03d:%03d\n",
2754                        (bank >> 8) & 0x7f, bank & 0x7f, program); */ }
2755            }
2756        }
2757    }
2758
2759    /* we have our new program change for this channel */
2760    pChannel->programNum = program;
2761    pChannel->regionIndex = regionIndex;
2762
2763    /*
2764    set a channel flag to request parameter updates
2765    for all the voices associated with this channel
2766    */
2767    pChannel->channelFlags |= CHANNEL_FLAG_UPDATE_CHANNEL_PARAMETERS;
2768
2769    return;
2770}
2771
2772/*----------------------------------------------------------------------------
2773 * VMAddSamples()
2774 *----------------------------------------------------------------------------
2775 * Purpose:
2776 * Synthesize the requested number of samples (block based processing)
2777 *
2778 * Inputs:
2779 * nNumSamplesToAdd - number of samples to write to buffer
2780 * psEASData - pointer to overall EAS data structure
2781 *
2782 * Outputs:
2783 * number of voices rendered
2784 *
2785 * Side Effects:
2786 * - samples are added to the presently free buffer
2787 *
2788 *----------------------------------------------------------------------------
2789*/
2790EAS_I32 VMAddSamples (S_VOICE_MGR *pVoiceMgr, EAS_I32 *pMixBuffer, EAS_I32 numSamples)
2791{
2792    S_SYNTH *pSynth;
2793    EAS_INT voicesRendered;
2794    EAS_INT voiceNum;
2795    EAS_BOOL done;
2796
2797#ifdef  _REVERB
2798    EAS_PCM *pReverbSendBuffer;
2799#endif  // ifdef    _REVERB
2800
2801#ifdef  _CHORUS
2802    EAS_PCM *pChorusSendBuffer;
2803#endif  // ifdef    _CHORUS
2804
2805    voicesRendered = 0;
2806    for (voiceNum = 0; voiceNum < MAX_SYNTH_VOICES; voiceNum++)
2807    {
2808
2809        /* retarget stolen voices */
2810        if ((pVoiceMgr->voices[voiceNum].voiceState == eVoiceStateStolen) && (pVoiceMgr->voices[voiceNum].gain <= 0))
2811            VMRetargetStolenVoice(pVoiceMgr, voiceNum);
2812
2813        /* get pointer to virtual synth */
2814        pSynth = pVoiceMgr->pSynth[pVoiceMgr->voices[voiceNum].channel >> 4];
2815
2816        /* synthesize active voices */
2817        if (pVoiceMgr->voices[voiceNum].voiceState != eVoiceStateFree)
2818        {
2819            done = GetSynthPtr(voiceNum)->pfUpdateVoice(pVoiceMgr, pSynth, &pVoiceMgr->voices[voiceNum], GetAdjustedVoiceNum(voiceNum), pMixBuffer, numSamples);
2820            voicesRendered++;
2821
2822            /* voice is finished */
2823            if (done == EAS_TRUE)
2824            {
2825                /* set gain of stolen voice to zero so it will be restarted */
2826                if (pVoiceMgr->voices[voiceNum].voiceState == eVoiceStateStolen)
2827                    pVoiceMgr->voices[voiceNum].gain = 0;
2828
2829                /* or return it to the free voice pool */
2830                else
2831                    VMFreeVoice(pVoiceMgr, pSynth, &pVoiceMgr->voices[voiceNum]);
2832            }
2833
2834            /* if this voice is scheduled to be muted, set the mute flag */
2835            if (pVoiceMgr->voices[voiceNum].voiceFlags & VOICE_FLAG_DEFER_MUTE)
2836            {
2837                pVoiceMgr->voices[voiceNum].voiceFlags &= ~(VOICE_FLAG_DEFER_MUTE | VOICE_FLAG_DEFER_MIDI_NOTE_OFF);
2838                VMMuteVoice(pVoiceMgr, voiceNum);
2839            }
2840
2841            /* if voice just started, advance state to play */
2842            if (pVoiceMgr->voices[voiceNum].voiceState == eVoiceStateStart)
2843                pVoiceMgr->voices[voiceNum].voiceState = eVoiceStatePlay;
2844        }
2845    }
2846
2847    return voicesRendered;
2848}
2849
2850/*----------------------------------------------------------------------------
2851 * VMRender()
2852 *----------------------------------------------------------------------------
2853 * Purpose:
2854 * This routine renders a frame of audio
2855 *
2856 * Inputs:
2857 * psEASData        - pointer to overall EAS data structure
2858 *
2859 * Outputs:
2860 * pVoicesRendered  - number of voices rendered this frame
2861 *
2862 * Side Effects:
2863 *
2864 *----------------------------------------------------------------------------
2865*/
2866EAS_RESULT VMRender (S_VOICE_MGR *pVoiceMgr, EAS_I32 numSamples, EAS_I32 *pMixBuffer, EAS_I32 *pVoicesRendered)
2867{
2868    S_SYNTH *pSynth;
2869    EAS_INT i;
2870    EAS_INT channel;
2871
2872#ifdef _CHECKED_BUILD
2873    SanityCheck(pVoiceMgr);
2874#endif
2875
2876    /* update MIDI channel parameters */
2877    *pVoicesRendered = 0;
2878    for (i = 0; i < MAX_VIRTUAL_SYNTHESIZERS; i++)
2879    {
2880        if (pVoiceMgr->pSynth[i] != NULL)
2881            VMUpdateStaticChannelParameters(pVoiceMgr, pVoiceMgr->pSynth[i]);
2882    }
2883
2884    /* synthesize a buffer of audio */
2885    *pVoicesRendered = VMAddSamples(pVoiceMgr, pMixBuffer, numSamples);
2886
2887    /*
2888     * check for deferred note-off messages
2889     * If flag is set, that means one or more voices are expecting deferred
2890     * midi note-off messages because the midi note-on and corresponding midi
2891     * note-off requests occurred during the same update interval. The goal
2892     * is the defer the note-off request so that the note can at least start.
2893    */
2894    for (i = 0; i < MAX_VIRTUAL_SYNTHESIZERS; i++)
2895    {
2896        pSynth = pVoiceMgr->pSynth[i];
2897
2898        if (pSynth== NULL)
2899            continue;
2900
2901        if (pSynth->synthFlags & SYNTH_FLAG_DEFERRED_MIDI_NOTE_OFF_PENDING)
2902            VMDeferredStopNote(pVoiceMgr, pSynth);
2903
2904        /* check if we need to reset the synth */
2905        if ((pSynth->synthFlags & SYNTH_FLAG_RESET_IS_REQUESTED) &&
2906            (pSynth->numActiveVoices == 0))
2907        {
2908            /*
2909            complete the process of resetting the synth now that
2910            all voices have muted
2911            */
2912#ifdef _DEBUG_VM
2913            { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMAddSamples: complete the reset process\n"); */ }
2914#endif
2915
2916            VMInitializeAllChannels(pVoiceMgr, pSynth);
2917            VMInitializeAllVoices(pVoiceMgr, pSynth->vSynthNum);
2918
2919            /* clear the reset flag */
2920            pSynth->synthFlags &= ~SYNTH_FLAG_RESET_IS_REQUESTED;
2921        }
2922
2923        /* clear channel update flags */
2924        for (channel = 0; channel < NUM_SYNTH_CHANNELS; channel++)
2925            pSynth->channels[channel].channelFlags &= ~CHANNEL_FLAG_UPDATE_CHANNEL_PARAMETERS;
2926
2927        }
2928
2929#ifdef _CHECKED_BUILD
2930    SanityCheck(pVoiceMgr);
2931#endif
2932
2933    return EAS_SUCCESS;
2934}
2935
2936/*----------------------------------------------------------------------------
2937 * VMInitWorkload()
2938 *----------------------------------------------------------------------------
2939 * Purpose:
2940 * Clears the workload counter
2941 *
2942 * Inputs:
2943 * pVoiceMgr            - pointer to instance data
2944 *
2945 * Outputs:
2946 *
2947 * Side Effects:
2948 *
2949 *----------------------------------------------------------------------------
2950*/
2951void VMInitWorkload (S_VOICE_MGR *pVoiceMgr)
2952{
2953    pVoiceMgr->workload = 0;
2954}
2955
2956/*----------------------------------------------------------------------------
2957 * VMSetWorkload()
2958 *----------------------------------------------------------------------------
2959 * Purpose:
2960 * Sets the max workload for a single frame.
2961 *
2962 * Inputs:
2963 * pVoiceMgr            - pointer to instance data
2964 *
2965 * Outputs:
2966 *
2967 * Side Effects:
2968 *
2969 *----------------------------------------------------------------------------
2970*/
2971void VMSetWorkload (S_VOICE_MGR *pVoiceMgr, EAS_I32 maxWorkLoad)
2972{
2973    pVoiceMgr->maxWorkLoad = maxWorkLoad;
2974}
2975
2976/*----------------------------------------------------------------------------
2977 * VMCheckWorkload()
2978 *----------------------------------------------------------------------------
2979 * Purpose:
2980 * Checks to see if work load has been exceeded on this frame.
2981 *
2982 * Inputs:
2983 * pVoiceMgr            - pointer to instance data
2984 *
2985 * Outputs:
2986 *
2987 * Side Effects:
2988 *
2989 *----------------------------------------------------------------------------
2990*/
2991EAS_BOOL VMCheckWorkload (S_VOICE_MGR *pVoiceMgr)
2992{
2993    if (pVoiceMgr->maxWorkLoad > 0)
2994        return (EAS_BOOL) (pVoiceMgr->workload >= pVoiceMgr->maxWorkLoad);
2995    return EAS_FALSE;
2996}
2997
2998/*----------------------------------------------------------------------------
2999 * VMActiveVoices()
3000 *----------------------------------------------------------------------------
3001 * Purpose:
3002 * Returns the number of active voices in the synthesizer.
3003 *
3004 * Inputs:
3005 * pEASData         - pointer to instance data
3006 *
3007 * Outputs:
3008 * Returns the number of active voices
3009 *
3010 * Side Effects:
3011 *
3012 *----------------------------------------------------------------------------
3013*/
3014EAS_I32 VMActiveVoices (S_SYNTH *pSynth)
3015{
3016    return pSynth->numActiveVoices;
3017}
3018
3019/*----------------------------------------------------------------------------
3020 * VMSetSynthPolyphony()
3021 *----------------------------------------------------------------------------
3022 * Purpose:
3023 * Set the synth to a new polyphony value. Value must be >= 1 and
3024 * <= MAX_SYNTH_VOICES. This function will pin the polyphony at those limits
3025 *
3026 * Inputs:
3027 * pVoiceMgr        pointer to synthesizer data
3028 * polyphonyCount   desired polyphony count
3029 * synth            synthesizer number (0 = onboard, 1 = DSP)
3030 *
3031 * Outputs:
3032 * Returns error code
3033 *
3034 * Side Effects:
3035 *
3036 *----------------------------------------------------------------------------
3037*/
3038EAS_RESULT VMSetSynthPolyphony (S_VOICE_MGR *pVoiceMgr, EAS_I32 synth, EAS_I32 polyphonyCount)
3039{
3040    EAS_INT i;
3041    EAS_INT activeVoices;
3042
3043    /* lower limit */
3044    if (polyphonyCount < 1)
3045        polyphonyCount = 1;
3046
3047    /* split architecture */
3048#if defined(_SECONDARY_SYNTH) || defined(EAS_SPLIT_WT_SYNTH)
3049    if (synth == EAS_MCU_SYNTH)
3050    {
3051        if (polyphonyCount > NUM_PRIMARY_VOICES)
3052            polyphonyCount = NUM_PRIMARY_VOICES;
3053        if (pVoiceMgr->maxPolyphonyPrimary == polyphonyCount)
3054            return EAS_SUCCESS;
3055        pVoiceMgr->maxPolyphonyPrimary = (EAS_U16) polyphonyCount;
3056    }
3057    else if (synth == EAS_DSP_SYNTH)
3058    {
3059        if (polyphonyCount > NUM_SECONDARY_VOICES)
3060            polyphonyCount = NUM_SECONDARY_VOICES;
3061        if (pVoiceMgr->maxPolyphonySecondary == polyphonyCount)
3062            return EAS_SUCCESS;
3063        pVoiceMgr->maxPolyphonySecondary = (EAS_U16) polyphonyCount;
3064    }
3065    else
3066        return EAS_ERROR_PARAMETER_RANGE;
3067
3068    /* setting for SP-MIDI */
3069    pVoiceMgr->maxPolyphony = pVoiceMgr->maxPolyphonyPrimary + pVoiceMgr->maxPolyphonySecondary;
3070
3071    /* standard architecture */
3072#else
3073    if (synth != EAS_MCU_SYNTH)
3074        return EAS_ERROR_PARAMETER_RANGE;
3075
3076    /* pin desired value to possible limits */
3077    if (polyphonyCount > MAX_SYNTH_VOICES)
3078        polyphonyCount = MAX_SYNTH_VOICES;
3079
3080    /* set polyphony, if value is different than current value */
3081    if (pVoiceMgr->maxPolyphony == polyphonyCount)
3082        return EAS_SUCCESS;
3083
3084    pVoiceMgr->maxPolyphony = (EAS_U16) polyphonyCount;
3085#endif
3086
3087    /* if SPMIDI enabled, update channel masking based on new polyphony */
3088    for (i = 0; i < MAX_VIRTUAL_SYNTHESIZERS; i++)
3089    {
3090        if (pVoiceMgr->pSynth[i])
3091        {
3092            if (pVoiceMgr->pSynth[i]->synthFlags & SYNTH_FLAG_SP_MIDI_ON)
3093                VMMIPUpdateChannelMuting(pVoiceMgr, pVoiceMgr->pSynth[i]);
3094            else
3095                pVoiceMgr->pSynth[i]->poolAlloc[0] = (EAS_U8) polyphonyCount;
3096        }
3097    }
3098
3099    /* are we under polyphony limit? */
3100    if (pVoiceMgr->activeVoices <= polyphonyCount)
3101        return EAS_SUCCESS;
3102
3103    /* count the number of active voices */
3104    activeVoices = 0;
3105    for (i = 0; i < MAX_SYNTH_VOICES; i++)
3106    {
3107
3108        /* is voice active? */
3109        if ((pVoiceMgr->voices[i].voiceState != eVoiceStateFree) && (pVoiceMgr->voices[i].voiceState != eVoiceStateMuting))
3110            activeVoices++;
3111    }
3112
3113    /* we may have to mute voices to reach new target */
3114    while (activeVoices > polyphonyCount)
3115    {
3116        S_SYNTH *pSynth;
3117        S_SYNTH_VOICE *pVoice;
3118        EAS_I32 currentPriority, bestPriority;
3119        EAS_INT bestCandidate;
3120
3121        /* find the lowest priority voice */
3122        bestPriority = bestCandidate = -1;
3123        for (i = 0; i < MAX_SYNTH_VOICES; i++)
3124        {
3125
3126            pVoice = &pVoiceMgr->voices[i];
3127
3128            /* ignore free and muting voices */
3129            if ((pVoice->voiceState == eVoiceStateFree) || (pVoice->voiceState == eVoiceStateMuting))
3130                continue;
3131
3132            pSynth = pVoiceMgr->pSynth[GET_VSYNTH(pVoice->channel)];
3133
3134            /* if voice is stolen or just started, reduce the likelihood it will be stolen */
3135            if (( pVoice->voiceState == eVoiceStateStolen) || (pVoice->voiceFlags & VOICE_FLAG_NO_SAMPLES_SYNTHESIZED_YET))
3136            {
3137                /* include velocity */
3138                currentPriority = 128 - pVoice->nextVelocity;
3139
3140                /* include channel priority */
3141                currentPriority += pSynth->channels[GET_CHANNEL(pVoice->nextChannel)].pool << CHANNEL_PRIORITY_STEAL_WEIGHT;
3142            }
3143            else
3144            {
3145                /* include age */
3146                currentPriority = (EAS_I32) pVoice->age << NOTE_AGE_STEAL_WEIGHT;
3147
3148                /* include note gain -higher gain is lower steal value */
3149                /*lint -e{704} use shift for performance */
3150                currentPriority += ((32768 >> (12 - NOTE_GAIN_STEAL_WEIGHT)) + 256) -
3151                    ((EAS_I32) pVoice->gain >> (12 - NOTE_GAIN_STEAL_WEIGHT));
3152
3153                /* include channel priority */
3154                currentPriority += pSynth->channels[GET_CHANNEL(pVoice->channel)].pool << CHANNEL_PRIORITY_STEAL_WEIGHT;
3155            }
3156
3157            /* include synth priority */
3158            currentPriority += pSynth->priority << SYNTH_PRIORITY_WEIGHT;
3159
3160            /* is this the best choice so far? */
3161            if (currentPriority > bestPriority)
3162            {
3163                bestPriority = currentPriority;
3164                bestCandidate = i;
3165            }
3166        }
3167
3168        /* shutdown best candidate */
3169        if (bestCandidate < 0)
3170        {
3171            { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "VMSetPolyphony: Unable to reduce polyphony\n"); */ }
3172            break;
3173        }
3174
3175        /* shut down this voice */
3176        /*lint -e{771} pSynth is initialized if bestCandidate >= 0 */
3177        VMMuteVoice(pVoiceMgr, bestCandidate);
3178        activeVoices--;
3179    }
3180
3181    return EAS_SUCCESS;
3182}
3183
3184/*----------------------------------------------------------------------------
3185 * VMGetSynthPolyphony()
3186 *----------------------------------------------------------------------------
3187 * Purpose:
3188 * Returns the current polyphony setting
3189 *
3190 * Inputs:
3191 * pVoiceMgr        pointer to synthesizer data
3192 * synth            synthesizer number (0 = onboard, 1 = DSP)
3193 *
3194 * Outputs:
3195 * Returns actual polyphony value set, as pinned by limits
3196 *
3197 * Side Effects:
3198 *
3199 *----------------------------------------------------------------------------
3200*/
3201EAS_RESULT VMGetSynthPolyphony (S_VOICE_MGR *pVoiceMgr, EAS_I32 synth, EAS_I32 *pPolyphonyCount)
3202{
3203
3204#if defined(_SECONDARY_SYNTH) || defined(EAS_SPLIT_WT_SYNTH)
3205    if (synth == EAS_MCU_SYNTH)
3206        *pPolyphonyCount = pVoiceMgr->maxPolyphonyPrimary;
3207    else if (synth == EAS_DSP_SYNTH)
3208        *pPolyphonyCount = pVoiceMgr->maxPolyphonySecondary;
3209    else
3210        return EAS_ERROR_PARAMETER_RANGE;
3211#else
3212    if (synth != EAS_MCU_SYNTH)
3213        return EAS_ERROR_PARAMETER_RANGE;
3214    *pPolyphonyCount = pVoiceMgr->maxPolyphony;
3215#endif
3216    return EAS_SUCCESS;
3217}
3218
3219/*----------------------------------------------------------------------------
3220 * VMSetPolyphony()
3221 *----------------------------------------------------------------------------
3222 * Purpose:
3223 * Set the virtual synth polyphony. 0 = no limit (i.e. can use
3224 * all available voices).
3225 *
3226 * Inputs:
3227 * pVoiceMgr        pointer to synthesizer data
3228 * polyphonyCount   desired polyphony count
3229 * pSynth           pointer to virtual synth
3230 *
3231 * Outputs:
3232 * Returns error code
3233 *
3234 * Side Effects:
3235 *
3236 *----------------------------------------------------------------------------
3237*/
3238EAS_RESULT VMSetPolyphony (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_I32 polyphonyCount)
3239{
3240    EAS_INT i;
3241    EAS_INT activeVoices;
3242
3243    /* check limits */
3244    if (polyphonyCount < 0)
3245        return EAS_ERROR_PARAMETER_RANGE;
3246
3247    /* zero is max polyphony */
3248    if ((polyphonyCount == 0) || (polyphonyCount > MAX_SYNTH_VOICES))
3249    {
3250        pSynth->maxPolyphony = 0;
3251        return EAS_SUCCESS;
3252    }
3253
3254    /* set new polyphony */
3255    pSynth->maxPolyphony = (EAS_U16) polyphonyCount;
3256
3257    /* max polyphony is minimum of virtual synth and actual synth */
3258    if (polyphonyCount > pVoiceMgr->maxPolyphony)
3259        polyphonyCount = pVoiceMgr->maxPolyphony;
3260
3261    /* if SP-MIDI mode, update the channel muting */
3262    if (pSynth->synthFlags & SYNTH_FLAG_SP_MIDI_ON)
3263        VMMIPUpdateChannelMuting(pVoiceMgr, pSynth);
3264    else
3265        pSynth->poolAlloc[0] = (EAS_U8) polyphonyCount;
3266
3267    /* are we under polyphony limit? */
3268    if (pSynth->numActiveVoices <= polyphonyCount)
3269        return EAS_SUCCESS;
3270
3271    /* count the number of active voices */
3272    activeVoices = 0;
3273    for (i = 0; i < MAX_SYNTH_VOICES; i++)
3274    {
3275        /* this synth? */
3276        if (GET_VSYNTH(pVoiceMgr->voices[i].nextChannel) != pSynth->vSynthNum)
3277            continue;
3278
3279        /* is voice active? */
3280        if ((pVoiceMgr->voices[i].voiceState != eVoiceStateFree) && (pVoiceMgr->voices[i].voiceState != eVoiceStateMuting))
3281            activeVoices++;
3282    }
3283
3284    /* we may have to mute voices to reach new target */
3285    while (activeVoices > polyphonyCount)
3286    {
3287        S_SYNTH_VOICE *pVoice;
3288        EAS_I32 currentPriority, bestPriority;
3289        EAS_INT bestCandidate;
3290
3291        /* find the lowest priority voice */
3292        bestPriority = bestCandidate = -1;
3293        for (i = 0; i < MAX_SYNTH_VOICES; i++)
3294        {
3295            pVoice = &pVoiceMgr->voices[i];
3296
3297            /* this synth? */
3298            if (GET_VSYNTH(pVoice->nextChannel) != pSynth->vSynthNum)
3299                continue;
3300
3301            /* if voice is stolen or just started, reduce the likelihood it will be stolen */
3302            if (( pVoice->voiceState == eVoiceStateStolen) || (pVoice->voiceFlags & VOICE_FLAG_NO_SAMPLES_SYNTHESIZED_YET))
3303            {
3304                /* include velocity */
3305                currentPriority = 128 - pVoice->nextVelocity;
3306
3307                /* include channel priority */
3308                currentPriority += pSynth->channels[GET_CHANNEL(pVoice->nextChannel)].pool << CHANNEL_PRIORITY_STEAL_WEIGHT;
3309            }
3310            else
3311            {
3312                /* include age */
3313                currentPriority = (EAS_I32) pVoice->age << NOTE_AGE_STEAL_WEIGHT;
3314
3315                /* include note gain -higher gain is lower steal value */
3316                /*lint -e{704} use shift for performance */
3317                currentPriority += ((32768 >> (12 - NOTE_GAIN_STEAL_WEIGHT)) + 256) -
3318                    ((EAS_I32) pVoice->gain >> (12 - NOTE_GAIN_STEAL_WEIGHT));
3319
3320                /* include channel priority */
3321                currentPriority += pSynth->channels[GET_CHANNEL(pVoice->nextChannel)].pool << CHANNEL_PRIORITY_STEAL_WEIGHT;
3322            }
3323
3324            /* is this the best choice so far? */
3325            if (currentPriority > bestPriority)
3326            {
3327                bestPriority = currentPriority;
3328                bestCandidate = i;
3329            }
3330        }
3331
3332        /* shutdown best candidate */
3333        if (bestCandidate < 0)
3334        {
3335            { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "VMSetPolyphony: Unable to reduce polyphony\n"); */ }
3336            break;
3337        }
3338
3339        /* shut down this voice */
3340        VMMuteVoice(pVoiceMgr, bestCandidate);
3341        activeVoices--;
3342    }
3343
3344    return EAS_SUCCESS;
3345}
3346
3347/*----------------------------------------------------------------------------
3348 * VMGetPolyphony()
3349 *----------------------------------------------------------------------------
3350 * Purpose:
3351 * Get the virtual synth polyphony
3352 *
3353 * Inputs:
3354 * pVoiceMgr        pointer to synthesizer data
3355 * pPolyphonyCount  pointer to variable to hold polyphony count
3356 * pSynth           pointer to virtual synth
3357 *
3358 * Outputs:
3359 * Returns error code
3360 *
3361 * Side Effects:
3362 *
3363 *----------------------------------------------------------------------------
3364*/
3365/*lint -esym(715, pVoiceMgr) reserved for future use */
3366EAS_RESULT VMGetPolyphony (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_I32 *pPolyphonyCount)
3367{
3368    *pPolyphonyCount = (EAS_U16) pSynth->maxPolyphony;
3369    return EAS_SUCCESS;
3370}
3371
3372/*----------------------------------------------------------------------------
3373 * VMSetPriority()
3374 *----------------------------------------------------------------------------
3375 * Purpose:
3376 * Set the virtual synth priority
3377 *
3378 * Inputs:
3379 * pVoiceMgr        pointer to synthesizer data
3380 * priority         new priority
3381 * pSynth           pointer to virtual synth
3382 *
3383 * Outputs:
3384 * Returns error code
3385 *
3386 * Side Effects:
3387 *
3388 *----------------------------------------------------------------------------
3389*/
3390/*lint -esym(715, pVoiceMgr) reserved for future use */
3391EAS_RESULT VMSetPriority (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_I32 priority)
3392{
3393    pSynth->priority = (EAS_U8) priority ;
3394    return EAS_SUCCESS;
3395}
3396
3397/*----------------------------------------------------------------------------
3398 * VMGetPriority()
3399 *----------------------------------------------------------------------------
3400 * Purpose:
3401 * Get the virtual synth priority
3402 *
3403 * Inputs:
3404 * pVoiceMgr        pointer to synthesizer data
3405 * pPriority        pointer to variable to hold priority
3406 * pSynth           pointer to virtual synth
3407 *
3408 * Outputs:
3409 * Returns error code
3410 *
3411 * Side Effects:
3412 *
3413 *----------------------------------------------------------------------------
3414*/
3415/*lint -esym(715, pVoiceMgr) reserved for future use */
3416EAS_RESULT VMGetPriority (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_I32 *pPriority)
3417{
3418    *pPriority = pSynth->priority;
3419    return EAS_SUCCESS;
3420}
3421
3422/*----------------------------------------------------------------------------
3423 * VMSetVolume()
3424 *----------------------------------------------------------------------------
3425 * Purpose:
3426 * Set the master volume for this synthesizer for this sequence.
3427 *
3428 * Inputs:
3429 * nSynthVolume - the desired master volume
3430 * psEASData - pointer to overall EAS data structure
3431 *
3432 * Outputs:
3433 *
3434 *
3435 * Side Effects:
3436 * overrides any previously set master volume from sysex
3437 *
3438 *----------------------------------------------------------------------------
3439*/
3440void VMSetVolume (S_SYNTH *pSynth, EAS_U16 masterVolume)
3441{
3442    pSynth->masterVolume = masterVolume;
3443    pSynth->synthFlags |= SYNTH_FLAG_UPDATE_ALL_CHANNEL_PARAMETERS;
3444}
3445
3446/*----------------------------------------------------------------------------
3447 * VMSetPitchBendRange()
3448 *----------------------------------------------------------------------------
3449 * Set the pitch bend range for the given channel.
3450 *----------------------------------------------------------------------------
3451*/
3452void VMSetPitchBendRange (S_SYNTH *pSynth, EAS_INT channel, EAS_I16 pitchBendRange)
3453{
3454    pSynth->channels[channel].pitchBendSensitivity = pitchBendRange;
3455}
3456
3457/*----------------------------------------------------------------------------
3458 * VMValidateEASLib()
3459 *----------------------------------------------------------------------------
3460 * Validates an EAS library
3461 *----------------------------------------------------------------------------
3462*/
3463EAS_RESULT VMValidateEASLib (EAS_SNDLIB_HANDLE pEAS)
3464{
3465    /* validate the sound library */
3466    if (pEAS)
3467    {
3468        if (pEAS->identifier != _EAS_LIBRARY_VERSION)
3469        {
3470            { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMValidateEASLib: Sound library mismatch in sound library: Read 0x%08x, expected 0x%08x\n",
3471                pEAS->identifier, _EAS_LIBRARY_VERSION); */ }
3472            return EAS_ERROR_SOUND_LIBRARY;
3473        }
3474
3475        /* check sample rate */
3476        if ((pEAS->libAttr & LIBFORMAT_SAMPLE_RATE_MASK) != _OUTPUT_SAMPLE_RATE)
3477        {
3478            { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMValidateEASLib: Sample rate mismatch in sound library: Read %lu, expected %lu\n",
3479                pEAS->libAttr & LIBFORMAT_SAMPLE_RATE_MASK, _OUTPUT_SAMPLE_RATE); */ }
3480            return EAS_ERROR_SOUND_LIBRARY;
3481        }
3482
3483#ifdef _WT_SYNTH
3484        /* check sample bit depth */
3485#ifdef _8_BIT_SAMPLES
3486        if (pEAS->libAttr & LIB_FORMAT_16_BIT_SAMPLES)
3487        {
3488            { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMValidateEASLib: Expected 8-bit samples and found 16-bit\n",
3489                pEAS->libAttr & LIBFORMAT_SAMPLE_RATE_MASK, _OUTPUT_SAMPLE_RATE); */ }
3490            return EAS_ERROR_SOUND_LIBRARY;
3491        }
3492#endif
3493#ifdef _16_BIT_SAMPLES
3494        if ((pEAS->libAttr & LIB_FORMAT_16_BIT_SAMPLES) == 0)
3495        {
3496            { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMValidateEASLib: Expected 16-bit samples and found 8-bit\n",
3497                pEAS->libAttr & LIBFORMAT_SAMPLE_RATE_MASK, _OUTPUT_SAMPLE_RATE); */ }
3498            return EAS_ERROR_SOUND_LIBRARY;
3499        }
3500#endif
3501#endif
3502    }
3503
3504    return EAS_SUCCESS;
3505}
3506
3507/*----------------------------------------------------------------------------
3508 * VMSetGlobalEASLib()
3509 *----------------------------------------------------------------------------
3510 * Purpose:
3511 * Sets the EAS library to be used by the synthesizer
3512 *
3513 * Inputs:
3514 * psEASData - pointer to overall EAS data structure
3515 *
3516 * Outputs:
3517 *
3518 *
3519 * Side Effects:
3520 *
3521 *----------------------------------------------------------------------------
3522*/
3523EAS_RESULT VMSetGlobalEASLib (S_VOICE_MGR *pVoiceMgr, EAS_SNDLIB_HANDLE pEAS)
3524{
3525    EAS_RESULT result;
3526
3527    result = VMValidateEASLib(pEAS);
3528    if (result != EAS_SUCCESS)
3529        return result;
3530
3531    pVoiceMgr->pGlobalEAS = pEAS;
3532    return EAS_SUCCESS;
3533}
3534
3535/*----------------------------------------------------------------------------
3536 * VMSetEASLib()
3537 *----------------------------------------------------------------------------
3538 * Purpose:
3539 * Sets the EAS library to be used by the synthesizer
3540 *
3541 * Inputs:
3542 * psEASData - pointer to overall EAS data structure
3543 *
3544 * Outputs:
3545 *
3546 *
3547 * Side Effects:
3548 *
3549 *----------------------------------------------------------------------------
3550*/
3551EAS_RESULT VMSetEASLib (S_SYNTH *pSynth, EAS_SNDLIB_HANDLE pEAS)
3552{
3553    EAS_RESULT result;
3554
3555    result = VMValidateEASLib(pEAS);
3556    if (result != EAS_SUCCESS)
3557        return result;
3558
3559    pSynth->pEAS = pEAS;
3560    return EAS_SUCCESS;
3561}
3562
3563#ifdef DLS_SYNTHESIZER
3564/*----------------------------------------------------------------------------
3565 * VMSetGlobalDLSLib()
3566 *----------------------------------------------------------------------------
3567 * Purpose:
3568 * Sets the DLS library to be used by the synthesizer
3569 *
3570 * Inputs:
3571 * psEASData - pointer to overall EAS data structure
3572 *
3573 * Outputs:
3574 *
3575 *
3576 * Side Effects:
3577 *
3578 *----------------------------------------------------------------------------
3579*/
3580EAS_RESULT VMSetGlobalDLSLib (EAS_DATA_HANDLE pEASData, EAS_DLSLIB_HANDLE pDLS)
3581{
3582
3583    if (pEASData->pVoiceMgr->pGlobalDLS)
3584        DLSCleanup(pEASData->hwInstData, pEASData->pVoiceMgr->pGlobalDLS);
3585
3586    pEASData->pVoiceMgr->pGlobalDLS = pDLS;
3587    return EAS_SUCCESS;
3588}
3589
3590/*----------------------------------------------------------------------------
3591 * VMSetDLSLib()
3592 *----------------------------------------------------------------------------
3593 * Purpose:
3594 * Sets the DLS library to be used by the synthesizer
3595 *
3596 * Inputs:
3597 * psEASData - pointer to overall EAS data structure
3598 *
3599 * Outputs:
3600 *
3601 *
3602 * Side Effects:
3603 *
3604 *----------------------------------------------------------------------------
3605*/
3606EAS_RESULT VMSetDLSLib (S_SYNTH *pSynth, EAS_DLSLIB_HANDLE pDLS)
3607{
3608    pSynth->pDLS = pDLS;
3609    return EAS_SUCCESS;
3610}
3611#endif
3612
3613/*----------------------------------------------------------------------------
3614 * VMSetTranposition()
3615 *----------------------------------------------------------------------------
3616 * Purpose:
3617 * Sets the global key transposition used by the synthesizer.
3618 * Transposes all melodic instruments up or down by the specified
3619 * amount. Range is limited to +/-12 semitones.
3620 *
3621 * Inputs:
3622 * psEASData - pointer to overall EAS data structure
3623 *
3624 * Outputs:
3625 *
3626 *
3627 * Side Effects:
3628 *
3629 *----------------------------------------------------------------------------
3630*/
3631void VMSetTranposition (S_SYNTH *pSynth, EAS_I32 transposition)
3632{
3633    pSynth->globalTranspose = (EAS_I8) transposition;
3634}
3635
3636/*----------------------------------------------------------------------------
3637 * VMGetTranposition()
3638 *----------------------------------------------------------------------------
3639 * Purpose:
3640 * Gets the global key transposition used by the synthesizer.
3641 * Transposes all melodic instruments up or down by the specified
3642 * amount. Range is limited to +/-12 semitones.
3643 *
3644 * Inputs:
3645 * psEASData - pointer to overall EAS data structure
3646 *
3647 * Outputs:
3648 *
3649 *
3650 * Side Effects:
3651 *
3652 *----------------------------------------------------------------------------
3653*/
3654void VMGetTranposition (S_SYNTH *pSynth, EAS_I32 *pTransposition)
3655{
3656    *pTransposition = pSynth->globalTranspose;
3657}
3658
3659/*----------------------------------------------------------------------------
3660 * VMGetNoteCount()
3661 *----------------------------------------------------------------------------
3662* Returns the total note count
3663*----------------------------------------------------------------------------
3664*/
3665EAS_I32 VMGetNoteCount (S_SYNTH *pSynth)
3666{
3667    return pSynth->totalNoteCount;
3668}
3669
3670/*----------------------------------------------------------------------------
3671 * VMMIDIShutdown()
3672 *----------------------------------------------------------------------------
3673 * Purpose:
3674 * Clean up any Synth related system issues.
3675 *
3676 * Inputs:
3677 * psEASData - pointer to overall EAS data structure
3678 *
3679 * Outputs:
3680 * None
3681 *
3682 * Side Effects:
3683 *
3684 *----------------------------------------------------------------------------
3685*/
3686void VMMIDIShutdown (S_EAS_DATA *pEASData, S_SYNTH *pSynth)
3687{
3688    EAS_INT vSynthNum;
3689
3690    /* decrement reference count, free if all references are gone */
3691    if (--pSynth->refCount > 0)
3692        return;
3693
3694    vSynthNum = pSynth->vSynthNum;
3695
3696    /* cleanup DLS load */
3697#ifdef DLS_SYNTHESIZER
3698    /*lint -e{550} result used only in debugging code */
3699    if (pSynth->pDLS != NULL)
3700    {
3701        EAS_RESULT result;
3702        if ((result = DLSCleanup(pEASData->hwInstData, pSynth->pDLS)) != EAS_SUCCESS)
3703            { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMMIDIShutdown: Error %ld cleaning up DLS collection\n", result); */ }
3704        pSynth->pDLS = NULL;
3705    }
3706#endif
3707
3708    VMReset(pEASData->pVoiceMgr, pSynth, EAS_TRUE);
3709
3710    /* check Configuration Module for static memory allocation */
3711    if (!pEASData->staticMemoryModel)
3712        EAS_HWFree(pEASData->hwInstData, pSynth);
3713
3714    /* clear pointer to MIDI state */
3715    pEASData->pVoiceMgr->pSynth[vSynthNum] = NULL;
3716}
3717
3718/*----------------------------------------------------------------------------
3719 * VMShutdown()
3720 *----------------------------------------------------------------------------
3721 * Purpose:
3722 * Clean up any Synth related system issues.
3723 *
3724 * Inputs:
3725 * psEASData - pointer to overall EAS data structure
3726 *
3727 * Outputs:
3728 * None
3729 *
3730 * Side Effects:
3731 *
3732 *----------------------------------------------------------------------------
3733*/
3734void VMShutdown (S_EAS_DATA *pEASData)
3735{
3736
3737    /* don't free a NULL pointer */
3738    if (pEASData->pVoiceMgr == NULL)
3739        return;
3740
3741#ifdef DLS_SYNTHESIZER
3742    /* if we have a global DLS collection, clean it up */
3743    if (pEASData->pVoiceMgr->pGlobalDLS)
3744    {
3745        DLSCleanup(pEASData->hwInstData, pEASData->pVoiceMgr->pGlobalDLS);
3746        pEASData->pVoiceMgr->pGlobalDLS = NULL;
3747    }
3748#endif
3749
3750    /* check Configuration Module for static memory allocation */
3751    if (!pEASData->staticMemoryModel)
3752        EAS_HWFree(pEASData->hwInstData, pEASData->pVoiceMgr);
3753    pEASData->pVoiceMgr = NULL;
3754}
3755
3756#ifdef EXTERNAL_AUDIO
3757/*----------------------------------------------------------------------------
3758 * EAS_RegExtAudioCallback()
3759 *----------------------------------------------------------------------------
3760 * Register a callback for external audio processing
3761 *----------------------------------------------------------------------------
3762*/
3763void VMRegExtAudioCallback (S_SYNTH *pSynth, EAS_VOID_PTR pInstData, EAS_EXT_PRG_CHG_FUNC cbProgChgFunc, EAS_EXT_EVENT_FUNC cbEventFunc)
3764{
3765    pSynth->pExtAudioInstData = pInstData;
3766    pSynth->cbProgChgFunc = cbProgChgFunc;
3767    pSynth->cbEventFunc = cbEventFunc;
3768}
3769
3770/*----------------------------------------------------------------------------
3771 * VMGetMIDIControllers()
3772 *----------------------------------------------------------------------------
3773 * Returns the MIDI controller values on the specified channel
3774 *----------------------------------------------------------------------------
3775*/
3776void VMGetMIDIControllers (S_SYNTH *pSynth, EAS_U8 channel, S_MIDI_CONTROLLERS *pControl)
3777{
3778    pControl->modWheel = pSynth->channels[channel].modWheel;
3779    pControl->volume = pSynth->channels[channel].volume;
3780    pControl->pan = pSynth->channels[channel].pan;
3781    pControl->expression = pSynth->channels[channel].expression;
3782    pControl->channelPressure = pSynth->channels[channel].channelPressure;
3783
3784#ifdef _REVERB
3785    pControl->reverbSend = pSynth->channels[channel].reverbSend;
3786#endif
3787
3788#ifdef _CHORUSE
3789    pControl->chorusSend = pSynth->channels[channel].chorusSend;
3790#endif
3791}
3792#endif
3793
3794#ifdef _SPLIT_ARCHITECTURE
3795/*----------------------------------------------------------------------------
3796 * VMStartFrame()
3797 *----------------------------------------------------------------------------
3798 * Purpose:
3799 * Starts an audio frame
3800 *
3801 * Inputs:
3802 *
3803 * Outputs:
3804 * Returns true if EAS_MixEnginePrep should be called (onboard mixing)
3805 *
3806 * Side Effects:
3807 *
3808 *----------------------------------------------------------------------------
3809*/
3810EAS_BOOL VMStartFrame (S_EAS_DATA *pEASData)
3811{
3812
3813    /* init counter for voices starts in split architecture */
3814#ifdef MAX_VOICE_STARTS
3815    pVoiceMgr->numVoiceStarts = 0;
3816#endif
3817
3818    return pFrameInterface->pfStartFrame(pEASData->pVoiceMgr->pFrameBuffer);
3819}
3820
3821/*----------------------------------------------------------------------------
3822 * VMEndFrame()
3823 *----------------------------------------------------------------------------
3824 * Purpose:
3825 * Stops an audio frame
3826 *
3827 * Inputs:
3828 *
3829 * Outputs:
3830 * Returns true if EAS_MixEnginePost should be called (onboard mixing)
3831 *
3832 * Side Effects:
3833 *
3834 *----------------------------------------------------------------------------
3835*/
3836EAS_BOOL VMEndFrame (S_EAS_DATA *pEASData)
3837{
3838
3839    return pFrameInterface->pfEndFrame(pEASData->pVoiceMgr->pFrameBuffer, pEASData->pMixBuffer, pEASData->masterGain);
3840}
3841#endif
3842
3843#ifdef TEST_HARNESS
3844/*----------------------------------------------------------------------------
3845 * SanityCheck()
3846 *----------------------------------------------------------------------------
3847*/
3848EAS_RESULT VMSanityCheck (EAS_DATA_HANDLE pEASData)
3849{
3850    S_SYNTH_VOICE *pVoice;
3851    S_SYNTH *pSynth;
3852    EAS_INT i;
3853    EAS_INT j;
3854    EAS_INT freeVoices;
3855    EAS_INT activeVoices;
3856    EAS_INT playingVoices;
3857    EAS_INT stolenVoices;
3858    EAS_INT releasingVoices;
3859    EAS_INT mutingVoices;
3860    EAS_INT poolCount[MAX_VIRTUAL_SYNTHESIZERS][NUM_SYNTH_CHANNELS];
3861    EAS_INT vSynthNum;
3862    EAS_RESULT result = EAS_SUCCESS;
3863
3864    /* initialize counts */
3865    EAS_HWMemSet(poolCount, 0, sizeof(poolCount));
3866    freeVoices = activeVoices = playingVoices = stolenVoices = releasingVoices = mutingVoices = 0;
3867
3868    /* iterate through all voices */
3869    for (i = 0; i < MAX_SYNTH_VOICES; i++)
3870    {
3871        pVoice = &pEASData->pVoiceMgr->voices[i];
3872        if (pVoice->voiceState != eVoiceStateFree)
3873        {
3874            vSynthNum = GET_VSYNTH(pVoice->channel);
3875            if (vSynthNum >= MAX_VIRTUAL_SYNTHESIZERS)
3876            {
3877                { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMSanityCheck: Voice %d has invalid virtual synth number %d\n", i, vSynthNum); */ }
3878                result = EAS_FAILURE;
3879                continue;
3880            }
3881            pSynth = pEASData->pVoiceMgr->pSynth[vSynthNum];
3882
3883            switch (pVoice->voiceState)
3884            {
3885                case eVoiceStateMuting:
3886                    activeVoices++;
3887                    mutingVoices++;
3888                    break;
3889
3890                case eVoiceStateStolen:
3891                    vSynthNum = GET_VSYNTH(pVoice->nextChannel);
3892                    if (vSynthNum >= MAX_VIRTUAL_SYNTHESIZERS)
3893                    {
3894                        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMSanityCheck: Voice %d has invalid virtual synth number %d\n", i, vSynthNum); */ }
3895                        result = EAS_FAILURE;
3896                        continue;
3897                    }
3898                    pSynth = pEASData->pVoiceMgr->pSynth[vSynthNum];
3899                    activeVoices++;
3900                    stolenVoices++;
3901                    poolCount[vSynthNum][pSynth->channels[GET_CHANNEL(pVoice->nextChannel)].pool]++;
3902                    break;
3903
3904                case eVoiceStateStart:
3905                case eVoiceStatePlay:
3906                    activeVoices++;
3907                    playingVoices++;
3908                    poolCount[vSynthNum][pSynth->channels[GET_CHANNEL(pVoice->channel)].pool]++;
3909                    break;
3910
3911                case eVoiceStateRelease:
3912                    activeVoices++;
3913                    releasingVoices++;
3914                    poolCount[vSynthNum][pSynth->channels[GET_CHANNEL(pVoice->channel)].pool]++;
3915                    break;
3916
3917                default:
3918                    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMSanityCheck : voice %d in invalid state\n", i); */ }
3919                    result = EAS_FAILURE;
3920                    break;
3921            }
3922        }
3923
3924        /* count free voices */
3925        else
3926            freeVoices++;
3927    }
3928
3929    /* dump state info */
3930    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "%d free\n", freeVoices); */ }
3931    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "%d active\n", activeVoices); */ }
3932    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "%d playing\n", playingVoices); */ }
3933    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "%d releasing\n", releasingVoices); */ }
3934    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "%d muting\n", mutingVoices); */ }
3935    { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "%d stolen\n", stolenVoices); */ }
3936
3937    if (pEASData->pVoiceMgr->activeVoices != activeVoices)
3938    {
3939        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Active voice mismatch was %d should be %d\n",
3940            pEASData->pVoiceMgr->activeVoices, activeVoices); */ }
3941        result = EAS_FAILURE;
3942    }
3943
3944    /* check virtual synth status */
3945    for (i = 0; i < MAX_VIRTUAL_SYNTHESIZERS; i++)
3946    {
3947        if (pEASData->pVoiceMgr->pSynth[i] == NULL)
3948            continue;
3949
3950        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Synth %d numActiveVoices: %d\n", i, pEASData->pVoiceMgr->pSynth[i]->numActiveVoices); */ }
3951        if (pEASData->pVoiceMgr->pSynth[i]->numActiveVoices > MAX_SYNTH_VOICES)
3952        {
3953            { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMSanityCheck: Synth %d illegal count for numActiveVoices: %d\n", i, pEASData->pVoiceMgr->pSynth[i]->numActiveVoices); */ }
3954            result = EAS_FAILURE;
3955        }
3956        for (j = 0; j < NUM_SYNTH_CHANNELS; j++)
3957        {
3958            if (poolCount[i][j] != pEASData->pVoiceMgr->pSynth[i]->poolCount[j])
3959            {
3960                { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Pool count mismatch synth %d pool %d, was %d, should be %d\n",
3961                    i, j, pEASData->pVoiceMgr->pSynth[i]->poolCount[j], poolCount[i][j]); */ }
3962                result = EAS_FAILURE;
3963            }
3964        }
3965    }
3966
3967    return result;
3968}
3969#endif
3970
3971
3972