// 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; }
void XTestCompression(XPTR compressedAndEncryptedData, long size, XPTR originalData, long originalSize) { XPTR pData, pData2; short int safe; if (compressedAndEncryptedData && originalData && size && originalSize) { // since this is encrypted, make a new copy and decrypt pData = XNewPtr(size); if (pData) { XBlockMove(compressedAndEncryptedData, pData, size); XDecryptData(pData, (unsigned long)size); // decrypt first pData2 = pData; pData = XDecompressPtr(pData2, (unsigned long)size, TRUE); // uncompress second if (pData) { size = XGetPtrSize(pData); // get new size if (size == originalSize) { safe = XMemCmp(pData, originalData, size); if (safe) { DebugStr("\pmemcmp failed"); } else { DebugStr("\pSAFE!"); } } else {
// This will place a sample into the sample cache. Used for eMidi files. void PV_SetSampleIntoCache(GM_Song *pSong, XLongResourceID theID, XPTR pSndFormatData) { register short int count; register void *pSample; CacheSampleInfo info; GM_SetCacheSamples(pSong, TRUE); // enable caching pSample = NULL; // first, find out if there is a sample already for this ID pSample = PV_FindSoundFromID(theID); if (pSample) { // yes, there is so free it count = PV_FindCacheIndexFromPtr(pSample); MusicGlobals->sampleCaches[count]->referenceCount = 1; PV_FreeCacheEntryFromPtr(pSample); } info.referenceCount = 1; pSample = PV_GetSampleData(theID, pSndFormatData, &info); if (pSample) { for (count = 0; count < MAX_SAMPLES; count++) { if (MusicGlobals->sampleCaches[count] == NULL) { MusicGlobals->sampleCaches[count] = (CacheSampleInfo *)XNewPtr((INT32)sizeof(CacheSampleInfo)); if (MusicGlobals->sampleCaches[count]) { *MusicGlobals->sampleCaches[count] = info; MusicGlobals->sampleCaches[count]->theID = theID; } break; } } } }
static GM_Song * PV_CreateSongFromMidi(XLongResourceID theID, XPTR useThisMidiData, INT32 midiSize) { XPTR theMidiData; GM_Song *theSong; INT32 count; theSong = NULL; if (useThisMidiData) { theMidiData = useThisMidiData; } else { midiSize = 0; theMidiData = XGetMidiData(theID, &midiSize, NULL); } if (theMidiData) { theSong = (GM_Song *)XNewPtr((INT32)sizeof(GM_Song)); if (theSong) { theSong->midiData = theMidiData; theSong->midiSize = midiSize; theSong->disposeSongDataWhenDone = (useThisMidiData == NULL) ? TRUE : FALSE; // Fill in remap first for (count = 0; count < (MAX_INSTRUMENTS*MAX_BANKS); count++) { theSong->instrumentRemap[count] = (XLongResourceID)-1; // no remap } } } return theSong; }
void * mod_realloc(void* ptr, long size) { XPTR p; p = NULL; if (ptr) { p = XNewPtr(size); if (p) { size = XGetPtrSize((XPTR)ptr); // get old size if (size) { XBlockMove(ptr, p, size); } else { XDisposePtr(p); p = NULL; // can't get size, so fail } XDisposePtr(ptr); } } return p; }
JNIEXPORT jlong JNICALL Java_com_sun_media_sound_HeadspaceSoundbank_nOpenResourceFromByteArray(JNIEnv* e, jobject thisObj, jbyteArray bankData, jint length) { XFILE file = NULL; void *pData = NULL; TRACE0("Java_com_sun_media_sound_HeadspaceSoundbank_nOpenResourceFromByteArray.\n"); pData = XNewPtr(length); // can have memory error if (!pData) { ERROR0("Java_com_sun_media_sound_HeadspaceSoundbank_nOpenResourceFromByteArray failed to allocate memory for soundbank\n"); return 0; } (*e)->GetByteArrayRegion(e, bankData, (jint)0, (jint)length, (jbyte*)pData); file = XFileOpenResourceFromMemory(pData, length, TRUE); if (!file) { ERROR0("Failed to create resource file from data \n"); XDisposePtr(pData); return 0; } TRACE1("Java_com_sun_media_sound_HeadspaceSoundbank_nOpenResourceFromByteArray completed, returning %lu.\n", file); return (jlong) (INT_PTR) file; }
// XProcessMPEGEncoder() seems to prepend some number zero samples to the waveform. // Scan the first frame buffer for the first nonzero sample and // set *startFrame to the offset to it. static unsigned long PV_ScanForAudioDataFromMPEG(XPTR pMPEGStream, unsigned long mpegStreamSize, unsigned long *pFrameBufferCount, OPErr *pErr) { unsigned long firstNonSilentFrame; XMPEGDecodedData *stream; OPErr err; char *tempBuffer; XBOOL done; if (pErr) { *pErr = NO_ERR; } firstNonSilentFrame = 0; stream = XOpenMPEGStreamFromMemory(pMPEGStream, mpegStreamSize, &err); if (stream) { if (pFrameBufferCount) { if (stream->maxFrameBuffers > *pFrameBufferCount) { *pFrameBufferCount = stream->maxFrameBuffers; } } tempBuffer = (char *)XNewPtr(stream->frameBufferSize * sizeof(long)); if (tempBuffer) { err = XFillMPEGStreamBuffer(stream, tempBuffer, &done); if (err == NO_ERR) { firstNonSilentFrame = PV_ScanForAudioData((short int*)tempBuffer, stream->frameBufferSize, (unsigned long)stream->channels); } XDisposePtr(tempBuffer); } XCloseMPEGStream(stream); } if (pErr) { *pErr = err; } #if 0 if (firstNonSilentFrame < 448) //481 { return 448; } #endif return firstNonSilentFrame; }
// Get an empty GM_CaptureAudioStream. Will return 0 if can't allocate stream static void *PV_GetEmptyCaptureAudioStream(void) { GM_CaptureAudioStream *pStream; void * ref; ref = 0; pStream = (GM_CaptureAudioStream *)XNewPtr((INT32)sizeof(GM_CaptureAudioStream)); if (pStream) { pStream->reference = (void *)-1; ref = (void *)pStream; } return ref; }
// Create instrument from 'snd' resource ID static GM_Instrument * PV_CreateInstrumentFromResource(GM_Instrument *theMaster, XLongResourceID theID) { GM_Instrument *theI; UBYTE *theSound; CacheSampleInfo sndInfo; theI = NULL; theSound = (UBYTE *)PV_GetSampleFromID(theID, &sndInfo); if (theSound) { theI = (GM_Instrument *)XNewPtr((INT32)sizeof(GM_Instrument)); if (theI) { theI->u.w.theWaveform = (SBYTE *)theSound; if (theMaster) { theI->disableSndLooping = theMaster->disableSndLooping; theI->playAtSampledFreq = theMaster->playAtSampledFreq; theI->doKeymapSplit = FALSE; theI->notPolyphonic = theMaster->notPolyphonic; theI->avoidReverb = theMaster->avoidReverb; theI->useSampleRate = theMaster->useSampleRate; theI->sampleAndHold = theMaster->sampleAndHold; } else { theI->disableSndLooping = FALSE; theI->playAtSampledFreq = FALSE; theI->doKeymapSplit = FALSE; theI->notPolyphonic = FALSE; theI->avoidReverb = FALSE; theI->useSampleRate = FALSE; theI->sampleAndHold = FALSE; } theI->u.w.bitSize = sndInfo.bitSize; theI->u.w.channels = sndInfo.channels; theI->u.w.waveformID = sndInfo.theID; 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->u.w.baseMidiPitch = (unsigned char)sndInfo.baseKey; theI->u.w.sampledRate = sndInfo.rate; } } return theI; }
// allocate and return an list of ID's collected from ID_SND, ID_CSND, ID_ESND. pCount will // be the number of ID's, and the long array will be the list. use XDisposePtr on the return // pointer XLongResourceID * XGetAllSoundID(long *pCount) { long count, size, totalResourceCount, resourceIndex, sampleCount; XResourceType resType; XLongResourceID theID; char name[256]; XPTR data; XLongResourceID *pArray; pArray = NULL; sampleCount = 0; totalResourceCount = XCountTypes(NULL); // get total number of resource types if (totalResourceCount && pCount) { pArray = (XLongResourceID *)XNewPtr((long)sizeof(XLongResourceID) * MAX_SAMPLES); if (pArray) { for (resourceIndex = 0; resourceIndex < totalResourceCount; resourceIndex++) { resType = XGetIndexedType(NULL, resourceIndex); if ( (resType == ID_SND) || (resType == ID_CSND) || (resType == ID_ESND) ) { for (count = 0; ; count++) { data = XGetIndexedResource(resType, &theID, count, name, &size); if (data) { XDisposePtr(data); // free pointer resource pArray[sampleCount++] = theID; if (sampleCount == (MAX_SAMPLES-1)) { break; } } else { break; } } } } *pCount = sampleCount; } } return pArray; }
XMPEGDecodedData * XOpenMPEGStreamFromMemory(XPTR pBlock, unsigned long blockSize, OPErr *pErr) { XMPEGDecodedData *pStream; if (!pBlock || !blockSize || !pErr) { BAE_ASSERT(FALSE); return NULL; } #if WRITE_FILE PV_SetupWriteFile(); #endif pStream = (XMPEGDecodedData *)XNewPtr(sizeof(XMPEGDecodedData)); if (pStream) { pStream->stream = MPG_NewStreamFromMemory(pBlock, blockSize); if (pStream->stream) { pStream->sampleRate = UNSIGNED_LONG_TO_XFIXED(MPG_GetSampleRate(pStream->stream)); pStream->bitSize = (XBYTE)MPG_GetBitSize(pStream->stream); pStream->channels = (XBYTE)MPG_GetChannels(pStream->stream); pStream->bitrate = MPG_GetBitrate(pStream->stream); pStream->lengthInBytes = MPG_GetSizeInBytes(pStream->stream); pStream->lengthInSamples = MPG_GetNumberOfSamples(pStream->stream); pStream->frameBufferSize = (unsigned long)MPG_GetBufferSize(pStream->stream); pStream->maxFrameBuffers = (unsigned long)MPG_GetMaxBuffers(pStream->stream); *pErr = NO_ERR; } else { BAE_ASSERT(FALSE); XDisposePtr((XPTR)pStream); pStream = NULL; *pErr = BAD_FILE; } } else { BAE_ASSERT(FALSE); *pErr = MEMORY_ERR; } return pStream; }
void GM_AddSongSynth(GM_Song *pSong, GM_Synth *pSynth) { GM_Synth *currentSynth = pSong->pSynths; // $$kk: 07.12.99: i am going to interpret // "pSynth == NULL" as meaning "use the software synth." // we should revisit this later. if (pSynth == NULL) { pSynth = (GM_Synth *)XNewPtr((INT32)sizeof(GM_Synth)); if (!pSynth) { // $$kk: 07.12.99: this is a memory err. should report it! // but for now this is a void method.... return; } pSynth->deviceHandle = NULL; pSynth->pProgramChangeProcPtr = PV_ProcessProgramChange; pSynth->pNoteOffProcPtr = PV_ProcessNoteOff; pSynth->pNoteOnProcPtr = PV_ProcessNoteOn; pSynth->pPitchBendProcPtr = PV_ProcessPitchBend; pSynth->pProcessControllerProcPtr = PV_ProcessController; pSynth->pProcessSongSoundOffProcPtr = GM_EndSongNotes; pSynth->pNext = NULL; } if (!currentSynth) { pSong->pSynths = pSynth; return; } while (currentSynth->pNext != NULL) { currentSynth = currentSynth->pNext; } currentSynth->pNext = pSynth; return; }
// This will return a pointer to a sound. If the sound is already loaded, then just the pointer is returned. static void * PV_GetSampleFromID(XLongResourceID theID, CacheSampleInfo *pInfo) { register short int count; register void *pSample; CacheSampleInfo *pCache; CacheSampleInfo *pNewCache; pSample = NULL; pCache = NULL; if (MusicGlobals->cacheSamples) { pCache = PV_FindCacheFromID(theID); } if (pCache == NULL) { // not loaded, so load it pSample = PV_GetSampleData(theID, NULL, pInfo); pInfo->referenceCount = 1; if (pSample) { for (count = 0; count < MAX_SAMPLES; count++) { if (MusicGlobals->sampleCaches[count] == NULL) { pNewCache = (CacheSampleInfo *)XNewPtr((INT32)sizeof(CacheSampleInfo)); if (pNewCache) { pNewCache->theID = theID; *pNewCache = *pInfo; MusicGlobals->sampleCaches[count] = pNewCache; } break; } } } } else { pCache->referenceCount++; *pInfo = *pCache; pSample = pInfo->pSampleData; } return pSample; }
static void PV_TrackNameCallback(void *threadContext, GM_Song *pSong, char markerType, void *pMetaText, INT32 metaTextLength, short currentTrack) { XBYTE **tnArray,*str; threadContext; if (markerType == 0x03) { // track name if (currentTrack != -1) { str = (XBYTE *)XNewPtr(metaTextLength+1); if (str) { XBlockMove(pMetaText,str+1,metaTextLength); str[0] = (XBYTE)metaTextLength; tnArray = (XBYTE **)pSong->metaEventCallbackReference; tnArray[currentTrack] = str; } } } }
// 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; }
static OPErr MixerSourceLineCallbackProc(void* context, GM_StreamMessage message, GM_StreamData* pAS) { JNIEnv* e = (JNIEnv*)context; VTRACE2("MixerSourceLineCallbackProc, context: %lx, message: %d\n", context, message); // for STREAM_CREATE, we just need to allocate the buffer. // we need to convert from gmData->dataLength (number of frames) to number of bytes. // $$kk: 03.15.98: this is the same calculation done by PV_GetSampleSizeInBytes. however, this // method is not available outside GenAudioStreams.c right now. if (message == STREAM_CREATE) { jint const byteCount = pAS->dataLength * pAS->channelSize * (pAS->dataBitSize / 8); VTRACE1("STREAM_CREATE: %lx\n", pAS->userReference); VTRACE3("STREAM_CREATE: byteCount = %d, dataLength = %lu, frameSize = %d.\n", byteCount, pAS->dataLength, (pAS->channelSize * (pAS->dataBitSize / 8)) ); pAS->pData = XNewPtr(byteCount); if (!pAS->pData) { ERROR1("MixerSourceLineCallbackProc: STREAM_CREATE: Failed to allocate %d bytes\n", byteCount); return MEMORY_ERR; } VTRACE1("STREAM_CREATE: %lx completed\n", pAS->userReference); } else if (message == STREAM_DESTROY) { VTRACE1("STREAM_DESTROY: %lx\n", pAS->userReference); CallToJavaStreamDestroy(e, (jobject)pAS->userReference); // $$kk: 12.21.98: for some reason, this causes the vm to crash some // random time later. so we have a memory leak here. i don't know // why we can't release the memory here.... // $$kk: 03.22.00: i am putting this code back in. the memory leak is // causing problems, and so far i have not been able to reproduce the // crash. the memory leak is reported in bug #4319431: "Memory leaks // on opening and closing source DataLines." if (pAS->pData) { XDisposePtr(pAS->pData); } VTRACE1("STREAM_DESTROY: %lx completed\n", pAS->userReference); } else if (message == STREAM_GET_DATA) { int length; int frameLengthInBytes = pAS->channelSize * (pAS->dataBitSize / 8); jbyteArray array = (jbyteArray)(*e)->GetObjectField(e, (jobject)pAS->userReference, g_dataArrayFieldID); if (array == NULL) { ERROR0("STREAM_GET_DATA: failed to get array\n"); return GENERAL_BAD; } // make the callback. // pAS->dataLength is the length in FRAMES. // length returned is length in FRAMES. length = (int)CallToJavaStreamGetData(e, (jobject)pAS->userReference, array, (jint)pAS->dataLength); // length will be -1 when the stream finishes if (length < 0) { VTRACE0("STREAM_GET_DATA: got negative length; stopping stream\n"); return STREAM_STOP_PLAY; } // get the relevant array elements // $$kk: 03.15.98: can we avoid this copy?? (*e)->GetByteArrayRegion(e, array, (jint)0, (jint)(length * frameLengthInBytes), (jbyte*)(pAS->pData)); // $$kk: 04.21.00: fix for bug #4332327: "JNI leaks local references on // keeping a SourceLine open for long." (*e)->DeleteLocalRef(e, array); // set dataLength to the number of frames read pAS->dataLength = length; VTRACE2("STREAM_GET_DATA: length: %d, pAS->dataLength: %lu\n", length, pAS->dataLength); VTRACE1("STREAM_GET_DATA: %lx completed\n", pAS->userReference); } else if (message == STREAM_START) { TRACE2("STREAM_START: stream: %lx, framePosition: %lu\n", pAS->userReference, pAS->startSample); CallToJavaStreamStart(e, (jobject)pAS->userReference); TRACE1("STREAM_START: %lx completed\n", pAS->userReference); } else if (message == STREAM_STOP) { TRACE2("STREAM_STOP: %lx, framePosition: %lu\n", pAS->userReference, pAS->startSample); CallToJavaStreamStop(e, (jobject)pAS->userReference); TRACE1("STREAM_STOP: %lx completed\n", pAS->userReference); } else if (message == STREAM_EOM) { TRACE2("STREAM_EOM: %lx, framePosition: %lu\n", pAS->userReference, pAS->startSample); CallToJavaStreamEOM(e, (jobject)pAS->userReference); TRACE1("STREAM_EOM: %lx completed\n", pAS->userReference); } else if (message == STREAM_ACTIVE) { TRACE2("STREAM_ACTIVE: %lx, framePosition: %lu\n", pAS->userReference, pAS->startSample); CallToJavaStreamActive(e, (jobject)pAS->userReference); TRACE1("STREAM_ACTIVE: %lx completed\n", pAS->userReference); } else if (message == STREAM_INACTIVE) { TRACE2("STREAM_INACTIVE: %lx, framePosition: %lu\n", pAS->userReference, pAS->startSample); CallToJavaStreamInactive(e, (jobject)pAS->userReference); TRACE1("STREAM_INACTIVE: %lx completed\n", pAS->userReference); } else { ERROR1("Bad message code in MixerSourceLineCallbackProc() : %d\n", message); return GENERAL_BAD; } return NO_ERR; }
XMPEGEncodeData * XOpenMPEGEncodeStreamFromMemory(GM_Waveform *pAudio, XMPEGEncodeRate encodeRate, OPErr *pErr) { XMPEGEncodeData *encode; OPErr theErr; MPEGEncoderPrivate *pPrivate; theErr = NO_ERR; encode = NULL; if (pAudio && pErr) { if ((pAudio->bitSize == 16) && (pAudio->channels <= 2)) { encode = (XMPEGEncodeData *)XNewPtr((long)sizeof(XMPEGEncodeData)); if (encode) { encode->pAudio = pAudio; encode->encodeRate = encodeRate; encode->pPrivateData = XNewPtr((long)sizeof(MPEGEncoderPrivate)); if (encode->pPrivateData) { pPrivate = MPEG_ENCODE_PRIVATE(encode); // create new mpeg stream to prepare for encoding. Returns NULL if failed. pPrivate->encoder = MPG_EncodeNewStream(encodeRate, XFIXED_TO_UNSIGNED_LONG(pAudio->sampledRate), pAudio->channels, pAudio->theWaveform, pAudio->waveFrames); if (pPrivate->encoder) { encode->maxFrameBuffers = MPG_EncodeMaxFrames(pPrivate->encoder); encode->frameBufferSizeInBytes = MPG_EncodeMaxFrameSize(pPrivate->encoder); encode->frameBufferSize = 0; encode->currentFrameBuffer = 0; pPrivate->compressedAudioPosition = 0; pPrivate->compressedAudioSizeInBytes = encode->maxFrameBuffers * encode->frameBufferSizeInBytes; pPrivate->pCompressedAudio = XNewPtr(pPrivate->compressedAudioSizeInBytes); if (pPrivate->pCompressedAudio == NULL) { BAE_ASSERT(FALSE); theErr = MEMORY_ERR; } } else { BAE_ASSERT(FALSE); theErr = PARAM_ERR; } } else { BAE_ASSERT(FALSE); theErr = MEMORY_ERR; } if (theErr) { XDisposePtr((XPTR)encode); encode = NULL; } } else { BAE_ASSERT(FALSE); theErr = MEMORY_ERR; } } else { BAE_ASSERT(FALSE); theErr = BAD_SAMPLE; } } else { BAE_ASSERT(FALSE); theErr = PARAM_ERR; } if (pErr) { *pErr = theErr; } return encode; }
// Given an mpeg bit encode rate, and a sample rate, this will return TRUE if // this encoder can encode, or FALSE if it will not work. XBOOL XIsValidMPEGSampleRateAndEncodeRate(XMPEGEncodeRate encodeRate, XFIXED sampleRate, SndCompressionSubType subType, short int numChannels) { XBOOL safe; int count; const int TEMP_BUFFER_FRAMES = 2500; const int TEMP_BUFFER_SIZE = TEMP_BUFFER_FRAMES * 2; XPTR tempBuffer; GM_Waveform *test; OPErr err; XMPEGDecodedData *stream; XFIXED newSampleRate; numChannels; // we igore stereo samples for now. safe = FALSE; if (subType == CS_MPEG1) { newSampleRate = XGetClosestMPEGSampleRate(sampleRate, subType); if (sampleRate < newSampleRate) { return safe; } } tempBuffer = XNewPtr(TEMP_BUFFER_SIZE); if (tempBuffer) { for (count = 0; count < TEMP_BUFFER_SIZE; count++) { ((XBYTE *)tempBuffer)[count] = (XBYTE)(127.0 * cos(count)); } test = GM_NewWaveform(); if (test) { XPTR encodedData; UINT32 encodedBytes; test->theWaveform = tempBuffer; test->bitSize = 16; test->channels = 1; test->compressionType = C_NONE; test->sampledRate = sampleRate; test->waveSize = TEMP_BUFFER_SIZE; test->waveFrames = TEMP_BUFFER_FRAMES; err = XCompressMPEG(test, XGetMPEGCompressionType(encodeRate), subType, NULL, NULL, &encodedData, &encodedBytes, NULL, NULL, NULL); if (err == NO_ERR) { // Have to open the stream simply to find out if the stream we just built is // valid. stream = XOpenMPEGStreamFromMemory(encodedData, encodedBytes, &err); if (stream && (err == NO_ERR)) { safe = TRUE; } XCloseMPEGStream(stream); } XDisposePtr(encodedData); test->theWaveform = NULL; GM_FreeWaveform(test); } XDisposePtr(tempBuffer); } return safe; }
// Set the song position in midi ticks OPErr GM_SetSongTickPosition(GM_Song *pSong, UINT32 songTickPosition) { GM_Song *theSong; OPErr theErr; XBOOL foundPosition; INT32 count; theErr = NO_ERR; theSong = (GM_Song *)XNewPtr((INT32)sizeof(GM_Song)); if (theSong) { *theSong = *pSong; PV_ClearSongInstruments(theSong); // don't free the instruments if (PV_ConfigureMusic(theSong) == NO_ERR) { theSong->AnalyzeMode = SCAN_DETERMINE_LENGTH; theSong->SomeTrackIsAlive = TRUE; theSong->loopSong = FALSE; foundPosition = FALSE; GM_PauseSong(pSong); while (theSong->SomeTrackIsAlive) { // don't need a thread context here because we don't callback theErr = PV_ProcessMidiSequencerSlice(NULL, theSong); if (theErr) { break; } if (theSong->CurrentMidiClock > (UFLOAT)songTickPosition) { foundPosition = TRUE; break; } } theSong->AnalyzeMode = SCAN_NORMAL; theSong->loopSong = pSong->loopSong; if (foundPosition) { for (count = 0; count < (MAX_INSTRUMENTS*MAX_BANKS); count++) { theSong->instrumentData[count] = pSong->instrumentData[count]; } // $$kk: 07.20.99: this should be called through the synth function pointer. // but i don't think it needs to be called at all 'cause the song gets paused...? // GM_EndSongNotes(pSong); *pSong = *theSong; // copy over all song information at the new position PV_ClearSongInstruments(theSong); // don't free the instruments GM_ResumeSong(pSong); } // free duplicate song theSong->midiData = NULL; theSong->disposeSongDataWhenDone = FALSE; theSong->songEndCallbackPtr = NULL; theSong->songTimeCallbackPtr = NULL; theSong->metaEventCallbackPtr = NULL; theSong->controllerCallback = NULL; } // don't need a thread context here because we don't callback GM_FreeSong(NULL, theSong); // we ignore the error codes, because it should be ok to dispose // since this song was never engaged } return theErr; }
OPErr GM_GetSongInstrumentChanges(void *theSongResource, GM_Song **outSong, XBYTE **outTrackNames) { OPErr err; ScanMode saveScan; XBOOL saveLoop,saveAlive; GM_Song *theSong; void *newMidiData; err = NO_ERR; // don't need a thread context here because we don't callback theSong = GM_LoadSong(NULL, NULL, 0, theSongResource, NULL, 0L, NULL, FALSE, TRUE, &err); if (!theSong) { return err; } err = PV_ConfigureMusic(theSong); // first pass: count the program changes, so we know how big to make our if (err == NO_ERR) { theSong->pPatchInfo = (PatchInfo *)XNewPtr((INT32)sizeof(PatchInfo)); if (theSong->pPatchInfo) { saveScan = theSong->AnalyzeMode; theSong->AnalyzeMode = SCAN_COUNT_PATCHES; saveLoop = theSong->loopSong; theSong->loopSong = FALSE; saveAlive = theSong->SomeTrackIsAlive; theSong->SomeTrackIsAlive = TRUE; while (theSong->SomeTrackIsAlive) { // don't need a thread context here because we don't callback err = PV_ProcessMidiSequencerSlice(NULL, theSong); if (err) { break; } } } else { err = MEMORY_ERR; } } // now theSong->pPatchInfo->pgmCount points to the number of program // changes without immediately preceding bank changes, and rsCount // is the number of program changes that were discovered to have been // written into the file in running status mode (implement this later) // done in two passes because we can't keep changing the midi data // pointer, so we have figure out how big it needs to be first and // move it in place if (err == NO_ERR) { // expand the ptr enough to accomodate bank messages (4 bytes each) newMidiData = XNewPtr(XGetPtrSize(theSong->midiData) + theSong->pPatchInfo->instrCount * 4); XBlockMove(theSong->midiData,newMidiData,XGetPtrSize(theSong->midiData)); XDisposePtr(theSong->midiData); theSong->midiData = newMidiData; // start over err = PV_ConfigureMusic(theSong); // second pass: get the program changes, add bank events before each one if (err == NO_ERR) { GM_SetSongMetaEventCallback(theSong, PV_TrackNameCallback, (INT32)outTrackNames); saveScan = theSong->AnalyzeMode; theSong->AnalyzeMode = SCAN_FIND_PATCHES; saveLoop = theSong->loopSong; theSong->loopSong = FALSE; saveAlive = theSong->SomeTrackIsAlive; theSong->SomeTrackIsAlive = TRUE; while (theSong->SomeTrackIsAlive) { // don't need a thread context here because we don't callback err = PV_ProcessMidiSequencerSlice(NULL, theSong); if (err) { break; } } } } // since there was an error, caller won't be using the song if (err) { if (theSong->pPatchInfo) { XDisposePtr(theSong->pPatchInfo); } // don't need a thread context here because we don't callback GM_FreeSong(NULL, theSong); // we ignore the error codes, because it should be ok to dispose // since this song was never engaged } else { *outSong = theSong; } return err; }
// pSong GM_Song structure. Data will be cloned for this function. // pErr OPErr error type UINT32 GM_GetSongTickLength(GM_Song *pSong, OPErr *pErr) { GM_Song *theSong; UFLOAT tickLength; *pErr = NO_ERR; tickLength = 0; if (pSong->songMidiTickLength == 0) { theSong = (GM_Song *)XNewPtr((INT32)sizeof(GM_Song)); if (theSong) { *theSong = *pSong; theSong->controllerCallback = NULL; // ignore callbacks theSong->songEndCallbackPtr = NULL; theSong->songTimeCallbackPtr = NULL; theSong->metaEventCallbackPtr = NULL; theSong->disposeSongDataWhenDone = FALSE; PV_ClearSongInstruments(theSong); // don't free the instruments if (PV_ConfigureMusic(theSong) == NO_ERR) { theSong->AnalyzeMode = SCAN_DETERMINE_LENGTH; theSong->SomeTrackIsAlive = TRUE; theSong->loopSong = FALSE; theSong->songLoopCount = 0; theSong->songMaxLoopCount = 0; while (theSong->SomeTrackIsAlive) { // don't need a thread context here because we don't callback *pErr = PV_ProcessMidiSequencerSlice(NULL, theSong); if (*pErr) { break; } } theSong->AnalyzeMode = SCAN_NORMAL; pSong->songMidiTickLength = (UINT32)theSong->CurrentMidiClock; tickLength = theSong->CurrentMidiClock; pSong->songMicrosecondLength = (UINT32)theSong->songMicroseconds; theSong->midiData = NULL; theSong->songEndCallbackPtr = NULL; theSong->disposeSongDataWhenDone = FALSE; if (*pErr) { tickLength = 0; } } // don't need a thread context here because we don't callback GM_FreeSong(NULL, theSong); // we ignore the error codes, because it should be ok to dispose // since this song was never engaged } } else { tickLength = (UFLOAT)pSong->songMidiTickLength; } return (UINT32)tickLength; }
// GM_CreateLiveSong is used to create an active midi object that can be // controled directly. ie via midi commands without loading midi data // // context context of song creation. C++ 'this' pointer, thread, etc. // Its just stored in the GM_Song->context variable // songID unique ID for song structure GM_Song * GM_CreateLiveSong(void *context, XShortResourceID songID) { GM_Song *pSong; short int count; pSong = NULL; pSong = (GM_Song *)XNewPtr((INT32)sizeof(GM_Song)); if (pSong) { /* // $$kk: 05.04.99 GM_Synth *pSynth = NULL; // $$kk: 06.21.99 #if USE_EXTERNAL_SYNTH == TRUE pSynth = PV_OpenExternalSynth(pSong); #endif // USE_EXTERNAL_SYNTH == TRUE if (pSynth == NULL) { pSynth = (GM_Synth *)XNewPtr((long)sizeof(GM_Synth)); pSynth->pProgramChangeProcPtr = PV_ProcessProgramChange; pSynth->pNoteOffProcPtr = PV_ProcessNoteOff; pSynth->pNoteOnProcPtr = PV_ProcessNoteOn; pSynth->pPitchBendProcPtr = PV_ProcessPitchBend; pSynth->pProcessControllerProcPtr = PV_ProcessController; } pSong->pSynth = pSynth; */ pSong->pSynths = NULL; // TEMP /* { GM_Synth *pSynth1 = (GM_Synth *)XNewPtr((long)sizeof(GM_Synth)); if (!pSynth1) { // *pErr = MEMORY_ERR; } else { pSynth1->pProgramChangeProcPtr = PV_ProcessProgramChange; pSynth1->pNoteOffProcPtr = PV_ProcessNoteOff; pSynth1->pNoteOnProcPtr = PV_ProcessNoteOn; pSynth1->pPitchBendProcPtr = PV_ProcessPitchBend; pSynth1->pProcessControllerProcPtr = PV_ProcessController; GM_AddSongSynth(pSong, pSynth1); } #if USE_EXTERNAL_SYNTH == TRUE { GM_Synth *pSynth2 = PV_OpenExternalSynth(pSong); if (pSynth2) { GM_AddSongSynth(pSong, pSynth2); } } #endif // USE_EXTERNAL_SYNTH == TRUE } */ // END TEMP pSong->context = context; // Fill in remap first for (count = 0; count < (MAX_INSTRUMENTS*MAX_BANKS); count++) { pSong->instrumentRemap[count] = (XLongResourceID)-1; // no remap } for (count = 0; count < MAX_CHANNELS; count++) { pSong->firstChannelBank[count] = 0; pSong->firstChannelProgram[count] = -1; } PV_ConfigureInstruments(pSong); pSong->defaultReverbType = GM_GetReverbType(); pSong->songID = songID; pSong->songPitchShift = 0; pSong->allowProgramChanges = TRUE; pSong->defaultPercusionProgram = -1; pSong->maxSongVoices = MusicGlobals->MaxNotes; pSong->mixLevel = MusicGlobals->mixLevel; pSong->maxEffectVoices = MusicGlobals->MaxEffects; PV_SetTempo(pSong, 0L); pSong->songVolume = MAX_SONG_VOLUME; } return pSong; }
// Scan the midi file and determine which instrument that need to be loaded and load them. OPErr GM_LoadSongInstruments(GM_Song *theSong, XShortResourceID *pArray, XBOOL loadInstruments) { register INT32 count, loadCount, instCount, newCount; XBOOL loopSongSave; OPErr theErr; SBYTE remapUsedSaved[MAX_INSTRUMENTS]; SBYTE remapUsed[MAX_INSTRUMENTS]; #if DISPLAY_INSTRUMENTS { char text[256]; drawDebug = DNew((char *)"\pInstruments to load"); sprintf(text, "SONG %ld debug file", (INT32)theSong->songID); #if DISPLAY_INSTRUMENTS_FILE DAttachFile(drawDebug, ctop(text)); DPrint(drawDebug, "Writing output to file: Ô%pÕ\r", text); #endif } #endif // Set the sequencer to mark instruments only theErr = NO_ERR; theSong->pUsedPatchList = (SBYTE *)XNewPtr((MAX_INSTRUMENTS*MAX_BANKS*128L) / 8); if (theSong->pUsedPatchList) { for (count = 0; count < MAX_INSTRUMENTS*MAX_BANKS; count++) { theSong->remapArray[count] = count; if (pArray) { pArray[count] = (XShortResourceID)-1; } } for (count = 0; count < MAX_CHANNELS; count++) { theSong->firstChannelBank[count] = 0; theSong->firstChannelProgram[count] = -1; } theErr = PV_ConfigureMusic(theSong); if (theErr == NO_ERR) { if (theSong->defaultPercusionProgram == -1) { theSong->channelBank[PERCUSSION_CHANNEL] = 0; theSong->firstChannelBank[PERCUSSION_CHANNEL] = 0; } else { if (theSong->defaultPercusionProgram) { theSong->firstChannelProgram[PERCUSSION_CHANNEL] = theSong->defaultPercusionProgram; GM_SetUsedInstrument(theSong, (XLongResourceID)theSong->defaultPercusionProgram, 60, TRUE); } } theSong->AnalyzeMode = SCAN_SAVE_PATCHES; theSong->SomeTrackIsAlive = TRUE; loopSongSave = theSong->loopSong; theSong->loopSong = FALSE; while (theSong->SomeTrackIsAlive) { theErr = PV_ProcessMidiSequencerSlice(NULL, theSong); if (theErr) { break; } } theSong->AnalyzeMode = SCAN_NORMAL; theSong->loopSong = loopSongSave; if (theErr == NO_ERR) { // are we trying to load any instruments? This is for the case were there are no program changes. We must do something newCount = FALSE; for (count = 0; count < MAX_CHANNELS; count++) { if (count != PERCUSSION_CHANNEL) // only look at non percussion channels { if (theSong->firstChannelProgram[count] != -1) { newCount = TRUE; break; } } } if (newCount == FALSE) { // there have been no program changes. So set up just the piano in all channels for (count = 0; count < MAX_CHANNELS; count++) { theSong->firstChannelProgram[count] = 0; theSong->channelProgram[count] = 0; } GM_SetUsedInstrument(theSong, 0, -1, TRUE); // load the entire piano } #if DISPLAY_INSTRUMENTS DPrint(drawDebug, "Loading instruments:\r"); #endif instCount = 0; for (count = 0; count < MAX_INSTRUMENTS*MAX_BANKS; count++) { // instrument needs to be loaded if (GM_IsInstrumentUsed(theSong, count, -1)) { #if DISPLAY_INSTRUMENTS DPrint(drawDebug, "Instrument %ld: ", (INT32)count); #endif loadCount = theSong->instrumentRemap[count]; if (loadCount == -1) { loadCount = count; } #if DISPLAY_INSTRUMENTS else { DPrint(drawDebug, "remapped to %ld ", (INT32)loadCount); } #endif #if DISPLAY_INSTRUMENTS DPrint(drawDebug, "loading instrument %ld\r", loadCount); #endif if (pArray) { pArray[instCount++] = (short)loadCount; } if (loadInstruments) { if (loadCount != count) { GM_GetInstrumentUsedRange(theSong, loadCount, remapUsedSaved); // save GM_GetInstrumentUsedRange(theSong, count, remapUsed); GM_SetInstrumentUsedRange(theSong, loadCount, remapUsed); } theErr = GM_LoadInstrument(theSong, loadCount); if (theErr != NO_ERR) { // if the instrument is some other bank, then go back to the standard GM bank before failing if (loadCount > MAX_INSTRUMENTS) { #if DISPLAY_INSTRUMENTS DPrint(drawDebug, "Failed loading extra bank instrument %ld, falling back to GM.\r", loadCount); #endif newCount = (loadCount % MAX_INSTRUMENTS); newCount += ((loadCount / MAX_INSTRUMENTS) & 1) * MAX_INSTRUMENTS; loadCount = newCount; #if DISPLAY_INSTRUMENTS DPrint(drawDebug, "Loading instrument %ld\r", loadCount); #endif theErr = GM_LoadInstrument(theSong, loadCount); if (theSong->ignoreBadInstruments) { theErr = NO_ERR; } } else { // we are in GM, so check our ignore flag if (theSong->ignoreBadInstruments) { theErr = NO_ERR; } } } if (loadCount != count) { GM_SetInstrumentUsedRange(theSong, loadCount, remapUsedSaved); // save } if (theErr) { #if DISPLAY_INSTRUMENTS DPrint(drawDebug, "Failed to load instrument %ld (%ld)\r", (INT32)loadCount, (INT32)theErr); #endif break; } theErr = GM_RemapInstrument(theSong, loadCount, count); // remap from: to // we are in GM, so check our ignore flag if (theSong->ignoreBadInstruments) { theErr = NO_ERR; } } } } } } if (theErr != NO_ERR) { GM_UnloadSongInstruments(theSong); // ignore error } XDisposePtr(theSong->pUsedPatchList); theSong->pUsedPatchList = NULL; } else { theErr = MEMORY_ERR; } #if DISPLAY_INSTRUMENTS DPrint(drawDebug, "\rClick to exit"); while (Button() == FALSE) {}; while (Button()) {}; FlushEvents(mDownMask, 0); DCopy(drawDebug); DDispose(drawDebug); #endif return theErr; }
// Set the song position in microseconds // $$kk: 08.12.98 merge: changed this method OPErr GM_SetSongMicrosecondPosition(GM_Song *pSong, UINT32 songMicrosecondPosition) { GM_Song *theSong; OPErr theErr; XBOOL foundPosition; INT32 count; XBOOL songPaused = FALSE; // $$kk: 02.10.98 // the way this was, it paused the song, changed the position, and resumed. // if this is applied to a paused song, it suddenly starts playing again! // i am adding a mechanism to record whether the song was paused and only // resume it in that case. theErr = NO_ERR; theSong = (GM_Song *)XNewPtr((INT32)sizeof(GM_Song)); if (theSong) { *theSong = *pSong; PV_ClearSongInstruments(theSong); // don't free the instruments if (PV_ConfigureMusic(theSong) == NO_ERR) { theSong->AnalyzeMode = SCAN_DETERMINE_LENGTH; theSong->SomeTrackIsAlive = TRUE; theSong->loopSong = FALSE; foundPosition = FALSE; // $$kk: 02.10.98: keep track of whether song is paused if (pSong->songPaused) songPaused = TRUE; GM_PauseSong(pSong); while (theSong->SomeTrackIsAlive) { // don't need a thread context here because we don't callback theErr = PV_ProcessMidiSequencerSlice(NULL, theSong); if (theErr) { break; } if (theSong->songMicroseconds > (UFLOAT)songMicrosecondPosition) { foundPosition = TRUE; break; } } theSong->AnalyzeMode = SCAN_NORMAL; theSong->loopSong = pSong->loopSong; if (foundPosition) { for (count = 0; count < (MAX_INSTRUMENTS*MAX_BANKS); count++) { theSong->instrumentData[count] = pSong->instrumentData[count]; } // $$kk: 07.20.99: this should be called through the synth function pointer. // but i don't think it needs to be called at all 'cause the song gets paused...? // GM_EndSongNotes(pSong); *pSong = *theSong; // copy over all song information at the new position PV_ClearSongInstruments(theSong); // don't free the instruments // $$kk: 02.10.98: do not resume if song was paused before if (!songPaused) { GM_ResumeSong(pSong); } } // free duplicate song theSong->midiData = NULL; theSong->songEndCallbackPtr = NULL; theSong->disposeSongDataWhenDone = FALSE; } // don't need a thread context here because we don't callback GM_FreeSong(NULL, theSong); // we ignore the error codes, because it should be ok to dispose // since this song was never engaged } return theErr; }
// 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; }
/* ** This will walk through all Instrument resources and collect all snd resources ** that are used. */ short int XGatherAllSoundsFromAllInstruments(XShortResourceID *pSndArray, short int maxArraySize) { short int count, icount, sndCount, jcount; XShortResourceID soundID; XBOOL goodSound; XShortResourceID instArray[MAX_INSTRUMENTS * MAX_BANKS]; short int totalInstruments; XShortResourceID sndArray[MAX_SAMPLES]; short int totalSnds; XShortResourceID *completeSndArray; short int completeSndCount; InstrumentResource *theX; long size; completeSndCount = 0; sndCount = 0; completeSndArray = (XShortResourceID *)XNewPtr(MAX_INSTRUMENTS * MAX_BANKS * 128L * sizeof(XShortResourceID)); if (completeSndArray) { sndCount = 0; totalInstruments = XGetInstrumentArray(instArray, MAX_INSTRUMENTS * MAX_BANKS); if (totalInstruments) { for (count = 0; count < totalInstruments; count++) { theX = (InstrumentResource *)XGetAndDetachResource(ID_INST, instArray[count], &size); if (theX) { totalSnds = XCollectSoundsFromInstrument(theX, sndArray, MAX_SAMPLES); XDisposePtr((XPTR)theX); for (icount = 0; icount < totalSnds; icount++) { completeSndArray[completeSndCount++] = sndArray[icount]; } } } XBubbleSortArray((short *)completeSndArray, (short)completeSndCount); // Remove duplicates for (jcount = 0; jcount < completeSndCount; jcount++) { goodSound = TRUE; soundID = completeSndArray[jcount]; for (count = 0; count < sndCount; count++) { if (soundID == pSndArray[count]) { goodSound = FALSE; break; } } if (goodSound) { if (sndCount < maxArraySize) { pSndArray[sndCount++] = soundID; } } } } XDisposePtr((XPTR)completeSndArray); } return sndCount; }
// given a MPEG stream and an offset, decode the mpeg stream into memory OPErr XExpandMPEG(GM_Waveform const* src, UINT32 startFrame, GM_Waveform* dst) { OPErr err; XMPEGDecodedData* stream; BAE_ASSERT(dst); BAE_ASSERT(src); *dst = *src; dst->theWaveform = NULL; dst->compressionType = C_NONE; stream = XOpenMPEGStreamFromMemory(src->theWaveform, src->waveSize, &err); if (stream) { UINT32 decodingBytes; XPTR decodingData; decodingBytes = stream->maxFrameBuffers * stream->frameBufferSize; decodingData = XNewPtr(decodingBytes); if (decodingData) { UINT32 const bytesPerFrame = dst->channels * sizeof(short); XBYTE* data; UINT32 startByte; UINT32 count; data = (XBYTE*)decodingData; startByte = startFrame * bytesPerFrame; count = 0; while (count < stream->maxFrameBuffers) { XBOOL done; err = XFillMPEGStreamBuffer(stream, data, &done); if (startByte == 0) { data += stream->frameBufferSize; } else if (startByte < stream->frameBufferSize) { XBlockMove(data + startByte, data, stream->frameBufferSize - startByte); data += stream->frameBufferSize - startByte; startByte = 0; } else { // don't increment "data", skip the buffer startByte -= stream->frameBufferSize; } if ((err != NO_ERR) || done) { break; } count++; } if (err == NO_ERR) { UINT32 decodedBytes; UINT32 definedBytes; decodedBytes = data - (XBYTE*)decodingData; definedBytes = dst->waveFrames * bytesPerFrame; if (decodedBytes < definedBytes) { //MOE: This is a little weird that the number of frames decoded is less // When ascertaining the true number of frames is better (with a // better MPEG decoder), we shouldn't see this happening. definedBytes = decodedBytes; dst->waveFrames = decodedBytes / bytesPerFrame; } if (definedBytes < decodingBytes) { XPTR trimmedData; trimmedData = XResizePtr(decodingData, definedBytes); BAE_ASSERT(trimmedData); // making memory block smaller shouldn't fail if (trimmedData) { decodingData = trimmedData; } } dst->theWaveform = (XSBYTE *)decodingData; dst->waveSize = definedBytes; } else { BAE_ASSERT(FALSE); // check "err" XDisposePtr(decodingData); decodingData = NULL; } } else { err = MEMORY_ERR; } XCloseMPEGStream(stream); } return err; }
// Given a list of instruments, this will return the sample ID's that are required to load // all of these instruments short int XGetSamplesFromInstruments(XShortResourceID *pInstArray, short int maxInstArraySize, XShortResourceID *pSndArray, short int maxSndArraySize) { register long count, instCount, sndCount, newCount, completeSndCount; XShortResourceID soundID; XShortResourceID *completeSndArray; XBOOL goodSound; InstrumentResource *theX; long size; sndCount = 0; completeSndArray = (XShortResourceID *)XNewPtr(sizeof(XShortResourceID) * MAX_INSTRUMENTS * 128L); if (completeSndArray) { if ( (pInstArray) && (pSndArray) ) { instCount = 0; for (count = 0; count < maxInstArraySize; count++) { if (pInstArray[count] != (XShortResourceID)-1) { instCount++; } else { break; } } for (count = 0; count < maxSndArraySize; count++) { pSndArray[count] = (XShortResourceID)-1; completeSndArray[count] = (XShortResourceID)-1; } completeSndCount = 0; for (count = 0; count < instCount; count++) { theX = (InstrumentResource *)XGetAndDetachResource(ID_INST, pInstArray[count], &size); if (theX) { newCount = XCollectSoundsFromInstrument(theX, &completeSndArray[completeSndCount], 128); XDisposePtr((XPTR)theX); completeSndCount += newCount; } } // remove duplicates in snds for (newCount = 0; newCount < completeSndCount; newCount++) { goodSound = TRUE; soundID = completeSndArray[newCount]; for (count = 0; count < sndCount; count++) { if (soundID == pSndArray[count]) { goodSound = FALSE; break; } } if (goodSound) { pSndArray[sndCount++] = (XShortResourceID)soundID; } } XBubbleSortArray((short *)pSndArray, (short)sndCount); } XDisposePtr(completeSndArray); } return (short)sndCount; }