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>
28 #include "FACT_internal.h"
30 /* AudioEngine implementation */
32 uint32_t FACTCreateEngine(
33 uint32_t dwCreationFlags
,
34 FACTAudioEngine
**ppEngine
36 return FACTCreateEngineWithCustomAllocatorEXT(
45 uint32_t FACTCreateEngineWithCustomAllocatorEXT(
46 uint32_t dwCreationFlags
,
47 FACTAudioEngine
**ppEngine
,
48 FAudioMallocFunc customMalloc
,
49 FAudioFreeFunc customFree
,
50 FAudioReallocFunc customRealloc
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;
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();
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
);
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(
125 pRendererDetails
->rendererID
,
126 deviceDetails
.DeviceID
,
127 sizeof(int16_t) * 0xFF
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
140 FAudio_PlatformUnlockMutex(pEngine
->apiLock
);
144 uint32_t FACTAudioEngine_GetFinalMixFormat(
145 FACTAudioEngine
*pEngine
,
146 FAudioWaveFormatExtensible
*pFinalMixFormat
148 FAudio_PlatformLockMutex(pEngine
->apiLock
);
151 &pEngine
->audio
->mixFormat
,
152 sizeof(FAudioWaveFormatExtensible
)
154 FAudio_PlatformUnlockMutex(pEngine
->apiLock
);
158 uint32_t FACTAudioEngine_Initialize(
159 FACTAudioEngine
*pEngine
,
160 const FACTRuntimeParameters
*pParams
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
;
231 parseRet
= FACT_INTERNAL_ParseAudioEngine(pEngine
, pParams
);
234 FAudio_PlatformUnlockMutex(pEngine
->apiLock
);
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)
277 deviceIndex
= pParams
->pRendererID
[0] - L
'0';
278 if (deviceIndex
> FAudio_PlatformGetDeviceCount())
283 if (FAudio_CreateMasteringVoice(
286 FAUDIO_DEFAULT_CHANNELS
,
287 FAUDIO_DEFAULT_SAMPLERATE
,
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(
313 &pEngine
->reverbVoice
,
314 1, /* Reverb will be omnidirectional */
315 masterDetails
.InputSampleRate
,
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
,
333 FAudio_PlatformUnlockMutex(pEngine
->apiLock
);
337 uint32_t FACTAudioEngine_ShutDown(FACTAudioEngine
*pEngine
)
339 uint32_t i
, refcount
;
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
);
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
);
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
);
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
);
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
);
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
);
442 uint32_t FACTAudioEngine_DoWork(FACTAudioEngine
*pEngine
)
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
;
461 cue
= ((FACTSoundBank
*) list
->entry
)->cueList
;
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(
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
485 FAudio_PlatformUnlockMutex(pEngine
->apiLock
);
489 uint32_t FACTAudioEngine_CreateSoundBank(
490 FACTAudioEngine
*pEngine
,
491 const void *pvBuffer
,
494 uint32_t dwAllocAttributes
,
495 FACTSoundBank
**ppSoundBank
498 FAudio_PlatformLockMutex(pEngine
->apiLock
);
499 retval
= FACT_INTERNAL_ParseSoundBank(
505 FAudio_PlatformUnlockMutex(pEngine
->apiLock
);
509 uint32_t FACTAudioEngine_CreateInMemoryWaveBank(
510 FACTAudioEngine
*pEngine
,
511 const void *pvBuffer
,
514 uint32_t dwAllocAttributes
,
515 FACTWaveBank
**ppWaveBank
517 FACTNotification
*note
;
519 FAudio_PlatformLockMutex(pEngine
->apiLock
);
520 retval
= FACT_INTERNAL_ParseWaveBank(
522 FAudio_memopen((void*) pvBuffer
, dwSize
),
525 FACT_INTERNAL_DefaultReadFile
,
526 FACT_INTERNAL_DefaultGetOverlappedResult
,
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
);
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 */
558 packetSize
= pParms
->packetSize
* 2048;
560 retval
= FACT_INTERNAL_ParseWaveBank(
566 pEngine
->pGetOverlappedResult
,
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
);
582 uint32_t FACTAudioEngine_PrepareWave(
583 FACTAudioEngine
*pEngine
,
585 const char *szWavePath
,
586 uint32_t wStreamingPacketSize
,
587 uint32_t dwAlignment
,
588 uint32_t dwPlayOffset
,
596 uint32_t FACTAudioEngine_PrepareInMemoryWave(
597 FACTAudioEngine
*pEngine
,
599 FACTWaveBankEntry entry
,
600 uint32_t *pdwSeekTable
, /* Optional! */
602 uint32_t dwPlayOffset
,
610 uint32_t FACTAudioEngine_PrepareStreamingWave(
611 FACTAudioEngine
*pEngine
,
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
,
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; \
646 FAudio_assert(0 && "TODO: "#nt" notification!"); \
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
;
664 pNotificationDescription
->pCue
->notifyOnDestroy
= 1;
665 pNotificationDescription
->pCue
->usercontext
= pNotificationDescription
->pvContext
;
668 #undef PERSIST_ACTION
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
;
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
;
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
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
;
726 pNotificationDescription
->pWave
->notifyOnDestroy
= 1;
727 pNotificationDescription
->pWave
->usercontext
= pNotificationDescription
->pvContext
;
730 #undef PERSIST_ACTION
733 #define PERSIST_ACTION pEngine->wb_context = pNotificationDescription->pvContext;
734 else HANDLE_PERSIST(WAVEBANKPREPARED
)
735 else HANDLE_PERSIST(WAVEBANKSTREAMING_INVALIDCONTENT
)
736 #undef PERSIST_ACTION
741 FAudio_assert(0 && "TODO: Unimplemented notification!");
744 #undef HANDLE_PERSIST
746 FAudio_PlatformUnlockMutex(pEngine
->apiLock
);
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; \
770 FAudio_assert(0 && "TODO: "#nt" notification!"); \
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
;
788 pNotificationDescription
->pCue
->notifyOnDestroy
= 0;
789 pNotificationDescription
->pCue
->usercontext
= pNotificationDescription
->pvContext
;
792 #undef PERSIST_ACTION
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
;
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
;
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
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
;
850 pNotificationDescription
->pWave
->notifyOnDestroy
= 0;
851 pNotificationDescription
->pWave
->usercontext
= pNotificationDescription
->pvContext
;
854 #undef PERSIST_ACTION
857 #define PERSIST_ACTION pEngine->wb_context = pNotificationDescription->pvContext;
858 else HANDLE_PERSIST(WAVEBANKPREPARED
)
859 else HANDLE_PERSIST(WAVEBANKSTREAMING_INVALIDCONTENT
)
860 #undef PERSIST_ACTION
865 FAudio_assert(0 && "TODO: Unimplemented notification!");
868 #undef HANDLE_PERSIST
870 FAudio_PlatformUnlockMutex(pEngine
->apiLock
);
874 uint16_t FACTAudioEngine_GetCategory(
875 FACTAudioEngine
*pEngine
,
876 const char *szFriendlyName
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
);
888 FAudio_PlatformUnlockMutex(pEngine
->apiLock
);
889 return FACTCATEGORY_INVALID
;
892 uint8_t FACT_INTERNAL_IsInCategory(
893 FACTAudioEngine
*engine
,
897 FACTAudioCategory
*cat
;
899 /* Same category, no need to go on a crazy hunt */
900 if (category
== target
)
905 /* Right, on with the crazy hunt */
906 cat
= &engine
->categories
[category
];
907 while (cat
->parentCategory
!= -1)
909 if (cat
->parentCategory
== target
)
913 cat
= &engine
->categories
[cat
->parentCategory
];
918 uint32_t FACTAudioEngine_Stop(
919 FACTAudioEngine
*pEngine
,
923 FACTCue
*cue
, *backup
;
926 FAudio_PlatformLockMutex(pEngine
->apiLock
);
927 list
= pEngine
->sbList
;
930 cue
= ((FACTSoundBank
*) list
->entry
)->cueList
;
933 if ( cue
->playingSound
!= NULL
&&
934 FACT_INTERNAL_IsInCategory(
937 cue
->playingSound
->sound
->category
940 if ( dwFlags
== FACT_FLAG_STOP_IMMEDIATE
&&
943 /* Just blow this up now */
945 FACTCue_Destroy(cue
);
950 /* If managed, the mixer will destroy for us */
951 FACTCue_Stop(cue
, dwFlags
);
962 FAudio_PlatformUnlockMutex(pEngine
->apiLock
);
966 uint32_t FACTAudioEngine_SetVolume(
967 FACTAudioEngine
*pEngine
,
972 FAudio_PlatformLockMutex(pEngine
->apiLock
);
973 pEngine
->categories
[nCategory
].currentVolume
= (
974 pEngine
->categories
[nCategory
].volume
*
977 for (i
= 0; i
< pEngine
->categoryCount
; i
+= 1)
979 if (pEngine
->categories
[i
].parentCategory
== nCategory
)
981 FACTAudioEngine_SetVolume(
984 pEngine
->categories
[i
].currentVolume
988 FAudio_PlatformUnlockMutex(pEngine
->apiLock
);
992 uint32_t FACTAudioEngine_Pause(
993 FACTAudioEngine
*pEngine
,
1000 FAudio_PlatformLockMutex(pEngine
->apiLock
);
1001 list
= pEngine
->sbList
;
1002 while (list
!= NULL
)
1004 cue
= ((FACTSoundBank
*) list
->entry
)->cueList
;
1007 if ( cue
->playingSound
!= NULL
&&
1008 FACT_INTERNAL_IsInCategory(
1011 cue
->playingSound
->sound
->category
1014 FACTCue_Pause(cue
, fPause
);
1020 FAudio_PlatformUnlockMutex(pEngine
->apiLock
);
1024 uint16_t FACTAudioEngine_GetGlobalVariableIndex(
1025 FACTAudioEngine
*pEngine
,
1026 const char *szFriendlyName
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
);
1039 FAudio_PlatformUnlockMutex(pEngine
->apiLock
);
1040 return FACTVARIABLEINDEX_INVALID
;
1043 uint32_t FACTAudioEngine_SetGlobalVariable(
1044 FACTAudioEngine
*pEngine
,
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(
1062 FAudio_PlatformUnlockMutex(pEngine
->apiLock
);
1066 uint32_t FACTAudioEngine_GetGlobalVariable(
1067 FACTAudioEngine
*pEngine
,
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
);
1084 /* SoundBank implementation */
1086 uint16_t FACTSoundBank_GetCueIndex(
1087 FACTSoundBank
*pSoundBank
,
1088 const char *szFriendlyName
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
1108 FAudio_PlatformUnlockMutex(pSoundBank
->parentEngine
->apiLock
);
1109 return FACTINDEX_INVALID
;
1112 uint32_t FACTSoundBank_GetNumCues(
1113 FACTSoundBank
*pSoundBank
,
1116 if (pSoundBank
== NULL
)
1122 FAudio_PlatformLockMutex(pSoundBank
->parentEngine
->apiLock
);
1123 *pnNumCues
= pSoundBank
->cueCount
;
1124 FAudio_PlatformUnlockMutex(pSoundBank
->parentEngine
->apiLock
);
1128 uint32_t FACTSoundBank_GetCueProperties(
1129 FACTSoundBank
*pSoundBank
,
1131 FACTCueProperties
*pProperties
1134 if (pSoundBank
== NULL
)
1139 FAudio_PlatformLockMutex(pSoundBank
->parentEngine
->apiLock
);
1141 if (pSoundBank
->cueNames
== NULL
)
1143 FAudio_zero(pProperties
->friendlyName
, 0xFF);
1148 pProperties
->friendlyName
,
1149 pSoundBank
->cueNames
[nCueIndex
],
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
)
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
;
1172 pProperties
->interactive
= 0;
1173 pProperties
->iaVariableIndex
= 0;
1175 pProperties
->numVariations
= pSoundBank
->variations
[i
].entryCount
;
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
);
1190 uint32_t FACTSoundBank_Prepare(
1191 FACTSoundBank
*pSoundBank
,
1200 if (pSoundBank
== NULL
)
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
;
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
];
1234 for (i
= 0; i
< pSoundBank
->variationCount
; i
+= 1)
1236 if ((*ppCue
)->data
->sbCode
== pSoundBank
->variationCodes
[i
])
1238 (*ppCue
)->variation
= &pSoundBank
->variations
[i
];
1242 if ((*ppCue
)->variation
&& (*ppCue
)->variation
->flags
== 3)
1244 (*ppCue
)->interactive
= pSoundBank
->parentEngine
->variables
[
1245 (*ppCue
)->variation
->variable
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
;
1261 (*ppCue
)->state
= FACT_STATE_PREPARED
;
1263 /* Add to the SoundBank Cue list */
1264 if (pSoundBank
->cueList
== NULL
)
1266 pSoundBank
->cueList
= *ppCue
;
1270 latest
= pSoundBank
->cueList
;
1271 while (latest
->next
!= NULL
)
1273 latest
= latest
->next
;
1275 latest
->next
= *ppCue
;
1278 FAudio_PlatformUnlockMutex(pSoundBank
->parentEngine
->apiLock
);
1282 uint32_t FACTSoundBank_Play(
1283 FACTSoundBank
*pSoundBank
,
1287 FACTCue
** ppCue
/* Optional! */
1290 if (pSoundBank
== NULL
)
1299 FAudio_PlatformLockMutex(pSoundBank
->parentEngine
->apiLock
);
1301 FACTSoundBank_Prepare(
1314 /* AKA we get to Destroy() this ourselves */
1315 result
->managed
= 1;
1317 FACTCue_Play(result
);
1319 FAudio_PlatformUnlockMutex(pSoundBank
->parentEngine
->apiLock
);
1323 uint32_t FACTSoundBank_Play3D(
1324 FACTSoundBank
*pSoundBank
,
1328 F3DAUDIO_DSP_SETTINGS
*pDSPSettings
,
1329 FACTCue
** ppCue
/* Optional! */
1332 if (pSoundBank
== NULL
)
1341 FAudio_PlatformLockMutex(pSoundBank
->parentEngine
->apiLock
);
1343 FACTSoundBank_Prepare(
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
);
1366 uint32_t FACTSoundBank_Stop(
1367 FACTSoundBank
*pSoundBank
,
1371 FACTCue
*backup
, *cue
;
1372 if (pSoundBank
== NULL
)
1377 FAudio_PlatformLockMutex(pSoundBank
->parentEngine
->apiLock
);
1378 cue
= pSoundBank
->cueList
;
1381 if (cue
->index
== nCueIndex
)
1383 if ( dwFlags
== FACT_FLAG_STOP_IMMEDIATE
&&
1386 /* Just blow this up now */
1388 FACTCue_Destroy(cue
);
1393 /* If managed, the mixer will destroy for us */
1394 FACTCue_Stop(cue
, dwFlags
);
1403 FAudio_PlatformUnlockMutex(pSoundBank
->parentEngine
->apiLock
);
1407 uint32_t FACTSoundBank_Destroy(FACTSoundBank
*pSoundBank
)
1411 FACTNotification note
;
1412 if (pSoundBank
== NULL
)
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
,
1429 pSoundBank
->parentEngine
->sbLock
,
1430 pSoundBank
->parentEngine
->pFree
1433 /* SoundBank Name */
1434 pSoundBank
->parentEngine
->pFree(pSoundBank
->name
);
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
);
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)
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
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
);
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
);
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
;
1527 note
.pvContext
= pSoundBank
->usercontext
;
1529 pSoundBank
->parentEngine
->notificationCallback(¬e
);
1532 mutex
= pSoundBank
->parentEngine
->apiLock
;
1533 pSoundBank
->parentEngine
->pFree(pSoundBank
);
1534 FAudio_PlatformUnlockMutex(mutex
);
1538 uint32_t FACTSoundBank_GetState(
1539 FACTSoundBank
*pSoundBank
,
1543 if (pSoundBank
== NULL
)
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
1564 FAudio_PlatformUnlockMutex(pSoundBank
->parentEngine
->apiLock
);
1568 /* WaveBank implementation */
1570 uint32_t FACTWaveBank_Destroy(FACTWaveBank
*pWaveBank
)
1575 FACTNotification note
;
1576 if (pWaveBank
== NULL
)
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
);
1594 FACTWave_Destroy(wave
);
1598 /* Remove this WaveBank from the Engine list */
1599 LinkedList_RemoveEntry(
1600 &pWaveBank
->parentEngine
->wbList
,
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
;
1643 note
.pvContext
= pWaveBank
->usercontext
;
1645 pWaveBank
->parentEngine
->notificationCallback(¬e
);
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
);
1660 uint32_t FACTWaveBank_GetState(
1661 FACTWaveBank
*pWaveBank
,
1665 if (pWaveBank
== NULL
)
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
1686 FAudio_PlatformUnlockMutex(pWaveBank
->parentEngine
->apiLock
);
1690 uint32_t FACTWaveBank_GetNumWaves(
1691 FACTWaveBank
*pWaveBank
,
1692 uint16_t *pnNumWaves
1694 if (pWaveBank
== NULL
)
1699 FAudio_PlatformLockMutex(pWaveBank
->parentEngine
->apiLock
);
1700 *pnNumWaves
= pWaveBank
->entryCount
;
1701 FAudio_PlatformUnlockMutex(pWaveBank
->parentEngine
->apiLock
);
1705 uint16_t FACTWaveBank_GetWaveIndex(
1706 FACTWaveBank
*pWaveBank
,
1707 const char *szFriendlyName
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
);
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
)
1742 FAudio_PlatformLockMutex(pWaveBank
->parentEngine
->apiLock
);
1744 entry
= &pWaveBank
->entries
[nWaveIndex
];
1746 if (pWaveBank
->waveBankNames
)
1749 pWaveProperties
->friendlyName
,
1750 &pWaveBank
->waveBankNames
[nWaveIndex
* 64],
1751 sizeof(pWaveProperties
->friendlyName
)
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)
1779 FAudio_assert(0 && "Unrecognized wFormatTag!");
1782 pWaveProperties
->loopRegion
= entry
->LoopRegion
;
1783 pWaveProperties
->streaming
= pWaveBank
->streaming
;
1785 FAudio_PlatformUnlockMutex(pWaveBank
->parentEngine
->apiLock
);
1789 uint32_t FACTWaveBank_Prepare(
1790 FACTWaveBank
*pWaveBank
,
1791 uint16_t nWaveIndex
,
1793 uint32_t dwPlayOffset
,
1797 FAudioBuffer buffer
;
1798 FAudioBufferWMA bufferWMA
;
1799 FAudioVoiceSends sends
;
1800 FAudioSendDescriptor send
;
1803 FAudioWaveFormatEx pcm
;
1804 FAudioADPCMWaveFormat adpcm
;
1805 FAudioXMA2WaveFormat xma2
;
1807 FACTWaveBankEntry
*entry
;
1808 FACTSeekTable
*seek
;
1809 if (pWaveBank
== NULL
)
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
;
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);
1837 if (dwFlags
& FACT_FLAG_UNITS_MS
)
1839 dwPlayOffset
= (uint32_t) (
1840 ( /* Samples per millisecond... */
1841 (float) entry
->Format
.nSamplesPerSec
/
1843 ) * (float) dwPlayOffset
1848 /* Create the voice */
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.
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
/
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;
1940 FAudio_assert(0 && "Rebuild your WaveBanks with ADPCM!");
1942 (*ppWave
)->callback
.callback
.OnBufferEnd
= pWaveBank
->streaming
?
1943 FACT_INTERNAL_OnBufferEnd
:
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
,
1957 FAUDIO_VOICE_USEFILTER
, /* FIXME: Can this be optional? */
1959 (FAudioVoiceCallback
*) &(*ppWave
)->callback
,
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
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
);
2000 (*ppWave
)->streamCache
= NULL
;
2002 buffer
.Flags
= FAUDIO_END_OF_STREAM
;
2003 buffer
.AudioBytes
= entry
->PlayRegion
.dwLength
;
2004 buffer
.pAudioData
= FAudio_memptr(
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;
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(
2037 FAudioSourceVoice_SubmitSourceBuffer(
2045 /* Add to the WaveBank Wave list */
2046 LinkedList_AddEntry(
2047 &pWaveBank
->waveList
,
2049 pWaveBank
->waveLock
,
2050 pWaveBank
->parentEngine
->pMalloc
2053 FAudio_PlatformUnlockMutex(pWaveBank
->parentEngine
->apiLock
);
2057 uint32_t FACTWaveBank_Play(
2058 FACTWaveBank
*pWaveBank
,
2059 uint16_t nWaveIndex
,
2061 uint32_t dwPlayOffset
,
2065 if (pWaveBank
== NULL
)
2070 FAudio_PlatformLockMutex(pWaveBank
->parentEngine
->apiLock
);
2071 FACTWaveBank_Prepare(
2079 FACTWave_Play(*ppWave
);
2080 FAudio_PlatformUnlockMutex(pWaveBank
->parentEngine
->apiLock
);
2084 uint32_t FACTWaveBank_Stop(
2085 FACTWaveBank
*pWaveBank
,
2086 uint16_t nWaveIndex
,
2091 if (pWaveBank
== NULL
)
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
);
2106 FAudio_PlatformUnlockMutex(pWaveBank
->parentEngine
->apiLock
);
2110 /* Wave implementation */
2112 uint32_t FACTWave_Destroy(FACTWave
*pWave
)
2115 FACTNotification note
;
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
,
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
;
2148 note
.pvContext
= pWave
->usercontext
;
2150 pWave
->parentBank
->parentEngine
->notificationCallback(¬e
);
2153 mutex
= pWave
->parentBank
->parentEngine
->apiLock
;
2154 pWave
->parentBank
->parentEngine
->pFree(pWave
);
2155 FAudio_PlatformUnlockMutex(mutex
);
2159 uint32_t FACTWave_Play(FACTWave
*pWave
)
2165 FAudio_PlatformLockMutex(pWave
->parentBank
->parentEngine
->apiLock
);
2166 FAudio_assert(!(pWave
->state
& (FACT_STATE_PLAYING
| FACT_STATE_STOPPING
)));
2167 pWave
->state
|= FACT_STATE_PLAYING
;
2172 FAudioSourceVoice_Start(pWave
->voice
, 0, 0);
2173 FAudio_PlatformUnlockMutex(pWave
->parentBank
->parentEngine
->apiLock
);
2177 uint32_t FACTWave_Stop(FACTWave
*pWave
, uint32_t dwFlags
)
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
;
2194 FACT_STATE_PLAYING
|
2195 FACT_STATE_STOPPING
|
2198 FAudioSourceVoice_Stop(pWave
->voice
, 0, 0);
2199 FAudioSourceVoice_FlushSourceBuffers(pWave
->voice
);
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(¬e
);
2221 FAudio_PlatformUnlockMutex(pWave
->parentBank
->parentEngine
->apiLock
);
2225 uint32_t FACTWave_Pause(FACTWave
*pWave
, int32_t fPause
)
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
2242 /* All we do is set the flag, the mixer handles the rest */
2245 pWave
->state
|= FACT_STATE_PAUSED
;
2246 FAudioSourceVoice_Stop(pWave
->voice
, 0, 0);
2250 pWave
->state
&= ~FACT_STATE_PAUSED
;
2251 FAudioSourceVoice_Start(pWave
->voice
, 0, 0);
2254 FAudio_PlatformUnlockMutex(pWave
->parentBank
->parentEngine
->apiLock
);
2258 uint32_t FACTWave_GetState(FACTWave
*pWave
, uint32_t *pdwState
)
2265 FAudio_PlatformLockMutex(pWave
->parentBank
->parentEngine
->apiLock
);
2266 *pdwState
= pWave
->state
;
2267 FAudio_PlatformUnlockMutex(pWave
->parentBank
->parentEngine
->apiLock
);
2271 uint32_t FACTWave_SetPitch(FACTWave
*pWave
, int16_t pitch
)
2277 FAudio_PlatformLockMutex(pWave
->parentBank
->parentEngine
->apiLock
);
2278 pWave
->pitch
= FAudio_clamp(
2280 FACTPITCH_MIN_TOTAL
,
2283 FAudioSourceVoice_SetFrequencyRatio(
2285 (float) FAudio_pow(2.0, pWave
->pitch
/ 1200.0),
2288 FAudio_PlatformUnlockMutex(pWave
->parentBank
->parentEngine
->apiLock
);
2292 uint32_t FACTWave_SetVolume(FACTWave
*pWave
, float volume
)
2298 FAudio_PlatformLockMutex(pWave
->parentBank
->parentEngine
->apiLock
);
2299 pWave
->volume
= FAudio_clamp(
2304 FAudioVoice_SetVolume(
2309 FAudio_PlatformUnlockMutex(pWave
->parentBank
->parentEngine
->apiLock
);
2313 uint32_t FACTWave_SetMatrixCoefficients(
2315 uint32_t uSrcChannelCount
,
2316 uint32_t uDstChannelCount
,
2317 float *pMatrixCoefficients
2320 float *mtxDst
, *mtxSrc
, *mtxTmp
= NULL
;
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...
2333 if (uSrcChannelCount
== 1 && pWave
->srcChannels
== 2)
2335 mtxTmp
= (float*) FAudio_alloca(
2337 pWave
->srcChannels
*
2341 mtxSrc
= pMatrixCoefficients
;
2342 for (i
= 0; i
< uDstChannelCount
; i
+= 1)
2344 mtxDst
[0] = *mtxSrc
;
2345 mtxDst
[1] = *mtxSrc
;
2349 uSrcChannelCount
= 2;
2350 pMatrixCoefficients
= mtxTmp
;
2352 else if (uSrcChannelCount
== 2 && pWave
->srcChannels
== 1)
2354 mtxTmp
= (float*) FAudio_alloca(
2356 pWave
->srcChannels
*
2360 mtxSrc
= pMatrixCoefficients
;
2361 for (i
= 0; i
< uDstChannelCount
; i
+= 1)
2363 *mtxDst
= (mtxSrc
[0] + mtxSrc
[1]) / 2.0f
;
2367 uSrcChannelCount
= 1;
2368 pMatrixCoefficients
= mtxTmp
;
2371 FAudio_PlatformLockMutex(pWave
->parentBank
->parentEngine
->apiLock
);
2373 FAudioVoice_SetOutputMatrix(
2375 pWave
->voice
->sends
.pSends
->pOutputVoice
,
2378 pMatrixCoefficients
,
2382 FAudio_PlatformUnlockMutex(pWave
->parentBank
->parentEngine
->apiLock
);
2385 FAudio_dealloca(mtxTmp
);
2390 uint32_t FACTWave_GetProperties(
2392 FACTWaveInstanceProperties
*pProperties
2398 FAudio_PlatformLockMutex(pWave
->parentBank
->parentEngine
->apiLock
);
2400 FACTWaveBank_GetWaveProperties(
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
);
2413 /* Cue implementation */
2415 uint32_t FACTCue_Destroy(FACTCue
*pCue
)
2417 FACTCue
*cue
, *prev
;
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
;
2436 if (cue
== prev
) /* First in list */
2438 pCue
->parentBank
->cueList
= cue
->next
;
2442 prev
->next
= 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
);
2460 uint32_t FACTCue_Play(FACTCue
*pCue
)
2468 uint16_t fadeInMS
= 0;
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
)
2485 tmp
= pCue
->parentBank
->cueList
;
2486 if (data
->maxInstanceBehavior
== 0) /* Fail */
2488 pCue
->state
|= FACT_STATE_STOPPED
;
2490 FACT_STATE_PLAYING
|
2491 FACT_STATE_STOPPING
|
2495 FACT_INTERNAL_SendCueNotification(pCue
, NOTIFY_CUESTOP
, FACTNOTIFICATIONTYPE_CUESTOP
);
2497 FAudio_PlatformUnlockMutex(
2498 pCue
->parentBank
->parentEngine
->apiLock
2502 else if (data
->maxInstanceBehavior
== 1) /* Queue */
2504 /* FIXME: How is this different from Replace Oldest? */
2508 tmp
->index
== pCue
->index
&&
2509 !(tmp
->state
& (FACT_STATE_STOPPING
| FACT_STATE_STOPPED
)) )
2517 else if (data
->maxInstanceBehavior
== 2) /* Replace Oldest */
2522 tmp
->index
== pCue
->index
&&
2523 !(tmp
->state
& (FACT_STATE_STOPPING
| FACT_STATE_STOPPED
)) )
2531 else if (data
->maxInstanceBehavior
== 3) /* Replace Quietest */
2533 limitmax
.maxf
= FACTVOLUME_MAX
;
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
)) )
2543 /* limitmax.maxf = tmp->playingSound->volume; */
2548 else if (data
->maxInstanceBehavior
== 4) /* Replace Lowest Priority */
2550 limitmax
.maxi
= 0xFF;
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
)) )
2560 limitmax
.maxi
= tmp
->playingSound
->sound
->priority
;
2567 fadeInMS
= data
->fadeInMS
;
2568 if (wnr
->playingSound
!= NULL
)
2570 FACT_INTERNAL_BeginFadeOut(wnr
->playingSound
, data
->fadeOutMS
);
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
2587 data
->instanceCount
+= 1;
2589 pCue
->state
|= FACT_STATE_PLAYING
;
2592 FACT_STATE_STOPPED
|
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
)
2605 FACTWave_SetMatrixCoefficients(
2609 pCue
->matrixCoefficients
2612 FACTWave_Play(pCue
->simpleWave
);
2615 FAudio_PlatformUnlockMutex(pCue
->parentBank
->parentEngine
->apiLock
);
2619 uint32_t FACTCue_Stop(FACTCue
*pCue
, uint32_t dwFlags
)
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
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
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 ) )
2661 pCue
->state
|= FACT_STATE_STOPPED
;
2663 FACT_STATE_PLAYING
|
2664 FACT_STATE_STOPPING
|
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
);
2682 if (pCue
->parentBank
->cues
[pCue
->index
].fadeOutMS
> 0)
2684 FACT_INTERNAL_BeginFadeOut(
2686 pCue
->parentBank
->cues
[pCue
->index
].fadeOutMS
2689 else if (pCue
->maxRpcReleaseTime
> 0)
2691 FACT_INTERNAL_BeginReleaseRPC(
2693 pCue
->maxRpcReleaseTime
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
);
2709 uint32_t FACTCue_GetState(FACTCue
*pCue
, uint32_t *pdwState
)
2716 FAudio_PlatformLockMutex(pCue
->parentBank
->parentEngine
->apiLock
);
2717 *pdwState
= pCue
->state
;
2718 FAudio_PlatformUnlockMutex(pCue
->parentBank
->parentEngine
->apiLock
);
2722 uint32_t FACTCue_SetMatrixCoefficients(
2724 uint32_t uSrcChannelCount
,
2725 uint32_t uDstChannelCount
,
2726 float *pMatrixCoefficients
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);
2737 pCue
->srcChannels
= uSrcChannelCount
;
2738 pCue
->dstChannels
= uDstChannelCount
;
2740 pCue
->matrixCoefficients
,
2741 pMatrixCoefficients
,
2742 sizeof(float) * uSrcChannelCount
* uDstChannelCount
2746 /* Apply to Waves if they exist */
2747 if (pCue
->simpleWave
!= NULL
)
2749 FACTWave_SetMatrixCoefficients(
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
,
2772 FACT_INTERNAL_SendCueNotification(pCue
, NOTIFY_CUESTOP
, FACTNOTIFICATIONTYPE_CUESTOP
);
2774 FAudio_PlatformUnlockMutex(pCue
->parentBank
->parentEngine
->apiLock
);
2778 uint16_t FACTCue_GetVariableIndex(
2780 const char *szFriendlyName
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
2799 FAudio_PlatformUnlockMutex(pCue
->parentBank
->parentEngine
->apiLock
);
2800 return FACTVARIABLEINDEX_INVALID
;
2803 uint32_t FACTCue_SetVariable(
2814 if (nIndex
== FACTINDEX_INVALID
)
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(
2831 FAudio_PlatformUnlockMutex(pCue
->parentBank
->parentEngine
->apiLock
);
2835 uint32_t FACTCue_GetVariable(
2847 if (nIndex
== FACTINDEX_INVALID
)
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
;
2864 *nValue
= pCue
->variableValues
[nIndex
];
2867 FAudio_PlatformUnlockMutex(pCue
->parentBank
->parentEngine
->apiLock
);
2871 uint32_t FACTCue_Pause(FACTCue
*pCue
, int32_t fPause
)
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
2890 /* Store elapsed time */
2891 pCue
->elapsed
+= FAudio_timems() - pCue
->start
;
2893 /* All we do is set the flag, not much to see here */
2896 pCue
->state
|= FACT_STATE_PAUSED
;
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
)
2915 pCue
->playingSound
->tracks
[i
].activeWave
.wave
,
2922 FAudio_PlatformUnlockMutex(pCue
->parentBank
->parentEngine
->apiLock
);
2926 uint32_t FACTCue_GetProperties(
2928 FACTCueInstanceProperties
**ppProperties
2932 FACTCueInstanceProperties
*cueProps
;
2933 FACTVariationProperties
*varProps
;
2934 FACTSoundProperties
*sndProps
;
2935 FACTWaveInstanceProperties waveProps
;
2941 FAudio_PlatformLockMutex(pCue
->parentBank
->parentEngine
->apiLock
);
2943 /* Alloc container (including variable length array space) */
2944 allocSize
= sizeof(FACTCueInstanceProperties
);
2945 if (pCue
->playingSound
!= NULL
)
2948 sizeof(FACTTrackProperties
) *
2949 pCue
->playingSound
->sound
->trackCount
2952 cueProps
= (FACTCueInstanceProperties
*) pCue
->parentBank
->parentEngine
->pMalloc(
2955 FAudio_zero(cueProps
, allocSize
);
2957 /* Cue Properties */
2958 FACTSoundBank_GetCueProperties(
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
;
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
,
3005 sndProps
->arrTrackProperties
[i
].duration
= (uint32_t) (
3007 (float) waveProps
.properties
.durationInSamples
/
3008 (float) waveProps
.properties
.format
.nSamplesPerSec
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
;
3027 uint32_t FACTCue_SetOutputVoices(
3029 const FAudioVoiceSends
*pSendList
/* Optional XAUDIO2_VOICE_SENDS */
3035 uint32_t FACTCue_SetOutputVoiceMatrix(
3037 const FAudioVoice
*pDestinationVoice
, /* Optional! */
3038 uint32_t SourceChannels
,
3039 uint32_t DestinationChannels
,
3040 const float *pLevelMatrix
/* SourceChannels * DestinationChannels */
3046 /* vim: set noexpandtab shiftwidth=8 tabstop=8: */