/*=============================================================================
	AUOutputBL.h
	
	$Log: AUOutputBL.cpp,v $
	Revision 1.4  2004/08/24 22:39:54  bills
	protect Print in release builds
	
	Revision 1.3  2004/08/20 18:56:56  bills
	add a Print method
	
	Revision 1.2  2004/07/09 02:50:31  bills
	fix handling of mDataByteSize
	
	Revision 1.1  2004/03/16 20:24:10  bills
	new class
	

	created 15 Mar 2004, William Stewart
	Copyright (c) 2004 Apple Computer, Inc.  All Rights Reserved

	$NoKeywords: $
=============================================================================*/
#include "AUOutputBL.h"

/*
struct AudioBufferList
{
	UInt32		mNumberBuffers;
	AudioBuffer	mBuffers[1];
};
struct AudioBuffer
{
	UInt32	mNumberChannels;	//	number of interleaved channels in the buffer
	UInt32	mDataByteSize;		//	the size of the buffer pointed to by mData
	void*	mData;				//	the pointer to the buffer
};
*/

AUOutputBL::AUOutputBL (const CAStreamBasicDescription &inDesc, UInt32 inDefaultNumFrames) 
		: mFormat (inDesc),
		  mBufferMemory(NULL),
		  mBufferList (NULL),
		  mNumberBuffers (0), // keep this here, so can ensure integrity of ABL
		  mBufferSize (0),
		  mFrames(inDefaultNumFrames)
{
	mNumberBuffers = mFormat.IsInterleaved() ? 1 : mFormat.NumberChannels();
	mBufferList = reinterpret_cast<AudioBufferList*>(new Byte[sizeof(UInt32) + (mNumberBuffers * sizeof(AudioBuffer))]);
}

AUOutputBL::~AUOutputBL()
{
	if (mBufferMemory)
		delete[] mBufferMemory;

	if (mBufferList)
		delete [] mBufferList;
}

void 	AUOutputBL::Prepare (UInt32 inNumFrames, bool inWantNullBufferIfAllocated) 
{
	UInt32 channelsPerBuffer = mFormat.IsInterleaved() ? mFormat.NumberChannels() : 1;
	
	if (mBufferMemory == NULL || inWantNullBufferIfAllocated)
	{
		mBufferList->mNumberBuffers = mNumberBuffers;
		AudioBuffer *buf = &mBufferList->mBuffers[0];
		for (UInt32 i = 0; i < mNumberBuffers; ++i, ++buf) {
			buf->mNumberChannels = channelsPerBuffer;
			buf->mDataByteSize = mFormat.FramesToBytes (inNumFrames);
			buf->mData = NULL;
		}
	}
	else
	{
		UInt32 nBytes = mFormat.FramesToBytes (inNumFrames);
		if ((nBytes * mNumberBuffers) > AllocatedBytes())
			throw OSStatus(-10874);//(kAudioUnitErr_TooManyFramesToProcess);
			
		mBufferList->mNumberBuffers = mNumberBuffers;
		AudioBuffer *buf = &mBufferList->mBuffers[0];
		Byte* p = mBufferMemory;
		for (UInt32 i = 0; i < mNumberBuffers; ++i, ++buf) {
			buf->mNumberChannels = channelsPerBuffer;
			buf->mDataByteSize = nBytes;
			buf->mData = p;
			p += mBufferSize;
		}
	}
}


void	AUOutputBL::Allocate (UInt32 inNumFrames)
{
	if (inNumFrames) 
	{
		UInt32 nBytes = mFormat.FramesToBytes (inNumFrames);
		
		if (nBytes <= AllocatedBytes()) 
			return;
		
			// align successive buffers for Altivec and to take alternating
			// cache line hits by spacing them by odd multiples of 16
		if (mNumberBuffers > 1)
			nBytes = (nBytes + (0x10 - (nBytes & 0xF))) | 0x10;
		
		mBufferSize = nBytes;
		
		UInt32 memorySize = mBufferSize * mNumberBuffers;
		Byte *newMemory = new Byte[memorySize];
		memset(newMemory, 0, memorySize);	// make buffer "hot"
		
		Byte *oldMemory = mBufferMemory;
		mBufferMemory = newMemory;
		delete[] oldMemory;
		
		mFrames = inNumFrames;
	} 
	else 
	{
		if (mBufferMemory) {
			delete [] mBufferMemory;
			mBufferMemory = NULL;
		}
		mBufferSize = 0;
		mFrames = 0;
	}
}

#if DEBUG
void			AUOutputBL::Print()
{
	printf ("AUOutputBL::Print\n");
	mFormat.Print();
	printf ("Num Buffers:%ld, mFrames:%ld, allocatedMemory:%c\n", mBufferList->mNumberBuffers, mFrames, (mBufferMemory != NULL ? 'T' : 'F'));
	AudioBuffer *buf = &mBufferList->mBuffers[0];
	for (UInt32 i = 0; i < mBufferList->mNumberBuffers; ++i, ++buf)
		printf ("\tBuffer:%ld, Size:%ld, Chans:%ld, Buffer:%X\n", i, buf->mDataByteSize, buf->mNumberChannels, int(buf->mData));
}
#endif

