faudio: Import upstream release 24.05.
[wine.git] / libs / faudio / src / FACT.c
blob013cea36b5b32ac7e55968e2ae91b35550597914
1 /* FAudio - XAudio Reimplementation for FNA
3 * Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team
5 * This software is provided 'as-is', without any express or implied warranty.
6 * In no event will the authors be held liable for any damages arising from
7 * the use of this software.
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software in a
15 * product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
18 * 2. Altered source versions must be plainly marked as such, and must not be
19 * misrepresented as being the original software.
21 * 3. This notice may not be removed or altered from any source distribution.
23 * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
27 #include "FAudioFX.h"
28 #include "FACT_internal.h"
30 /* AudioEngine implementation */
32 uint32_t FACTCreateEngine(
33 uint32_t dwCreationFlags,
34 FACTAudioEngine **ppEngine
35 ) {
36 return FACTCreateEngineWithCustomAllocatorEXT(
37 dwCreationFlags,
38 ppEngine,
39 FAudio_malloc,
40 FAudio_free,
41 FAudio_realloc
45 uint32_t FACTCreateEngineWithCustomAllocatorEXT(
46 uint32_t dwCreationFlags,
47 FACTAudioEngine **ppEngine,
48 FAudioMallocFunc customMalloc,
49 FAudioFreeFunc customFree,
50 FAudioReallocFunc customRealloc
51 ) {
52 /* TODO: Anything fun with dwCreationFlags? */
53 FAudio_PlatformAddRef();
54 *ppEngine = (FACTAudioEngine*) customMalloc(sizeof(FACTAudioEngine));
55 if (*ppEngine == NULL)
57 return -1; /* TODO: E_OUTOFMEMORY */
59 FAudio_zero(*ppEngine, sizeof(FACTAudioEngine));
60 (*ppEngine)->sbLock = FAudio_PlatformCreateMutex();
61 (*ppEngine)->wbLock = FAudio_PlatformCreateMutex();
62 (*ppEngine)->apiLock = FAudio_PlatformCreateMutex();
63 (*ppEngine)->pMalloc = customMalloc;
64 (*ppEngine)->pFree = customFree;
65 (*ppEngine)->pRealloc = customRealloc;
66 (*ppEngine)->refcount = 1;
67 return 0;
70 uint32_t FACTAudioEngine_AddRef(FACTAudioEngine *pEngine)
72 FAudio_PlatformLockMutex(pEngine->apiLock);
73 pEngine->refcount += 1;
74 FAudio_PlatformUnlockMutex(pEngine->apiLock);
75 return pEngine->refcount;
78 uint32_t FACTAudioEngine_Release(FACTAudioEngine *pEngine)
80 FAudio_PlatformLockMutex(pEngine->apiLock);
81 pEngine->refcount -= 1;
82 if (pEngine->refcount > 0)
84 FAudio_PlatformUnlockMutex(pEngine->apiLock);
85 return pEngine->refcount;
87 FACTAudioEngine_ShutDown(pEngine);
88 FAudio_PlatformDestroyMutex(pEngine->sbLock);
89 FAudio_PlatformDestroyMutex(pEngine->wbLock);
90 FAudio_PlatformUnlockMutex(pEngine->apiLock);
91 FAudio_PlatformDestroyMutex(pEngine->apiLock);
92 if (pEngine->settings != NULL)
94 pEngine->pFree(pEngine->settings);
96 pEngine->pFree(pEngine);
97 FAudio_PlatformRelease();
98 return 0;
101 uint32_t FACTAudioEngine_GetRendererCount(
102 FACTAudioEngine *pEngine,
103 uint16_t *pnRendererCount
105 FAudio_PlatformLockMutex(pEngine->apiLock);
106 *pnRendererCount = (uint16_t) FAudio_PlatformGetDeviceCount();
107 FAudio_PlatformUnlockMutex(pEngine->apiLock);
108 return 0;
111 uint32_t FACTAudioEngine_GetRendererDetails(
112 FACTAudioEngine *pEngine,
113 uint16_t nRendererIndex,
114 FACTRendererDetails *pRendererDetails
116 FAudioDeviceDetails deviceDetails;
118 FAudio_PlatformLockMutex(pEngine->apiLock);
120 FAudio_PlatformGetDeviceDetails(
121 nRendererIndex,
122 &deviceDetails
124 FAudio_memcpy(
125 pRendererDetails->rendererID,
126 deviceDetails.DeviceID,
127 sizeof(int16_t) * 0xFF
129 FAudio_memcpy(
130 pRendererDetails->displayName,
131 deviceDetails.DisplayName,
132 sizeof(int16_t) * 0xFF
134 /* FIXME: Which defaults does it care about...? */
135 pRendererDetails->defaultDevice = (deviceDetails.Role & (
136 FAudioGlobalDefaultDevice |
137 FAudioDefaultGameDevice
138 )) != 0;
140 FAudio_PlatformUnlockMutex(pEngine->apiLock);
141 return 0;
144 uint32_t FACTAudioEngine_GetFinalMixFormat(
145 FACTAudioEngine *pEngine,
146 FAudioWaveFormatExtensible *pFinalMixFormat
148 FAudio_PlatformLockMutex(pEngine->apiLock);
149 FAudio_memcpy(
150 pFinalMixFormat,
151 &pEngine->audio->mixFormat,
152 sizeof(FAudioWaveFormatExtensible)
154 FAudio_PlatformUnlockMutex(pEngine->apiLock);
155 return 0;
158 uint32_t FACTAudioEngine_Initialize(
159 FACTAudioEngine *pEngine,
160 const FACTRuntimeParameters *pParams
162 uint32_t parseRet;
163 uint32_t deviceIndex;
164 FAudioVoiceDetails masterDetails;
165 FAudioEffectDescriptor reverbDesc;
166 FAudioEffectChain reverbChain;
168 FAudio_PlatformLockMutex(pEngine->apiLock);
170 if (!pParams->pGlobalSettingsBuffer || pParams->globalSettingsBufferSize == 0)
172 /* No file? Just go with a safe default. (Also why are you using XACT) */
173 pEngine->categoryCount = 3;
174 pEngine->variableCount = 0;
175 pEngine->rpcCount = 0;
176 pEngine->dspPresetCount = 0;
177 pEngine->dspParameterCount = 0;
179 pEngine->categories = (FACTAudioCategory*) pEngine->pMalloc(
180 sizeof(FACTAudioCategory) * pEngine->categoryCount
182 pEngine->categoryNames = (char**) pEngine->pMalloc(
183 sizeof(char*) * pEngine->categoryCount
186 pEngine->categoryNames[0] = pEngine->pMalloc(7);
187 FAudio_strlcpy(pEngine->categoryNames[0], "Global", 7);
188 pEngine->categories[0].instanceLimit = 255;
189 pEngine->categories[0].fadeInMS = 0;
190 pEngine->categories[0].fadeOutMS = 0;
191 pEngine->categories[0].maxInstanceBehavior = 0;
192 pEngine->categories[0].parentCategory = -1;
193 pEngine->categories[0].volume = 1.0f;
194 pEngine->categories[0].visibility = 1;
195 pEngine->categories[0].instanceCount = 0;
196 pEngine->categories[0].currentVolume = 1.0f;
198 pEngine->categoryNames[1] = pEngine->pMalloc(8);
199 FAudio_strlcpy(pEngine->categoryNames[1], "Default", 8);
200 pEngine->categories[1].instanceLimit = 255;
201 pEngine->categories[1].fadeInMS = 0;
202 pEngine->categories[1].fadeOutMS = 0;
203 pEngine->categories[1].maxInstanceBehavior = 0;
204 pEngine->categories[1].parentCategory = 0;
205 pEngine->categories[1].volume = 1.0f;
206 pEngine->categories[1].visibility = 1;
207 pEngine->categories[1].instanceCount = 0;
208 pEngine->categories[1].currentVolume = 1.0f;
210 pEngine->categoryNames[2] = pEngine->pMalloc(6);
211 FAudio_strlcpy(pEngine->categoryNames[2], "Music", 6);
212 pEngine->categories[2].instanceLimit = 255;
213 pEngine->categories[2].fadeInMS = 0;
214 pEngine->categories[2].fadeOutMS = 0;
215 pEngine->categories[2].maxInstanceBehavior = 0;
216 pEngine->categories[2].parentCategory = 0;
217 pEngine->categories[2].volume = 1.0f;
218 pEngine->categories[2].visibility = 1;
219 pEngine->categories[2].instanceCount = 0;
220 pEngine->categories[2].currentVolume = 1.0f;
222 pEngine->variables = NULL;
223 pEngine->variableNames = NULL;
224 pEngine->globalVariableValues = NULL;
225 pEngine->rpcs = NULL;
226 pEngine->dspPresets = NULL;
228 else
230 /* Parse the file */
231 parseRet = FACT_INTERNAL_ParseAudioEngine(pEngine, pParams);
232 if (parseRet != 0)
234 FAudio_PlatformUnlockMutex(pEngine->apiLock);
235 return parseRet;
239 /* Peristent Notifications */
240 pEngine->notifications = 0;
241 pEngine->cue_context = NULL;
242 pEngine->sb_context = NULL;
243 pEngine->wb_context = NULL;
244 pEngine->wave_context = NULL;
246 /* Assign the callbacks */
247 pEngine->notificationCallback = pParams->fnNotificationCallback;
248 pEngine->pReadFile = pParams->fileIOCallbacks.readFileCallback;
249 pEngine->pGetOverlappedResult = pParams->fileIOCallbacks.getOverlappedResultCallback;
250 if (pEngine->pReadFile == NULL)
252 pEngine->pReadFile = FACT_INTERNAL_DefaultReadFile;
254 if (pEngine->pGetOverlappedResult == NULL)
256 pEngine->pGetOverlappedResult = FACT_INTERNAL_DefaultGetOverlappedResult;
259 /* Init the FAudio subsystem */
260 pEngine->audio = pParams->pXAudio2;
261 if (pEngine->audio == NULL)
263 FAudio_assert(pParams->pMasteringVoice == NULL);
264 FAudioCreate(&pEngine->audio, 0, FAUDIO_DEFAULT_PROCESSOR);
267 /* Create the audio device */
268 pEngine->master = pParams->pMasteringVoice;
269 if (pEngine->master == NULL)
271 if (pParams->pRendererID == NULL || pParams->pRendererID[0] == 0)
273 deviceIndex = 0;
275 else
277 deviceIndex = pParams->pRendererID[0] - L'0';
278 if (deviceIndex > FAudio_PlatformGetDeviceCount())
280 deviceIndex = 0;
283 if (FAudio_CreateMasteringVoice(
284 pEngine->audio,
285 &pEngine->master,
286 FAUDIO_DEFAULT_CHANNELS,
287 FAUDIO_DEFAULT_SAMPLERATE,
289 deviceIndex,
290 NULL
291 ) != 0) {
292 FAudio_Release(pEngine->audio);
293 FAudio_PlatformUnlockMutex(pEngine->apiLock);
294 return FAUDIO_E_INVALID_CALL;
298 /* Create the reverb effect, if applicable */
299 if (pEngine->dspPresetCount > 0) /* Never more than 1...? */
301 FAudioVoice_GetVoiceDetails(pEngine->master, &masterDetails);
303 /* Reverb effect chain... */
304 FAudioCreateReverb(&reverbDesc.pEffect, 0);
305 reverbDesc.InitialState = 1;
306 reverbDesc.OutputChannels = (masterDetails.InputChannels == 6) ? 6 : 1;
307 reverbChain.EffectCount = 1;
308 reverbChain.pEffectDescriptors = &reverbDesc;
310 /* Reverb submix voice... */
311 FAudio_CreateSubmixVoice(
312 pEngine->audio,
313 &pEngine->reverbVoice,
314 1, /* Reverb will be omnidirectional */
315 masterDetails.InputSampleRate,
318 NULL,
319 &reverbChain
322 /* We can release now, the submix owns this! */
323 FAPOBase_Release((FAPOBase*) reverbDesc.pEffect);
326 pEngine->initialized = 1;
327 pEngine->apiThread = FAudio_PlatformCreateThread(
328 FACT_INTERNAL_APIThread,
329 "FACT Thread",
330 pEngine
333 FAudio_PlatformUnlockMutex(pEngine->apiLock);
334 return 0;
337 uint32_t FACTAudioEngine_ShutDown(FACTAudioEngine *pEngine)
339 uint32_t i, refcount;
340 FAudioMutex mutex;
341 FAudioMallocFunc pMalloc;
342 FAudioFreeFunc pFree;
343 FAudioReallocFunc pRealloc;
345 /* Close thread, then lock ASAP */
346 pEngine->initialized = 0;
347 FAudio_PlatformWaitThread(pEngine->apiThread, NULL);
348 FAudio_PlatformLockMutex(pEngine->apiLock);
350 /* Stop the platform stream before freeing stuff! */
351 if (pEngine->audio != NULL)
353 FAudio_StopEngine(pEngine->audio);
356 /* Purge All pending notifactions */
357 while (pEngine->wb_notifications_list)
359 FACTNotification *note = (FACTNotification*) pEngine->wb_notifications_list->entry;
360 pEngine->notificationCallback(note);
361 LinkedList_RemoveEntry(&pEngine->wb_notifications_list, note, pEngine->apiLock, pEngine->pFree);
364 pEngine->notifications = 0;
366 /* This method destroys all existing cues, sound banks, and wave banks.
367 * It blocks until all cues are destroyed.
369 while (pEngine->wbList != NULL)
371 FACTWaveBank_Destroy((FACTWaveBank*) pEngine->wbList->entry);
373 while (pEngine->sbList != NULL)
375 FACTSoundBank_Destroy((FACTSoundBank*) pEngine->sbList->entry);
378 /* Category data */
379 for (i = 0; i < pEngine->categoryCount; i += 1)
381 pEngine->pFree(pEngine->categoryNames[i]);
383 pEngine->pFree(pEngine->categoryNames);
384 pEngine->pFree(pEngine->categories);
386 /* Variable data */
387 for (i = 0; i < pEngine->variableCount; i += 1)
389 pEngine->pFree(pEngine->variableNames[i]);
391 pEngine->pFree(pEngine->variableNames);
392 pEngine->pFree(pEngine->variables);
393 pEngine->pFree(pEngine->globalVariableValues);
395 /* RPC data */
396 for (i = 0; i < pEngine->rpcCount; i += 1)
398 pEngine->pFree(pEngine->rpcs[i].points);
400 pEngine->pFree(pEngine->rpcs);
401 pEngine->pFree(pEngine->rpcCodes);
403 /* DSP data */
404 for (i = 0; i < pEngine->dspPresetCount; i += 1)
406 pEngine->pFree(pEngine->dspPresets[i].parameters);
408 pEngine->pFree(pEngine->dspPresets);
409 pEngine->pFree(pEngine->dspPresetCodes);
411 /* Audio resources */
412 if (pEngine->reverbVoice != NULL)
414 FAudioVoice_DestroyVoice(pEngine->reverbVoice);
416 if (pEngine->master != NULL)
418 FAudioVoice_DestroyVoice(pEngine->master);
420 if (pEngine->audio != NULL)
422 FAudio_Release(pEngine->audio);
425 /* Finally. */
426 refcount = pEngine->refcount;
427 mutex = pEngine->apiLock;
428 pMalloc = pEngine->pMalloc;
429 pFree = pEngine->pFree;
430 pRealloc = pEngine->pRealloc;
431 FAudio_zero(pEngine, sizeof(FACTAudioEngine));
432 pEngine->pMalloc = pMalloc;
433 pEngine->pFree = pFree;
434 pEngine->pRealloc = pRealloc;
435 pEngine->refcount = refcount;
436 pEngine->apiLock = mutex;
438 FAudio_PlatformUnlockMutex(pEngine->apiLock);
439 return 0;
442 uint32_t FACTAudioEngine_DoWork(FACTAudioEngine *pEngine)
444 uint8_t i;
445 FACTCue *cue;
446 LinkedList *list;
447 FACTNotification *note;
449 FAudio_PlatformLockMutex(pEngine->apiLock);
451 while (pEngine->wb_notifications_list)
453 note = (FACTNotification*) pEngine->wb_notifications_list->entry;
454 pEngine->notificationCallback(note);
455 LinkedList_RemoveEntry(&pEngine->wb_notifications_list, note, pEngine->apiLock, pEngine->pFree);
458 list = pEngine->sbList;
459 while (list != NULL)
461 cue = ((FACTSoundBank*) list->entry)->cueList;
462 while (cue != NULL)
464 if (cue->playingSound != NULL)
465 for (i = 0; i < cue->playingSound->sound->trackCount; i += 1)
467 if ( cue->playingSound->tracks[i].upcomingWave.wave == NULL &&
468 cue->playingSound->tracks[i].waveEvtInst->loopCount > 0 )
470 FACT_INTERNAL_GetNextWave(
471 cue,
472 cue->playingSound->sound,
473 &cue->playingSound->sound->tracks[i],
474 &cue->playingSound->tracks[i],
475 cue->playingSound->tracks[i].waveEvt,
476 cue->playingSound->tracks[i].waveEvtInst
480 cue = cue->next;
482 list = list->next;
485 FAudio_PlatformUnlockMutex(pEngine->apiLock);
486 return 0;
489 uint32_t FACTAudioEngine_CreateSoundBank(
490 FACTAudioEngine *pEngine,
491 const void *pvBuffer,
492 uint32_t dwSize,
493 uint32_t dwFlags,
494 uint32_t dwAllocAttributes,
495 FACTSoundBank **ppSoundBank
497 uint32_t retval;
498 FAudio_PlatformLockMutex(pEngine->apiLock);
499 retval = FACT_INTERNAL_ParseSoundBank(
500 pEngine,
501 pvBuffer,
502 dwSize,
503 ppSoundBank
505 FAudio_PlatformUnlockMutex(pEngine->apiLock);
506 return retval;
509 uint32_t FACTAudioEngine_CreateInMemoryWaveBank(
510 FACTAudioEngine *pEngine,
511 const void *pvBuffer,
512 uint32_t dwSize,
513 uint32_t dwFlags,
514 uint32_t dwAllocAttributes,
515 FACTWaveBank **ppWaveBank
517 FACTNotification *note;
518 uint32_t retval;
519 FAudio_PlatformLockMutex(pEngine->apiLock);
520 retval = FACT_INTERNAL_ParseWaveBank(
521 pEngine,
522 FAudio_memopen((void*) pvBuffer, dwSize),
525 FACT_INTERNAL_DefaultReadFile,
526 FACT_INTERNAL_DefaultGetOverlappedResult,
528 ppWaveBank
530 if (pEngine->notifications & NOTIFY_WAVEBANKPREPARED)
532 note = (FACTNotification*) pEngine->pMalloc(sizeof(FACTNotification));
533 note->type = FACTNOTIFICATIONTYPE_WAVEBANKPREPARED;
534 note->waveBank.pWaveBank = *ppWaveBank;
535 note->pvContext = pEngine->wb_context;
536 LinkedList_AddEntry(&pEngine->wb_notifications_list, note, pEngine->apiLock, pEngine->pMalloc);
538 FAudio_PlatformUnlockMutex(pEngine->apiLock);
539 return retval;
542 uint32_t FACTAudioEngine_CreateStreamingWaveBank(
543 FACTAudioEngine *pEngine,
544 const FACTStreamingParameters *pParms,
545 FACTWaveBank **ppWaveBank
547 FACTNotification *note;
548 uint32_t retval, packetSize;
549 FAudio_PlatformLockMutex(pEngine->apiLock);
550 if ( pEngine->pReadFile == FACT_INTERNAL_DefaultReadFile &&
551 pEngine->pGetOverlappedResult == FACT_INTERNAL_DefaultGetOverlappedResult )
553 /* Our I/O doesn't care about packets, set to 0 as an optimization */
554 packetSize = 0;
556 else
558 packetSize = pParms->packetSize * 2048;
560 retval = FACT_INTERNAL_ParseWaveBank(
561 pEngine,
562 pParms->file,
563 pParms->offset,
564 packetSize,
565 pEngine->pReadFile,
566 pEngine->pGetOverlappedResult,
568 ppWaveBank
570 if (pEngine->notifications & NOTIFY_WAVEBANKPREPARED)
572 note = (FACTNotification*) pEngine->pMalloc(sizeof(FACTNotification));
573 note->type = FACTNOTIFICATIONTYPE_WAVEBANKPREPARED;
574 note->waveBank.pWaveBank = *ppWaveBank;
575 note->pvContext = pEngine->wb_context;
576 LinkedList_AddEntry(&pEngine->wb_notifications_list, note, pEngine->apiLock, pEngine->pMalloc);
578 FAudio_PlatformUnlockMutex(pEngine->apiLock);
579 return retval;
582 uint32_t FACTAudioEngine_PrepareWave(
583 FACTAudioEngine *pEngine,
584 uint32_t dwFlags,
585 const char *szWavePath,
586 uint32_t wStreamingPacketSize,
587 uint32_t dwAlignment,
588 uint32_t dwPlayOffset,
589 uint8_t nLoopCount,
590 FACTWave **ppWave
592 /* TODO: FACTWave */
593 return 0;
596 uint32_t FACTAudioEngine_PrepareInMemoryWave(
597 FACTAudioEngine *pEngine,
598 uint32_t dwFlags,
599 FACTWaveBankEntry entry,
600 uint32_t *pdwSeekTable, /* Optional! */
601 uint8_t *pbWaveData,
602 uint32_t dwPlayOffset,
603 uint8_t nLoopCount,
604 FACTWave **ppWave
606 /* TODO: FACTWave */
607 return 0;
610 uint32_t FACTAudioEngine_PrepareStreamingWave(
611 FACTAudioEngine *pEngine,
612 uint32_t dwFlags,
613 FACTWaveBankEntry entry,
614 FACTStreamingParameters streamingParams,
615 uint32_t dwAlignment,
616 uint32_t *pdwSeekTable, /* Optional! */
617 uint8_t *pbWaveData, /* ABI bug, do not use! */
618 uint32_t dwPlayOffset,
619 uint8_t nLoopCount,
620 FACTWave **ppWave
622 /* TODO: FACTWave */
623 return 0;
626 uint32_t FACTAudioEngine_RegisterNotification(
627 FACTAudioEngine *pEngine,
628 const FACTNotificationDescription *pNotificationDescription
630 FAudio_assert(pEngine != NULL);
631 FAudio_assert(pNotificationDescription != NULL);
632 FAudio_assert(pEngine->notificationCallback != NULL);
634 FAudio_PlatformLockMutex(pEngine->apiLock);
636 #define HANDLE_PERSIST(nt) \
637 if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_##nt) \
639 if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST) \
641 pEngine->notifications |= NOTIFY_##nt; \
642 PERSIST_ACTION \
644 else \
646 FAudio_assert(0 && "TODO: "#nt" notification!"); \
650 /* Cues */
651 #define PERSIST_ACTION pEngine->cue_context = pNotificationDescription->pvContext;
652 HANDLE_PERSIST(CUEPREPARED)
653 else HANDLE_PERSIST(CUEPLAY)
654 else HANDLE_PERSIST(CUESTOP)
655 else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_CUEDESTROYED)
657 if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
659 pEngine->notifications |= NOTIFY_CUEDESTROY;
660 pEngine->cue_context = pNotificationDescription->pvContext;
662 else
664 pNotificationDescription->pCue->notifyOnDestroy = 1;
665 pNotificationDescription->pCue->usercontext = pNotificationDescription->pvContext;
668 #undef PERSIST_ACTION
670 /* Markers */
671 #define PERSIST_ACTION
672 else HANDLE_PERSIST(MARKER)
673 #undef PERSIST_ACTION
675 /* SoundBank/WaveBank Destruction */
676 else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED)
678 if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
680 pEngine->notifications |= NOTIFY_SOUNDBANKDESTROY;
681 pEngine->sb_context = pNotificationDescription->pvContext;
683 else
685 pNotificationDescription->pSoundBank->notifyOnDestroy = 1;
686 pNotificationDescription->pSoundBank->usercontext = pNotificationDescription->pvContext;
689 else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED)
691 if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
693 pEngine->notifications |= NOTIFY_WAVEBANKDESTROY;
694 pEngine->wb_context = pNotificationDescription->pvContext;
696 else
698 pNotificationDescription->pWaveBank->notifyOnDestroy = 1;
699 pNotificationDescription->pWaveBank->usercontext = pNotificationDescription->pvContext;
703 /* Variables, Auditioning Tool */
704 #define PERSIST_ACTION
705 else HANDLE_PERSIST(LOCALVARIABLECHANGED)
706 else HANDLE_PERSIST(GLOBALVARIABLECHANGED)
707 else HANDLE_PERSIST(GUICONNECTED)
708 else HANDLE_PERSIST(GUIDISCONNECTED)
709 #undef PERSIST_ACTION
711 /* Waves */
712 #define PERSIST_ACTION pEngine->wave_context = pNotificationDescription->pvContext;
713 else HANDLE_PERSIST(WAVEPREPARED)
714 else HANDLE_PERSIST(WAVEPLAY)
715 else HANDLE_PERSIST(WAVESTOP)
716 else HANDLE_PERSIST(WAVELOOPED)
717 else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_WAVEDESTROYED)
719 if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
721 pEngine->notifications |= NOTIFY_WAVEDESTROY;
722 pEngine->wave_context = pNotificationDescription->pvContext;
724 else
726 pNotificationDescription->pWave->notifyOnDestroy = 1;
727 pNotificationDescription->pWave->usercontext = pNotificationDescription->pvContext;
730 #undef PERSIST_ACTION
732 /* WaveBanks */
733 #define PERSIST_ACTION pEngine->wb_context = pNotificationDescription->pvContext;
734 else HANDLE_PERSIST(WAVEBANKPREPARED)
735 else HANDLE_PERSIST(WAVEBANKSTREAMING_INVALIDCONTENT)
736 #undef PERSIST_ACTION
738 /* Anything else? */
739 else
741 FAudio_assert(0 && "TODO: Unimplemented notification!");
744 #undef HANDLE_PERSIST
746 FAudio_PlatformUnlockMutex(pEngine->apiLock);
747 return 0;
750 uint32_t FACTAudioEngine_UnRegisterNotification(
751 FACTAudioEngine *pEngine,
752 const FACTNotificationDescription *pNotificationDescription
754 FAudio_assert(pEngine != NULL);
755 FAudio_assert(pNotificationDescription != NULL);
756 FAudio_assert(pEngine->notificationCallback != NULL);
758 FAudio_PlatformLockMutex(pEngine->apiLock);
760 #define HANDLE_PERSIST(nt) \
761 if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_##nt) \
763 if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST) \
765 pEngine->notifications &= ~NOTIFY_##nt; \
766 PERSIST_ACTION \
768 else \
770 FAudio_assert(0 && "TODO: "#nt" notification!"); \
774 /* Cues */
775 #define PERSIST_ACTION pEngine->cue_context = pNotificationDescription->pvContext;
776 HANDLE_PERSIST(CUEPREPARED)
777 else HANDLE_PERSIST(CUEPLAY)
778 else HANDLE_PERSIST(CUESTOP)
779 else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_CUEDESTROYED)
781 if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
783 pEngine->notifications &= ~NOTIFY_CUEDESTROY;
784 pEngine->cue_context = pNotificationDescription->pvContext;
786 else
788 pNotificationDescription->pCue->notifyOnDestroy = 0;
789 pNotificationDescription->pCue->usercontext = pNotificationDescription->pvContext;
792 #undef PERSIST_ACTION
794 /* Markers */
795 #define PERSIST_ACTION
796 else HANDLE_PERSIST(MARKER)
797 #undef PERSIST_ACTION
799 /* SoundBank/WaveBank Destruction */
800 else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED)
802 if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
804 pEngine->notifications &= ~NOTIFY_SOUNDBANKDESTROY;
805 pEngine->sb_context = pNotificationDescription->pvContext;
807 else
809 pNotificationDescription->pSoundBank->notifyOnDestroy = 0;
810 pNotificationDescription->pSoundBank->usercontext = pNotificationDescription->pvContext;
813 else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED)
815 if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
817 pEngine->notifications &= ~NOTIFY_WAVEBANKDESTROY;
818 pEngine->wb_context = pNotificationDescription->pvContext;
820 else
822 pNotificationDescription->pWaveBank->notifyOnDestroy = 0;
823 pNotificationDescription->pWaveBank->usercontext = pNotificationDescription->pvContext;
827 /* Variables, Auditioning Tool */
828 #define PERSIST_ACTION
829 else HANDLE_PERSIST(LOCALVARIABLECHANGED)
830 else HANDLE_PERSIST(GLOBALVARIABLECHANGED)
831 else HANDLE_PERSIST(GUICONNECTED)
832 else HANDLE_PERSIST(GUIDISCONNECTED)
833 #undef PERSIST_ACTION
835 /* Waves */
836 #define PERSIST_ACTION pEngine->wave_context = pNotificationDescription->pvContext;
837 else HANDLE_PERSIST(WAVEPREPARED)
838 else HANDLE_PERSIST(WAVEPLAY)
839 else HANDLE_PERSIST(WAVESTOP)
840 else HANDLE_PERSIST(WAVELOOPED)
841 else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_WAVEDESTROYED)
843 if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
845 pEngine->notifications &= ~NOTIFY_WAVEDESTROY;
846 pEngine->wave_context = pNotificationDescription->pvContext;
848 else
850 pNotificationDescription->pWave->notifyOnDestroy = 0;
851 pNotificationDescription->pWave->usercontext = pNotificationDescription->pvContext;
854 #undef PERSIST_ACTION
856 /* WaveBanks */
857 #define PERSIST_ACTION pEngine->wb_context = pNotificationDescription->pvContext;
858 else HANDLE_PERSIST(WAVEBANKPREPARED)
859 else HANDLE_PERSIST(WAVEBANKSTREAMING_INVALIDCONTENT)
860 #undef PERSIST_ACTION
862 /* Anything else? */
863 else
865 FAudio_assert(0 && "TODO: Unimplemented notification!");
868 #undef HANDLE_PERSIST
870 FAudio_PlatformUnlockMutex(pEngine->apiLock);
871 return 0;
874 uint16_t FACTAudioEngine_GetCategory(
875 FACTAudioEngine *pEngine,
876 const char *szFriendlyName
878 uint16_t i;
879 FAudio_PlatformLockMutex(pEngine->apiLock);
880 for (i = 0; i < pEngine->categoryCount; i += 1)
882 if (FAudio_strcmp(szFriendlyName, pEngine->categoryNames[i]) == 0)
884 FAudio_PlatformUnlockMutex(pEngine->apiLock);
885 return i;
888 FAudio_PlatformUnlockMutex(pEngine->apiLock);
889 return FACTCATEGORY_INVALID;
892 uint8_t FACT_INTERNAL_IsInCategory(
893 FACTAudioEngine *engine,
894 uint16_t target,
895 uint16_t category
897 FACTAudioCategory *cat;
899 /* Same category, no need to go on a crazy hunt */
900 if (category == target)
902 return 1;
905 /* Right, on with the crazy hunt */
906 cat = &engine->categories[category];
907 while (cat->parentCategory != -1)
909 if (cat->parentCategory == target)
911 return 1;
913 cat = &engine->categories[cat->parentCategory];
915 return 0;
918 uint32_t FACTAudioEngine_Stop(
919 FACTAudioEngine *pEngine,
920 uint16_t nCategory,
921 uint32_t dwFlags
923 FACTCue *cue, *backup;
924 LinkedList *list;
926 FAudio_PlatformLockMutex(pEngine->apiLock);
927 list = pEngine->sbList;
928 while (list != NULL)
930 cue = ((FACTSoundBank*) list->entry)->cueList;
931 while (cue != NULL)
933 if ( cue->playingSound != NULL &&
934 FACT_INTERNAL_IsInCategory(
935 pEngine,
936 nCategory,
937 cue->playingSound->sound->category
940 if ( dwFlags == FACT_FLAG_STOP_IMMEDIATE &&
941 cue->managed )
943 /* Just blow this up now */
944 backup = cue->next;
945 FACTCue_Destroy(cue);
946 cue = backup;
948 else
950 /* If managed, the mixer will destroy for us */
951 FACTCue_Stop(cue, dwFlags);
952 cue = cue->next;
955 else
957 cue = cue->next;
960 list = list->next;
962 FAudio_PlatformUnlockMutex(pEngine->apiLock);
963 return 0;
966 uint32_t FACTAudioEngine_SetVolume(
967 FACTAudioEngine *pEngine,
968 uint16_t nCategory,
969 float volume
971 uint16_t i;
972 FAudio_PlatformLockMutex(pEngine->apiLock);
973 pEngine->categories[nCategory].currentVolume = (
974 pEngine->categories[nCategory].volume *
975 volume
977 for (i = 0; i < pEngine->categoryCount; i += 1)
979 if (pEngine->categories[i].parentCategory == nCategory)
981 FACTAudioEngine_SetVolume(
982 pEngine,
984 pEngine->categories[i].currentVolume
988 FAudio_PlatformUnlockMutex(pEngine->apiLock);
989 return 0;
992 uint32_t FACTAudioEngine_Pause(
993 FACTAudioEngine *pEngine,
994 uint16_t nCategory,
995 int32_t fPause
997 FACTCue *cue;
998 LinkedList *list;
1000 FAudio_PlatformLockMutex(pEngine->apiLock);
1001 list = pEngine->sbList;
1002 while (list != NULL)
1004 cue = ((FACTSoundBank*) list->entry)->cueList;
1005 while (cue != NULL)
1007 if ( cue->playingSound != NULL &&
1008 FACT_INTERNAL_IsInCategory(
1009 pEngine,
1010 nCategory,
1011 cue->playingSound->sound->category
1014 FACTCue_Pause(cue, fPause);
1016 cue = cue->next;
1018 list = list->next;
1020 FAudio_PlatformUnlockMutex(pEngine->apiLock);
1021 return 0;
1024 uint16_t FACTAudioEngine_GetGlobalVariableIndex(
1025 FACTAudioEngine *pEngine,
1026 const char *szFriendlyName
1028 uint16_t i;
1029 FAudio_PlatformLockMutex(pEngine->apiLock);
1030 for (i = 0; i < pEngine->variableCount; i += 1)
1032 if ( FAudio_strcmp(szFriendlyName, pEngine->variableNames[i]) == 0 &&
1033 !(pEngine->variables[i].accessibility & 0x04) )
1035 FAudio_PlatformUnlockMutex(pEngine->apiLock);
1036 return i;
1039 FAudio_PlatformUnlockMutex(pEngine->apiLock);
1040 return FACTVARIABLEINDEX_INVALID;
1043 uint32_t FACTAudioEngine_SetGlobalVariable(
1044 FACTAudioEngine *pEngine,
1045 uint16_t nIndex,
1046 float nValue
1048 FACTVariable *var;
1050 FAudio_PlatformLockMutex(pEngine->apiLock);
1052 var = &pEngine->variables[nIndex];
1053 FAudio_assert(var->accessibility & 0x01);
1054 FAudio_assert(!(var->accessibility & 0x02));
1055 FAudio_assert(!(var->accessibility & 0x04));
1056 pEngine->globalVariableValues[nIndex] = FAudio_clamp(
1057 nValue,
1058 var->minValue,
1059 var->maxValue
1062 FAudio_PlatformUnlockMutex(pEngine->apiLock);
1063 return 0;
1066 uint32_t FACTAudioEngine_GetGlobalVariable(
1067 FACTAudioEngine *pEngine,
1068 uint16_t nIndex,
1069 float *pnValue
1071 FACTVariable *var;
1073 FAudio_PlatformLockMutex(pEngine->apiLock);
1075 var = &pEngine->variables[nIndex];
1076 FAudio_assert(var->accessibility & 0x01);
1077 FAudio_assert(!(var->accessibility & 0x04));
1078 *pnValue = pEngine->globalVariableValues[nIndex];
1080 FAudio_PlatformUnlockMutex(pEngine->apiLock);
1081 return 0;
1084 /* SoundBank implementation */
1086 uint16_t FACTSoundBank_GetCueIndex(
1087 FACTSoundBank *pSoundBank,
1088 const char *szFriendlyName
1090 uint16_t i;
1091 if (pSoundBank == NULL)
1093 return FACTINDEX_INVALID;
1096 FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1097 if (pSoundBank->cueNames != NULL)
1098 for (i = 0; i < pSoundBank->cueCount; i += 1)
1100 if (FAudio_strcmp(szFriendlyName, pSoundBank->cueNames[i]) == 0)
1102 FAudio_PlatformUnlockMutex(
1103 pSoundBank->parentEngine->apiLock
1105 return i;
1108 FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1109 return FACTINDEX_INVALID;
1112 uint32_t FACTSoundBank_GetNumCues(
1113 FACTSoundBank *pSoundBank,
1114 uint16_t *pnNumCues
1116 if (pSoundBank == NULL)
1118 *pnNumCues = 0;
1119 return 0;
1122 FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1123 *pnNumCues = pSoundBank->cueCount;
1124 FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1125 return 0;
1128 uint32_t FACTSoundBank_GetCueProperties(
1129 FACTSoundBank *pSoundBank,
1130 uint16_t nCueIndex,
1131 FACTCueProperties *pProperties
1133 uint16_t i;
1134 if (pSoundBank == NULL)
1136 return 1;
1139 FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1141 if (pSoundBank->cueNames == NULL)
1143 FAudio_zero(pProperties->friendlyName, 0xFF);
1145 else
1147 FAudio_strlcpy(
1148 pProperties->friendlyName,
1149 pSoundBank->cueNames[nCueIndex],
1150 0xFF
1153 if (!(pSoundBank->cues[nCueIndex].flags & 0x04))
1155 for (i = 0; i < pSoundBank->variationCount; i += 1)
1157 if (pSoundBank->variationCodes[i] == pSoundBank->cues[nCueIndex].sbCode)
1159 break;
1163 FAudio_assert(i < pSoundBank->variationCount && "Variation table not found!");
1165 if (pSoundBank->variations[i].flags == 3)
1167 pProperties->interactive = 1;
1168 pProperties->iaVariableIndex = pSoundBank->variations[i].variable;
1170 else
1172 pProperties->interactive = 0;
1173 pProperties->iaVariableIndex = 0;
1175 pProperties->numVariations = pSoundBank->variations[i].entryCount;
1177 else
1179 pProperties->interactive = 0;
1180 pProperties->iaVariableIndex = 0;
1181 pProperties->numVariations = 0;
1183 pProperties->maxInstances = pSoundBank->cues[nCueIndex].instanceLimit;
1184 pProperties->currentInstances = pSoundBank->cues[nCueIndex].instanceCount;
1186 FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1187 return 0;
1190 uint32_t FACTSoundBank_Prepare(
1191 FACTSoundBank *pSoundBank,
1192 uint16_t nCueIndex,
1193 uint32_t dwFlags,
1194 int32_t timeOffset,
1195 FACTCue** ppCue
1197 uint16_t i;
1198 FACTCue *latest;
1200 if (pSoundBank == NULL)
1202 *ppCue = NULL;
1203 return 1;
1206 *ppCue = (FACTCue*) pSoundBank->parentEngine->pMalloc(sizeof(FACTCue));
1207 FAudio_zero(*ppCue, sizeof(FACTCue));
1209 FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1211 /* Engine references */
1212 (*ppCue)->parentBank = pSoundBank;
1213 (*ppCue)->next = NULL;
1214 (*ppCue)->managed = 0;
1215 (*ppCue)->index = nCueIndex;
1216 (*ppCue)->notifyOnDestroy = 0;
1217 (*ppCue)->usercontext = NULL;
1219 /* Sound data */
1220 (*ppCue)->data = &pSoundBank->cues[nCueIndex];
1221 if ((*ppCue)->data->flags & 0x04)
1223 for (i = 0; i < pSoundBank->soundCount; i += 1)
1225 if ((*ppCue)->data->sbCode == pSoundBank->soundCodes[i])
1227 (*ppCue)->sound = &pSoundBank->sounds[i];
1228 break;
1232 else
1234 for (i = 0; i < pSoundBank->variationCount; i += 1)
1236 if ((*ppCue)->data->sbCode == pSoundBank->variationCodes[i])
1238 (*ppCue)->variation = &pSoundBank->variations[i];
1239 break;
1242 if ((*ppCue)->variation && (*ppCue)->variation->flags == 3)
1244 (*ppCue)->interactive = pSoundBank->parentEngine->variables[
1245 (*ppCue)->variation->variable
1246 ].initialValue;
1250 /* Instance data */
1251 (*ppCue)->variableValues = (float*) pSoundBank->parentEngine->pMalloc(
1252 sizeof(float) * pSoundBank->parentEngine->variableCount
1254 for (i = 0; i < pSoundBank->parentEngine->variableCount; i += 1)
1256 (*ppCue)->variableValues[i] =
1257 pSoundBank->parentEngine->variables[i].initialValue;
1260 /* Playback */
1261 (*ppCue)->state = FACT_STATE_PREPARED;
1263 /* Add to the SoundBank Cue list */
1264 if (pSoundBank->cueList == NULL)
1266 pSoundBank->cueList = *ppCue;
1268 else
1270 latest = pSoundBank->cueList;
1271 while (latest->next != NULL)
1273 latest = latest->next;
1275 latest->next = *ppCue;
1278 FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1279 return 0;
1282 uint32_t FACTSoundBank_Play(
1283 FACTSoundBank *pSoundBank,
1284 uint16_t nCueIndex,
1285 uint32_t dwFlags,
1286 int32_t timeOffset,
1287 FACTCue** ppCue /* Optional! */
1289 FACTCue *result;
1290 if (pSoundBank == NULL)
1292 if (ppCue != NULL)
1294 *ppCue = NULL;
1296 return 1;
1299 FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1301 FACTSoundBank_Prepare(
1302 pSoundBank,
1303 nCueIndex,
1304 dwFlags,
1305 timeOffset,
1306 &result
1308 if (ppCue != NULL)
1310 *ppCue = result;
1312 else
1314 /* AKA we get to Destroy() this ourselves */
1315 result->managed = 1;
1317 FACTCue_Play(result);
1319 FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1320 return 0;
1323 uint32_t FACTSoundBank_Play3D(
1324 FACTSoundBank *pSoundBank,
1325 uint16_t nCueIndex,
1326 uint32_t dwFlags,
1327 int32_t timeOffset,
1328 F3DAUDIO_DSP_SETTINGS *pDSPSettings,
1329 FACTCue** ppCue /* Optional! */
1331 FACTCue *result;
1332 if (pSoundBank == NULL)
1334 if (ppCue != NULL)
1336 *ppCue = NULL;
1338 return 1;
1341 FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1343 FACTSoundBank_Prepare(
1344 pSoundBank,
1345 nCueIndex,
1346 dwFlags,
1347 timeOffset,
1348 &result
1350 if (ppCue != NULL)
1352 *ppCue = result;
1354 else
1356 /* AKA we get to Destroy() this ourselves */
1357 result->managed = 1;
1359 FACT3DApply(pDSPSettings, result);
1360 FACTCue_Play(result);
1362 FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1363 return 0;
1366 uint32_t FACTSoundBank_Stop(
1367 FACTSoundBank *pSoundBank,
1368 uint16_t nCueIndex,
1369 uint32_t dwFlags
1371 FACTCue *backup, *cue;
1372 if (pSoundBank == NULL)
1374 return 1;
1377 FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1378 cue = pSoundBank->cueList;
1379 while (cue != NULL)
1381 if (cue->index == nCueIndex)
1383 if ( dwFlags == FACT_FLAG_STOP_IMMEDIATE &&
1384 cue->managed )
1386 /* Just blow this up now */
1387 backup = cue->next;
1388 FACTCue_Destroy(cue);
1389 cue = backup;
1391 else
1393 /* If managed, the mixer will destroy for us */
1394 FACTCue_Stop(cue, dwFlags);
1395 cue = cue->next;
1398 else
1400 cue = cue->next;
1403 FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1404 return 0;
1407 uint32_t FACTSoundBank_Destroy(FACTSoundBank *pSoundBank)
1409 uint16_t i, j, k;
1410 FAudioMutex mutex;
1411 FACTNotification note;
1412 if (pSoundBank == NULL)
1414 return 1;
1417 FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1419 /* Synchronously destroys all cues that are associated */
1420 while (pSoundBank->cueList != NULL)
1422 FACTCue_Destroy(pSoundBank->cueList);
1425 /* Remove this SoundBank from the Engine list */
1426 LinkedList_RemoveEntry(
1427 &pSoundBank->parentEngine->sbList,
1428 pSoundBank,
1429 pSoundBank->parentEngine->sbLock,
1430 pSoundBank->parentEngine->pFree
1433 /* SoundBank Name */
1434 pSoundBank->parentEngine->pFree(pSoundBank->name);
1436 /* Cue data */
1437 pSoundBank->parentEngine->pFree(pSoundBank->cues);
1439 /* WaveBank Name data */
1440 for (i = 0; i < pSoundBank->wavebankCount; i += 1)
1442 pSoundBank->parentEngine->pFree(pSoundBank->wavebankNames[i]);
1444 pSoundBank->parentEngine->pFree(pSoundBank->wavebankNames);
1446 /* Sound data */
1447 for (i = 0; i < pSoundBank->soundCount; i += 1)
1449 for (j = 0; j < pSoundBank->sounds[i].trackCount; j += 1)
1451 for (k = 0; k < pSoundBank->sounds[i].tracks[j].eventCount; k += 1)
1453 #define MATCH(t) \
1454 pSoundBank->sounds[i].tracks[j].events[k].type == t
1455 if ( MATCH(FACTEVENT_PLAYWAVE) ||
1456 MATCH(FACTEVENT_PLAYWAVETRACKVARIATION) ||
1457 MATCH(FACTEVENT_PLAYWAVEEFFECTVARIATION) ||
1458 MATCH(FACTEVENT_PLAYWAVETRACKEFFECTVARIATION) )
1460 if (pSoundBank->sounds[i].tracks[j].events[k].wave.isComplex)
1462 pSoundBank->parentEngine->pFree(
1463 pSoundBank->sounds[i].tracks[j].events[k].wave.complex.tracks
1465 pSoundBank->parentEngine->pFree(
1466 pSoundBank->sounds[i].tracks[j].events[k].wave.complex.wavebanks
1468 pSoundBank->parentEngine->pFree(
1469 pSoundBank->sounds[i].tracks[j].events[k].wave.complex.weights
1473 #undef MATCH
1475 pSoundBank->parentEngine->pFree(
1476 pSoundBank->sounds[i].tracks[j].events
1479 pSoundBank->parentEngine->pFree(pSoundBank->sounds[i].tracks);
1480 pSoundBank->parentEngine->pFree(pSoundBank->sounds[i].rpcCodes);
1481 pSoundBank->parentEngine->pFree(pSoundBank->sounds[i].dspCodes);
1483 pSoundBank->parentEngine->pFree(pSoundBank->sounds);
1484 pSoundBank->parentEngine->pFree(pSoundBank->soundCodes);
1486 /* Variation data */
1487 for (i = 0; i < pSoundBank->variationCount; i += 1)
1489 pSoundBank->parentEngine->pFree(
1490 pSoundBank->variations[i].entries
1493 pSoundBank->parentEngine->pFree(pSoundBank->variations);
1494 pSoundBank->parentEngine->pFree(pSoundBank->variationCodes);
1496 /* Transition data */
1497 for (i = 0; i < pSoundBank->transitionCount; i += 1)
1499 pSoundBank->parentEngine->pFree(
1500 pSoundBank->transitions[i].entries
1503 pSoundBank->parentEngine->pFree(pSoundBank->transitions);
1504 pSoundBank->parentEngine->pFree(pSoundBank->transitionCodes);
1506 /* Cue Name data */
1507 if (pSoundBank->cueNames != NULL)
1509 for (i = 0; i < pSoundBank->cueCount; i += 1)
1511 pSoundBank->parentEngine->pFree(pSoundBank->cueNames[i]);
1513 pSoundBank->parentEngine->pFree(pSoundBank->cueNames);
1516 /* Finally. */
1517 if (pSoundBank->notifyOnDestroy || pSoundBank->parentEngine->notifications & NOTIFY_SOUNDBANKDESTROY)
1519 note.type = FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED;
1520 note.soundBank.pSoundBank = pSoundBank;
1521 if (pSoundBank->parentEngine->notifications & NOTIFY_SOUNDBANKDESTROY)
1523 note.pvContext = pSoundBank->parentEngine->sb_context;
1525 else
1527 note.pvContext = pSoundBank->usercontext;
1529 pSoundBank->parentEngine->notificationCallback(&note);
1532 mutex = pSoundBank->parentEngine->apiLock;
1533 pSoundBank->parentEngine->pFree(pSoundBank);
1534 FAudio_PlatformUnlockMutex(mutex);
1535 return 0;
1538 uint32_t FACTSoundBank_GetState(
1539 FACTSoundBank *pSoundBank,
1540 uint32_t *pdwState
1542 uint16_t i;
1543 if (pSoundBank == NULL)
1545 *pdwState = 0;
1546 return 1;
1549 FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1551 *pdwState = FACT_STATE_PREPARED;
1552 for (i = 0; i < pSoundBank->cueCount; i += 1)
1554 if (pSoundBank->cues[i].instanceCount > 0)
1556 *pdwState |= FACT_STATE_INUSE;
1557 FAudio_PlatformUnlockMutex(
1558 pSoundBank->parentEngine->apiLock
1560 return 0;
1564 FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1565 return 0;
1568 /* WaveBank implementation */
1570 uint32_t FACTWaveBank_Destroy(FACTWaveBank *pWaveBank)
1572 uint32_t i;
1573 FACTWave *wave;
1574 FAudioMutex mutex;
1575 FACTNotification note;
1576 if (pWaveBank == NULL)
1578 return 1;
1581 FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1583 /* Synchronously destroys any cues that are using the wavebank */
1584 while (pWaveBank->waveList != NULL)
1586 wave = (FACTWave*) pWaveBank->waveList->entry;
1587 if (wave->parentCue != NULL)
1589 /* Destroying this Cue destroys the Wave */
1590 FACTCue_Destroy(wave->parentCue);
1592 else
1594 FACTWave_Destroy(wave);
1598 /* Remove this WaveBank from the Engine list */
1599 LinkedList_RemoveEntry(
1600 &pWaveBank->parentEngine->wbList,
1601 pWaveBank,
1602 pWaveBank->parentEngine->wbLock,
1603 pWaveBank->parentEngine->pFree
1606 /* Free everything, finally. */
1607 pWaveBank->parentEngine->pFree(pWaveBank->name);
1608 pWaveBank->parentEngine->pFree(pWaveBank->entries);
1609 pWaveBank->parentEngine->pFree(pWaveBank->entryRefs);
1610 if (pWaveBank->seekTables != NULL)
1612 for (i = 0; i < pWaveBank->entryCount; i += 1)
1614 if (pWaveBank->seekTables[i].entries != NULL)
1616 pWaveBank->parentEngine->pFree(
1617 pWaveBank->seekTables[i].entries
1621 pWaveBank->parentEngine->pFree(pWaveBank->seekTables);
1624 if (!pWaveBank->streaming)
1626 FAudio_close(pWaveBank->io);
1629 if (pWaveBank->packetBuffer != NULL)
1631 pWaveBank->parentEngine->pFree(pWaveBank->packetBuffer);
1633 if (pWaveBank->notifyOnDestroy || pWaveBank->parentEngine->notifications & NOTIFY_WAVEBANKDESTROY)
1635 note.type = FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED;
1636 note.waveBank.pWaveBank = pWaveBank;
1637 if (pWaveBank->parentEngine->notifications & NOTIFY_WAVEBANKDESTROY)
1639 note.pvContext = pWaveBank->parentEngine->wb_context;
1641 else
1643 note.pvContext = pWaveBank->usercontext;
1645 pWaveBank->parentEngine->notificationCallback(&note);
1647 FAudio_PlatformDestroyMutex(pWaveBank->waveLock);
1649 if (pWaveBank->waveBankNames != NULL)
1651 pWaveBank->parentEngine->pFree(pWaveBank->waveBankNames);
1654 mutex = pWaveBank->parentEngine->apiLock;
1655 pWaveBank->parentEngine->pFree(pWaveBank);
1656 FAudio_PlatformUnlockMutex(mutex);
1657 return 0;
1660 uint32_t FACTWaveBank_GetState(
1661 FACTWaveBank *pWaveBank,
1662 uint32_t *pdwState
1664 uint32_t i;
1665 if (pWaveBank == NULL)
1667 *pdwState = 0;
1668 return 1;
1671 FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1673 *pdwState = FACT_STATE_PREPARED;
1674 for (i = 0; i < pWaveBank->entryCount; i += 1)
1676 if (pWaveBank->entryRefs[i] > 0)
1678 *pdwState |= FACT_STATE_INUSE;
1679 FAudio_PlatformUnlockMutex(
1680 pWaveBank->parentEngine->apiLock
1682 return 0;
1686 FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1687 return 0;
1690 uint32_t FACTWaveBank_GetNumWaves(
1691 FACTWaveBank *pWaveBank,
1692 uint16_t *pnNumWaves
1694 if (pWaveBank == NULL)
1696 *pnNumWaves = 0;
1697 return 1;
1699 FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1700 *pnNumWaves = pWaveBank->entryCount;
1701 FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1702 return 0;
1705 uint16_t FACTWaveBank_GetWaveIndex(
1706 FACTWaveBank *pWaveBank,
1707 const char *szFriendlyName
1709 uint16_t i;
1710 char *curName;
1711 if (pWaveBank == NULL || pWaveBank->waveBankNames == NULL)
1713 return FACTINDEX_INVALID;
1716 FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1717 curName = pWaveBank->waveBankNames;
1718 for (i = 0; i < pWaveBank->entryCount; i += 1, curName += 64)
1720 if (FAudio_strncmp(szFriendlyName, curName, 64) == 0)
1722 FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1723 return i;
1726 FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1728 return FACTINDEX_INVALID;
1731 uint32_t FACTWaveBank_GetWaveProperties(
1732 FACTWaveBank *pWaveBank,
1733 uint16_t nWaveIndex,
1734 FACTWaveProperties *pWaveProperties
1736 FACTWaveBankEntry *entry;
1737 if (pWaveBank == NULL)
1739 return 1;
1742 FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1744 entry = &pWaveBank->entries[nWaveIndex];
1746 if (pWaveBank->waveBankNames)
1748 FAudio_memcpy(
1749 pWaveProperties->friendlyName,
1750 &pWaveBank->waveBankNames[nWaveIndex * 64],
1751 sizeof(pWaveProperties->friendlyName)
1754 else
1756 FAudio_zero(
1757 pWaveProperties->friendlyName,
1758 sizeof(pWaveProperties->friendlyName)
1762 pWaveProperties->format = entry->Format;
1763 pWaveProperties->durationInSamples = entry->PlayRegion.dwLength;
1764 if (entry->Format.wFormatTag == 0)
1766 pWaveProperties->durationInSamples /= (8 << entry->Format.wBitsPerSample) / 8;
1767 pWaveProperties->durationInSamples /= entry->Format.nChannels;
1769 else if (entry->Format.wFormatTag == FAUDIO_FORMAT_MSADPCM)
1771 pWaveProperties->durationInSamples = (
1772 pWaveProperties->durationInSamples /
1773 ((entry->Format.wBlockAlign + 22) * entry->Format.nChannels) *
1774 ((entry->Format.wBlockAlign + 16) * 2)
1777 else
1779 FAudio_assert(0 && "Unrecognized wFormatTag!");
1782 pWaveProperties->loopRegion = entry->LoopRegion;
1783 pWaveProperties->streaming = pWaveBank->streaming;
1785 FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1786 return 0;
1789 uint32_t FACTWaveBank_Prepare(
1790 FACTWaveBank *pWaveBank,
1791 uint16_t nWaveIndex,
1792 uint32_t dwFlags,
1793 uint32_t dwPlayOffset,
1794 uint8_t nLoopCount,
1795 FACTWave **ppWave
1797 FAudioBuffer buffer;
1798 FAudioBufferWMA bufferWMA;
1799 FAudioVoiceSends sends;
1800 FAudioSendDescriptor send;
1801 union
1803 FAudioWaveFormatEx pcm;
1804 FAudioADPCMWaveFormat adpcm;
1805 FAudioXMA2WaveFormat xma2;
1806 } format;
1807 FACTWaveBankEntry *entry;
1808 FACTSeekTable *seek;
1809 if (pWaveBank == NULL)
1811 *ppWave = NULL;
1812 return 1;
1815 *ppWave = (FACTWave*) pWaveBank->parentEngine->pMalloc(sizeof(FACTWave));
1817 FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1819 entry = &pWaveBank->entries[nWaveIndex];
1821 /* Engine references */
1822 (*ppWave)->parentBank = pWaveBank;
1823 (*ppWave)->parentCue = NULL;
1824 (*ppWave)->index = nWaveIndex;
1825 (*ppWave)->notifyOnDestroy = 0;
1826 (*ppWave)->usercontext = NULL;
1828 /* Playback */
1829 (*ppWave)->state = FACT_STATE_PREPARED;
1830 (*ppWave)->volume = 1.0f;
1831 (*ppWave)->pitch = 0;
1832 (*ppWave)->loopCount = nLoopCount;
1834 /* TODO: Convert dwPlayOffset to a byte offset */
1835 FAudio_assert(dwPlayOffset == 0);
1836 #if 0
1837 if (dwFlags & FACT_FLAG_UNITS_MS)
1839 dwPlayOffset = (uint32_t) (
1840 ( /* Samples per millisecond... */
1841 (float) entry->Format.nSamplesPerSec /
1842 1000.0f
1843 ) * (float) dwPlayOffset
1846 #endif
1848 /* Create the voice */
1849 send.Flags = 0;
1850 send.pOutputVoice = pWaveBank->parentEngine->master;
1851 sends.SendCount = 1;
1852 sends.pSends = &send;
1853 format.pcm.nChannels = entry->Format.nChannels;
1854 format.pcm.nSamplesPerSec = entry->Format.nSamplesPerSec;
1855 if (entry->Format.wFormatTag == 0x0)
1857 format.pcm.wFormatTag = FAUDIO_FORMAT_PCM;
1858 format.pcm.wBitsPerSample = 8 << entry->Format.wBitsPerSample;
1859 format.pcm.nBlockAlign = format.pcm.nChannels * format.pcm.wBitsPerSample / 8;
1860 format.pcm.nAvgBytesPerSec = format.pcm.nBlockAlign * format.pcm.nSamplesPerSec;
1861 format.pcm.cbSize = 0;
1863 else if (entry->Format.wFormatTag == 0x1)
1865 /* XMA2 is quite similar to WMA Pro... is what everyone thought.
1866 * What a great way to start this comment.
1868 * Let's reconstruct the extra data because who knows what decoder we're dealing with in <present year>.
1869 * It's also a good exercise in understanding XMA2 metadata and feeding blocks into the decoder properly.
1870 * At the time of writing this patch, it's FFmpeg via gstreamer which doesn't even respect most of this.
1871 * ... which means: good luck to whoever ends up finding inaccuracies here in the future!
1873 * dwLoopLength seems to match dwPlayLength in everything I've seen that had bLoopCount == 0.
1874 * dwLoopBegin can be > 0 even with bLoopCount == 0 because why not. Let's ignore that.
1876 * dwSamplesEncoded is usually close to dwPlayLength but not always (if ever?) equal. Let's assume equality.
1877 * The XMA2 seek table uses sample indices as opposed to WMA's byte index seek table.
1879 * nBlockAlign uses aWMABlockAlign given the entire WMA Pro thing BUT it's expected to be the block size for decoding.
1880 * The XMA2 block size MUST be a multiple of 2048 BUT entry->PlayRegion.dwLength / seek->entryCount doesn't respect that.
1881 * And even when correctly guesstimating the block size, we sometimes end up with block sizes >= 64k BYTES. nBlockAlign IS 16-BIT!
1882 * Scrap nBlockAlign. I've given up and made all FAudio gstreamer functions use dwBytesPerBlock if available.
1883 * Still though, if we don't want FAudio_INTERNAL_DecodeGSTREAMER to hang, the total data length must match (see SoundEffect.cs in FNA).
1884 * As such, we round up when guessing the block size, feed GStreamer with zeroes^Wundersized blocks and hope for the best.
1886 * This is FUN.
1887 * -ade
1889 FAudio_assert(entry->Format.wBitsPerSample != 0);
1891 seek = &pWaveBank->seekTables[nWaveIndex];
1892 format.pcm.wFormatTag = FAUDIO_FORMAT_XMAUDIO2;
1893 format.pcm.wBitsPerSample = 16;
1894 format.pcm.nAvgBytesPerSec = aWMAAvgBytesPerSec[entry->Format.wBlockAlign >> 5];
1895 format.pcm.nBlockAlign = aWMABlockAlign[entry->Format.wBlockAlign & 0x1F];
1896 format.pcm.cbSize = (
1897 sizeof(FAudioXMA2WaveFormat) -
1898 sizeof(FAudioWaveFormatEx)
1900 format.xma2.wNumStreams = (format.pcm.nChannels + 1) / 2;
1901 format.xma2.dwChannelMask = format.pcm.nChannels > 1 ? 0xFFFFFFFF >> (32 - format.pcm.nChannels) : 0;
1902 format.xma2.dwSamplesEncoded = seek->entries[seek->entryCount - 1];
1903 format.xma2.dwBytesPerBlock = (uint16_t) FAudio_ceil(
1904 (double) entry->PlayRegion.dwLength /
1905 (double) seek->entryCount /
1906 2048.0
1907 ) * 2048;
1908 format.xma2.dwPlayBegin = format.xma2.dwLoopBegin = 0;
1909 format.xma2.dwPlayLength = format.xma2.dwLoopLength = format.xma2.dwSamplesEncoded;
1910 format.xma2.bLoopCount = 0;
1911 format.xma2.bEncoderVersion = 4;
1912 format.xma2.wBlockCount = seek->entryCount;
1914 else if (entry->Format.wFormatTag == 0x2)
1916 format.pcm.wFormatTag = FAUDIO_FORMAT_MSADPCM;
1917 format.pcm.nBlockAlign = (entry->Format.wBlockAlign + 22) * format.pcm.nChannels;
1918 format.pcm.wBitsPerSample = 16;
1919 format.pcm.cbSize = (
1920 sizeof(FAudioADPCMWaveFormat) -
1921 sizeof(FAudioWaveFormatEx)
1923 format.adpcm.wSamplesPerBlock = (
1924 ((format.pcm.nBlockAlign / format.pcm.nChannels) - 6) * 2
1927 else if (entry->Format.wFormatTag == 0x3)
1929 /* Apparently this is used to detect WMA Pro...? */
1930 FAudio_assert(entry->Format.wBitsPerSample == 0);
1932 format.pcm.wFormatTag = FAUDIO_FORMAT_WMAUDIO2;
1933 format.pcm.nAvgBytesPerSec = aWMAAvgBytesPerSec[entry->Format.wBlockAlign >> 5];
1934 format.pcm.nBlockAlign = aWMABlockAlign[entry->Format.wBlockAlign & 0x1F];
1935 format.pcm.wBitsPerSample = 16;
1936 format.pcm.cbSize = 0;
1938 else
1940 FAudio_assert(0 && "Rebuild your WaveBanks with ADPCM!");
1942 (*ppWave)->callback.callback.OnBufferEnd = pWaveBank->streaming ?
1943 FACT_INTERNAL_OnBufferEnd :
1944 NULL;
1945 (*ppWave)->callback.callback.OnBufferStart = NULL;
1946 (*ppWave)->callback.callback.OnLoopEnd = NULL;
1947 (*ppWave)->callback.callback.OnStreamEnd = FACT_INTERNAL_OnStreamEnd;
1948 (*ppWave)->callback.callback.OnVoiceError = NULL;
1949 (*ppWave)->callback.callback.OnVoiceProcessingPassEnd = NULL;
1950 (*ppWave)->callback.callback.OnVoiceProcessingPassStart = NULL;
1951 (*ppWave)->callback.wave = *ppWave;
1952 (*ppWave)->srcChannels = format.pcm.nChannels;
1953 FAudio_CreateSourceVoice(
1954 pWaveBank->parentEngine->audio,
1955 &(*ppWave)->voice,
1956 &format.pcm,
1957 FAUDIO_VOICE_USEFILTER, /* FIXME: Can this be optional? */
1958 4.0f,
1959 (FAudioVoiceCallback*) &(*ppWave)->callback,
1960 &sends,
1961 NULL
1963 if (pWaveBank->streaming)
1965 /* Init stream cache info */
1966 if (format.pcm.wFormatTag == FAUDIO_FORMAT_PCM)
1968 (*ppWave)->streamSize = (
1969 format.pcm.nSamplesPerSec *
1970 format.pcm.nBlockAlign
1973 else if (format.pcm.wFormatTag == FAUDIO_FORMAT_MSADPCM)
1975 (*ppWave)->streamSize = (
1976 format.pcm.nSamplesPerSec /
1977 format.adpcm.wSamplesPerBlock *
1978 format.pcm.nBlockAlign
1981 else
1983 /* Screw it, load the whole thing */
1984 (*ppWave)->streamSize = entry->PlayRegion.dwLength;
1986 /* XACT does NOT support loop subregions for these formats */
1987 FAudio_assert(entry->LoopRegion.dwStartSample == 0);
1988 FAudio_assert(entry->LoopRegion.dwTotalSamples == 0 || entry->LoopRegion.dwTotalSamples == entry->Duration);
1990 (*ppWave)->streamCache = (uint8_t*) pWaveBank->parentEngine->pMalloc(
1991 (*ppWave)->streamSize
1993 (*ppWave)->streamOffset = entry->PlayRegion.dwOffset;
1995 /* Read and submit first buffer from the WaveBank */
1996 FACT_INTERNAL_OnBufferEnd(&(*ppWave)->callback.callback, NULL);
1998 else
2000 (*ppWave)->streamCache = NULL;
2002 buffer.Flags = FAUDIO_END_OF_STREAM;
2003 buffer.AudioBytes = entry->PlayRegion.dwLength;
2004 buffer.pAudioData = FAudio_memptr(
2005 pWaveBank->io,
2006 entry->PlayRegion.dwOffset
2008 buffer.PlayBegin = 0;
2009 buffer.PlayLength = entry->Duration;
2010 if (nLoopCount == 0)
2012 buffer.LoopBegin = 0;
2013 buffer.LoopLength = 0;
2014 buffer.LoopCount = 0;
2016 else
2018 buffer.LoopBegin = entry->LoopRegion.dwStartSample;
2019 buffer.LoopLength = entry->LoopRegion.dwTotalSamples;
2020 buffer.LoopCount = nLoopCount;
2022 buffer.pContext = NULL;
2023 if (format.pcm.wFormatTag == FAUDIO_FORMAT_WMAUDIO2)
2025 bufferWMA.pDecodedPacketCumulativeBytes =
2026 pWaveBank->seekTables[nWaveIndex].entries;
2027 bufferWMA.PacketCount =
2028 pWaveBank->seekTables[nWaveIndex].entryCount;
2029 FAudioSourceVoice_SubmitSourceBuffer(
2030 (*ppWave)->voice,
2031 &buffer,
2032 &bufferWMA
2035 else
2037 FAudioSourceVoice_SubmitSourceBuffer(
2038 (*ppWave)->voice,
2039 &buffer,
2040 NULL
2045 /* Add to the WaveBank Wave list */
2046 LinkedList_AddEntry(
2047 &pWaveBank->waveList,
2048 *ppWave,
2049 pWaveBank->waveLock,
2050 pWaveBank->parentEngine->pMalloc
2053 FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
2054 return 0;
2057 uint32_t FACTWaveBank_Play(
2058 FACTWaveBank *pWaveBank,
2059 uint16_t nWaveIndex,
2060 uint32_t dwFlags,
2061 uint32_t dwPlayOffset,
2062 uint8_t nLoopCount,
2063 FACTWave **ppWave
2065 if (pWaveBank == NULL)
2067 *ppWave = NULL;
2068 return 1;
2070 FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
2071 FACTWaveBank_Prepare(
2072 pWaveBank,
2073 nWaveIndex,
2074 dwFlags,
2075 dwPlayOffset,
2076 nLoopCount,
2077 ppWave
2079 FACTWave_Play(*ppWave);
2080 FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
2081 return 0;
2084 uint32_t FACTWaveBank_Stop(
2085 FACTWaveBank *pWaveBank,
2086 uint16_t nWaveIndex,
2087 uint32_t dwFlags
2089 FACTWave *wave;
2090 LinkedList *list;
2091 if (pWaveBank == NULL)
2093 return 1;
2095 FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
2096 list = pWaveBank->waveList;
2097 while (list != NULL)
2099 wave = (FACTWave*) list->entry;
2100 if (wave->index == nWaveIndex)
2102 FACTWave_Stop(wave, dwFlags);
2104 list = list->next;
2106 FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
2107 return 0;
2110 /* Wave implementation */
2112 uint32_t FACTWave_Destroy(FACTWave *pWave)
2114 FAudioMutex mutex;
2115 FACTNotification note;
2116 if (pWave == NULL)
2118 return 1;
2121 FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2123 /* Stop before we start deleting everything */
2124 FACTWave_Stop(pWave, FACT_FLAG_STOP_IMMEDIATE);
2126 LinkedList_RemoveEntry(
2127 &pWave->parentBank->waveList,
2128 pWave,
2129 pWave->parentBank->waveLock,
2130 pWave->parentBank->parentEngine->pFree
2133 FAudioVoice_DestroyVoice(pWave->voice);
2134 if (pWave->streamCache != NULL)
2136 pWave->parentBank->parentEngine->pFree(pWave->streamCache);
2138 if (pWave->notifyOnDestroy || pWave->parentBank->parentEngine->notifications & NOTIFY_WAVEDESTROY)
2140 note.type = FACTNOTIFICATIONTYPE_WAVEDESTROYED;
2141 note.wave.pWave = pWave;
2142 if (pWave->parentBank->parentEngine->notifications & NOTIFY_WAVEDESTROY)
2144 note.pvContext = pWave->parentBank->parentEngine->wave_context;
2146 else
2148 note.pvContext = pWave->usercontext;
2150 pWave->parentBank->parentEngine->notificationCallback(&note);
2153 mutex = pWave->parentBank->parentEngine->apiLock;
2154 pWave->parentBank->parentEngine->pFree(pWave);
2155 FAudio_PlatformUnlockMutex(mutex);
2156 return 0;
2159 uint32_t FACTWave_Play(FACTWave *pWave)
2161 if (pWave == NULL)
2163 return 1;
2165 FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2166 FAudio_assert(!(pWave->state & (FACT_STATE_PLAYING | FACT_STATE_STOPPING)));
2167 pWave->state |= FACT_STATE_PLAYING;
2168 pWave->state &= ~(
2169 FACT_STATE_PAUSED |
2170 FACT_STATE_STOPPED
2172 FAudioSourceVoice_Start(pWave->voice, 0, 0);
2173 FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2174 return 0;
2177 uint32_t FACTWave_Stop(FACTWave *pWave, uint32_t dwFlags)
2179 if (pWave == NULL)
2181 return 1;
2183 FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2185 /* There are two ways that a Wave might be stopped immediately:
2186 * 1. The program explicitly asks for it
2187 * 2. The Wave is paused and therefore we can't do fade/release effects
2189 if ( dwFlags & FACT_FLAG_STOP_IMMEDIATE ||
2190 pWave->state & FACT_STATE_PAUSED )
2192 pWave->state |= FACT_STATE_STOPPED;
2193 pWave->state &= ~(
2194 FACT_STATE_PLAYING |
2195 FACT_STATE_STOPPING |
2196 FACT_STATE_PAUSED
2198 FAudioSourceVoice_Stop(pWave->voice, 0, 0);
2199 FAudioSourceVoice_FlushSourceBuffers(pWave->voice);
2201 else
2203 pWave->state |= FACT_STATE_STOPPING;
2204 FAudioSourceVoice_ExitLoop(pWave->voice, 0);
2207 if (pWave->parentBank->parentEngine->notifications & NOTIFY_WAVESTOP)
2209 FACTNotification note;
2210 note.type = FACTNOTIFICATIONTYPE_WAVESTOP;
2211 note.wave.cueIndex = pWave->parentCue->index;
2212 note.wave.pCue = pWave->parentCue;
2213 note.wave.pSoundBank = pWave->parentCue->parentBank;
2214 note.wave.pWave = pWave;
2215 note.wave.pWaveBank = pWave->parentBank;
2216 note.pvContext = pWave->parentBank->parentEngine->wave_context;
2218 pWave->parentBank->parentEngine->notificationCallback(&note);
2221 FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2222 return 0;
2225 uint32_t FACTWave_Pause(FACTWave *pWave, int32_t fPause)
2227 if (pWave == NULL)
2229 return 1;
2231 FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2233 /* FIXME: Does the Cue STOPPING/STOPPED rule apply here too? */
2234 if (pWave->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))
2236 FAudio_PlatformUnlockMutex(
2237 pWave->parentBank->parentEngine->apiLock
2239 return 0;
2242 /* All we do is set the flag, the mixer handles the rest */
2243 if (fPause)
2245 pWave->state |= FACT_STATE_PAUSED;
2246 FAudioSourceVoice_Stop(pWave->voice, 0, 0);
2248 else
2250 pWave->state &= ~FACT_STATE_PAUSED;
2251 FAudioSourceVoice_Start(pWave->voice, 0, 0);
2254 FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2255 return 0;
2258 uint32_t FACTWave_GetState(FACTWave *pWave, uint32_t *pdwState)
2260 if (pWave == NULL)
2262 *pdwState = 0;
2263 return 1;
2265 FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2266 *pdwState = pWave->state;
2267 FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2268 return 0;
2271 uint32_t FACTWave_SetPitch(FACTWave *pWave, int16_t pitch)
2273 if (pWave == NULL)
2275 return 1;
2277 FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2278 pWave->pitch = FAudio_clamp(
2279 pitch,
2280 FACTPITCH_MIN_TOTAL,
2281 FACTPITCH_MAX_TOTAL
2283 FAudioSourceVoice_SetFrequencyRatio(
2284 pWave->voice,
2285 (float) FAudio_pow(2.0, pWave->pitch / 1200.0),
2288 FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2289 return 0;
2292 uint32_t FACTWave_SetVolume(FACTWave *pWave, float volume)
2294 if (pWave == NULL)
2296 return 1;
2298 FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2299 pWave->volume = FAudio_clamp(
2300 volume,
2301 FACTVOLUME_MIN,
2302 FACTVOLUME_MAX
2304 FAudioVoice_SetVolume(
2305 pWave->voice,
2306 pWave->volume,
2309 FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2310 return 0;
2313 uint32_t FACTWave_SetMatrixCoefficients(
2314 FACTWave *pWave,
2315 uint32_t uSrcChannelCount,
2316 uint32_t uDstChannelCount,
2317 float *pMatrixCoefficients
2319 uint32_t i;
2320 float *mtxDst, *mtxSrc, *mtxTmp = NULL;
2321 if (pWave == NULL)
2323 return 1;
2326 /* There seems to be this weird feature in XACT where the channel count
2327 * can be completely wrong and it'll go to the right place.
2328 * I guess these XACT functions do some extra work to merge coefficients
2329 * but I have no idea where it really happens and XAudio2 definitely
2330 * does NOT like it when this is wrong, so here it goes...
2331 * -flibit
2333 if (uSrcChannelCount == 1 && pWave->srcChannels == 2)
2335 mtxTmp = (float*) FAudio_alloca(
2336 sizeof(float) *
2337 pWave->srcChannels *
2338 uDstChannelCount
2340 mtxDst = mtxTmp;
2341 mtxSrc = pMatrixCoefficients;
2342 for (i = 0; i < uDstChannelCount; i += 1)
2344 mtxDst[0] = *mtxSrc;
2345 mtxDst[1] = *mtxSrc;
2346 mtxDst += 2;
2347 mtxSrc += 1;
2349 uSrcChannelCount = 2;
2350 pMatrixCoefficients = mtxTmp;
2352 else if (uSrcChannelCount == 2 && pWave->srcChannels == 1)
2354 mtxTmp = (float*) FAudio_alloca(
2355 sizeof(float) *
2356 pWave->srcChannels *
2357 uDstChannelCount
2359 mtxDst = mtxTmp;
2360 mtxSrc = pMatrixCoefficients;
2361 for (i = 0; i < uDstChannelCount; i += 1)
2363 *mtxDst = (mtxSrc[0] + mtxSrc[1]) / 2.0f;
2364 mtxDst += 1;
2365 mtxSrc += 2;
2367 uSrcChannelCount = 1;
2368 pMatrixCoefficients = mtxTmp;
2371 FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2373 FAudioVoice_SetOutputMatrix(
2374 pWave->voice,
2375 pWave->voice->sends.pSends->pOutputVoice,
2376 uSrcChannelCount,
2377 uDstChannelCount,
2378 pMatrixCoefficients,
2382 FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2383 if (mtxTmp != NULL)
2385 FAudio_dealloca(mtxTmp);
2387 return 0;
2390 uint32_t FACTWave_GetProperties(
2391 FACTWave *pWave,
2392 FACTWaveInstanceProperties *pProperties
2394 if (pWave == NULL)
2396 return 1;
2398 FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2400 FACTWaveBank_GetWaveProperties(
2401 pWave->parentBank,
2402 pWave->index,
2403 &pProperties->properties
2406 /* FIXME: This is unsupported on PC, do we care about this? */
2407 pProperties->backgroundMusic = 0;
2409 FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2410 return 0;
2413 /* Cue implementation */
2415 uint32_t FACTCue_Destroy(FACTCue *pCue)
2417 FACTCue *cue, *prev;
2418 FAudioMutex mutex;
2419 if (pCue == NULL)
2421 return 1;
2424 FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2426 /* Stop before we start deleting everything */
2427 FACTCue_Stop(pCue, FACT_FLAG_STOP_IMMEDIATE);
2429 /* Remove this Cue from the SoundBank list */
2430 cue = pCue->parentBank->cueList;
2431 prev = cue;
2432 while (cue != NULL)
2434 if (cue == pCue)
2436 if (cue == prev) /* First in list */
2438 pCue->parentBank->cueList = cue->next;
2440 else
2442 prev->next = cue->next;
2444 break;
2446 prev = cue;
2447 cue = cue->next;
2449 FAudio_assert(cue != NULL && "Could not find Cue reference!");
2451 pCue->parentBank->parentEngine->pFree(pCue->variableValues);
2452 FACT_INTERNAL_SendCueNotification(pCue, NOTIFY_CUEDESTROY, FACTNOTIFICATIONTYPE_CUEDESTROYED);
2454 mutex = pCue->parentBank->parentEngine->apiLock;
2455 pCue->parentBank->parentEngine->pFree(pCue);
2456 FAudio_PlatformUnlockMutex(mutex);
2457 return 0;
2460 uint32_t FACTCue_Play(FACTCue *pCue)
2462 union
2464 float maxf;
2465 uint8_t maxi;
2466 } limitmax;
2467 FACTCue *tmp, *wnr;
2468 uint16_t fadeInMS = 0;
2469 FACTCueData *data;
2470 if (pCue == NULL)
2472 return 1;
2475 FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2477 FAudio_assert(!(pCue->state & (FACT_STATE_PLAYING | FACT_STATE_STOPPING)));
2479 data = &pCue->parentBank->cues[pCue->index];
2481 /* Cue Instance Limits */
2482 if (data->instanceCount >= data->instanceLimit)
2484 wnr = NULL;
2485 tmp = pCue->parentBank->cueList;
2486 if (data->maxInstanceBehavior == 0) /* Fail */
2488 pCue->state |= FACT_STATE_STOPPED;
2489 pCue->state &= ~(
2490 FACT_STATE_PLAYING |
2491 FACT_STATE_STOPPING |
2492 FACT_STATE_PAUSED
2495 FACT_INTERNAL_SendCueNotification(pCue, NOTIFY_CUESTOP, FACTNOTIFICATIONTYPE_CUESTOP);
2497 FAudio_PlatformUnlockMutex(
2498 pCue->parentBank->parentEngine->apiLock
2500 return 1;
2502 else if (data->maxInstanceBehavior == 1) /* Queue */
2504 /* FIXME: How is this different from Replace Oldest? */
2505 while (tmp != NULL)
2507 if ( tmp != pCue &&
2508 tmp->index == pCue->index &&
2509 !(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED)) )
2511 wnr = tmp;
2512 break;
2514 tmp = tmp->next;
2517 else if (data->maxInstanceBehavior == 2) /* Replace Oldest */
2519 while (tmp != NULL)
2521 if ( tmp != pCue &&
2522 tmp->index == pCue->index &&
2523 !(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED)) )
2525 wnr = tmp;
2526 break;
2528 tmp = tmp->next;
2531 else if (data->maxInstanceBehavior == 3) /* Replace Quietest */
2533 limitmax.maxf = FACTVOLUME_MAX;
2534 while (tmp != NULL)
2536 if ( tmp != pCue &&
2537 tmp->index == pCue->index &&
2538 tmp->playingSound != NULL &&
2539 /*FIXME: tmp->playingSound->volume < limitmax.maxf &&*/
2540 !(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED)) )
2542 wnr = tmp;
2543 /* limitmax.maxf = tmp->playingSound->volume; */
2545 tmp = tmp->next;
2548 else if (data->maxInstanceBehavior == 4) /* Replace Lowest Priority */
2550 limitmax.maxi = 0xFF;
2551 while (tmp != NULL)
2553 if ( tmp != pCue &&
2554 tmp->index == pCue->index &&
2555 tmp->playingSound != NULL &&
2556 tmp->playingSound->sound->priority < limitmax.maxi &&
2557 !(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED)) )
2559 wnr = tmp;
2560 limitmax.maxi = tmp->playingSound->sound->priority;
2562 tmp = tmp->next;
2565 if (wnr != NULL)
2567 fadeInMS = data->fadeInMS;
2568 if (wnr->playingSound != NULL)
2570 FACT_INTERNAL_BeginFadeOut(wnr->playingSound, data->fadeOutMS);
2572 else
2574 FACTCue_Stop(wnr, 0);
2579 /* Need an initial sound to play */
2580 if (!FACT_INTERNAL_CreateSound(pCue, fadeInMS))
2582 FAudio_PlatformUnlockMutex(
2583 pCue->parentBank->parentEngine->apiLock
2585 return 1;
2587 data->instanceCount += 1;
2589 pCue->state |= FACT_STATE_PLAYING;
2590 pCue->state &= ~(
2591 FACT_STATE_PAUSED |
2592 FACT_STATE_STOPPED |
2593 FACT_STATE_PREPARED
2596 FACT_INTERNAL_SendCueNotification(pCue, NOTIFY_CUEPLAY, FACTNOTIFICATIONTYPE_CUEPLAY);
2598 pCue->start = FAudio_timems();
2600 /* If it's a simple wave, just play it! */
2601 if (pCue->simpleWave != NULL)
2603 if (pCue->active3D)
2605 FACTWave_SetMatrixCoefficients(
2606 pCue->simpleWave,
2607 pCue->srcChannels,
2608 pCue->dstChannels,
2609 pCue->matrixCoefficients
2612 FACTWave_Play(pCue->simpleWave);
2615 FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2616 return 0;
2619 uint32_t FACTCue_Stop(FACTCue *pCue, uint32_t dwFlags)
2621 if (pCue == NULL)
2623 return 1;
2625 FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2627 /* If we're already stopped, there's nothing to do... */
2628 if (pCue->state & FACT_STATE_STOPPED)
2630 FAudio_PlatformUnlockMutex(
2631 pCue->parentBank->parentEngine->apiLock
2633 return 0;
2636 /* If we're stopping and we haven't asked for IMMEDIATE, we're already
2637 * doing what the application is asking us to do...
2639 if ( (pCue->state & FACT_STATE_STOPPING) &&
2640 !(dwFlags & FACT_FLAG_STOP_IMMEDIATE) )
2642 FAudio_PlatformUnlockMutex(
2643 pCue->parentBank->parentEngine->apiLock
2645 return 0;
2648 /* There are three ways that a Cue might be stopped immediately:
2649 * 1. The program explicitly asks for it
2650 * 2. The Cue is paused and therefore we can't do fade/release effects
2651 * 3. The Cue is stopped "as authored" and has no fade effects
2653 if ( dwFlags & FACT_FLAG_STOP_IMMEDIATE ||
2654 pCue->state & FACT_STATE_PAUSED ||
2655 pCue->playingSound == NULL ||
2656 ( pCue->parentBank->cues[pCue->index].fadeOutMS == 0 &&
2657 pCue->maxRpcReleaseTime == 0 ) )
2659 pCue->start = 0;
2660 pCue->elapsed = 0;
2661 pCue->state |= FACT_STATE_STOPPED;
2662 pCue->state &= ~(
2663 FACT_STATE_PLAYING |
2664 FACT_STATE_STOPPING |
2665 FACT_STATE_PAUSED
2668 if (pCue->simpleWave != NULL)
2670 FACTWave_Destroy(pCue->simpleWave);
2671 pCue->simpleWave = NULL;
2673 pCue->data->instanceCount -= 1;
2675 else if (pCue->playingSound != NULL)
2677 FACT_INTERNAL_DestroySound(pCue->playingSound);
2680 else
2682 if (pCue->parentBank->cues[pCue->index].fadeOutMS > 0)
2684 FACT_INTERNAL_BeginFadeOut(
2685 pCue->playingSound,
2686 pCue->parentBank->cues[pCue->index].fadeOutMS
2689 else if (pCue->maxRpcReleaseTime > 0)
2691 FACT_INTERNAL_BeginReleaseRPC(
2692 pCue->playingSound,
2693 pCue->maxRpcReleaseTime
2696 else
2698 /* Pretty sure this doesn't happen, but just in case? */
2699 pCue->state |= FACT_STATE_STOPPING;
2703 FACT_INTERNAL_SendCueNotification(pCue, NOTIFY_CUESTOP, FACTNOTIFICATIONTYPE_CUESTOP);
2705 FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2706 return 0;
2709 uint32_t FACTCue_GetState(FACTCue *pCue, uint32_t *pdwState)
2711 if (pCue == NULL)
2713 *pdwState = 0;
2714 return 1;
2716 FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2717 *pdwState = pCue->state;
2718 FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2719 return 0;
2722 uint32_t FACTCue_SetMatrixCoefficients(
2723 FACTCue *pCue,
2724 uint32_t uSrcChannelCount,
2725 uint32_t uDstChannelCount,
2726 float *pMatrixCoefficients
2728 uint8_t i;
2730 FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2732 /* See FACTCue.matrixCoefficients declaration */
2733 FAudio_assert(uSrcChannelCount > 0 && uSrcChannelCount < 3);
2734 FAudio_assert(uDstChannelCount > 0 && uDstChannelCount < 9);
2736 /* Local storage */
2737 pCue->srcChannels = uSrcChannelCount;
2738 pCue->dstChannels = uDstChannelCount;
2739 FAudio_memcpy(
2740 pCue->matrixCoefficients,
2741 pMatrixCoefficients,
2742 sizeof(float) * uSrcChannelCount * uDstChannelCount
2744 pCue->active3D = 1;
2746 /* Apply to Waves if they exist */
2747 if (pCue->simpleWave != NULL)
2749 FACTWave_SetMatrixCoefficients(
2750 pCue->simpleWave,
2751 uSrcChannelCount,
2752 uDstChannelCount,
2753 pMatrixCoefficients
2756 else if (pCue->playingSound != NULL)
2758 for (i = 0; i < pCue->playingSound->sound->trackCount; i += 1)
2760 if (pCue->playingSound->tracks[i].activeWave.wave != NULL)
2762 FACTWave_SetMatrixCoefficients(
2763 pCue->playingSound->tracks[i].activeWave.wave,
2764 uSrcChannelCount,
2765 uDstChannelCount,
2766 pMatrixCoefficients
2772 FACT_INTERNAL_SendCueNotification(pCue, NOTIFY_CUESTOP, FACTNOTIFICATIONTYPE_CUESTOP);
2774 FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2775 return 0;
2778 uint16_t FACTCue_GetVariableIndex(
2779 FACTCue *pCue,
2780 const char *szFriendlyName
2782 uint16_t i;
2783 if (pCue == NULL)
2785 return FACTVARIABLEINDEX_INVALID;
2787 FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2788 for (i = 0; i < pCue->parentBank->parentEngine->variableCount; i += 1)
2790 if ( FAudio_strcmp(szFriendlyName, pCue->parentBank->parentEngine->variableNames[i]) == 0 &&
2791 pCue->parentBank->parentEngine->variables[i].accessibility & 0x04 )
2793 FAudio_PlatformUnlockMutex(
2794 pCue->parentBank->parentEngine->apiLock
2796 return i;
2799 FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2800 return FACTVARIABLEINDEX_INVALID;
2803 uint32_t FACTCue_SetVariable(
2804 FACTCue *pCue,
2805 uint16_t nIndex,
2806 float nValue
2808 FACTVariable *var;
2809 if (pCue == NULL)
2811 return 1;
2814 if (nIndex == FACTINDEX_INVALID)
2816 return 1;
2819 FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2821 var = &pCue->parentBank->parentEngine->variables[nIndex];
2822 FAudio_assert(var->accessibility & 0x01);
2823 FAudio_assert(!(var->accessibility & 0x02));
2824 FAudio_assert(var->accessibility & 0x04);
2825 pCue->variableValues[nIndex] = FAudio_clamp(
2826 nValue,
2827 var->minValue,
2828 var->maxValue
2831 FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2832 return 0;
2835 uint32_t FACTCue_GetVariable(
2836 FACTCue *pCue,
2837 uint16_t nIndex,
2838 float *nValue
2840 FACTVariable *var;
2841 if (pCue == NULL)
2843 *nValue = 0.0f;
2844 return 1;
2847 if (nIndex == FACTINDEX_INVALID)
2849 return 1;
2852 FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2854 var = &pCue->parentBank->parentEngine->variables[nIndex];
2855 FAudio_assert(var->accessibility & 0x01);
2856 FAudio_assert(var->accessibility & 0x04);
2858 if (nIndex == 0) /* NumCueInstances */
2860 *nValue = pCue->parentBank->cues[pCue->index].instanceCount;
2862 else
2864 *nValue = pCue->variableValues[nIndex];
2867 FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2868 return 0;
2871 uint32_t FACTCue_Pause(FACTCue *pCue, int32_t fPause)
2873 uint8_t i;
2874 if (pCue == NULL)
2876 return 1;
2879 FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2881 /* "A stopping or stopped cue cannot be paused." */
2882 if (pCue->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))
2884 FAudio_PlatformUnlockMutex(
2885 pCue->parentBank->parentEngine->apiLock
2887 return 0;
2890 /* Store elapsed time */
2891 pCue->elapsed += FAudio_timems() - pCue->start;
2893 /* All we do is set the flag, not much to see here */
2894 if (fPause)
2896 pCue->state |= FACT_STATE_PAUSED;
2898 else
2900 pCue->state &= ~FACT_STATE_PAUSED;
2903 /* Pause the Waves */
2904 if (pCue->simpleWave != NULL)
2906 FACTWave_Pause(pCue->simpleWave, fPause);
2908 else if (pCue->playingSound != NULL)
2910 for (i = 0; i < pCue->playingSound->sound->trackCount; i += 1)
2912 if (pCue->playingSound->tracks[i].activeWave.wave != NULL)
2914 FACTWave_Pause(
2915 pCue->playingSound->tracks[i].activeWave.wave,
2916 fPause
2922 FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2923 return 0;
2926 uint32_t FACTCue_GetProperties(
2927 FACTCue *pCue,
2928 FACTCueInstanceProperties **ppProperties
2930 uint32_t i;
2931 size_t allocSize;
2932 FACTCueInstanceProperties *cueProps;
2933 FACTVariationProperties *varProps;
2934 FACTSoundProperties *sndProps;
2935 FACTWaveInstanceProperties waveProps;
2936 if (pCue == NULL)
2938 return 1;
2941 FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2943 /* Alloc container (including variable length array space) */
2944 allocSize = sizeof(FACTCueInstanceProperties);
2945 if (pCue->playingSound != NULL)
2947 allocSize += (
2948 sizeof(FACTTrackProperties) *
2949 pCue->playingSound->sound->trackCount
2952 cueProps = (FACTCueInstanceProperties*) pCue->parentBank->parentEngine->pMalloc(
2953 allocSize
2955 FAudio_zero(cueProps, allocSize);
2957 /* Cue Properties */
2958 FACTSoundBank_GetCueProperties(
2959 pCue->parentBank,
2960 pCue->index,
2961 &cueProps->cueProperties
2964 /* Variation Properties */
2965 varProps = &cueProps->activeVariationProperties.variationProperties;
2966 if (pCue->playingVariation != NULL)
2968 varProps->index = 0; /* TODO: Index of what...? */
2969 /* TODO: This is just max - min right? Also why u8 wtf */
2970 varProps->weight = (uint8_t) (
2971 pCue->playingVariation->maxWeight -
2972 pCue->playingVariation->minWeight
2974 if (pCue->variation->flags == 3)
2976 varProps->iaVariableMin =
2977 pCue->playingVariation->minWeight;
2978 varProps->iaVariableMax =
2979 pCue->playingVariation->maxWeight;
2981 else
2983 varProps->iaVariableMin = 0;
2984 varProps->iaVariableMax = 0;
2986 varProps->linger = pCue->playingVariation->linger;
2989 /* Sound Properties */
2990 sndProps = &cueProps->activeVariationProperties.soundProperties;
2991 if (pCue->playingSound != NULL)
2993 sndProps->category = pCue->playingSound->sound->category;
2994 sndProps->priority = pCue->playingSound->sound->priority;
2995 sndProps->pitch = pCue->playingSound->sound->pitch;
2996 sndProps->volume = pCue->playingSound->sound->volume;
2997 sndProps->numTracks = pCue->playingSound->sound->trackCount;
2999 for (i = 0; i < sndProps->numTracks; i += 1)
3001 if (FACTWave_GetProperties(
3002 pCue->playingSound->tracks[i].activeWave.wave,
3003 &waveProps
3004 ) == 0) {
3005 sndProps->arrTrackProperties[i].duration = (uint32_t) (
3007 (float) waveProps.properties.durationInSamples /
3008 (float) waveProps.properties.format.nSamplesPerSec
3009 ) / 1000.0f
3011 sndProps->arrTrackProperties[i].numVariations = 1; /* ? */
3012 sndProps->arrTrackProperties[i].numChannels =
3013 waveProps.properties.format.nChannels;
3014 sndProps->arrTrackProperties[i].waveVariation = 0; /* ? */
3015 sndProps->arrTrackProperties[i].loopCount =
3016 pCue->playingSound->tracks[i].waveEvt->wave.loopCount;
3021 FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
3023 *ppProperties = cueProps;
3024 return 0;
3027 uint32_t FACTCue_SetOutputVoices(
3028 FACTCue *pCue,
3029 const FAudioVoiceSends *pSendList /* Optional XAUDIO2_VOICE_SENDS */
3031 /* TODO */
3032 return 0;
3035 uint32_t FACTCue_SetOutputVoiceMatrix(
3036 FACTCue *pCue,
3037 const FAudioVoice *pDestinationVoice, /* Optional! */
3038 uint32_t SourceChannels,
3039 uint32_t DestinationChannels,
3040 const float *pLevelMatrix /* SourceChannels * DestinationChannels */
3042 /* TODO */
3043 return 0;
3046 /* vim: set noexpandtab shiftwidth=8 tabstop=8: */