Commit Description:
Add timers for Simulation and various engines...
Commit Description:
Add timers for Simulation and various engines Starting to add additional timers for different stages of the process of updating in order to get more insight into what is slowing it down. The update takes 9ms, which is much longer than it used to. Engine-specific timers are coming later.
File last commit:
Show/Diff file:
Action:
FNA/lib/FAudio/src/FACT.c
2710 lines | 63.2 KiB | text/x-c | CLexer
/* FAudio - XAudio Reimplementation for FNA
*
* Copyright (c) 2011-2020 Ethan Lee, Luigi Auriemma, and the MonoGame Team
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
*
*/
#include "FAudioFX.h"
#include "FACT_internal.h"
/* AudioEngine implementation */
uint32_t FACTCreateEngine(
uint32_t dwCreationFlags,
FACTAudioEngine **ppEngine
) {
return FACTCreateEngineWithCustomAllocatorEXT(
dwCreationFlags,
ppEngine,
FAudio_malloc,
FAudio_free,
FAudio_realloc
);
}
uint32_t FACTCreateEngineWithCustomAllocatorEXT(
uint32_t dwCreationFlags,
FACTAudioEngine **ppEngine,
FAudioMallocFunc customMalloc,
FAudioFreeFunc customFree,
FAudioReallocFunc customRealloc
) {
/* TODO: Anything fun with dwCreationFlags? */
FAudio_PlatformAddRef();
*ppEngine = (FACTAudioEngine*) customMalloc(sizeof(FACTAudioEngine));
if (*ppEngine == NULL)
{
return -1; /* TODO: E_OUTOFMEMORY */
}
FAudio_zero(*ppEngine, sizeof(FACTAudioEngine));
(*ppEngine)->sbLock = FAudio_PlatformCreateMutex();
(*ppEngine)->wbLock = FAudio_PlatformCreateMutex();
(*ppEngine)->apiLock = FAudio_PlatformCreateMutex();
(*ppEngine)->pMalloc = customMalloc;
(*ppEngine)->pFree = customFree;
(*ppEngine)->pRealloc = customRealloc;
(*ppEngine)->refcount = 1;
return 0;
}
uint32_t FACTAudioEngine_AddRef(FACTAudioEngine *pEngine)
{
FAudio_PlatformLockMutex(pEngine->apiLock);
pEngine->refcount += 1;
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return pEngine->refcount;
}
uint32_t FACTAudioEngine_Release(FACTAudioEngine *pEngine)
{
FAudio_PlatformLockMutex(pEngine->apiLock);
pEngine->refcount -= 1;
if (pEngine->refcount > 0)
{
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return pEngine->refcount;
}
FACTAudioEngine_ShutDown(pEngine);
FAudio_PlatformDestroyMutex(pEngine->sbLock);
FAudio_PlatformDestroyMutex(pEngine->wbLock);
FAudio_PlatformUnlockMutex(pEngine->apiLock);
FAudio_PlatformDestroyMutex(pEngine->apiLock);
pEngine->pFree(pEngine);
FAudio_PlatformRelease();
return 0;
}
uint32_t FACTAudioEngine_GetRendererCount(
FACTAudioEngine *pEngine,
uint16_t *pnRendererCount
) {
FAudio_PlatformLockMutex(pEngine->apiLock);
*pnRendererCount = (uint16_t) FAudio_PlatformGetDeviceCount();
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return 0;
}
uint32_t FACTAudioEngine_GetRendererDetails(
FACTAudioEngine *pEngine,
uint16_t nRendererIndex,
FACTRendererDetails *pRendererDetails
) {
FAudioDeviceDetails deviceDetails;
FAudio_PlatformLockMutex(pEngine->apiLock);
FAudio_PlatformGetDeviceDetails(
nRendererIndex,
&deviceDetails
);
FAudio_memcpy(
pRendererDetails->rendererID,
deviceDetails.DeviceID,
sizeof(int16_t) * 0xFF
);
FAudio_memcpy(
pRendererDetails->displayName,
deviceDetails.DisplayName,
sizeof(int16_t) * 0xFF
);
/* FIXME: Which defaults does it care about...? */
pRendererDetails->defaultDevice = (deviceDetails.Role & (
FAudioGlobalDefaultDevice |
FAudioDefaultGameDevice
)) != 0;
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return 0;
}
uint32_t FACTAudioEngine_GetFinalMixFormat(
FACTAudioEngine *pEngine,
FAudioWaveFormatExtensible *pFinalMixFormat
) {
FAudio_PlatformLockMutex(pEngine->apiLock);
FAudio_memcpy(
pFinalMixFormat,
&pEngine->audio->mixFormat,
sizeof(FAudioWaveFormatExtensible)
);
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return 0;
}
uint32_t FACTAudioEngine_Initialize(
FACTAudioEngine *pEngine,
const FACTRuntimeParameters *pParams
) {
uint32_t parseRet;
uint32_t deviceIndex;
FAudioVoiceDetails masterDetails;
FAudioEffectDescriptor reverbDesc;
FAudioEffectChain reverbChain;
FAudio_PlatformLockMutex(pEngine->apiLock);
/* Parse the file */
parseRet = FACT_INTERNAL_ParseAudioEngine(pEngine, pParams);
if (parseRet != 0)
{
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return parseRet;
}
/* Assign the callbacks */
pEngine->notificationCallback = pParams->fnNotificationCallback;
pEngine->pReadFile = pParams->fileIOCallbacks.readFileCallback;
pEngine->pGetOverlappedResult = pParams->fileIOCallbacks.getOverlappedResultCallback;
if (pEngine->pReadFile == NULL)
{
pEngine->pReadFile = FACT_INTERNAL_DefaultReadFile;
}
if (pEngine->pGetOverlappedResult == NULL)
{
pEngine->pGetOverlappedResult = FACT_INTERNAL_DefaultGetOverlappedResult;
}
/* Init the FAudio subsystem */
pEngine->audio = pParams->pXAudio2;
if (pEngine->audio == NULL)
{
FAudio_assert(pParams->pMasteringVoice == NULL);
FAudioCreate(&pEngine->audio, 0, FAUDIO_DEFAULT_PROCESSOR);
}
/* Create the audio device */
pEngine->master = pParams->pMasteringVoice;
if (pEngine->master == NULL)
{
if (pParams->pRendererID == NULL || pParams->pRendererID[0] == 0)
{
deviceIndex = 0;
}
else
{
deviceIndex = pParams->pRendererID[0] - L'0';
if (deviceIndex > FAudio_PlatformGetDeviceCount())
{
deviceIndex = 0;
}
}
if (FAudio_CreateMasteringVoice(
pEngine->audio,
&pEngine->master,
FAUDIO_DEFAULT_CHANNELS,
FAUDIO_DEFAULT_SAMPLERATE,
0,
deviceIndex,
NULL
) != 0) {
FAudio_Release(pEngine->audio);
return FAUDIO_E_INVALID_CALL;
}
}
/* Create the reverb effect, if applicable */
if (pEngine->dspPresetCount > 0) /* Never more than 1...? */
{
FAudioVoice_GetVoiceDetails(pEngine->master, &masterDetails);
/* Reverb effect chain... */
FAudioCreateReverb(&reverbDesc.pEffect, 0);
reverbDesc.InitialState = 1;
reverbDesc.OutputChannels = (masterDetails.InputChannels == 6) ? 6 : 1;
reverbChain.EffectCount = 1;
reverbChain.pEffectDescriptors = &reverbDesc;
/* Reverb submix voice... */
FAudio_CreateSubmixVoice(
pEngine->audio,
&pEngine->reverbVoice,
1, /* Reverb will be omnidirectional */
masterDetails.InputSampleRate,
0,
0,
NULL,
&reverbChain
);
/* We can release now, the submix owns this! */
FAPOBase_Release((FAPOBase*) reverbDesc.pEffect);
}
pEngine->initialized = 1;
pEngine->apiThread = FAudio_PlatformCreateThread(
FACT_INTERNAL_APIThread,
"FACT Thread",
pEngine
);
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return 0;
}
uint32_t FACTAudioEngine_ShutDown(FACTAudioEngine *pEngine)
{
uint32_t i, refcount;
FAudioMutex mutex;
FAudioMallocFunc pMalloc;
FAudioFreeFunc pFree;
FAudioReallocFunc pRealloc;
/* Close thread, then lock ASAP */
pEngine->initialized = 0;
FAudio_PlatformWaitThread(pEngine->apiThread, NULL);
FAudio_PlatformLockMutex(pEngine->apiLock);
/* Stop the platform stream before freeing stuff! */
if (pEngine->audio != NULL)
{
FAudio_StopEngine(pEngine->audio);
}
/* This method destroys all existing cues, sound banks, and wave banks.
* It blocks until all cues are destroyed.
*/
while (pEngine->wbList != NULL)
{
FACTWaveBank_Destroy((FACTWaveBank*) pEngine->wbList->entry);
}
while (pEngine->sbList != NULL)
{
FACTSoundBank_Destroy((FACTSoundBank*) pEngine->sbList->entry);
}
/* Category data */
for (i = 0; i < pEngine->categoryCount; i += 1)
{
pEngine->pFree(pEngine->categoryNames[i]);
}
pEngine->pFree(pEngine->categoryNames);
pEngine->pFree(pEngine->categories);
/* Variable data */
for (i = 0; i < pEngine->variableCount; i += 1)
{
pEngine->pFree(pEngine->variableNames[i]);
}
pEngine->pFree(pEngine->variableNames);
pEngine->pFree(pEngine->variables);
pEngine->pFree(pEngine->globalVariableValues);
/* RPC data */
for (i = 0; i < pEngine->rpcCount; i += 1)
{
pEngine->pFree(pEngine->rpcs[i].points);
}
pEngine->pFree(pEngine->rpcs);
pEngine->pFree(pEngine->rpcCodes);
/* DSP data */
for (i = 0; i < pEngine->dspPresetCount; i += 1)
{
pEngine->pFree(pEngine->dspPresets[i].parameters);
}
pEngine->pFree(pEngine->dspPresets);
pEngine->pFree(pEngine->dspPresetCodes);
/* Audio resources */
if (pEngine->reverbVoice != NULL)
{
FAudioVoice_DestroyVoice(pEngine->reverbVoice);
}
if (pEngine->master != NULL)
{
FAudioVoice_DestroyVoice(pEngine->master);
}
if (pEngine->audio != NULL)
{
FAudio_Release(pEngine->audio);
}
/* Finally. */
refcount = pEngine->refcount;
mutex = pEngine->apiLock;
pMalloc = pEngine->pMalloc;
pFree = pEngine->pFree;
pRealloc = pEngine->pRealloc;
FAudio_zero(pEngine, sizeof(FACTAudioEngine));
pEngine->pMalloc = pMalloc;
pEngine->pFree = pFree;
pEngine->pRealloc = pRealloc;
pEngine->refcount = refcount;
pEngine->apiLock = mutex;
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return 0;
}
uint32_t FACTAudioEngine_DoWork(FACTAudioEngine *pEngine)
{
uint8_t i;
FACTCue *cue;
LinkedList *list;
FAudio_PlatformLockMutex(pEngine->apiLock);
list = pEngine->sbList;
while (list != NULL)
{
cue = ((FACTSoundBank*) list->entry)->cueList;
while (cue != NULL)
{
if (cue->playingSound != NULL)
for (i = 0; i < cue->playingSound->sound->trackCount; i += 1)
{
if ( cue->playingSound->tracks[i].upcomingWave.wave == NULL &&
cue->playingSound->tracks[i].waveEvtInst->loopCount > 0 )
{
FACT_INTERNAL_GetNextWave(
cue,
cue->playingSound->sound,
&cue->playingSound->sound->tracks[i],
&cue->playingSound->tracks[i],
cue->playingSound->tracks[i].waveEvt,
cue->playingSound->tracks[i].waveEvtInst
);
}
}
cue = cue->next;
}
list = list->next;
}
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return 0;
}
uint32_t FACTAudioEngine_CreateSoundBank(
FACTAudioEngine *pEngine,
const void *pvBuffer,
uint32_t dwSize,
uint32_t dwFlags,
uint32_t dwAllocAttributes,
FACTSoundBank **ppSoundBank
) {
uint32_t retval;
FAudio_PlatformLockMutex(pEngine->apiLock);
retval = FACT_INTERNAL_ParseSoundBank(
pEngine,
pvBuffer,
dwSize,
ppSoundBank
);
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return retval;
}
uint32_t FACTAudioEngine_CreateInMemoryWaveBank(
FACTAudioEngine *pEngine,
const void *pvBuffer,
uint32_t dwSize,
uint32_t dwFlags,
uint32_t dwAllocAttributes,
FACTWaveBank **ppWaveBank
) {
uint32_t retval;
FAudio_PlatformLockMutex(pEngine->apiLock);
retval = FACT_INTERNAL_ParseWaveBank(
pEngine,
FAudio_memopen((void*) pvBuffer, dwSize),
0,
0,
FACT_INTERNAL_DefaultReadFile,
FACT_INTERNAL_DefaultGetOverlappedResult,
0,
ppWaveBank
);
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return retval;
}
uint32_t FACTAudioEngine_CreateStreamingWaveBank(
FACTAudioEngine *pEngine,
const FACTStreamingParameters *pParms,
FACTWaveBank **ppWaveBank
) {
uint32_t retval, packetSize;
FAudio_PlatformLockMutex(pEngine->apiLock);
if ( pEngine->pReadFile == FACT_INTERNAL_DefaultReadFile &&
pEngine->pGetOverlappedResult == FACT_INTERNAL_DefaultGetOverlappedResult )
{
/* Our I/O doesn't care about packets, set to 0 as an optimization */
packetSize = 0;
}
else
{
packetSize = pParms->packetSize * 2048;
}
retval = FACT_INTERNAL_ParseWaveBank(
pEngine,
pParms->file,
pParms->offset,
packetSize,
pEngine->pReadFile,
pEngine->pGetOverlappedResult,
1,
ppWaveBank
);
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return retval;
}
uint32_t FACTAudioEngine_PrepareWave(
FACTAudioEngine *pEngine,
uint32_t dwFlags,
const char *szWavePath,
uint32_t wStreamingPacketSize,
uint32_t dwAlignment,
uint32_t dwPlayOffset,
uint8_t nLoopCount,
FACTWave **ppWave
) {
/* TODO: FACTWave */
return 0;
}
uint32_t FACTAudioEngine_PrepareInMemoryWave(
FACTAudioEngine *pEngine,
uint32_t dwFlags,
FACTWaveBankEntry entry,
uint32_t *pdwSeekTable, /* Optional! */
uint8_t *pbWaveData,
uint32_t dwPlayOffset,
uint8_t nLoopCount,
FACTWave **ppWave
) {
/* TODO: FACTWave */
return 0;
}
uint32_t FACTAudioEngine_PrepareStreamingWave(
FACTAudioEngine *pEngine,
uint32_t dwFlags,
FACTWaveBankEntry entry,
FACTStreamingParameters streamingParams,
uint32_t dwAlignment,
uint32_t *pdwSeekTable, /* Optional! */
uint8_t *pbWaveData,
uint32_t dwPlayOffset,
uint8_t nLoopCount,
FACTWave **ppWave
) {
/* TODO: FACTWave */
return 0;
}
uint32_t FACTAudioEngine_RegisterNotification(
FACTAudioEngine *pEngine,
const FACTNotificationDescription *pNotificationDescription
) {
FAudio_assert(pEngine != NULL);
FAudio_assert(pNotificationDescription != NULL);
FAudio_assert(pEngine->notificationCallback != NULL);
FAudio_PlatformLockMutex(pEngine->apiLock);
if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_CUEDESTROYED)
{
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
{
pEngine->notifications |= NOTIFY_CUEDESTROY;
pEngine->cue_context = pNotificationDescription->pvContext;
}
else
{
pNotificationDescription->pCue->notifyOnDestroy = 1;
pNotificationDescription->pCue->usercontext = pNotificationDescription->pvContext;
}
}
else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED)
{
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
{
pEngine->notifications |= NOTIFY_SOUNDBANKDESTROY;
pEngine->sb_context = pNotificationDescription->pvContext;
}
else
{
pNotificationDescription->pSoundBank->notifyOnDestroy = 1;
pNotificationDescription->pSoundBank->usercontext = pNotificationDescription->pvContext;
}
}
else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED)
{
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
{
pEngine->notifications |= NOTIFY_WAVEBANKDESTROY;
pEngine->wb_context = pNotificationDescription->pvContext;
}
else
{
pNotificationDescription->pWaveBank->notifyOnDestroy = 1;
pNotificationDescription->pWaveBank->usercontext = pNotificationDescription->pvContext;
}
}
else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_WAVEDESTROYED)
{
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
{
pEngine->notifications |= NOTIFY_WAVEDESTROY;
pEngine->wave_context = pNotificationDescription->pvContext;
}
else
{
pNotificationDescription->pWave->notifyOnDestroy = 1;
pNotificationDescription->pWave->usercontext = pNotificationDescription->pvContext;
}
}
else
{
FAudio_assert(0 && "TODO: Unimplemented notification!");
}
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return 0;
}
uint32_t FACTAudioEngine_UnRegisterNotification(
FACTAudioEngine *pEngine,
const FACTNotificationDescription *pNotificationDescription
) {
FAudio_assert(pEngine != NULL);
FAudio_assert(pNotificationDescription != NULL);
FAudio_assert(pEngine->notificationCallback != NULL);
FAudio_PlatformLockMutex(pEngine->apiLock);
if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_CUEDESTROYED)
{
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
{
pEngine->notifications &= ~NOTIFY_CUEDESTROY;
pEngine->cue_context = pNotificationDescription->pvContext;
}
else
{
pNotificationDescription->pCue->notifyOnDestroy = 0;
pNotificationDescription->pCue->usercontext = pNotificationDescription->pvContext;
}
}
else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED)
{
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
{
pEngine->notifications &= ~NOTIFY_SOUNDBANKDESTROY;
pEngine->sb_context = pNotificationDescription->pvContext;
}
else
{
pNotificationDescription->pSoundBank->notifyOnDestroy = 0;
pNotificationDescription->pSoundBank->usercontext = pNotificationDescription->pvContext;
}
}
else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED)
{
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
{
pEngine->notifications &= ~NOTIFY_WAVEBANKDESTROY;
pEngine->wb_context = pNotificationDescription->pvContext;
}
else
{
pNotificationDescription->pWaveBank->notifyOnDestroy = 0;
pNotificationDescription->pWaveBank->usercontext = pNotificationDescription->pvContext;
}
}
else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_WAVEDESTROYED)
{
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
{
pEngine->notifications &= ~NOTIFY_WAVEDESTROY;
pEngine->wave_context = pNotificationDescription->pvContext;
}
else
{
pNotificationDescription->pWave->notifyOnDestroy = 0;
pNotificationDescription->pWave->usercontext = pNotificationDescription->pvContext;
}
}
else
{
FAudio_assert(0 && "TODO: Unimplemented notification!");
}
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return 0;
}
uint16_t FACTAudioEngine_GetCategory(
FACTAudioEngine *pEngine,
const char *szFriendlyName
) {
uint16_t i;
FAudio_PlatformLockMutex(pEngine->apiLock);
for (i = 0; i < pEngine->categoryCount; i += 1)
{
if (FAudio_strcmp(szFriendlyName, pEngine->categoryNames[i]) == 0)
{
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return i;
}
}
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return FACTCATEGORY_INVALID;
}
uint8_t FACT_INTERNAL_IsInCategory(
FACTAudioEngine *engine,
uint16_t target,
uint16_t category
) {
FACTAudioCategory *cat;
/* Same category, no need to go on a crazy hunt */
if (category == target)
{
return 1;
}
/* Right, on with the crazy hunt */
cat = &engine->categories[category];
while (cat->parentCategory != -1)
{
if (cat->parentCategory == target)
{
return 1;
}
cat = &engine->categories[cat->parentCategory];
}
return 0;
}
uint32_t FACTAudioEngine_Stop(
FACTAudioEngine *pEngine,
uint16_t nCategory,
uint32_t dwFlags
) {
FACTCue *cue, *backup;
LinkedList *list;
FAudio_PlatformLockMutex(pEngine->apiLock);
list = pEngine->sbList;
while (list != NULL)
{
cue = ((FACTSoundBank*) list->entry)->cueList;
while (cue != NULL)
{
if ( cue->playingSound != NULL &&
FACT_INTERNAL_IsInCategory(
pEngine,
nCategory,
cue->playingSound->sound->category
) )
{
if ( dwFlags == FACT_FLAG_STOP_IMMEDIATE &&
cue->managed )
{
/* Just blow this up now */
backup = cue->next;
FACTCue_Destroy(cue);
cue = backup;
}
else
{
/* If managed, the mixer will destroy for us */
FACTCue_Stop(cue, dwFlags);
cue = cue->next;
}
}
else
{
cue = cue->next;
}
}
list = list->next;
}
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return 0;
}
uint32_t FACTAudioEngine_SetVolume(
FACTAudioEngine *pEngine,
uint16_t nCategory,
float volume
) {
uint16_t i;
FAudio_PlatformLockMutex(pEngine->apiLock);
pEngine->categories[nCategory].currentVolume = (
pEngine->categories[nCategory].volume *
volume
);
for (i = 0; i < pEngine->categoryCount; i += 1)
{
if (pEngine->categories[i].parentCategory == nCategory)
{
FACTAudioEngine_SetVolume(
pEngine,
i,
pEngine->categories[i].currentVolume
);
}
}
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return 0;
}
uint32_t FACTAudioEngine_Pause(
FACTAudioEngine *pEngine,
uint16_t nCategory,
int32_t fPause
) {
FACTCue *cue;
LinkedList *list;
FAudio_PlatformLockMutex(pEngine->apiLock);
list = pEngine->sbList;
while (list != NULL)
{
cue = ((FACTSoundBank*) list->entry)->cueList;
while (cue != NULL)
{
if ( cue->playingSound != NULL &&
FACT_INTERNAL_IsInCategory(
pEngine,
nCategory,
cue->playingSound->sound->category
) )
{
FACTCue_Pause(cue, fPause);
}
cue = cue->next;
}
list = list->next;
}
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return 0;
}
uint16_t FACTAudioEngine_GetGlobalVariableIndex(
FACTAudioEngine *pEngine,
const char *szFriendlyName
) {
uint16_t i;
FAudio_PlatformLockMutex(pEngine->apiLock);
for (i = 0; i < pEngine->variableCount; i += 1)
{
if ( FAudio_strcmp(szFriendlyName, pEngine->variableNames[i]) == 0 &&
!(pEngine->variables[i].accessibility & 0x04) )
{
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return i;
}
}
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return FACTVARIABLEINDEX_INVALID;
}
uint32_t FACTAudioEngine_SetGlobalVariable(
FACTAudioEngine *pEngine,
uint16_t nIndex,
float nValue
) {
FACTVariable *var;
FAudio_PlatformLockMutex(pEngine->apiLock);
var = &pEngine->variables[nIndex];
FAudio_assert(var->accessibility & 0x01);
FAudio_assert(!(var->accessibility & 0x02));
FAudio_assert(!(var->accessibility & 0x04));
pEngine->globalVariableValues[nIndex] = FAudio_clamp(
nValue,
var->minValue,
var->maxValue
);
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return 0;
}
uint32_t FACTAudioEngine_GetGlobalVariable(
FACTAudioEngine *pEngine,
uint16_t nIndex,
float *pnValue
) {
FACTVariable *var;
FAudio_PlatformLockMutex(pEngine->apiLock);
var = &pEngine->variables[nIndex];
FAudio_assert(var->accessibility & 0x01);
FAudio_assert(!(var->accessibility & 0x04));
*pnValue = pEngine->globalVariableValues[nIndex];
FAudio_PlatformUnlockMutex(pEngine->apiLock);
return 0;
}
/* SoundBank implementation */
uint16_t FACTSoundBank_GetCueIndex(
FACTSoundBank *pSoundBank,
const char *szFriendlyName
) {
uint16_t i;
if (pSoundBank == NULL)
{
return FACTINDEX_INVALID;
}
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
for (i = 0; i < pSoundBank->cueCount; i += 1)
{
if (FAudio_strcmp(szFriendlyName, pSoundBank->cueNames[i]) == 0)
{
FAudio_PlatformUnlockMutex(
pSoundBank->parentEngine->apiLock
);
return i;
}
}
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
return FACTINDEX_INVALID;
}
uint32_t FACTSoundBank_GetNumCues(
FACTSoundBank *pSoundBank,
uint16_t *pnNumCues
) {
if (pSoundBank == NULL)
{
*pnNumCues = 0;
return 0;
}
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
*pnNumCues = pSoundBank->cueCount;
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTSoundBank_GetCueProperties(
FACTSoundBank *pSoundBank,
uint16_t nCueIndex,
FACTCueProperties *pProperties
) {
uint16_t i;
if (pSoundBank == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
FAudio_strlcpy(
pProperties->friendlyName,
pSoundBank->cueNames[nCueIndex],
0xFF
);
if (!(pSoundBank->cues[nCueIndex].flags & 0x04))
{
for (i = 0; i < pSoundBank->variationCount; i += 1)
{
if (pSoundBank->variationCodes[i] == pSoundBank->cues[nCueIndex].sbCode)
{
break;
}
}
FAudio_assert(i < pSoundBank->variationCount && "Variation table not found!");
if (pSoundBank->variations[i].flags == 3)
{
pProperties->interactive = 1;
pProperties->iaVariableIndex = pSoundBank->variations[i].variable;
}
else
{
pProperties->interactive = 0;
pProperties->iaVariableIndex = 0;
}
pProperties->numVariations = pSoundBank->variations[i].entryCount;
}
else
{
pProperties->interactive = 0;
pProperties->iaVariableIndex = 0;
pProperties->numVariations = 0;
}
pProperties->maxInstances = pSoundBank->cues[nCueIndex].instanceLimit;
pProperties->currentInstances = pSoundBank->cues[nCueIndex].instanceCount;
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTSoundBank_Prepare(
FACTSoundBank *pSoundBank,
uint16_t nCueIndex,
uint32_t dwFlags,
int32_t timeOffset,
FACTCue** ppCue
) {
uint16_t i;
FACTCue *latest;
if (pSoundBank == NULL)
{
*ppCue = NULL;
return 1;
}
*ppCue = (FACTCue*) pSoundBank->parentEngine->pMalloc(sizeof(FACTCue));
FAudio_zero(*ppCue, sizeof(FACTCue));
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
/* Engine references */
(*ppCue)->parentBank = pSoundBank;
(*ppCue)->next = NULL;
(*ppCue)->managed = 0;
(*ppCue)->index = nCueIndex;
(*ppCue)->notifyOnDestroy = 0;
(*ppCue)->usercontext = NULL;
/* Sound data */
(*ppCue)->data = &pSoundBank->cues[nCueIndex];
if ((*ppCue)->data->flags & 0x04)
{
for (i = 0; i < pSoundBank->soundCount; i += 1)
{
if ((*ppCue)->data->sbCode == pSoundBank->soundCodes[i])
{
(*ppCue)->sound = &pSoundBank->sounds[i];
break;
}
}
}
else
{
for (i = 0; i < pSoundBank->variationCount; i += 1)
{
if ((*ppCue)->data->sbCode == pSoundBank->variationCodes[i])
{
(*ppCue)->variation = &pSoundBank->variations[i];
break;
}
}
if ((*ppCue)->variation->flags == 3)
{
(*ppCue)->interactive = pSoundBank->parentEngine->variables[
(*ppCue)->variation->variable
].initialValue;
}
}
/* Instance data */
(*ppCue)->variableValues = (float*) pSoundBank->parentEngine->pMalloc(
sizeof(float) * pSoundBank->parentEngine->variableCount
);
for (i = 0; i < pSoundBank->parentEngine->variableCount; i += 1)
{
(*ppCue)->variableValues[i] =
pSoundBank->parentEngine->variables[i].initialValue;
}
/* Playback */
(*ppCue)->state = FACT_STATE_PREPARED;
/* Add to the SoundBank Cue list */
if (pSoundBank->cueList == NULL)
{
pSoundBank->cueList = *ppCue;
}
else
{
latest = pSoundBank->cueList;
while (latest->next != NULL)
{
latest = latest->next;
}
latest->next = *ppCue;
}
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTSoundBank_Play(
FACTSoundBank *pSoundBank,
uint16_t nCueIndex,
uint32_t dwFlags,
int32_t timeOffset,
FACTCue** ppCue /* Optional! */
) {
FACTCue *result;
if (pSoundBank == NULL)
{
if (ppCue != NULL)
{
*ppCue = NULL;
}
return 1;
}
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
FACTSoundBank_Prepare(
pSoundBank,
nCueIndex,
dwFlags,
timeOffset,
&result
);
if (ppCue != NULL)
{
*ppCue = result;
}
else
{
/* AKA we get to Destroy() this ourselves */
result->managed = 1;
}
FACTCue_Play(result);
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTSoundBank_Play3D(
FACTSoundBank *pSoundBank,
uint16_t nCueIndex,
uint32_t dwFlags,
int32_t timeOffset,
F3DAUDIO_DSP_SETTINGS *pDSPSettings,
FACTCue** ppCue /* Optional! */
) {
FACTCue *result;
if (pSoundBank == NULL)
{
if (ppCue != NULL)
{
*ppCue = NULL;
}
return 1;
}
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
FACTSoundBank_Prepare(
pSoundBank,
nCueIndex,
dwFlags,
timeOffset,
&result
);
if (ppCue != NULL)
{
*ppCue = result;
}
else
{
/* AKA we get to Destroy() this ourselves */
result->managed = 1;
}
FACT3DApply(pDSPSettings, result);
FACTCue_Play(result);
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTSoundBank_Stop(
FACTSoundBank *pSoundBank,
uint16_t nCueIndex,
uint32_t dwFlags
) {
FACTCue *backup, *cue;
if (pSoundBank == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
cue = pSoundBank->cueList;
while (cue != NULL)
{
if (cue->index == nCueIndex)
{
if ( dwFlags == FACT_FLAG_STOP_IMMEDIATE &&
cue->managed )
{
/* Just blow this up now */
backup = cue->next;
FACTCue_Destroy(cue);
cue = backup;
}
else
{
/* If managed, the mixer will destroy for us */
FACTCue_Stop(cue, dwFlags);
cue = cue->next;
}
}
else
{
cue = cue->next;
}
}
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTSoundBank_Destroy(FACTSoundBank *pSoundBank)
{
uint16_t i, j, k;
FAudioMutex mutex;
FACTNotification note;
if (pSoundBank == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
/* Synchronously destroys all cues that are associated */
while (pSoundBank->cueList != NULL)
{
FACTCue_Destroy(pSoundBank->cueList);
}
if (pSoundBank->parentEngine != NULL)
{
/* Remove this SoundBank from the Engine list */
LinkedList_RemoveEntry(
&pSoundBank->parentEngine->sbList,
pSoundBank,
pSoundBank->parentEngine->sbLock,
pSoundBank->parentEngine->pFree
);
}
/* SoundBank Name */
pSoundBank->parentEngine->pFree(pSoundBank->name);
/* Cue data */
pSoundBank->parentEngine->pFree(pSoundBank->cues);
/* WaveBank Name data */
for (i = 0; i < pSoundBank->wavebankCount; i += 1)
{
pSoundBank->parentEngine->pFree(pSoundBank->wavebankNames[i]);
}
pSoundBank->parentEngine->pFree(pSoundBank->wavebankNames);
/* Sound data */
for (i = 0; i < pSoundBank->soundCount; i += 1)
{
for (j = 0; j < pSoundBank->sounds[i].trackCount; j += 1)
{
for (k = 0; k < pSoundBank->sounds[i].tracks[j].eventCount; k += 1)
{
#define MATCH(t) \
pSoundBank->sounds[i].tracks[j].events[k].type == t
if ( MATCH(FACTEVENT_PLAYWAVE) ||
MATCH(FACTEVENT_PLAYWAVETRACKVARIATION) ||
MATCH(FACTEVENT_PLAYWAVEEFFECTVARIATION) ||
MATCH(FACTEVENT_PLAYWAVETRACKEFFECTVARIATION) )
{
if (pSoundBank->sounds[i].tracks[j].events[k].wave.isComplex)
{
pSoundBank->parentEngine->pFree(
pSoundBank->sounds[i].tracks[j].events[k].wave.complex.tracks
);
pSoundBank->parentEngine->pFree(
pSoundBank->sounds[i].tracks[j].events[k].wave.complex.wavebanks
);
pSoundBank->parentEngine->pFree(
pSoundBank->sounds[i].tracks[j].events[k].wave.complex.weights
);
}
}
#undef MATCH
}
pSoundBank->parentEngine->pFree(
pSoundBank->sounds[i].tracks[j].events
);
}
pSoundBank->parentEngine->pFree(pSoundBank->sounds[i].tracks);
pSoundBank->parentEngine->pFree(pSoundBank->sounds[i].rpcCodes);
pSoundBank->parentEngine->pFree(pSoundBank->sounds[i].dspCodes);
}
pSoundBank->parentEngine->pFree(pSoundBank->sounds);
pSoundBank->parentEngine->pFree(pSoundBank->soundCodes);
/* Variation data */
for (i = 0; i < pSoundBank->variationCount; i += 1)
{
pSoundBank->parentEngine->pFree(
pSoundBank->variations[i].entries
);
}
pSoundBank->parentEngine->pFree(pSoundBank->variations);
pSoundBank->parentEngine->pFree(pSoundBank->variationCodes);
/* Transition data */
for (i = 0; i < pSoundBank->transitionCount; i += 1)
{
pSoundBank->parentEngine->pFree(
pSoundBank->transitions[i].entries
);
}
pSoundBank->parentEngine->pFree(pSoundBank->transitions);
pSoundBank->parentEngine->pFree(pSoundBank->transitionCodes);
/* Cue Name data */
for (i = 0; i < pSoundBank->cueCount; i += 1)
{
pSoundBank->parentEngine->pFree(pSoundBank->cueNames[i]);
}
pSoundBank->parentEngine->pFree(pSoundBank->cueNames);
/* Finally. */
if (pSoundBank->notifyOnDestroy || pSoundBank->parentEngine->notifications & NOTIFY_SOUNDBANKDESTROY)
{
note.type = FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED;
note.soundBank.pSoundBank = pSoundBank;
if (pSoundBank->parentEngine->notifications & NOTIFY_SOUNDBANKDESTROY)
{
note.pvContext = pSoundBank->parentEngine->sb_context;
}
else
{
note.pvContext = pSoundBank->usercontext;
}
pSoundBank->parentEngine->notificationCallback(&note);
}
mutex = pSoundBank->parentEngine->apiLock;
pSoundBank->parentEngine->pFree(pSoundBank);
FAudio_PlatformUnlockMutex(mutex);
return 0;
}
uint32_t FACTSoundBank_GetState(
FACTSoundBank *pSoundBank,
uint32_t *pdwState
) {
uint16_t i;
if (pSoundBank == NULL)
{
*pdwState = 0;
return 1;
}
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
*pdwState = FACT_STATE_PREPARED;
for (i = 0; i < pSoundBank->cueCount; i += 1)
{
if (pSoundBank->cues[i].instanceCount > 0)
{
*pdwState |= FACT_STATE_INUSE;
FAudio_PlatformUnlockMutex(
pSoundBank->parentEngine->apiLock
);
return 0;
}
}
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
return 0;
}
/* WaveBank implementation */
uint32_t FACTWaveBank_Destroy(FACTWaveBank *pWaveBank)
{
uint32_t i;
FACTWave *wave;
FAudioMutex mutex;
FACTNotification note;
if (pWaveBank == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
/* Synchronously destroys any cues that are using the wavebank */
while (pWaveBank->waveList != NULL)
{
wave = (FACTWave*) pWaveBank->waveList->entry;
if (wave->parentCue != NULL)
{
/* Destroying this Cue destroys the Wave */
FACTCue_Destroy(wave->parentCue);
}
else
{
FACTWave_Destroy(wave);
}
}
if (pWaveBank->parentEngine != NULL)
{
/* Remove this WaveBank from the Engine list */
LinkedList_RemoveEntry(
&pWaveBank->parentEngine->wbList,
pWaveBank,
pWaveBank->parentEngine->wbLock,
pWaveBank->parentEngine->pFree
);
}
/* Free everything, finally. */
pWaveBank->parentEngine->pFree(pWaveBank->name);
pWaveBank->parentEngine->pFree(pWaveBank->entries);
pWaveBank->parentEngine->pFree(pWaveBank->entryRefs);
if (pWaveBank->seekTables != NULL)
{
for (i = 0; i < pWaveBank->entryCount; i += 1)
{
if (pWaveBank->seekTables[i].entries != NULL)
{
pWaveBank->parentEngine->pFree(
pWaveBank->seekTables[i].entries
);
}
}
pWaveBank->parentEngine->pFree(pWaveBank->seekTables);
}
FAudio_close(pWaveBank->io);
if (pWaveBank->packetBuffer != NULL)
{
pWaveBank->parentEngine->pFree(pWaveBank->packetBuffer);
}
if (pWaveBank->notifyOnDestroy || pWaveBank->parentEngine->notifications & NOTIFY_WAVEBANKDESTROY)
{
note.type = FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED;
note.waveBank.pWaveBank = pWaveBank;
if (pWaveBank->parentEngine->notifications & NOTIFY_WAVEBANKDESTROY)
{
note.pvContext = pWaveBank->parentEngine->wb_context;
}
else
{
note.pvContext = pWaveBank->usercontext;
}
pWaveBank->parentEngine->notificationCallback(&note);
}
FAudio_PlatformDestroyMutex(pWaveBank->waveLock);
mutex = pWaveBank->parentEngine->apiLock;
pWaveBank->parentEngine->pFree(pWaveBank);
FAudio_PlatformUnlockMutex(mutex);
return 0;
}
uint32_t FACTWaveBank_GetState(
FACTWaveBank *pWaveBank,
uint32_t *pdwState
) {
uint32_t i;
if (pWaveBank == NULL)
{
*pdwState = 0;
return 1;
}
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
*pdwState = FACT_STATE_PREPARED;
for (i = 0; i < pWaveBank->entryCount; i += 1)
{
if (pWaveBank->entryRefs[i] > 0)
{
*pdwState |= FACT_STATE_INUSE;
FAudio_PlatformUnlockMutex(
pWaveBank->parentEngine->apiLock
);
return 0;
}
}
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTWaveBank_GetNumWaves(
FACTWaveBank *pWaveBank,
uint16_t *pnNumWaves
) {
if (pWaveBank == NULL)
{
*pnNumWaves = 0;
return 1;
}
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
*pnNumWaves = pWaveBank->entryCount;
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
return 0;
}
uint16_t FACTWaveBank_GetWaveIndex(
FACTWaveBank *pWaveBank,
const char *szFriendlyName
) {
FAudio_assert(0 && "WaveBank name tables are not supported!");
return 0;
}
uint32_t FACTWaveBank_GetWaveProperties(
FACTWaveBank *pWaveBank,
uint16_t nWaveIndex,
FACTWaveProperties *pWaveProperties
) {
FACTWaveBankEntry *entry;
if (pWaveBank == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
entry = &pWaveBank->entries[nWaveIndex];
/* FIXME: Name tables! -flibit */
FAudio_zero(
pWaveProperties->friendlyName,
sizeof(pWaveProperties->friendlyName)
);
pWaveProperties->format = entry->Format;
pWaveProperties->durationInSamples = entry->PlayRegion.dwLength;
if (entry->Format.wFormatTag == 0)
{
pWaveProperties->durationInSamples /= (8 << entry->Format.wBitsPerSample) / 8;
pWaveProperties->durationInSamples /= entry->Format.nChannels;
}
else if (entry->Format.wFormatTag == FAUDIO_FORMAT_MSADPCM)
{
pWaveProperties->durationInSamples = (
pWaveProperties->durationInSamples /
((entry->Format.wBlockAlign + 22) * entry->Format.nChannels) *
((entry->Format.wBlockAlign + 16) * 2)
);
}
pWaveProperties->loopRegion = entry->LoopRegion;
pWaveProperties->streaming = pWaveBank->streaming;
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTWaveBank_Prepare(
FACTWaveBank *pWaveBank,
uint16_t nWaveIndex,
uint32_t dwFlags,
uint32_t dwPlayOffset,
uint8_t nLoopCount,
FACTWave **ppWave
) {
FAudioBuffer buffer;
FAudioBufferWMA bufferWMA;
FAudioVoiceSends sends;
FAudioSendDescriptor send;
FAudioADPCMWaveFormat format;
FACTWaveBankEntry *entry;
if (pWaveBank == NULL)
{
*ppWave = NULL;
return 1;
}
*ppWave = (FACTWave*) pWaveBank->parentEngine->pMalloc(sizeof(FACTWave));
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
entry = &pWaveBank->entries[nWaveIndex];
/* Engine references */
(*ppWave)->parentBank = pWaveBank;
(*ppWave)->parentCue = NULL;
(*ppWave)->index = nWaveIndex;
(*ppWave)->notifyOnDestroy = 0;
(*ppWave)->usercontext = NULL;
/* Playback */
(*ppWave)->state = FACT_STATE_PREPARED;
(*ppWave)->volume = 1.0f;
(*ppWave)->pitch = 0;
(*ppWave)->loopCount = nLoopCount;
/* TODO: Convert dwPlayOffset to a byte offset */
FAudio_assert(dwPlayOffset == 0);
#if 0
if (dwFlags & FACT_FLAG_UNITS_MS)
{
dwPlayOffset = (uint32_t) (
( /* Samples per millisecond... */
(float) entry->Format.nSamplesPerSec /
1000.0f
) * (float) dwPlayOffset
);
}
#endif
/* Create the voice */
send.Flags = 0;
send.pOutputVoice = pWaveBank->parentEngine->master;
sends.SendCount = 1;
sends.pSends = &send;
format.wfx.nChannels = entry->Format.nChannels;
format.wfx.nSamplesPerSec = entry->Format.nSamplesPerSec;
if (entry->Format.wFormatTag == 0x0)
{
format.wfx.wFormatTag = FAUDIO_FORMAT_PCM;
format.wfx.wBitsPerSample = 8 << entry->Format.wBitsPerSample;
format.wfx.nBlockAlign = format.wfx.nChannels * format.wfx.wBitsPerSample / 8;
format.wfx.nAvgBytesPerSec = format.wfx.nBlockAlign * format.wfx.nSamplesPerSec;
format.wfx.cbSize = 0;
}
else if (entry->Format.wFormatTag == 0x1)
{
/* XMA2 is quite similar to WMA Pro. */
FAudio_assert(entry->Format.wBitsPerSample != 0);
format.wfx.wFormatTag = FAUDIO_FORMAT_XMAUDIO2;
format.wfx.nAvgBytesPerSec = aWMAAvgBytesPerSec[entry->Format.wBlockAlign >> 5];
format.wfx.nBlockAlign = aWMABlockAlign[entry->Format.wBlockAlign & 0x1F];
format.wfx.wBitsPerSample = 16;
format.wfx.cbSize = 0;
}
else if (entry->Format.wFormatTag == 0x2)
{
format.wfx.wFormatTag = FAUDIO_FORMAT_MSADPCM;
format.wfx.nBlockAlign = (entry->Format.wBlockAlign + 22) * format.wfx.nChannels;
format.wfx.wBitsPerSample = 16;
format.wfx.cbSize = (
sizeof(FAudioADPCMWaveFormat) -
sizeof(FAudioWaveFormatEx)
);
format.wSamplesPerBlock = (
((format.wfx.nBlockAlign / format.wfx.nChannels) - 6) * 2
);
}
else if (entry->Format.wFormatTag == 0x3)
{
/* Apparently this is used to detect WMA Pro...? */
FAudio_assert(entry->Format.wBitsPerSample == 0);
format.wfx.wFormatTag = FAUDIO_FORMAT_WMAUDIO2;
format.wfx.nAvgBytesPerSec = aWMAAvgBytesPerSec[entry->Format.wBlockAlign >> 5];
format.wfx.nBlockAlign = aWMABlockAlign[entry->Format.wBlockAlign & 0x1F];
format.wfx.wBitsPerSample = 16;
format.wfx.cbSize = 0;
}
else
{
FAudio_assert(0 && "Rebuild your WaveBanks with ADPCM!");
}
(*ppWave)->callback.callback.OnBufferEnd = pWaveBank->streaming ?
FACT_INTERNAL_OnBufferEnd :
NULL;
(*ppWave)->callback.callback.OnBufferStart = NULL;
(*ppWave)->callback.callback.OnLoopEnd = NULL;
(*ppWave)->callback.callback.OnStreamEnd = FACT_INTERNAL_OnStreamEnd;
(*ppWave)->callback.callback.OnVoiceError = NULL;
(*ppWave)->callback.callback.OnVoiceProcessingPassEnd = NULL;
(*ppWave)->callback.callback.OnVoiceProcessingPassStart = NULL;
(*ppWave)->callback.wave = *ppWave;
(*ppWave)->srcChannels = format.wfx.nChannels;
FAudio_CreateSourceVoice(
pWaveBank->parentEngine->audio,
&(*ppWave)->voice,
&format.wfx,
FAUDIO_VOICE_USEFILTER, /* FIXME: Can this be optional? */
4.0f,
(FAudioVoiceCallback*) &(*ppWave)->callback,
&sends,
NULL
);
if (pWaveBank->streaming)
{
/* Init stream cache info */
if (format.wfx.wFormatTag == FAUDIO_FORMAT_PCM)
{
(*ppWave)->streamSize = (
format.wfx.nSamplesPerSec *
format.wfx.nBlockAlign
);
}
else if (format.wfx.wFormatTag == FAUDIO_FORMAT_MSADPCM)
{
(*ppWave)->streamSize = (
format.wfx.nSamplesPerSec /
format.wSamplesPerBlock *
format.wfx.nBlockAlign
);
}
else
{
/* Screw it, load the whole thing */
(*ppWave)->streamSize = entry->PlayRegion.dwLength;
/* XACT does NOT support loop subregions for these formats */
FAudio_assert(entry->LoopRegion.dwStartSample == 0);
FAudio_assert(entry->LoopRegion.dwTotalSamples == entry->Duration);
}
(*ppWave)->streamCache = (uint8_t*) pWaveBank->parentEngine->pMalloc(
(*ppWave)->streamSize
);
(*ppWave)->streamOffset = entry->PlayRegion.dwOffset;
/* Read and submit first buffer from the WaveBank */
FACT_INTERNAL_OnBufferEnd(&(*ppWave)->callback.callback, NULL);
}
else
{
(*ppWave)->streamCache = NULL;
buffer.Flags = FAUDIO_END_OF_STREAM;
buffer.AudioBytes = entry->PlayRegion.dwLength;
buffer.pAudioData = FAudio_memptr(
pWaveBank->io,
entry->PlayRegion.dwOffset
);
buffer.PlayBegin = 0;
buffer.PlayLength = entry->Duration;
if (nLoopCount == 0)
{
buffer.LoopBegin = 0;
buffer.LoopLength = 0;
buffer.LoopCount = 0;
}
else
{
buffer.LoopBegin = entry->LoopRegion.dwStartSample;
buffer.LoopLength = entry->LoopRegion.dwTotalSamples;
buffer.LoopCount = nLoopCount;
}
buffer.pContext = NULL;
if ( format.wfx.wFormatTag == FAUDIO_FORMAT_WMAUDIO2 ||
format.wfx.wFormatTag == FAUDIO_FORMAT_XMAUDIO2 )
{
bufferWMA.pDecodedPacketCumulativeBytes =
pWaveBank->seekTables[nWaveIndex].entries;
bufferWMA.PacketCount =
pWaveBank->seekTables[nWaveIndex].entryCount;
FAudioSourceVoice_SubmitSourceBuffer(
(*ppWave)->voice,
&buffer,
&bufferWMA
);
}
else
{
FAudioSourceVoice_SubmitSourceBuffer(
(*ppWave)->voice,
&buffer,
NULL
);
}
}
/* Add to the WaveBank Wave list */
LinkedList_AddEntry(
&pWaveBank->waveList,
*ppWave,
pWaveBank->waveLock,
pWaveBank->parentEngine->pMalloc
);
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTWaveBank_Play(
FACTWaveBank *pWaveBank,
uint16_t nWaveIndex,
uint32_t dwFlags,
uint32_t dwPlayOffset,
uint8_t nLoopCount,
FACTWave **ppWave
) {
if (pWaveBank == NULL)
{
*ppWave = NULL;
return 1;
}
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
FACTWaveBank_Prepare(
pWaveBank,
nWaveIndex,
dwFlags,
dwPlayOffset,
nLoopCount,
ppWave
);
FACTWave_Play(*ppWave);
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTWaveBank_Stop(
FACTWaveBank *pWaveBank,
uint16_t nWaveIndex,
uint32_t dwFlags
) {
FACTWave *wave;
LinkedList *list;
if (pWaveBank == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
list = pWaveBank->waveList;
while (list != NULL)
{
wave = (FACTWave*) list->entry;
if (wave->index == nWaveIndex)
{
FACTWave_Stop(wave, dwFlags);
}
list = list->next;
}
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
return 0;
}
/* Wave implementation */
uint32_t FACTWave_Destroy(FACTWave *pWave)
{
FAudioMutex mutex;
FACTNotification note;
if (pWave == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
/* Stop before we start deleting everything */
FACTWave_Stop(pWave, FACT_FLAG_STOP_IMMEDIATE);
LinkedList_RemoveEntry(
&pWave->parentBank->waveList,
pWave,
pWave->parentBank->waveLock,
pWave->parentBank->parentEngine->pFree
);
FAudioVoice_DestroyVoice(pWave->voice);
if (pWave->streamCache != NULL)
{
pWave->parentBank->parentEngine->pFree(pWave->streamCache);
}
if (pWave->notifyOnDestroy || pWave->parentBank->parentEngine->notifications & NOTIFY_WAVEDESTROY)
{
note.type = FACTNOTIFICATIONTYPE_WAVEDESTROYED;
note.wave.pWave = pWave;
if (pWave->parentBank->parentEngine->notifications & NOTIFY_WAVEDESTROY)
{
note.pvContext = pWave->parentBank->parentEngine->wave_context;
}
else
{
note.pvContext = pWave->usercontext;
}
pWave->parentBank->parentEngine->notificationCallback(&note);
}
mutex = pWave->parentBank->parentEngine->apiLock;
pWave->parentBank->parentEngine->pFree(pWave);
FAudio_PlatformUnlockMutex(mutex);
return 0;
}
uint32_t FACTWave_Play(FACTWave *pWave)
{
if (pWave == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
FAudio_assert(!(pWave->state & (FACT_STATE_PLAYING | FACT_STATE_STOPPING)));
pWave->state |= FACT_STATE_PLAYING;
pWave->state &= ~(
FACT_STATE_PAUSED |
FACT_STATE_STOPPED
);
FAudioSourceVoice_Start(pWave->voice, 0, 0);
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTWave_Stop(FACTWave *pWave, uint32_t dwFlags)
{
if (pWave == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
/* There are two ways that a Wave might be stopped immediately:
* 1. The program explicitly asks for it
* 2. The Wave is paused and therefore we can't do fade/release effects
*/
if ( dwFlags & FACT_FLAG_STOP_IMMEDIATE ||
pWave->state & FACT_STATE_PAUSED )
{
pWave->state |= FACT_STATE_STOPPED;
pWave->state &= ~(
FACT_STATE_PLAYING |
FACT_STATE_STOPPING |
FACT_STATE_PAUSED
);
FAudioSourceVoice_Stop(pWave->voice, 0, 0);
FAudioSourceVoice_FlushSourceBuffers(pWave->voice);
}
else
{
pWave->state |= FACT_STATE_STOPPING;
FAudioSourceVoice_ExitLoop(pWave->voice, 0);
}
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTWave_Pause(FACTWave *pWave, int32_t fPause)
{
if (pWave == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
/* FIXME: Does the Cue STOPPING/STOPPED rule apply here too? */
if (pWave->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))
{
FAudio_PlatformUnlockMutex(
pWave->parentBank->parentEngine->apiLock
);
return 0;
}
/* All we do is set the flag, the mixer handles the rest */
if (fPause)
{
pWave->state |= FACT_STATE_PAUSED;
FAudioSourceVoice_Stop(pWave->voice, 0, 0);
}
else
{
pWave->state &= ~FACT_STATE_PAUSED;
FAudioSourceVoice_Start(pWave->voice, 0, 0);
}
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTWave_GetState(FACTWave *pWave, uint32_t *pdwState)
{
if (pWave == NULL)
{
*pdwState = 0;
return 1;
}
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
*pdwState = pWave->state;
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTWave_SetPitch(FACTWave *pWave, int16_t pitch)
{
if (pWave == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
pWave->pitch = FAudio_clamp(
pitch,
FACTPITCH_MIN_TOTAL,
FACTPITCH_MAX_TOTAL
);
FAudioSourceVoice_SetFrequencyRatio(
pWave->voice,
(float) FAudio_pow(2.0, pWave->pitch / 1200.0),
0
);
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTWave_SetVolume(FACTWave *pWave, float volume)
{
if (pWave == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
pWave->volume = FAudio_clamp(
volume,
FACTVOLUME_MIN,
FACTVOLUME_MAX
);
FAudioVoice_SetVolume(
pWave->voice,
pWave->volume,
0
);
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTWave_SetMatrixCoefficients(
FACTWave *pWave,
uint32_t uSrcChannelCount,
uint32_t uDstChannelCount,
float *pMatrixCoefficients
) {
uint32_t i;
float *mtxDst, *mtxSrc;
if (pWave == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
/* There seems to be this weird feature in XACT where the channel count
* can be completely wrong and it'll go to the right place.
* I guess these XACT functions do some extra work to merge coefficients
* but I have no idea where it really happens and XAudio2 definitely
* does NOT like it when this is wrong, so here it goes...
* -flibit
*/
if (uSrcChannelCount == 1 && pWave->srcChannels == 2)
{
mtxDst = pMatrixCoefficients + ((uDstChannelCount - 1) * 2);
mtxSrc = pMatrixCoefficients + (uDstChannelCount - 1);
for (i = 0; i < uDstChannelCount; i += 1)
{
mtxDst[0] = *mtxSrc;
mtxDst[1] = *mtxSrc;
mtxDst -= 2;
mtxSrc -= 1;
}
uSrcChannelCount = 2;
}
else if (uSrcChannelCount == 2 && pWave->srcChannels == 1)
{
mtxDst = pMatrixCoefficients;
mtxSrc = pMatrixCoefficients;
for (i = 0; i < uDstChannelCount; i += 1)
{
*mtxDst = (mtxSrc[0] + mtxSrc[1]) / 2.0f;
mtxDst += 1;
mtxSrc += 2;
}
uSrcChannelCount = 1;
}
FAudioVoice_SetOutputMatrix(
pWave->voice,
pWave->voice->sends.pSends->pOutputVoice,
uSrcChannelCount,
uDstChannelCount,
pMatrixCoefficients,
0
);
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTWave_GetProperties(
FACTWave *pWave,
FACTWaveInstanceProperties *pProperties
) {
if (pWave == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
FACTWaveBank_GetWaveProperties(
pWave->parentBank,
pWave->index,
&pProperties->properties
);
/* FIXME: This is unsupported on PC, do we care about this? */
pProperties->backgroundMusic = 0;
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
return 0;
}
/* Cue implementation */
uint32_t FACTCue_Destroy(FACTCue *pCue)
{
FACTCue *cue, *prev;
FAudioMutex mutex;
FACTNotification note;
if (pCue == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
/* Stop before we start deleting everything */
FACTCue_Stop(pCue, FACT_FLAG_STOP_IMMEDIATE);
/* Remove this Cue from the SoundBank list */
cue = pCue->parentBank->cueList;
prev = cue;
while (cue != NULL)
{
if (cue == pCue)
{
if (cue == prev) /* First in list */
{
pCue->parentBank->cueList = cue->next;
}
else
{
prev->next = cue->next;
}
break;
}
prev = cue;
cue = cue->next;
}
FAudio_assert(cue != NULL && "Could not find Cue reference!");
pCue->parentBank->parentEngine->pFree(pCue->variableValues);
if (pCue->notifyOnDestroy || pCue->parentBank->parentEngine->notifications & NOTIFY_CUEDESTROY)
{
note.type = FACTNOTIFICATIONTYPE_CUEDESTROYED;
note.cue.pCue = pCue;
if (pCue->parentBank->parentEngine->notifications & NOTIFY_CUEDESTROY)
{
note.pvContext = pCue->parentBank->parentEngine->cue_context;
}
else
{
note.pvContext = pCue->usercontext;
}
pCue->parentBank->parentEngine->notificationCallback(&note);
}
mutex = pCue->parentBank->parentEngine->apiLock;
pCue->parentBank->parentEngine->pFree(pCue);
FAudio_PlatformUnlockMutex(mutex);
return 0;
}
uint32_t FACTCue_Play(FACTCue *pCue)
{
union
{
float maxf;
uint8_t maxi;
} limitmax;
FACTCue *tmp, *wnr;
uint16_t fadeInMS = 0;
FACTCueData *data;
if (pCue == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
FAudio_assert(!(pCue->state & (FACT_STATE_PLAYING | FACT_STATE_STOPPING)));
data = &pCue->parentBank->cues[pCue->index];
/* Cue Instance Limits */
if (data->instanceCount >= data->instanceLimit)
{
wnr = NULL;
tmp = pCue->parentBank->cueList;
if (data->maxInstanceBehavior == 0) /* Fail */
{
pCue->state |= FACT_STATE_STOPPED;
pCue->state &= ~(
FACT_STATE_PLAYING |
FACT_STATE_STOPPING |
FACT_STATE_PAUSED
);
FAudio_PlatformUnlockMutex(
pCue->parentBank->parentEngine->apiLock
);
return 1;
}
else if (data->maxInstanceBehavior == 1) /* Queue */
{
/* FIXME: How is this different from Replace Oldest? */
while (tmp != NULL)
{
if ( tmp != pCue &&
tmp->index == pCue->index &&
!(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED)) )
{
wnr = tmp;
break;
}
tmp = tmp->next;
}
}
else if (data->maxInstanceBehavior == 2) /* Replace Oldest */
{
while (tmp != NULL)
{
if ( tmp != pCue &&
tmp->index == pCue->index &&
!(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED)) )
{
wnr = tmp;
break;
}
tmp = tmp->next;
}
}
else if (data->maxInstanceBehavior == 3) /* Replace Quietest */
{
limitmax.maxf = FACTVOLUME_MAX;
while (tmp != NULL)
{
if ( tmp != pCue &&
tmp->index == pCue->index &&
tmp->playingSound != NULL &&
/*FIXME: tmp->playingSound->volume < limitmax.maxf &&*/
!(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED)) )
{
wnr = tmp;
/* limitmax.maxf = tmp->playingSound->volume; */
}
tmp = tmp->next;
}
}
else if (data->maxInstanceBehavior == 4) /* Replace Lowest Priority */
{
limitmax.maxi = 0xFF;
while (tmp != NULL)
{
if ( tmp != pCue &&
tmp->index == pCue->index &&
tmp->playingSound != NULL &&
tmp->playingSound->sound->priority < limitmax.maxi &&
!(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED)) )
{
wnr = tmp;
limitmax.maxi = tmp->playingSound->sound->priority;
}
tmp = tmp->next;
}
}
if (wnr != NULL)
{
fadeInMS = data->fadeInMS;
if (wnr->playingSound != NULL)
{
FACT_INTERNAL_BeginFadeOut(wnr->playingSound, data->fadeOutMS);
}
else
{
FACTCue_Stop(wnr, 0);
}
}
}
/* Need an initial sound to play */
if (!FACT_INTERNAL_CreateSound(pCue, fadeInMS))
{
FAudio_PlatformUnlockMutex(
pCue->parentBank->parentEngine->apiLock
);
return 1;
}
data->instanceCount += 1;
pCue->state |= FACT_STATE_PLAYING;
pCue->state &= ~(
FACT_STATE_PAUSED |
FACT_STATE_STOPPED |
FACT_STATE_PREPARED
);
pCue->start = FAudio_timems();
/* If it's a simple wave, just play it! */
if (pCue->simpleWave != NULL)
{
if (pCue->active3D)
{
FACTWave_SetMatrixCoefficients(
pCue->simpleWave,
pCue->srcChannels,
pCue->dstChannels,
pCue->matrixCoefficients
);
}
FACTWave_Play(pCue->simpleWave);
}
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTCue_Stop(FACTCue *pCue, uint32_t dwFlags)
{
if (pCue == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
/* If we're already stopped, there's nothing to do... */
if (pCue->state & FACT_STATE_STOPPED)
{
FAudio_PlatformUnlockMutex(
pCue->parentBank->parentEngine->apiLock
);
return 0;
}
/* If we're stopping and we haven't asked for IMMEDIATE, we're already
* doing what the application is asking us to do...
*/
if ( (pCue->state & FACT_STATE_STOPPING) &&
!(dwFlags & FACT_FLAG_STOP_IMMEDIATE) )
{
FAudio_PlatformUnlockMutex(
pCue->parentBank->parentEngine->apiLock
);
return 0;
}
/* There are three ways that a Cue might be stopped immediately:
* 1. The program explicitly asks for it
* 2. The Cue is paused and therefore we can't do fade/release effects
* 3. The Cue is stopped "as authored" and has no fade effects
*/
if ( dwFlags & FACT_FLAG_STOP_IMMEDIATE ||
pCue->state & FACT_STATE_PAUSED ||
pCue->playingSound == NULL ||
( pCue->parentBank->cues[pCue->index].fadeOutMS == 0 &&
pCue->maxRpcReleaseTime == 0 ) )
{
pCue->start = 0;
pCue->elapsed = 0;
pCue->state |= FACT_STATE_STOPPED;
pCue->state &= ~(
FACT_STATE_PLAYING |
FACT_STATE_STOPPING |
FACT_STATE_PAUSED
);
if (pCue->simpleWave != NULL)
{
FACTWave_Destroy(pCue->simpleWave);
pCue->simpleWave = NULL;
pCue->data->instanceCount -= 1;
}
else if (pCue->playingSound != NULL)
{
FACT_INTERNAL_DestroySound(pCue->playingSound);
}
}
else
{
if (pCue->parentBank->cues[pCue->index].fadeOutMS > 0)
{
FACT_INTERNAL_BeginFadeOut(
pCue->playingSound,
pCue->parentBank->cues[pCue->index].fadeOutMS
);
}
else if (pCue->maxRpcReleaseTime > 0)
{
FACT_INTERNAL_BeginReleaseRPC(
pCue->playingSound,
pCue->maxRpcReleaseTime
);
}
pCue->state |= FACT_STATE_STOPPING;
}
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTCue_GetState(FACTCue *pCue, uint32_t *pdwState)
{
if (pCue == NULL)
{
*pdwState = 0;
return 1;
}
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
*pdwState = pCue->state;
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTCue_SetMatrixCoefficients(
FACTCue *pCue,
uint32_t uSrcChannelCount,
uint32_t uDstChannelCount,
float *pMatrixCoefficients
) {
uint8_t i;
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
/* See FACTCue.matrixCoefficients declaration */
FAudio_assert(uSrcChannelCount > 0 && uSrcChannelCount < 3);
FAudio_assert(uDstChannelCount > 0 && uDstChannelCount < 9);
/* Local storage */
pCue->srcChannels = uSrcChannelCount;
pCue->dstChannels = uDstChannelCount;
FAudio_memcpy(
pCue->matrixCoefficients,
pMatrixCoefficients,
sizeof(float) * uSrcChannelCount * uDstChannelCount
);
pCue->active3D = 1;
/* Apply to Waves if they exist */
if (pCue->simpleWave != NULL)
{
FACTWave_SetMatrixCoefficients(
pCue->simpleWave,
uSrcChannelCount,
uDstChannelCount,
pMatrixCoefficients
);
}
else if (pCue->playingSound != NULL)
{
for (i = 0; i < pCue->playingSound->sound->trackCount; i += 1)
{
if (pCue->playingSound->tracks[i].activeWave.wave != NULL)
{
FACTWave_SetMatrixCoefficients(
pCue->playingSound->tracks[i].activeWave.wave,
uSrcChannelCount,
uDstChannelCount,
pMatrixCoefficients
);
}
}
}
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
return 0;
}
uint16_t FACTCue_GetVariableIndex(
FACTCue *pCue,
const char *szFriendlyName
) {
uint16_t i;
if (pCue == NULL)
{
return FACTVARIABLEINDEX_INVALID;
}
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
for (i = 0; i < pCue->parentBank->parentEngine->variableCount; i += 1)
{
if ( FAudio_strcmp(szFriendlyName, pCue->parentBank->parentEngine->variableNames[i]) == 0 &&
pCue->parentBank->parentEngine->variables[i].accessibility & 0x04 )
{
FAudio_PlatformUnlockMutex(
pCue->parentBank->parentEngine->apiLock
);
return i;
}
}
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
return FACTVARIABLEINDEX_INVALID;
}
uint32_t FACTCue_SetVariable(
FACTCue *pCue,
uint16_t nIndex,
float nValue
) {
FACTVariable *var;
if (pCue == NULL)
{
return 1;
}
if (nIndex == FACTINDEX_INVALID)
{
return 1;
}
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
var = &pCue->parentBank->parentEngine->variables[nIndex];
FAudio_assert(var->accessibility & 0x01);
FAudio_assert(!(var->accessibility & 0x02));
FAudio_assert(var->accessibility & 0x04);
pCue->variableValues[nIndex] = FAudio_clamp(
nValue,
var->minValue,
var->maxValue
);
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTCue_GetVariable(
FACTCue *pCue,
uint16_t nIndex,
float *nValue
) {
FACTVariable *var;
if (pCue == NULL)
{
*nValue = 0.0f;
return 1;
}
if (nIndex == FACTINDEX_INVALID)
{
return 1;
}
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
var = &pCue->parentBank->parentEngine->variables[nIndex];
FAudio_assert(var->accessibility & 0x01);
FAudio_assert(var->accessibility & 0x04);
if (nIndex == 0) /* NumCueInstances */
{
*nValue = pCue->parentBank->cues[pCue->index].instanceCount;
}
else
{
*nValue = pCue->variableValues[nIndex];
}
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTCue_Pause(FACTCue *pCue, int32_t fPause)
{
uint8_t i;
if (pCue == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
/* "A stopping or stopped cue cannot be paused." */
if (pCue->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))
{
FAudio_PlatformUnlockMutex(
pCue->parentBank->parentEngine->apiLock
);
return 0;
}
/* Store elapsed time */
pCue->elapsed += FAudio_timems() - pCue->start;
/* All we do is set the flag, not much to see here */
if (fPause)
{
pCue->state |= FACT_STATE_PAUSED;
}
else
{
pCue->state &= ~FACT_STATE_PAUSED;
}
/* Pause the Waves */
if (pCue->simpleWave != NULL)
{
FACTWave_Pause(pCue->simpleWave, fPause);
}
else if (pCue->playingSound != NULL)
{
for (i = 0; i < pCue->playingSound->sound->trackCount; i += 1)
{
if (pCue->playingSound->tracks[i].activeWave.wave != NULL)
{
FACTWave_Pause(
pCue->playingSound->tracks[i].activeWave.wave,
fPause
);
}
}
}
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
return 0;
}
uint32_t FACTCue_GetProperties(
FACTCue *pCue,
FACTCueInstanceProperties **ppProperties
) {
uint32_t i;
size_t allocSize;
FACTCueInstanceProperties *cueProps;
FACTVariationProperties *varProps;
FACTSoundProperties *sndProps;
FACTWaveInstanceProperties waveProps;
if (pCue == NULL)
{
return 1;
}
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
/* Alloc container (including variable length array space) */
allocSize = sizeof(FACTCueInstanceProperties);
if (pCue->playingSound != NULL)
{
allocSize += (
sizeof(FACTTrackProperties) *
pCue->playingSound->sound->trackCount
);
}
cueProps = (FACTCueInstanceProperties*) pCue->parentBank->parentEngine->pMalloc(
allocSize
);
FAudio_zero(cueProps, allocSize);
/* Cue Properties */
FACTSoundBank_GetCueProperties(
pCue->parentBank,
pCue->index,
&cueProps->cueProperties
);
/* Variation Properties */
varProps = &cueProps->activeVariationProperties.variationProperties;
if (pCue->playingVariation != NULL)
{
varProps->index = 0; /* TODO: Index of what...? */
/* TODO: This is just max - min right? Also why u8 wtf */
varProps->weight = (uint8_t) (
pCue->playingVariation->maxWeight -
pCue->playingVariation->minWeight
);
if (pCue->variation->flags == 3)
{
varProps->iaVariableMin =
pCue->playingVariation->minWeight;
varProps->iaVariableMax =
pCue->playingVariation->maxWeight;
}
else
{
varProps->iaVariableMin = 0;
varProps->iaVariableMax = 0;
}
varProps->linger = pCue->playingVariation->linger;
}
/* Sound Properties */
sndProps = &cueProps->activeVariationProperties.soundProperties;
if (pCue->playingSound != NULL)
{
sndProps->category = pCue->playingSound->sound->category;
sndProps->priority = pCue->playingSound->sound->priority;
sndProps->pitch = pCue->playingSound->sound->pitch;
sndProps->volume = pCue->playingSound->sound->volume;
sndProps->numTracks = pCue->playingSound->sound->trackCount;
for (i = 0; i < sndProps->numTracks; i += 1)
{
if (FACTWave_GetProperties(
pCue->playingSound->tracks[i].activeWave.wave,
&waveProps
) == 0) {
sndProps->arrTrackProperties[i].duration = (uint32_t) (
(
(float) waveProps.properties.durationInSamples /
(float) waveProps.properties.format.nSamplesPerSec
) / 1000.0f
);
sndProps->arrTrackProperties[i].numVariations = 1; /* ? */
sndProps->arrTrackProperties[i].numChannels =
waveProps.properties.format.nChannels;
sndProps->arrTrackProperties[i].waveVariation = 0; /* ? */
sndProps->arrTrackProperties[i].loopCount =
pCue->playingSound->tracks[i].waveEvt->wave.loopCount;
}
}
}
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
*ppProperties = cueProps;
return 0;
}
uint32_t FACTCue_SetOutputVoices(
FACTCue *pCue,
const FAudioVoiceSends *pSendList /* Optional XAUDIO2_VOICE_SENDS */
) {
/* TODO */
return 0;
}
uint32_t FACTCue_SetOutputVoiceMatrix(
FACTCue *pCue,
const FAudioVoice *pDestinationVoice, /* Optional! */
uint32_t SourceChannels,
uint32_t DestinationChannels,
const float *pLevelMatrix /* SourceChannels * DestinationChannels */
) {
/* TODO */
return 0;
}
/* vim: set noexpandtab shiftwidth=8 tabstop=8: */