summaryrefslogtreecommitdiffstats
path: root/arm-fm-22k/lib_src/eas_voicemgt.c
diff options
context:
space:
mode:
Diffstat (limited to 'arm-fm-22k/lib_src/eas_voicemgt.c')
-rw-r--r--arm-fm-22k/lib_src/eas_voicemgt.c3971
1 files changed, 3971 insertions, 0 deletions
diff --git a/arm-fm-22k/lib_src/eas_voicemgt.c b/arm-fm-22k/lib_src/eas_voicemgt.c
new file mode 100644
index 0000000..873f29d
--- /dev/null
+++ b/arm-fm-22k/lib_src/eas_voicemgt.c
@@ -0,0 +1,3971 @@
+/*----------------------------------------------------------------------------
+ *
+ * File:
+ * eas_voicemgt.c
+ *
+ * Contents and purpose:
+ * Implements the synthesizer functions.
+ *
+ * Copyright Sonic Network Inc. 2004
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *----------------------------------------------------------------------------
+ * Revision Control:
+ * $Revision: 794 $
+ * $Date: 2007-08-01 00:08:48 -0700 (Wed, 01 Aug 2007) $
+ *----------------------------------------------------------------------------
+*/
+
+/* includes */
+#include "eas.h"
+#include "eas_data.h"
+#include "eas_config.h"
+#include "eas_report.h"
+#include "eas_midictrl.h"
+#include "eas_host.h"
+#include "eas_synth_protos.h"
+#include "eas_vm_protos.h"
+
+#ifdef DLS_SYNTHESIZER
+#include "eas_mdls.h"
+#endif
+
+// #define _DEBUG_VM
+
+/* some defines for workload */
+#define WORKLOAD_AMOUNT_SMALL_INCREMENT 5
+#define WORKLOAD_AMOUNT_START_NOTE 10
+#define WORKLOAD_AMOUNT_STOP_NOTE 10
+#define WORKLOAD_AMOUNT_KEY_GROUP 10
+#define WORKLOAD_AMOUNT_POLY_LIMIT 10
+
+/* pointer to base sound library */
+extern S_EAS easSoundLib;
+
+#ifdef TEST_HARNESS
+extern S_EAS easTestLib;
+EAS_SNDLIB_HANDLE VMGetLibHandle(EAS_INT libNum)
+{
+ switch (libNum)
+ {
+ case 0:
+ return &easSoundLib;
+#ifdef _WT_SYNTH
+ case 1:
+ return &easTestLib;
+#endif
+ default:
+ return NULL;
+ }
+}
+#endif
+
+/* pointer to synthesizer interface(s) */
+#ifdef _WT_SYNTH
+extern const S_SYNTH_INTERFACE wtSynth;
+#endif
+
+#ifdef _FM_SYNTH
+extern const S_SYNTH_INTERFACE fmSynth;
+#endif
+
+typedef S_SYNTH_INTERFACE *S_SYNTH_INTERFACE_HANDLE;
+
+/* wavetable on MCU */
+#if defined(EAS_WT_SYNTH)
+const S_SYNTH_INTERFACE *const pPrimarySynth = &wtSynth;
+
+/* FM on MCU */
+#elif defined(EAS_FM_SYNTH)
+const S_SYNTH_INTERFACE *const pPrimarySynth = &fmSynth;
+
+/* wavetable drums on MCU, FM melodic on DSP */
+#elif defined(EAS_HYBRID_SYNTH)
+const S_SYNTH_INTERFACE *const pPrimarySynth = &wtSynth;
+const S_SYNTH_INTERFACE *const pSecondarySynth = &fmSynth;
+
+/* wavetable drums on MCU, wavetable melodic on DSP */
+#elif defined(EAS_SPLIT_WT_SYNTH)
+const S_SYNTH_INTERFACE *const pPrimarySynth = &wtSynth;
+extern const S_FRAME_INTERFACE wtFrameInterface;
+const S_FRAME_INTERFACE *const pFrameInterface = &wtFrameInterface;
+
+/* wavetable drums on MCU, FM melodic on DSP */
+#elif defined(EAS_SPLIT_HYBRID_SYNTH)
+const S_SYNTH_INTERFACE *const pPrimarySynth = &wtSynth;
+const S_SYNTH_INTERFACE *const pSecondarySynth = &fmSynth;
+extern const S_FRAME_INTERFACE fmFrameInterface;
+const S_FRAME_INTERFACE *const pFrameInterface = &fmFrameInterface;
+
+/* FM on DSP */
+#elif defined(EAS_SPLIT_FM_SYNTH)
+const S_SYNTH_INTERFACE *const pPrimarySynth = &fmSynth;
+extern const S_FRAME_INTERFACE fmFrameInterface;
+const S_FRAME_INTERFACE *const pFrameInterface = &fmFrameInterface;
+
+#else
+#error "Undefined architecture option"
+#endif
+
+/*----------------------------------------------------------------------------
+ * inline functions
+ *----------------------------------------------------------------------------
+*/
+EAS_INLINE const S_REGION* GetRegionPtr (S_SYNTH *pSynth, EAS_U16 regionIndex)
+{
+#if defined(DLS_SYNTHESIZER)
+ if (regionIndex & FLAG_RGN_IDX_DLS_SYNTH)
+ return &pSynth->pDLS->pDLSRegions[regionIndex & REGION_INDEX_MASK].wtRegion.region;
+#endif
+#if defined(_HYBRID_SYNTH)
+ if (regionIndex & FLAG_RGN_IDX_FM_SYNTH)
+ return &pSynth->pEAS->pFMRegions[regionIndex & REGION_INDEX_MASK].region;
+ else
+ return &pSynth->pEAS->pWTRegions[regionIndex].region;
+#elif defined(_WT_SYNTH)
+ return &pSynth->pEAS->pWTRegions[regionIndex].region;
+#elif defined(_FM_SYNTH)
+ return &pSynth->pEAS->pFMRegions[regionIndex].region;
+#endif
+}
+
+/*lint -esym(715, voiceNum) used in some implementation */
+EAS_INLINE const S_SYNTH_INTERFACE* GetSynthPtr (EAS_INT voiceNum)
+{
+#if defined(_HYBRID_SYNTH)
+ if (voiceNum < NUM_PRIMARY_VOICES)
+ return pPrimarySynth;
+ else
+ return pSecondarySynth;
+#else
+ return pPrimarySynth;
+#endif
+}
+
+EAS_INLINE EAS_INT GetAdjustedVoiceNum (EAS_INT voiceNum)
+{
+#if defined(_HYBRID_SYNTH)
+ if (voiceNum >= NUM_PRIMARY_VOICES)
+ return voiceNum - NUM_PRIMARY_VOICES;
+#endif
+ return voiceNum;
+}
+
+EAS_INLINE EAS_U8 VSynthToChannel (S_SYNTH *pSynth, EAS_U8 channel)
+{
+ /*lint -e{734} synthNum is always 0-15 */
+ return channel | (pSynth->vSynthNum << 4);
+}
+
+/*----------------------------------------------------------------------------
+ * InitVoice()
+ *----------------------------------------------------------------------------
+ * Initialize a synthesizer voice
+ *----------------------------------------------------------------------------
+*/
+void InitVoice (S_SYNTH_VOICE *pVoice)
+{
+ pVoice->channel = UNASSIGNED_SYNTH_CHANNEL;
+ pVoice->nextChannel = UNASSIGNED_SYNTH_CHANNEL;
+ pVoice->note = pVoice->nextNote = DEFAULT_KEY_NUMBER;
+ pVoice->velocity = pVoice->nextVelocity = DEFAULT_VELOCITY;
+ pVoice->regionIndex = DEFAULT_REGION_INDEX;
+ pVoice->age = DEFAULT_AGE;
+ pVoice->voiceFlags = DEFAULT_VOICE_FLAGS;
+ pVoice->voiceState = DEFAULT_VOICE_STATE;
+}
+
+/*----------------------------------------------------------------------------
+ * IncVoicePoolCount()
+ *----------------------------------------------------------------------------
+ * Updates the voice pool count when a voice changes state
+ *----------------------------------------------------------------------------
+*/
+static void IncVoicePoolCount (S_VOICE_MGR *pVoiceMgr, S_SYNTH_VOICE *pVoice)
+{
+ S_SYNTH *pSynth;
+ EAS_INT pool;
+
+ /* ignore muting voices */
+ if (pVoice->voiceState == eVoiceStateMuting)
+ return;
+
+ if (pVoice->voiceState == eVoiceStateStolen)
+ {
+ pSynth = pVoiceMgr->pSynth[GET_VSYNTH(pVoice->nextChannel)];
+ pool = pSynth->channels[GET_CHANNEL(pVoice->nextChannel)].pool;
+ }
+ else
+ {
+ pSynth = pVoiceMgr->pSynth[GET_VSYNTH(pVoice->channel)];
+ pool = pSynth->channels[GET_CHANNEL(pVoice->channel)].pool;
+ }
+
+ pSynth->poolCount[pool]++;
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IncVoicePoolCount: Synth=%d pool=%d\n", pSynth->vSynthNum, pool); */ }
+#endif
+}
+
+/*----------------------------------------------------------------------------
+ * DecVoicePoolCount()
+ *----------------------------------------------------------------------------
+ * Updates the voice pool count when a voice changes state
+ *----------------------------------------------------------------------------
+*/
+static void DecVoicePoolCount (S_VOICE_MGR *pVoiceMgr, S_SYNTH_VOICE *pVoice)
+{
+ S_SYNTH *pSynth;
+ EAS_INT pool;
+
+ /* ignore muting voices */
+ if (pVoice->voiceState == eVoiceStateMuting)
+ return;
+
+ if (pVoice->voiceState == eVoiceStateStolen)
+ {
+ pSynth = pVoiceMgr->pSynth[GET_VSYNTH(pVoice->nextChannel)];
+ pool = pSynth->channels[GET_CHANNEL(pVoice->nextChannel)].pool;
+ }
+ else
+ {
+ pSynth = pVoiceMgr->pSynth[GET_VSYNTH(pVoice->channel)];
+ pool = pSynth->channels[GET_CHANNEL(pVoice->channel)].pool;
+ }
+
+ pSynth->poolCount[pool]--;
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "DecVoicePoolCount: Synth=%d pool=%d\n", pSynth->vSynthNum, pool); */ }
+#endif
+}
+
+/*----------------------------------------------------------------------------
+ * VMInitialize()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT VMInitialize (S_EAS_DATA *pEASData)
+{
+ S_VOICE_MGR *pVoiceMgr;
+ EAS_INT i;
+
+ /* check Configuration Module for data allocation */
+ if (pEASData->staticMemoryModel)
+ pVoiceMgr = EAS_CMEnumData(EAS_CM_SYNTH_DATA);
+ else
+ pVoiceMgr = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_VOICE_MGR));
+ if (!pVoiceMgr)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitialize: Failed to allocate synthesizer memory\n"); */ }
+ return EAS_ERROR_MALLOC_FAILED;
+ }
+ EAS_HWMemSet(pVoiceMgr, 0, sizeof(S_VOICE_MGR));
+
+ /* initialize non-zero variables */
+ pVoiceMgr->pGlobalEAS = (S_EAS*) &easSoundLib;
+ pVoiceMgr->maxPolyphony = (EAS_U16) MAX_SYNTH_VOICES;
+
+#if defined(_SECONDARY_SYNTH) || defined(EAS_SPLIT_WT_SYNTH)
+ pVoiceMgr->maxPolyphonyPrimary = NUM_PRIMARY_VOICES;
+ pVoiceMgr->maxPolyphonySecondary = NUM_SECONDARY_VOICES;
+#endif
+
+ /* set max workload to zero */
+ pVoiceMgr->maxWorkLoad = 0;
+
+ /* initialize the voice manager parameters */
+ for (i = 0; i < MAX_SYNTH_VOICES; i++)
+ InitVoice(&pVoiceMgr->voices[i]);
+
+ /* initialize the synth */
+ /*lint -e{522} return unused at this time */
+ pPrimarySynth->pfInitialize(pVoiceMgr);
+
+ /* initialize the off-chip synth */
+#ifdef _HYBRID_SYNTH
+ /*lint -e{522} return unused at this time */
+ pSecondarySynth->pfInitialize(pVoiceMgr);
+#endif
+
+ pEASData->pVoiceMgr = pVoiceMgr;
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * VMInitMIDI()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT VMInitMIDI (S_EAS_DATA *pEASData, S_SYNTH **ppSynth)
+{
+ EAS_RESULT result;
+ S_SYNTH *pSynth;
+ EAS_INT virtualSynthNum;
+
+ *ppSynth = NULL;
+
+ /* static memory model only allows one synth */
+ if (pEASData->staticMemoryModel)
+ {
+ if (pEASData->pVoiceMgr->pSynth[0] != NULL)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI: No virtual synthesizer support for static memory model\n"); */ }
+ return EAS_ERROR_NO_VIRTUAL_SYNTHESIZER;
+ }
+
+ /* check Configuration Module for data allocation */
+ pSynth = EAS_CMEnumData(EAS_CM_MIDI_DATA);
+ virtualSynthNum = 0;
+ }
+
+ /* dynamic memory model */
+ else
+ {
+ for (virtualSynthNum = 0; virtualSynthNum < MAX_VIRTUAL_SYNTHESIZERS; virtualSynthNum++)
+ if (pEASData->pVoiceMgr->pSynth[virtualSynthNum] == NULL)
+ break;
+ if (virtualSynthNum == MAX_VIRTUAL_SYNTHESIZERS)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI: Exceeded number of active virtual synthesizers"); */ }
+ return EAS_ERROR_NO_VIRTUAL_SYNTHESIZER;
+ }
+ pSynth = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_SYNTH));
+ }
+
+ /* make sure we have a valid memory pointer */
+ if (pSynth == NULL)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI: Failed to allocate synthesizer memory\n"); */ }
+ return EAS_ERROR_MALLOC_FAILED;
+ }
+ EAS_HWMemSet(pSynth, 0, sizeof(S_SYNTH));
+
+ /* set the sound library pointer */
+ if ((result = VMSetEASLib(pSynth, pEASData->pVoiceMgr->pGlobalEAS)) != EAS_SUCCESS)
+ {
+ VMMIDIShutdown(pEASData, pSynth);
+ return result;
+ }
+
+ /* link in DLS bank if downloaded */
+#ifdef DLS_SYNTHESIZER
+ if (pEASData->pVoiceMgr->pGlobalDLS)
+ {
+ pSynth->pDLS = pEASData->pVoiceMgr->pGlobalDLS;
+ DLSAddRef(pSynth->pDLS);
+ }
+#endif
+
+ /* initialize MIDI state variables */
+ pSynth->synthFlags = DEFAULT_SYNTH_FLAGS;
+ pSynth->masterVolume = DEFAULT_SYNTH_MASTER_VOLUME;
+ pSynth->refCount = 1;
+ pSynth->priority = DEFAULT_SYNTH_PRIORITY;
+ pSynth->poolAlloc[0] = (EAS_U8) pEASData->pVoiceMgr->maxPolyphony;
+
+ VMInitializeAllChannels(pEASData->pVoiceMgr, pSynth);
+
+ pSynth->vSynthNum = (EAS_U8) virtualSynthNum;
+ pEASData->pVoiceMgr->pSynth[virtualSynthNum] = pSynth;
+
+ *ppSynth = pSynth;
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * VMIncRefCount()
+ *----------------------------------------------------------------------------
+ * Increment reference count for virtual synth
+ *----------------------------------------------------------------------------
+*/
+void VMIncRefCount (S_SYNTH *pSynth)
+{
+ pSynth->refCount++;
+}
+
+/*----------------------------------------------------------------------------
+ * VMReset()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * We call this routine to start the process of reseting the synth.
+ * This routine sets a flag for the entire synth indicating that we want
+ * to reset.
+ * We also force all voices to mute quickly.
+ * However, we do not actually perform any synthesis in this routine. That
+ * is, we do not ramp the voices down from this routine, but instead, we
+ * let the "regular" synth processing steps take care of adding the ramp
+ * down samples to the output buffer. After we are sure that all voices
+ * have completed ramping down, we continue the process of resetting the
+ * synth (from another routine).
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ * force - force reset even if voices are active
+ *
+ * Outputs:
+ *
+ * Side Effects:
+ * - set a flag (in psSynthObject->m_nFlags) indicating synth reset requested.
+ * - force all voices to update their envelope states to mute
+ *
+ *----------------------------------------------------------------------------
+*/
+void VMReset (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_BOOL force)
+{
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMReset: request to reset synth. Force = %d\n", force); */ }
+#endif
+
+ /* force voices to off state - may cause audio artifacts */
+ if (force)
+ {
+ pVoiceMgr->activeVoices -= pSynth->numActiveVoices;
+ pSynth->numActiveVoices = 0;
+ VMInitializeAllVoices(pVoiceMgr, pSynth->vSynthNum);
+ }
+ else
+ VMMuteAllVoices(pVoiceMgr, pSynth);
+
+ /* don't reset if voices are still playing */
+ if (pSynth->numActiveVoices == 0)
+ {
+ EAS_INT i;
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMReset: complete the reset process\n"); */ }
+#endif
+
+ VMInitializeAllChannels(pVoiceMgr, pSynth);
+ for (i = 0; i < NUM_SYNTH_CHANNELS; i++)
+ pSynth->poolCount[i] = 0;
+
+ /* set polyphony */
+ if (pSynth->maxPolyphony < pVoiceMgr->maxPolyphony)
+ pSynth->poolAlloc[0] = (EAS_U8) pVoiceMgr->maxPolyphony;
+ else
+ pSynth->poolAlloc[0] = (EAS_U8) pSynth->maxPolyphony;
+
+ /* clear reset flag */
+ pSynth->synthFlags &= ~SYNTH_FLAG_RESET_IS_REQUESTED;
+ }
+
+ /* handle reset after voices are muted */
+ else
+ pSynth->synthFlags |= SYNTH_FLAG_RESET_IS_REQUESTED;
+}
+
+/*----------------------------------------------------------------------------
+ * VMInitializeAllChannels()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ *----------------------------------------------------------------------------
+*/
+void VMInitializeAllChannels (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth)
+{
+ S_SYNTH_CHANNEL *pChannel;
+ EAS_INT i;
+
+ VMResetControllers(pSynth);
+
+ /* init each channel */
+ pChannel = pSynth->channels;
+
+ for (i = 0; i < NUM_SYNTH_CHANNELS; i++, pChannel++)
+ {
+ pChannel->channelFlags = DEFAULT_CHANNEL_FLAGS;
+ pChannel->staticGain = DEFAULT_CHANNEL_STATIC_GAIN;
+ pChannel->staticPitch = DEFAULT_CHANNEL_STATIC_PITCH;
+ pChannel->pool = 0;
+
+ /* the drum channel needs a different init */
+ if (i == DEFAULT_DRUM_CHANNEL)
+ {
+ pChannel->bankNum = DEFAULT_RHYTHM_BANK_NUMBER;
+ pChannel->channelFlags |= CHANNEL_FLAG_RHYTHM_CHANNEL;
+ }
+ else
+ pChannel->bankNum = DEFAULT_MELODY_BANK_NUMBER;
+
+ VMProgramChange(pVoiceMgr, pSynth, (EAS_U8) i, DEFAULT_SYNTH_PROGRAM_NUMBER);
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+ * VMResetControllers()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ *----------------------------------------------------------------------------
+*/
+void VMResetControllers (S_SYNTH *pSynth)
+{
+ S_SYNTH_CHANNEL *pChannel;
+ EAS_INT i;
+
+ pChannel = pSynth->channels;
+
+ for (i = 0; i < NUM_SYNTH_CHANNELS; i++, pChannel++)
+ {
+ pChannel->pitchBend = DEFAULT_PITCH_BEND;
+ pChannel->modWheel = DEFAULT_MOD_WHEEL;
+ pChannel->volume = DEFAULT_CHANNEL_VOLUME;
+ pChannel->pan = DEFAULT_PAN;
+ pChannel->expression = DEFAULT_EXPRESSION;
+
+#ifdef _REVERB
+ pSynth->channels[i].reverbSend = DEFAULT_REVERB_SEND;
+#endif
+
+#ifdef _CHORUS
+ pSynth->channels[i].chorusSend = DEFAULT_CHORUS_SEND;
+#endif
+
+ pChannel->channelPressure = DEFAULT_CHANNEL_PRESSURE;
+ pChannel->registeredParam = DEFAULT_REGISTERED_PARAM;
+ pChannel->pitchBendSensitivity = DEFAULT_PITCH_BEND_SENSITIVITY;
+ pChannel->finePitch = DEFAULT_FINE_PITCH;
+ pChannel->coarsePitch = DEFAULT_COARSE_PITCH;
+
+ /* update all voices on this channel */
+ pChannel->channelFlags |= CHANNEL_FLAG_UPDATE_CHANNEL_PARAMETERS;
+ }
+}
+
+/*----------------------------------------------------------------------------
+ * VMInitializeAllVoices()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ *----------------------------------------------------------------------------
+*/
+void VMInitializeAllVoices (S_VOICE_MGR *pVoiceMgr, EAS_INT vSynthNum)
+{
+ EAS_INT i;
+
+ /* initialize the voice manager parameters */
+ for (i = 0; i < MAX_SYNTH_VOICES; i++)
+ {
+ if (pVoiceMgr->voices[i].voiceState != eVoiceStateStolen)
+ {
+ if (GET_VSYNTH(pVoiceMgr->voices[i].channel) == vSynthNum)
+ InitVoice(&pVoiceMgr->voices[i]);
+ }
+ else
+ {
+ if (GET_VSYNTH(pVoiceMgr->voices[i].nextChannel) == vSynthNum)
+ InitVoice(&pVoiceMgr->voices[i]);
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------
+ * VMMuteVoice()
+ *----------------------------------------------------------------------------
+ * Mute the selected voice
+ *----------------------------------------------------------------------------
+*/
+void VMMuteVoice (S_VOICE_MGR *pVoiceMgr, EAS_I32 voiceNum)
+{
+ S_SYNTH *pSynth;
+ S_SYNTH_VOICE *pVoice;
+
+ /* take no action if voice is already muted */
+ pVoice = &pVoiceMgr->voices[voiceNum];
+ if ((pVoice->voiceState == eVoiceStateMuting) || (pVoice->voiceState == eVoiceStateFree))
+ return;
+
+ /* one less voice in pool */
+ DecVoicePoolCount(pVoiceMgr, pVoice);
+
+ pSynth = pVoiceMgr->pSynth[GET_VSYNTH(pVoice->channel)];
+ GetSynthPtr(voiceNum)->pfMuteVoice(pVoiceMgr, pSynth, pVoice, GetAdjustedVoiceNum(voiceNum));
+ pVoice->voiceState = eVoiceStateMuting;
+
+}
+
+/*----------------------------------------------------------------------------
+ * VMReleaseVoice()
+ *----------------------------------------------------------------------------
+ * Release the selected voice
+ *----------------------------------------------------------------------------
+*/
+void VMReleaseVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_I32 voiceNum)
+{
+ S_SYNTH_VOICE *pVoice = &pVoiceMgr->voices[voiceNum];
+
+ /* take no action if voice is already free, muting, or releasing */
+ if (( pVoice->voiceState == eVoiceStateMuting) ||
+ (pVoice->voiceState == eVoiceStateFree) ||
+ (pVoice->voiceState == eVoiceStateRelease))
+ return;
+
+ /* stolen voices should just be muted */
+ if (pVoice->voiceState == eVoiceStateStolen)
+ VMMuteVoice(pVoiceMgr, voiceNum);
+
+ /* release this voice */
+ GetSynthPtr(voiceNum)->pfReleaseVoice(pVoiceMgr, pSynth, &pVoiceMgr->voices[voiceNum], GetAdjustedVoiceNum(voiceNum));
+ pVoice->voiceState = eVoiceStateRelease;
+}
+
+/*----------------------------------------------------------------------------
+ * VMInitMIPTable()
+ *----------------------------------------------------------------------------
+ * Initialize the SP-MIDI MIP table in preparation for receiving MIP message
+ *----------------------------------------------------------------------------
+*/
+void VMInitMIPTable (S_SYNTH *pSynth)
+{
+ EAS_INT i;
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMInitMIPTable\n"); */ }
+#endif
+
+ /* clear SP-MIDI flag */
+ pSynth->synthFlags &= ~SYNTH_FLAG_SP_MIDI_ON;
+ for (i = 0; i < NUM_SYNTH_CHANNELS; i++)
+ {
+ pSynth->channels[i].pool = 0;
+ pSynth->channels[i].mip = 0;
+ }
+}
+
+/*----------------------------------------------------------------------------
+ * VMSetMIPEntry()
+ *----------------------------------------------------------------------------
+ * Sets the priority and MIP level for a MIDI channel
+ *----------------------------------------------------------------------------
+*/
+/*lint -esym(715, pVoiceMgr) reserved for future use */
+void VMSetMIPEntry (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 priority, EAS_U8 mip)
+{
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMSetMIPEntry: channel=%d, priority=%d, MIP=%d\n", channel, priority, mip); */ }
+#endif
+
+ /* save data for use by MIP message processing */
+ if (priority < NUM_SYNTH_CHANNELS)
+ {
+ pSynth->channels[channel].pool = priority;
+ pSynth->channels[channel].mip = mip;
+ }
+}
+
+/*----------------------------------------------------------------------------
+ * VMMIPUpdateChannelMuting()
+ *----------------------------------------------------------------------------
+ * This routine is called after an SP-MIDI message is received and
+ * any time the allocated polyphony changes. It mutes or unmutes
+ * channels based on polyphony.
+ *----------------------------------------------------------------------------
+*/
+void VMMIPUpdateChannelMuting (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth)
+{
+ EAS_INT i;
+ EAS_INT maxPolyphony;
+ EAS_INT channel;
+ EAS_INT vSynthNum;
+ EAS_INT pool;
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMUpdateMIPTable\n"); */ }
+#endif
+
+ /* determine max polyphony */
+ if (pSynth->maxPolyphony)
+ maxPolyphony = pSynth->maxPolyphony;
+ else
+ maxPolyphony = pVoiceMgr->maxPolyphony;
+
+ /* process channels */
+ for (i = 0; i < NUM_SYNTH_CHANNELS; i++)
+ {
+
+ /* channel must be in MIP message and must meet allocation target */
+ if ((pSynth->channels[i].mip != 0) && (pSynth->channels[i].mip <= maxPolyphony))
+ pSynth->channels[i].channelFlags &= ~CHANNEL_FLAG_MUTE;
+ else
+ pSynth->channels[i].channelFlags |= CHANNEL_FLAG_MUTE;
+
+ /* reset voice pool count */
+ pSynth->poolCount[i] = 0;
+ }
+
+ /* mute any voices on muted channels, and count unmuted voices */
+ for (i = 0; i < MAX_SYNTH_VOICES; i++)
+ {
+
+ /* ignore free voices */
+ if (pVoiceMgr->voices[i].voiceState == eVoiceStateFree)
+ continue;
+
+ /* get channel and virtual synth */
+ if (pVoiceMgr->voices[i].voiceState != eVoiceStateStolen)
+ {
+ vSynthNum = GET_VSYNTH(pVoiceMgr->voices[i].channel);
+ channel = GET_CHANNEL(pVoiceMgr->voices[i].channel);
+ }
+ else
+ {
+ vSynthNum = GET_VSYNTH(pVoiceMgr->voices[i].nextChannel);
+ channel = GET_CHANNEL(pVoiceMgr->voices[i].nextChannel);
+ }
+
+ /* ignore voices on other synths */
+ if (vSynthNum != pSynth->vSynthNum)
+ continue;
+
+ /* count voices */
+ pool = pSynth->channels[channel].pool;
+
+ /* deal with muted channels */
+ if (pSynth->channels[channel].channelFlags & CHANNEL_FLAG_MUTE)
+ {
+ /* mute stolen voices scheduled to play on this channel */
+ if (pVoiceMgr->voices[i].voiceState == eVoiceStateStolen)
+ pVoiceMgr->voices[i].voiceState = eVoiceStateMuting;
+
+ /* release voices that aren't already muting */
+ else if (pVoiceMgr->voices[i].voiceState != eVoiceStateMuting)
+ {
+ VMReleaseVoice(pVoiceMgr, pSynth, i);
+ pSynth->poolCount[pool]++;
+ }
+ }
+
+ /* not muted, count this voice */
+ else
+ pSynth->poolCount[pool]++;
+ }
+}
+
+/*----------------------------------------------------------------------------
+ * VMUpdateMIPTable()
+ *----------------------------------------------------------------------------
+ * This routine is called at the end of the SysEx message to allow
+ * the Voice Manager to complete the initialization of the MIP
+ * table. It assigns channels to the appropriate voice pool based
+ * on the MIP setting and calculates the voices allocated for each
+ * pool.
+ *----------------------------------------------------------------------------
+*/
+/*lint -esym(715, pVoiceMgr) reserved for future use */
+void VMUpdateMIPTable (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth)
+{
+ S_SYNTH_CHANNEL *pChannel;
+ EAS_INT i;
+ EAS_INT currentMIP;
+ EAS_INT currentPool;
+ EAS_INT priority[NUM_SYNTH_CHANNELS];
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMUpdateMIPTable\n"); */ }
+#endif
+
+ /* set SP-MIDI flag */
+ pSynth->synthFlags |= SYNTH_FLAG_SP_MIDI_ON;
+
+ /* sort channels into priority order */
+ for (i = 0; i < NUM_SYNTH_CHANNELS; i++)
+ priority[i] = -1;
+ for (i = 0; i < NUM_SYNTH_CHANNELS; i++)
+ {
+ if (pSynth->channels[i].pool != DEFAULT_SP_MIDI_PRIORITY)
+ priority[pSynth->channels[i].pool] = i;
+ }
+
+ /* process channels in priority order */
+ currentMIP = 0;
+ currentPool = -1;
+ for (i = 0; i < NUM_SYNTH_CHANNELS; i++)
+ {
+ /* stop when we run out of channels */
+ if (priority[i] == -1)
+ break;
+
+ pChannel = &pSynth->channels[priority[i]];
+
+ /* when 2 or more channels have the same MIP setting, they
+ * share a common voice pool
+ */
+ if (pChannel->mip == currentMIP)
+ pChannel->pool = (EAS_U8) currentPool;
+
+ /* new voice pool */
+ else
+ {
+ currentPool++;
+ pSynth->poolAlloc[currentPool] = (EAS_U8) (pChannel->mip - currentMIP);
+ currentMIP = pChannel->mip;
+ }
+ }
+
+ /* set SP-MIDI flag */
+ pSynth->synthFlags |= SYNTH_FLAG_SP_MIDI_ON;
+
+ /* update channel muting */
+ VMMIPUpdateChannelMuting (pVoiceMgr, pSynth);
+}
+
+/*----------------------------------------------------------------------------
+ * VMMuteAllVoices()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * We call this in an emergency reset situation.
+ * This forces all voices to mute quickly.
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ * Side Effects:
+ * - forces all voices to update their envelope states to mute
+ *
+ *----------------------------------------------------------------------------
+*/
+void VMMuteAllVoices (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth)
+{
+ EAS_INT i;
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMMuteAllVoices: about to mute all voices!!\n"); */ }
+#endif
+
+ for (i = 0; i < MAX_SYNTH_VOICES; i++)
+ {
+ /* for stolen voices, check new channel */
+ if (pVoiceMgr->voices[i].voiceState == eVoiceStateStolen)
+ {
+ if (GET_VSYNTH(pVoiceMgr->voices[i].nextChannel) == pSynth->vSynthNum)
+ VMMuteVoice(pVoiceMgr, i);
+ }
+
+ else if (pSynth->vSynthNum == GET_VSYNTH(pVoiceMgr->voices[i].channel))
+ VMMuteVoice(pVoiceMgr, i);
+ }
+}
+
+/*----------------------------------------------------------------------------
+ * VMReleaseAllVoices()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * We call this after we've encountered the end of the Midi file.
+ * This ensures all voices are either in release (because we received their
+ * note off already) or forces them to mute quickly.
+ * We use this as a safety to prevent bad midi files from playing forever.
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ * Side Effects:
+ * - forces all voices to update their envelope states to release or mute
+ *
+ *----------------------------------------------------------------------------
+*/
+void VMReleaseAllVoices (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth)
+{
+ EAS_INT i;
+
+ /* release sustain pedal on all channels */
+ for (i = 0; i < NUM_SYNTH_CHANNELS; i++)
+ {
+ if (pSynth->channels[ i ].channelFlags & CHANNEL_FLAG_SUSTAIN_PEDAL)
+ {
+ VMReleaseAllDeferredNoteOffs(pVoiceMgr, pSynth, (EAS_U8) i);
+ pSynth->channels[i].channelFlags &= ~CHANNEL_FLAG_SUSTAIN_PEDAL;
+ }
+ }
+
+ /* release all voices */
+ for (i = 0; i < MAX_SYNTH_VOICES; i++)
+ {
+
+ switch (pVoiceMgr->voices[i].voiceState)
+ {
+ case eVoiceStateStart:
+ case eVoiceStatePlay:
+ /* only release voices on this synth */
+ if (GET_VSYNTH(pVoiceMgr->voices[i].channel) == pSynth->vSynthNum)
+ VMReleaseVoice(pVoiceMgr, pSynth, i);
+ break;
+
+ case eVoiceStateStolen:
+ if (GET_VSYNTH(pVoiceMgr->voices[i].nextChannel) == pSynth->vSynthNum)
+ VMMuteVoice(pVoiceMgr, i);
+ break;
+
+ case eVoiceStateFree:
+ case eVoiceStateRelease:
+ case eVoiceStateMuting:
+ break;
+
+ case eVoiceStateInvalid:
+ default:
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMReleaseAllVoices: error, %d is an unrecognized state\n",
+ pVoiceMgr->voices[i].voiceState); */ }
+#endif
+ break;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------
+ * VMAllNotesOff()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Quickly mute all notes on the given channel.
+ *
+ * Inputs:
+ * nChannel - quickly turn off all notes on this channel
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ * Side Effects:
+ * - forces all voices on this channel to update their envelope states to mute
+ *
+ *----------------------------------------------------------------------------
+*/
+void VMAllNotesOff (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel)
+{
+ EAS_INT voiceNum;
+ S_SYNTH_VOICE *pVoice;
+
+#ifdef _DEBUG_VM
+ if (channel >= NUM_SYNTH_CHANNELS)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMAllNotesOff: error, %d invalid channel number\n",
+ channel); */ }
+ return;
+ }
+#endif
+
+ /* increment workload */
+ pVoiceMgr->workload += WORKLOAD_AMOUNT_SMALL_INCREMENT;
+
+ /* check each voice */
+ channel = VSynthToChannel(pSynth, channel);
+ for (voiceNum = 0; voiceNum < MAX_SYNTH_VOICES; voiceNum++)
+ {
+ pVoice = &pVoiceMgr->voices[voiceNum];
+ if (pVoice->voiceState != eVoiceStateFree)
+ {
+ if (((pVoice->voiceState != eVoiceStateStolen) && (channel == pVoice->channel)) ||
+ ((pVoice->voiceState == eVoiceStateStolen) && (channel == pVoice->nextChannel)))
+ {
+ /* this voice is assigned to the requested channel */
+ GetSynthPtr(voiceNum)->pfMuteVoice(pVoiceMgr, pSynth, pVoice, GetAdjustedVoiceNum(voiceNum));
+ pVoice->voiceState = eVoiceStateMuting;
+ }
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------
+ * VMDeferredStopNote()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Stop the notes that had deferred note-off requests.
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ * None.
+ *
+ * Side Effects:
+ * voices that have had deferred note-off requests are now put into release
+ * psSynthObject->m_sVoice[i].m_nFlags has the VOICE_FLAG_DEFER_MIDI_NOTE_OFF
+ * cleared
+ *----------------------------------------------------------------------------
+*/
+void VMDeferredStopNote (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth)
+{
+ EAS_INT voiceNum;
+ EAS_INT channel;
+ EAS_BOOL deferredNoteOff;
+
+ deferredNoteOff = EAS_FALSE;
+
+ /* check each voice to see if it requires a deferred note off */
+ for (voiceNum=0; voiceNum < MAX_SYNTH_VOICES; voiceNum++)
+ {
+ if (pVoiceMgr->voices[voiceNum].voiceFlags & VOICE_FLAG_DEFER_MIDI_NOTE_OFF)
+ {
+ /* check if this voice was stolen */
+ if (pVoiceMgr->voices[voiceNum].voiceState == eVoiceStateStolen)
+ {
+ /*
+ This voice was stolen, AND it also has a deferred note-off.
+ The stolen note must be completely ramped down at this point.
+ The note that caused the stealing to occur, however, must
+ have received a note-off request before the note that caused
+ stealing ever had a chance to even start. We want to give
+ the note that caused the stealing a chance to play, so we
+ start it on the next update interval, and we defer sending
+ the note-off request until the subsequent update interval.
+ So do not send the note-off request for this voice because
+ this voice was stolen and should have completed ramping down,
+ Also, do not clear the global flag nor this voice's flag
+ because we must indicate that the subsequent update interval,
+ after the note that caused stealing has started, should
+ then send the deferred note-off request.
+ */
+ deferredNoteOff = EAS_TRUE;
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMDeferredStopNote: defer request to stop voice %d (channel=%d note=%d) - voice not started\n",
+ voiceNum,
+ pVoiceMgr->voices[voiceNum].nextChannel,
+ pVoiceMgr->voices[voiceNum].note); */ }
+
+ /* sanity check: this stolen voice better be ramped to zero */
+ if (0 != pVoiceMgr->voices[voiceNum].gain)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMDeferredStopNote: warning, this voice did not complete its ramp to zero\n"); */ }
+ }
+#endif // #ifdef _DEBUG_VM
+
+ }
+ else
+ {
+ /* clear the flag using exor */
+ pVoiceMgr->voices[voiceNum].voiceFlags ^=
+ VOICE_FLAG_DEFER_MIDI_NOTE_OFF;
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMDeferredStopNote: Stop voice %d (channel=%d note=%d)\n",
+ voiceNum,
+ pVoiceMgr->voices[voiceNum].nextChannel,
+ pVoiceMgr->voices[voiceNum].note); */ }
+#endif
+
+ channel = pVoiceMgr->voices[voiceNum].channel & 15;
+
+ /* check if sustain pedal is on */
+ if (pSynth->channels[channel].channelFlags & CHANNEL_FLAG_SUSTAIN_PEDAL)
+ {
+ GetSynthPtr(voiceNum)->pfSustainPedal(pVoiceMgr, pSynth, &pVoiceMgr->voices[voiceNum], &pSynth->channels[channel], GetAdjustedVoiceNum(voiceNum));
+ }
+
+ /* release this voice */
+ else
+ VMReleaseVoice(pVoiceMgr, pSynth, voiceNum);
+
+ }
+
+ }
+
+ }
+
+ /* clear the deferred note-off flag, unless there's another one pending */
+ if (deferredNoteOff == EAS_FALSE)
+ pSynth->synthFlags ^= SYNTH_FLAG_DEFERRED_MIDI_NOTE_OFF_PENDING;
+}
+
+/*----------------------------------------------------------------------------
+ * VMReleaseAllDeferredNoteOffs()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Call this functin when the sustain flag is presently set but
+ * we are now transitioning from damper pedal on to
+ * damper pedal off. This means all notes in this channel
+ * that received a note off while the damper pedal was on, and
+ * had their note-off requests deferred, should now proceed to
+ * the release state.
+ *
+ * Inputs:
+ * nChannel - this channel has its sustain pedal transitioning from on to off
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ * Side Effects:
+ * any voice with deferred note offs on this channel are updated such that
+ * pVoice->m_sEG1.m_eState = eEnvelopeStateRelease
+ * pVoice->m_sEG1.m_nIncrement = release increment
+ * pVoice->m_nFlags = clear the deferred note off flag
+ *----------------------------------------------------------------------------
+*/
+void VMReleaseAllDeferredNoteOffs (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel)
+{
+ S_SYNTH_VOICE *pVoice;
+ EAS_INT voiceNum;
+
+#ifdef _DEBUG_VM
+ if (channel >= NUM_SYNTH_CHANNELS)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMReleaseAllDeferredNoteOffs: error, %d invalid channel number\n",
+ channel); */ }
+ return;
+ }
+#endif /* #ifdef _DEBUG_VM */
+
+ /* increment workload */
+ pVoiceMgr->workload += WORKLOAD_AMOUNT_SMALL_INCREMENT;
+
+ /* find all the voices assigned to this channel */
+ channel = VSynthToChannel(pSynth, channel);
+ for (voiceNum = 0; voiceNum < MAX_SYNTH_VOICES; voiceNum++)
+ {
+
+ pVoice = &pVoiceMgr->voices[voiceNum];
+ if (channel == pVoice->channel)
+ {
+
+ /* does this voice have a deferred note off? */
+ if (pVoice->voiceFlags & VOICE_FLAG_SUSTAIN_PEDAL_DEFER_NOTE_OFF)
+ {
+ /* release voice */
+ VMReleaseVoice(pVoiceMgr, pSynth, voiceNum);
+
+ /* use exor to flip bit, clear the flag */
+ pVoice->voiceFlags &= ~VOICE_FLAG_SUSTAIN_PEDAL_DEFER_NOTE_OFF;
+
+ }
+
+ }
+ }
+
+ return;
+}
+
+/*----------------------------------------------------------------------------
+ * VMCatchNotesForSustainPedal()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Call this function when the sustain flag is presently clear and
+ * the damper pedal is off and we are transitioning from damper pedal OFF to
+ * damper pedal ON. Currently sounding notes should be left
+ * unchanged. However, we should try to "catch" notes if possible.
+ * If any notes are in release and have levels >= sustain level, catch them,
+ * otherwise, let them continue to release.
+ *
+ * Inputs:
+ * nChannel - this channel has its sustain pedal transitioning from on to off
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *----------------------------------------------------------------------------
+*/
+void VMCatchNotesForSustainPedal (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel)
+{
+ EAS_INT voiceNum;
+
+#ifdef _DEBUG_VM
+ if (channel >= NUM_SYNTH_CHANNELS)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMCatchNotesForSustainPedal: error, %d invalid channel number\n",
+ channel); */ }
+ return;
+ }
+#endif
+
+ pVoiceMgr->workload += WORKLOAD_AMOUNT_SMALL_INCREMENT;
+ channel = VSynthToChannel(pSynth, channel);
+
+ /* find all the voices assigned to this channel */
+ for (voiceNum = 0; voiceNum < MAX_SYNTH_VOICES; voiceNum++)
+ {
+ if (channel == pVoiceMgr->voices[voiceNum].channel)
+ {
+ if (eVoiceStateRelease == pVoiceMgr->voices[voiceNum].voiceState)
+ GetSynthPtr(voiceNum)->pfSustainPedal(pVoiceMgr, pSynth, &pVoiceMgr->voices[voiceNum], &pSynth->channels[channel], GetAdjustedVoiceNum(voiceNum));
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------
+ * VMUpdateAllNotesAge()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Increment the note age for all of the active voices.
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ * Side Effects:
+ * m_nAge for all voices is incremented
+ *----------------------------------------------------------------------------
+*/
+void VMUpdateAllNotesAge (S_VOICE_MGR *pVoiceMgr, EAS_U16 age)
+{
+ EAS_INT i;
+
+ for (i = 0; i < MAX_SYNTH_VOICES; i++)
+ {
+ if (age - pVoiceMgr->voices[i].age > 0)
+ pVoiceMgr->voices[i].age++;
+ }
+}
+
+/*----------------------------------------------------------------------------
+ * VMStolenVoice()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * The selected voice is being stolen. Sets the parameters so that the
+ * voice will begin playing the new sound on the next buffer.
+ *
+ * Inputs:
+ * pVoice - pointer to voice to steal
+ * nChannel - the channel to start a note on
+ * nKeyNumber - the key number to start a note for
+ * nNoteVelocity - the key velocity from this note
+ *
+ * Outputs:
+ * None
+ *----------------------------------------------------------------------------
+*/
+static void VMStolenVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_I32 voiceNum, EAS_U8 channel, EAS_U8 note, EAS_U8 velocity, EAS_U16 regionIndex)
+{
+ S_SYNTH_VOICE *pVoice = &pVoiceMgr->voices[voiceNum];
+
+ /* one less voice in old pool */
+ DecVoicePoolCount(pVoiceMgr, pVoice);
+
+ /* mute the sound that is currently playing */
+ GetSynthPtr(voiceNum)->pfMuteVoice(pVoiceMgr, pVoiceMgr->pSynth[GET_VSYNTH(pVoice->channel)], &pVoiceMgr->voices[voiceNum], GetAdjustedVoiceNum(voiceNum));
+ pVoice->voiceState = eVoiceStateStolen;
+
+ /* set new note data */
+ pVoice->nextChannel = VSynthToChannel(pSynth, channel);
+ pVoice->nextNote = note;
+ pVoice->nextVelocity = velocity;
+ pVoice->nextRegionIndex = regionIndex;
+
+ /* one more voice in new pool */
+ IncVoicePoolCount(pVoiceMgr, pVoice);
+
+ /* clear the deferred flags */
+ pVoice->voiceFlags &=
+ ~(VOICE_FLAG_DEFER_MIDI_NOTE_OFF |
+ VOICE_FLAG_DEFER_MUTE |
+ VOICE_FLAG_SUSTAIN_PEDAL_DEFER_NOTE_OFF);
+
+ /* all notes older than this one get "younger" */
+ VMUpdateAllNotesAge(pVoiceMgr, pVoice->age);
+
+ /* assign current age to this note and increment for the next note */
+ pVoice->age = pVoiceMgr->age++;
+}
+
+/*----------------------------------------------------------------------------
+ * VMFreeVoice()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * The selected voice is done playing and being returned to the
+ * pool of free voices
+ *
+ * Inputs:
+ * pVoice - pointer to voice to free
+ *
+ * Outputs:
+ * None
+ *----------------------------------------------------------------------------
+*/
+static void VMFreeVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, S_SYNTH_VOICE *pVoice)
+{
+
+ /* do nothing if voice is already free */
+ if (pVoice->voiceState == eVoiceStateFree)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "VMFreeVoice: Attempt to free voice that is already free\n"); */ }
+ return;
+ }
+
+ /* if we jump directly to free without passing muting stage,
+ * we need to adjust the voice count */
+ DecVoicePoolCount(pVoiceMgr, pVoice);
+
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "VMFreeVoice: Synth=%d\n", pSynth->vSynthNum); */ }
+#endif
+
+ /* return to free voice pool */
+ pVoiceMgr->activeVoices--;
+ pSynth->numActiveVoices--;
+ InitVoice(pVoice);
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMFreeVoice: free voice %d\n", pVoice - pVoiceMgr->voices); */ }
+#endif
+
+ /* all notes older than this one get "younger" */
+ VMUpdateAllNotesAge(pVoiceMgr, pVoice->age);
+ }
+
+/*----------------------------------------------------------------------------
+ * VMRetargetStolenVoice()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * The selected voice has been stolen and needs to be initalized with
+ * the paramters of its new note.
+ *
+ * Inputs:
+ * pVoice - pointer to voice to retarget
+ *
+ * Outputs:
+ * None
+ *----------------------------------------------------------------------------
+*/
+static EAS_BOOL VMRetargetStolenVoice (S_VOICE_MGR *pVoiceMgr, EAS_I32 voiceNum)
+{
+ EAS_U8 flags;
+ S_SYNTH_CHANNEL *pMIDIChannel;
+ S_SYNTH_VOICE *pVoice;
+ S_SYNTH *pSynth;
+ S_SYNTH *pNextSynth;
+
+ /* establish some pointers */
+ pVoice = &pVoiceMgr->voices[voiceNum];
+ pSynth = pVoiceMgr->pSynth[GET_VSYNTH(pVoice->channel)];
+ pMIDIChannel = &pSynth->channels[pVoice->channel & 15];
+ pNextSynth = pVoiceMgr->pSynth[GET_VSYNTH(pVoice->nextChannel)];
+
+#ifdef _DEBUG_VM
+{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMRetargetStolenVoice: retargeting stolen voice %d on channel %d\n",
+ voiceNum, pVoice->channel); */ }
+
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "\to channel %d note: %d velocity: %d\n",
+ pVoice->nextChannel, pVoice->nextNote, pVoice->nextVelocity); */ }
+#endif
+
+ /* make sure new channel hasn't been muted by SP-MIDI since the voice was stolen */
+ if ((pSynth->synthFlags & SYNTH_FLAG_SP_MIDI_ON) &&
+ (pMIDIChannel->channelFlags & CHANNEL_FLAG_MUTE))
+ {
+ VMFreeVoice(pVoiceMgr, pSynth, &pVoiceMgr->voices[voiceNum]);
+ return EAS_FALSE;
+ }
+
+ /* if assigned to a new synth, correct the active voice count */
+ if (pVoice->channel != pVoice->nextChannel)
+ {
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMRetargetStolenVoice: Note assigned to different virtual synth, adjusting numActiveVoices\n"); */ }
+#endif
+ pSynth->numActiveVoices--;
+ pNextSynth->numActiveVoices++;
+ }
+
+ /* assign new channel number, and increase channel voice count */
+ pVoice->channel = pVoice->nextChannel;
+ pMIDIChannel = &pNextSynth->channels[pVoice->channel & 15];
+
+ /* assign other data */
+ pVoice->note = pVoice->nextNote;
+ pVoice->velocity = pVoice->nextVelocity;
+ pVoice->nextChannel = UNASSIGNED_SYNTH_CHANNEL;
+ pVoice->regionIndex = pVoice->nextRegionIndex;
+
+ /* save the flags, pfStartVoice() will clear them */
+ flags = pVoice->voiceFlags;
+
+ /* keep track of the note-start related workload */
+ pVoiceMgr->workload += WORKLOAD_AMOUNT_START_NOTE;
+
+ /* setup the voice parameters */
+ pVoice->voiceState = eVoiceStateStart;
+
+ /*lint -e{522} return not used at this time */
+ GetSynthPtr(voiceNum)->pfStartVoice(pVoiceMgr, pNextSynth, &pVoiceMgr->voices[voiceNum], GetAdjustedVoiceNum(voiceNum), pVoice->regionIndex);
+
+ /* did the new note already receive a MIDI note-off request? */
+ if (flags & VOICE_FLAG_DEFER_MIDI_NOTE_OFF)
+ {
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMRetargetVoice: stolen note note-off request deferred\n"); */ }
+#endif
+ pVoice->voiceFlags |= VOICE_FLAG_DEFER_MIDI_NOTE_OFF;
+ pNextSynth->synthFlags |= SYNTH_FLAG_DEFERRED_MIDI_NOTE_OFF_PENDING;
+ }
+
+ return EAS_TRUE;
+}
+
+/*----------------------------------------------------------------------------
+ * VMCheckKeyGroup()
+ *----------------------------------------------------------------------------
+ * If the note that we've been asked to start is in the same key group as
+ * any currently playing notes, then we must shut down the currently playing
+ * note in the same key group
+ *----------------------------------------------------------------------------
+*/
+void VMCheckKeyGroup (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U16 keyGroup, EAS_U8 channel)
+{
+ const S_REGION *pRegion;
+ EAS_INT voiceNum;
+
+ /* increment frame workload */
+ pVoiceMgr->workload += WORKLOAD_AMOUNT_KEY_GROUP;
+
+ /* need to check all voices in case this is a layered sound */
+ channel = VSynthToChannel(pSynth, channel);
+ for (voiceNum = 0; voiceNum < MAX_SYNTH_VOICES; voiceNum++)
+ {
+ if (pVoiceMgr->voices[voiceNum].voiceState != eVoiceStateStolen)
+ {
+ /* voice must be on the same channel */
+ if (channel == pVoiceMgr->voices[voiceNum].channel)
+ {
+ /* check key group */
+ pRegion = GetRegionPtr(pSynth, pVoiceMgr->voices[voiceNum].regionIndex);
+ if (keyGroup == (pRegion->keyGroupAndFlags & 0x0f00))
+ {
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMCheckKeyGroup: voice %d matches key group %d\n", voiceNum, keyGroup >> 8); */ }
+#endif
+
+ /* if this voice was just started, set it to mute on the next buffer */
+ if (pVoiceMgr->voices[voiceNum].voiceFlags & VOICE_FLAG_NO_SAMPLES_SYNTHESIZED_YET)
+ pVoiceMgr->voices[voiceNum].voiceFlags |= VOICE_FLAG_DEFER_MUTE;
+
+ /* mute immediately */
+ else
+ VMMuteVoice(pVoiceMgr, voiceNum);
+ }
+ }
+ }
+
+ /* for stolen voice, check new values */
+ else
+ {
+ /* voice must be on the same channel */
+ if (channel == pVoiceMgr->voices[voiceNum].nextChannel)
+ {
+ /* check key group */
+ pRegion = GetRegionPtr(pSynth, pVoiceMgr->voices[voiceNum].nextRegionIndex);
+ if (keyGroup == (pRegion->keyGroupAndFlags & 0x0f00))
+ {
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMCheckKeyGroup: voice %d matches key group %d\n", voiceNum, keyGroup >> 8); */ }
+#endif
+
+ /* if this voice was just started, set it to mute on the next buffer */
+ if (pVoiceMgr->voices[voiceNum].voiceFlags & VOICE_FLAG_NO_SAMPLES_SYNTHESIZED_YET)
+ pVoiceMgr->voices[voiceNum].voiceFlags |= VOICE_FLAG_DEFER_MUTE;
+
+ /* mute immediately */
+ else
+ VMMuteVoice(pVoiceMgr, voiceNum);
+ }
+ }
+
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------
+ * VMCheckPolyphonyLimiting()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * We only play at most 2 of the same note on a MIDI channel.
+ * E.g., if we are asked to start note 36, and there are already two voices
+ * that are playing note 36, then we must steal the voice playing
+ * the oldest note 36 and use that stolen voice to play the new note 36.
+ *
+ * Inputs:
+ * nChannel - synth channel that wants to start a new note
+ * nKeyNumber - new note's midi note number
+ * nNoteVelocity - new note's velocity
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ * pbVoiceStealingRequired - flag: this routine sets true if we needed to
+ * steal a voice
+ * *
+ * Side Effects:
+ * psSynthObject->m_sVoice[free voice num].m_nKeyNumber may be assigned
+ * psSynthObject->m_sVoice[free voice num].m_nVelocity may be assigned
+ *----------------------------------------------------------------------------
+*/
+EAS_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)
+{
+ EAS_INT voiceNum;
+ EAS_INT oldestVoiceNum;
+ EAS_INT numVoicesPlayingNote;
+ EAS_U16 age;
+ EAS_U16 oldestNoteAge;
+
+ pVoiceMgr->workload += WORKLOAD_AMOUNT_POLY_LIMIT;
+
+ numVoicesPlayingNote = 0;
+ oldestVoiceNum = MAX_SYNTH_VOICES;
+ oldestNoteAge = 0;
+ channel = VSynthToChannel(pSynth, channel);
+
+ /* examine each voice on this channel playing this note */
+ for (voiceNum = lowVoice; voiceNum <= highVoice; voiceNum++)
+ {
+ /* check stolen notes separately */
+ if (pVoiceMgr->voices[voiceNum].voiceState != eVoiceStateStolen)
+ {
+
+ /* same channel and note ? */
+ if ((channel == pVoiceMgr->voices[voiceNum].channel) && (note == pVoiceMgr->voices[voiceNum].note))
+ {
+ numVoicesPlayingNote++;
+ age = pVoiceMgr->age - pVoiceMgr->voices[voiceNum].age;
+
+ /* is this the oldest voice for this note? */
+ if (age >= oldestNoteAge)
+ {
+ oldestNoteAge = age;
+ oldestVoiceNum = voiceNum;
+ }
+ }
+ }
+
+ /* handle stolen voices */
+ else
+ {
+ /* same channel and note ? */
+ if ((channel == pVoiceMgr->voices[voiceNum].nextChannel) && (note == pVoiceMgr->voices[voiceNum].nextNote))
+ {
+ numVoicesPlayingNote++;
+ }
+ }
+ }
+
+ /* check to see if we exceeded poly limit */
+ if (numVoicesPlayingNote < DEFAULT_CHANNEL_POLYPHONY_LIMIT)
+ return EAS_FALSE;
+
+ /* make sure we have a voice to steal */
+ if (oldestVoiceNum != MAX_SYNTH_VOICES)
+ {
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMCheckPolyphonyLimiting: voice %d has the oldest note\n", oldestVoiceNum); */ }
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "VMCheckPolyphonyLimiting: polyphony limiting requires shutting down note %d \n", pVoiceMgr->voices[oldestVoiceNum].note); */ }
+#endif
+ VMStolenVoice(pVoiceMgr, pSynth, oldestVoiceNum, channel, note, velocity, regionIndex);
+ return EAS_TRUE;
+ }
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "VMCheckPolyphonyLimiting: No oldest voice to steal\n"); */ }
+#endif
+ return EAS_FALSE;
+}
+
+/*----------------------------------------------------------------------------
+ * VMStartVoice()
+ *----------------------------------------------------------------------------
+ * Starts a voice given a region index
+ *----------------------------------------------------------------------------
+*/
+void VMStartVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 note, EAS_U8 velocity, EAS_U16 regionIndex)
+{
+ const S_REGION *pRegion;
+ S_SYNTH_CHANNEL *pChannel;
+ EAS_INT voiceNum;
+ EAS_INT maxSynthPoly;
+ EAS_I32 lowVoice, highVoice;
+ EAS_U16 keyGroup;
+
+ pChannel = &pSynth->channels[channel];
+ pRegion = GetRegionPtr(pSynth, regionIndex);
+
+ /* select correct synth */
+#if defined(_SECONDARY_SYNTH) || defined(EAS_SPLIT_WT_SYNTH)
+ {
+#ifdef EAS_SPLIT_WT_SYNTH
+ if ((pRegion->keyGroupAndFlags & REGION_FLAG_OFF_CHIP) == 0)
+#else
+ if ((regionIndex & FLAG_RGN_IDX_FM_SYNTH) == 0)
+#endif
+ {
+ lowVoice = 0;
+ highVoice = NUM_PRIMARY_VOICES - 1;
+ }
+ else
+ {
+ lowVoice = NUM_PRIMARY_VOICES;
+ highVoice = MAX_SYNTH_VOICES - 1;
+ }
+ }
+#else
+ lowVoice = 0;
+ highVoice = MAX_SYNTH_VOICES - 1;
+#endif
+
+ /* keep track of the note-start related workload */
+ pVoiceMgr->workload+= WORKLOAD_AMOUNT_START_NOTE;
+
+ /* other voices in pool, check for key group and poly limiting */
+ if (pSynth->poolCount[pChannel->pool] != 0)
+ {
+
+ /* check for key group exclusivity */
+ keyGroup = pRegion->keyGroupAndFlags & 0x0f00;
+ if (keyGroup!= 0)
+ VMCheckKeyGroup(pVoiceMgr, pSynth, keyGroup, channel);
+
+ /* check polyphony limit and steal a voice if necessary */
+ if ((pRegion->keyGroupAndFlags & REGION_FLAG_NON_SELF_EXCLUSIVE) == 0)
+ {
+ if (VMCheckPolyphonyLimiting(pVoiceMgr, pSynth, channel, note, velocity, regionIndex, lowVoice, highVoice) == EAS_TRUE)
+ return;
+ }
+ }
+
+ /* check max poly allocation */
+ if ((pSynth->maxPolyphony == 0) || (pVoiceMgr->maxPolyphony < pSynth->maxPolyphony))
+ maxSynthPoly = pVoiceMgr->maxPolyphony;
+ else
+ maxSynthPoly = pSynth->maxPolyphony;
+
+ /* any free voices? */
+ if ((pVoiceMgr->activeVoices < pVoiceMgr->maxPolyphony) &&
+ (pSynth->numActiveVoices < maxSynthPoly) &&
+ (EAS_SUCCESS == VMFindAvailableVoice(pVoiceMgr, &voiceNum, lowVoice, highVoice)))
+ {
+ S_SYNTH_VOICE *pVoice = &pVoiceMgr->voices[voiceNum];
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "VMStartVoice: Synth=%d\n", pSynth->vSynthNum); */ }
+#endif
+
+ /* bump voice counts */
+ pVoiceMgr->activeVoices++;
+ pSynth->numActiveVoices++;
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMStartVoice: voice %d assigned to channel %d note %d velocity %d\n",
+ voiceNum, channel, note, velocity); */ }
+#endif
+
+ /* save parameters */
+ pVoiceMgr->voices[voiceNum].channel = VSynthToChannel(pSynth, channel);
+ pVoiceMgr->voices[voiceNum].note = note;
+ pVoiceMgr->voices[voiceNum].velocity = velocity;
+
+ /* establish note age for voice stealing */
+ pVoiceMgr->voices[voiceNum].age = pVoiceMgr->age++;
+
+ /* setup the synthesis parameters */
+ pVoiceMgr->voices[voiceNum].voiceState = eVoiceStateStart;
+
+ /* increment voice pool count */
+ IncVoicePoolCount(pVoiceMgr, pVoice);
+
+ /* start voice on correct synth */
+ /*lint -e{522} return not used at this time */
+ GetSynthPtr(voiceNum)->pfStartVoice(pVoiceMgr, pSynth, &pVoiceMgr->voices[voiceNum], GetAdjustedVoiceNum(voiceNum), regionIndex);
+ return;
+ }
+
+ /* no free voices, we have to steal one using appropriate algorithm */
+ if (VMStealVoice(pVoiceMgr, pSynth, &voiceNum, channel, note, lowVoice, highVoice) == EAS_SUCCESS)
+ VMStolenVoice(pVoiceMgr, pSynth, voiceNum, channel, note, velocity, regionIndex);
+
+#ifdef _DEBUG_VM
+ else
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMStartVoice: Could not steal a voice for channel %d note %d velocity %d\n",
+ channel, note, velocity); */ }
+ }
+#endif
+
+ return;
+}
+
+/*----------------------------------------------------------------------------
+ * VMStartNote()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Update the synth's state to play the requested note on the requested
+ * channel if possible.
+ *
+ * Inputs:
+ * nChannel - the channel to start a note on
+ * nKeyNumber - the key number to start a note for
+ * nNoteVelocity - the key velocity from this note
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ * Side Effects:
+ * psSynthObject->m_nNumActiveVoices may be incremented
+ * psSynthObject->m_sVoice[free voice num].m_nSynthChannel may be assigned
+ * psSynthObject->m_sVoice[free voice num].m_nKeyNumber is assigned
+ * psSynthObject->m_sVoice[free voice num].m_nVelocity is assigned
+ *----------------------------------------------------------------------------
+*/
+void VMStartNote (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 note, EAS_U8 velocity)
+{
+ S_SYNTH_CHANNEL *pChannel;
+ EAS_U16 regionIndex;
+ EAS_I16 adjustedNote;
+
+ /* bump note count */
+ pSynth->totalNoteCount++;
+
+ pChannel = &pSynth->channels[channel];
+
+ /* check channel mute */
+ if (pChannel->channelFlags & CHANNEL_FLAG_MUTE)
+ return;
+
+#ifdef EXTERNAL_AUDIO
+ /* pass event to external audio when requested */
+ if ((pChannel->channelFlags & CHANNEL_FLAG_EXTERNAL_AUDIO) && (pSynth->cbEventFunc != NULL))
+ {
+ S_EXT_AUDIO_EVENT event;
+ event.channel = channel;
+ event.note = note;
+ event.velocity = velocity;
+ event.noteOn = EAS_TRUE;
+ if (pSynth->cbEventFunc(pSynth->pExtAudioInstData, &event))
+ return;
+ }
+#endif
+
+ /* start search at first region */
+ regionIndex = pChannel->regionIndex;
+
+ /* handle transposition */
+ adjustedNote = note;
+ if (pChannel->channelFlags & CHANNEL_FLAG_RHYTHM_CHANNEL)
+ adjustedNote += pChannel->coarsePitch;
+ else
+ adjustedNote += pChannel->coarsePitch + pSynth->globalTranspose;
+
+ /* limit adjusted key number so it does not wraparound, over/underflow */
+ if (adjustedNote < 0)
+ {
+ adjustedNote = 0;
+ }
+ else if (adjustedNote > 127)
+ {
+ adjustedNote = 127;
+ }
+
+#if defined(DLS_SYNTHESIZER)
+ if (regionIndex & FLAG_RGN_IDX_DLS_SYNTH)
+ {
+ /* DLS voice */
+ for (;;)
+ {
+ /*lint -e{740,826} cast OK, we know this is actually a DLS region */
+ const S_DLS_REGION *pDLSRegion = (S_DLS_REGION*) GetRegionPtr(pSynth, regionIndex);
+
+ /* check key against this region's key and velocity range */
+ if (((adjustedNote >= pDLSRegion->wtRegion.region.rangeLow) && (adjustedNote <= pDLSRegion->wtRegion.region.rangeHigh)) &&
+ ((velocity >= pDLSRegion->velLow) && (velocity <= pDLSRegion->velHigh)))
+ {
+ VMStartVoice(pVoiceMgr, pSynth, channel, note, velocity, regionIndex);
+ }
+
+ /* last region in program? */
+ if (pDLSRegion->wtRegion.region.keyGroupAndFlags & REGION_FLAG_LAST_REGION)
+ break;
+
+ /* advance to next region */
+ regionIndex++;
+ }
+ }
+ else
+#endif
+
+ /* braces here for #if clause */
+ {
+ /* EAS voice */
+ for (;;)
+ {
+ const S_REGION *pRegion = GetRegionPtr(pSynth, regionIndex);
+
+ /* check key against this region's keyrange */
+ if ((adjustedNote >= pRegion->rangeLow) && (adjustedNote <= pRegion->rangeHigh))
+ {
+ VMStartVoice(pVoiceMgr, pSynth, channel, note, velocity, regionIndex);
+ break;
+ }
+
+ /* last region in program? */
+ if (pRegion->keyGroupAndFlags & REGION_FLAG_LAST_REGION)
+ break;
+
+ /* advance to next region */
+ regionIndex++;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------
+ * VMStopNote()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Update the synth's state to end the requested note on the requested
+ * channel.
+ *
+ * Inputs:
+ * nChannel - the channel to stop a note on
+ * nKeyNumber - the key number for this note off
+ * nNoteVelocity - the note-off velocity
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ * Side Effects:
+ * psSynthObject->m_sVoice[free voice num].m_nSynthChannel may be assigned
+ * psSynthObject->m_sVoice[free voice num].m_nKeyNumber is assigned
+ * psSynthObject->m_sVoice[free voice num].m_nVelocity is assigned
+ *----------------------------------------------------------------------------
+*/
+/*lint -esym(715, velocity) reserved for future use */
+void VMStopNote (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 note, EAS_U8 velocity)
+{
+ S_SYNTH_CHANNEL *pChannel;
+ EAS_INT voiceNum;
+
+ pChannel = &(pSynth->channels[channel]);
+
+#ifdef EXTERNAL_AUDIO
+ if ((pChannel->channelFlags & CHANNEL_FLAG_EXTERNAL_AUDIO) && (pSynth->cbEventFunc != NULL))
+ {
+ S_EXT_AUDIO_EVENT event;
+ event.channel = channel;
+ event.note = note;
+ event.velocity = velocity;
+ event.noteOn = EAS_FALSE;
+ if (pSynth->cbEventFunc(pSynth->pExtAudioInstData, &event))
+ return;
+ }
+#endif
+
+ /* keep track of the note-start workload */
+ pVoiceMgr->workload += WORKLOAD_AMOUNT_STOP_NOTE;
+
+ channel = VSynthToChannel(pSynth, channel);
+
+ for (voiceNum=0; voiceNum < MAX_SYNTH_VOICES; voiceNum++)
+ {
+
+ /* stolen notes are handled separately */
+ if (eVoiceStateStolen != pVoiceMgr->voices[voiceNum].voiceState)
+ {
+
+ /* channel and key number must match */
+ if ((channel == pVoiceMgr->voices[voiceNum].channel) && (note == pVoiceMgr->voices[voiceNum].note))
+ {
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMStopNote: voice %d channel %d note %d\n",
+ voiceNum, channel, note); */ }
+#endif
+
+ /* if sustain pedal is down, set deferred note-off flag */
+ if (pChannel->channelFlags & CHANNEL_FLAG_SUSTAIN_PEDAL)
+ {
+ pVoiceMgr->voices[voiceNum].voiceFlags |= VOICE_FLAG_SUSTAIN_PEDAL_DEFER_NOTE_OFF;
+ continue;
+ }
+
+ /* if this note just started, wait before we stop it */
+ if (pVoiceMgr->voices[voiceNum].voiceFlags & VOICE_FLAG_NO_SAMPLES_SYNTHESIZED_YET)
+ {
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "\tDeferred: Not started yet\n"); */ }
+#endif
+ pVoiceMgr->voices[voiceNum].voiceFlags |= VOICE_FLAG_DEFER_MIDI_NOTE_OFF;
+ pSynth->synthFlags |= SYNTH_FLAG_DEFERRED_MIDI_NOTE_OFF_PENDING;
+ }
+
+ /* release voice */
+ else
+ VMReleaseVoice(pVoiceMgr, pSynth, voiceNum);
+
+ }
+ }
+
+ /* process stolen notes, new channel and key number must match */
+ else if ((channel == pVoiceMgr->voices[voiceNum].nextChannel) && (note == pVoiceMgr->voices[voiceNum].nextNote))
+ {
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMStopNote: voice %d channel %d note %d\n\tDeferred: Stolen voice\n",
+ voiceNum, channel, note); */ }
+#endif
+ pVoiceMgr->voices[voiceNum].voiceFlags |= VOICE_FLAG_DEFER_MIDI_NOTE_OFF;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------
+ * VMFindAvailableVoice()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Find an available voice and return the voice number if available.
+ *
+ * Inputs:
+ * pnVoiceNumber - really an output, returns the voice number found
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ * success - if there is an available voice
+ * failure - otherwise
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT VMFindAvailableVoice (S_VOICE_MGR *pVoiceMgr, EAS_INT *pVoiceNumber, EAS_I32 lowVoice, EAS_I32 highVoice)
+{
+ EAS_INT voiceNum;
+
+ /* Check each voice to see if it has been assigned to a synth channel */
+ for (voiceNum = lowVoice; voiceNum <= highVoice; voiceNum++)
+ {
+ /* check if this voice has been assigned to a synth channel */
+ if ( pVoiceMgr->voices[voiceNum].voiceState == eVoiceStateFree)
+ {
+ *pVoiceNumber = voiceNum; /* this voice is available */
+ return EAS_SUCCESS;
+ }
+ }
+
+ /* if we reach here, we have not found a free voice */
+ *pVoiceNumber = UNASSIGNED_SYNTH_VOICE;
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMFindAvailableVoice: error, could not find an available voice\n"); */ }
+#endif
+ return EAS_FAILURE;
+}
+
+/*----------------------------------------------------------------------------
+ * VMStealVoice()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Steal a voice and return the voice number
+ *
+ * Stealing algorithm: steal the best choice with minimal work, taking into
+ * account SP-Midi channel priorities and polyphony allocation.
+ *
+ * In one pass through all the voices, figure out which voice to steal
+ * taking into account a number of different factors:
+ * Priority of the voice's MIDI channel
+ * Number of voices over the polyphony allocation for voice's MIDI channel
+ * Amplitude of the voice
+ * Note age
+ * Key velocity (for voices that haven't been started yet)
+ * If any matching notes are found
+ *
+ * Inputs:
+ * pnVoiceNumber - really an output, see below
+ * nChannel - the channel that this voice wants to be started on
+ * nKeyNumber - the key number for this new voice
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ * pnVoiceNumber - voice number of the voice that was stolen
+ * EAS_RESULT EAS_SUCCESS - always successful
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT VMStealVoice (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_INT *pVoiceNumber, EAS_U8 channel, EAS_U8 note, EAS_I32 lowVoice, EAS_I32 highVoice)
+{
+ S_SYNTH_VOICE *pCurrVoice;
+ S_SYNTH *pCurrSynth;
+ EAS_INT voiceNum;
+ EAS_INT bestCandidate;
+ EAS_U8 currChannel;
+ EAS_U8 currNote;
+ EAS_I32 bestPriority;
+ EAS_I32 currentPriority;
+
+ /* determine which voice to steal */
+ bestPriority = 0;
+ bestCandidate = MAX_SYNTH_VOICES;
+
+ for (voiceNum = lowVoice; voiceNum <= highVoice; voiceNum++)
+ {
+ pCurrVoice = &pVoiceMgr->voices[voiceNum];
+
+ /* ignore free voices */
+ if (pCurrVoice->voiceState == eVoiceStateFree)
+ continue;
+
+ /* for stolen voices, use the new parameters, not the old */
+ if (pCurrVoice->voiceState == eVoiceStateStolen)
+ {
+ pCurrSynth = pVoiceMgr->pSynth[GET_VSYNTH(pCurrVoice->nextChannel)];
+ currChannel = pCurrVoice->nextChannel;
+ currNote = pCurrVoice->nextNote;
+ }
+ else
+ {
+ pCurrSynth = pVoiceMgr->pSynth[GET_VSYNTH(pCurrVoice->channel)];
+ currChannel = pCurrVoice->channel;
+ currNote = pCurrVoice->note;
+ }
+
+ /* ignore voices that are higher priority */
+ if (pSynth->priority > pCurrSynth->priority)
+ continue;
+#ifdef _DEBUG_VM
+// { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMStealVoice: New priority = %d exceeds old priority = %d\n", pSynth->priority, pCurrSynth->priority); */ }
+#endif
+
+ /* if voice is stolen or just started, reduce the likelihood it will be stolen */
+ if (( pCurrVoice->voiceState == eVoiceStateStolen) || (pCurrVoice->voiceFlags & VOICE_FLAG_NO_SAMPLES_SYNTHESIZED_YET))
+ {
+ currentPriority = 128 - pCurrVoice->nextVelocity;
+ }
+ else
+ {
+ /* compute the priority of this voice, higher means better for stealing */
+ /* use not age */
+ currentPriority = (EAS_I32) pCurrVoice->age << NOTE_AGE_STEAL_WEIGHT;
+
+ /* include note gain -higher gain is lower steal value */
+ /*lint -e{704} use shift for performance */
+ currentPriority += ((32768 >> (12 - NOTE_GAIN_STEAL_WEIGHT)) + 256) -
+ ((EAS_I32) pCurrVoice->gain >> (12 - NOTE_GAIN_STEAL_WEIGHT));
+ }
+
+ /* in SP-MIDI mode, include over poly allocation and channel priority */
+ if (pSynth->synthFlags & SYNTH_FLAG_SP_MIDI_ON)
+ {
+ S_SYNTH_CHANNEL *pChannel = &pCurrSynth->channels[GET_CHANNEL(currChannel)];
+ /*lint -e{701} use shift for performance */
+ if (pSynth->poolCount[pChannel->pool] >= pSynth->poolAlloc[pChannel->pool])
+ currentPriority += (pSynth->poolCount[pChannel->pool] -pSynth->poolAlloc[pChannel->pool] + 1) << CHANNEL_POLY_STEAL_WEIGHT;
+
+ /* include channel priority */
+ currentPriority += (EAS_I32)(pChannel->pool << CHANNEL_PRIORITY_STEAL_WEIGHT);
+ }
+
+ /* if a note is already playing that matches this note, consider stealing it more readily */
+ if ((note == currNote) && (channel == currChannel))
+ currentPriority += NOTE_MATCH_PENALTY;
+
+ /* is this the best choice so far? */
+ if (currentPriority >= bestPriority)
+ {
+ bestPriority = currentPriority;
+ bestCandidate = voiceNum;
+ }
+ }
+
+ /* may happen if all voices are allocated to a higher priority virtual synth */
+ if (bestCandidate == MAX_SYNTH_VOICES)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMStealVoice: Unable to allocate a voice\n"); */ }
+ return EAS_ERROR_NO_VOICE_ALLOCATED;
+ }
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMStealVoice: Voice %d stolen\n", bestCandidate); */ }
+
+ /* are we stealing a stolen voice? */
+ if (pVoiceMgr->voices[bestCandidate].voiceState == eVoiceStateStolen)
+ {
+ { /* 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",
+ bestCandidate,
+ pVoiceMgr->voices[bestCandidate].nextChannel,
+ pVoiceMgr->voices[bestCandidate].nextNote,
+ pVoiceMgr->voices[bestCandidate].nextVelocity); */ }
+ }
+#endif
+
+ *pVoiceNumber = (EAS_U16) bestCandidate;
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * VMChannelPressure()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Change the channel pressure for the given channel
+ *
+ * Inputs:
+ * nChannel - the MIDI channel
+ * nVelocity - the channel pressure value
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ * Side Effects:
+ * psSynthObject->m_sChannel[nChannel].m_nChannelPressure is updated
+ *----------------------------------------------------------------------------
+*/
+void VMChannelPressure (S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 value)
+{
+ S_SYNTH_CHANNEL *pChannel;
+
+ pChannel = &(pSynth->channels[channel]);
+ pChannel->channelPressure = value;
+
+ /*
+ set a channel flag to request parameter updates
+ for all the voices associated with this channel
+ */
+ pChannel->channelFlags |= CHANNEL_FLAG_UPDATE_CHANNEL_PARAMETERS;
+}
+
+/*----------------------------------------------------------------------------
+ * VMPitchBend()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Change the pitch wheel value for the given channel.
+ * This routine constructs the proper 14-bit argument when the calling routine
+ * passes the pitch LSB and MSB.
+ *
+ * Note: some midi disassemblers display a bipolar pitch bend value.
+ * We can display the bipolar value using
+ * if m_nPitchBend >= 0x2000
+ * bipolar pitch bend = postive (m_nPitchBend - 0x2000)
+ * else
+ * bipolar pitch bend = negative (0x2000 - m_nPitchBend)
+ *
+ * Inputs:
+ * nChannel - the MIDI channel
+ * nPitchLSB - the LSB byte of the pitch bend message
+ * nPitchMSB - the MSB byte of the pitch bend message
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ * Side Effects:
+ * psSynthObject->m_sChannel[nChannel].m_nPitchBend is changed
+ *
+ *----------------------------------------------------------------------------
+*/
+void VMPitchBend (S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 nPitchLSB, EAS_U8 nPitchMSB)
+{
+ S_SYNTH_CHANNEL *pChannel;
+
+ pChannel = &(pSynth->channels[channel]);
+ pChannel->pitchBend = (EAS_I16) ((nPitchMSB << 7) | nPitchLSB);
+
+ /*
+ set a channel flag to request parameter updates
+ for all the voices associated with this channel
+ */
+ pChannel->channelFlags |= CHANNEL_FLAG_UPDATE_CHANNEL_PARAMETERS;
+}
+
+/*----------------------------------------------------------------------------
+ * VMControlChange()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Change the controller (or mode) for the given channel.
+ *
+ * Inputs:
+ * nChannel - the MIDI channel
+ * nControllerNumber - the MIDI controller number
+ * nControlValue - the value for this controller message
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ * Side Effects:
+ * psSynthObject->m_sChannel[nChannel] controller is changed
+ *
+ *----------------------------------------------------------------------------
+*/
+void VMControlChange (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 controller, EAS_U8 value)
+{
+ S_SYNTH_CHANNEL *pChannel;
+
+ pChannel = &(pSynth->channels[channel]);
+
+ /*
+ set a channel flag to request parameter updates
+ for all the voices associated with this channel
+ */
+ pChannel->channelFlags |= CHANNEL_FLAG_UPDATE_CHANNEL_PARAMETERS;
+
+ switch ( controller )
+ {
+ case MIDI_CONTROLLER_BANK_SELECT_MSB:
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMControlChange: Bank Select MSB: msb 0x%X\n", value); */ }
+#endif
+ /* use this MSB with a zero LSB, until we get an LSB message */
+ pChannel->bankNum = value << 8;
+ break;
+
+ case MIDI_CONTROLLER_MOD_WHEEL:
+ /* we treat mod wheel as a 7-bit controller and only use the MSB */
+ pChannel->modWheel = value;
+ break;
+
+ case MIDI_CONTROLLER_VOLUME:
+ /* we treat volume as a 7-bit controller and only use the MSB */
+ pChannel->volume = value;
+ break;
+
+ case MIDI_CONTROLLER_PAN:
+ /* we treat pan as a 7-bit controller and only use the MSB */
+ pChannel->pan = value;
+ break;
+
+ case MIDI_CONTROLLER_EXPRESSION:
+ /* we treat expression as a 7-bit controller and only use the MSB */
+ pChannel->expression = value;
+ break;
+
+ case MIDI_CONTROLLER_BANK_SELECT_LSB:
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMControlChange: Bank Select LSB: lsb 0x%X\n", value); */ }
+#endif
+ /*
+ construct bank number as 7-bits (stored as 8) of existing MSB
+ and 7-bits of new LSB (also stored as 8(
+ */
+ pChannel->bankNum =
+ (pChannel->bankNum & 0xFF00) | value;
+
+ break;
+
+ case MIDI_CONTROLLER_SUSTAIN_PEDAL:
+ /* we treat sustain pedal as a boolean on/off bit flag */
+ if (value < 64)
+ {
+ /*
+ we are requested to turn the pedal off, but first check
+ if the pedal is already on
+ */
+ if (0 !=
+ (pChannel->channelFlags & CHANNEL_FLAG_SUSTAIN_PEDAL)
+ )
+ {
+ /*
+ The sustain flag is presently set and the damper pedal is on.
+ We are therefore transitioning from damper pedal ON to
+ damper pedal OFF. This means all notes in this channel
+ that received a note off while the damper pedal was on, and
+ had their note-off requests deferred, should now proceed to
+ the release state.
+ */
+ VMReleaseAllDeferredNoteOffs(pVoiceMgr, pSynth, channel);
+ } /* end if sustain pedal is already on */
+
+ /* turn the sustain pedal off */
+ pChannel->channelFlags &= ~CHANNEL_FLAG_SUSTAIN_PEDAL;
+ }
+ else
+ {
+ /*
+ we are requested to turn the pedal on, but first check
+ if the pedal is already off
+ */
+ if (0 ==
+ (pChannel->channelFlags & CHANNEL_FLAG_SUSTAIN_PEDAL)
+ )
+ {
+ /*
+ The sustain flag is presently clear and the damper pedal is off.
+ We are therefore transitioning from damper pedal OFF to
+ damper pedal ON. Currently sounding notes should be left
+ unchanged. However, we should try to "catch" notes if possible.
+ If any notes have levels >= sustain level, catch them,
+ otherwise, let them continue to release.
+ */
+ VMCatchNotesForSustainPedal(pVoiceMgr, pSynth, channel);
+ }
+
+ /* turn the sustain pedal on */
+ pChannel->channelFlags |= CHANNEL_FLAG_SUSTAIN_PEDAL;
+ }
+
+ break;
+#ifdef _REVERB
+ case MIDI_CONTROLLER_REVERB_SEND:
+ /* we treat send as a 7-bit controller and only use the MSB */
+ pSynth->channels[channel].reverbSend = value;
+ break;
+#endif
+#ifdef _CHORUS
+ case MIDI_CONTROLLER_CHORUS_SEND:
+ /* we treat send as a 7-bit controller and only use the MSB */
+ pSynth->channels[channel].chorusSend = value;
+ break;
+#endif
+ case MIDI_CONTROLLER_RESET_CONTROLLERS:
+ /* despite the Midi message name, not ALL controllers are reset */
+ pChannel->modWheel = DEFAULT_MOD_WHEEL;
+ pChannel->expression = DEFAULT_EXPRESSION;
+
+ /* turn the sustain pedal off as default/reset */
+ pChannel->channelFlags &= ~CHANNEL_FLAG_SUSTAIN_PEDAL;
+ pChannel->pitchBend = DEFAULT_PITCH_BEND;
+
+ /* reset channel pressure */
+ pChannel->channelPressure = DEFAULT_CHANNEL_PRESSURE;
+
+ /* reset RPN values */
+ pChannel->registeredParam = DEFAULT_REGISTERED_PARAM;
+ pChannel->pitchBendSensitivity = DEFAULT_PITCH_BEND_SENSITIVITY;
+ pChannel->finePitch = DEFAULT_FINE_PITCH;
+ pChannel->coarsePitch = DEFAULT_COARSE_PITCH;
+
+ /*
+ program change, bank select, channel volume CC7, pan CC10
+ are NOT reset
+ */
+ break;
+
+ /*
+ For logical reasons, the RPN data entry are grouped together.
+ However, keep in mind that these cases are not necessarily in
+ ascending order.
+ e.g., MIDI_CONTROLLER_DATA_ENTRY_MSB == 6,
+ whereas MIDI_CONTROLLER_SUSTAIN_PEDAL == 64.
+ So arrange these case statements in whatever manner is more efficient for
+ the processor / compiler.
+ */
+ case MIDI_CONTROLLER_ENTER_DATA_MSB:
+ case MIDI_CONTROLLER_ENTER_DATA_LSB:
+ case MIDI_CONTROLLER_SELECT_RPN_LSB:
+ case MIDI_CONTROLLER_SELECT_RPN_MSB:
+ case MIDI_CONTROLLER_SELECT_NRPN_MSB:
+ case MIDI_CONTROLLER_SELECT_NRPN_LSB:
+ VMUpdateRPNStateMachine(pSynth, channel, controller, value);
+ break;
+
+ case MIDI_CONTROLLER_ALL_SOUND_OFF:
+ case MIDI_CONTROLLER_ALL_NOTES_OFF:
+ case MIDI_CONTROLLER_OMNI_OFF:
+ case MIDI_CONTROLLER_OMNI_ON:
+ case MIDI_CONTROLLER_MONO_ON_POLY_OFF:
+ case MIDI_CONTROLLER_POLY_ON_MONO_OFF:
+ /* NOTE: we treat all sounds off the same as all notes off */
+ VMAllNotesOff(pVoiceMgr, pSynth, channel);
+ break;
+
+ default:
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMControlChange: controller %d not yet implemented\n", controller); */ }
+#endif
+ break;
+
+ }
+
+ return;
+}
+
+/*----------------------------------------------------------------------------
+ * VMUpdateRPNStateMachine()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Call this function when we want to parse RPN related controller messages.
+ * We only support RPN0 (pitch bend sensitivity), RPN1 (fine tuning) and
+ * RPN2 (coarse tuning). Any other RPNs or NRPNs are ignored for now.
+ *.
+ * Supports any order, so not a state machine anymore. This function was
+ * rewritten to work correctly regardless of order.
+ *
+ * Inputs:
+ * nChannel - the channel this controller message is coming from
+ * nControllerNumber - which RPN related controller
+ * nControlValue - the value of the RPN related controller
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ * returns EAS_RESULT, which is typically EAS_SUCCESS, since there are
+ * few possible errors
+ *
+ * Side Effects:
+ * gsSynthObject.m_sChannel[nChannel].m_nPitchBendSensitivity
+ * (or m_nFinePitch or m_nCoarsePitch)
+ * will be updated if the proper RPN message is received.
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT VMUpdateRPNStateMachine (S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 controller, EAS_U8 value)
+{
+ S_SYNTH_CHANNEL *pChannel;
+
+#ifdef _DEBUG_VM
+ if (channel >= NUM_SYNTH_CHANNELS)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMUpdateRPNStateMachines: error, %d invalid channel number\n",
+ channel); */ }
+ return EAS_FAILURE;
+ }
+#endif
+
+ pChannel = &(pSynth->channels[channel]);
+
+ switch (controller)
+ {
+ case MIDI_CONTROLLER_SELECT_NRPN_MSB:
+ case MIDI_CONTROLLER_SELECT_NRPN_LSB:
+ pChannel->registeredParam = DEFAULT_REGISTERED_PARAM;
+ break;
+ case MIDI_CONTROLLER_SELECT_RPN_MSB:
+ pChannel->registeredParam =
+ (pChannel->registeredParam & 0x7F) | (value<<7);
+ break;
+ case MIDI_CONTROLLER_SELECT_RPN_LSB:
+ pChannel->registeredParam =
+ (pChannel->registeredParam & 0x7F00) | value;
+ break;
+ case MIDI_CONTROLLER_ENTER_DATA_MSB:
+ switch (pChannel->registeredParam)
+ {
+ case 0:
+ pChannel->pitchBendSensitivity = value * 100;
+ break;
+ case 1:
+ /*lint -e{702} <avoid division for performance reasons>*/
+ pChannel->finePitch = (EAS_I8)((((value << 7) - 8192) * 100) >> 13);
+ break;
+ case 2:
+ pChannel->coarsePitch = (EAS_I8)(value - 64);
+ break;
+ default:
+ break;
+ }
+ break;
+ case MIDI_CONTROLLER_ENTER_DATA_LSB:
+ switch (pChannel->registeredParam)
+ {
+ case 0:
+ //ignore lsb
+ break;
+ case 1:
+ //ignore lsb
+ break;
+ case 2:
+ //ignore lsb
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ return EAS_FAILURE; //not a RPN related controller
+ }
+
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * VMUpdateStaticChannelParameters()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Update all of the static channel parameters for channels that have had
+ * a controller change values
+ * Or if the synth has signalled that all channels must forcibly
+ * be updated
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ * none
+ *
+ * Side Effects:
+ * - psSynthObject->m_sChannel[].m_nStaticGain and m_nStaticPitch
+ * are updated for channels whose controller values have changed
+ * or if the synth has signalled that all channels must forcibly
+ * be updated
+ *----------------------------------------------------------------------------
+*/
+void VMUpdateStaticChannelParameters (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth)
+{
+ EAS_INT channel;
+
+ if (pSynth->synthFlags & SYNTH_FLAG_UPDATE_ALL_CHANNEL_PARAMETERS)
+ {
+ /*
+ the synth wants us to forcibly update all channel
+ parameters. This event occurs when we are about to
+ finish resetting the synth
+ */
+ for (channel = 0; channel < NUM_SYNTH_CHANNELS; channel++)
+ {
+#ifdef _HYBRID_SYNTH
+ if (pSynth->channels[channel].regionIndex & FLAG_RGN_IDX_FM_SYNTH)
+ pSecondarySynth->pfUpdateChannel(pVoiceMgr, pSynth, (EAS_U8) channel);
+ else
+ pPrimarySynth->pfUpdateChannel(pVoiceMgr, pSynth, (EAS_U8) channel);
+#else
+ pPrimarySynth->pfUpdateChannel(pVoiceMgr, pSynth, (EAS_U8) channel);
+#endif
+ }
+
+ /*
+ clear the flag to indicates we have now forcibly
+ updated all channel parameters
+ */
+ pSynth->synthFlags &= ~SYNTH_FLAG_UPDATE_ALL_CHANNEL_PARAMETERS;
+ }
+ else
+ {
+
+ /* only update channel params if signalled by a channel flag */
+ for (channel = 0; channel < NUM_SYNTH_CHANNELS; channel++)
+ {
+ if ( 0 != (pSynth->channels[channel].channelFlags & CHANNEL_FLAG_UPDATE_CHANNEL_PARAMETERS))
+ {
+#ifdef _HYBRID_SYNTH
+ if (pSynth->channels[channel].regionIndex & FLAG_RGN_IDX_FM_SYNTH)
+ pSecondarySynth->pfUpdateChannel(pVoiceMgr, pSynth, (EAS_U8) channel);
+ else
+ pPrimarySynth->pfUpdateChannel(pVoiceMgr, pSynth, (EAS_U8) channel);
+#else
+ pPrimarySynth->pfUpdateChannel(pVoiceMgr, pSynth, (EAS_U8) channel);
+#endif
+ }
+ }
+
+ }
+
+ return;
+}
+
+/*----------------------------------------------------------------------------
+ * VMFindProgram()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Look up an individual program in sound library. This function
+ * searches the bank list for a program, then the individual program
+ * list.
+ *
+ * Inputs:
+ *
+ * Outputs:
+ *----------------------------------------------------------------------------
+*/
+static EAS_RESULT VMFindProgram (const S_EAS *pEAS, EAS_U32 bank, EAS_U8 programNum, EAS_U16 *pRegionIndex)
+{
+ EAS_U32 locale;
+ const S_PROGRAM *p;
+ EAS_U16 i;
+ EAS_U16 regionIndex;
+
+ /* make sure we have a valid sound library */
+ if (pEAS == NULL)
+ return EAS_FAILURE;
+
+ /* search the banks */
+ for (i = 0; i < pEAS->numBanks; i++)
+ {
+ if (bank == (EAS_U32) pEAS->pBanks[i].locale)
+ {
+ regionIndex = pEAS->pBanks[i].regionIndex[programNum];
+ if (regionIndex != INVALID_REGION_INDEX)
+ {
+ *pRegionIndex = regionIndex;
+ return EAS_SUCCESS;
+ }
+ break;
+ }
+ }
+
+ /* establish locale */
+ locale = ( bank << 8) | programNum;
+
+ /* search for program */
+ for (i = 0, p = pEAS->pPrograms; i < pEAS->numPrograms; i++, p++)
+ {
+ if (p->locale == locale)
+ {
+ *pRegionIndex = p->regionIndex;
+ return EAS_SUCCESS;
+ }
+ }
+
+ return EAS_FAILURE;
+}
+
+#ifdef DLS_SYNTHESIZER
+/*----------------------------------------------------------------------------
+ * VMFindDLSProgram()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Look up an individual program in sound library. This function
+ * searches the bank list for a program, then the individual program
+ * list.
+ *
+ * Inputs:
+ *
+ * Outputs:
+ *----------------------------------------------------------------------------
+*/
+static EAS_RESULT VMFindDLSProgram (const S_DLS *pDLS, EAS_U32 bank, EAS_U8 programNum, EAS_U16 *pRegionIndex)
+{
+ EAS_U32 locale;
+ const S_PROGRAM *p;
+ EAS_U16 i;
+
+ /* make sure we have a valid sound library */
+ if (pDLS == NULL)
+ return EAS_FAILURE;
+
+ /* establish locale */
+ locale = (bank << 8) | programNum;
+
+ /* search for program */
+ for (i = 0, p = pDLS->pDLSPrograms; i < pDLS->numDLSPrograms; i++, p++)
+ {
+ if (p->locale == locale)
+ {
+ *pRegionIndex = p->regionIndex;
+ return EAS_SUCCESS;
+ }
+ }
+
+ return EAS_FAILURE;
+}
+#endif
+
+/*----------------------------------------------------------------------------
+ * VMProgramChange()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Change the instrument (program) for the given channel.
+ *
+ * Depending on the program number, and the bank selected for this channel, the
+ * program may be in ROM, RAM (from SMAF or CMX related RAM wavetable), or
+ * Alternate wavetable (from mobile DLS or other DLS file)
+ *
+ * This function figures out what wavetable should be used, and sets it up as the
+ * wavetable to use for this channel. Also the channel may switch from a melodic
+ * channel to a rhythm channel, or vice versa.
+ *
+ * Inputs:
+ *
+ * Outputs:
+ * Side Effects:
+ * gsSynthObject.m_sChannel[nChannel].m_nProgramNumber is likely changed
+ * gsSynthObject.m_sChannel[nChannel].m_psEAS may be changed
+ * gsSynthObject.m_sChannel[nChannel].m_bRhythmChannel may be changed
+ *
+ *----------------------------------------------------------------------------
+*/
+/*lint -esym(715, pVoiceMgr) reserved for future use */
+void VMProgramChange (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_U8 channel, EAS_U8 program)
+{
+ S_SYNTH_CHANNEL *pChannel;
+ EAS_U32 bank;
+ EAS_U16 regionIndex;
+
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "VMProgramChange: vSynthNum=%d, channel=%d, program=%d\n", pSynth->vSynthNum, channel, program); */ }
+#endif
+
+ /* setup pointer to MIDI channel data */
+ pChannel = &pSynth->channels[channel];
+ bank = pChannel->bankNum;
+
+ /* allow channels to switch between being melodic or rhythm channels, using GM2 CC values */
+ if ((bank & 0xFF00) == DEFAULT_RHYTHM_BANK_NUMBER)
+ {
+ /* make it a rhythm channel */
+ pChannel->channelFlags |= CHANNEL_FLAG_RHYTHM_CHANNEL;
+ }
+ else if ((bank & 0xFF00) == DEFAULT_MELODY_BANK_NUMBER)
+ {
+ /* make it a melody channel */
+ pChannel->channelFlags &= ~CHANNEL_FLAG_RHYTHM_CHANNEL;
+ }
+
+ regionIndex = DEFAULT_REGION_INDEX;
+
+#ifdef EXTERNAL_AUDIO
+ /* give the external audio interface a chance to handle it */
+ if (pSynth->cbProgChgFunc != NULL)
+ {
+ S_EXT_AUDIO_PRG_CHG prgChg;
+ prgChg.channel = channel;
+ prgChg.bank = (EAS_U16) bank;
+ prgChg.program = program;
+ if (pSynth->cbProgChgFunc(pSynth->pExtAudioInstData, &prgChg))
+ pChannel->channelFlags |= CHANNEL_FLAG_EXTERNAL_AUDIO;
+ }
+
+#endif
+
+
+#ifdef DLS_SYNTHESIZER
+ /* first check for DLS program that may overlay the internal instrument */
+ if (VMFindDLSProgram(pSynth->pDLS, bank, program, &regionIndex) != EAS_SUCCESS)
+#endif
+
+ /* braces to support 'if' clause above */
+ {
+
+ /* look in the internal banks */
+ if (VMFindProgram(pSynth->pEAS, bank, program, &regionIndex) != EAS_SUCCESS)
+
+ /* fall back to default bank */
+ {
+ if (pSynth->channels[channel].channelFlags & CHANNEL_FLAG_RHYTHM_CHANNEL)
+ bank = DEFAULT_RHYTHM_BANK_NUMBER;
+ else
+ bank = DEFAULT_MELODY_BANK_NUMBER;
+
+ if (VMFindProgram(pSynth->pEAS, bank, program, &regionIndex) != EAS_SUCCESS)
+
+ /* switch to program 0 in the default bank */
+ {
+ if (VMFindProgram(pSynth->pEAS, bank, 0, &regionIndex) != EAS_SUCCESS)
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "VMProgramChange: No program @ %03d:%03d:%03d\n",
+ (bank >> 8) & 0x7f, bank & 0x7f, program); */ }
+ }
+ }
+ }
+
+ /* we have our new program change for this channel */
+ pChannel->programNum = program;
+ pChannel->regionIndex = regionIndex;
+
+ /*
+ set a channel flag to request parameter updates
+ for all the voices associated with this channel
+ */
+ pChannel->channelFlags |= CHANNEL_FLAG_UPDATE_CHANNEL_PARAMETERS;
+
+ return;
+}
+
+/*----------------------------------------------------------------------------
+ * VMAddSamples()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Synthesize the requested number of samples (block based processing)
+ *
+ * Inputs:
+ * nNumSamplesToAdd - number of samples to write to buffer
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ * number of voices rendered
+ *
+ * Side Effects:
+ * - samples are added to the presently free buffer
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_I32 VMAddSamples (S_VOICE_MGR *pVoiceMgr, EAS_I32 *pMixBuffer, EAS_I32 numSamples)
+{
+ S_SYNTH *pSynth;
+ EAS_INT voicesRendered;
+ EAS_INT voiceNum;
+ EAS_BOOL done;
+
+#ifdef _REVERB
+ EAS_PCM *pReverbSendBuffer;
+#endif // ifdef _REVERB
+
+#ifdef _CHORUS
+ EAS_PCM *pChorusSendBuffer;
+#endif // ifdef _CHORUS
+
+ voicesRendered = 0;
+ for (voiceNum = 0; voiceNum < MAX_SYNTH_VOICES; voiceNum++)
+ {
+
+ /* retarget stolen voices */
+ if ((pVoiceMgr->voices[voiceNum].voiceState == eVoiceStateStolen) && (pVoiceMgr->voices[voiceNum].gain <= 0))
+ VMRetargetStolenVoice(pVoiceMgr, voiceNum);
+
+ /* get pointer to virtual synth */
+ pSynth = pVoiceMgr->pSynth[pVoiceMgr->voices[voiceNum].channel >> 4];
+
+ /* synthesize active voices */
+ if (pVoiceMgr->voices[voiceNum].voiceState != eVoiceStateFree)
+ {
+ done = GetSynthPtr(voiceNum)->pfUpdateVoice(pVoiceMgr, pSynth, &pVoiceMgr->voices[voiceNum], GetAdjustedVoiceNum(voiceNum), pMixBuffer, numSamples);
+ voicesRendered++;
+
+ /* voice is finished */
+ if (done == EAS_TRUE)
+ {
+ /* set gain of stolen voice to zero so it will be restarted */
+ if (pVoiceMgr->voices[voiceNum].voiceState == eVoiceStateStolen)
+ pVoiceMgr->voices[voiceNum].gain = 0;
+
+ /* or return it to the free voice pool */
+ else
+ VMFreeVoice(pVoiceMgr, pSynth, &pVoiceMgr->voices[voiceNum]);
+ }
+
+ /* if this voice is scheduled to be muted, set the mute flag */
+ if (pVoiceMgr->voices[voiceNum].voiceFlags & VOICE_FLAG_DEFER_MUTE)
+ {
+ pVoiceMgr->voices[voiceNum].voiceFlags &= ~(VOICE_FLAG_DEFER_MUTE | VOICE_FLAG_DEFER_MIDI_NOTE_OFF);
+ VMMuteVoice(pVoiceMgr, voiceNum);
+ }
+
+ /* if voice just started, advance state to play */
+ if (pVoiceMgr->voices[voiceNum].voiceState == eVoiceStateStart)
+ pVoiceMgr->voices[voiceNum].voiceState = eVoiceStatePlay;
+ }
+ }
+
+ return voicesRendered;
+}
+
+/*----------------------------------------------------------------------------
+ * VMRender()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * This routine renders a frame of audio
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ * pVoicesRendered - number of voices rendered this frame
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT VMRender (S_VOICE_MGR *pVoiceMgr, EAS_I32 numSamples, EAS_I32 *pMixBuffer, EAS_I32 *pVoicesRendered)
+{
+ S_SYNTH *pSynth;
+ EAS_INT i;
+ EAS_INT channel;
+
+#ifdef _CHECKED_BUILD
+ SanityCheck(pVoiceMgr);
+#endif
+
+ /* update MIDI channel parameters */
+ *pVoicesRendered = 0;
+ for (i = 0; i < MAX_VIRTUAL_SYNTHESIZERS; i++)
+ {
+ if (pVoiceMgr->pSynth[i] != NULL)
+ VMUpdateStaticChannelParameters(pVoiceMgr, pVoiceMgr->pSynth[i]);
+ }
+
+ /* synthesize a buffer of audio */
+ *pVoicesRendered = VMAddSamples(pVoiceMgr, pMixBuffer, numSamples);
+
+ /*
+ * check for deferred note-off messages
+ * If flag is set, that means one or more voices are expecting deferred
+ * midi note-off messages because the midi note-on and corresponding midi
+ * note-off requests occurred during the same update interval. The goal
+ * is the defer the note-off request so that the note can at least start.
+ */
+ for (i = 0; i < MAX_VIRTUAL_SYNTHESIZERS; i++)
+ {
+ pSynth = pVoiceMgr->pSynth[i];
+
+ if (pSynth== NULL)
+ continue;
+
+ if (pSynth->synthFlags & SYNTH_FLAG_DEFERRED_MIDI_NOTE_OFF_PENDING)
+ VMDeferredStopNote(pVoiceMgr, pSynth);
+
+ /* check if we need to reset the synth */
+ if ((pSynth->synthFlags & SYNTH_FLAG_RESET_IS_REQUESTED) &&
+ (pSynth->numActiveVoices == 0))
+ {
+ /*
+ complete the process of resetting the synth now that
+ all voices have muted
+ */
+#ifdef _DEBUG_VM
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "VMAddSamples: complete the reset process\n"); */ }
+#endif
+
+ VMInitializeAllChannels(pVoiceMgr, pSynth);
+ VMInitializeAllVoices(pVoiceMgr, pSynth->vSynthNum);
+
+ /* clear the reset flag */
+ pSynth->synthFlags &= ~SYNTH_FLAG_RESET_IS_REQUESTED;
+ }
+
+ /* clear channel update flags */
+ for (channel = 0; channel < NUM_SYNTH_CHANNELS; channel++)
+ pSynth->channels[channel].channelFlags &= ~CHANNEL_FLAG_UPDATE_CHANNEL_PARAMETERS;
+
+ }
+
+#ifdef _CHECKED_BUILD
+ SanityCheck(pVoiceMgr);
+#endif
+
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * VMInitWorkload()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Clears the workload counter
+ *
+ * Inputs:
+ * pVoiceMgr - pointer to instance data
+ *
+ * Outputs:
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+void VMInitWorkload (S_VOICE_MGR *pVoiceMgr)
+{
+ pVoiceMgr->workload = 0;
+}
+
+/*----------------------------------------------------------------------------
+ * VMSetWorkload()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Sets the max workload for a single frame.
+ *
+ * Inputs:
+ * pVoiceMgr - pointer to instance data
+ *
+ * Outputs:
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+void VMSetWorkload (S_VOICE_MGR *pVoiceMgr, EAS_I32 maxWorkLoad)
+{
+ pVoiceMgr->maxWorkLoad = maxWorkLoad;
+}
+
+/*----------------------------------------------------------------------------
+ * VMCheckWorkload()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Checks to see if work load has been exceeded on this frame.
+ *
+ * Inputs:
+ * pVoiceMgr - pointer to instance data
+ *
+ * Outputs:
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_BOOL VMCheckWorkload (S_VOICE_MGR *pVoiceMgr)
+{
+ if (pVoiceMgr->maxWorkLoad > 0)
+ return (EAS_BOOL) (pVoiceMgr->workload >= pVoiceMgr->maxWorkLoad);
+ return EAS_FALSE;
+}
+
+/*----------------------------------------------------------------------------
+ * VMActiveVoices()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Returns the number of active voices in the synthesizer.
+ *
+ * Inputs:
+ * pEASData - pointer to instance data
+ *
+ * Outputs:
+ * Returns the number of active voices
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_I32 VMActiveVoices (S_SYNTH *pSynth)
+{
+ return pSynth->numActiveVoices;
+}
+
+/*----------------------------------------------------------------------------
+ * VMSetSynthPolyphony()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Set the synth to a new polyphony value. Value must be >= 1 and
+ * <= MAX_SYNTH_VOICES. This function will pin the polyphony at those limits
+ *
+ * Inputs:
+ * pVoiceMgr pointer to synthesizer data
+ * polyphonyCount desired polyphony count
+ * synth synthesizer number (0 = onboard, 1 = DSP)
+ *
+ * Outputs:
+ * Returns error code
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT VMSetSynthPolyphony (S_VOICE_MGR *pVoiceMgr, EAS_I32 synth, EAS_I32 polyphonyCount)
+{
+ EAS_INT i;
+ EAS_INT activeVoices;
+
+ /* lower limit */
+ if (polyphonyCount < 1)
+ polyphonyCount = 1;
+
+ /* split architecture */
+#if defined(_SECONDARY_SYNTH) || defined(EAS_SPLIT_WT_SYNTH)
+ if (synth == EAS_MCU_SYNTH)
+ {
+ if (polyphonyCount > NUM_PRIMARY_VOICES)
+ polyphonyCount = NUM_PRIMARY_VOICES;
+ if (pVoiceMgr->maxPolyphonyPrimary == polyphonyCount)
+ return EAS_SUCCESS;
+ pVoiceMgr->maxPolyphonyPrimary = (EAS_U16) polyphonyCount;
+ }
+ else if (synth == EAS_DSP_SYNTH)
+ {
+ if (polyphonyCount > NUM_SECONDARY_VOICES)
+ polyphonyCount = NUM_SECONDARY_VOICES;
+ if (pVoiceMgr->maxPolyphonySecondary == polyphonyCount)
+ return EAS_SUCCESS;
+ pVoiceMgr->maxPolyphonySecondary = (EAS_U16) polyphonyCount;
+ }
+ else
+ return EAS_ERROR_PARAMETER_RANGE;
+
+ /* setting for SP-MIDI */
+ pVoiceMgr->maxPolyphony = pVoiceMgr->maxPolyphonyPrimary + pVoiceMgr->maxPolyphonySecondary;
+
+ /* standard architecture */
+#else
+ if (synth != EAS_MCU_SYNTH)
+ return EAS_ERROR_PARAMETER_RANGE;
+
+ /* pin desired value to possible limits */
+ if (polyphonyCount > MAX_SYNTH_VOICES)
+ polyphonyCount = MAX_SYNTH_VOICES;
+
+ /* set polyphony, if value is different than current value */
+ if (pVoiceMgr->maxPolyphony == polyphonyCount)
+ return EAS_SUCCESS;
+
+ pVoiceMgr->maxPolyphony = (EAS_U16) polyphonyCount;
+#endif
+
+ /* if SPMIDI enabled, update channel masking based on new polyphony */
+ for (i = 0; i < MAX_VIRTUAL_SYNTHESIZERS; i++)
+ {
+ if (pVoiceMgr->pSynth[i])
+ {
+ if (pVoiceMgr->pSynth[i]->synthFlags & SYNTH_FLAG_SP_MIDI_ON)
+ VMMIPUpdateChannelMuting(pVoiceMgr, pVoiceMgr->pSynth[i]);
+ else
+ pVoiceMgr->pSynth[i]->poolAlloc[0] = (EAS_U8) polyphonyCount;
+ }
+ }
+
+ /* are we under polyphony limit? */
+ if (pVoiceMgr->activeVoices <= polyphonyCount)
+ return EAS_SUCCESS;
+
+ /* count the number of active voices */
+ activeVoices = 0;
+ for (i = 0; i < MAX_SYNTH_VOICES; i++)
+ {
+
+ /* is voice active? */
+ if ((pVoiceMgr->voices[i].voiceState != eVoiceStateFree) && (pVoiceMgr->voices[i].voiceState != eVoiceStateMuting))
+ activeVoices++;
+ }
+
+ /* we may have to mute voices to reach new target */
+ while (activeVoices > polyphonyCount)
+ {
+ S_SYNTH *pSynth;
+ S_SYNTH_VOICE *pVoice;
+ EAS_I32 currentPriority, bestPriority;
+ EAS_INT bestCandidate;
+
+ /* find the lowest priority voice */
+ bestPriority = bestCandidate = -1;
+ for (i = 0; i < MAX_SYNTH_VOICES; i++)
+ {
+
+ pVoice = &pVoiceMgr->voices[i];
+
+ /* ignore free and muting voices */
+ if ((pVoice->voiceState == eVoiceStateFree) || (pVoice->voiceState == eVoiceStateMuting))
+ continue;
+
+ pSynth = pVoiceMgr->pSynth[GET_VSYNTH(pVoice->channel)];
+
+ /* if voice is stolen or just started, reduce the likelihood it will be stolen */
+ if (( pVoice->voiceState == eVoiceStateStolen) || (pVoice->voiceFlags & VOICE_FLAG_NO_SAMPLES_SYNTHESIZED_YET))
+ {
+ /* include velocity */
+ currentPriority = 128 - pVoice->nextVelocity;
+
+ /* include channel priority */
+ currentPriority += pSynth->channels[GET_CHANNEL(pVoice->nextChannel)].pool << CHANNEL_PRIORITY_STEAL_WEIGHT;
+ }
+ else
+ {
+ /* include age */
+ currentPriority = (EAS_I32) pVoice->age << NOTE_AGE_STEAL_WEIGHT;
+
+ /* include note gain -higher gain is lower steal value */
+ /*lint -e{704} use shift for performance */
+ currentPriority += ((32768 >> (12 - NOTE_GAIN_STEAL_WEIGHT)) + 256) -
+ ((EAS_I32) pVoice->gain >> (12 - NOTE_GAIN_STEAL_WEIGHT));
+
+ /* include channel priority */
+ currentPriority += pSynth->channels[GET_CHANNEL(pVoice->channel)].pool << CHANNEL_PRIORITY_STEAL_WEIGHT;
+ }
+
+ /* include synth priority */
+ currentPriority += pSynth->priority << SYNTH_PRIORITY_WEIGHT;
+
+ /* is this the best choice so far? */
+ if (currentPriority > bestPriority)
+ {
+ bestPriority = currentPriority;
+ bestCandidate = i;
+ }
+ }
+
+ /* shutdown best candidate */
+ if (bestCandidate < 0)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "VMSetPolyphony: Unable to reduce polyphony\n"); */ }
+ break;
+ }
+
+ /* shut down this voice */
+ /*lint -e{771} pSynth is initialized if bestCandidate >= 0 */
+ VMMuteVoice(pVoiceMgr, bestCandidate);
+ activeVoices--;
+ }
+
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * VMGetSynthPolyphony()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Returns the current polyphony setting
+ *
+ * Inputs:
+ * pVoiceMgr pointer to synthesizer data
+ * synth synthesizer number (0 = onboard, 1 = DSP)
+ *
+ * Outputs:
+ * Returns actual polyphony value set, as pinned by limits
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT VMGetSynthPolyphony (S_VOICE_MGR *pVoiceMgr, EAS_I32 synth, EAS_I32 *pPolyphonyCount)
+{
+
+#if defined(_SECONDARY_SYNTH) || defined(EAS_SPLIT_WT_SYNTH)
+ if (synth == EAS_MCU_SYNTH)
+ *pPolyphonyCount = pVoiceMgr->maxPolyphonyPrimary;
+ else if (synth == EAS_DSP_SYNTH)
+ *pPolyphonyCount = pVoiceMgr->maxPolyphonySecondary;
+ else
+ return EAS_ERROR_PARAMETER_RANGE;
+#else
+ if (synth != EAS_MCU_SYNTH)
+ return EAS_ERROR_PARAMETER_RANGE;
+ *pPolyphonyCount = pVoiceMgr->maxPolyphony;
+#endif
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * VMSetPolyphony()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Set the virtual synth polyphony. 0 = no limit (i.e. can use
+ * all available voices).
+ *
+ * Inputs:
+ * pVoiceMgr pointer to synthesizer data
+ * polyphonyCount desired polyphony count
+ * pSynth pointer to virtual synth
+ *
+ * Outputs:
+ * Returns error code
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT VMSetPolyphony (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_I32 polyphonyCount)
+{
+ EAS_INT i;
+ EAS_INT activeVoices;
+
+ /* check limits */
+ if (polyphonyCount < 0)
+ return EAS_ERROR_PARAMETER_RANGE;
+
+ /* zero is max polyphony */
+ if ((polyphonyCount == 0) || (polyphonyCount > MAX_SYNTH_VOICES))
+ {
+ pSynth->maxPolyphony = 0;
+ return EAS_SUCCESS;
+ }
+
+ /* set new polyphony */
+ pSynth->maxPolyphony = (EAS_U16) polyphonyCount;
+
+ /* max polyphony is minimum of virtual synth and actual synth */
+ if (polyphonyCount > pVoiceMgr->maxPolyphony)
+ polyphonyCount = pVoiceMgr->maxPolyphony;
+
+ /* if SP-MIDI mode, update the channel muting */
+ if (pSynth->synthFlags & SYNTH_FLAG_SP_MIDI_ON)
+ VMMIPUpdateChannelMuting(pVoiceMgr, pSynth);
+ else
+ pSynth->poolAlloc[0] = (EAS_U8) polyphonyCount;
+
+ /* are we under polyphony limit? */
+ if (pSynth->numActiveVoices <= polyphonyCount)
+ return EAS_SUCCESS;
+
+ /* count the number of active voices */
+ activeVoices = 0;
+ for (i = 0; i < MAX_SYNTH_VOICES; i++)
+ {
+ /* this synth? */
+ if (GET_VSYNTH(pVoiceMgr->voices[i].nextChannel) != pSynth->vSynthNum)
+ continue;
+
+ /* is voice active? */
+ if ((pVoiceMgr->voices[i].voiceState != eVoiceStateFree) && (pVoiceMgr->voices[i].voiceState != eVoiceStateMuting))
+ activeVoices++;
+ }
+
+ /* we may have to mute voices to reach new target */
+ while (activeVoices > polyphonyCount)
+ {
+ S_SYNTH_VOICE *pVoice;
+ EAS_I32 currentPriority, bestPriority;
+ EAS_INT bestCandidate;
+
+ /* find the lowest priority voice */
+ bestPriority = bestCandidate = -1;
+ for (i = 0; i < MAX_SYNTH_VOICES; i++)
+ {
+ pVoice = &pVoiceMgr->voices[i];
+
+ /* this synth? */
+ if (GET_VSYNTH(pVoice->nextChannel) != pSynth->vSynthNum)
+ continue;
+
+ /* if voice is stolen or just started, reduce the likelihood it will be stolen */
+ if (( pVoice->voiceState == eVoiceStateStolen) || (pVoice->voiceFlags & VOICE_FLAG_NO_SAMPLES_SYNTHESIZED_YET))
+ {
+ /* include velocity */
+ currentPriority = 128 - pVoice->nextVelocity;
+
+ /* include channel priority */
+ currentPriority += pSynth->channels[GET_CHANNEL(pVoice->nextChannel)].pool << CHANNEL_PRIORITY_STEAL_WEIGHT;
+ }
+ else
+ {
+ /* include age */
+ currentPriority = (EAS_I32) pVoice->age << NOTE_AGE_STEAL_WEIGHT;
+
+ /* include note gain -higher gain is lower steal value */
+ /*lint -e{704} use shift for performance */
+ currentPriority += ((32768 >> (12 - NOTE_GAIN_STEAL_WEIGHT)) + 256) -
+ ((EAS_I32) pVoice->gain >> (12 - NOTE_GAIN_STEAL_WEIGHT));
+
+ /* include channel priority */
+ currentPriority += pSynth->channels[GET_CHANNEL(pVoice->nextChannel)].pool << CHANNEL_PRIORITY_STEAL_WEIGHT;
+ }
+
+ /* is this the best choice so far? */
+ if (currentPriority > bestPriority)
+ {
+ bestPriority = currentPriority;
+ bestCandidate = i;
+ }
+ }
+
+ /* shutdown best candidate */
+ if (bestCandidate < 0)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "VMSetPolyphony: Unable to reduce polyphony\n"); */ }
+ break;
+ }
+
+ /* shut down this voice */
+ VMMuteVoice(pVoiceMgr, bestCandidate);
+ activeVoices--;
+ }
+
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * VMGetPolyphony()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Get the virtual synth polyphony
+ *
+ * Inputs:
+ * pVoiceMgr pointer to synthesizer data
+ * pPolyphonyCount pointer to variable to hold polyphony count
+ * pSynth pointer to virtual synth
+ *
+ * Outputs:
+ * Returns error code
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+/*lint -esym(715, pVoiceMgr) reserved for future use */
+EAS_RESULT VMGetPolyphony (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_I32 *pPolyphonyCount)
+{
+ *pPolyphonyCount = (EAS_U16) pSynth->maxPolyphony;
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * VMSetPriority()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Set the virtual synth priority
+ *
+ * Inputs:
+ * pVoiceMgr pointer to synthesizer data
+ * priority new priority
+ * pSynth pointer to virtual synth
+ *
+ * Outputs:
+ * Returns error code
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+/*lint -esym(715, pVoiceMgr) reserved for future use */
+EAS_RESULT VMSetPriority (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_I32 priority)
+{
+ pSynth->priority = (EAS_U8) priority ;
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * VMGetPriority()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Get the virtual synth priority
+ *
+ * Inputs:
+ * pVoiceMgr pointer to synthesizer data
+ * pPriority pointer to variable to hold priority
+ * pSynth pointer to virtual synth
+ *
+ * Outputs:
+ * Returns error code
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+/*lint -esym(715, pVoiceMgr) reserved for future use */
+EAS_RESULT VMGetPriority (S_VOICE_MGR *pVoiceMgr, S_SYNTH *pSynth, EAS_I32 *pPriority)
+{
+ *pPriority = pSynth->priority;
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * VMSetVolume()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Set the master volume for this synthesizer for this sequence.
+ *
+ * Inputs:
+ * nSynthVolume - the desired master volume
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ *
+ * Side Effects:
+ * overrides any previously set master volume from sysex
+ *
+ *----------------------------------------------------------------------------
+*/
+void VMSetVolume (S_SYNTH *pSynth, EAS_U16 masterVolume)
+{
+ pSynth->masterVolume = masterVolume;
+ pSynth->synthFlags |= SYNTH_FLAG_UPDATE_ALL_CHANNEL_PARAMETERS;
+}
+
+/*----------------------------------------------------------------------------
+ * VMSetPitchBendRange()
+ *----------------------------------------------------------------------------
+ * Set the pitch bend range for the given channel.
+ *----------------------------------------------------------------------------
+*/
+void VMSetPitchBendRange (S_SYNTH *pSynth, EAS_INT channel, EAS_I16 pitchBendRange)
+{
+ pSynth->channels[channel].pitchBendSensitivity = pitchBendRange;
+}
+
+/*----------------------------------------------------------------------------
+ * VMValidateEASLib()
+ *----------------------------------------------------------------------------
+ * Validates an EAS library
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT VMValidateEASLib (EAS_SNDLIB_HANDLE pEAS)
+{
+ /* validate the sound library */
+ if (pEAS)
+ {
+ if (pEAS->identifier != _EAS_LIBRARY_VERSION)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMValidateEASLib: Sound library mismatch in sound library: Read 0x%08x, expected 0x%08x\n",
+ pEAS->identifier, _EAS_LIBRARY_VERSION); */ }
+ return EAS_ERROR_SOUND_LIBRARY;
+ }
+
+ /* check sample rate */
+ if ((pEAS->libAttr & LIBFORMAT_SAMPLE_RATE_MASK) != _OUTPUT_SAMPLE_RATE)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMValidateEASLib: Sample rate mismatch in sound library: Read %lu, expected %lu\n",
+ pEAS->libAttr & LIBFORMAT_SAMPLE_RATE_MASK, _OUTPUT_SAMPLE_RATE); */ }
+ return EAS_ERROR_SOUND_LIBRARY;
+ }
+
+#ifdef _WT_SYNTH
+ /* check sample bit depth */
+#ifdef _8_BIT_SAMPLES
+ if (pEAS->libAttr & LIB_FORMAT_16_BIT_SAMPLES)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMValidateEASLib: Expected 8-bit samples and found 16-bit\n",
+ pEAS->libAttr & LIBFORMAT_SAMPLE_RATE_MASK, _OUTPUT_SAMPLE_RATE); */ }
+ return EAS_ERROR_SOUND_LIBRARY;
+ }
+#endif
+#ifdef _16_BIT_SAMPLES
+ if ((pEAS->libAttr & LIB_FORMAT_16_BIT_SAMPLES) == 0)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMValidateEASLib: Expected 16-bit samples and found 8-bit\n",
+ pEAS->libAttr & LIBFORMAT_SAMPLE_RATE_MASK, _OUTPUT_SAMPLE_RATE); */ }
+ return EAS_ERROR_SOUND_LIBRARY;
+ }
+#endif
+#endif
+ }
+
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * VMSetGlobalEASLib()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Sets the EAS library to be used by the synthesizer
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT VMSetGlobalEASLib (S_VOICE_MGR *pVoiceMgr, EAS_SNDLIB_HANDLE pEAS)
+{
+ EAS_RESULT result;
+
+ result = VMValidateEASLib(pEAS);
+ if (result != EAS_SUCCESS)
+ return result;
+
+ pVoiceMgr->pGlobalEAS = pEAS;
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * VMSetEASLib()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Sets the EAS library to be used by the synthesizer
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT VMSetEASLib (S_SYNTH *pSynth, EAS_SNDLIB_HANDLE pEAS)
+{
+ EAS_RESULT result;
+
+ result = VMValidateEASLib(pEAS);
+ if (result != EAS_SUCCESS)
+ return result;
+
+ pSynth->pEAS = pEAS;
+ return EAS_SUCCESS;
+}
+
+#ifdef DLS_SYNTHESIZER
+/*----------------------------------------------------------------------------
+ * VMSetGlobalDLSLib()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Sets the DLS library to be used by the synthesizer
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT VMSetGlobalDLSLib (EAS_DATA_HANDLE pEASData, EAS_DLSLIB_HANDLE pDLS)
+{
+
+ if (pEASData->pVoiceMgr->pGlobalDLS)
+ DLSCleanup(pEASData->hwInstData, pEASData->pVoiceMgr->pGlobalDLS);
+
+ pEASData->pVoiceMgr->pGlobalDLS = pDLS;
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * VMSetDLSLib()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Sets the DLS library to be used by the synthesizer
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT VMSetDLSLib (S_SYNTH *pSynth, EAS_DLSLIB_HANDLE pDLS)
+{
+ pSynth->pDLS = pDLS;
+ return EAS_SUCCESS;
+}
+#endif
+
+/*----------------------------------------------------------------------------
+ * VMSetTranposition()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Sets the global key transposition used by the synthesizer.
+ * Transposes all melodic instruments up or down by the specified
+ * amount. Range is limited to +/-12 semitones.
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+void VMSetTranposition (S_SYNTH *pSynth, EAS_I32 transposition)
+{
+ pSynth->globalTranspose = (EAS_I8) transposition;
+}
+
+/*----------------------------------------------------------------------------
+ * VMGetTranposition()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Gets the global key transposition used by the synthesizer.
+ * Transposes all melodic instruments up or down by the specified
+ * amount. Range is limited to +/-12 semitones.
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ *
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+void VMGetTranposition (S_SYNTH *pSynth, EAS_I32 *pTransposition)
+{
+ *pTransposition = pSynth->globalTranspose;
+}
+
+/*----------------------------------------------------------------------------
+ * VMGetNoteCount()
+ *----------------------------------------------------------------------------
+* Returns the total note count
+*----------------------------------------------------------------------------
+*/
+EAS_I32 VMGetNoteCount (S_SYNTH *pSynth)
+{
+ return pSynth->totalNoteCount;
+}
+
+/*----------------------------------------------------------------------------
+ * VMMIDIShutdown()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Clean up any Synth related system issues.
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ * None
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+void VMMIDIShutdown (S_EAS_DATA *pEASData, S_SYNTH *pSynth)
+{
+ EAS_INT vSynthNum;
+
+ /* decrement reference count, free if all references are gone */
+ if (--pSynth->refCount > 0)
+ return;
+
+ vSynthNum = pSynth->vSynthNum;
+
+ /* cleanup DLS load */
+#ifdef DLS_SYNTHESIZER
+ /*lint -e{550} result used only in debugging code */
+ if (pSynth->pDLS != NULL)
+ {
+ EAS_RESULT result;
+ if ((result = DLSCleanup(pEASData->hwInstData, pSynth->pDLS)) != EAS_SUCCESS)
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMMIDIShutdown: Error %ld cleaning up DLS collection\n", result); */ }
+ pSynth->pDLS = NULL;
+ }
+#endif
+
+ VMReset(pEASData->pVoiceMgr, pSynth, EAS_TRUE);
+
+ /* check Configuration Module for static memory allocation */
+ if (!pEASData->staticMemoryModel)
+ EAS_HWFree(pEASData->hwInstData, pSynth);
+
+ /* clear pointer to MIDI state */
+ pEASData->pVoiceMgr->pSynth[vSynthNum] = NULL;
+}
+
+/*----------------------------------------------------------------------------
+ * VMShutdown()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Clean up any Synth related system issues.
+ *
+ * Inputs:
+ * psEASData - pointer to overall EAS data structure
+ *
+ * Outputs:
+ * None
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+void VMShutdown (S_EAS_DATA *pEASData)
+{
+
+ /* don't free a NULL pointer */
+ if (pEASData->pVoiceMgr == NULL)
+ return;
+
+#ifdef DLS_SYNTHESIZER
+ /* if we have a global DLS collection, clean it up */
+ if (pEASData->pVoiceMgr->pGlobalDLS)
+ {
+ DLSCleanup(pEASData->hwInstData, pEASData->pVoiceMgr->pGlobalDLS);
+ pEASData->pVoiceMgr->pGlobalDLS = NULL;
+ }
+#endif
+
+ /* check Configuration Module for static memory allocation */
+ if (!pEASData->staticMemoryModel)
+ EAS_HWFree(pEASData->hwInstData, pEASData->pVoiceMgr);
+ pEASData->pVoiceMgr = NULL;
+}
+
+#ifdef EXTERNAL_AUDIO
+/*----------------------------------------------------------------------------
+ * EAS_RegExtAudioCallback()
+ *----------------------------------------------------------------------------
+ * Register a callback for external audio processing
+ *----------------------------------------------------------------------------
+*/
+void VMRegExtAudioCallback (S_SYNTH *pSynth, EAS_VOID_PTR pInstData, EAS_EXT_PRG_CHG_FUNC cbProgChgFunc, EAS_EXT_EVENT_FUNC cbEventFunc)
+{
+ pSynth->pExtAudioInstData = pInstData;
+ pSynth->cbProgChgFunc = cbProgChgFunc;
+ pSynth->cbEventFunc = cbEventFunc;
+}
+
+/*----------------------------------------------------------------------------
+ * VMGetMIDIControllers()
+ *----------------------------------------------------------------------------
+ * Returns the MIDI controller values on the specified channel
+ *----------------------------------------------------------------------------
+*/
+void VMGetMIDIControllers (S_SYNTH *pSynth, EAS_U8 channel, S_MIDI_CONTROLLERS *pControl)
+{
+ pControl->modWheel = pSynth->channels[channel].modWheel;
+ pControl->volume = pSynth->channels[channel].volume;
+ pControl->pan = pSynth->channels[channel].pan;
+ pControl->expression = pSynth->channels[channel].expression;
+ pControl->channelPressure = pSynth->channels[channel].channelPressure;
+
+#ifdef _REVERB
+ pControl->reverbSend = pSynth->channels[channel].reverbSend;
+#endif
+
+#ifdef _CHORUSE
+ pControl->chorusSend = pSynth->channels[channel].chorusSend;
+#endif
+}
+#endif
+
+#ifdef _SPLIT_ARCHITECTURE
+/*----------------------------------------------------------------------------
+ * VMStartFrame()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Starts an audio frame
+ *
+ * Inputs:
+ *
+ * Outputs:
+ * Returns true if EAS_MixEnginePrep should be called (onboard mixing)
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_BOOL VMStartFrame (S_EAS_DATA *pEASData)
+{
+
+ /* init counter for voices starts in split architecture */
+#ifdef MAX_VOICE_STARTS
+ pVoiceMgr->numVoiceStarts = 0;
+#endif
+
+ return pFrameInterface->pfStartFrame(pEASData->pVoiceMgr->pFrameBuffer);
+}
+
+/*----------------------------------------------------------------------------
+ * VMEndFrame()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Stops an audio frame
+ *
+ * Inputs:
+ *
+ * Outputs:
+ * Returns true if EAS_MixEnginePost should be called (onboard mixing)
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_BOOL VMEndFrame (S_EAS_DATA *pEASData)
+{
+
+ return pFrameInterface->pfEndFrame(pEASData->pVoiceMgr->pFrameBuffer, pEASData->pMixBuffer, pEASData->masterGain);
+}
+#endif
+
+#ifdef TEST_HARNESS
+/*----------------------------------------------------------------------------
+ * SanityCheck()
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT VMSanityCheck (EAS_DATA_HANDLE pEASData)
+{
+ S_SYNTH_VOICE *pVoice;
+ S_SYNTH *pSynth;
+ EAS_INT i;
+ EAS_INT j;
+ EAS_INT freeVoices;
+ EAS_INT activeVoices;
+ EAS_INT playingVoices;
+ EAS_INT stolenVoices;
+ EAS_INT releasingVoices;
+ EAS_INT mutingVoices;
+ EAS_INT poolCount[MAX_VIRTUAL_SYNTHESIZERS][NUM_SYNTH_CHANNELS];
+ EAS_INT vSynthNum;
+ EAS_RESULT result = EAS_SUCCESS;
+
+ /* initialize counts */
+ EAS_HWMemSet(poolCount, 0, sizeof(poolCount));
+ freeVoices = activeVoices = playingVoices = stolenVoices = releasingVoices = mutingVoices = 0;
+
+ /* iterate through all voices */
+ for (i = 0; i < MAX_SYNTH_VOICES; i++)
+ {
+ pVoice = &pEASData->pVoiceMgr->voices[i];
+ if (pVoice->voiceState != eVoiceStateFree)
+ {
+ vSynthNum = GET_VSYNTH(pVoice->channel);
+ if (vSynthNum >= MAX_VIRTUAL_SYNTHESIZERS)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMSanityCheck: Voice %d has invalid virtual synth number %d\n", i, vSynthNum); */ }
+ result = EAS_FAILURE;
+ continue;
+ }
+ pSynth = pEASData->pVoiceMgr->pSynth[vSynthNum];
+
+ switch (pVoice->voiceState)
+ {
+ case eVoiceStateMuting:
+ activeVoices++;
+ mutingVoices++;
+ break;
+
+ case eVoiceStateStolen:
+ vSynthNum = GET_VSYNTH(pVoice->nextChannel);
+ if (vSynthNum >= MAX_VIRTUAL_SYNTHESIZERS)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMSanityCheck: Voice %d has invalid virtual synth number %d\n", i, vSynthNum); */ }
+ result = EAS_FAILURE;
+ continue;
+ }
+ pSynth = pEASData->pVoiceMgr->pSynth[vSynthNum];
+ activeVoices++;
+ stolenVoices++;
+ poolCount[vSynthNum][pSynth->channels[GET_CHANNEL(pVoice->nextChannel)].pool]++;
+ break;
+
+ case eVoiceStateStart:
+ case eVoiceStatePlay:
+ activeVoices++;
+ playingVoices++;
+ poolCount[vSynthNum][pSynth->channels[GET_CHANNEL(pVoice->channel)].pool]++;
+ break;
+
+ case eVoiceStateRelease:
+ activeVoices++;
+ releasingVoices++;
+ poolCount[vSynthNum][pSynth->channels[GET_CHANNEL(pVoice->channel)].pool]++;
+ break;
+
+ default:
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMSanityCheck : voice %d in invalid state\n", i); */ }
+ result = EAS_FAILURE;
+ break;
+ }
+ }
+
+ /* count free voices */
+ else
+ freeVoices++;
+ }
+
+ /* dump state info */
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "%d free\n", freeVoices); */ }
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "%d active\n", activeVoices); */ }
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "%d playing\n", playingVoices); */ }
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "%d releasing\n", releasingVoices); */ }
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "%d muting\n", mutingVoices); */ }
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "%d stolen\n", stolenVoices); */ }
+
+ if (pEASData->pVoiceMgr->activeVoices != activeVoices)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Active voice mismatch was %d should be %d\n",
+ pEASData->pVoiceMgr->activeVoices, activeVoices); */ }
+ result = EAS_FAILURE;
+ }
+
+ /* check virtual synth status */
+ for (i = 0; i < MAX_VIRTUAL_SYNTHESIZERS; i++)
+ {
+ if (pEASData->pVoiceMgr->pSynth[i] == NULL)
+ continue;
+
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Synth %d numActiveVoices: %d\n", i, pEASData->pVoiceMgr->pSynth[i]->numActiveVoices); */ }
+ if (pEASData->pVoiceMgr->pSynth[i]->numActiveVoices > MAX_SYNTH_VOICES)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMSanityCheck: Synth %d illegal count for numActiveVoices: %d\n", i, pEASData->pVoiceMgr->pSynth[i]->numActiveVoices); */ }
+ result = EAS_FAILURE;
+ }
+ for (j = 0; j < NUM_SYNTH_CHANNELS; j++)
+ {
+ if (poolCount[i][j] != pEASData->pVoiceMgr->pSynth[i]->poolCount[j])
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Pool count mismatch synth %d pool %d, was %d, should be %d\n",
+ i, j, pEASData->pVoiceMgr->pSynth[i]->poolCount[j], poolCount[i][j]); */ }
+ result = EAS_FAILURE;
+ }
+ }
+ }
+
+ return result;
+}
+#endif
+
+