INT32 GM_GetUsedPatchlist(void *theExternalSong, void *theExternalMidiData, INT32 midiSize, XShortResourceID *pInstrumentArray, OPErr *pErr) { GM_Song *theSong; INT32 count; *pErr = NO_ERR; // don't need a thread context here because we don't callback theSong = GM_LoadSong(NULL, NULL, 0, theExternalSong, theExternalMidiData, midiSize, pInstrumentArray, FALSE, TRUE, pErr); if (theSong) { GM_FreeSong(NULL, theSong); // we ignore the error codes, because it should be ok to dispose // since this song was never engaged } count = 0; if (*pErr == NO_ERR) { for (; count < MAX_INSTRUMENTS*MAX_BANKS; count++) { if (pInstrumentArray[count] == (XShortResourceID)-1) { break; } } } return count; }
JNIEXPORT void JNICALL Java_com_sun_media_sound_AbstractPlayer_nClose(JNIEnv* e, jobject thisObj, jlong id) { GM_Song *pSong = (GM_Song *) (INT_PTR) id; OPErr err; int waitTime; TRACE3("Java_com_sun_media_sound_AbstractPlayer_nClose: e: %lu, thisObj: %lu, pSong: %lu.\n", e, thisObj, (UINT32) id); if (pSong) { GM_KillSongNotes(pSong); pSong->disposeSongDataWhenDone = TRUE; // free our midi pointer GM_PauseSong(pSong); /* remove song from list of playing songs */ GM_RemoveFromSongsToPlay(pSong); /* set to impossible scan mode to disable processing */ pSong->AnalyzeMode = (ScanMode) -1; /* fix for 4795377: closing sequencer sometimes crashes the VM */ QGM_ClearSongFromQueue(pSong); /* wait until there was at least one mixer slice * processed so that the song notes are really * not used anymore */ waitTime = HAE_GetSliceTimeInMicroseconds()/1000 + 5; TRACE1("nClose: waiting %d millis to process GM_KillSongNotes...\n", waitTime); SleepMillisInJava(e, waitTime); do { /* this may fail - wait until it was able to kill the song */ err = GM_FreeSong((void *)e, pSong); if (err == STILL_PLAYING) { ERROR0("nClose: STILL_PLAYING! wait 5 millis...\n"); SleepMillisInJava(e, 5); } else if (err != NO_ERR) { ERROR1("nClose: GM_FreeSong returned error code: %d\n", err); } } while (err == STILL_PLAYING); } else { ERROR0("Java_com_sun_media_sound_AbstractPlayer_nClose: pSong is NULL\n"); } TRACE0("Java_com_sun_media_sound_AbstractPlayer_nClose completed.\n"); }
JNIEXPORT void JNICALL Java_com_sun_media_sound_MixerSynth_nDestroySynthesizer(JNIEnv* e, jobject thisObj, jlong id) { GM_Song *pSong = (GM_Song *) (INT_PTR) id; TRACE0("Java_com_sun_media_sound_MixerSynth_nDestroySynthesizer.\n"); if (pSong) { GM_KillSongNotes(pSong); pSong->disposeSongDataWhenDone = TRUE; // free our midi pointer GM_FreeSong((void *)e, pSong); } else { ERROR0("pSong is NULL\n"); } TRACE0("Java_com_sun_media_sound_MixerSynth_nDestroySynthesizer completed.\n"); }
// 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_Song * GM_LoadSong(void *threadContext, void *context, XShortResourceID songID, void *theExternalSong, void *theExternalMidiData, INT32 midiSize, XShortResourceID *pInstrumentArray, XBOOL loadInstruments, XBOOL ignoreBadInstruments, OPErr *pErr) { GM_Song *pSong; XLongResourceID songObjectID; *pErr = MEMORY_ERR; pSong = NULL; if (theExternalSong) { songObjectID = (XLongResourceID)XGetSongResourceObjectID(theExternalSong); switch (XGetSongResourceObjectType(theExternalSong)) { case SONG_TYPE_SMS: pSong = PV_CreateSongFromMidi(songObjectID, theExternalMidiData, midiSize); break; case SONG_TYPE_RMF: if (theExternalMidiData == NULL) { // we only support midi data via the resource manager pSong = PV_CreateSongFromMidi(songObjectID, NULL, 0); } else { *pErr = PARAM_ERR; } break; case SONG_TYPE_BAD: case SONG_TYPE_RMF_LINEAR: /* satisfy compiler */ break; } } // load instruments if (pSong) { pSong->pSynths = NULL; pSong->context = context; GM_MergeExternalSong(theExternalSong, songID, pSong); pSong->ignoreBadInstruments = ignoreBadInstruments; *pErr = GM_LoadSongInstruments(pSong, pInstrumentArray, loadInstruments); if (*pErr) { GM_FreeSong(threadContext, pSong); // we ignore the error codes, because it should be ok to dispose pSong = NULL; } else { // song length not calculated pSong->songMidiTickLength = 0; pSong->songMicrosecondLength = 0; *pErr = NO_ERR; } } return pSong; }
// 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; }