// Increase the number of key splits. This will not clean the entry's that are added. // This will add from the end. This will freeup the passed InstrumentResource InstrumentResource * XAddKeySplit(InstrumentResource *theX, short int howMany) { long size, moveSize, size2; short int total; KeySplit *pSplits; char *pSrc, *pDest; InstrumentResource *theNewX; theNewX = NULL; if (theX) { total = (short)XGetShort(&theX->keySplitCount); size = XGetPtrSize(theX); size2 = (long)(sizeof(KeySplit) * howMany); theNewX = (InstrumentResource *)XNewPtr(size2 + size); if (theNewX) { XBlockMove(theX, theNewX, size); // make copy XPutShort(&theNewX->keySplitCount, (unsigned short)(total + howMany)); // must move all zones down pSplits = (KeySplit *) ( ((unsigned char *)&theX->keySplitCount) + sizeof(unsigned short int)); pSrc = (char *)&pSplits[total]; pSplits = (KeySplit *) ( ((unsigned char *)&theNewX->keySplitCount) + sizeof(unsigned short int)); pDest = (char *)&pSplits[total + howMany]; moveSize = size - (pSrc - (char *)theX); // this move must handle overlapping data XBlockMove(pSrc, pDest, moveSize); } } return theNewX; }
long XGetSongTempoFactor(SongResource *pSong) { long tempo; tempo = 16667; // 1.0 if (pSong) { switch (((SongResource_SMS *)pSong)->songType) { case SONG_TYPE_SMS: tempo = XGetShort(&((SongResource_SMS *)pSong)->songTempo); break; case SONG_TYPE_RMF: tempo = XGetShort(&((SongResource_RMF *)pSong)->songTempo); break; } if (tempo == 0) { tempo = 16667; } } return tempo; }
// Set a keysplit entry. The result will be ordered for the 68k CPU void XSetKeySplitFromPtr(InstrumentResource *theX, short int entry, KeySplit *keysplit) { KeySplit *pSplits; short int count; count = (short)XGetShort(&theX->keySplitCount); if ( (count) && (entry < count) ) { pSplits = (KeySplit *) ( ((unsigned char *)&theX->keySplitCount) + sizeof(short int)); pSplits[entry] = *keysplit; XPutShort(&pSplits[entry].sndResourceID, (unsigned short)keysplit->sndResourceID); XPutShort(&pSplits[entry].miscParameter1, (unsigned short)keysplit->miscParameter1); XPutShort(&pSplits[entry].miscParameter2, (unsigned short)keysplit->miscParameter2); } else { XSetMemory(keysplit, (long)sizeof(KeySplit), 0); } }
// Given an array of instruments, this will return an array of SND resources that are required to load // these instruments short int XGetTotalKeysplits(XShortResourceID *instArray, short int totalInstruments, XShortResourceID *sndArray, short int totalSnds) { register short int count, count2, count3, count4, keyCount; KeySplit theSplit; InstrumentResource *theInstrument; char *pLoaded; long size; keyCount = 0; if (instArray && totalInstruments && sndArray && totalSnds) { pLoaded = (char *)XNewPtr((long)sizeof(char) * totalSnds); if (pLoaded) { for (count = 0; count < totalInstruments; count++) { theInstrument = (InstrumentResource *)XGetAndDetachResource(ID_INST, instArray[count], &size); if (theInstrument) { count3 = (short)XGetShort(&theInstrument->keySplitCount); for (count2 = 0; count2 < count3; count2++) { XGetKeySplitFromPtr(theInstrument, count2, &theSplit); count4 = PV_ConvertResourceID2Index(sndArray, totalSnds, theSplit.sndResourceID); if (count4) { if (pLoaded[count4] == 0) { pLoaded[count4] = 1; keyCount++; } } } } } XDisposePtr((XPTR)pLoaded); } } return keyCount; }
void GM_MergeExternalSong(void *theExternalSong, XShortResourceID theSongID, GM_Song *theSong) { short int maps; short int count; short int number; SongResource_SMS *songSMS; SongResource_RMF *songRMF; Remap *pMap; if (theExternalSong && theSong) { switch (((SongResource_SMS *)theExternalSong)->songType) { case SONG_TYPE_SMS: songSMS = (SongResource_SMS *)theExternalSong; theSong->songID = theSongID; theSong->songPitchShift = songSMS->songPitchShift; theSong->allowProgramChanges = (songSMS->flags1 & XBF_enableMIDIProgram) ? TRUE : FALSE; theSong->defaultPercusionProgram = songSMS->defaultPercusionProgram; theSong->defaultReverbType = songSMS->reverbType; theSong->maxSongVoices = songSMS->maxNotes; theSong->mixLevel = XGetShort(&songSMS->mixLevel); theSong->maxEffectVoices = songSMS->maxEffects; theSong->ignoreBadInstruments = (songSMS->flags2 & XBF_ignoreBadPatches) ? TRUE : FALSE; maps = XGetShort(&songSMS->remapCount); PV_SetTempo(theSong, XGetShort(&songSMS->songTempo)); theSong->songVolume = XGetSongVolume((SongResource *)theExternalSong); // Load instruments if ((songSMS->flags1 & XBF_enableMIDIProgram) == FALSE) { number = (songSMS->flags1 & XBF_fileTrackFlag) ? MAX_TRACKS : MAX_CHANNELS; for (count = 0; count < number; count++) { theSong->instrumentRemap[count] = count; } } // Fill in remap first if (maps) { pMap = (Remap *)&songSMS->remaps; for (count = 0; count < maps; count++) { number = XGetShort(&pMap[count].instrumentNumber) & ((MAX_INSTRUMENTS*MAX_BANKS)-1); theSong->instrumentRemap[number] = XGetShort(&pMap[count].ResourceINSTID); } } break; case SONG_TYPE_RMF: songRMF = (SongResource_RMF *)theExternalSong; theSong->songID = theSongID; theSong->songPitchShift = songRMF->songPitchShift; theSong->allowProgramChanges = TRUE; // aloways allow program changes theSong->defaultPercusionProgram = -1; // GM percussion only theSong->defaultReverbType = songRMF->reverbType; theSong->maxSongVoices = XGetShort(&songRMF->maxNotes); theSong->mixLevel = XGetShort(&songRMF->mixLevel); theSong->maxEffectVoices = XGetShort(&songRMF->maxEffects); theSong->ignoreBadInstruments = TRUE; PV_SetTempo(theSong, XGetShort(&songRMF->songTempo)); theSong->songVolume = XGetSongVolume((SongResource *)theExternalSong); break; } } }
// Instruments 0 to MAX_INSTRUMENTS*MAX_BANKS are the standard MIDI instrument placements. // This will create an internal instrument from and external instrument. If theX is non-null then it // will use that data to create the GM_Instrument GM_Instrument * PV_GetInstrument(XLongResourceID theID, void *theExternalX, INT32 patchSize) { GM_Instrument *theI, *theS; InstrumentResource *theX; INT32 size; short int count; UBYTE *theSound; KeySplit theXSplit; CacheSampleInfo sndInfo; LOOPCOUNT i; INT32 theSampleID; theI = NULL; theX = (InstrumentResource *)theExternalX; if (theExternalX == NULL) { theX = (InstrumentResource *)XGetAndDetachResource(ID_INST, theID, &patchSize); } if (theX) { if (XGetShort(&theX->keySplitCount) < 2) // if its 1, then it has no splits { // get the sample ID from a short, values can be negative // then allow conversion to take place. // NOTE: I know this is awfull, but if you change it things will break. The // internal ID values are all 32 bit signed, and some of the external // file structures are 16 bit signed. theSampleID = (INT32)((short)XGetShort(&theX->sndResourceID)); theSound = (UBYTE *)PV_GetSampleFromID((INT32)theSampleID, &sndInfo); if (theSound) { theI = (GM_Instrument *)XNewPtr((INT32)sizeof(GM_Instrument)); if (theI) { theI->u.w.theWaveform = (SBYTE *)theSound; theI->disableSndLooping = TEST_FLAG_VALUE(theX->flags1, ZBF_disableSndLooping); theI->playAtSampledFreq = TEST_FLAG_VALUE(theX->flags2, ZBF_playAtSampledFreq); theI->doKeymapSplit = FALSE; theI->notPolyphonic = TEST_FLAG_VALUE(theX->flags2, ZBF_notPolyphonic); theI->avoidReverb = TEST_FLAG_VALUE(theX->flags1, ZBF_avoidReverb); theI->useSampleRate = TEST_FLAG_VALUE(theX->flags1, ZBF_useSampleRate); theI->sampleAndHold = TEST_FLAG_VALUE(theX->flags1, ZBF_sampleAndHold); theI->useSoundModifierAsRootKey = TEST_FLAG_VALUE(theX->flags2, ZBF_useSoundModifierAsRootKey); PV_GetEnvelopeData(theX, theI, patchSize); // get envelope theI->u.w.bitSize = sndInfo.bitSize; theI->u.w.channels = sndInfo.channels; theI->u.w.waveformID = XGetShort(&theX->sndResourceID); theI->u.w.waveSize = sndInfo.waveSize; theI->u.w.waveFrames = sndInfo.waveFrames; theI->u.w.startLoop = sndInfo.loopStart; theI->u.w.endLoop = sndInfo.loopEnd; theI->masterRootKey = XGetShort(&theX->midiRootKey); theI->panPlacement = theX->panPlacement; theI->u.w.baseMidiPitch = (unsigned char)sndInfo.baseKey; theI->u.w.sampledRate = sndInfo.rate; // NOTE!! If ZBF_useSoundModifierAsRootKey is TRUE, then we are using // the Sound Modifier data blocks as a root key replacement for samples in // the particular split theI->miscParameter1 = XGetShort(&theX->miscParameter1); theI->miscParameter2 = XGetShort(&theX->miscParameter2); if (theI->useSoundModifierAsRootKey) { theI->enableSoundModifier = FALSE; if (theI->miscParameter2 == 0) // Forces a default value of 0 to 100 { theI->miscParameter2 = 100; } } else { theI->enableSoundModifier = TEST_FLAG_VALUE(theX->flags2, ZBF_enableSoundModifier); theI->smodResourceID = theX->smodResourceID; // Process sample in place if ( (theI->enableSoundModifier) && (theI->u.w.bitSize == 8) && (theI->u.w.channels == 1) ) { #if DISPLAY_INSTRUMENTS DPrint(drawDebug, "---->Processing instrument %ld with SMOD %ld\r", (INT32)theID, (INT32)theI->smodResourceID); #endif PV_ProcessSampleWithSMOD(theI->u.w.theWaveform, theI->u.w.waveSize, theI->u.w.waveformID, theI->smodResourceID, theI->miscParameter1, theI->miscParameter2); } } } } } else { // Keysplits #if DISPLAY_INSTRUMENTS DPrint(drawDebug, "----->Processing %ld keysplits\r", (INT32)XGetShort(&theX->keySplitCount)); #endif size = XGetShort(&theX->keySplitCount) * (INT32)sizeof(GM_KeymapSplit); size += (INT32)sizeof(GM_KeymapSplitInfo); theI = (GM_Instrument *)XNewPtr(size + (INT32)sizeof(GM_Instrument)); if (theI) { theI->disableSndLooping = TEST_FLAG_VALUE(theX->flags1, ZBF_disableSndLooping); theI->doKeymapSplit = TRUE; theI->notPolyphonic = TEST_FLAG_VALUE(theX->flags2, ZBF_notPolyphonic); theI->avoidReverb = TEST_FLAG_VALUE(theX->flags1, ZBF_avoidReverb); theI->useSampleRate = TEST_FLAG_VALUE(theX->flags1, ZBF_useSampleRate); theI->sampleAndHold = TEST_FLAG_VALUE(theX->flags1, ZBF_sampleAndHold); theI->playAtSampledFreq = TEST_FLAG_VALUE(theX->flags2, ZBF_playAtSampledFreq); theI->useSoundModifierAsRootKey = TEST_FLAG_VALUE(theX->flags2, ZBF_useSoundModifierAsRootKey); PV_GetEnvelopeData(theX, theI, patchSize); // get envelope theI->u.k.KeymapSplitCount = XGetShort(&theX->keySplitCount); theI->u.k.defaultInstrumentID = (XShortResourceID)XGetShort(&theX->sndResourceID); theI->masterRootKey = XGetShort(&theX->midiRootKey); theI->panPlacement = theX->panPlacement; // NOTE!! If ZBF_useSoundModifierAsRootKey is TRUE, then we are using // the Sound Modifier data blocks as a root key replacement for samples in // the particular split theI->miscParameter1 = XGetShort(&theX->miscParameter1); theI->miscParameter2 = XGetShort(&theX->miscParameter2); if (theI->useSoundModifierAsRootKey) { theI->enableSoundModifier = FALSE; if (theI->miscParameter2 == 0) // Forces a default value of 0 to 100 { theI->miscParameter2 = 100; } } else { theI->enableSoundModifier = TEST_FLAG_VALUE(theX->flags2, ZBF_enableSoundModifier); theI->smodResourceID = theX->smodResourceID; } for (count = 0; count < theI->u.k.KeymapSplitCount; count++) { XGetKeySplitFromPtr(theX, count, &theXSplit); theI->u.k.keySplits[count].lowMidi = theXSplit.lowMidi; theI->u.k.keySplits[count].highMidi = theXSplit.highMidi; theI->u.k.keySplits[count].miscParameter1 = theXSplit.miscParameter1; if (theI->useSoundModifierAsRootKey && (theXSplit.miscParameter2 == 0)) // Forces a default value of 0 to 100 { theXSplit.miscParameter2 = 100; } theI->u.k.keySplits[count].miscParameter2 = theXSplit.miscParameter2; // if (GM_IsInstrumentRangeUsed(theID, (INT16)theXSplit.lowMidi, (INT16)theXSplit.highMidi)) { #if DISPLAY_INSTRUMENTS DPrint(drawDebug, "------->Keysplit %ld low %ld high %ld\r", (INT32)count, (INT32)theXSplit.lowMidi, (INT32)theXSplit.highMidi); #endif theS = PV_CreateInstrumentFromResource(theI, (XLongResourceID)theXSplit.sndResourceID); theI->u.k.keySplits[count].pSplitInstrument = theS; if (theS) { theS->useSoundModifierAsRootKey = theI->useSoundModifierAsRootKey; theS->miscParameter1 = theXSplit.miscParameter1; if (theS->useSoundModifierAsRootKey && (theXSplit.miscParameter2 == 0)) // Forces a default value of 0 to 100 { theXSplit.miscParameter2 = 100; } theS->miscParameter2 = theXSplit.miscParameter2; theS->masterRootKey = theI->masterRootKey; theS->avoidReverb = theI->avoidReverb; theS->volumeADSRRecord = theI->volumeADSRRecord; for (i = 0; i < theI->LFORecordCount; i++) { theS->LFORecords[i] = theI->LFORecords[i]; } theS->LFORecordCount = theI->LFORecordCount; for (i = 0; i < theI->curveRecordCount; i++) { theS->curve[i] = theI->curve[i]; } theS->curveRecordCount = theI->curveRecordCount; theS->LPF_frequency = theI->LPF_frequency; theS->LPF_resonance = theI->LPF_resonance; theS->LPF_lowpassAmount = theI->LPF_lowpassAmount; if (theS->useSoundModifierAsRootKey == FALSE) { // Process sample in place if ( (theS->enableSoundModifier) && (theS->u.w.bitSize == 8) && (theS->u.w.channels == 1) ) { #if DISPLAY_INSTRUMENTS DPrint(drawDebug, "----->Processing instrument %ld with SMOD %ld\r", (INT32)theID, (INT32)theI->smodResourceID); #endif PV_ProcessSampleWithSMOD( theS->u.w.theWaveform, theS->u.w.waveSize, theXSplit.sndResourceID, theS->smodResourceID, theS->miscParameter1, theS->miscParameter2); } } } } } } } if (theExternalX == NULL) { XDisposePtr((XPTR)theX); } } #if DISPLAY_INSTRUMENTS if (theI) { DPrint(drawDebug, "-------->INST info: masterRootKey %ld\r", (INT32)theI->masterRootKey); } #endif return theI; }
// Given an external instrument resource and an internal resource type fill the envelope data // and if not there, place a default envelope static void PV_GetEnvelopeData(InstrumentResource *theX, GM_Instrument *theI, INT32 theXSize) { INT32 count, count2, lfoCount; INT32 size, unitCount, unitType, unitSubCount; unsigned short int data; register char *pData, *pData2; register char *pUnit; register KeySplit *pSplits; register LFORecord *pLFO; register ADSRRecord *pADSR; register CurveRecord *pCurve; XBOOL disableModWheel; disableModWheel = FALSE; theI->volumeADSRRecord.currentTime = 0; theI->volumeADSRRecord.currentPosition = 0; theI->volumeADSRRecord.currentLevel = 0; theI->volumeADSRRecord.previousTarget = 0; theI->volumeADSRRecord.mode = 0; theI->volumeADSRRecord.sustainingDecayLevel = 65536; theI->LPF_frequency = 0; theI->LPF_resonance = 0; theI->LPF_lowpassAmount = 0; // fill default theI->volumeADSRRecord.ADSRLevel[0] = VOLUME_RANGE; theI->volumeADSRRecord.ADSRFlags[0] = ADSR_TERMINATE; theI->volumeADSRRecord.ADSRTime[0] = 0; theI->LFORecordCount = 0; theI->curveRecordCount = 0; theI->extendedFormat = FALSE; pUnit = NULL; size = theXSize; if (theX && size) { if (theX->flags1 & ZBF_extendedFormat) { // search for end of tremlo data $8000. If not there, don't walk past end of instrument pSplits = (KeySplit *) ( ((char *)&theX->keySplitCount) + sizeof(short)); count = XGetShort(&theX->keySplitCount); pData = (char *)&pSplits[count]; pData2 = (char *)theX; size -= (INT32) (pData - pData2); for (count = 0; count < size; count++) { data = XGetShort(&pData[count]); if (data == 0x8000) { count += 4; // skip past end token and extra word data = (unsigned short)pData[count] + 1; // get first string length; count2 = (INT32)pData[count+data] + 1; // get second string length pUnit = (char *) (&pData[count + data + count2]); // NOTE: src will be non aligned, possibly on a byte boundry. break; } } if (pUnit) { theI->extendedFormat = TRUE; pUnit += 12; // reserved global space unitCount = *pUnit; // how many unit records? pUnit++; // byte if (unitCount) { lfoCount = 0; for (count = 0; count < unitCount; count++) { unitType = XGetLong(pUnit) & 0x5F5F5F5F; pUnit += 4; // long switch (unitType) { case INST_EXPONENTIAL_CURVE: if (theI->curveRecordCount >= MAX_CURVES) // can only handle 4 { goto bailoninstrument; } pCurve = &theI->curve[theI->curveRecordCount]; theI->curveRecordCount++; pCurve->tieFrom = XGetLong(pUnit) & 0x5F5F5F5F; pUnit += 4; pCurve->tieTo = XGetLong(pUnit) & 0x5F5F5F5F; pUnit += 4; unitSubCount = *pUnit++; if (unitSubCount > MAX_CURVES) { goto bailoninstrument; } pCurve->curveCount = (short int)unitSubCount; for (count2 = 0; count2 < unitSubCount; count2++) { pCurve->from_Value[count2] = *pUnit++; pCurve->to_Scalar[count2] = XGetShort(pUnit); pUnit += 2; } // there's one extra slot in the definition to allow for this PAST the end of the 8 possible entries pCurve->from_Value[count2] = 127; pCurve->to_Scalar[count2] = pCurve->to_Scalar[count2]; break; case INST_ADSR_ENVELOPE: unitSubCount = *pUnit; // how many unit records? pUnit++; // byte if (unitSubCount > ADSR_STAGES) { // can't have more than ADSR_STAGES stages goto bailoninstrument; } pADSR = &theI->volumeADSRRecord; for (count2 = 0; count2 < unitSubCount; count2++) { pADSR->ADSRLevel[count2] = XGetLong(pUnit); pUnit += 4; pADSR->ADSRTime[count2] = XGetLong(pUnit); pUnit += 4; pADSR->ADSRFlags[count2] = XGetLong(pUnit) & 0x5F5F5F5F; pUnit += 4; } break; case INST_LOW_PASS_FILTER: // low pass global filter parameters theI->LPF_frequency = XGetLong(pUnit); pUnit += 4; theI->LPF_resonance = XGetLong(pUnit); pUnit += 4; theI->LPF_lowpassAmount = XGetLong(pUnit); pUnit += 4; break; case INST_DEFAULT_MOD: // default mod wheel hookup disableModWheel = TRUE; break; // LFO types case INST_PITCH_LFO: case INST_VOLUME_LFO: case INST_STEREO_PAN_LFO: case INST_STEREO_PAN_NAME2: case INST_LOW_PASS_AMOUNT: case INST_LPF_DEPTH: case INST_LPF_FREQUENCY: if (lfoCount > MAX_LFOS) { goto bailoninstrument; } unitSubCount = *pUnit; // how many unit records? pUnit++; // byte if (unitSubCount > ADSR_STAGES) { // can't have more than ADSR_STAGES stages //DEBUG_STR("\p too many stages"); goto bailoninstrument; } pLFO = &theI->LFORecords[lfoCount]; for (count2 = 0; count2 < unitSubCount; count2++) { pLFO->a.ADSRLevel[count2] = XGetLong(pUnit); pUnit += 4; pLFO->a.ADSRTime[count2] = XGetLong(pUnit); pUnit += 4; pLFO->a.ADSRFlags[count2] = XGetLong(pUnit) & 0x5F5F5F5F; pUnit += 4; } pLFO->where_to_feed = unitType & 0x5F5F5F5F; pLFO->period = XGetLong(pUnit); pUnit += 4; pLFO->waveShape = XGetLong(pUnit); pUnit += 4; pLFO->DC_feed = XGetLong(pUnit); pUnit += 4; pLFO->level = XGetLong(pUnit); pUnit += 4; pLFO->currentWaveValue = 0; pLFO->currentTime = 0; pLFO->LFOcurrentTime = 0; pLFO->a.currentTime = 0; pLFO->a.currentPosition = 0; pLFO->a.currentLevel = 0; pLFO->a.previousTarget = 0; pLFO->a.mode = 0; pLFO->a.sustainingDecayLevel = 65536; lfoCount++; break; } } if ((lfoCount < (MAX_LFOS-1)) && (theI->curveRecordCount < MAX_CURVES) && (disableModWheel == FALSE)) { pCurve = &theI->curve[theI->curveRecordCount]; theI->curveRecordCount++; // Create a straight-line curve function to tie mod wheel to pitch LFO pCurve->tieFrom = MOD_WHEEL_CONTROL; pCurve->tieTo = PITCH_LFO; pCurve->curveCount = 2; pCurve->from_Value[0] = 0; pCurve->to_Scalar[0] = 0; pCurve->from_Value[1] = 127; pCurve->to_Scalar[1] = 256; pCurve->from_Value[2] = 127; pCurve->to_Scalar[2] = 256; // Create a default pitch LFO unit to tie the MOD wheel to. pLFO = &theI->LFORecords[lfoCount]; pLFO->where_to_feed = PITCH_LFO; pLFO->period = 180000; pLFO->waveShape = SINE_WAVE; pLFO->DC_feed = 0; pLFO->level = 64; pLFO->currentWaveValue = 0; pLFO->currentTime = 0; pLFO->LFOcurrentTime = 0; pLFO->a.ADSRLevel[0] = 65536; pLFO->a.ADSRTime[0] = 0; pLFO->a.ADSRFlags[0] = ADSR_TERMINATE; pLFO->a.currentTime = 0; pLFO->a.currentPosition = 0; pLFO->a.currentLevel = 0; pLFO->a.previousTarget = 0; pLFO->a.mode = 0; pLFO->a.sustainingDecayLevel = 65536; lfoCount++; } theI->LFORecordCount = (unsigned char)lfoCount; bailoninstrument: ; } } } } }
/* ** Given an INST resource, this will return an array of snd resources ** that are used for this instrument */ short int XCollectSoundsFromInstrument(InstrumentResource *theX, XShortResourceID *sndArray, short maxArraySize) { short int sndOutCount, sndCount, splitCount, count, count2; KeySplit theSplit; XShortResourceID countSndArray[MAX_SAMPLES]; /* Max samples per instrument */ XBOOL goodSound; XShortResourceID soundID; sndCount = 0; sndOutCount = 0; if (theX) { splitCount = (short)XGetShort(&theX->keySplitCount); if ( splitCount == 0) { if (maxArraySize > 1) { sndCount = 1; countSndArray[0] = (XShortResourceID)XGetShort(&theX->sndResourceID); if (countSndArray[0] == (XShortResourceID)-1) { sndCount = 0; } } } else { if (maxArraySize > splitCount) { sndCount = 1; countSndArray[0] = (XShortResourceID)XGetShort(&theX->sndResourceID); if (countSndArray[0] == (XShortResourceID)-1) { sndCount = 0; } for (count = 0; count < splitCount; count++) { XGetKeySplitFromPtr(theX, count, &theSplit); countSndArray[sndCount] = theSplit.sndResourceID; if (countSndArray[sndCount] != (XShortResourceID)-1) { sndCount++; } } } } XBubbleSortArray((short *)countSndArray, (short)sndCount); // Remove duplicates sndOutCount = 0; for (count = 0; count < sndCount; count++) { goodSound = TRUE; soundID = countSndArray[count]; for (count2 = 0; count2 < sndOutCount; count2++) { if (soundID == sndArray[count2]) { goodSound = FALSE; break; } } if (goodSound) { sndArray[sndOutCount++] = soundID; } } } return sndOutCount; }