/*=============================================================================
	CABufferQueue.h
	
	$Log: CABufferQueue.h,v $
	Revision 1.4.2.1  2005/04/21 19:28:35  dwyatt
	bring over a few things from TOT for the benefit of test tools
	
	Revision 1.4  2004/12/16 23:26:39  dwyatt
	fix warning
	
	Revision 1.3  2004/12/15 23:18:35  dwyatt
	add debug print code
	
	Revision 1.2  2004/05/26 00:34:43  dwyatt
	get rid of the lock on the work thread's queue
	
	Revision 1.1  2004/01/14 00:08:09  dwyatt
	moved from Source/Tests/AudioFileUtility/Utility
	
	Revision 1.1  2003/10/15 00:27:11  dwyatt
	initial checkin
	
	created Fri Oct 10 2003, Doug Wyatt
	Copyright (c) 2003 Apple Computer, Inc.  All Rights Reserved

	$NoKeywords: $
=============================================================================*/

#ifndef __CABufferQueue_h__
#define __CABufferQueue_h__

#include "CAPThread.h"
#include "CAGuard.h"
#include "CAStreamBasicDescription.h"
#include "CABufferList.h"
#include <list>
#include "CAAtomicFIFO.h"

// ____________________________________________________________________________

// Abstraction for moving audio buffers between threads.
// Has abstract subclasses for push and pull.
class CABufferQueue {
	friend class CAPushBufferQueue;
	friend class CAPullBufferQueue;
public:
						CABufferQueue(int nBuffers, UInt32 bufferSizeFrames);
	virtual				~CABufferQueue();

	void				SetFormat(const CAStreamBasicDescription &fmt);
	UInt32				GetBufferSizeFrames() const { return mBufferSizeFrames; }
	int					ErrorCount() const { return mErrorCount; }
	
	// -----
	class Buffer {
	public:
		Buffer(CABufferQueue *owner, const CAStreamBasicDescription &fmt, UInt32 nBytes);
		
		~Buffer() { delete mMemory; }
		
		CABufferQueue * Queue() { return mQueue; }
		CABufferList *  GetBufferList() { return mMemory; }
		UInt32			FrameCount() { return mEndFrame - mStartFrame; }
		void			SetEmpty() { mStartFrame = mEndFrame = 0; }
		
		void			SetInProgress(bool b) { mInProgress = b; }
		bool			InProgress() const { return mInProgress; }
		bool			ReachedEndOfStream() const { return mEndOfStream; }

		bool			CopyInto(AudioBufferList *destBufferList, int bytesPerFrame, UInt32 &framesProduced, UInt32 &framesRequired);	// return true if buffer emptied

		bool			CopyFrom(const AudioBufferList *srcBufferList, int bytesPerFrame, UInt32 &framesProduced, UInt32 &framesRequired); // return true if buffer filled and not end-of-stream
		
		Buffer *		get_next() { return mNext; }
		void			set_next(Buffer *b) { mNext = b; }
		
#if DEBUG
		void			print() {
							printf("Buffer %p:\n  inProgress %d, endOfStream %d, frames %ld-%ld\n", this, mInProgress, mEndOfStream, mStartFrame, mEndFrame);
						}
#endif
		
	protected:
		Buffer *		mNext;
		CABufferQueue * mQueue;
		CABufferList *	mMemory;
		UInt32			mByteSize;
		
		bool			mInProgress;				// true if in the work queue
		bool			mEndOfStream;				// true if the operation resulted in end-of-stream
		UInt32			mStartFrame, mEndFrame;		// produce/consume pointers within the buffer
	};
	
#if DEBUG
	void	print() {
		printf("BufferQueue %p\n  mCurrentBuffer=%d\n", this, mCurrentBuffer);
		if (mBuffers)
		for (int i = 0; i < mNumberBuffers; ++i) {
			Buffer *b = mBuffers[i];
			printf("  buffer[%d]: ", i);
			if (b)
				b->print();
			else printf("NULL\n");
		}
	}
#endif
	
protected:	
	virtual Buffer *	CreateBuffer(const CAStreamBasicDescription &fmt, UInt32 nBytes) = 0;
	virtual void		ProcessBuffer(Buffer *b) = 0;
	void				CancelBuffers();
	void				CancelAndDisposeBuffers();
	
	CABufferList *		GetBufferList() { return mBufferList; }
	const Buffer *		GetCurrentBuffer() const { return mBuffers[mCurrentBuffer]; }
	UInt32				GetBytesPerFrame() const { return mBytesPerFrame; }

private:
	
	// -----
	class WorkThread : public CAPThread {
	public:
		WorkThread();
		
		static void * ThreadEntry(void *param)
		{
			static_cast<WorkThread *>(param)->Run();
			return NULL;
		}
		void	Run();
		void	Stop();
		
		void	AddBuffer(Buffer *buffer);
		void	RemoveBuffers(CABufferQueue *owner);
	
	private:
		typedef std::list<Buffer *> WorkQueue;

		bool			mStopped;
		WorkQueue		mWorkQueue;
		CAGuard			mRunGuard;
		TAtomicStack<Buffer>  mBuffersToAdd;
	};
	
	static WorkThread *	sWorkThread;
	
	// -----
private:
	WorkThread *		mWorkThread;

	int					mCurrentBuffer;
	int					mNumberBuffers;
	Buffer **			mBuffers;					// array of pointers
	UInt32				mBufferSizeFrames;
	UInt32				mBytesPerFrame;				// function of client format
	CABufferList *		mBufferList;				// maintained in SetFormat
protected:
	int					mErrorCount;
};

// ____________________________________________________________________________

// Abstract class.
// The client pushes buffers in; they are consumed (via ProcessBuffer) on the work thread.
// (ex: file recorder)
class CAPushBufferQueue : public CABufferQueue {
public:
	CAPushBufferQueue(int nBuffers, UInt32 bufferSizeFrames) :
		CABufferQueue(nBuffers, bufferSizeFrames) { }

	void			PushBuffer(UInt32 inNumberFrames, const AudioBufferList *inBufferList);
						// push a buffer in
	void			Flush();
						// emit a possibly incomplete final buffer
};

// ____________________________________________________________________________

// Abstract class.
// The client pulls buffers out; they are produced (via ProcessBuffer) on the work thread.
// (ex: file player)
class CAPullBufferQueue : public CABufferQueue {
public:
	CAPullBufferQueue(int nBuffers, UInt32 bufferSizeFrames) :
		CABufferQueue(nBuffers, bufferSizeFrames),
		mEndOfStream(false) { }

	void			Prime();
						// produce initial buffers
	void			PullBuffer(UInt32 &ioFrames, AudioBufferList *outBufferList);
						// pull a buffer out
	bool			ReachedEndOfStream() const { return mEndOfStream; }

protected:
	bool			mEndOfStream;
};

#endif // __CABufferQueue_h__
