// 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; }
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"); }
// 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; }
// 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; }