//
// audioengine/soundfactory.cpp
//
// Copyright (C) 1999-2006 Rockstar Games.  All Rights Reserved.
//
#include "engine.h"
#include "remotecontrol.h"
#include "soundfactory.h"
#include "metadatamanager.h"

#include "audiosoundtypes/sound.h"
#include "audiosoundtypes/soundclassfactory.h"

#include "diag/output.h"
#include "profile/element.h"
#include "profile/group.h"
#include "profile/page.h"
#include "system/memory.h"
#include "system/param.h"
#include "system/timemgr.h"

namespace rage {

#if !__SPU

PF_PAGE(SoundFactoryTimingPage,"SoundFactory Timing");
PF_GROUP(SoundFactoryTiming);
PF_LINK(SoundFactoryTimingPage, SoundFactoryTiming);
PF_TIMER(BucketLockTime, SoundFactoryTiming);

PARAM(printlargesounds, "[RAGE Audio] warn when playing large sound hierarchies");
PARAM(fakesoundschema, "[RAGE Audio] fake the sound schema version");
#if __BANK
PARAM(loadtestsounds, "Load test_sounds.dat");

u32 audSoundFactory::sm_NumParentSoundsCreated = 0;
u32 audSoundFactory::sm_NumSoundsCreated = 0;
atRangeArray<audSoundFactory::BucketSoundCount, 32> audSoundFactory::sm_BucketSoundCount;
#endif


audSoundFactory::audSoundFactory()
{
#if __BANK
	m_HadAuditionStop = m_HadAuditionStart = false;
	m_AuditionStopSoundHash = m_AuditionStartSoundHash = 0;
#endif
}

audSoundFactory::~audSoundFactory()
{
}

audSound *audSoundFactory::GetInstance(const u32 soundNameHash, const audSoundInternalInitParams* initParams, audSoundScratchInitParams *scratchInitParams, audSoundParentInitParams *parentInitParams)
{
	void *pMetadata = m_MetadataMgr.GetObjectMetadataPtr(soundNameHash);
	return GetInstanceFromMetadata(pMetadata, NULL, initParams, scratchInitParams, parentInitParams, true);
}

audSound *audSoundFactory::GetInstance(const audMetadataRef soundRef, const audSoundInternalInitParams* initParams, audSoundScratchInitParams *scratchInitParams, audSoundParentInitParams *parentInitParams)
{
	void *pMetadata = m_MetadataMgr.GetObjectMetadataPtr(soundRef);	
	return GetInstanceFromMetadata(pMetadata, NULL, initParams, scratchInitParams, parentInitParams, true);
}

audSound *audSoundFactory::GetChildInstance(const audMetadataRef metadataOffset, audSound *parent, const audSoundInternalInitParams* initParams, audSoundScratchInitParams *scratchInitParams, bool lockBucket)
{
	void *pMetadata = m_MetadataMgr.GetObjectMetadataPtr(metadataOffset);
	return GetInstanceFromMetadata(pMetadata, parent, initParams, scratchInitParams, NULL, lockBucket);
}

audSound *audSoundFactory::GetChildInstance(u32 soundNameHash, audSound *parent, const audSoundInternalInitParams* initParams, audSoundScratchInitParams *scratchInitParams, bool lockBucket)
{
	void *pMetadata = m_MetadataMgr.GetObjectMetadataPtr(soundNameHash);
	return GetInstanceFromMetadata(pMetadata, parent, initParams, scratchInitParams, NULL, lockBucket);
}

audSound *audSoundFactory::GetInstanceFromMetadata(const void *metadata, audSound *parent, const audSoundInternalInitParams* initParams, audSoundScratchInitParams *scratchInitParams, audSoundParentInitParams *parentInitParams, bool lockBucket)
{
	audSound *pSound = NULL;
	if(!metadata)
		return pSound;

	u32 classID = *((u8*)metadata);

	// no longer need to lock this bucket for the entire Init process if we're locking on SPU
#if !AUD_SOUNDPOOL_LOCK_ON_SPU || 1
	if (lockBucket)
	{
		PF_FUNC(BucketLockTime);
		audSound::GetStaticPool().LockBucket(initParams->BucketId);
	}
#endif

	pSound = gSoundClassFactory(initParams->BucketId, classID);

	if(pSound)
	{
		BANK_ONLY(sm_NumSoundsCreated++);

#if AUD_DEBUG_SOUNDS
		pSound->SetIsInitialising(true);
#endif // AUD_DEBUG_SOUNDS

		audAssertf((parent?parent->GetMetadata() != (Sound*)metadata:true), "Attempted to instantiate a self-referencing sound: %s", "(unknown)");
				
		if(parent)
		{
			// need to setparent before Init()
			pSound->SetParent(initParams->BucketId, parent);

			BANK_ONLY(sm_BucketSoundCount[initParams->BucketId].NumSoundsCreatedInHierarchy++);
			BANK_ONLY(sm_BucketSoundCount[initParams->BucketId].PeakSoundSlotsForHierarchy = Max(sm_BucketSoundCount[initParams->BucketId].PeakSoundSlotsForHierarchy, sm_BucketSoundCount[initParams->BucketId].NumSoundSlotsFreeForHierarchy - Min<u32>(sm_BucketSoundCount[initParams->BucketId].NumSoundSlotsFreeForHierarchy,audSound::GetStaticPool().GetBucket(initParams->BucketId)->numSoundSlotsFree)));
		}
		else
		{
			Assert(parentInitParams);
			pSound->InitAsParent(parentInitParams);
			BANK_ONLY(sm_NumParentSoundsCreated++);
			BANK_ONLY(sm_BucketSoundCount[initParams->BucketId].NumSoundsCreatedInHierarchy = 1);
			BANK_ONLY(sm_BucketSoundCount[initParams->BucketId].NumSoundSlotsFreeForHierarchy = audSound::GetStaticPool().GetBucket(initParams->BucketId)->numSoundSlotsFree);
			BANK_ONLY(sm_BucketSoundCount[initParams->BucketId].PeakSoundSlotsForHierarchy = 1);
		}

		// If we fail to prepare because the effect route is invalid, *don't* delete the sound. 
		// We want to act as though it initalized/prepared correctly so that the rest of the sound hierarchy is allowed to play out
		if(!pSound->Init(metadata, initParams, scratchInitParams) && pSound->IsEffectRouteValid())
		{
			delete pSound;
			pSound = NULL;
		}
		else
		{
#if AUD_DEBUG_SOUNDS
			pSound->SetIsInitialising(false);
#endif
			pSound->SetIsInitialised();
			if(!parent)
			{
				pSound->PostInitAsParent(parentInitParams);
				audSound::GetStaticPool().MarkSoundSlotAsParent(initParams->BucketId, audSound::GetStaticPool().GetSoundSlotIndex(initParams->BucketId, pSound));			

#if __USEDEBUGAUDIO								
				const u32 slotsUsed = sm_BucketSoundCount[initParams->BucketId].PeakSoundSlotsForHierarchy;
				if(initParams->IsAuditioning || (PARAM_printlargesounds.Get() && (sm_BucketSoundCount[initParams->BucketId].NumSoundsCreatedInHierarchy > 40 || slotsUsed > 40)))
				{
					const Sound *soundData = reinterpret_cast<const Sound*>(metadata);
					const char *soundName = m_MetadataMgr.GetObjectNameFromNameTableOffset(soundData->NameTableOffset);
					audWarningf("Sound hierarchy %s created %u sounds (%u sound slots)", soundName, sm_BucketSoundCount[initParams->BucketId].NumSoundsCreatedInHierarchy, slotsUsed);
				}				
#endif // __BANK
			}
		}
	}
	else
	{
#if __USEDEBUGAUDIO
		if(m_MetadataMgr.IsRAVEConnected())
		{
			if(audSound::GetStaticPool().GetBucket(initParams->BucketId)->numSoundSlotsFree == 0)
			{
				const Sound *soundData = reinterpret_cast<const Sound*>(metadata);
				const char *soundName = m_MetadataMgr.GetObjectNameFromNameTableOffset(soundData->NameTableOffset);
				audWarningf("Failed to create sound %s (type id %u) - no space in the bucket", soundName, classID);
			}
		}
#endif
	}
#if !AUD_SOUNDPOOL_LOCK_ON_SPU || 1
	if (lockBucket)
		audSound::GetStaticPool().UnlockBucket(initParams->BucketId);
#endif
	return pSound;
}

audEnvironmentSound *audSoundFactory::GetEnvironmentSound(audSound *parent, const audSoundInternalInitParams *initParams, audSoundScratchInitParams *scratchInitParams) const
{
	audEnvironmentSound *sound = (audEnvironmentSound*)audSound::GetStaticPool().AllocateSoundSlot(sizeof(audEnvironmentSound), initParams->BucketId);
	if(sound)
	{
		::new(sound) audEnvironmentSound();
		sound->SetParent(initParams->BucketId, parent);
#if AUD_DEBUG_SOUNDS
		sound->SetIsInitialising(true);
#endif
		if(!sound->Init(NULL, initParams, scratchInitParams))
		{			
			delete sound;
			return NULL;
		}
#if AUD_DEBUG_SOUNDS
		sound->SetIsInitialising(false);
#endif
		sound->SetIsInitialised();
	}
	return sound;
}

const Sound *audSoundFactory::GetMetadataPtr(const u32 nameHash) const
{
	return GetMetadataPtr(GetMetadataManager().GetObjectMetadataRefFromHash(nameHash));
}

const Sound *audSoundFactory::GetMetadataPtr(const audMetadataRef metadataRef) const
{
	Sound *ret = (Sound*)GetMetadataManager().GetObjectMetadataPtr(metadataRef);
	if(ret)
	{
		if(audVerifyf(ret->ClassId != Sound::TYPE_ID && 
			gSoundsIsOfType(ret->ClassId, Sound::TYPE_ID), "Trying to request invalid sound metadata, got type %u (name %s)", ret->ClassId, GetMetadataManager().GetObjectName(metadataRef)))
		{
			return ret;
		}
	}
	return NULL;
}

void audSoundFactory::ProcessHierarchy(const u32 soundNameHash, audSoundProcessHierarchyFn& fn) const
{
	audMetadataRef metadataRef = GetMetadataManager().GetObjectMetadataRefFromHash(soundNameHash);
	if(metadataRef.IsValid())
	{
		ProcessHierarchy(metadataRef, fn);
	}
}

void audSoundFactory::ProcessHierarchy(audMetadataRef metadataRef, audSoundProcessHierarchyFn& fn) const
{
	const Sound* soundMetadataCompressed = GetMetadataPtr(metadataRef);
		
	if(soundMetadataCompressed)
	{
		Sound uncompressedMetadata;
		const void* actualSoundMetadata = audSound::DecompressMetadata_Untyped(soundMetadataCompressed, uncompressedMetadata);

		audAssertf(soundMetadataCompressed->ClassId <= AUD_NUM_SOUNDDEFS, "Unrecognised sound type (ID %d) encountered when parsing sound hierarchy", soundMetadataCompressed->ClassId);
		fn(soundMetadataCompressed->ClassId, actualSoundMetadata);

		if(actualSoundMetadata)
		{
			switch(soundMetadataCompressed->ClassId)
			{
			case SimpleSound::TYPE_ID:
				{
					break;
				}

			case LoopingSound::TYPE_ID:
				{
					const LoopingSound * sound = reinterpret_cast<const LoopingSound*>(actualSoundMetadata);
					ProcessHierarchy(sound->SoundRef, fn);
				}
				break;

			case EnvelopeSound::TYPE_ID:
				{
					const EnvelopeSound * sound = reinterpret_cast<const EnvelopeSound*>(actualSoundMetadata);
					ProcessHierarchy(sound->SoundRef, fn);
				}
				break;

			case TwinLoopSound::TYPE_ID:
				{
					const TwinLoopSound * sound = reinterpret_cast<const TwinLoopSound*>(actualSoundMetadata);

					for(u32 loop = 0; loop < sound->numSoundRefs; loop++)
					{
						ProcessHierarchy(sound->SoundRef[loop].SoundId, fn);
					}
				}
				break;

			case SpeechSound::TYPE_ID:
				break;

			case OnStopSound::TYPE_ID:
				{
					const OnStopSound * sound = reinterpret_cast<const OnStopSound*>(actualSoundMetadata);
					ProcessHierarchy(sound->ChildSoundRef, fn);
					ProcessHierarchy(sound->FinishedSoundRef, fn);
					ProcessHierarchy(sound->StopSoundRef, fn);
				}
				break;

			case WrapperSound::TYPE_ID:
				{
					const WrapperSound * sound = reinterpret_cast<const WrapperSound*>(actualSoundMetadata);
					ProcessHierarchy(sound->SoundRef, fn);
				}
				break;

			case SequentialSound::TYPE_ID:
				{
					const SequentialSound * sound = reinterpret_cast<const SequentialSound*>(actualSoundMetadata);

					for(u32 loop = 0; loop < sound->numSoundRefs; loop++)
					{
						ProcessHierarchy(sound->SoundRef[loop].SoundId, fn);
					}
				}
				break;

			case StreamingSound::TYPE_ID:
				{
					const StreamingSound * sound = reinterpret_cast<const StreamingSound*>(actualSoundMetadata);

					for(u32 loop = 0; loop < sound->numSoundRefs; loop++)
					{
						ProcessHierarchy(sound->SoundRef[loop].SoundId, fn);
					}
				}
				break;

			case RetriggeredOverlappedSound::TYPE_ID:
				{
					const RetriggeredOverlappedSound * sound = reinterpret_cast<const RetriggeredOverlappedSound*>(actualSoundMetadata);
					ProcessHierarchy(sound->SoundRef, fn);
					ProcessHierarchy(sound->StartSound, fn);
					ProcessHierarchy(sound->StopSound, fn);
				}
				break;

			case CrossfadeSound::TYPE_ID:
				{
					const CrossfadeSound * sound = reinterpret_cast<const CrossfadeSound*>(actualSoundMetadata);
					ProcessHierarchy(sound->FarSoundRef, fn);
					ProcessHierarchy(sound->NearSoundRef, fn);
				}
				break;

			case CollapsingStereoSound::TYPE_ID:
				{
					const CollapsingStereoSound * sound = reinterpret_cast<const CollapsingStereoSound*>(actualSoundMetadata);
					ProcessHierarchy(sound->LeftSoundRef, fn);
					ProcessHierarchy(sound->RightSoundRef, fn);
				}
				break;

			case MultitrackSound::TYPE_ID:
				{
					const MultitrackSound * sound = reinterpret_cast<const MultitrackSound*>(actualSoundMetadata);

					for(u32 loop = 0; loop < sound->numSoundRefs; loop++)
					{
						ProcessHierarchy(sound->SoundRef[loop].SoundId, fn);
					}
				}
				break;

			case RandomizedSound::TYPE_ID: 
				{
					const RandomizedSound * sound = reinterpret_cast<const RandomizedSound*>(actualSoundMetadata);

					// variations are after variable length history array
					u8 *p = (u8*)&sound->HistorySpace[sound->numHistorySpaceElems];
					u8 size = *p;
					p++;
					RandomizedSound::tVariations* variations = (RandomizedSound::tVariations*)p;

					for(u32 loop = 0; loop < size; loop++)
					{
						ProcessHierarchy(variations[loop].Variation, fn);
					}
				}
				break;

			case EnvironmentSound::TYPE_ID:
				break;

			case DynamicEntitySound::TYPE_ID:
				break;

			case SequentialOverlapSound::TYPE_ID:
				{
					const SequentialOverlapSound * sound = reinterpret_cast<const SequentialOverlapSound*>(actualSoundMetadata);

					for(u32 loop = 0; loop < sound->numChildSounds; loop++)
					{
						ProcessHierarchy(sound->ChildSound[loop].SoundRef, fn);
					}
				}
				break;

			case ModularSynthSound::TYPE_ID:
				{
					const ModularSynthSound * sound = reinterpret_cast<const ModularSynthSound*>(actualSoundMetadata);

					for(u32 loop = 0; loop < sound->numEnvironmentSounds; loop++)
					{
						ProcessHierarchy(sound->EnvironmentSound[loop].SoundRef, fn);
					}
				}
				break;

			case GranularSound::TYPE_ID:
				{
					break;
				}
		
			case DirectionalSound::TYPE_ID:
				{
					const DirectionalSound *sound = reinterpret_cast<const DirectionalSound*>(actualSoundMetadata);
					ProcessHierarchy(sound->SoundRef, fn);
					break;
				}

			case SwitchSound::TYPE_ID:
				{
					const SwitchSound *sound = reinterpret_cast<const SwitchSound*>(actualSoundMetadata);

					for(u32 loop = 0; loop < sound->numSoundRefs; loop++)
					{
						ProcessHierarchy(sound->SoundRef[loop].SoundId, fn);
					}
					break;
				}

			case VariableCurveSound::TYPE_ID:
				{
					const VariableCurveSound *sound = reinterpret_cast<const VariableCurveSound*>(actualSoundMetadata);
					ProcessHierarchy(sound->SoundRef, fn);
					break;
				}

			case VariablePrintValueSound::TYPE_ID:
				break;

			case VariableBlockSound::TYPE_ID:
				{
					const VariableBlockSound *sound = reinterpret_cast<const VariableBlockSound*>(actualSoundMetadata);
					ProcessHierarchy(sound->SoundRef, fn);
				}
				break;

			case IfSound::TYPE_ID:
				{
					const IfSound *sound = reinterpret_cast<const IfSound*>(actualSoundMetadata);
					ProcessHierarchy(sound->TrueSoundRef, fn);
					ProcessHierarchy(sound->FalseSoundRef, fn);
				}
				break;

			case MathOperationSound::TYPE_ID:
				{
					const MathOperationSound *sound = reinterpret_cast<const MathOperationSound*>(actualSoundMetadata);
					ProcessHierarchy(sound->SoundRef, fn);
				}
				break;

			case FluctuatorSound::TYPE_ID:
				{
					const FluctuatorSound * sound = reinterpret_cast<const FluctuatorSound*>(actualSoundMetadata);
					ProcessHierarchy(sound->SoundRef, fn);
				}
				break;

			case ParameterTransformSound::TYPE_ID:
				{
					const ParameterTransformSound * sound = reinterpret_cast<const ParameterTransformSound*>(actualSoundMetadata);
					ProcessHierarchy(sound->SoundRef, fn);
				}
				break;		

			case AutomationSound::TYPE_ID:
				{
					const AutomationSound * sound = reinterpret_cast<const AutomationSound*>(actualSoundMetadata);
					ProcessHierarchy(sound->SoundRef, fn);
				}
				break;

			default:
				{
					// If we hit this assert then a new sound has been created - just need to add an appropriate case label above to support it
					audAssertf(false, "Unhandled sound type (ID %d) encountered when parsing sound hierarchy", soundMetadataCompressed->ClassId);
				}
				break;
			}
		}
	}
}

bool audSoundFactory::Init()
{
	RAGE_TRACK(audSoundFactory_Init);
	const char *metadataFileName = g_AudioEngine.GetConfig().GetSoundMetadata();

	audDisplayf("sizeof(audSound): %" SIZETFMT "u", sizeof(audSound));
	audDisplayf("largest sound size: %u", gSoundClassFactoryMaxSize());

	s32 schemaVersion = SOUNDDEFS_SCHEMA_VERSION;

#if __BANK
	if(PARAM_fakesoundschema.Get(schemaVersion))
	{
		audWarningf("Faking sound schema version: using %d rather than %d", schemaVersion, SOUNDDEFS_SCHEMA_VERSION);
	}
#endif

	bool ret;

#if __BANK && ENABLE_DEBUG_HEAP
	// When using RAVE for sound data we turn off compression, which costs around 1.8MB with the current set.
	// This is enough to cause problems for the game, so allocate sound data from DEBUG memory in this case.	
	if(g_sysHasDebugHeap && g_AudioEngine.GetRemoteControl().IsPresent() &&
			g_AudioEngine.GetRemoteControl().ShouldUseRAVEForMetadataType(ATSTRINGHASH("SOUNDS", 0x94FA081B)))
	{
		audDisplayf("Allocating uncompressed sound data from debug memory");
		RAGE_TRACK(audSoundFactory_Debug);
		USE_DEBUG_MEMORY();
		ret = m_MetadataMgr.Init("Sounds", metadataFileName, audMetadataManager::External_NameTable_BankOnly, schemaVersion, schemaVersion >= 51);
	}
	else	
#endif
	{			
#if __BANK && ENABLE_DEBUG_HEAP
		audDisplayf("Using regular memory for sound data: %d, %d", g_sysHasDebugHeap, g_AudioEngine.GetRemoteControl().ShouldUseRAVEForMetadataType(ATSTRINGHASH("SOUNDS", 0x94FA081B)));
#endif

		ret = m_MetadataMgr.Init("Sounds", metadataFileName, audMetadataManager::External_NameTable_BankOnly, schemaVersion, schemaVersion >= 51);
	}

	m_MetadataMgr.SetObjectModifiedCallback(this);

#if __BANK
	if(PARAM_loadtestsounds.Get())
	{
		LoadMetadataChunk("TEST", "audio:/config/test_sounds.dat");
	}
#endif

	return ret;
}

void audSoundFactory::Shutdown()
{
	m_MetadataMgr.Shutdown();
}

const char *audSoundFactory::GetBankNameFromIndex(const u32 bankNameIndex) const
{
	const u32* remappedBank = m_BankRemappings.Access(bankNameIndex);
	return m_MetadataMgr.GetStringFromTableIndex(remappedBank ? *remappedBank : bankNameIndex);
}

u32 audSoundFactory::GetBankIndexFromName(const char *bankName) const
{
	const u32 bankId = m_MetadataMgr.GetStringIdFromName(bankName);
	const u32* remappedBank = m_BankRemappings.Access(bankId);
	return remappedBank ? *remappedBank : bankId;
}

u32 audSoundFactory::GetBankIndexFromName(const u32 bankNameHash) const
{
	const u32 bankId = m_MetadataMgr.GetStringIdFromHash(bankNameHash);
	const u32* remappedBank = m_BankRemappings.Access(bankId);
	return remappedBank ? *remappedBank : bankId;
}

u32 audSoundFactory::GetBankIndexFromMetadataRef(const u32 ref) const
{
	const u32 bankId = m_MetadataMgr.GetStringTableIndexFromMetadataRef(ref);
	const u32* remappedBank = m_BankRemappings.Access(bankId);
	return remappedBank ? *remappedBank : bankId;
}

void audSoundFactory::AddBankRemapping(u32 originalBankNameHash, u32 newBankNameHash)
{
	// Go directly via the metadatamanger so we don't use the remapping table!
	u32 originalBankId = m_MetadataMgr.GetStringIdFromHash(originalBankNameHash);
	u32 newBankId = m_MetadataMgr.GetStringIdFromHash(newBankNameHash);

	if(audVerifyf(originalBankId != AUD_INVALID_BANK_ID && newBankId != AUD_INVALID_BANK_ID, "Invalid original bank ID used in bank remapping (%d -> %d)", originalBankId, newBankId))
	{
		u32* existingRemapping = m_BankRemappings.Access(originalBankId);

		if(!existingRemapping)
		{	
			audDisplayf("Adding bank mapping from %s (%d) to %s (%d)", m_MetadataMgr.GetStringFromTableIndex(originalBankId), originalBankId, m_MetadataMgr.GetStringFromTableIndex(newBankId), newBankId);
			m_BankRemappings.Insert(originalBankId, newBankId);
		}
		else if(*existingRemapping != newBankId)
		{
			audDisplayf("Modifying bank mapping from %s (%d) to %s (%d) (was %s (%d)", m_MetadataMgr.GetStringFromTableIndex(originalBankId), originalBankId, m_MetadataMgr.GetStringFromTableIndex(newBankId), newBankId, m_MetadataMgr.GetStringFromTableIndex(*existingRemapping), *existingRemapping);
			*existingRemapping = newBankId;
		}
	}	
}

void audSoundFactory::RemoveBankRemapping(u32 originalBankNameHash)
{ 	
	// Go directly via the metadatamanger so we don't use the remapping table!
	u32 originalBankId = m_MetadataMgr.GetStringIdFromHash(originalBankNameHash);

	if(m_BankRemappings.Delete(originalBankId))
	{
		audDisplayf("Removing bank remapping for %s (%d)", m_MetadataMgr.GetStringFromTableIndex(originalBankId), originalBankId);
	}
}

u32 audSoundFactory::GetMaxSoundTypeSize()
{
	return Max(gSoundClassFactoryMaxSize(), u32(sizeof(audMathOp)*MathOperationSound::MAX_OPERATIONS));
}

#if __BANK && 0
void audSoundFactory::OnObjectAuditionStart(const u32 soundNameHash)
{
	audMetadataObjectInfo info;
	if(m_MetadataMgr.GetObjectInfo(soundNameHash, info))
	{
		if(gSoundsIsOfType(info.GetType(), Sound::TYPE_ID))
		{
			g_AudioEngine.GetRemoteControl().TriggerSound(soundNameHash);
		}
	}
	else
	{
		audWarningf("RAVE Auditioning: Failed to find sound with hash %u", soundNameHash);
	}
}

void audSoundFactory::OnObjectAuditionStop(const u32 UNUSED_PARAM(soundNameHash))
{
	g_AudioEngine.GetRemoteControl().StopSound();
}
#endif

const char *audSoundFactory::GetTypeName(const u32 typeId) const
{
	return gSoundClassFactoryGetTypeName(typeId);
}
#endif //!__SPU


#if __SPU
#define PPU_ONLY_SOUND_FN(soundType, typeFn) NULL
#else
#define PPU_ONLY_SOUND_FN(soundType, typeFn) &soundType::typeFn
#endif


#if !AUD_SOUND_USE_VIRTUALS

#define SOUND_FUNC_LIST(fnName,typeFn) \
	(Sound ## fnName ## Function)&audLoopingSound::typeFn, \
	(Sound ## fnName ## Function)&audEnvelopeSound::typeFn, \
	(Sound ## fnName ## Function)&audTwinLoopSound::typeFn, \
	(Sound ## fnName ## Function)&audSpeechSound::typeFn, \
	(Sound ## fnName ## Function)&audOnStopSound::typeFn, \
	(Sound ## fnName ## Function)&audWrapperSound::typeFn, \
	(Sound ## fnName ## Function)&audSequentialSound::typeFn, \
	(Sound ## fnName ## Function)PPU_ONLY_SOUND_FN(audStreamingSound, typeFn), \
	(Sound ## fnName ## Function)&audRetriggeredOverlappedSound::typeFn, \
	(Sound ## fnName ## Function)&audCrossfadeSound::typeFn, \
	(Sound ## fnName ## Function)&audCollapsingStereoSound::typeFn, \
	(Sound ## fnName ## Function)&audSimpleSound::typeFn, \
	(Sound ## fnName ## Function)&audMultitrackSound::typeFn, \
	(Sound ## fnName ## Function)&audRandomizedSound::typeFn, \
	(Sound ## fnName ## Function)&audEnvironmentSound::typeFn, \
	(Sound ## fnName ## Function)&audDynamicEntitySound::typeFn, \
	(Sound ## fnName ## Function)&audSequentialOverlapSound::typeFn, \
	(Sound ## fnName ## Function)&audModularSynthSound::typeFn, \
	(Sound ## fnName ## Function)&audGranularSound::typeFn,\
	(Sound ## fnName ## Function)&audDirectionalSound::typeFn, \
	(Sound ## fnName ## Function)&audKineticSound::typeFn, \
	(Sound ## fnName ## Function)&audSwitchSound::typeFn,\
	(Sound ## fnName ## Function)&audVariableCurveSound::typeFn,\
	(Sound ## fnName ## Function)&audVariablePrintValueSound::typeFn,\
	(Sound ## fnName ## Function)&audVariableBlockSound::typeFn,\
	(Sound ## fnName ## Function)&audIfSound::typeFn,\
	(Sound ## fnName ## Function)&audMathOperationSound::typeFn,\
	(Sound ## fnName ## Function)&audParameterTransformSound::typeFn,\
	(Sound ## fnName ## Function)&audFluctuatorSound::typeFn,\
	(Sound ## fnName ## Function)&audAutomationSound::typeFn, \
	(Sound ## fnName ## Function)PPU_ONLY_SOUND_FN(audExternalStreamSound, typeFn)

#if __SPU
#define AUD_SPU_REL __attribute__((__section__(".data.rel.ro")))
#else
#define AUD_SPU_REL
#endif
#define SOUND_FUNC_TABLE(fn, typeFn) Sound ## fn ## Function audSound::sm_SoundType ## fn ## Functions[AUD_NUM_SOUNDDEFS] AUD_SPU_REL = { SOUND_FUNC_LIST(fn,typeFn) }

#if AUD_CPP_MEMBER_INVOKES

SOUND_FUNC_TABLE(Update, AudioUpdate);
SOUND_FUNC_TABLE(Play, AudioPlay);
SOUND_FUNC_TABLE(Prepare, AudioPrepare);
SOUND_FUNC_TABLE(Kill, AudioKill);
SOUND_FUNC_TABLE(ActionReleaseRequest, ActionReleaseRequest);
SOUND_FUNC_TABLE(ComputeDurationMsExcludingStartOffsetAndPredelay, ComputeDurationMsExcludingStartOffsetAndPredelay);
SOUND_FUNC_TABLE(Pause, Pause);
SOUND_FUNC_TABLE(Destruct, sDestruct);

#else

SOUND_FUNC_TABLE(Update, sAudioUpdate);
SOUND_FUNC_TABLE(Play, sAudioPlay);
SOUND_FUNC_TABLE(Prepare, sAudioPrepare);
SOUND_FUNC_TABLE(Kill, sAudioKill);
SOUND_FUNC_TABLE(ActionReleaseRequest, sActionReleaseRequest);
SOUND_FUNC_TABLE(ComputeDurationMsExcludingStartOffsetAndPredelay, sComputeDurationMsExcludingStartOffsetAndPredelay);
SOUND_FUNC_TABLE(Pause, sPause);
SOUND_FUNC_TABLE(Destruct, sDestruct);

#endif

#if __SPU


void InitFunctionPointers()
{
#if 1
	// Calculate the PIC offset that needs to be added to each vtable entry.
	// The ila loads the non-relocated address of the relative branch
	// target, which can the be subtracted from the "return address" stored
	// by the branch.
	register u32 picOffset;
	register qword t0, t1;
	__asm__ __volatile__
	(
		"ila    %1,.+8\n\t"
		"brsl   %2,.+4\n\t"
		"sf     %0,%1,%2"
		:	"=r"(picOffset),
			"=r"(t0),
			"=r"(t1)
	);

	static u32 s_prevRelocation AUD_SPU_REL = 0;
	const s32 adjust = picOffset - s_prevRelocation;
		
	for(s32 i = 0; i < AUD_NUM_SOUNDDEFS; i++)
	{		
		audSound::sm_SoundTypeUpdateFunctions[i] = (SoundUpdateFunction)((size_t)audSound::sm_SoundTypeUpdateFunctions[i]+adjust);
		audSound::sm_SoundTypePlayFunctions[i] = (SoundPlayFunction)((size_t)audSound::sm_SoundTypePlayFunctions[i]+adjust);
		audSound::sm_SoundTypePrepareFunctions[i] = (SoundPrepareFunction)((size_t)audSound::sm_SoundTypePrepareFunctions[i]+adjust);
		audSound::sm_SoundTypeKillFunctions[i] = (SoundKillFunction)((size_t)audSound::sm_SoundTypeKillFunctions[i]+adjust);
		audSound::sm_SoundTypeActionReleaseRequestFunctions[i] = (SoundActionReleaseRequestFunction)((size_t)audSound::sm_SoundTypeActionReleaseRequestFunctions[i]+adjust);
		audSound::sm_SoundTypeComputeDurationMsExcludingStartOffsetAndPredelayFunctions[i] = (SoundComputeDurationMsExcludingStartOffsetAndPredelayFunction)((size_t)audSound::sm_SoundTypeComputeDurationMsExcludingStartOffsetAndPredelayFunctions[i]+adjust);
		audSound::sm_SoundTypePauseFunctions[i] = (SoundPauseFunction)((size_t)audSound::sm_SoundTypePauseFunctions[i]+adjust);
		audSound::sm_SoundTypeDestructFunctions[i] = (SoundDestructFunction)((size_t)audSound::sm_SoundTypeDestructFunctions[i] +adjust);
	}

	//audDisplayf("%u, %u, %u, %zu, %zu", picOffset, s_prevRelocation, adjust, (size_t)&audLoopingSound::sAudioUpdate, (size_t)audSound::sm_SoundTypeUpdateFunctions[0]);

	s_prevRelocation = picOffset;
#else
	s32 i = 0;

#define SETUP_FUNC_TABLE(fn, typeFn) i = 0;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audLoopingSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audEnvelopeSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audTwinLoopSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audSpeechSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audOnStopSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audWrapperSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audSequentialSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)PPU_ONLY_SOUND_FN(audStreamingSound, typeFn);\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audRetriggeredOverlappedSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audCrossfadeSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audCollapsingStereoSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audSimpleSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audMultitrackSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audRandomizedSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audEnvironmentSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audDynamicEntitySound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audSequentialOverlapSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audModularSynthSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audGranularSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audDirectionalSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audKineticSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audSwitchSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audVariableCurveSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audVariablePrintValueSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audVariableBlockSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audIfSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audMathOperationSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audParameterTransformSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audFluctuatorSound::typeFn;\
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)&audAutomationSound::typeFn; \
							audSound::sm_SoundType ## fn ## Functions[i++] = (Sound ## fn ##Function)PPU_ONLY_SOUND_FN(audExternalStreamSound, typeFn);
	
#if AUD_CPP_MEMBER_INVOKES
	// Route directly to member functions
	SETUP_FUNC_TABLE(Update, AudioUpdate);
	SETUP_FUNC_TABLE(Play, AudioPlay);
	SETUP_FUNC_TABLE(Prepare, AudioPrepare);
	SETUP_FUNC_TABLE(Kill, AudioKill);
	SETUP_FUNC_TABLE(ActionReleaseRequest, ActionReleaseRequest);
	SETUP_FUNC_TABLE(ComputeDurationMsExcludingStartOffsetAndPredelay, ComputeDurationMsExcludingStartOffsetAndPredelay);
	SETUP_FUNC_TABLE(Pause, Pause);
	SETUP_FUNC_TABLE(Destruct, sDestruct);
#else
	// Route via static wrappers
	SETUP_FUNC_TABLE(Update, sAudioUpdate);
	SETUP_FUNC_TABLE(Play, sAudioPlay);
	SETUP_FUNC_TABLE(Prepare, sAudioPrepare);
	SETUP_FUNC_TABLE(Kill, sAudioKill);
	SETUP_FUNC_TABLE(ActionReleaseRequest, sActionReleaseRequest);
	SETUP_FUNC_TABLE(ComputeDurationMsExcludingStartOffsetAndPredelay, sComputeDurationMsExcludingStartOffsetAndPredelay);
	SETUP_FUNC_TABLE(Pause, sPause);
	SETUP_FUNC_TABLE(Destruct, sDestruct);
#endif

#endif
}
#endif


#endif
} // namespace rage
