예제 #1
0
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;
}
예제 #2
0
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");
}
예제 #3
0
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");
}
예제 #4
0
// 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;
}
예제 #5
0
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;
}
예제 #6
0
//	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;
}
예제 #7
0
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;
}
예제 #8
0
// 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;
}