//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	AudioFilePlayer.h
//
//	$Log: AudioFilePlayer.h,v $
//	Revision 1.12  2003/04/18 23:05:13  baron
//	Use AudioFileReadPackets() in the special case of short files that are read entirely into memory.
//	
//	Revision 1.11  2003/04/18 17:37:43  baron
//	Make play file code work for aac.
//	
//	Revision 1.10  2003/04/09 22:33:11  baron
//	Pass audio data around by packets instead of bytes.
//	
//	Revision 1.9  2003/03/13 22:11:33  baron
//	Remove V1 code. Replace usage of AudioFile Read/WriteByte APIs with the Read/WritePacket versions.
//	
//	Revision 1.8  2002/06/23 22:17:20  bills
//	fix print
//	
//	Revision 1.7  2002/06/18 06:09:43  bills
//	change to render proc for V2 units
//	
//	Revision 1.6  2002/06/10 00:53:05  bills
//	cleanup defines for auv2
//	
//	Revision 1.5  2002/06/02 04:38:23  bills
//	tweaks to now fully support v1 or v2 audio unit
//	
//	Revision 1.4  2002/06/01 23:58:19  bills
//	first pass at v2 units
//	
//	Revision 1.3  2002/05/19 23:23:23  bills
//	cleanup header
//	
//	Revision 1.2  2002/05/18 03:46:21  bills
//	add some additional API
//	
//	Revision 1.1  2002/05/18 01:19:47  bills
//	new location
//	
//	Revision 1.1  2002/05/17 07:56:45  bills
//	new dir loc
//	
//	Revision 1.6  2002/05/17 07:48:05  bills
//	Add FilePlay "C" API
//	
//	Revision 1.5  2002/05/16 09:22:09  bills
//	add notifications for file underruns
//	
//	Revision 1.4  2002/05/16 09:07:13  bills
//	use a single read thread for files
//	
//	Revision 1.3  2002/05/15 07:31:08  bills
//	add GetFileFormat call
//	
//	Revision 1.2  2002/05/15 06:21:59  bills
//	now use AudioConverter to do file reading
//	
//	Revision 1.1  2002/05/14 08:12:23  bills
//	initial checkin
//	
//
//	General class for playing back a file
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#ifndef __AudioFilePlayer_H__
#define __AudioFilePlayer_H__

#include <CoreServices/CoreServices.h>

#include <AudioToolbox/AudioToolbox.h>
#include <AudioUnit/AudioUnit.h>

#include "AudioFilePlay.h"

#define THROW_RESULT(str) 										\
	if (result) {												\
		printf ("Error:%s=0x%lX,%ld,%s\n\n",		 			\
						str,result, result, (char*)&result);	\
		throw result;											\
	}

typedef void (*AudioFileManagerErrorNotification)(void *				inRefCon,
											OSStatus					inErrorCode);

class AudioFileManager;

#pragma mark __________ AudioFilePlayer
class AudioFilePlayer
{
public:
	AudioFilePlayer (const FSRef	&inFileRef);
	
	~AudioFilePlayer();

	void			SetDestination (AudioUnit					&inDestUnit, 
								int			 					inBusNumber);
	
	void			SetNotifier (AudioFilePlayNotifier inNotifier, void *inRefCon)
	{
		mNotifier = inNotifier;
		mRefCon = inRefCon;
	}
	
	void			Connect();
	
	void 			Disconnect();

	void 			DoNotification (OSStatus inError) const;
	
	bool			IsConnected () const { return mConnected; }

	void			SetLooping (bool inLoop);
		
	bool			IsLooping () const;
	
	UInt32			GetBusNumber () const { return mBusNumber; }
	
	AudioUnit		GetDestUnit () const { return mPlayUnit; }
	
	AudioConverterRef	GetAudioConverter() const { return mConverter; }
	
	void			Print() const 
	{
		CAShow (mAudioFileID);
		printf ("Destination Bus:%ld\n", GetBusNumber());
		printf ("Is Connected:%s\n", (IsConnected() ? "true" : "false"));
		printf ("Using Reader Thread:%s\n", (mUsingReaderThread ? "true" : "false"));
		if (mConverter) CAShow (mConverter);
		printf ("- - - - - - - - - - - - - - \n");
	}
	
	const AudioStreamBasicDescription& 		GetFileFormat() const { return mFileDescription; }
	
private:
	AudioUnit		 				mPlayUnit;
	UInt32							mBusNumber;
	AudioFileID						mAudioFileID;
	
	AudioUnitInputCallback 			mInputCallback;
	AURenderCallbackStruct			mRenderCallback;

	AudioStreamBasicDescription 	mFileDescription;
	
	bool							mConnected;
	bool							mUsingReaderThread;
	
	AudioFileManager*				mAudioFileManager;
	AudioConverterRef				mConverter;
	
	AudioFilePlayNotifier 			mNotifier;
	void*							mRefCon;
    	
#pragma mark __________ Private_Methods
	
	void		OpenFile (const FSRef& inRef, SInt64& outFileSize, SInt64& outPacketCount, UInt32& outMaxPacketSize);
};

#pragma mark __________ AudioFileManager
class AudioFileManager
{
public:
	AudioFileManager (AudioFilePlayer& inParent, AudioFileID inFile)
		: mParent (inParent),
		  mAudioFileID (inFile),
		  mFileBuffer (0),
		  mIsLooping (false),
          mCurrentByteCountInBuffer(0),
          mCurrentPacketCountInBuffer(0),
          mPacketDescriptions(NULL)
		{}

	
	virtual ~AudioFileManager();
	
	
	void				Connect (AudioConverterRef inConverter) 
	{
		mParentConverter = inConverter;
		DoConnect();
	}

		// this method should NOT be called by an object of this class
		// as it is called by the parent's Disconnect() method
	virtual void		Disconnect () {}

	void				SetLooping (bool inLooping) { mIsLooping = inLooping; }
			
	bool				IsLooping () const { return mIsLooping; }

	const AudioFileID&	GetFileID() const { return mAudioFileID; }

	const char*			GetFileBuffer () { return mFileBuffer; }
	
	const AudioFilePlayer& 	GetParent () const { return mParent; }
	
protected:
	AudioFilePlayer& 				mParent;
	AudioConverterRef				mParentConverter;
	const AudioFileID				mAudioFileID;
	char*							mFileBuffer;
	bool							mIsLooping;
	
	OSStatus			Render (AudioBuffer &ioData);

	OSStatus			Render (AudioBufferList &ioData, UInt32 inNumFrames);
	
	virtual OSStatus 	GetFileData (void** inOutData, UInt32 *inOutDataSize, UInt32 *outPacketCount, 															AudioStreamPacketDescription	**outPacketDescriptions) = 0;
	
	virtual void		DoConnect () = 0;
		
	virtual void		AfterRender () = 0;

public:

    UInt32							mCurrentByteCountInBuffer;
    UInt32							mCurrentPacketCountInBuffer;
    AudioStreamPacketDescription*	mPacketDescriptions;

	static OSStatus 	FileRenderProc (void 							*inRefCon, 
										AudioUnitRenderActionFlags 		*inActionFlags,
										const AudioTimeStamp 			*inTimeStamp, 
										UInt32 							inBusNumber,
										UInt32							inNumFrames, 
										AudioBufferList 				*ioData);

	static OSStatus 	ACComplexInputProc (AudioConverterRef		inAudioConverter,
								UInt32*								ioNumberDataPackets,
								AudioBufferList*					ioData,
								AudioStreamPacketDescription** 		outDataPacketDescription,
								void*								inUserData);
};

#pragma mark __________ AudioFileData
class AudioFileData : public AudioFileManager
{
public:
	AudioFileData (	AudioFilePlayer		&inParent, 
                    AudioFileID 		&inFile, 
                    SInt64 				inFileLength,
                    SInt64				inPacketCount,
                    UInt32				inMaxPacketSize,
                    UInt32				inChunkSizeInPackets);

protected:
	virtual void		DoConnect ();

	virtual OSStatus 	GetFileData (void** inOutData, UInt32 *inOutDataSize, UInt32 *outPacketCount, 															AudioStreamPacketDescription	**outPacketDescriptions);

	virtual void		AfterRender ();

private:
	bool						mFirstTime;
	bool						mShouldFinish;
	UInt32						mFileLength;
};

struct FileThreadVariables : public AudioFileManager 
{
	const UInt32					mChunkSizeInPackets;
	const SInt64					mFileLength;
    const SInt64					mPacketCount;
    const UInt32					mMaxPacketSize;
    SInt64							mReadPacketPosition;
	bool							mWriteToFirstBuffer;
	bool							mFinishedReadingData;

	FileThreadVariables (	const UInt32 					inChunkSizeInPackets,
                            const SInt64 					inFileLength,
                            const SInt64					inPacketCount,
                            UInt32							inMaxPacketSize,
                            AudioFilePlayer					&inParent, 
                            AudioFileID 					&inFile) 
		: AudioFileManager (inParent, inFile),
		  mChunkSizeInPackets (inChunkSizeInPackets),
		  mFileLength (inFileLength),
		  mPacketCount (inPacketCount),
		  mMaxPacketSize (inMaxPacketSize),
		  mReadPacketPosition (0),
          mWriteToFirstBuffer (false),
		  mFinishedReadingData (false)
		{}
	
	virtual ~FileThreadVariables() {}
};


#pragma mark __________ AudioFileReaderThread
class AudioFileReaderThread 
	: public FileThreadVariables
{
public:
	AudioFileReaderThread (AudioFilePlayer	&inParent, 
							AudioFileID 	&inFile, 
							SInt64 			inFileLength,
                            SInt64			inPacketCount,
                            UInt32			inMaxPacketSize,
                            UInt32			inChunkSizeInPackets);

	virtual void		Disconnect ();

protected:
	virtual void		DoConnect ();

	virtual OSStatus 	GetFileData (	void** inOutData, UInt32 *inOutDataSize, UInt32 *outPacketCount, 															AudioStreamPacketDescription	**outPacketDescriptions);
    
	virtual void		AfterRender ();

private:
	bool						mReadFromFirstBuffer;
	bool						mLockUnsuccessful;
	bool						mIsEngaged;
	
	int							mNumTimesAskedSinceFinished;
};


#endif
