Commit Description:
Various UI improvements.
Commit Description:
Various UI improvements.
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: */