OPErr XCloseMPEGEncodeStream(XMPEGEncodeData *stream, XPTR *pReturnedBuffer, unsigned long *pReturnedSize) { MPEGEncoderPrivate *pPrivate; OPErr err; err = NO_ERR; if (stream) { pPrivate = MPEG_ENCODE_PRIVATE(stream); if (pPrivate->encoder) { MPG_EncodeFreeStream(pPrivate->encoder); } if (pReturnedSize && pReturnedBuffer) { *pReturnedBuffer = pPrivate->pCompressedAudio; *pReturnedSize = pPrivate->compressedAudioPosition; } XDisposePtr((XPTR)stream->pPrivateData); XDisposePtr((XPTR)stream); } else { BAE_ASSERT(FALSE); err = PARAM_ERR; } return err; }
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; }
// Free cache entry static void PV_FreeCacheEntry(CacheSampleInfo *pCache) { if (pCache) { if (pCache->pSampleData) { XDisposePtr(pCache->pMasterPtr); } XDisposePtr(pCache); } }
// Given a song pointer, this will attempt to free all memory related to the song: midi // data, instruments, samples, etc. It can fail and will return STILL_PLAYING if // midi data is still being accessed, or samples, or instruments. // // If you pass NULL, then this function will be called recursively will all songs // currently playing. OPErr GM_FreeSong(void *threadContext, GM_Song *pSong) { OPErr err; XPTR midiData; err = NO_ERR; GM_EndSong(threadContext, pSong); if (pSong) { GM_KillSongNotes(pSong); // we must kill the notes because we are about to free // instrument memory if (pSong->processingSlice == FALSE) { GM_PauseSong(pSong); midiData = (XPTR)pSong->midiData; // save midi pointer now pSong->midiData = NULL; // and disable midi decoder now, just // in case the decoder thread comes to life GM_SetCacheSamples(pSong, FALSE); err = GM_UnloadSongInstruments(pSong); if (err == NO_ERR) { if (pSong->disposeSongDataWhenDone) { XDisposePtr(midiData); } XDisposePtr((XPTR)pSong->controllerCallback); #if 0 && USE_CREATION_API == TRUE if (pSong->pPatchInfo) { PV_FreePatchInfo(pSong); // must free before killing pSong pointer } #endif XDisposePtr((XPTR)pSong); } else { //DebugStr("\pGM_FreeSong::GM_UnloadSongInstruments::STILL_PLAYING"); } } else { //DebugStr("\pGM_FreeSong::STILL_PLAYING"); err = STILL_PLAYING; } } return err; }
XBOOL XIsSampleUsedInAllInstruments(XShortResourceID soundSampleID, XShortResourceID *pWhichInstrument) { InstrumentResource *theX; short int count, totalInstruments; XBOOL used; long size; XShortResourceID instArray[MAX_INSTRUMENTS * MAX_BANKS]; used = FALSE; totalInstruments = XGetInstrumentArray(instArray, MAX_INSTRUMENTS * MAX_BANKS); if (totalInstruments && pWhichInstrument) { for (count = 0; count < totalInstruments; count++) { theX = (InstrumentResource *)XGetAndDetachResource(ID_INST, instArray[count], &size); if (theX) { used = XIsSoundUsedInInstrument(theX, soundSampleID); XDisposePtr((XPTR)theX); if (used) { *pWhichInstrument = instArray[count]; break; } } } } return used; }
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; }
/* ** Return the number of INST's and an array of resource IDs */ short int XGetInstrumentArray(XShortResourceID *instArray, short maxArraySize) { short int icount, totalCount; long size; XPTR theRes; /* Collect INST resources */ totalCount = 0; if (instArray) { for (icount = 0; icount < maxArraySize; icount++) { theRes = XGetAndDetachResource(ID_INST, icount, &size); if (theRes) { XDisposePtr(theRes); instArray[totalCount++] = (XShortResourceID)icount; if (totalCount > maxArraySize) { totalCount = maxArraySize; break; } } } XBubbleSortArray((short *)instArray, (short)totalCount); } return totalCount; }
static void PV_FreeCaptureAudioStream(GM_CaptureAudioStream *found) { GM_CaptureAudioStream *next, *last; found = PV_CaptureAudioStreamGetFromReference(found); // verify as valid if (found) { if (found->streamID == CAPTURE_STREAM_ID) { last = next = theCaptureStreams; while (next) { if (next == found) // found object in list? { if (next == theCaptureStreams) // is object the top object { theCaptureStreams = next->pNext; // yes, change to next object } else { if (last) // no, change last to point beyond next { last->pNext = next->pNext; } } next->streamID = 0; XDisposePtr(next); // clean up break; } last = next; next = next->pNext; } } } }
// 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; }
XShortResourceID XCheckValidInstrument(XShortResourceID theID) { InstrumentResource *theX; short int totalSnds; XShortResourceID sndArray[MAX_SAMPLES]; short int count; XShortResourceID badLoad; XPTR theSnd; long size; badLoad = 0; theX = (InstrumentResource *)XGetAndDetachResource(ID_INST, theID, &size); if (theX) { totalSnds = XCollectSoundsFromInstrument(theX, sndArray, MAX_SAMPLES); XDisposePtr((XPTR)theX); for (count = 0; count < totalSnds; count++) { theSnd = XGetAndDetachResource(ID_CSND, sndArray[count], &size); // look for compressed version first if (theSnd == NULL) { theSnd = XGetAndDetachResource(ID_SND, sndArray[count], &size); } if (theSnd == NULL) { theSnd = XGetAndDetachResource(ID_ESND, sndArray[count], &size); } if (theSnd == NULL) { badLoad = sndArray[count]; } XDisposePtr(theSnd); if (badLoad) { break; } } } return badLoad; }
short int XCollectSoundsFromInstrumentID(XShortResourceID theID, XShortResourceID *sndArray, short maxArraySize) { InstrumentResource *theX; short int totalSnds; long size; totalSnds = 0; theX = (InstrumentResource *)XGetAndDetachResource(ID_INST, theID, &size); if (theX) { totalSnds = XCollectSoundsFromInstrument(theX, sndArray, maxArraySize); XDisposePtr((XPTR)theX); } return totalSnds; }
// 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; }
// 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; }
OPErr XCloseMPEGStream(XMPEGDecodedData *pStream) { OPErr err; #if WRITE_FILE PV_CleanupWriteFile(); #endif err = NO_ERR; if (pStream) { if (pStream->stream) { MPG_FreeStream(pStream->stream); } XDisposePtr((XPTR)pStream); } else { BAE_ASSERT(FALSE); err = PARAM_ERR; } return err; }
XERR XCopyInstrumentResources(XShortResourceID *pInstCopy, short int instCount, XFILE readFileRef, XFILE writeFileRef, XBOOL copyNames) { short int count; XPTR pData; short int theID; long size; char theName[256]; if (instCount && pInstCopy) { XFileUseThisResourceFile(readFileRef); /* from resource file */ for (count = 0; count < instCount; count++) { theID = pInstCopy[count]; XFileUseThisResourceFile(writeFileRef); pData = XGetFileResource(writeFileRef, ID_INST, theID, theName, &size); if (pData == NULL) // check to see that its not there already!! { XFileUseThisResourceFile(readFileRef); /* from resource file */ pData = XGetFileResource(readFileRef, ID_INST, theID, theName, &size); if (pData) { XFileUseThisResourceFile(writeFileRef); if (copyNames == FALSE) { theName[0] = 0; } XAddResource(ID_INST, theID, theName, pData, size); } } XDisposePtr(pData); } } return 0; }
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; }
// Multi source user config based streaming // This will setup a streaming audio object. // // INPUT: // userReference This is a reference value that will be returned and should be passed along to all AudioStream // functions. // // pProc is a GM_StreamObjectProc proc pointer. At startup of the streaming the proc will be called // with STREAM_CREATE, then followed by two STREAM_GET_DATA calls to get two buffers of data, // and finally STREAM_DESTROY when finished. // // OUTPUT: // long This is an audio stream reference number. Will be 0 if error void *GM_AudioCaptureStreamSetup( void *platformContext, // platform context void *userReference, // user reference GM_StreamObjectProc pProc, // control callback UINT32 bufferSize, // buffer size in bytes XFIXED sampleRate, // Fixed 16.16 char dataBitSize, // 8 or 16 bit data char channelSize, // 1 or 2 channels of date OPErr *pErr) { GM_CaptureAudioStream *pStream; void * returnedReference; OPErr haeErr; haeErr = NO_ERR; returnedReference = 0; if ( (pErr) && (pProc) && ( (channelSize >= 1) || (channelSize <= 2) ) && ( (dataBitSize == 8) || (dataBitSize == 16) ) ) { returnedReference = PV_GetEmptyCaptureAudioStream(); if (returnedReference) { pStream = (GM_CaptureAudioStream *)returnedReference; pStream->streamCallback = pProc; pStream->streamUnderflow = FALSE; pStream->reference = userReference; pStream->samplesCaptured = 0; pStream->streamShuttingDown = FALSE; pStream->streamData.pData = NULL; pStream->streamData.userReference = pStream->reference; pStream->streamData.streamReference = pStream; pStream->streamData.sampleRate = sampleRate; pStream->streamData.dataBitSize = dataBitSize; pStream->streamData.channelSize = channelSize; pStream->platformContext = platformContext; // $$kk: 10.13.98: right now we are not allowing the user to set the size; // we force use of the device value.... pStream->streamData.dataLength = bufferSize / PV_GetSampleSizeInBytes(&pStream->streamData); // $$kk: 10.09.98: here we request allocation of enough memory for both buffers haeErr = (*pProc)(platformContext, STREAM_CREATE, &pStream->streamData); if (haeErr == NO_ERR) { pStream->pStreamBuffer1 = pStream->streamData.pData; // convert from frames to bytes // $$kk: 10.09.98: we divide the allocated data into two buffers, each half the total length pStream->streamBufferLength = pStream->streamData.dataLength * PV_GetSampleSizeInBytes(&pStream->streamData) / 2; pStream->pStreamBuffer2 = (char *)pStream->streamData.pData + pStream->streamBufferLength; PV_AddCaptureAudioStream(pStream); // ok add stream } else { pStream->streamCallback = NULL; (*pProc)(platformContext, STREAM_DESTROY, &pStream->streamData); haeErr = DEVICE_UNAVAILABLE; } } else { haeErr = MEMORY_ERR; } } else { haeErr = PARAM_ERR; } if (haeErr) { XDisposePtr((XPTR)returnedReference); returnedReference = 0; } if (pErr) { *pErr = haeErr; } return returnedReference; }
/* ** 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; }
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; }
// 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; }
// 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 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; }
// Return a pointer to a 'snd' resource from its ID. // If a handle is passed, then its assumed to be a snd resource and is used instead. It is not disposed // of after use, so you must dispose of it static void * PV_GetSampleData(XLongResourceID theID, XPTR useThisSnd, CacheSampleInfo *pInfo) { XPTR theData, thePreSound; char *theSound; CacheSampleInfo theSoundData; SampleDataInfo newSoundInfo; INT32 size; theSound = NULL; if (useThisSnd) { theData = useThisSnd; } else { theData = XGetSoundResourceByID(theID, &size); } if (theData) { // convert snd resource into a simple pointer of data with information thePreSound = XGetSamplePtrFromSnd(theData, &newSoundInfo); if (newSoundInfo.pMasterPtr != theData) { // this means that XGetSamplePtrFromSnd created a new sample XDisposePtr(theData); } if (thePreSound) { // validate loop points if ((newSoundInfo.loopStart > newSoundInfo.loopEnd) || (newSoundInfo.loopEnd > newSoundInfo.frames) || ((newSoundInfo.loopEnd - newSoundInfo.loopStart) < MIN_LOOP_SIZE) ) { // disable loops newSoundInfo.loopStart = 0; newSoundInfo.loopEnd = 0; } theSoundData.theID = (short int)theID; theSoundData.waveSize = newSoundInfo.size; theSoundData.waveFrames = newSoundInfo.frames; theSoundData.loopStart = newSoundInfo.loopStart; theSoundData.loopEnd = newSoundInfo.loopEnd; theSoundData.baseKey = newSoundInfo.baseKey; theSoundData.bitSize = (char)newSoundInfo.bitSize; theSoundData.channels = (char)newSoundInfo.channels; theSoundData.rate = newSoundInfo.rate; #if DISPLAY_INSTRUMENTS DPrint(drawDebug, "---->Getting 'snd' ID %ld rate %lX loopstart %ld loopend %ld basekey %ld\r", (INT32)theID, theSoundData.rate, theSoundData.loopStart, theSoundData.loopEnd, (INT32)theSoundData.baseKey); #endif theSoundData.pSampleData = thePreSound; theSoundData.pMasterPtr = newSoundInfo.pMasterPtr; theSoundData.cacheBlockID = ID_INST; *pInfo = theSoundData; theSound = (char *)thePreSound; } } return theSound; }
// This will encode an MPEG stream from a formatted GM_Waveform OPErr XCompressMPEG(GM_Waveform const* wave, SndCompressionType compressionType, SndCompressionSubType compressionSubType, XCompressStatusProc proc, void* procData, XPTR* pCompressedData, XDWORD* pCompressedBytes, XDWORD* pFrameBufferCount, XDWORD* pFrameBufferBytes, //MOE: we don't really need these XDWORD* startFrame) { XMPEGEncodeRate const encodeRate = XGetMPEGEncodeRate(compressionType); GM_Waveform hackedWave; XMPEGEncodeRate hackedEncodeRate; OPErr err; XMPEGEncodeData* mpegEncode; unsigned long originalOffset; // capture any original silence prior to encoding originalOffset = PV_ScanForAudioData((short int *)wave->theWaveform, wave->waveSize, wave->channels); hackedWave = *wave; XGetClosestMPEGSampleRateAndEncodeRate(wave->sampledRate, encodeRate, &hackedWave.sampledRate, &hackedEncodeRate, compressionSubType ); // MOE: The above code depends on the fact that XMPEGEncodeRate{}s are defined to // equal their bitrates divided by 1024 (defined in X_Formats.h). *pCompressedData = NULL; mpegEncode = XOpenMPEGEncodeStreamFromMemory(&hackedWave, hackedEncodeRate, &err); if (mpegEncode) { OPErr closeErr; while (TRUE) { err = XProcessMPEGEncoder(mpegEncode); if (err != NO_ERR) { if (err == STREAM_STOP_PLAY) err = NO_ERR; else BAE_ASSERT(FALSE); break; } if (proc && (*proc)(procData, mpegEncode->currentFrameBuffer, mpegEncode->maxFrameBuffers)) { err = ABORTED_PROCESS; break; } } if (pFrameBufferBytes) { *pFrameBufferBytes = mpegEncode->frameBufferSizeInBytes; } if (pFrameBufferCount) { *pFrameBufferCount = mpegEncode->maxFrameBuffers; } closeErr = XCloseMPEGEncodeStream(mpegEncode, pCompressedData, pCompressedBytes); if (err == NO_ERR) { err = closeErr; } if ((err == NO_ERR) && startFrame) { // 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. #if 0 *startFrame = 0; #else *startFrame = PV_ScanForAudioDataFromMPEG(*pCompressedData, *pCompressedBytes, pFrameBufferCount, &err); if (*startFrame > originalOffset) { *startFrame -= originalOffset; } #endif } if (err != NO_ERR) { XDisposePtr(*pCompressedData); *pCompressedData = NULL; } } return err; }
// 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; }
OPErr GM_UnloadInstrument(GM_Song *pSong, XLongResourceID instrument) { register GM_Instrument *theI; register GM_KeymapSplit *k; register OPErr theErr; register short int splitCount; theErr = BAD_INSTRUMENT; if ( (instrument >= 0) && (instrument < (MAX_INSTRUMENTS*MAX_BANKS)) ) { if (pSong) { theErr = NO_ERR; theI = pSong->instrumentData[instrument]; if (theI) { if (theI->processingSlice == FALSE) { theI->usageReferenceCount--; // decrement reference count if (theI->usageReferenceCount == 0) { pSong->instrumentData[instrument] = NULL; // do this first so other threads won't walk into // bad news land if (theI->doKeymapSplit) { k = theI->u.k.keySplits; for (splitCount = 0; splitCount < theI->u.k.KeymapSplitCount; splitCount++) { if (k->pSplitInstrument) { if (k->pSplitInstrument->u.w.theWaveform) { PV_FreeCacheEntryFromPtr(k->pSplitInstrument->u.w.theWaveform); } XDisposePtr(k->pSplitInstrument); } k++; } } else { if (theI->u.w.theWaveform) { PV_FreeCacheEntryFromPtr(theI->u.w.theWaveform); } } XDisposePtr((void FAR *)theI); } else { // duplicate reference theErr = NO_ERR; } } else { theErr = STILL_PLAYING; } } // else // { // theErr = BAD_INSTRUMENT; // } } else { theErr = NOT_SETUP; } } else { theErr = PARAM_ERR; } return theErr; }
// Given a song ID and two arrays, this will return the INST resources ID and the 'snd ' resource ID // that are needed to load the song terminated with a -1. // Will return 0 for success or 1 for failure OPErr XGetSongInstrumentList(XShortResourceID theSongID, XShortResourceID *pInstArray, short int maxInstArraySize, XShortResourceID *pSndArray, short int maxSndArraySize) { long count, instCount, sndCount, newCount, completeSndCount; XShortResourceID soundID; SongResource *theSong; XShortResourceID completeSndArray[MAX_SAMPLES]; XShortResourceID completeInstArray[MAX_INSTRUMENTS * MAX_BANKS]; XBOOL goodSound; OPErr theErr; long size; theErr = NO_ERR; if ( (pInstArray) && (pSndArray) ) { for (count = 0; count < maxInstArraySize; count++) { pInstArray[count] = (XShortResourceID)-1; completeInstArray[count] = (XShortResourceID)-1; } for (count = 0; count < maxSndArraySize; count++) { pSndArray[count] = (XShortResourceID)-1; completeSndArray[count] = (XShortResourceID)-1; } theSong = (SongResource *)XGetAndDetachResource(ID_SONG, theSongID, &size); if (theSong) { instCount = GM_GetUsedPatchlist(theSong, NULL, 0L, completeInstArray, &theErr); if (instCount && (theErr == 0) ) { // remove duplicates in inst sndCount = 0; for (newCount = 0; newCount < instCount; newCount++) { goodSound = TRUE; soundID = completeInstArray[newCount]; for (count = 0; count < sndCount; count++) { if (soundID == pInstArray[count]) { goodSound = FALSE; break; } } if (goodSound) { pInstArray[sndCount++] = (XShortResourceID)soundID; } } instCount = sndCount; XBubbleSortArray((short *)pInstArray, (short)instCount); completeSndCount = 0; for (count = 0; count < instCount; count++) { newCount = XCollectSoundsFromInstrumentID(pInstArray[count], &completeSndArray[completeSndCount], 128); completeSndCount += newCount; } // remove duplicates in snds sndCount = 0; 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); } else { theErr = BAD_MIDI_DATA; } } XDisposePtr((XPTR)theSong); } return theErr; }
XERR XCopySndResources(XShortResourceID *pSndCopy, short int sndCount, XFILE readFileRef, XFILE writeFileRef, XBOOL protect, XBOOL copyNames) { short int count, resCount; XPTR pData; short int theID; char theName[256]; long size; long soundTypes[] = {ID_SND, ID_ESND, ID_CSND}; if (sndCount && pSndCopy) { XFileUseThisResourceFile(readFileRef); /* from resource file */ for (count = 0; count < sndCount; count++) { theID = pSndCopy[count]; // determine if the resource is already in the written file XFileUseThisResourceFile(writeFileRef); pData = XGetAndDetachResource(ID_SND, theID, &size); if (pData == NULL) { pData = XGetAndDetachResource(ID_ESND, theID, &size); } if (pData == NULL) { pData = XGetAndDetachResource(ID_CSND, theID, &size); } XDisposePtr(pData); if (pData == NULL) // check to see that its not there already!! { for (resCount = 0; resCount < 3; resCount++) { XFileUseThisResourceFile(readFileRef); /* from resource file */ pData = XGetFileResource(readFileRef, soundTypes[resCount], theID, theName, &size); if (pData) { if (copyNames == FALSE) { theName[0] = 0; } XFileUseThisResourceFile(writeFileRef); if (protect && (soundTypes[resCount] == ID_SND)) { XEncryptData(pData, size); XAddResource(ID_ESND, theID, theName, pData, size); } else { XAddResource(soundTypes[resCount], theID, theName, pData, size); } XDisposePtr(pData); } } } else { XDisposePtr(pData); } } } return 0; }
XERR XCopySongMidiResources(XLongResourceID theSongID, XFILE readFileRef, XFILE writeFileRef, XBOOL protect, XBOOL copyNames) { XPTR pData; SongResource *theSong; long songSize; XResourceType theDataType; short int theID; char theName[256], theSongName[256]; unsigned long dataSize, newSize; XPTR newMidiData; XFileUseThisResourceFile(readFileRef); // from resource file theSong = (SongResource *)XGetFileResource(readFileRef, ID_SONG, theSongID, theSongName, &songSize); if (theSong) { theID = XGetSongResourceObjectID(theSong); if (protect) { pData = XGetMidiData(theID, (long *)&dataSize, &theDataType); if (pData) { newMidiData = XCompressAndEncrypt(pData, dataSize, &newSize); if (newMidiData) { theDataType = ID_ECMI; XDisposePtr(pData); pData = newMidiData; } else { theDataType = ID_MIDI; } } } else { theDataType = ID_CMID; pData = XGetFileResource(readFileRef, ID_CMID, theID, theName, (long *)&dataSize); if (pData == NULL) { theDataType = ID_ECMI; pData = XGetFileResource(readFileRef, ID_ECMI, theID, theName, (long *)&dataSize); } if (pData == NULL) { theDataType = ID_EMID; pData = XGetFileResource(readFileRef, ID_EMID, theID, theName, (long *)&dataSize); } if (pData == NULL) { theDataType = ID_MIDI; pData = XGetFileResource(readFileRef, ID_MIDI, theID, theName, (long *)&dataSize); } if (pData == NULL) { theDataType = ID_MIDI; // convert it to the new type pData = XGetFileResource(readFileRef, ID_MIDI_OLD, theID, theName, (long *)&dataSize); } } if (pData) { // write midi resource XFileUseThisResourceFile(writeFileRef); if (copyNames == FALSE) { theName[0] = 0; } XAddResource(theDataType, theID, theName, pData, (long)dataSize); XDisposePtr(pData); // write song resource XFileUseThisResourceFile(readFileRef); if (protect) { XSetSongLocked(theSong, TRUE); } XFileUseThisResourceFile(writeFileRef); XAddResource(ID_SONG, theID, theName, theSong, songSize); XDisposePtr(theSong); } } return 0; }