eas_voicemgt.c revision 7df30109963092559d3760c0661a020f9daf1030
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