/*=============================================================================
	CAAudioBufferList.cp

	$Log: CAAudioBufferList.cpp,v $
	Revision 1.1  2004/08/23 06:22:34  jcm10
	first checked in
	
	Revision 1.8  2004/07/03 01:17:06  jcm10
	add HasData()
	
	Revision 1.7  2004/05/15 00:15:46  jcm10
	add sEmptyBufferList
	
	Revision 1.6  2004/02/12 01:50:44  jcm10
	fixed GetBufferForChannel()
	
	Revision 1.5  2004/02/11 23:39:33  jcm10
	improve the Copy() method to support varying forms of ABL's
	
	Revision 1.4  2003/03/12 01:58:48  jcm10
	add CalculateByteSize()
	
	Revision 1.3  2002/10/04 00:36:27  dwyatt
	fix compile error: need string.h
	
	Revision 1.2  2002/09/27 22:16:21  jcm10
	add Clear() and Sum()
	
	Revision 1.1  2002/03/01 01:52:40  jcm10
	moved here from ../Utility
	
	Revision 1.4  2002/02/28 23:24:29  jcm10
	added the CA prefix to DebugMacros and LogMacros for more consistency
	
	Revision 1.3  2001/11/15 02:20:25  jcm10
	call standard C library fucntions without the namespace to
	make cpp-precomp happy
	
	Revision 1.2  2001/04/19 18:57:32  jcm10
	initialize the mNumberBuffers field in Create()
	
	Revision 1.1  2001/04/05 01:36:12  jcm10
	first checked in
	
	Revision 0.0  2001/04/04 16:55:43  jcm10
	created
		
	$NoKeywords: $
=============================================================================*/

//=============================================================================
//	Includes
//=============================================================================

#include "CAAudioBufferList.h"
#include "CADebugMacros.h"
#include "CALogMacros.h"
#include <stdlib.h>
#include <string.h>

//=============================================================================
//	CAAudioBufferList
//=============================================================================

AudioBufferList*	CAAudioBufferList::Create(UInt32 inNumberBuffers)
{
	UInt32 theSize = CalculateByteSize(inNumberBuffers);
	AudioBufferList* theAnswer = static_cast<AudioBufferList*>(calloc(1, theSize));
	if(theAnswer != NULL)
	{
		theAnswer->mNumberBuffers = inNumberBuffers;
	}
	return theAnswer;
}

void	CAAudioBufferList::Destroy(AudioBufferList* inBufferList)
{
	free(inBufferList);
}

UInt32	CAAudioBufferList::CalculateByteSize(UInt32 inNumberBuffers)
{
	UInt32 theSize = sizeof(AudioBufferList) - sizeof(AudioBuffer);
	theSize += inNumberBuffers * sizeof(AudioBuffer);
	return theSize;
}

UInt32	CAAudioBufferList::GetTotalNumberChannels(const AudioBufferList& inBufferList)
{
	UInt32 theAnswer = 0;
	
	for(UInt32 theIndex = 0; theIndex < inBufferList.mNumberBuffers; ++theIndex)
	{
		theAnswer += inBufferList.mBuffers[theIndex].mNumberChannels;
	}
	
	return theAnswer;
}

bool	CAAudioBufferList::GetBufferForChannel(const AudioBufferList& inBufferList, UInt32 inChannel, UInt32& outBufferNumber, UInt32& outBufferChannel)
{
	bool theAnswer = false;
	UInt32 theIndex = 0;
	
	while((theIndex < inBufferList.mNumberBuffers) && (inChannel >= inBufferList.mBuffers[theIndex].mNumberChannels))
	{
		inChannel -= inBufferList.mBuffers[theIndex].mNumberChannels;
		++theIndex;
	}
	
	if(theIndex < inBufferList.mNumberBuffers)
	{
		outBufferNumber = theIndex;
		outBufferChannel = inChannel;
		theAnswer = true;
	}
	
	return theAnswer;
}

void	CAAudioBufferList::Clear(AudioBufferList& outBufferList)
{
	//	assumes that "0" is actually the 0 value for this stream format
	for(UInt32 theBufferIndex = 0; theBufferIndex < outBufferList.mNumberBuffers; ++theBufferIndex)
	{
		if(outBufferList.mBuffers[theBufferIndex].mData != NULL)
		{
			memset(outBufferList.mBuffers[theBufferIndex].mData, 0, outBufferList.mBuffers[theBufferIndex].mDataByteSize);
		}
	}
}

void	CAAudioBufferList::Copy(const AudioBufferList& inSource, UInt32 inStartingSourceChannel, AudioBufferList& outDestination, UInt32 inStartingDestinationChannel)
{
	//  This is a brute force copy method that can handle ABL's that have different buffer layouts
	//  This means that this method is probably not the fastest way to do this for all cases.
	//  This method also assumes that both the source and destination sample formats are Float32

	UInt32 theInputChannel = inStartingSourceChannel;
	UInt32 theNumberInputChannels = GetTotalNumberChannels(inSource);
	UInt32 theOutputChannel = inStartingDestinationChannel;
	UInt32 theNumberOutputChannels = GetTotalNumberChannels(outDestination);
	
	while((theInputChannel < theNumberInputChannels) && (theOutputChannel < theNumberOutputChannels))
	{
		UInt32 theInputBufferIndex;
		UInt32 theInputBufferChannel;
		GetBufferForChannel(inSource, theInputChannel, theInputBufferIndex, theInputBufferChannel);
		
		UInt32 theOutputBufferIndex;
		UInt32 theOutputBufferChannel;
		GetBufferForChannel(inSource, theOutputChannel, theOutputBufferIndex, theOutputBufferChannel);
		
		CopyChannel(inSource.mBuffers[theInputBufferIndex], theInputBufferChannel, outDestination.mBuffers[theOutputBufferIndex], theOutputBufferChannel);
		
		++theInputChannel;
		++theOutputChannel;
	}
}

void	CAAudioBufferList::CopyChannel(const AudioBuffer& inSource, UInt32 inSourceChannel, AudioBuffer& outDestination, UInt32 inDestinationChannel)
{
	//  set up the stuff for the loop
	UInt32 theNumberFramesToCopy = outDestination.mDataByteSize / (outDestination.mNumberChannels * sizeof(Float32));
	const Float32* theSource = static_cast<const Float32*>(inSource.mData);
	Float32* theDestination = static_cast<Float32*>(outDestination.mData);
	
	//  loop through the data and copy the samples
	while(theNumberFramesToCopy > 0)
	{
		//  copy the data
		theDestination[inDestinationChannel] = theSource[inSourceChannel];
		
		//  adjust the pointers
		--theNumberFramesToCopy;
		theSource += inSource.mNumberChannels;
		theDestination += outDestination.mNumberChannels;
	}
}

void	CAAudioBufferList::Sum(const AudioBufferList& inSourceBufferList, AudioBufferList& ioSummedBufferList)
{
	//	assumes that the buffers are Float32 samples and the listst have the same layout
	//	this is a lame algorithm, by the way. it could at least be unrolled a couple of times
	for(UInt32 theBufferIndex = 0; theBufferIndex < ioSummedBufferList.mNumberBuffers; ++theBufferIndex)
	{
		Float32* theSourceBuffer = static_cast<Float32*>(inSourceBufferList.mBuffers[theBufferIndex].mData);
		Float32* theSummedBuffer = static_cast<Float32*>(ioSummedBufferList.mBuffers[theBufferIndex].mData);
		UInt32 theNumberSamplesToMix = ioSummedBufferList.mBuffers[theBufferIndex].mDataByteSize / sizeof(Float32);
		if((theSourceBuffer != NULL) && (theSummedBuffer != NULL) && (theNumberSamplesToMix > 0))
		{
			while(theNumberSamplesToMix > 0)
			{
				*theSummedBuffer += *theSourceBuffer;
				++theSummedBuffer;
				++theSourceBuffer;
				--theNumberSamplesToMix;
			}
		}
	}
}

bool	CAAudioBufferList::HasData(AudioBufferList& inBufferList)
{
	bool hasData = false;
	for(UInt32 theBufferIndex = 0; !hasData && (theBufferIndex < inBufferList.mNumberBuffers); ++theBufferIndex)
	{
		if(inBufferList.mBuffers[theBufferIndex].mData != NULL)
		{
			UInt32* theBuffer = (UInt32*)inBufferList.mBuffers[theBufferIndex].mData;
			UInt32 theNumberSamples = inBufferList.mBuffers[theBufferIndex].mDataByteSize / sizeof(UInt32);
			for(UInt32 theSampleIndex = 0; !hasData && (theSampleIndex < theNumberSamples); ++theSampleIndex)
			{
				hasData = theBuffer[theSampleIndex] != 0;
			}
		}
	}
	return hasData;
}

#if CoreAudio_Debug
void	CAAudioBufferList::PrintToLog(const AudioBufferList& inBufferList)
{
	PrintInt("  Number streams: ", inBufferList.mNumberBuffers);
	
	for(UInt32 theIndex = 0; theIndex < inBufferList.mNumberBuffers; ++theIndex)
	{
		PrintIndexedInt("  Channels in stream", theIndex + 1, inBufferList.mBuffers[theIndex].mNumberChannels);
		PrintIndexedInt("  Buffer size of stream", theIndex + 1, inBufferList.mBuffers[theIndex].mDataByteSize);
	}
}
#endif

AudioBufferList CAAudioBufferList::sEmptyBufferList = { 0, { 0, 0, NULL } };
		
