//
// audioengine/soundpool.cpp
//
// Copyright (C) 1999-2013 Rockstar Games.  All Rights Reserved.
//

#include "engine.h"
#include "entity.h"
#include "requestedsettings.h"
#include "soundfactory.h"
#include "soundpool.h"

#include "audiohardware/debug.h"
#include "audiohardware/driver.h"
#include "audiohardware/driverutil.h"
#include "audiohardware/voicemgr.h"
#include "audiosoundtypes/environmentsound.h"
#include "audiosoundtypes/sound.h"
#include "grcore/debugdraw.h"

#if __PPU
#pragma comment(lib,"sync_stub")
#endif

namespace rage
{

#if __DEV
u32 g_MaxRequestedSettingsSlotsUsed = 0;
u32 g_MaxSoundSlotsUsed = 0;
u32 g_MaxVoicesUsed = 0;
u32 g_MaxSoundsAllocated = 0;
bool g_PrintSoundPoolWhenFull = false;
bool g_AssertOnSoundPoolFull = false;

#endif

#if !__SPU
#if __DEV
audSound **audSoundPool::sm_SoundPointers;
audRequestedSettings **audSoundPool::sm_RequestedSettingsPointers;
#endif
#endif // !__SPU

NOTFINAL_ONLY(u32 audSoundPool::sm_SkippedChildProcessing = 0);

#if !__SPU
#if __PS3 && AUD_SOUNDPOOL_LOCK_ON_SPU
class audBucketLock
{
public:
	audBucketLock(RageCellSyncMutex &sema) : m_Sema(&sema)
	{
		rageCellSyncMutexLock(&sema);
	}
	~audBucketLock()
	{
		rageCellSyncMutexUnlock(m_Sema);
	}
private:
	audBucketLock &operator=(const audBucketLock &){return *this;}
	RageCellSyncMutex *m_Sema;
};
#else//__PS3
class audBucketLock
{
public:
#if !__FINAL
	audBucketLock(sysCriticalSectionToken &critSec, sysIpcCurrentThreadId& threadId) : m_CrticalSection(&critSec), m_ThreadId(&threadId)
#else
	audBucketLock(sysCriticalSectionToken &critSec) : m_CrticalSection(&critSec)
#endif
	{
		critSec.Lock();
#if !__FINAL
		threadId = rage::sysIpcGetCurrentThreadId();
		sys_lwsync();
#endif
	}
	~audBucketLock()
	{
		m_CrticalSection->Unlock();
#if !__FINAL
		*m_ThreadId = NULL;
		sys_lwsync();
#endif
	}
private:
	audBucketLock &operator=(const audBucketLock &){return *this;}
	sysCriticalSectionToken *m_CrticalSection;
#if !__FINAL
	sysIpcCurrentThreadId* m_ThreadId;
#endif
};
#endif//__PS3

void audSoundPool::LockBucket(const u32 bucketId)
{
#if __PS3 && AUD_SOUNDPOOL_LOCK_ON_SPU
	rageCellSyncMutexLock(&m_BucketLocks[bucketId]);
#else
	m_BucketLocks[bucketId].Lock();
#if !__FINAL
	m_ThreadIds[bucketId] = rage::sysIpcGetCurrentThreadId();
	sys_lwsync();
#endif
#endif
}

void audSoundPool::UnlockBucket(const u32 bucketId)
{
#if __PS3 && AUD_SOUNDPOOL_LOCK_ON_SPU
	rageCellSyncMutexUnlock(&m_BucketLocks[bucketId]);
#else
	m_BucketLocks[bucketId].Unlock();
#if !__FINAL
	m_ThreadIds[bucketId] = NULL;
	sys_lwsync();
#endif
#endif
}

#if (__PS3 && AUD_SOUNDPOOL_LOCK_ON_SPU) || __FINAL
// use critical section instead of sema
#define AUD_SCOPED_BUCKET_LOCK(id) audBucketLock __bucketLock(m_BucketLocks[id])
#else
#define AUD_SCOPED_BUCKET_LOCK(id) audBucketLock __bucketLock(m_BucketLocks[id], m_ThreadIds[id])
#endif

BANK_ONLY(sysCriticalSectionToken audSoundPool::sm_DebugPrintToken);

#endif//!__SPU

audSoundPool::~audSoundPool()
{
	if (m_PoolBuffer)
	{
		audWarningf("audSoundPool::Shutdown() wasn't called - this isn't what killed the game, please don't put this in as an audio bug :-)");
	}
	// HACK_GTA4
	if (m_Buckets/*.GetElements()*/)
	{
		audWarningf("audSoundPool::Shutdown() wasn't called - this isn't what killed the game, please don't put this in as an audio bug :-)");
	}
}
#if !__SPU

void audSoundPool::Init(u32 soundSlotSize, u32 soundSlotsPerBucket, u32 requestedSettingsSlotSize, u32 requestedSettingsSlotsPerBucket, u32 numBuckets, u32 numReserveBuckets)
{
	m_NumBuckets = 0;
	m_NumSoundSlotsPerBucket = 0;
	m_NumSoundSlotsPerBucketAligned = 0;
	m_NumRequestedSettingsSlotsPerBucket = 0;
	m_NumRequestedSettingsSlotsPerBucketAligned = requestedSettingsSlotsPerBucket;
	m_SoundSlotSize = 0;
	m_RequestedSettingsSlotSize = 0;
	m_PoolBuffer = NULL;

	Assert(soundSlotsPerBucket <= g_audMaxSoundSlotsPerBucket);
	Assert(requestedSettingsSlotsPerBucket <= g_audMaxRequestedSettingsSlotsPerBucket);
	// num slots per bucket must be aligned to 32
	m_NumSoundSlotsPerBucketAligned = (soundSlotsPerBucket + 31) & ~31;

	m_SoundToReqSetRatio = m_NumSoundSlotsPerBucketAligned / requestedSettingsSlotsPerBucket;
	
#if __XENON
	// each slot should be 128 byte aligned to maximise cache utilisation
	m_SoundSlotSize = (soundSlotSize + 127) & ~127;
	m_RequestedSettingsSlotSize = (requestedSettingsSlotSize + 127) & ~127;
	audDisplayf("sizeof(audRequestedSettings): %u, aligned: %u", sizeof(audRequestedSettings), m_RequestedSettingsSlotSize);
#else
	m_SoundSlotSize = (soundSlotSize + 15) & ~15;
	m_RequestedSettingsSlotSize = (requestedSettingsSlotSize + 15) & ~15;
#endif
	m_NumSoundSlotsPerBucket = soundSlotsPerBucket;	
	m_NumRequestedSettingsSlotsPerBucket = requestedSettingsSlotsPerBucket;
	m_NumBuckets = numBuckets;
	m_NumBucketsReserved = numReserveBuckets;
	m_CurrentReservedBucketId = numBuckets - numReserveBuckets;

	// allocate pool storage
	const u32 soundBucketSize = (m_SoundSlotSize * m_NumSoundSlotsPerBucket);
	const u32 requestedSettingsBucketSize = (m_RequestedSettingsSlotSize * m_NumRequestedSettingsSlotsPerBucket);
	const u32 poolBufferSize = (soundBucketSize + requestedSettingsBucketSize) * m_NumBuckets;
	m_PoolBuffer = g_AudioEngine.AllocateVirtual(poolBufferSize, 128);
	Assert(m_PoolBuffer);
	m_PoolBufferSize = poolBufferSize;

	IF_DEBUG_MEMORY_FILL_N(sysMemSet(m_PoolBuffer, 0xad, poolBufferSize),DMF_AUDIOPOOL); // Nuke the pool with an easily identifiable pattern

	// allocate state storage
	const u32 bucketHeaderSize = m_NumBuckets * sizeof(audSoundPoolBucket);
	audSoundPoolBucket* poolBucket = (audSoundPoolBucket*) g_AudioEngine.AllocateVirtual(bucketHeaderSize, 128);
	//::new (&m_Buckets) atUserArray<audSoundPoolBucket>(poolBucket, (unsigned short) m_NumBuckets, true);

	m_Buckets = poolBucket;

	TrapZ((size_t)poolBucket);

	audDisplayf("audSoundPool Memory: %u pool %u bucketHeader for %u buckets, %u sounds @ %u, %u reqSets @ %u", 
		poolBufferSize, bucketHeaderSize, m_NumBuckets, m_NumSoundSlotsPerBucket, m_SoundSlotSize, m_NumRequestedSettingsSlotsPerBucket, m_RequestedSettingsSlotSize);


#if AUD_SUPPORT_RAVE_EDITING
	// Allocate variance cache
	m_VarianceCache.Resize(numBuckets);
	for(u32 i = 0; i < numBuckets; i++)
	{
		m_VarianceCache[i].Resize(m_NumSoundSlotsPerBucket);
	}
#endif

#if AUD_SOUNDPOOL_LOCK_ON_SPU
	m_BucketLocks = (RageCellSyncMutex*)g_AudioEngine.AllocateVirtual(sizeof(RageCellSyncMutex) * m_NumBuckets, 16);
#else
	m_BucketLocks = rage_new sysCriticalSectionToken[m_NumBuckets];
#if !__FINAL
	m_ThreadIds = rage_new sysIpcCurrentThreadId[m_NumBuckets];
#endif
#endif
	for(u32 i = 0 ; i < m_NumBuckets; i++)
	{
		audSoundPoolBucket &rBucket = m_Buckets[i];

		sysMemSet(&rBucket, 0, sizeof(m_Buckets[i]));

		rBucket.baseSoundPtr = (char *)m_PoolBuffer + (i * (soundBucketSize + requestedSettingsBucketSize));
		PointerRangeTrapCheck(rBucket.baseSoundPtr, soundBucketSize);
		rBucket.baseRequestedSettingsPtr = (char*)m_Buckets[i].baseSoundPtr + soundBucketSize;
		PointerRangeTrapCheck(rBucket.baseRequestedSettingsPtr, requestedSettingsBucketSize);
		Assign(rBucket.numSoundSlotsFree, m_NumSoundSlotsPerBucket);
		Assign(rBucket.numRequestedSettingsSlotsFree, m_NumRequestedSettingsSlotsPerBucket);
		Assign(rBucket.numVoicesFree, g_audMaxVoicesPerBucket);

#if AUD_SOUNDPOOL_LOCK_ON_SPU
		rageCellSyncMutexInitialize(&m_BucketLocks[i]);
#elif !__FINAL
		m_ThreadIds[i] = NULL;
#endif
		rBucket.currentUniqueId = 0;

#if __DEV
		rBucket.reqSetOwners = rage_new const char*[m_NumRequestedSettingsSlotsPerBucket];
		sysMemSet(rBucket.reqSetOwners, 0, sizeof(const char*) * m_NumRequestedSettingsSlotsPerBucket);
#endif

		// Initialise free lists
		m_Buckets[i].firstFreeSoundSlotIndex = 0;
		for(u32 j = 0; j < m_NumSoundSlotsPerBucket - 1; j++)
		{
			const u32 index = j+1;
			Assign(m_Buckets[i].soundFreeList[j], index);
		}
		m_Buckets[i].soundFreeList[m_NumSoundSlotsPerBucket-1] = 0xff;

		m_Buckets[i].firstFreeRequestedSettingsSlotIndex = 0;
		for(u32 j = 0; j < m_NumRequestedSettingsSlotsPerBucket - 1; j++)
		{
			const u32 index = j+1;
			Assign(m_Buckets[i].requestedSettingsFreeList[j], index);
		}
		m_Buckets[i].requestedSettingsFreeList[m_NumRequestedSettingsSlotsPerBucket-1] = 0xff;
	}

#if __DEV
	// build a handy index->sound/requested setting lookup table to aid debugging
	sm_SoundPointers = (audSound**)g_AudioEngine.AllocateVirtual(m_NumSoundSlotsPerBucket * m_NumBuckets * sizeof(audSound*));
	sm_RequestedSettingsPointers = (audRequestedSettings**)g_AudioEngine.AllocateVirtual(m_NumRequestedSettingsSlotsPerBucket * m_NumBuckets * sizeof(audRequestedSettings*));

	for(u8 bucket = 0 ; bucket < m_NumBuckets; bucket++)
	{
		for(u8 slot = 0; slot < m_NumSoundSlotsPerBucket; slot++)
		{
			void *p = GetSoundSlot(bucket, slot);
			Assert(p);
			sm_SoundPointers[(bucket * m_NumSoundSlotsPerBucket) + slot] = (audSound*)p;
		}
		for(u8 slot = 0; slot < m_NumRequestedSettingsSlotsPerBucket; slot++)
		{
			void *p = GetRequestedSettingsSlot(bucket, slot);
			Assert(p);
			sm_RequestedSettingsPointers[(bucket * m_NumRequestedSettingsSlotsPerBucket) + slot] = (audRequestedSettings*)p;
		}
		
	}
#endif
}

void audSoundPool::Shutdown()
{
	Assert(m_BucketLocks);
#if AUD_SOUNDPOOL_LOCK_ON_SPU
	g_AudioEngine.FreeVirtual(m_BucketLocks);
#else
	delete[] m_BucketLocks;
#if !__FINAL
	delete [] m_ThreadIds;
#endif
#endif

#if __DEV
	for(u32 i = 0; i < m_NumBuckets; i++)
	{
		if(m_Buckets[i].reqSetOwners)
		{
			delete[] m_Buckets[i].reqSetOwners;
		}
	}
#endif

	m_BucketLocks = NULL;

#if !__FINAL && !AUD_SOUNDPOOL_LOCK_ON_SPU
	m_ThreadIds = NULL;
#endif

	Assert(m_PoolBuffer);
	g_AudioEngine.FreeVirtual(m_PoolBuffer);
	m_PoolBuffer = NULL;
	// HACK_GTA4
	Assert(m_Buckets/*.GetElements()*/);
	g_AudioEngine.FreeVirtual(m_Buckets/*.GetElements()*/);
	//::new (&m_Buckets) atUserArray<audSoundPoolBucket>(NULL, 0);
	m_Buckets = NULL;

#if __DEV
	g_AudioEngine.FreeVirtual(sm_SoundPointers);
	g_AudioEngine.FreeVirtual(sm_RequestedSettingsPointers);
#endif
}

#endif // !__SPU


void *audSoundPool::AllocateSoundSlot(const size_t size, const u32 bucketId, const bool isStorageOnly)
{
	void *ret = NULL;
	audSoundPoolBucket *bucket;
	u32 slotSize;

#if __SPU
	bucket = g_Bucket;
	slotSize = g_BucketSoundSlotSize;
	if (size > slotSize)
	{
		Quitf("audSoundPool: Requested allocation too big for slot size");
	}
	// Don't need to lock the SPU version as we hold a lock for the entire update
#else
	if (bucketId >= m_NumBuckets)
	{
		Quitf(ERR_AUD_SOUND_1,"audSoundPool: Invalid bucketId passed to audSoundPool::AllocateSoundSlot().  Fatal");
	}
	bucket = &m_Buckets[bucketId];
	slotSize = m_SoundSlotSize;
	if (size > slotSize)
	{
		Quitf(ERR_AUD_SOUND_2,"audSoundPool: Requested allocation too big for slot size");
	}
	//AUD_SCOPED_BUCKET_LOCK(bucketId);
#endif

#if __DEV
	if(g_AssertOnSoundPoolFull || IsReservedBucket(bucketId))
	{	
		AssertMsg(bucket->numSoundSlotsFree > 0 , "audSoundPool: Requested allocation on full bucket");
	}
#endif

	if(bucket->numSoundSlotsFree > 0)
	{
		Assert(bucket->firstFreeSoundSlotIndex != 0xff);
		const u32 slotIndex = bucket->firstFreeSoundSlotIndex;
		bucket->firstFreeSoundSlotIndex = bucket->soundFreeList[slotIndex];
		
#if AUD_POOL_CHECK_ALLOCATION
		// safety check: this slot should not be marked allocated
		Assert(bucket->soundSlotAllocationState.IsClear(slotIndex));
		bucket->soundSlotAllocationState.Set(slotIndex);
		// the 'is parent' flag should be clear by default
		
#if __ASSERT 
		const u32 blockIndex = (slotIndex>>5);
		const u32 bitIndex = slotIndex - (blockIndex<<5);
		const u32 bitMask = (1<<bitIndex);
		Assert((bucket->soundSlotIsParent[blockIndex] & bitMask) == 0);
#endif
#endif

		bucket->soundSlotAllocationType.Set(slotIndex, isStorageOnly);
		bucket->numSoundSlotsFree--;

#if __DEV
		g_MaxSoundSlotsUsed = Max(g_MaxSoundSlotsUsed, m_NumSoundSlotsPerBucket - bucket->numSoundSlotsFree);
#endif
		ret = (char*)bucket->baseSoundPtr + (slotSize * slotIndex);
#if !__FINAL
#if !__SPU
		PointerRangeTrapCheck(ret, slotSize);
#endif
		TRAP_ONLY(IF_DEBUG_MEMORY_FILL_N(TrapNE(*reinterpret_cast<u32*>(ret), 0xadadadad),DMF_AUDIOPOOL));
#endif
		sysMemSet(ret, 0, slotSize);
		return ret;
	}
	else
	{
#if !__SPU
#if __DEV
		if(g_PrintSoundPoolWhenFull)
		{
#if __DEV
			DebugPrintSoundPool();
#else
			audWarningf("Sound pool full");
#endif
			g_PrintSoundPoolWhenFull = false;
		}

		if(g_AssertOnSoundPoolFull || IsReservedBucket(bucketId))
		{
			AssertMsg(m_Buckets[bucketId].numSoundSlotsFree > 0 , "audSoundPool: Requested allocation on full bucket");
		}
#endif
#endif
		
	}

	return ret;
}

void audSoundPool::MarkSoundSlotAsParent(const u32 bucketId, const u32 slotId)
{
	const u32 block = slotId >> 5;
	const u32 offset = slotId - (block<<5);
#if __SPU
	(void)bucketId;
	Assert(g_Bucket->soundSlotIsParent[block] & 1<<offset == 0);
	g_Bucket->soundSlotIsParent[block] |= (1<<offset);
#else
	Assert(!(m_Buckets[bucketId].soundSlotIsParent[block] & 1<<offset));

#if	AUD_POOL_CHECK_ALLOCATION
	Assert(m_Buckets[bucketId].soundSlotAllocationState.IsSet(slotId));
#endif

	m_Buckets[bucketId].soundSlotIsParent[block] |= (1<<offset);
#endif
}

#if __DEV
void *audSoundPool::AllocateRequestedSettingsSlot(const size_t ASSERT_ONLY(size), const u32 bucketId, const char *owner)
#else
void *audSoundPool::AllocateRequestedSettingsSlot(const size_t ASSERT_ONLY(size), const u32 bucketId)
#endif
{
	void *ret = NULL;
	audSoundPoolBucket *bucket;
	u32 slotSize;

#if __SPU
	bucket = g_Bucket;
	slotSize = g_BucketRequestedSettingsSlotSize;
	// Don't need to lock on the SPU as we hold the lock for the entire bucket update
#else
	bucket = &m_Buckets[bucketId];
	slotSize = m_RequestedSettingsSlotSize;
	AssertMsg(bucketId < m_NumBuckets , "audSoundPool: Invalid bucketId passed to audSoundPool::New()");
	//AUD_SCOPED_BUCKET_LOCK(bucketId);
#endif

	AssertMsg(size <= m_RequestedSettingsSlotSize , "audSoundPool: Requested allocation too big for slot size");
	AssertMsg(bucketId < m_NumBuckets , "audSoundPool: Invalid bucketId passed to audSoundPool::New()");

#if __DEV
	if(g_AssertOnSoundPoolFull || IsReservedBucket(bucketId))
	{
		AssertMsg(bucket->numRequestedSettingsSlotsFree > 0 , "audSoundPool: Requested allocation on full bucket");
	}
#endif


	if(bucket->numRequestedSettingsSlotsFree > 0)
	{
		Assert(bucket->firstFreeRequestedSettingsSlotIndex != 0xff);
		const u32 slotIndex = bucket->firstFreeRequestedSettingsSlotIndex;
		bucket->firstFreeRequestedSettingsSlotIndex = bucket->requestedSettingsFreeList[slotIndex];

#if AUD_POOL_CHECK_ALLOCATION		
		Assert(bucket->requestedSettingsSlotAllocationState.IsClear(slotIndex));
		bucket->requestedSettingsSlotAllocationState.Set(slotIndex);
#endif // AUD_POOL_CHECK_ALLOCATION
				
		bucket->numRequestedSettingsSlotsFree--;

		DEV_ONLY(g_MaxRequestedSettingsSlotsUsed = Max(g_MaxRequestedSettingsSlotsUsed, m_NumRequestedSettingsSlotsPerBucket - bucket->numRequestedSettingsSlotsFree));

#if !__SPU
		DEV_ONLY(bucket->reqSetOwners[slotIndex] = owner);
#endif
		ret = (char*)bucket->baseRequestedSettingsPtr + (slotSize * slotIndex);
#if !__FINAL
#if !__SPU
						PointerRangeTrapCheck(ret, slotSize);
#endif
#if !__PS3
						IF_DEBUG_MEMORY_FILL_N(TrapNE(*reinterpret_cast<u32*>(ret), 0xadadadad),DMF_AUDIOPOOL);
#endif
#endif
		sysMemSet(ret, 0, slotSize);
		return ret;		
	}
	else
	{
		Assert(bucket->firstFreeRequestedSettingsSlotIndex == 0xff);
#if !__SPU
#if __DEV
		if(g_PrintSoundPoolWhenFull)
		{
#if __DEV
			DebugPrintSoundPool();
#else
			audWarningf("Sound pool full");
#endif
			g_PrintSoundPoolWhenFull = false;
		}

		if(g_AssertOnSoundPoolFull || IsReservedBucket(bucketId))
		{
			AssertMsg(m_Buckets[bucketId].numRequestedSettingsSlotsFree > 0 , "audSoundPool: Requested allocation on full bucket");
		}
#endif
#endif

	}
	return ret;
}

void audSoundPool::DeleteSound(void *p, u32 bucketId)
{
	Assert(p);

#if __SPU
	audSoundPoolBucket *bucket = g_Bucket;
	u32 slotSize = g_BucketSoundSlotSize;
	bucketId = 0;
	// Don't need to lock on the SPU as we hold a lock for the entire bucket update
#else
	audSoundPoolBucket *bucket = &m_Buckets[bucketId];
	u32 slotSize = m_SoundSlotSize;
	//AUD_SCOPED_BUCKET_LOCK(bucketId);
#endif

	const u32 bucketSlotIndex = (u32)(((size_t)p - (size_t)bucket->baseSoundPtr) / slotSize);
	

#if AUD_POOL_CHECK_ALLOCATION	
	audAssertf(bucket->soundSlotAllocationState.IsSet(bucketSlotIndex), "audSoundPool::Delete() called on free slot.");
	bucket->soundSlotAllocationState.Clear(bucketSlotIndex);
#endif

	// clear the IsParent bit to prevent this slot from being processed
	const u32 block = (bucketSlotIndex>>5);
	const u32 bitIndex = bucketSlotIndex - (block<<5);
	const u32 bitMask = 1<<bitIndex;
	bucket->soundSlotIsParent[block] &= ~bitMask;
	bucket->numSoundSlotsFree++;

	// add to free list
	Assign(bucket->soundFreeList[bucketSlotIndex], bucket->firstFreeSoundSlotIndex);
	Assign(bucket->firstFreeSoundSlotIndex, bucketSlotIndex);

	// if this is not a storage slot check that we're not deleting a sound that has a pending child sound instantiation
	if(bucket->soundSlotAllocationType.IsClear(bucketSlotIndex) && ((audSound*)p)->HasPendingChildInstantation())
	{
		for(u32 i = 0; i < bucket->numChildRequests; i++)
		{
			if(bucket->childSoundRequests[i].parentSoundIdx == bucketSlotIndex)
			{
				// invalidate this request
				bucket->childSoundRequests[i].parentSoundIdx = 0xff;
			}
		}
	}

#if !__FINAL
	IF_DEBUG_MEMORY_FILL_N(sysMemSet(p, 0xad, slotSize),DMF_AUDIOPOOL); // Debug audio signature
#if !__SPU
	PointerRangeTrapCheck(p, slotSize);
	//	Store the time we were last freed
	u32* uintPtr = static_cast<u32*>(p);
	uintPtr[1] = g_AudioEngine.GetTimeInMilliseconds();
#endif
#endif
}

bool audSoundPool::IsParentSoundSlot(const u32 bucketId, const u32 slotId)
{
	const u32 block = (slotId>>5);
	const u32 blockIndex = slotId - (block<<5);
	const u32 bitMask = 1<<blockIndex;

	if(m_Buckets[bucketId].soundSlotIsParent[block] & bitMask)
	{
#if AUD_POOL_CHECK_ALLOCATION
		Assert(m_Buckets[bucketId].soundSlotAllocationState.IsSet(slotId));
#endif
		Assert(m_Buckets[bucketId].soundSlotAllocationType.IsClear(slotId));
		return true;
	}
	return false;
}

void audSoundPool::DeleteRequestedSettings(void *p, u32 bucketId)
{
	Assert(p);

#if __SPU
	audSoundPoolBucket *bucket = g_Bucket;
	u32 slotSize = g_BucketRequestedSettingsSlotSize;
	bucketId = 0;
	// Don't need to lock on the SPU as we hold a lock for the entire bucket update
#else
	audSoundPoolBucket *bucket = &m_Buckets[bucketId];
	u32 slotSize = m_RequestedSettingsSlotSize;
	//AUD_SCOPED_BUCKET_LOCK(bucketId);
#endif

	u32 bucketSlotIndex = (u32)(((size_t)p - (size_t)bucket->baseRequestedSettingsPtr) / slotSize);
	
	DeleteRequestedSettings(bucketId, bucketSlotIndex);
}

void audSoundPool::DeleteRequestedSettings(u32 bucketId, u32 bucketSlotIndex)
{
#if __SPU
	audSoundPoolBucket *bucket = g_Bucket;
	bucketId = 0;
	// Don't need to lock on the SPU as we hold a lock for the entire bucket update
#else
	audSoundPoolBucket *bucket = &m_Buckets[bucketId];
	//AUD_SCOPED_BUCKET_LOCK(bucketId);
#endif

	bucket->requestedSettingsFreeList[bucketSlotIndex] = bucket->firstFreeRequestedSettingsSlotIndex;
	Assign(bucket->firstFreeRequestedSettingsSlotIndex, bucketSlotIndex);
	bucket->numRequestedSettingsSlotsFree++;

#if AUD_POOL_CHECK_ALLOCATION	
	AssertMsg(bucket->requestedSettingsSlotAllocationState.IsSet(bucketSlotIndex), "audSoundPool::DeleteRequestedSettings() called on free slot");
	bucket->requestedSettingsSlotAllocationState.Clear(bucketSlotIndex);
#endif // AUD_POOL_CHECK_ALLOCATION	

#if !__FINAL && !__PS3 
	void *p = GetRequestedSettingsSlot(bucketId, bucketSlotIndex);
	IF_DEBUG_MEMORY_FILL_N(sysMemSet(p, 0xad, m_RequestedSettingsSlotSize),DMF_AUDIOPOOL); // Debug audio signature
	PointerRangeTrapCheck(p, m_RequestedSettingsSlotSize);
	//	Store the time we were last freed
	reinterpret_cast<u32*>(p)[1] = g_AudioEngine.GetTimeInMilliseconds();
#endif

#if !__SPU
	DEV_ONLY(bucket->reqSetOwners[bucketSlotIndex] = NULL);
#endif
}

u8 audSoundPool::AllocateVoice(const u32 bucketId)
{
#if __SPU
	audSoundPoolBucket *bucket = g_Bucket;
#else
	Assert(bucketId < m_NumBuckets);
	audSoundPoolBucket *bucket = &m_Buckets[bucketId];
#endif
	if(bucket->numVoicesFree > 0)
	{
		for(u32 i = 0; i < g_audMaxVoicesPerBucket; i++)
		{
			if(bucket->voiceAllocationState.IsClear(i))
			{
				bucket->voiceAllocationState.Set(i);
				// TODO: remove this, or change environment sound (1 unnecessary constructor call per allocation)
				new(&bucket->voiceSettings[i]) audVoiceSettings();

				bucket->numVoicesFree--;
	#if __DEV
				g_MaxVoicesUsed = Max(g_MaxVoicesUsed, g_audMaxVoicesPerBucket - bucket->numVoicesFree);
	#endif

				u8 ret;
				Assign(ret,i);
				return ret;
			}
		}
		Assert(0);
		return 0xff;
	}
	else
	{
		audWarningf("bucket %u is out of voices", bucketId);
		return 0xff;
	}
}

void audSoundPool::FreeVoice(const u32 bucketId, const u32 voiceId)
{
#if __SPU
	audSoundPoolBucket *bucket = g_Bucket;
#else
	Assert(bucketId < m_NumBuckets);
	audSoundPoolBucket *bucket = &m_Buckets[bucketId];
#endif

	Assert(bucket->voiceAllocationState.IsSet(voiceId));
	bucket->voiceAllocationState.Clear(voiceId);
	bucket->numVoicesFree++;
}

u32 audSoundPool::GetUniqueId(const u32 bucketId)
{
#if __SPU
	audSoundPoolBucket *bucket = g_Bucket;
#else
	Assert(bucketId < m_NumBuckets);
	//AUD_SCOPED_BUCKET_LOCK(bucketId);
	audSoundPoolBucket *bucket = &m_Buckets[bucketId];
#endif

	const u32 ret = 1 + (bucketId<<8) + bucket->currentUniqueId++;

	return ret;
}

#if !__SPU

u32 audSoundPool::GetEmptiestBucketId()
{
	u32 bucketId = 0, numFree = 0;
	for(u32 i = 0; i < (m_NumBuckets-m_NumBucketsReserved); i++)
	{
		// consider the lower of reqSetSlotsFree (scaled up to be comparable) and soundSlotsFree
		const u32 numSoundSlotsFree = Min<u32>(m_Buckets[i].numSoundSlotsFree, m_Buckets[i].numRequestedSettingsSlotsFree * m_SoundToReqSetRatio);
		if(numSoundSlotsFree > numFree)
		{
			numFree = numSoundSlotsFree;
			bucketId = i;
		}
	}
	
	return bucketId;
}

void audSoundPool::ProcessChildSoundRequests(const audEngineContext *context, const u32 bucketId)
{
	if(m_Buckets[bucketId].numChildRequests)
	{		
		AUD_SCOPED_BUCKET_LOCK(bucketId);

		m_Buckets[bucketId].isProcessingChildSoundRequests = true;

		for(u32 i = 0; i < m_Buckets[bucketId].numChildRequests; i++)
		{
			if(m_Buckets[bucketId].childSoundRequests[i].parentSoundIdx != 0xff)
			{
				audSound *parent = (audSound*)GetSoundSlot(bucketId, m_Buckets[bucketId].childSoundRequests[i].parentSoundIdx);
				Assert(parent);
				// need to use the correct timer
				const u32 updateTimeMs = context->timers[parent->GetTimerId()].timeInMs;
				if(m_Buckets[bucketId].childSoundRequests[i].metadataOffset.IsValid())
				{
					audSound *child = SOUNDFACTORY.GetChildInstance(m_Buckets[bucketId].childSoundRequests[i].metadataOffset, parent, &m_Buckets[bucketId].childSoundRequests[i].initParams, &m_Buckets[bucketId].childSoundRequests[i].scratchInitParams, false);
					parent->_HandleDeferredChildInstantiation(updateTimeMs, m_Buckets[bucketId].childSoundRequests[i].childIndex, child);
				}
				else
				{
					// fake child instantiation, used for a generic PPU callback
					parent->_HandleDeferredChildInstantiation(updateTimeMs, ~0U, NULL);
				}
			}
		}
		m_Buckets[bucketId].numChildRequests = 0;
		m_Buckets[bucketId].isProcessingChildSoundRequests = false;
	}
	else
	{
		NOTFINAL_ONLY(sm_SkippedChildProcessing++);
	}
}

#endif // !__SPU

audSound *audSoundPool::ParentSoundIterator::Next()
{
	while(m_Block < m_NumBlocks)
	{		
		if(m_CurrentParentBlock != 0)
		{
			for(; m_CurrentIndex < 32; m_CurrentIndex++)
			{
				// check this slot is allocated, is a parent sound and isn't marked storage only
				if(m_CurrentParentBlock & m_BitMask)
				{
					const u32 index = (m_Block << 5) + m_CurrentIndex;

#if AUD_POOL_CHECK_ALLOCATION
					Assert(m_Bucket->soundSlotAllocationState.IsSet(index));
					Assert(m_Bucket->soundSlotAllocationType.IsClear(index));
#endif					

					audSound *sound = reinterpret_cast<audSound*>((u8*)m_Bucket->baseSoundPtr + m_SoundSlotSize * index);
					Assert(sound->IsInitialised());
					Assert(!sound->_GetParent());
					if(sound->IsInitialised() && !sound->_GetParent())
					{
						m_CurrentIndex++;
						// Update the current block mask, removing this sound
						m_CurrentParentBlock &= ~m_BitMask;
						m_BitMask <<= 1;
						return sound;
					}
				}
				m_BitMask <<= 1;
			}
		}

		m_Block++;
		m_CurrentIndex = 0;
		m_BitMask = 1;
		if(m_Block < m_NumBlocks)
		{
			m_CurrentParentBlock = m_Bucket->soundSlotIsParent[m_Block];
		}
	}

	return NULL;
}

void audSoundPool::RequestChildSound(const u32 bucketId, const u32 parentIdx, const audMetadataRef offset, const audSoundInternalInitParams &initParams, const u32 childIndex, const audSoundScratchInitParams *scratchInitParams)
{
#if __SPU
	audSoundPoolBucket *bucket = g_Bucket;
#else
	Assert(bucketId < m_NumBuckets);
	audSoundPoolBucket *bucket = &m_Buckets[bucketId];
#endif
#if __ASSERT && !__SPU
	audSound *parentSound = reinterpret_cast<audSound*>(GetSoundSlot(bucketId, parentIdx));
	Assertf(bucket->numChildRequests < g_audMaxChildSoundRequestsPerBucket, "parent sound: %s", parentSound->GetName());
#else
	Assert(bucket->numChildRequests < g_audMaxChildSoundRequestsPerBucket);
#endif
	if(bucket->numChildRequests < g_audMaxChildSoundRequestsPerBucket)
	{
		Assign(bucket->childSoundRequests[bucket->numChildRequests].parentSoundIdx, parentIdx);
		Assign(bucket->childSoundRequests[bucket->numChildRequests].childIndex, childIndex);
		bucket->childSoundRequests[bucket->numChildRequests].metadataOffset = offset;
		bucket->childSoundRequests[bucket->numChildRequests].initParams = initParams;
		if(scratchInitParams)
		{
			bucket->childSoundRequests[bucket->numChildRequests].scratchInitParams = *scratchInitParams;
		}
		bucket->numChildRequests++;
	}
	else
	{
		audErrorf("Too many child callback requests in bucket %u, parent sound: %u", bucketId, parentIdx);
	}
}

u32 audSoundPool::GetVoiceId(const u32 bucketId, const audVoiceSettings *voiceSettings)
{
	Assert(bucketId < m_NumBuckets);
	audSoundPoolBucket *bucket = &m_Buckets[bucketId];
	return (u32)(voiceSettings - &bucket->voiceSettings[0]);
}

#if !__SPU
#if !__FINAL
void audSoundPool::GetPoolUtilisation(audSoundPoolStats &stats) const
{
	stats.skippedChildProcessing = sm_SkippedChildProcessing;
	sm_SkippedChildProcessing = 0;

	stats.soundSlotsFree = 0;
	stats.leastSoundSlotsFree = ~0U; stats.mostSoundSlotsFree = 0;
	stats.leastReqSetsSlotsFree = ~0U; stats.mostReqSetsSlotsFree = 0;
	stats.leastVoicesFree = ~0U;
	stats.fullestBucket = 0; stats.emptiestBucket = 0;
	for(u32 i = 0; i < m_NumBuckets - m_NumBucketsReserved; i++)
	{
		const audSoundPoolBucket& rBucket = m_Buckets[i];
		const u32 numSoundSlotsFree = rBucket.numSoundSlotsFree;
		const u32 numRequestedSettingsSlotsFree = rBucket.numRequestedSettingsSlotsFree;
		const u32 numVoicesFree = rBucket.numVoicesFree;

		if(numSoundSlotsFree < stats.leastSoundSlotsFree)
		{
			stats.fullestBucket = i;
			stats.leastSoundSlotsFree = numSoundSlotsFree;
		}
		if(numSoundSlotsFree > stats.mostSoundSlotsFree)
		{
			stats.mostSoundSlotsFree = numSoundSlotsFree;
			stats.emptiestBucket = i;
		}
		
		stats.leastReqSetsSlotsFree = Min(numRequestedSettingsSlotsFree,stats.leastReqSetsSlotsFree);
		stats.mostReqSetsSlotsFree = Max(numRequestedSettingsSlotsFree,stats.mostReqSetsSlotsFree);
		stats.leastVoicesFree = Min(numVoicesFree, stats.leastVoicesFree);
		
		stats.soundSlotsFree += numSoundSlotsFree;
	}
	stats.mostVoicesUsed = g_audMaxVoicesPerBucket - stats.leastVoicesFree;
	stats.soundSlotsAllocated = (m_NumBuckets * m_NumSoundSlotsPerBucket) - stats.soundSlotsFree;
#if __DEV
	g_MaxSoundsAllocated = Max(g_MaxSoundsAllocated,stats.soundSlotsAllocated);
#endif
}
void audSoundPool::PointerRangeTrapCheck(void* TRAP_ONLY(p), u32 TRAP_ONLY(size))
{
	TRAP_ONLY(const size_t lowerBound = (size_t)m_PoolBuffer);
	TRAP_ONLY(const size_t upperBound = (size_t)m_PoolBuffer + m_PoolBufferSize);
	TrapLT((size_t)p, lowerBound);
	TrapGT((size_t)p+size, upperBound);
}
#endif

#if __BANK

void audSoundPool::DebugDrawBuckets(f32 yScroll, u32 numSoundsPerBucket) const
{
#if __USEDEBUGAUDIO
	f32 yCoord = 0.05f - yScroll;
	f32 stepRate = 0.02f;

	for (u32 bucketIndex = 0; bucketIndex < m_NumBuckets; bucketIndex++)
	{
		f32 xCoord = 0.05f;
		const audSoundPoolBucket& rBucket = m_Buckets[bucketIndex];
		const u32 numSoundSlotsFree = rBucket.numSoundSlotsFree;
		char tempString[128];
		formatf(tempString, "Bucket %d: Slots Free: %d/%d (%.02f%%)", bucketIndex, numSoundSlotsFree, m_NumSoundSlotsPerBucketAligned, ((numSoundSlotsFree / (f32)m_NumSoundSlotsPerBucketAligned) * 100));
		grcDebugDraw::Text(Vector2(xCoord, yCoord), Color32(255, 255, 255), tempString);
		yCoord += stepRate;
		xCoord += stepRate;

		audSound::GetStaticPool().LockBucket(bucketIndex);
		atFixedBitSet<g_audMaxSoundSlotsPerBucket> allocationState;
		ComputeSoundSlotAllocationState(bucketIndex, allocationState);

		struct SoundInstance
		{
			u32 nto;
			u32 count;
			u32 stateCounts[AUD_SOUND_STATE_MAX];
		};

		atFixedArray<SoundInstance, g_audMaxSoundSlotsPerBucket> soundInstances;

		for (u32 i = 0; i < m_NumSoundSlotsPerBucketAligned >> 5; i++)
		{
			for (u32 j = 0; j < 32; j++)
			{
				const u32 slotId = (i << 5) + j;

				// check this slot is allocated and its of type 0 (sound)
				if (slotId < g_audMaxSoundSlotsPerBucket && allocationState.IsSet(slotId))
				{
					if (rBucket.soundSlotAllocationType.IsClear(slotId))
					{
						audSound* sound = (audSound*)((u8*)rBucket.baseSoundPtr + slotId * m_SoundSlotSize);

						if (sound)
						{
							bool found = false;
							const u32 nameTableOffset = sound->GetNameTableOffset();

							if (nameTableOffset != 0xFFFFFF)
							{
								for (SoundInstance& existingInstance : soundInstances)
								{
									if (existingInstance.nto == nameTableOffset)
									{
										existingInstance.count++;
										existingInstance.stateCounts[sound->GetPlayState()]++;
										found = true;
										break;
									}
								}

								if (!found)
								{
									SoundInstance newInstance;
									newInstance.nto = nameTableOffset;
									newInstance.count = 1;
									sysMemSet(&newInstance.stateCounts, 0, sizeof(u32) * AUD_SOUND_STATE_MAX);
									newInstance.stateCounts[sound->GetPlayState()] = 1;
									soundInstances.Push(newInstance);
								}
							}
						}
					}
				}
			}
		}

		soundInstances.QSort(0, -1, [](const SoundInstance* a, const SoundInstance* b) -> int
			{
				if (a->count > b->count)
				{
					return -1;
				}
				else if (a->count < b->count)
				{
					return 1;
				}
				else
				{
					return a->nto < b->nto ? -1 : 1;
				}
			});

		for (u32 sortedInstance = 0; sortedInstance < numSoundsPerBucket; sortedInstance++)
		{
			if (sortedInstance < (u32)soundInstances.GetCount())
			{
				formatf(tempString, "%s (%u %s - %d dormant, %d preparing, %d playing, %d deleting)",
					g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetNameFromNTO_Debug(soundInstances[sortedInstance].nto),
					soundInstances[sortedInstance].count,
					soundInstances[sortedInstance].count > 1 ? "instances" : "instance",
					soundInstances[sortedInstance].stateCounts[AUD_SOUND_DORMANT],
					soundInstances[sortedInstance].stateCounts[AUD_SOUND_PREPARING],
					soundInstances[sortedInstance].stateCounts[AUD_SOUND_PLAYING],
					soundInstances[sortedInstance].stateCounts[AUD_SOUND_WAITING_TO_BE_DELETED]);

				grcDebugDraw::Text(Vector2(xCoord, yCoord), Color32(255, 255, 255), tempString);
			}

			yCoord += stepRate;
		}

		audSound::GetStaticPool().UnlockBucket(bucketIndex);
		yCoord += stepRate;
	}
#endif
}

void audSoundPool::ComputeSoundSlotAllocationState(const u32 bucketId, atFixedBitSet<g_audMaxSoundSlotsPerBucket> &allocationState) const
{
	const audSoundPoolBucket &bucket = m_Buckets[bucketId];
	
	allocationState.SetAll();

	u32 currentIndex = bucket.firstFreeSoundSlotIndex;
	while(currentIndex != 0xff)
	{
		allocationState.Clear(currentIndex);
		currentIndex = bucket.soundFreeList[currentIndex];
	}
}

void audSoundPool::DebugPrintDormantSounds() const
{
	SYS_CS_SYNC(audSoundPool::sm_DebugPrintToken);

	audDisplayf("Dormant sounds:");
	const u32 now = g_AudioEngine.GetTimeInMilliseconds();
	audDisplayf(",BucketId,SlotId,Name,TypeID,EntityName,ParentId,Lifetime");
	for(u32 bucketId = 0; bucketId < m_NumBuckets; bucketId++)
	{
		const audSoundPoolBucket& rBucket = m_Buckets[bucketId];
		audSound::GetStaticPool().LockBucket(bucketId);
		atFixedBitSet<g_audMaxSoundSlotsPerBucket> allocationState;
		ComputeSoundSlotAllocationState(bucketId, allocationState);
		for(u32 i = 0 ; i < m_NumSoundSlotsPerBucketAligned >> 5; i++)
		{			
			for(u32 j = 0; j < 32; j++)
			{
				const u32 slotId = (i<<5) + j;
				// check this slot is allocated and isnt marked storage only
				if(allocationState.IsSet(slotId)  && rBucket.soundSlotAllocationType.IsClear(slotId))
				{
					audSound *sound = (audSound*)((char*)m_Buckets[bucketId].baseSoundPtr + (((i<<5) + j) * m_SoundSlotSize));
					if(sound->IsInitialised())
					{
						if(sound->GetPlayState() == AUD_SOUND_DORMANT)
						{
							u32 parentId = (sound->_GetParent()?GetSoundSlotIndex(bucketId, sound->_GetParent()):0xff);
							audDisplayf(",%u,%u,%s,%u,%s,%u,%u", bucketId, slotId, sound->GetName(), sound->GetSoundTypeID(), (sound->GetRequestedSettings()&&sound->GetEntity()?sound->GetEntity()->GetName():"no entity"), parentId,now - sound->GetTimeInitialised());
						}
					}
				}
			}
		}
		audSound::GetStaticPool().UnlockBucket(bucketId);
	}
}


void audSoundPool::DebugPrintWaitingToBeDeletedSounds() const
{
	SYS_CS_SYNC(audSoundPool::sm_DebugPrintToken);

	audDisplayf("Sounds waiting to be deleted:");
	for(u32 bucketId = 0; bucketId < m_NumBuckets; bucketId++)
	{
		audSound::GetStaticPool().LockBucket(bucketId);
		atFixedBitSet<g_audMaxSoundSlotsPerBucket> allocationState;
		ComputeSoundSlotAllocationState(bucketId, allocationState);
		for(u32 i = 0 ; i < m_NumSoundSlotsPerBucketAligned >> 5; i++)
		{
			const audSoundPoolBucket& rBucket = m_Buckets[bucketId];
					
			for(u32 j = 0; j < 32; j++)
			{
				const u32 slotId = (i<<5) + j;
				// check this slot is allocated and isnt marked storage only
				if(allocationState.IsSet(slotId) && rBucket.soundSlotAllocationType.IsClear(slotId))
				{
					audSound *sound = (audSound*)((char*)m_Buckets[bucketId].baseSoundPtr + (slotId * m_SoundSlotSize));
					if(sound->IsInitialised())
					{
						if(sound->GetPlayState() == AUD_SOUND_WAITING_TO_BE_DELETED)
						{
							audDisplayf(",%u,%u,%s,%u,%s", bucketId, slotId, sound->GetName(), sound->GetSoundTypeID(), (sound->GetRequestedSettings()&&sound->GetEntity()?sound->GetEntity()->GetName():"no entity"));
						}
					}
				}
			}
		}
		audSound::GetStaticPool().UnlockBucket(bucketId);
	}
}

void audSoundPool::DebugPrintSoundPool() const
{
	SYS_CS_SYNC(audSoundPool::sm_DebugPrintToken);

	audSoundPoolStats stats;
	GetPoolUtilisation(stats);
	audDisplayf("Sound Pool Utilisation: fullestBucket: %u, emptiestBucket: %u, mostSlotsFree: %u, totalSlotsAllocated: %u, totalSlotsFree: %u", stats.fullestBucket, stats.emptiestBucket, stats.mostSoundSlotsFree, stats.soundSlotsAllocated, stats.soundSlotsFree);

#if __DEV
	for(u32 bucketId = 0 ; bucketId < m_NumBuckets; bucketId++)
	{
		audDisplayf("Requested Settings Allocation for bucket %u (%u slots free)", bucketId, m_Buckets[bucketId].numRequestedSettingsSlotsFree);
		for(u32 i = 0; i < m_NumRequestedSettingsSlotsPerBucket; i++)
		{
			if(m_Buckets[bucketId].reqSetOwners[i])
			{
				audDisplayf("%u,%s", i, m_Buckets[bucketId].reqSetOwners[i]);
			}
		}
	}
#endif

	audDisplayf(",bucketId,slot,Name,Type,State,Entity,Parent,Lifetime,playtime");

	u32 now = g_AudioEngine.GetTimeInMilliseconds();
	for(u32 bucketId = 0; bucketId < m_NumBuckets; bucketId++)
	{
		audSound::GetStaticPool().LockBucket(bucketId);
		atFixedBitSet<g_audMaxSoundSlotsPerBucket> allocationState;
		ComputeSoundSlotAllocationState(bucketId, allocationState);
		for(u32 i = 0 ; i < m_NumSoundSlotsPerBucketAligned>>5; i++)
		{
			const audSoundPoolBucket& rBucket = m_Buckets[bucketId];
					
			for(u32 j = 0; j < 32; j++)
			{
				const u32 slotId = (i<<5) + j;
				// check this slot is allocated and its of type 0 (sound)
				if(slotId < m_NumSoundSlotsPerBucket && allocationState.IsSet(slotId))
				{
					if(rBucket.soundSlotAllocationType.IsClear(slotId))
					{
						audSound *sound = (audSound*)((u8*)m_Buckets[bucketId].baseSoundPtr + slotId * m_SoundSlotSize);
						if(sound->IsInitialised())
						{
							const char *soundState="invalid";
							switch(sound->GetPlayState())
							{
							case AUD_SOUND_DORMANT:
								soundState = "dormant";
								break;
							case AUD_SOUND_PREPARING:
								soundState = "preparing";
								break;
							case AUD_SOUND_PLAYING:
								soundState = "playing";
								break;
							case AUD_SOUND_WAITING_TO_BE_DELETED:
								soundState = "waiting to be deleted";
								break;
							}
							char printBuf[128];
							u32 parentId = (sound->_GetParent()?GetSoundSlotIndex(bucketId, sound->_GetParent()):0xff);
							formatf(printBuf, sizeof(printBuf), "%u,%u,%u", parentId, now - sound->GetTimeInitialised(), sound->GetCurrentPlayTime(now));					

							char nameBuf[64];

							if(sound->GetSoundTypeID() == EnvironmentSound::TYPE_ID)
							{
								formatf(nameBuf, "%s [EnvironmentSound]", sound->_GetParent() ? sound->_GetParent()->GetName() : "NULL Parent");
							}
							else
							{
								formatf(nameBuf, "%s", sound->GetName());
							}
							audDisplayf(",%u,%u,%s,%s,%s,%s,%s", bucketId, slotId, nameBuf, g_AudioEngine.GetSoundManager().GetFactory().GetTypeName(sound->GetSoundTypeID()), 
								soundState,(sound->GetRequestedSettings()&&sound->GetEntity()?sound->GetEntity()->GetName():"no entity"),printBuf);
							
						}
						else
						{
							audDisplayf("%u,%u,[Uninitialised],,,,,,,", bucketId, slotId);
						}
					}
					else
					{
						audDisplayf("%u,%u,[storage],,,,,,,", bucketId, slotId);
					}
				}
			}
		}
		audSound::GetStaticPool().UnlockBucket(bucketId);
	}

}

bool audSoundPool::MatchRAGFilter(const char* soundName, const char* soundTag, const char* nameFilter, const char* ignoreNameFilter, const char* tagFilter, const char* ignoreTagFilter)
{
	if(soundName)
	{
		if(nameFilter[0] != 0 && !audEngineUtil::MatchName(soundName, nameFilter))
		{
			return false;
		}

		if(ignoreNameFilter[0] != 0 && audEngineUtil::MatchName(soundName, ignoreNameFilter))
		{
			return false;
		}
	}

	if(soundTag)
	{
		if(tagFilter[0] != 0 && !audEngineUtil::MatchName(soundTag, tagFilter))
		{
			return false;
		}

		if(ignoreTagFilter[0] != 0 && audEngineUtil::MatchName(soundTag, ignoreTagFilter))
		{
			return false;
		}
	}
	else if(tagFilter[0] != 0)
	{
		return false;
	}

	return true;
}

struct PlayingSoundInfo
{
	const char* soundName;
	const char* entityName;
	u32 pcmCost;
};

static s32 SoundInfoCompareFunc (PlayingSoundInfo* const* a, PlayingSoundInfo* const* b) { return (*b)->pcmCost - (*a)->pcmCost; }

void audSoundPool::DebugDrawSounds(bool drawPositionInfo, bool drawEntityInfo, bool drawSoundHierarchy, bool drawSoundPCMCosts, const char* nameFilter, const char* ignoreNameFilter, const char* tagFilter, const char* ignoreTagFilter, bool parentSoundsOnly, Vec3V currentPlayerPosition, f32 viewScroll, f32 yPositionSpread)
{		
	char tempBuffer[256];
	u32 numMatchingSounds = 0;
	audSoundDebugDrawManager drawMgr(100.f, 50.f - (1280 * viewScroll), 50.f, 1280.f);    
	atMap<audSound*,PlayingSoundInfo> sourceCostMap;
	u32 totalPCMSourceCost = 0;	
	f32 yCoord = 0.02f - viewScroll;
	f32 nonPositionalSoundYCoord = 0.02f - viewScroll;
	f32 positionalSoundYCoord = nonPositionalSoundYCoord;
	f32 nonPositionalSoundXCoord = 0.7f;
	f32 positionalSoundXCoord = 0.05f;

	if(drawPositionInfo)
	{
		grcDebugDraw::Text(Vector2(nonPositionalSoundXCoord, nonPositionalSoundYCoord), Color32(255, 255, 255), "Non Positional Sounds:", true, 1.5f, 1.5f);
		grcDebugDraw::Text(Vector2(positionalSoundXCoord, positionalSoundYCoord), Color32(255, 255, 255), "Positional Sounds:", true, 1.5f, 1.5f);
		positionalSoundYCoord += 0.03f;
		nonPositionalSoundYCoord += 0.03f;
	}

	for(u32 bucketId = 0; bucketId < m_NumBuckets; bucketId++)
	{
		audSound::GetStaticPool().LockBucket(bucketId);
		atFixedBitSet<g_audMaxSoundSlotsPerBucket> allocationState;
		ComputeSoundSlotAllocationState(bucketId, allocationState);
		for(u32 i = 0 ; i < m_NumSoundSlotsPerBucketAligned>>5; i++)
		{
			const audSoundPoolBucket& rBucket = m_Buckets[bucketId];

			for(u32 j = 0; j < 32; j++)
			{
				const u32 slotId = (i<<5) + j;
				// check this slot is allocated and its of type 0 (sound)
				if(slotId < g_audMaxSoundSlotsPerBucket && allocationState.IsSet(slotId))
				{
					if(rBucket.soundSlotAllocationType.IsClear(slotId))
					{
						audSound *sound = (audSound*)((u8*)m_Buckets[bucketId].baseSoundPtr + slotId * m_SoundSlotSize);

						if(sound)
						{														
							const char* soundName = sound->GetName();
							audSound* parentSound = sound->GetTopLevelParent();
							audEntity* parentEntity = parentSound? parentSound->GetEntity() : NULL;

							if(!parentSoundsOnly || parentSound == NULL || sound == parentSound)
							{		                                
								if(MatchRAGFilter(soundName, parentEntity? parentEntity->GetEntityName() : NULL, nameFilter, ignoreNameFilter, tagFilter, ignoreTagFilter))
								{
									if(drawPositionInfo && parentSound && parentSound->IsReferencedByGame())
									{
										Vec3V soundPosition = parentSound->GetRequestedPosition() + sound->GetDebugPositionOffset();

										if(!IsZeroAll(soundPosition))
										{											
											Color32 sphereColor = Color32(0xE0000000 | atStringHash(soundName));
											sphereColor.SetAlpha(128);									

											if(!IsZeroAll(currentPlayerPosition))
											{
												grcDebugDraw::Line(soundPosition, currentPlayerPosition, Color32(255,255,255), Color32(255,255,255));
											}

											grcDebugDraw::Sphere(soundPosition, 0.5f, sphereColor, true, 1, 16);
											ScalarV rotationAngle = ScalarV(((j/32.f) * 360) * DtoR);
											Vec3V offset = Vec3V(Sin(rotationAngle), Cos(rotationAngle), ScalarV(V_ZERO)) * ScalarV(yPositionSpread);

											if(drawEntityInfo)
											{
												char debugInfo[256];
												formatf(debugInfo, "%s\n%s\nPlaytime: %u", soundName, parentEntity? parentEntity->GetEntityName() : "Unknown Entity", sound->GetCurrentPlayTime(g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0)));
												grcDebugDraw::Text(soundPosition + offset, Color32(255,255,255), debugInfo);
											}
											else if (sound->GetSoundTypeID() == EnvironmentSound::TYPE_ID && ((audEnvironmentSound*)sound)->GetPcmSourceChannelId() != -1)
											{
												char debugInfo[256];
												formatf(debugInfo, "%s (%d)", soundName, ((audEnvironmentSound*)sound)->GetPcmSourceChannelId());
												grcDebugDraw::Text(soundPosition + offset, Color32(255, 255, 255), debugInfo);
											}
											else
											{
												grcDebugDraw::Text(soundPosition + offset, Color32(255,255,255), soundName);
											}

											if(!IsZeroAll(offset))
											{
												grcDebugDraw::Line(soundPosition, soundPosition + offset, Color32(255,0,0), Color32(255,0,0));
											}

											grcDebugDraw::Text(Vector2(positionalSoundXCoord, positionalSoundYCoord), Color32(255, 255, 255), soundName, true, 1.5f, 1.5f);	
											positionalSoundYCoord += 0.015f;
										}
										else
										{											
											grcDebugDraw::Text(Vector2(nonPositionalSoundXCoord, nonPositionalSoundYCoord), Color32(255, 255, 255), soundName, true, 1.5f, 1.5f);	
											nonPositionalSoundYCoord += 0.015f;
										}
									}												

									if(drawSoundHierarchy)
									{
										sound->DebugDraw(drawMgr, true, false);
										drawMgr.SkipLine();
									}

									numMatchingSounds++;
								}
							}							

							if(drawSoundPCMCosts)
							{
								if(sound->GetSoundTypeID() == EnvironmentSound::TYPE_ID)
								{
									u32 sourceCost = ((audEnvironmentSound*)sound)->GetPCMSourceCost();								
									audSound* parentSound = sound->GetTopLevelParent();

									if(parentSound)
									{										
										PlayingSoundInfo* existingInfo = sourceCostMap.Access(parentSound);

										if(existingInfo)
										{
											existingInfo->pcmCost += sourceCost;
										}
										else
										{
											PlayingSoundInfo soundInfo;
											soundInfo.soundName = parentSound->GetName();
											soundInfo.entityName = parentEntity? parentEntity->GetEntityName() : NULL; 
											soundInfo.pcmCost = sourceCost;
											sourceCostMap.Insert(parentSound, soundInfo);
										}
									}

									totalPCMSourceCost += sourceCost;
								}
							}							
						}					
					}
				}
			}
		}

		audSound::GetStaticPool().UnlockBucket(bucketId);
	}

	formatf(tempBuffer, "Num Sounds Found: %d", numMatchingSounds);
	grcDebugDraw::Text(Vector2(0.42f, 0.02f), Color32(255, 255, 255), tempBuffer, true, 1.5f, 1.5f);	

	if(drawSoundPCMCosts)
	{
		formatf(tempBuffer, "Total PCM Source Cost: %d", totalPCMSourceCost);
		grcDebugDraw::Text(Vector2(0.02f, yCoord), Color32(255, 255, 255), tempBuffer, true, 1.5f, 1.5f);	
		yCoord += 0.02f;

		atMap<audSound*,PlayingSoundInfo>::Iterator iter = sourceCostMap.CreateIterator();
		atArray<PlayingSoundInfo*> soundInfoList;
		u32 pcmCostForMatchedSounds = 0;

		while(iter)
		{
			PlayingSoundInfo* soundInfo = iter.GetDataPtr();

			if(MatchRAGFilter(soundInfo->soundName, soundInfo->entityName, nameFilter, ignoreNameFilter, tagFilter, ignoreTagFilter))
			{            
				pcmCostForMatchedSounds += soundInfo->pcmCost;
				soundInfoList.PushAndGrow(soundInfo);
			}

			++iter;
		}

		formatf(tempBuffer, "PCM Source Cost for matched sounds: %d", pcmCostForMatchedSounds);
		grcDebugDraw::Text(Vector2(0.02f, yCoord), Color32(255, 255, 255), tempBuffer, true, 1.5f, 1.5f);	
		yCoord += 0.03f;

		soundInfoList.QSort(0, -1, SoundInfoCompareFunc);

		for(s32 loop = 0; loop < soundInfoList.GetCount(); loop++)
		{
			PlayingSoundInfo* soundInfo = soundInfoList[loop];		
			formatf(tempBuffer, "%s: %d", soundInfo->soundName, soundInfo->pcmCost);
			grcDebugDraw::Text(Vector2(0.02f, yCoord), Color32(255, 255, 255), tempBuffer, true, 1.5f, 1.5f);	
			yCoord += 0.015f;
		}
	}	
}


void audSoundPool::DebugPrintSoundPoolOldSounds() const
{
	SYS_CS_SYNC(audSoundPool::sm_DebugPrintToken);

	audSoundPoolStats stats;
	GetPoolUtilisation(stats);
	Displayf("Sound Pool Utilisation: fullestBucket: %u, emptiestBucket: %u, mostSlotsFree: %u, totalSlotsAllocated: %u, totalSlotsFree: %u\n", stats.fullestBucket, stats.emptiestBucket, stats.mostSoundSlotsFree, stats.soundSlotsAllocated, stats.soundSlotsFree);

	u32 now = g_AudioEngine.GetTimeInMilliseconds();
	for(u32 bucketId = 0; bucketId < m_NumBuckets; bucketId++)
	{
		audSound::GetStaticPool().LockBucket(bucketId);
		atFixedBitSet<g_audMaxSoundSlotsPerBucket> allocationState;
		ComputeSoundSlotAllocationState(bucketId, allocationState);

		for(u32 i = 0 ; i < m_NumSoundSlotsPerBucketAligned >> 5; i++)
		{
			const audSoundPoolBucket& rBucket = m_Buckets[bucketId];
			
			for(u32 j = 0; j < 32; j++)
			{
				const u32 slotId = (i<<5)+j;
				// check this slot is allocated and its of type 0 (sound)
				if(slotId < m_NumSoundSlotsPerBucket && allocationState.IsSet(slotId)  && rBucket.soundSlotAllocationType.IsClear(slotId))
				{
					audSound *sound = (audSound*)((char*)m_Buckets[bucketId].baseSoundPtr + slotId * m_SoundSlotSize);
					if(sound->IsInitialised() && !sound->_GetParent() && now - sound->GetTimeInitialised() > 25000)
					{
						const char *soundState="invalid";
						switch(sound->GetPlayState())
						{
						case AUD_SOUND_DORMANT:
							soundState = "dormant";
							break;
						case AUD_SOUND_PREPARING:
							soundState = "preparing";
							break;
						case AUD_SOUND_PLAYING:
							soundState = "playing";
							break;
						case AUD_SOUND_WAITING_TO_BE_DELETED:
							soundState = "waiting to be deleted";
							break;
						}
						char printBuf[128];
						formatf(printBuf, sizeof(printBuf), "[timeSinceInit: %u, playtime: %u]", now - sound->GetTimeInitialised(), sound->GetCurrentPlayTime(now));

						audDisplayf("%u,%u,%s,%u,%s,%s,%s", bucketId, slotId, sound->GetName(), sound->GetSoundTypeID(), soundState,(sound->GetRequestedSettings()&&sound->GetEntity()?sound->GetEntity()->GetName():"no entity"),printBuf);

					}
				}
			}
		}
		audSound::GetStaticPool().UnlockBucket(bucketId);
	}
}

void audSoundPool::DebugPrintSoundsUsingEnvironmentGroup(const audEnvironmentGroupInterface* group, const u32 slotIndex) const
{
	u32 now = g_AudioEngine.GetTimeInMilliseconds();
	for(u32 bucketId = 0; bucketId < m_NumBuckets; bucketId++)
	{
		audSound::GetStaticPool().LockBucket(bucketId);
		atFixedBitSet<g_audMaxSoundSlotsPerBucket> allocationState;
		ComputeSoundSlotAllocationState(bucketId, allocationState);
		for(u32 i = 0 ; i < m_NumSoundSlotsPerBucketAligned>>5; i++)
		{
			const audSoundPoolBucket& rBucket = m_Buckets[bucketId];

			for(u32 j = 0; j < 32; j++)
			{
				const u32 slotId = (i<<5) + j;
				// check this slot is allocated and its of type 0 (sound)
				if(slotId < m_NumSoundSlotsPerBucket && allocationState.IsSet(slotId))
				{
					if(rBucket.soundSlotAllocationType.IsClear(slotId))
					{
						audSound *sound = (audSound*)((u8*)m_Buckets[bucketId].baseSoundPtr + slotId * m_SoundSlotSize);

						if(sound->GetRequestedSettings())
						{
							if(sound->GetRequestedSettings()->GetEnvironmentGroup() == group && sound->_GetParent() == NULL)
							{
								if(sound->IsInitialised())
								{
									const char *soundState="invalid";
									switch(sound->GetPlayState())
									{
									case AUD_SOUND_DORMANT:
										soundState = "dormant";
										break;
									case AUD_SOUND_PREPARING:
										soundState = "preparing";
										break;
									case AUD_SOUND_PLAYING:
										soundState = "playing";
										break;
									case AUD_SOUND_WAITING_TO_BE_DELETED:
										soundState = "waiting to be deleted";
										break;
									}

									char printBuf[128];
									formatf(printBuf, sizeof(printBuf), "%u,%u", now - sound->GetTimeInitialised(), sound->GetCurrentPlayTime(now));					

									char nameBuf[64];

									if(sound->GetSoundTypeID() == EnvironmentSound::TYPE_ID)
									{
										formatf(nameBuf, "%s [EnvironmentSound]", sound->_GetParent() ? sound->_GetParent()->GetName() : "NULL Parent");
									}
									else
									{
										formatf(nameBuf, "%s", sound->GetName());
									}

									audDisplayf(",%u,,,,,,%s,%s,%s,%s", slotIndex, nameBuf, g_AudioEngine.GetSoundManager().GetFactory().GetTypeName(sound->GetSoundTypeID()), 
										soundState, printBuf);

								}
								else
								{
									audDisplayf(",%u,,,,,,[Uninitialised],,,,", slotIndex);
								}
							}
						}
					}
				}
			}
		}
		audSound::GetStaticPool().UnlockBucket(bucketId);
	}
}

#endif // __BANK

#endif //!__SPU

} // namespace rage
