/****************************/
/*      AUDIO.C				*/
/* (c)2004 Pangea Software  */
/* By Brian Greenstone      */
/****************************/


/****************************/
/*    EXTERNS				*/
/****************************/

extern	FSSpec		gMyResourcesFolderFSSpec;


/****************************/
/*    PROTOTYPES            */
/****************************/

static void *MySongThread(void *in);
pascal void EndOfSongCallback(QTCallBack cb, long refCon);


/****************************/
/*    CONSTANTS             */
/****************************/




/**********************/
/*     VARIABLES      */
/**********************/

static CGrafPtr		gQTDummyPort = nil;
Movie				gSongMovie = nil;
static	Boolean		gSongPlayingFlag = false;
static	Boolean		gLoopSongFlag = false;

static volatile  Boolean gInQuicktimeFunction = false;
static volatile	 gInMoviesTask = false;

static	QTCallBack		gMovieCallback;
static	QTCallBackUPP	gMovieCallbackUPP;	


/***************** INIT AUDIO **********************/
//
// Initializes Quicktime, and creates a pthread for updating MoviesTask()
//

void InitAudio(void)
{
	pthread_t 			myThread;
	OSErr				err;

		/* ACTIVATE QUICKTIME */

	EnterMovies();

	
		/* INIT MOVIES TASK THREAD */
					
	err = pthread_create(&myThread, NULL, MySongThread, nil);
	if (err != noErr)
		DoFatalAlert("\ppthread_create failed");

}

/********************* MY SONG THREAD ************************/
//
// A code thread dedicated to updateing MoviesTask()
//

static void *MySongThread(void *in)
{
AbsoluteTime	expTime;
#pragma unused (in)

	while(true)						// loop inside here forever
	{
			/* CALCULATE THE TIME OF NEXT LOOP */
			//
			// Calculate current time + 100 milliseconds.
			// ThatÕs when weÕll want to loop thru again.
			//

		expTime = AddDurationToAbsolute(100 * kDurationMillisecond,
										UpTime());
		
		/* UPDATE MOVIES TASK */
		//
		// We must be very careful not to call MoviesTask()
		// while some other Quicktime call is in progress!
		//
		
		if (!gInQuicktimeFunction)
		{		
			if (gSongMovie)			
			{
				gInMoviesTask = true;
				MoviesTask(gSongMovie, 0);
				gInMoviesTask = false;
			}
		}

			/* WAIT UNTIL ITÕS TIME TO LOOP AGAIN */

		MPDelayUntil(&expTime);
	}
	
    return 0;
}

/*********************** PLAY SONG ***********************/
//
// Loads the specified sound file and starts playing it.
//
// INPUT:	songFileName = pascal string that is the audio file's filename.
//			loopFlag = true if we want the song to loop, false if only play once.
//

void PlaySong(Str255 songFileName, Boolean loopFlag)
{
	OSErr 		iErr;
	FSSpec		spec;
	short		myRefNum;
	GrafPtr		oldPort;
	TimeValue 	timeNow, duration;
	Fixed 		playRate;
	Media 		theMedia;
	
			/********************************/
			/* CREATE MOVIE FROM SOUND FILE */
			/********************************/
			
	/* CREATE FSSPEC FOR THE SOUND FILE */
	
	iErr = FSMakeFSSpec(gMyResourcesFolderFSSpec.vRefNum,
						gMyResourcesFolderFSSpec.parID,
						songFileName,
						&spec);
	if (iErr)
		DoFatalAlert("\pSong file not found");
	
	
	/* GOT TO SET A DUMMY PORT FOR QUICKTIME */
	//
	// Even though weÕre only playing audio, we must set a
	// dummy port for Quicktime to ÒrenderÓ into.
	//
	
	if (gQTDummyPort == nil)					// create a blank graf port
		gQTDummyPort = CreateNewPort();
	
	GetPort(&oldPort);						// backup current port	 
	SetPort(gQTDummyPort);					// set dummy as active port
	
	
	/* OPEN THE SOUND FILE AS A QT MOVIE */
	
	iErr = OpenMovieFile(&spec, &myRefNum, fsRdPerm);
	if ((myRefNum == 0) || (iErr != noErr))
		DoFatalAlert("\pError opening movie file");
	
	
	/* CREATE A NEW QT MOVIE FROM THE OPEN FILE */
	
	NewMovieFromFile(&gSongMovie,
					 myRefNum,
					 0,
					 nil,
					 newMovieActive,
					 nil);
	
	CloseMovieFile(myRefNum);
	
			/******************/	
			/* SET MOVIE INFO */
			/******************/
	
	/* SET MOVIE PLAY HINTS */			
	
	SetMoviePlayHints(gSongMovie,
					  0, 						// turn these hints off	
					  hintsUseSoundInterp|hintsHighQuality);	
	
	if (loopFlag)								// turn loop hint on
		SetMoviePlayHints(gSongMovie, hintsLoop, hintsLoop);	
		
														
	/* PRE-LOAD MOVIE INTO RAM */
	
	timeNow = GetMovieTime(gSongMovie, nil);
	duration = GetMovieDuration(gSongMovie);
	playRate = GetMoviePreferredRate(gSongMovie);
	theMedia = GetTrackMedia(GetMovieIndTrack(gSongMovie, 1));		
	
	LoadMovieIntoRam(gSongMovie, timeNow, duration, keepInRam);
	LoadMediaIntoRam(theMedia, timeNow, duration, keepInRam);
	
	PrePrerollMovie(gSongMovie, timeNow, playRate, nil, nil);
	PrerollMovie(gSongMovie, timeNow, playRate); 
	
	
			/* SET LOOPING CALLBACK */
			
	if (loopFlag)
	{
		TimeBase timebase = GetMovieTimeBase(gSongMovie);	// get timebase of movie
		gMovieCallback = NewCallBack(timebase,				// create a new callback ref
							callBackAtExtremes);

		gMovieCallbackUPP = NewQTCallBackUPP(EndOfSongCallback);	// create callback UPP
			
		iErr = CallMeWhen(gMovieCallback,				// callback ref
					gMovieCallbackUPP,					// function to callback
					0,
					triggerAtStop,						// call when movie stops
					0,0);
					
		if (iErr != noErr)
			DoFatalAlert("\pcould not create callback");
	}
	
	
				/***************************/
				/* START THE MUSIC PLAYING */
				/***************************/
	
	StartMovie(gSongMovie);
	
	gSongPlayingFlag = true;
	gLoopSongFlag = loopFlag;								// remember if we want it to loop
	
	
				/* RESTORE THE PORT */
	
	SetPort(oldPort);
	
}

/***************** END OF SONG CALLBACK *********************/
//
// This gets called by MoviesTask() when the movie has ended.
// Here we rewind the song and get it playing again to make it loop.
//
// NOTE:  Since this is called from MoviesTask() and we only call
// MoviesTask() inside our thread function, we don't need to use
// our safety flags gInQuicktimeFunction or gInMoviesTask because
// these were already dealt with when MoviesTask() was called.
// Also, be careful what we call inside this function since we're
// in a thread.  Not all OS calls are thread-safe!
//

pascal void EndOfSongCallback(QTCallBack callback, long refCon)
{
#pragma unused (callback, refCon)

	GoToBeginningOfMovie(gSongMovie);	// rewind song

	CallMeWhen(gMovieCallback,			// reinstate the callback
				gMovieCallbackUPP,
				0,
				triggerAtStop,
				0,0);
					
	StartMovie(gSongMovie);				// start playing again	
}



/*********************** FREE OUR SONG MOVIE *********************/
//
// Stops a song from playing and frees up the Quicktime movie.
//

void FreeOurSongMovie(void)
{
			/* BE SAFE */
			
	gInQuicktimeFunction = true;	// dont allow song task to start anything new
	while(gInMoviesTask);			// DONT DO ANYTHING WHILE IN THE SONG TASK!!

		
		/* DISPOSE THE LOOP CALLBACK */
		
	if (gLoopSongFlag)
	{
		DisposeCallBack(gMovieCallback);
		DisposeQTCallBackUPP(gMovieCallbackUPP);	
	}

		
		/* STOP AND DISPOSE MOVIE */
				
	gSongPlayingFlag = false;		// clear this

	StopMovie(gSongMovie);			// stop the movie
	DisposeMovie(gSongMovie);		// dispose of the movie
		
	gSongMovie = nil;
	
	gInQuicktimeFunction = false;
}
















