/*=============================================================================
	CABufferList.cpp
	
	$Log: CABufferList.cpp,v $
	Revision 1.17  2004/08/23 19:14:02  bills
	fix spelling mistake
	
	Revision 1.16  2004/08/23 06:23:25  jcm10
	make it build on Windows
	
	Revision 1.15  2004/03/15 22:56:34  dwyatt
	cleanup, lose changes made for benefit of CAAudioUnit, remove unimplemented "use external buffer" stuff
	
	Revision 1.14  2004/03/13 01:32:22  bills
	fix ExternalOwnedBuffer implementation
	
	Revision 1.13  2003/12/04 22:55:20  luke
	add PrepareBuffer() function & make prepareXXX() args frames, not bytes
	
	Revision 1.12  2003/12/04 20:13:07  luke
	wrong comparison (should be >, not <=)
	
	Revision 1.11  2003/12/03 23:14:18  bills
	change prepare NULL buffer list to return a pointer not a reference
	
	Revision 1.10  2003/12/03 20:47:10  dwyatt
	fix broken build
	
	Revision 1.9  2003/12/02 20:51:34  luke
	add PrepareNullBuffer (from AUBuffer) for CAAudioUnit's benefit
	
	Revision 1.8  2003/10/09 23:24:01  dwyatt
	cosmetic
	
	Revision 1.7  2003/08/02 00:26:40  dwyatt
	revamp print
	
	Revision 1.6  2003/04/11 19:05:08  asynth
	fix two bugs in AllocateBuffersAndCopyFrom
	
	Revision 1.5  2003/04/08 21:54:09  asynth
	use memmove
	
	Revision 1.4  2003/04/08 21:49:57  asynth
	FillBufferFromInputProc was copying memory that had just been freed it in a certain case.
	
	Revision 1.3  2003/03/13 01:22:01  crogers
	make allocated buffers "hot" by calling memset()
	
	Revision 1.2  2003/02/27 01:21:40  dwyatt
	remove unnecessary code for forcing alignment
	
	Revision 1.1  2002/06/06 01:33:35  dwyatt
	initial checkin
	
	created 31 Apr 2002, 09:13, Doug Wyatt
	Copyright (c) 2002 Apple Computer, Inc.  All Rights Reserved

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

#include "CABufferList.h"
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
	#include <CoreServices/CoreServices.h>
#else
	#include <Endian.h>
#endif

void		CABufferList::AllocateBuffers(UInt32 nBytes)
{
	if (nBytes <= GetNumBytes()) return;
	
	if (mNumberBuffers > 1)
		// align successive buffers for Altivec and to take alternating
		// cache line hits by spacing them by odd multiples of 16
		nBytes = (nBytes + (0x10 - (nBytes & 0xF))) | 0x10;
	UInt32 memorySize = nBytes * mNumberBuffers;
	Byte *newMemory = new Byte[memorySize], *p = newMemory;
	memset(newMemory, 0, memorySize);	// get page faults now, not later
	
	AudioBuffer *buf = mBuffers;
	for (UInt32 i = mNumberBuffers; i--; ++buf) {
		if (buf->mData != NULL && buf->mDataByteSize > 0)
			// preserve existing buffer contents
			memcpy(p, buf->mData, buf->mDataByteSize);
		buf->mDataByteSize = nBytes;
		buf->mData = p;
		p += nBytes;
	}
	Byte *oldMemory = mBufferMemory;
	mBufferMemory = newMemory;
	delete[] oldMemory;
}

void		CABufferList::AllocateBuffersAndCopyFrom(UInt32 nBytes, CABufferList *inSrcList, CABufferList *inSetPtrList)
{
	if (mNumberBuffers != inSrcList->mNumberBuffers) return;
	if (mNumberBuffers != inSetPtrList->mNumberBuffers) return;
	if (nBytes <= GetNumBytes()) {
		CopyAllFrom(inSrcList, inSetPtrList);
		return;
	}
	inSetPtrList->VerifyNotTrashingOwnedBuffer();
	UInt32 fromByteSize = inSrcList->GetNumBytes();
	
	if (mNumberBuffers > 1)
		// align successive buffers for Altivec and to take alternating
		// cache line hits by spacing them by odd multiples of 16
		nBytes = (nBytes + (0x10 - (nBytes & 0xF))) | 0x10;
	UInt32 memorySize = nBytes * mNumberBuffers;
	Byte *newMemory = new Byte[memorySize], *p = newMemory;
	memset(newMemory, 0, memorySize);	// make buffer "hot"
	
	AudioBuffer *buf = mBuffers;
	AudioBuffer *ptrBuf = inSetPtrList->mBuffers;
	AudioBuffer *srcBuf = inSrcList->mBuffers;
	for (UInt32 i = mNumberBuffers; i--; ++buf, ++ptrBuf, ++srcBuf) {
		if (srcBuf->mData != NULL && srcBuf->mDataByteSize > 0)
			// preserve existing buffer contents
			memmove(p, srcBuf->mData, srcBuf->mDataByteSize);
		buf->mDataByteSize = nBytes;
		buf->mData = p;
		ptrBuf->mDataByteSize = srcBuf->mDataByteSize;
		ptrBuf->mData = p;
		p += nBytes;
	}
	Byte *oldMemory = mBufferMemory;
	mBufferMemory = newMemory;
	if (inSrcList != inSetPtrList)
		inSrcList->BytesConsumed(fromByteSize);
	delete[] oldMemory;
}

void		CABufferList::DeallocateBuffers()
{
	AudioBuffer *buf = mBuffers;
	for (UInt32 i = mNumberBuffers; i--; ++buf) {
		buf->mData = NULL;
		buf->mDataByteSize = 0;
	}
	if (mBufferMemory != NULL) {
		delete[] mBufferMemory;
		mBufferMemory = NULL;
	}
    
}

extern "C" void CAShowAudioBufferList(const AudioBufferList *abl, int framesToPrint, int wordSize)
{
	printf("AudioBufferList @ %p:\n", abl);
	const AudioBuffer *buf = abl->mBuffers;
	for (UInt32 i = 0; i < abl->mNumberBuffers; ++i, ++buf) {
		printf("  [%2ld]: %ldch, %ld bytes @ %p", 
			i, buf->mNumberChannels, buf->mDataByteSize, buf->mData);
		if (framesToPrint) {
			printf(":");
			Byte *p = (Byte *)buf->mData;
			for (int j = 0; j < framesToPrint; ++j)
				switch (wordSize) {
				case 0:
					printf(" %6.3f", *(Float32 *)p);
					p += sizeof(Float32);
					break;
				case 1:
				case -1:
					printf(" %02X", *p);
					p += 1;
					break;
				case 2:
					printf(" %04X", EndianU16_BtoN(*(UInt16 *)p));
					p += 2;
					break;
				case 3:
					printf(" %06X", (p[0] << 16) | (p[1] << 8) | p[2]);
					p += 3;
					break;
				case 4:
					printf(" %08lX", EndianU32_BtoN(*(UInt32 *)p));
					p += 4;
					break;
				case -2:
					printf(" %04X", EndianU16_LtoN(*(UInt16 *)p));
					p += 2;
					break;
				case -3:
					printf(" %06X", (p[2] << 16) | (p[1] << 8) | p[0]);
					p += 3;
					break;
				case -4:
					printf(" %08lX", EndianU32_LtoN(*(UInt32 *)p));
					p += 4;
					break;
				}
		}
		printf("\n");
	}
}

