Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/faudio/src/FACT.c
4389 views
1
/* FAudio - XAudio Reimplementation for FNA
2
*
3
* Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team
4
*
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.
8
*
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:
12
*
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.
17
*
18
* 2. Altered source versions must be plainly marked as such, and must not be
19
* misrepresented as being the original software.
20
*
21
* 3. This notice may not be removed or altered from any source distribution.
22
*
23
* Ethan "flibitijibibo" Lee <[email protected]>
24
*
25
*/
26
27
#include "FAudioFX.h"
28
#include "FACT_internal.h"
29
30
/* AudioEngine implementation */
31
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
42
);
43
}
44
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)
56
{
57
return -1; /* TODO: E_OUTOFMEMORY */
58
}
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;
68
}
69
70
uint32_t FACTAudioEngine_AddRef(FACTAudioEngine *pEngine)
71
{
72
FAudio_PlatformLockMutex(pEngine->apiLock);
73
pEngine->refcount += 1;
74
FAudio_PlatformUnlockMutex(pEngine->apiLock);
75
return pEngine->refcount;
76
}
77
78
uint32_t FACTAudioEngine_Release(FACTAudioEngine *pEngine)
79
{
80
FAudio_PlatformLockMutex(pEngine->apiLock);
81
pEngine->refcount -= 1;
82
if (pEngine->refcount > 0)
83
{
84
FAudio_PlatformUnlockMutex(pEngine->apiLock);
85
return pEngine->refcount;
86
}
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)
93
{
94
pEngine->pFree(pEngine->settings);
95
}
96
pEngine->pFree(pEngine);
97
FAudio_PlatformRelease();
98
return 0;
99
}
100
101
uint32_t FACTAudioEngine_GetRendererCount(
102
FACTAudioEngine *pEngine,
103
uint16_t *pnRendererCount
104
) {
105
FAudio_PlatformLockMutex(pEngine->apiLock);
106
*pnRendererCount = (uint16_t) FAudio_PlatformGetDeviceCount();
107
FAudio_PlatformUnlockMutex(pEngine->apiLock);
108
return 0;
109
}
110
111
uint32_t FACTAudioEngine_GetRendererDetails(
112
FACTAudioEngine *pEngine,
113
uint16_t nRendererIndex,
114
FACTRendererDetails *pRendererDetails
115
) {
116
FAudioDeviceDetails deviceDetails;
117
118
FAudio_PlatformLockMutex(pEngine->apiLock);
119
120
FAudio_PlatformGetDeviceDetails(
121
nRendererIndex,
122
&deviceDetails
123
);
124
FAudio_memcpy(
125
pRendererDetails->rendererID,
126
deviceDetails.DeviceID,
127
sizeof(int16_t) * 0xFF
128
);
129
FAudio_memcpy(
130
pRendererDetails->displayName,
131
deviceDetails.DisplayName,
132
sizeof(int16_t) * 0xFF
133
);
134
/* FIXME: Which defaults does it care about...? */
135
pRendererDetails->defaultDevice = (deviceDetails.Role & (
136
FAudioGlobalDefaultDevice |
137
FAudioDefaultGameDevice
138
)) != 0;
139
140
FAudio_PlatformUnlockMutex(pEngine->apiLock);
141
return 0;
142
}
143
144
uint32_t FACTAudioEngine_GetFinalMixFormat(
145
FACTAudioEngine *pEngine,
146
FAudioWaveFormatExtensible *pFinalMixFormat
147
) {
148
FAudio_PlatformLockMutex(pEngine->apiLock);
149
FAudio_memcpy(
150
pFinalMixFormat,
151
&pEngine->audio->mixFormat,
152
sizeof(FAudioWaveFormatExtensible)
153
);
154
FAudio_PlatformUnlockMutex(pEngine->apiLock);
155
return 0;
156
}
157
158
uint32_t FACTAudioEngine_Initialize(
159
FACTAudioEngine *pEngine,
160
const FACTRuntimeParameters *pParams
161
) {
162
uint32_t parseRet;
163
uint32_t deviceIndex;
164
FAudioVoiceDetails masterDetails;
165
FAudioEffectDescriptor reverbDesc;
166
FAudioEffectChain reverbChain;
167
168
FAudio_PlatformLockMutex(pEngine->apiLock);
169
170
if (!pParams->pGlobalSettingsBuffer || pParams->globalSettingsBufferSize == 0)
171
{
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;
178
179
pEngine->categories = (FACTAudioCategory*) pEngine->pMalloc(
180
sizeof(FACTAudioCategory) * pEngine->categoryCount
181
);
182
pEngine->categoryNames = (char**) pEngine->pMalloc(
183
sizeof(char*) * pEngine->categoryCount
184
);
185
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;
197
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;
209
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;
221
222
pEngine->variables = NULL;
223
pEngine->variableNames = NULL;
224
pEngine->globalVariableValues = NULL;
225
pEngine->rpcs = NULL;
226
pEngine->dspPresets = NULL;
227
}
228
else
229
{
230
/* Parse the file */
231
parseRet = FACT_INTERNAL_ParseAudioEngine(pEngine, pParams);
232
if (parseRet != 0)
233
{
234
FAudio_PlatformUnlockMutex(pEngine->apiLock);
235
return parseRet;
236
}
237
}
238
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;
245
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)
251
{
252
pEngine->pReadFile = FACT_INTERNAL_DefaultReadFile;
253
}
254
if (pEngine->pGetOverlappedResult == NULL)
255
{
256
pEngine->pGetOverlappedResult = FACT_INTERNAL_DefaultGetOverlappedResult;
257
}
258
259
/* Init the FAudio subsystem */
260
pEngine->audio = pParams->pXAudio2;
261
if (pEngine->audio == NULL)
262
{
263
FAudio_assert(pParams->pMasteringVoice == NULL);
264
FAudioCreate(&pEngine->audio, 0, FAUDIO_DEFAULT_PROCESSOR);
265
}
266
267
/* Create the audio device */
268
pEngine->master = pParams->pMasteringVoice;
269
if (pEngine->master == NULL)
270
{
271
if (pParams->pRendererID == NULL || pParams->pRendererID[0] == 0)
272
{
273
deviceIndex = 0;
274
}
275
else
276
{
277
deviceIndex = pParams->pRendererID[0] - L'0';
278
if (deviceIndex > FAudio_PlatformGetDeviceCount())
279
{
280
deviceIndex = 0;
281
}
282
}
283
if (FAudio_CreateMasteringVoice(
284
pEngine->audio,
285
&pEngine->master,
286
FAUDIO_DEFAULT_CHANNELS,
287
FAUDIO_DEFAULT_SAMPLERATE,
288
0,
289
deviceIndex,
290
NULL
291
) != 0) {
292
FAudio_Release(pEngine->audio);
293
FAudio_PlatformUnlockMutex(pEngine->apiLock);
294
return FAUDIO_E_INVALID_CALL;
295
}
296
}
297
298
/* Create the reverb effect, if applicable */
299
if (pEngine->dspPresetCount > 0) /* Never more than 1...? */
300
{
301
FAudioVoice_GetVoiceDetails(pEngine->master, &masterDetails);
302
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;
309
310
/* Reverb submix voice... */
311
FAudio_CreateSubmixVoice(
312
pEngine->audio,
313
&pEngine->reverbVoice,
314
1, /* Reverb will be omnidirectional */
315
masterDetails.InputSampleRate,
316
0,
317
0,
318
NULL,
319
&reverbChain
320
);
321
322
/* We can release now, the submix owns this! */
323
FAPOBase_Release((FAPOBase*) reverbDesc.pEffect);
324
}
325
326
pEngine->initialized = 1;
327
pEngine->apiThread = FAudio_PlatformCreateThread(
328
FACT_INTERNAL_APIThread,
329
"FACT Thread",
330
pEngine
331
);
332
333
FAudio_PlatformUnlockMutex(pEngine->apiLock);
334
return 0;
335
}
336
337
uint32_t FACTAudioEngine_ShutDown(FACTAudioEngine *pEngine)
338
{
339
uint32_t i, refcount;
340
FAudioMutex mutex;
341
FAudioMallocFunc pMalloc;
342
FAudioFreeFunc pFree;
343
FAudioReallocFunc pRealloc;
344
345
/* Close thread, then lock ASAP */
346
pEngine->initialized = 0;
347
FAudio_PlatformWaitThread(pEngine->apiThread, NULL);
348
FAudio_PlatformLockMutex(pEngine->apiLock);
349
350
/* Stop the platform stream before freeing stuff! */
351
if (pEngine->audio != NULL)
352
{
353
FAudio_StopEngine(pEngine->audio);
354
}
355
356
/* Purge All pending notifactions */
357
while (pEngine->wb_notifications_list)
358
{
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);
362
}
363
364
pEngine->notifications = 0;
365
366
/* This method destroys all existing cues, sound banks, and wave banks.
367
* It blocks until all cues are destroyed.
368
*/
369
while (pEngine->wbList != NULL)
370
{
371
FACTWaveBank_Destroy((FACTWaveBank*) pEngine->wbList->entry);
372
}
373
while (pEngine->sbList != NULL)
374
{
375
FACTSoundBank_Destroy((FACTSoundBank*) pEngine->sbList->entry);
376
}
377
378
/* Category data */
379
for (i = 0; i < pEngine->categoryCount; i += 1)
380
{
381
pEngine->pFree(pEngine->categoryNames[i]);
382
}
383
pEngine->pFree(pEngine->categoryNames);
384
pEngine->pFree(pEngine->categories);
385
386
/* Variable data */
387
for (i = 0; i < pEngine->variableCount; i += 1)
388
{
389
pEngine->pFree(pEngine->variableNames[i]);
390
}
391
pEngine->pFree(pEngine->variableNames);
392
pEngine->pFree(pEngine->variables);
393
pEngine->pFree(pEngine->globalVariableValues);
394
395
/* RPC data */
396
for (i = 0; i < pEngine->rpcCount; i += 1)
397
{
398
pEngine->pFree(pEngine->rpcs[i].points);
399
}
400
pEngine->pFree(pEngine->rpcs);
401
pEngine->pFree(pEngine->rpcCodes);
402
403
/* DSP data */
404
for (i = 0; i < pEngine->dspPresetCount; i += 1)
405
{
406
pEngine->pFree(pEngine->dspPresets[i].parameters);
407
}
408
pEngine->pFree(pEngine->dspPresets);
409
pEngine->pFree(pEngine->dspPresetCodes);
410
411
/* Audio resources */
412
if (pEngine->reverbVoice != NULL)
413
{
414
FAudioVoice_DestroyVoice(pEngine->reverbVoice);
415
}
416
if (pEngine->master != NULL)
417
{
418
FAudioVoice_DestroyVoice(pEngine->master);
419
}
420
if (pEngine->audio != NULL)
421
{
422
FAudio_Release(pEngine->audio);
423
}
424
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;
437
438
FAudio_PlatformUnlockMutex(pEngine->apiLock);
439
return 0;
440
}
441
442
uint32_t FACTAudioEngine_DoWork(FACTAudioEngine *pEngine)
443
{
444
uint8_t i;
445
FACTCue *cue;
446
LinkedList *list;
447
FACTNotification *note;
448
449
FAudio_PlatformLockMutex(pEngine->apiLock);
450
451
while (pEngine->wb_notifications_list)
452
{
453
note = (FACTNotification*) pEngine->wb_notifications_list->entry;
454
pEngine->notificationCallback(note);
455
LinkedList_RemoveEntry(&pEngine->wb_notifications_list, note, pEngine->apiLock, pEngine->pFree);
456
}
457
458
list = pEngine->sbList;
459
while (list != NULL)
460
{
461
cue = ((FACTSoundBank*) list->entry)->cueList;
462
while (cue != NULL)
463
{
464
if (cue->playingSound != NULL)
465
for (i = 0; i < cue->playingSound->sound->trackCount; i += 1)
466
{
467
if ( cue->playingSound->tracks[i].upcomingWave.wave == NULL &&
468
cue->playingSound->tracks[i].waveEvtInst->loopCount > 0 )
469
{
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
477
);
478
}
479
}
480
cue = cue->next;
481
}
482
list = list->next;
483
}
484
485
FAudio_PlatformUnlockMutex(pEngine->apiLock);
486
return 0;
487
}
488
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
496
) {
497
uint32_t retval;
498
FAudio_PlatformLockMutex(pEngine->apiLock);
499
retval = FACT_INTERNAL_ParseSoundBank(
500
pEngine,
501
pvBuffer,
502
dwSize,
503
ppSoundBank
504
);
505
FAudio_PlatformUnlockMutex(pEngine->apiLock);
506
return retval;
507
}
508
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
516
) {
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),
523
0,
524
0,
525
FACT_INTERNAL_DefaultReadFile,
526
FACT_INTERNAL_DefaultGetOverlappedResult,
527
0,
528
ppWaveBank
529
);
530
if (pEngine->notifications & NOTIFY_WAVEBANKPREPARED)
531
{
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);
537
}
538
FAudio_PlatformUnlockMutex(pEngine->apiLock);
539
return retval;
540
}
541
542
uint32_t FACTAudioEngine_CreateStreamingWaveBank(
543
FACTAudioEngine *pEngine,
544
const FACTStreamingParameters *pParms,
545
FACTWaveBank **ppWaveBank
546
) {
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 )
552
{
553
/* Our I/O doesn't care about packets, set to 0 as an optimization */
554
packetSize = 0;
555
}
556
else
557
{
558
packetSize = pParms->packetSize * 2048;
559
}
560
retval = FACT_INTERNAL_ParseWaveBank(
561
pEngine,
562
pParms->file,
563
pParms->offset,
564
packetSize,
565
pEngine->pReadFile,
566
pEngine->pGetOverlappedResult,
567
1,
568
ppWaveBank
569
);
570
if (pEngine->notifications & NOTIFY_WAVEBANKPREPARED)
571
{
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);
577
}
578
FAudio_PlatformUnlockMutex(pEngine->apiLock);
579
return retval;
580
}
581
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
591
) {
592
/* TODO: FACTWave */
593
return 0;
594
}
595
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
605
) {
606
/* TODO: FACTWave */
607
return 0;
608
}
609
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
621
) {
622
/* TODO: FACTWave */
623
return 0;
624
}
625
626
uint32_t FACTAudioEngine_RegisterNotification(
627
FACTAudioEngine *pEngine,
628
const FACTNotificationDescription *pNotificationDescription
629
) {
630
FAudio_assert(pEngine != NULL);
631
FAudio_assert(pNotificationDescription != NULL);
632
FAudio_assert(pEngine->notificationCallback != NULL);
633
634
FAudio_PlatformLockMutex(pEngine->apiLock);
635
636
#define HANDLE_PERSIST(nt) \
637
if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_##nt) \
638
{ \
639
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST) \
640
{ \
641
pEngine->notifications |= NOTIFY_##nt; \
642
PERSIST_ACTION \
643
} \
644
else \
645
{ \
646
FAudio_assert(0 && "TODO: "#nt" notification!"); \
647
} \
648
}
649
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)
656
{
657
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
658
{
659
pEngine->notifications |= NOTIFY_CUEDESTROY;
660
pEngine->cue_context = pNotificationDescription->pvContext;
661
}
662
else
663
{
664
pNotificationDescription->pCue->notifyOnDestroy = 1;
665
pNotificationDescription->pCue->usercontext = pNotificationDescription->pvContext;
666
}
667
}
668
#undef PERSIST_ACTION
669
670
/* Markers */
671
#define PERSIST_ACTION
672
else HANDLE_PERSIST(MARKER)
673
#undef PERSIST_ACTION
674
675
/* SoundBank/WaveBank Destruction */
676
else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED)
677
{
678
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
679
{
680
pEngine->notifications |= NOTIFY_SOUNDBANKDESTROY;
681
pEngine->sb_context = pNotificationDescription->pvContext;
682
}
683
else
684
{
685
pNotificationDescription->pSoundBank->notifyOnDestroy = 1;
686
pNotificationDescription->pSoundBank->usercontext = pNotificationDescription->pvContext;
687
}
688
}
689
else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED)
690
{
691
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
692
{
693
pEngine->notifications |= NOTIFY_WAVEBANKDESTROY;
694
pEngine->wb_context = pNotificationDescription->pvContext;
695
}
696
else
697
{
698
pNotificationDescription->pWaveBank->notifyOnDestroy = 1;
699
pNotificationDescription->pWaveBank->usercontext = pNotificationDescription->pvContext;
700
}
701
}
702
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
710
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)
718
{
719
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
720
{
721
pEngine->notifications |= NOTIFY_WAVEDESTROY;
722
pEngine->wave_context = pNotificationDescription->pvContext;
723
}
724
else
725
{
726
pNotificationDescription->pWave->notifyOnDestroy = 1;
727
pNotificationDescription->pWave->usercontext = pNotificationDescription->pvContext;
728
}
729
}
730
#undef PERSIST_ACTION
731
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
737
738
/* Anything else? */
739
else
740
{
741
FAudio_assert(0 && "TODO: Unimplemented notification!");
742
}
743
744
#undef HANDLE_PERSIST
745
746
FAudio_PlatformUnlockMutex(pEngine->apiLock);
747
return 0;
748
}
749
750
uint32_t FACTAudioEngine_UnRegisterNotification(
751
FACTAudioEngine *pEngine,
752
const FACTNotificationDescription *pNotificationDescription
753
) {
754
FAudio_assert(pEngine != NULL);
755
FAudio_assert(pNotificationDescription != NULL);
756
FAudio_assert(pEngine->notificationCallback != NULL);
757
758
FAudio_PlatformLockMutex(pEngine->apiLock);
759
760
#define HANDLE_PERSIST(nt) \
761
if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_##nt) \
762
{ \
763
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST) \
764
{ \
765
pEngine->notifications &= ~NOTIFY_##nt; \
766
PERSIST_ACTION \
767
} \
768
else \
769
{ \
770
FAudio_assert(0 && "TODO: "#nt" notification!"); \
771
} \
772
}
773
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)
780
{
781
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
782
{
783
pEngine->notifications &= ~NOTIFY_CUEDESTROY;
784
pEngine->cue_context = pNotificationDescription->pvContext;
785
}
786
else
787
{
788
pNotificationDescription->pCue->notifyOnDestroy = 0;
789
pNotificationDescription->pCue->usercontext = pNotificationDescription->pvContext;
790
}
791
}
792
#undef PERSIST_ACTION
793
794
/* Markers */
795
#define PERSIST_ACTION
796
else HANDLE_PERSIST(MARKER)
797
#undef PERSIST_ACTION
798
799
/* SoundBank/WaveBank Destruction */
800
else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED)
801
{
802
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
803
{
804
pEngine->notifications &= ~NOTIFY_SOUNDBANKDESTROY;
805
pEngine->sb_context = pNotificationDescription->pvContext;
806
}
807
else
808
{
809
pNotificationDescription->pSoundBank->notifyOnDestroy = 0;
810
pNotificationDescription->pSoundBank->usercontext = pNotificationDescription->pvContext;
811
}
812
}
813
else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED)
814
{
815
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
816
{
817
pEngine->notifications &= ~NOTIFY_WAVEBANKDESTROY;
818
pEngine->wb_context = pNotificationDescription->pvContext;
819
}
820
else
821
{
822
pNotificationDescription->pWaveBank->notifyOnDestroy = 0;
823
pNotificationDescription->pWaveBank->usercontext = pNotificationDescription->pvContext;
824
}
825
}
826
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
834
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)
842
{
843
if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
844
{
845
pEngine->notifications &= ~NOTIFY_WAVEDESTROY;
846
pEngine->wave_context = pNotificationDescription->pvContext;
847
}
848
else
849
{
850
pNotificationDescription->pWave->notifyOnDestroy = 0;
851
pNotificationDescription->pWave->usercontext = pNotificationDescription->pvContext;
852
}
853
}
854
#undef PERSIST_ACTION
855
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
861
862
/* Anything else? */
863
else
864
{
865
FAudio_assert(0 && "TODO: Unimplemented notification!");
866
}
867
868
#undef HANDLE_PERSIST
869
870
FAudio_PlatformUnlockMutex(pEngine->apiLock);
871
return 0;
872
}
873
874
uint16_t FACTAudioEngine_GetCategory(
875
FACTAudioEngine *pEngine,
876
const char *szFriendlyName
877
) {
878
uint16_t i;
879
FAudio_PlatformLockMutex(pEngine->apiLock);
880
for (i = 0; i < pEngine->categoryCount; i += 1)
881
{
882
if (FAudio_strcmp(szFriendlyName, pEngine->categoryNames[i]) == 0)
883
{
884
FAudio_PlatformUnlockMutex(pEngine->apiLock);
885
return i;
886
}
887
}
888
FAudio_PlatformUnlockMutex(pEngine->apiLock);
889
return FACTCATEGORY_INVALID;
890
}
891
892
uint8_t FACT_INTERNAL_IsInCategory(
893
FACTAudioEngine *engine,
894
uint16_t target,
895
uint16_t category
896
) {
897
FACTAudioCategory *cat;
898
899
/* Same category, no need to go on a crazy hunt */
900
if (category == target)
901
{
902
return 1;
903
}
904
905
/* Right, on with the crazy hunt */
906
cat = &engine->categories[category];
907
while (cat->parentCategory != -1)
908
{
909
if (cat->parentCategory == target)
910
{
911
return 1;
912
}
913
cat = &engine->categories[cat->parentCategory];
914
}
915
return 0;
916
}
917
918
uint32_t FACTAudioEngine_Stop(
919
FACTAudioEngine *pEngine,
920
uint16_t nCategory,
921
uint32_t dwFlags
922
) {
923
FACTCue *cue, *backup;
924
LinkedList *list;
925
926
FAudio_PlatformLockMutex(pEngine->apiLock);
927
list = pEngine->sbList;
928
while (list != NULL)
929
{
930
cue = ((FACTSoundBank*) list->entry)->cueList;
931
while (cue != NULL)
932
{
933
if ( cue->playingSound != NULL &&
934
FACT_INTERNAL_IsInCategory(
935
pEngine,
936
nCategory,
937
cue->playingSound->sound->category
938
) )
939
{
940
if ( dwFlags == FACT_FLAG_STOP_IMMEDIATE &&
941
cue->managed )
942
{
943
/* Just blow this up now */
944
backup = cue->next;
945
FACTCue_Destroy(cue);
946
cue = backup;
947
}
948
else
949
{
950
/* If managed, the mixer will destroy for us */
951
FACTCue_Stop(cue, dwFlags);
952
cue = cue->next;
953
}
954
}
955
else
956
{
957
cue = cue->next;
958
}
959
}
960
list = list->next;
961
}
962
FAudio_PlatformUnlockMutex(pEngine->apiLock);
963
return 0;
964
}
965
966
uint32_t FACTAudioEngine_SetVolume(
967
FACTAudioEngine *pEngine,
968
uint16_t nCategory,
969
float volume
970
) {
971
uint16_t i;
972
FAudio_PlatformLockMutex(pEngine->apiLock);
973
pEngine->categories[nCategory].currentVolume = (
974
pEngine->categories[nCategory].volume *
975
volume
976
);
977
for (i = 0; i < pEngine->categoryCount; i += 1)
978
{
979
if (pEngine->categories[i].parentCategory == nCategory)
980
{
981
FACTAudioEngine_SetVolume(
982
pEngine,
983
i,
984
pEngine->categories[i].currentVolume
985
);
986
}
987
}
988
FAudio_PlatformUnlockMutex(pEngine->apiLock);
989
return 0;
990
}
991
992
uint32_t FACTAudioEngine_Pause(
993
FACTAudioEngine *pEngine,
994
uint16_t nCategory,
995
int32_t fPause
996
) {
997
FACTCue *cue;
998
LinkedList *list;
999
1000
FAudio_PlatformLockMutex(pEngine->apiLock);
1001
list = pEngine->sbList;
1002
while (list != NULL)
1003
{
1004
cue = ((FACTSoundBank*) list->entry)->cueList;
1005
while (cue != NULL)
1006
{
1007
if ( cue->playingSound != NULL &&
1008
FACT_INTERNAL_IsInCategory(
1009
pEngine,
1010
nCategory,
1011
cue->playingSound->sound->category
1012
) )
1013
{
1014
FACTCue_Pause(cue, fPause);
1015
}
1016
cue = cue->next;
1017
}
1018
list = list->next;
1019
}
1020
FAudio_PlatformUnlockMutex(pEngine->apiLock);
1021
return 0;
1022
}
1023
1024
uint16_t FACTAudioEngine_GetGlobalVariableIndex(
1025
FACTAudioEngine *pEngine,
1026
const char *szFriendlyName
1027
) {
1028
uint16_t i;
1029
FAudio_PlatformLockMutex(pEngine->apiLock);
1030
for (i = 0; i < pEngine->variableCount; i += 1)
1031
{
1032
if ( FAudio_strcmp(szFriendlyName, pEngine->variableNames[i]) == 0 &&
1033
!(pEngine->variables[i].accessibility & 0x04) )
1034
{
1035
FAudio_PlatformUnlockMutex(pEngine->apiLock);
1036
return i;
1037
}
1038
}
1039
FAudio_PlatformUnlockMutex(pEngine->apiLock);
1040
return FACTVARIABLEINDEX_INVALID;
1041
}
1042
1043
uint32_t FACTAudioEngine_SetGlobalVariable(
1044
FACTAudioEngine *pEngine,
1045
uint16_t nIndex,
1046
float nValue
1047
) {
1048
FACTVariable *var;
1049
1050
FAudio_PlatformLockMutex(pEngine->apiLock);
1051
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
1060
);
1061
1062
FAudio_PlatformUnlockMutex(pEngine->apiLock);
1063
return 0;
1064
}
1065
1066
uint32_t FACTAudioEngine_GetGlobalVariable(
1067
FACTAudioEngine *pEngine,
1068
uint16_t nIndex,
1069
float *pnValue
1070
) {
1071
FACTVariable *var;
1072
1073
FAudio_PlatformLockMutex(pEngine->apiLock);
1074
1075
var = &pEngine->variables[nIndex];
1076
FAudio_assert(var->accessibility & 0x01);
1077
FAudio_assert(!(var->accessibility & 0x04));
1078
*pnValue = pEngine->globalVariableValues[nIndex];
1079
1080
FAudio_PlatformUnlockMutex(pEngine->apiLock);
1081
return 0;
1082
}
1083
1084
/* SoundBank implementation */
1085
1086
uint16_t FACTSoundBank_GetCueIndex(
1087
FACTSoundBank *pSoundBank,
1088
const char *szFriendlyName
1089
) {
1090
uint16_t i;
1091
if (pSoundBank == NULL)
1092
{
1093
return FACTINDEX_INVALID;
1094
}
1095
1096
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1097
if (pSoundBank->cueNames != NULL)
1098
for (i = 0; i < pSoundBank->cueCount; i += 1)
1099
{
1100
if (FAudio_strcmp(szFriendlyName, pSoundBank->cueNames[i]) == 0)
1101
{
1102
FAudio_PlatformUnlockMutex(
1103
pSoundBank->parentEngine->apiLock
1104
);
1105
return i;
1106
}
1107
}
1108
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1109
return FACTINDEX_INVALID;
1110
}
1111
1112
uint32_t FACTSoundBank_GetNumCues(
1113
FACTSoundBank *pSoundBank,
1114
uint16_t *pnNumCues
1115
) {
1116
if (pSoundBank == NULL)
1117
{
1118
*pnNumCues = 0;
1119
return 0;
1120
}
1121
1122
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1123
*pnNumCues = pSoundBank->cueCount;
1124
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1125
return 0;
1126
}
1127
1128
uint32_t FACTSoundBank_GetCueProperties(
1129
FACTSoundBank *pSoundBank,
1130
uint16_t nCueIndex,
1131
FACTCueProperties *pProperties
1132
) {
1133
uint16_t i;
1134
if (pSoundBank == NULL)
1135
{
1136
return 1;
1137
}
1138
1139
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1140
1141
if (pSoundBank->cueNames == NULL)
1142
{
1143
FAudio_zero(pProperties->friendlyName, 0xFF);
1144
}
1145
else
1146
{
1147
FAudio_strlcpy(
1148
pProperties->friendlyName,
1149
pSoundBank->cueNames[nCueIndex],
1150
0xFF
1151
);
1152
}
1153
if (!(pSoundBank->cues[nCueIndex].flags & 0x04))
1154
{
1155
for (i = 0; i < pSoundBank->variationCount; i += 1)
1156
{
1157
if (pSoundBank->variationCodes[i] == pSoundBank->cues[nCueIndex].sbCode)
1158
{
1159
break;
1160
}
1161
}
1162
1163
FAudio_assert(i < pSoundBank->variationCount && "Variation table not found!");
1164
1165
if (pSoundBank->variations[i].flags == 3)
1166
{
1167
pProperties->interactive = 1;
1168
pProperties->iaVariableIndex = pSoundBank->variations[i].variable;
1169
}
1170
else
1171
{
1172
pProperties->interactive = 0;
1173
pProperties->iaVariableIndex = 0;
1174
}
1175
pProperties->numVariations = pSoundBank->variations[i].entryCount;
1176
}
1177
else
1178
{
1179
pProperties->interactive = 0;
1180
pProperties->iaVariableIndex = 0;
1181
pProperties->numVariations = 0;
1182
}
1183
pProperties->maxInstances = pSoundBank->cues[nCueIndex].instanceLimit;
1184
pProperties->currentInstances = pSoundBank->cues[nCueIndex].instanceCount;
1185
1186
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1187
return 0;
1188
}
1189
1190
uint32_t FACTSoundBank_Prepare(
1191
FACTSoundBank *pSoundBank,
1192
uint16_t nCueIndex,
1193
uint32_t dwFlags,
1194
int32_t timeOffset,
1195
FACTCue** ppCue
1196
) {
1197
uint16_t i;
1198
FACTCue *latest;
1199
1200
if (pSoundBank == NULL)
1201
{
1202
*ppCue = NULL;
1203
return 1;
1204
}
1205
1206
*ppCue = (FACTCue*) pSoundBank->parentEngine->pMalloc(sizeof(FACTCue));
1207
FAudio_zero(*ppCue, sizeof(FACTCue));
1208
1209
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1210
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;
1218
1219
/* Sound data */
1220
(*ppCue)->data = &pSoundBank->cues[nCueIndex];
1221
if ((*ppCue)->data->flags & 0x04)
1222
{
1223
for (i = 0; i < pSoundBank->soundCount; i += 1)
1224
{
1225
if ((*ppCue)->data->sbCode == pSoundBank->soundCodes[i])
1226
{
1227
(*ppCue)->sound = &pSoundBank->sounds[i];
1228
break;
1229
}
1230
}
1231
}
1232
else
1233
{
1234
for (i = 0; i < pSoundBank->variationCount; i += 1)
1235
{
1236
if ((*ppCue)->data->sbCode == pSoundBank->variationCodes[i])
1237
{
1238
(*ppCue)->variation = &pSoundBank->variations[i];
1239
break;
1240
}
1241
}
1242
if ((*ppCue)->variation && (*ppCue)->variation->flags == 3)
1243
{
1244
(*ppCue)->interactive = pSoundBank->parentEngine->variables[
1245
(*ppCue)->variation->variable
1246
].initialValue;
1247
}
1248
}
1249
1250
/* Instance data */
1251
(*ppCue)->variableValues = (float*) pSoundBank->parentEngine->pMalloc(
1252
sizeof(float) * pSoundBank->parentEngine->variableCount
1253
);
1254
for (i = 0; i < pSoundBank->parentEngine->variableCount; i += 1)
1255
{
1256
(*ppCue)->variableValues[i] =
1257
pSoundBank->parentEngine->variables[i].initialValue;
1258
}
1259
1260
/* Playback */
1261
(*ppCue)->state = FACT_STATE_PREPARED;
1262
1263
/* Add to the SoundBank Cue list */
1264
if (pSoundBank->cueList == NULL)
1265
{
1266
pSoundBank->cueList = *ppCue;
1267
}
1268
else
1269
{
1270
latest = pSoundBank->cueList;
1271
while (latest->next != NULL)
1272
{
1273
latest = latest->next;
1274
}
1275
latest->next = *ppCue;
1276
}
1277
1278
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1279
return 0;
1280
}
1281
1282
uint32_t FACTSoundBank_Play(
1283
FACTSoundBank *pSoundBank,
1284
uint16_t nCueIndex,
1285
uint32_t dwFlags,
1286
int32_t timeOffset,
1287
FACTCue** ppCue /* Optional! */
1288
) {
1289
FACTCue *result;
1290
if (pSoundBank == NULL)
1291
{
1292
if (ppCue != NULL)
1293
{
1294
*ppCue = NULL;
1295
}
1296
return 1;
1297
}
1298
1299
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1300
1301
FACTSoundBank_Prepare(
1302
pSoundBank,
1303
nCueIndex,
1304
dwFlags,
1305
timeOffset,
1306
&result
1307
);
1308
if (ppCue != NULL)
1309
{
1310
*ppCue = result;
1311
}
1312
else
1313
{
1314
/* AKA we get to Destroy() this ourselves */
1315
result->managed = 1;
1316
}
1317
FACTCue_Play(result);
1318
1319
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1320
return 0;
1321
}
1322
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! */
1330
) {
1331
FACTCue *result;
1332
if (pSoundBank == NULL)
1333
{
1334
if (ppCue != NULL)
1335
{
1336
*ppCue = NULL;
1337
}
1338
return 1;
1339
}
1340
1341
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1342
1343
FACTSoundBank_Prepare(
1344
pSoundBank,
1345
nCueIndex,
1346
dwFlags,
1347
timeOffset,
1348
&result
1349
);
1350
if (ppCue != NULL)
1351
{
1352
*ppCue = result;
1353
}
1354
else
1355
{
1356
/* AKA we get to Destroy() this ourselves */
1357
result->managed = 1;
1358
}
1359
FACT3DApply(pDSPSettings, result);
1360
FACTCue_Play(result);
1361
1362
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1363
return 0;
1364
}
1365
1366
uint32_t FACTSoundBank_Stop(
1367
FACTSoundBank *pSoundBank,
1368
uint16_t nCueIndex,
1369
uint32_t dwFlags
1370
) {
1371
FACTCue *backup, *cue;
1372
if (pSoundBank == NULL)
1373
{
1374
return 1;
1375
}
1376
1377
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1378
cue = pSoundBank->cueList;
1379
while (cue != NULL)
1380
{
1381
if (cue->index == nCueIndex)
1382
{
1383
if ( dwFlags == FACT_FLAG_STOP_IMMEDIATE &&
1384
cue->managed )
1385
{
1386
/* Just blow this up now */
1387
backup = cue->next;
1388
FACTCue_Destroy(cue);
1389
cue = backup;
1390
}
1391
else
1392
{
1393
/* If managed, the mixer will destroy for us */
1394
FACTCue_Stop(cue, dwFlags);
1395
cue = cue->next;
1396
}
1397
}
1398
else
1399
{
1400
cue = cue->next;
1401
}
1402
}
1403
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1404
return 0;
1405
}
1406
1407
uint32_t FACTSoundBank_Destroy(FACTSoundBank *pSoundBank)
1408
{
1409
uint16_t i, j, k;
1410
FAudioMutex mutex;
1411
FACTNotification note;
1412
if (pSoundBank == NULL)
1413
{
1414
return 1;
1415
}
1416
1417
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1418
1419
/* Synchronously destroys all cues that are associated */
1420
while (pSoundBank->cueList != NULL)
1421
{
1422
FACTCue_Destroy(pSoundBank->cueList);
1423
}
1424
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
1431
);
1432
1433
/* SoundBank Name */
1434
pSoundBank->parentEngine->pFree(pSoundBank->name);
1435
1436
/* Cue data */
1437
pSoundBank->parentEngine->pFree(pSoundBank->cues);
1438
1439
/* WaveBank Name data */
1440
for (i = 0; i < pSoundBank->wavebankCount; i += 1)
1441
{
1442
pSoundBank->parentEngine->pFree(pSoundBank->wavebankNames[i]);
1443
}
1444
pSoundBank->parentEngine->pFree(pSoundBank->wavebankNames);
1445
1446
/* Sound data */
1447
for (i = 0; i < pSoundBank->soundCount; i += 1)
1448
{
1449
for (j = 0; j < pSoundBank->sounds[i].trackCount; j += 1)
1450
{
1451
for (k = 0; k < pSoundBank->sounds[i].tracks[j].eventCount; k += 1)
1452
{
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) )
1459
{
1460
if (pSoundBank->sounds[i].tracks[j].events[k].wave.isComplex)
1461
{
1462
pSoundBank->parentEngine->pFree(
1463
pSoundBank->sounds[i].tracks[j].events[k].wave.complex.tracks
1464
);
1465
pSoundBank->parentEngine->pFree(
1466
pSoundBank->sounds[i].tracks[j].events[k].wave.complex.wavebanks
1467
);
1468
pSoundBank->parentEngine->pFree(
1469
pSoundBank->sounds[i].tracks[j].events[k].wave.complex.weights
1470
);
1471
}
1472
}
1473
#undef MATCH
1474
}
1475
pSoundBank->parentEngine->pFree(
1476
pSoundBank->sounds[i].tracks[j].events
1477
);
1478
}
1479
pSoundBank->parentEngine->pFree(pSoundBank->sounds[i].tracks);
1480
pSoundBank->parentEngine->pFree(pSoundBank->sounds[i].rpcCodes);
1481
pSoundBank->parentEngine->pFree(pSoundBank->sounds[i].dspCodes);
1482
}
1483
pSoundBank->parentEngine->pFree(pSoundBank->sounds);
1484
pSoundBank->parentEngine->pFree(pSoundBank->soundCodes);
1485
1486
/* Variation data */
1487
for (i = 0; i < pSoundBank->variationCount; i += 1)
1488
{
1489
pSoundBank->parentEngine->pFree(
1490
pSoundBank->variations[i].entries
1491
);
1492
}
1493
pSoundBank->parentEngine->pFree(pSoundBank->variations);
1494
pSoundBank->parentEngine->pFree(pSoundBank->variationCodes);
1495
1496
/* Transition data */
1497
for (i = 0; i < pSoundBank->transitionCount; i += 1)
1498
{
1499
pSoundBank->parentEngine->pFree(
1500
pSoundBank->transitions[i].entries
1501
);
1502
}
1503
pSoundBank->parentEngine->pFree(pSoundBank->transitions);
1504
pSoundBank->parentEngine->pFree(pSoundBank->transitionCodes);
1505
1506
/* Cue Name data */
1507
if (pSoundBank->cueNames != NULL)
1508
{
1509
for (i = 0; i < pSoundBank->cueCount; i += 1)
1510
{
1511
pSoundBank->parentEngine->pFree(pSoundBank->cueNames[i]);
1512
}
1513
pSoundBank->parentEngine->pFree(pSoundBank->cueNames);
1514
}
1515
1516
/* Finally. */
1517
if (pSoundBank->notifyOnDestroy || pSoundBank->parentEngine->notifications & NOTIFY_SOUNDBANKDESTROY)
1518
{
1519
note.type = FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED;
1520
note.soundBank.pSoundBank = pSoundBank;
1521
if (pSoundBank->parentEngine->notifications & NOTIFY_SOUNDBANKDESTROY)
1522
{
1523
note.pvContext = pSoundBank->parentEngine->sb_context;
1524
}
1525
else
1526
{
1527
note.pvContext = pSoundBank->usercontext;
1528
}
1529
pSoundBank->parentEngine->notificationCallback(&note);
1530
}
1531
1532
mutex = pSoundBank->parentEngine->apiLock;
1533
pSoundBank->parentEngine->pFree(pSoundBank);
1534
FAudio_PlatformUnlockMutex(mutex);
1535
return 0;
1536
}
1537
1538
uint32_t FACTSoundBank_GetState(
1539
FACTSoundBank *pSoundBank,
1540
uint32_t *pdwState
1541
) {
1542
uint16_t i;
1543
if (pSoundBank == NULL)
1544
{
1545
*pdwState = 0;
1546
return 1;
1547
}
1548
1549
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1550
1551
*pdwState = FACT_STATE_PREPARED;
1552
for (i = 0; i < pSoundBank->cueCount; i += 1)
1553
{
1554
if (pSoundBank->cues[i].instanceCount > 0)
1555
{
1556
*pdwState |= FACT_STATE_INUSE;
1557
FAudio_PlatformUnlockMutex(
1558
pSoundBank->parentEngine->apiLock
1559
);
1560
return 0;
1561
}
1562
}
1563
1564
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1565
return 0;
1566
}
1567
1568
/* WaveBank implementation */
1569
1570
uint32_t FACTWaveBank_Destroy(FACTWaveBank *pWaveBank)
1571
{
1572
uint32_t i;
1573
FACTWave *wave;
1574
FAudioMutex mutex;
1575
FACTNotification note;
1576
if (pWaveBank == NULL)
1577
{
1578
return 1;
1579
}
1580
1581
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1582
1583
/* Synchronously destroys any cues that are using the wavebank */
1584
while (pWaveBank->waveList != NULL)
1585
{
1586
wave = (FACTWave*) pWaveBank->waveList->entry;
1587
if (wave->parentCue != NULL)
1588
{
1589
/* Destroying this Cue destroys the Wave */
1590
FACTCue_Destroy(wave->parentCue);
1591
}
1592
else
1593
{
1594
FACTWave_Destroy(wave);
1595
}
1596
}
1597
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
1604
);
1605
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)
1611
{
1612
for (i = 0; i < pWaveBank->entryCount; i += 1)
1613
{
1614
if (pWaveBank->seekTables[i].entries != NULL)
1615
{
1616
pWaveBank->parentEngine->pFree(
1617
pWaveBank->seekTables[i].entries
1618
);
1619
}
1620
}
1621
pWaveBank->parentEngine->pFree(pWaveBank->seekTables);
1622
}
1623
1624
if (!pWaveBank->streaming)
1625
{
1626
FAudio_close(pWaveBank->io);
1627
}
1628
1629
if (pWaveBank->packetBuffer != NULL)
1630
{
1631
pWaveBank->parentEngine->pFree(pWaveBank->packetBuffer);
1632
}
1633
if (pWaveBank->notifyOnDestroy || pWaveBank->parentEngine->notifications & NOTIFY_WAVEBANKDESTROY)
1634
{
1635
note.type = FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED;
1636
note.waveBank.pWaveBank = pWaveBank;
1637
if (pWaveBank->parentEngine->notifications & NOTIFY_WAVEBANKDESTROY)
1638
{
1639
note.pvContext = pWaveBank->parentEngine->wb_context;
1640
}
1641
else
1642
{
1643
note.pvContext = pWaveBank->usercontext;
1644
}
1645
pWaveBank->parentEngine->notificationCallback(&note);
1646
}
1647
FAudio_PlatformDestroyMutex(pWaveBank->waveLock);
1648
1649
if (pWaveBank->waveBankNames != NULL)
1650
{
1651
pWaveBank->parentEngine->pFree(pWaveBank->waveBankNames);
1652
}
1653
1654
mutex = pWaveBank->parentEngine->apiLock;
1655
pWaveBank->parentEngine->pFree(pWaveBank);
1656
FAudio_PlatformUnlockMutex(mutex);
1657
return 0;
1658
}
1659
1660
uint32_t FACTWaveBank_GetState(
1661
FACTWaveBank *pWaveBank,
1662
uint32_t *pdwState
1663
) {
1664
uint32_t i;
1665
if (pWaveBank == NULL)
1666
{
1667
*pdwState = 0;
1668
return 1;
1669
}
1670
1671
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1672
1673
*pdwState = FACT_STATE_PREPARED;
1674
for (i = 0; i < pWaveBank->entryCount; i += 1)
1675
{
1676
if (pWaveBank->entryRefs[i] > 0)
1677
{
1678
*pdwState |= FACT_STATE_INUSE;
1679
FAudio_PlatformUnlockMutex(
1680
pWaveBank->parentEngine->apiLock
1681
);
1682
return 0;
1683
}
1684
}
1685
1686
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1687
return 0;
1688
}
1689
1690
uint32_t FACTWaveBank_GetNumWaves(
1691
FACTWaveBank *pWaveBank,
1692
uint16_t *pnNumWaves
1693
) {
1694
if (pWaveBank == NULL)
1695
{
1696
*pnNumWaves = 0;
1697
return 1;
1698
}
1699
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1700
*pnNumWaves = pWaveBank->entryCount;
1701
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1702
return 0;
1703
}
1704
1705
uint16_t FACTWaveBank_GetWaveIndex(
1706
FACTWaveBank *pWaveBank,
1707
const char *szFriendlyName
1708
) {
1709
uint16_t i;
1710
char *curName;
1711
if (pWaveBank == NULL || pWaveBank->waveBankNames == NULL)
1712
{
1713
return FACTINDEX_INVALID;
1714
}
1715
1716
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1717
curName = pWaveBank->waveBankNames;
1718
for (i = 0; i < pWaveBank->entryCount; i += 1, curName += 64)
1719
{
1720
if (FAudio_strncmp(szFriendlyName, curName, 64) == 0)
1721
{
1722
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1723
return i;
1724
}
1725
}
1726
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1727
1728
return FACTINDEX_INVALID;
1729
}
1730
1731
uint32_t FACTWaveBank_GetWaveProperties(
1732
FACTWaveBank *pWaveBank,
1733
uint16_t nWaveIndex,
1734
FACTWaveProperties *pWaveProperties
1735
) {
1736
FACTWaveBankEntry *entry;
1737
if (pWaveBank == NULL)
1738
{
1739
return 1;
1740
}
1741
1742
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1743
1744
entry = &pWaveBank->entries[nWaveIndex];
1745
1746
if (pWaveBank->waveBankNames)
1747
{
1748
FAudio_memcpy(
1749
pWaveProperties->friendlyName,
1750
&pWaveBank->waveBankNames[nWaveIndex * 64],
1751
sizeof(pWaveProperties->friendlyName)
1752
);
1753
}
1754
else
1755
{
1756
FAudio_zero(
1757
pWaveProperties->friendlyName,
1758
sizeof(pWaveProperties->friendlyName)
1759
);
1760
}
1761
1762
pWaveProperties->format = entry->Format;
1763
pWaveProperties->durationInSamples = entry->PlayRegion.dwLength;
1764
if (entry->Format.wFormatTag == 0)
1765
{
1766
pWaveProperties->durationInSamples /= (8 << entry->Format.wBitsPerSample) / 8;
1767
pWaveProperties->durationInSamples /= entry->Format.nChannels;
1768
}
1769
else if (entry->Format.wFormatTag == FAUDIO_FORMAT_MSADPCM)
1770
{
1771
pWaveProperties->durationInSamples = (
1772
pWaveProperties->durationInSamples /
1773
((entry->Format.wBlockAlign + 22) * entry->Format.nChannels) *
1774
((entry->Format.wBlockAlign + 16) * 2)
1775
);
1776
}
1777
else
1778
{
1779
FAudio_assert(0 && "Unrecognized wFormatTag!");
1780
}
1781
1782
pWaveProperties->loopRegion = entry->LoopRegion;
1783
pWaveProperties->streaming = pWaveBank->streaming;
1784
1785
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1786
return 0;
1787
}
1788
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
1796
) {
1797
FAudioBuffer buffer;
1798
FAudioBufferWMA bufferWMA;
1799
FAudioVoiceSends sends;
1800
FAudioSendDescriptor send;
1801
union
1802
{
1803
FAudioWaveFormatEx pcm;
1804
FAudioADPCMWaveFormat adpcm;
1805
FAudioXMA2WaveFormat xma2;
1806
} format;
1807
FACTWaveBankEntry *entry;
1808
FACTSeekTable *seek;
1809
if (pWaveBank == NULL)
1810
{
1811
*ppWave = NULL;
1812
return 1;
1813
}
1814
1815
*ppWave = (FACTWave*) pWaveBank->parentEngine->pMalloc(sizeof(FACTWave));
1816
1817
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1818
1819
entry = &pWaveBank->entries[nWaveIndex];
1820
1821
/* Engine references */
1822
(*ppWave)->parentBank = pWaveBank;
1823
(*ppWave)->parentCue = NULL;
1824
(*ppWave)->index = nWaveIndex;
1825
(*ppWave)->notifyOnDestroy = 0;
1826
(*ppWave)->usercontext = NULL;
1827
1828
/* Playback */
1829
(*ppWave)->state = FACT_STATE_PREPARED;
1830
(*ppWave)->volume = 1.0f;
1831
(*ppWave)->pitch = 0;
1832
(*ppWave)->loopCount = nLoopCount;
1833
1834
/* TODO: Convert dwPlayOffset to a byte offset */
1835
FAudio_assert(dwPlayOffset == 0);
1836
#if 0
1837
if (dwFlags & FACT_FLAG_UNITS_MS)
1838
{
1839
dwPlayOffset = (uint32_t) (
1840
( /* Samples per millisecond... */
1841
(float) entry->Format.nSamplesPerSec /
1842
1000.0f
1843
) * (float) dwPlayOffset
1844
);
1845
}
1846
#endif
1847
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)
1856
{
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;
1862
}
1863
else if (entry->Format.wFormatTag == 0x1)
1864
{
1865
/* XMA2 is quite similar to WMA Pro... is what everyone thought.
1866
* What a great way to start this comment.
1867
*
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!
1872
*
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.
1875
*
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.
1878
*
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.
1885
*
1886
* This is FUN.
1887
* -ade
1888
*/
1889
FAudio_assert(entry->Format.wBitsPerSample != 0);
1890
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)
1899
);
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;
1913
}
1914
else if (entry->Format.wFormatTag == 0x2)
1915
{
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)
1922
);
1923
format.adpcm.wSamplesPerBlock = (
1924
((format.pcm.nBlockAlign / format.pcm.nChannels) - 6) * 2
1925
);
1926
}
1927
else if (entry->Format.wFormatTag == 0x3)
1928
{
1929
/* Apparently this is used to detect WMA Pro...? */
1930
FAudio_assert(entry->Format.wBitsPerSample == 0);
1931
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;
1937
}
1938
else
1939
{
1940
FAudio_assert(0 && "Rebuild your WaveBanks with ADPCM!");
1941
}
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
1962
);
1963
if (pWaveBank->streaming)
1964
{
1965
/* Init stream cache info */
1966
if (format.pcm.wFormatTag == FAUDIO_FORMAT_PCM)
1967
{
1968
(*ppWave)->streamSize = (
1969
format.pcm.nSamplesPerSec *
1970
format.pcm.nBlockAlign
1971
);
1972
}
1973
else if (format.pcm.wFormatTag == FAUDIO_FORMAT_MSADPCM)
1974
{
1975
(*ppWave)->streamSize = (
1976
format.pcm.nSamplesPerSec /
1977
format.adpcm.wSamplesPerBlock *
1978
format.pcm.nBlockAlign
1979
);
1980
}
1981
else
1982
{
1983
/* Screw it, load the whole thing */
1984
(*ppWave)->streamSize = entry->PlayRegion.dwLength;
1985
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);
1989
}
1990
(*ppWave)->streamCache = (uint8_t*) pWaveBank->parentEngine->pMalloc(
1991
(*ppWave)->streamSize
1992
);
1993
(*ppWave)->streamOffset = entry->PlayRegion.dwOffset;
1994
1995
/* Read and submit first buffer from the WaveBank */
1996
FACT_INTERNAL_OnBufferEnd(&(*ppWave)->callback.callback, NULL);
1997
}
1998
else
1999
{
2000
(*ppWave)->streamCache = NULL;
2001
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
2007
);
2008
buffer.PlayBegin = 0;
2009
buffer.PlayLength = entry->Duration;
2010
if (nLoopCount == 0)
2011
{
2012
buffer.LoopBegin = 0;
2013
buffer.LoopLength = 0;
2014
buffer.LoopCount = 0;
2015
}
2016
else
2017
{
2018
buffer.LoopBegin = entry->LoopRegion.dwStartSample;
2019
buffer.LoopLength = entry->LoopRegion.dwTotalSamples;
2020
buffer.LoopCount = nLoopCount;
2021
}
2022
buffer.pContext = NULL;
2023
if (format.pcm.wFormatTag == FAUDIO_FORMAT_WMAUDIO2)
2024
{
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
2033
);
2034
}
2035
else
2036
{
2037
FAudioSourceVoice_SubmitSourceBuffer(
2038
(*ppWave)->voice,
2039
&buffer,
2040
NULL
2041
);
2042
}
2043
}
2044
2045
/* Add to the WaveBank Wave list */
2046
LinkedList_AddEntry(
2047
&pWaveBank->waveList,
2048
*ppWave,
2049
pWaveBank->waveLock,
2050
pWaveBank->parentEngine->pMalloc
2051
);
2052
2053
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
2054
return 0;
2055
}
2056
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
2064
) {
2065
if (pWaveBank == NULL)
2066
{
2067
*ppWave = NULL;
2068
return 1;
2069
}
2070
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
2071
FACTWaveBank_Prepare(
2072
pWaveBank,
2073
nWaveIndex,
2074
dwFlags,
2075
dwPlayOffset,
2076
nLoopCount,
2077
ppWave
2078
);
2079
FACTWave_Play(*ppWave);
2080
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
2081
return 0;
2082
}
2083
2084
uint32_t FACTWaveBank_Stop(
2085
FACTWaveBank *pWaveBank,
2086
uint16_t nWaveIndex,
2087
uint32_t dwFlags
2088
) {
2089
FACTWave *wave;
2090
LinkedList *list;
2091
if (pWaveBank == NULL)
2092
{
2093
return 1;
2094
}
2095
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
2096
list = pWaveBank->waveList;
2097
while (list != NULL)
2098
{
2099
wave = (FACTWave*) list->entry;
2100
if (wave->index == nWaveIndex)
2101
{
2102
FACTWave_Stop(wave, dwFlags);
2103
}
2104
list = list->next;
2105
}
2106
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
2107
return 0;
2108
}
2109
2110
/* Wave implementation */
2111
2112
uint32_t FACTWave_Destroy(FACTWave *pWave)
2113
{
2114
FAudioMutex mutex;
2115
FACTNotification note;
2116
if (pWave == NULL)
2117
{
2118
return 1;
2119
}
2120
2121
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2122
2123
/* Stop before we start deleting everything */
2124
FACTWave_Stop(pWave, FACT_FLAG_STOP_IMMEDIATE);
2125
2126
LinkedList_RemoveEntry(
2127
&pWave->parentBank->waveList,
2128
pWave,
2129
pWave->parentBank->waveLock,
2130
pWave->parentBank->parentEngine->pFree
2131
);
2132
2133
FAudioVoice_DestroyVoice(pWave->voice);
2134
if (pWave->streamCache != NULL)
2135
{
2136
pWave->parentBank->parentEngine->pFree(pWave->streamCache);
2137
}
2138
if (pWave->notifyOnDestroy || pWave->parentBank->parentEngine->notifications & NOTIFY_WAVEDESTROY)
2139
{
2140
note.type = FACTNOTIFICATIONTYPE_WAVEDESTROYED;
2141
note.wave.pWave = pWave;
2142
if (pWave->parentBank->parentEngine->notifications & NOTIFY_WAVEDESTROY)
2143
{
2144
note.pvContext = pWave->parentBank->parentEngine->wave_context;
2145
}
2146
else
2147
{
2148
note.pvContext = pWave->usercontext;
2149
}
2150
pWave->parentBank->parentEngine->notificationCallback(&note);
2151
}
2152
2153
mutex = pWave->parentBank->parentEngine->apiLock;
2154
pWave->parentBank->parentEngine->pFree(pWave);
2155
FAudio_PlatformUnlockMutex(mutex);
2156
return 0;
2157
}
2158
2159
uint32_t FACTWave_Play(FACTWave *pWave)
2160
{
2161
if (pWave == NULL)
2162
{
2163
return 1;
2164
}
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
2171
);
2172
FAudioSourceVoice_Start(pWave->voice, 0, 0);
2173
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2174
return 0;
2175
}
2176
2177
uint32_t FACTWave_Stop(FACTWave *pWave, uint32_t dwFlags)
2178
{
2179
if (pWave == NULL)
2180
{
2181
return 1;
2182
}
2183
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2184
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
2188
*/
2189
if ( dwFlags & FACT_FLAG_STOP_IMMEDIATE ||
2190
pWave->state & FACT_STATE_PAUSED )
2191
{
2192
pWave->state |= FACT_STATE_STOPPED;
2193
pWave->state &= ~(
2194
FACT_STATE_PLAYING |
2195
FACT_STATE_STOPPING |
2196
FACT_STATE_PAUSED
2197
);
2198
FAudioSourceVoice_Stop(pWave->voice, 0, 0);
2199
FAudioSourceVoice_FlushSourceBuffers(pWave->voice);
2200
}
2201
else
2202
{
2203
pWave->state |= FACT_STATE_STOPPING;
2204
FAudioSourceVoice_ExitLoop(pWave->voice, 0);
2205
}
2206
2207
if (pWave->parentBank->parentEngine->notifications & NOTIFY_WAVESTOP)
2208
{
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;
2217
2218
pWave->parentBank->parentEngine->notificationCallback(&note);
2219
}
2220
2221
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2222
return 0;
2223
}
2224
2225
uint32_t FACTWave_Pause(FACTWave *pWave, int32_t fPause)
2226
{
2227
if (pWave == NULL)
2228
{
2229
return 1;
2230
}
2231
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2232
2233
/* FIXME: Does the Cue STOPPING/STOPPED rule apply here too? */
2234
if (pWave->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))
2235
{
2236
FAudio_PlatformUnlockMutex(
2237
pWave->parentBank->parentEngine->apiLock
2238
);
2239
return 0;
2240
}
2241
2242
/* All we do is set the flag, the mixer handles the rest */
2243
if (fPause)
2244
{
2245
pWave->state |= FACT_STATE_PAUSED;
2246
FAudioSourceVoice_Stop(pWave->voice, 0, 0);
2247
}
2248
else
2249
{
2250
pWave->state &= ~FACT_STATE_PAUSED;
2251
FAudioSourceVoice_Start(pWave->voice, 0, 0);
2252
}
2253
2254
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2255
return 0;
2256
}
2257
2258
uint32_t FACTWave_GetState(FACTWave *pWave, uint32_t *pdwState)
2259
{
2260
if (pWave == NULL)
2261
{
2262
*pdwState = 0;
2263
return 1;
2264
}
2265
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2266
*pdwState = pWave->state;
2267
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2268
return 0;
2269
}
2270
2271
uint32_t FACTWave_SetPitch(FACTWave *pWave, int16_t pitch)
2272
{
2273
if (pWave == NULL)
2274
{
2275
return 1;
2276
}
2277
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2278
pWave->pitch = FAudio_clamp(
2279
pitch,
2280
FACTPITCH_MIN_TOTAL,
2281
FACTPITCH_MAX_TOTAL
2282
);
2283
FAudioSourceVoice_SetFrequencyRatio(
2284
pWave->voice,
2285
(float) FAudio_pow(2.0, pWave->pitch / 1200.0),
2286
0
2287
);
2288
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2289
return 0;
2290
}
2291
2292
uint32_t FACTWave_SetVolume(FACTWave *pWave, float volume)
2293
{
2294
if (pWave == NULL)
2295
{
2296
return 1;
2297
}
2298
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2299
pWave->volume = FAudio_clamp(
2300
volume,
2301
FACTVOLUME_MIN,
2302
FACTVOLUME_MAX
2303
);
2304
FAudioVoice_SetVolume(
2305
pWave->voice,
2306
pWave->volume,
2307
0
2308
);
2309
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2310
return 0;
2311
}
2312
2313
uint32_t FACTWave_SetMatrixCoefficients(
2314
FACTWave *pWave,
2315
uint32_t uSrcChannelCount,
2316
uint32_t uDstChannelCount,
2317
float *pMatrixCoefficients
2318
) {
2319
uint32_t i;
2320
float *mtxDst, *mtxSrc, *mtxTmp = NULL;
2321
if (pWave == NULL)
2322
{
2323
return 1;
2324
}
2325
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
2332
*/
2333
if (uSrcChannelCount == 1 && pWave->srcChannels == 2)
2334
{
2335
mtxTmp = (float*) FAudio_alloca(
2336
sizeof(float) *
2337
pWave->srcChannels *
2338
uDstChannelCount
2339
);
2340
mtxDst = mtxTmp;
2341
mtxSrc = pMatrixCoefficients;
2342
for (i = 0; i < uDstChannelCount; i += 1)
2343
{
2344
mtxDst[0] = *mtxSrc;
2345
mtxDst[1] = *mtxSrc;
2346
mtxDst += 2;
2347
mtxSrc += 1;
2348
}
2349
uSrcChannelCount = 2;
2350
pMatrixCoefficients = mtxTmp;
2351
}
2352
else if (uSrcChannelCount == 2 && pWave->srcChannels == 1)
2353
{
2354
mtxTmp = (float*) FAudio_alloca(
2355
sizeof(float) *
2356
pWave->srcChannels *
2357
uDstChannelCount
2358
);
2359
mtxDst = mtxTmp;
2360
mtxSrc = pMatrixCoefficients;
2361
for (i = 0; i < uDstChannelCount; i += 1)
2362
{
2363
*mtxDst = (mtxSrc[0] + mtxSrc[1]) / 2.0f;
2364
mtxDst += 1;
2365
mtxSrc += 2;
2366
}
2367
uSrcChannelCount = 1;
2368
pMatrixCoefficients = mtxTmp;
2369
}
2370
2371
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2372
2373
FAudioVoice_SetOutputMatrix(
2374
pWave->voice,
2375
pWave->voice->sends.pSends->pOutputVoice,
2376
uSrcChannelCount,
2377
uDstChannelCount,
2378
pMatrixCoefficients,
2379
0
2380
);
2381
2382
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2383
if (mtxTmp != NULL)
2384
{
2385
FAudio_dealloca(mtxTmp);
2386
}
2387
return 0;
2388
}
2389
2390
uint32_t FACTWave_GetProperties(
2391
FACTWave *pWave,
2392
FACTWaveInstanceProperties *pProperties
2393
) {
2394
if (pWave == NULL)
2395
{
2396
return 1;
2397
}
2398
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2399
2400
FACTWaveBank_GetWaveProperties(
2401
pWave->parentBank,
2402
pWave->index,
2403
&pProperties->properties
2404
);
2405
2406
/* FIXME: This is unsupported on PC, do we care about this? */
2407
pProperties->backgroundMusic = 0;
2408
2409
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2410
return 0;
2411
}
2412
2413
/* Cue implementation */
2414
2415
uint32_t FACTCue_Destroy(FACTCue *pCue)
2416
{
2417
FACTCue *cue, *prev;
2418
FAudioMutex mutex;
2419
if (pCue == NULL)
2420
{
2421
return 1;
2422
}
2423
2424
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2425
2426
/* Stop before we start deleting everything */
2427
FACTCue_Stop(pCue, FACT_FLAG_STOP_IMMEDIATE);
2428
2429
/* Remove this Cue from the SoundBank list */
2430
cue = pCue->parentBank->cueList;
2431
prev = cue;
2432
while (cue != NULL)
2433
{
2434
if (cue == pCue)
2435
{
2436
if (cue == prev) /* First in list */
2437
{
2438
pCue->parentBank->cueList = cue->next;
2439
}
2440
else
2441
{
2442
prev->next = cue->next;
2443
}
2444
break;
2445
}
2446
prev = cue;
2447
cue = cue->next;
2448
}
2449
FAudio_assert(cue != NULL && "Could not find Cue reference!");
2450
2451
pCue->parentBank->parentEngine->pFree(pCue->variableValues);
2452
FACT_INTERNAL_SendCueNotification(pCue, NOTIFY_CUEDESTROY, FACTNOTIFICATIONTYPE_CUEDESTROYED);
2453
2454
mutex = pCue->parentBank->parentEngine->apiLock;
2455
pCue->parentBank->parentEngine->pFree(pCue);
2456
FAudio_PlatformUnlockMutex(mutex);
2457
return 0;
2458
}
2459
2460
uint32_t FACTCue_Play(FACTCue *pCue)
2461
{
2462
union
2463
{
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)
2471
{
2472
return 1;
2473
}
2474
2475
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2476
2477
FAudio_assert(!(pCue->state & (FACT_STATE_PLAYING | FACT_STATE_STOPPING)));
2478
2479
data = &pCue->parentBank->cues[pCue->index];
2480
2481
/* Cue Instance Limits */
2482
if (data->instanceCount >= data->instanceLimit)
2483
{
2484
wnr = NULL;
2485
tmp = pCue->parentBank->cueList;
2486
if (data->maxInstanceBehavior == 0) /* Fail */
2487
{
2488
pCue->state |= FACT_STATE_STOPPED;
2489
pCue->state &= ~(
2490
FACT_STATE_PLAYING |
2491
FACT_STATE_STOPPING |
2492
FACT_STATE_PAUSED
2493
);
2494
2495
FACT_INTERNAL_SendCueNotification(pCue, NOTIFY_CUESTOP, FACTNOTIFICATIONTYPE_CUESTOP);
2496
2497
FAudio_PlatformUnlockMutex(
2498
pCue->parentBank->parentEngine->apiLock
2499
);
2500
return 1;
2501
}
2502
else if (data->maxInstanceBehavior == 1) /* Queue */
2503
{
2504
/* FIXME: How is this different from Replace Oldest? */
2505
while (tmp != NULL)
2506
{
2507
if ( tmp != pCue &&
2508
tmp->index == pCue->index &&
2509
!(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED)) )
2510
{
2511
wnr = tmp;
2512
break;
2513
}
2514
tmp = tmp->next;
2515
}
2516
}
2517
else if (data->maxInstanceBehavior == 2) /* Replace Oldest */
2518
{
2519
while (tmp != NULL)
2520
{
2521
if ( tmp != pCue &&
2522
tmp->index == pCue->index &&
2523
!(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED)) )
2524
{
2525
wnr = tmp;
2526
break;
2527
}
2528
tmp = tmp->next;
2529
}
2530
}
2531
else if (data->maxInstanceBehavior == 3) /* Replace Quietest */
2532
{
2533
limitmax.maxf = FACTVOLUME_MAX;
2534
while (tmp != NULL)
2535
{
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)) )
2541
{
2542
wnr = tmp;
2543
/* limitmax.maxf = tmp->playingSound->volume; */
2544
}
2545
tmp = tmp->next;
2546
}
2547
}
2548
else if (data->maxInstanceBehavior == 4) /* Replace Lowest Priority */
2549
{
2550
limitmax.maxi = 0xFF;
2551
while (tmp != NULL)
2552
{
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)) )
2558
{
2559
wnr = tmp;
2560
limitmax.maxi = tmp->playingSound->sound->priority;
2561
}
2562
tmp = tmp->next;
2563
}
2564
}
2565
if (wnr != NULL)
2566
{
2567
fadeInMS = data->fadeInMS;
2568
if (wnr->playingSound != NULL)
2569
{
2570
FACT_INTERNAL_BeginFadeOut(wnr->playingSound, data->fadeOutMS);
2571
}
2572
else
2573
{
2574
FACTCue_Stop(wnr, 0);
2575
}
2576
}
2577
}
2578
2579
/* Need an initial sound to play */
2580
if (!FACT_INTERNAL_CreateSound(pCue, fadeInMS))
2581
{
2582
FAudio_PlatformUnlockMutex(
2583
pCue->parentBank->parentEngine->apiLock
2584
);
2585
return 1;
2586
}
2587
data->instanceCount += 1;
2588
2589
pCue->state |= FACT_STATE_PLAYING;
2590
pCue->state &= ~(
2591
FACT_STATE_PAUSED |
2592
FACT_STATE_STOPPED |
2593
FACT_STATE_PREPARED
2594
);
2595
2596
FACT_INTERNAL_SendCueNotification(pCue, NOTIFY_CUEPLAY, FACTNOTIFICATIONTYPE_CUEPLAY);
2597
2598
pCue->start = FAudio_timems();
2599
2600
/* If it's a simple wave, just play it! */
2601
if (pCue->simpleWave != NULL)
2602
{
2603
if (pCue->active3D)
2604
{
2605
FACTWave_SetMatrixCoefficients(
2606
pCue->simpleWave,
2607
pCue->srcChannels,
2608
pCue->dstChannels,
2609
pCue->matrixCoefficients
2610
);
2611
}
2612
FACTWave_Play(pCue->simpleWave);
2613
}
2614
2615
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2616
return 0;
2617
}
2618
2619
uint32_t FACTCue_Stop(FACTCue *pCue, uint32_t dwFlags)
2620
{
2621
if (pCue == NULL)
2622
{
2623
return 1;
2624
}
2625
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2626
2627
/* If we're already stopped, there's nothing to do... */
2628
if (pCue->state & FACT_STATE_STOPPED)
2629
{
2630
FAudio_PlatformUnlockMutex(
2631
pCue->parentBank->parentEngine->apiLock
2632
);
2633
return 0;
2634
}
2635
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...
2638
*/
2639
if ( (pCue->state & FACT_STATE_STOPPING) &&
2640
!(dwFlags & FACT_FLAG_STOP_IMMEDIATE) )
2641
{
2642
FAudio_PlatformUnlockMutex(
2643
pCue->parentBank->parentEngine->apiLock
2644
);
2645
return 0;
2646
}
2647
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
2652
*/
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 ) )
2658
{
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
2666
);
2667
2668
if (pCue->simpleWave != NULL)
2669
{
2670
FACTWave_Destroy(pCue->simpleWave);
2671
pCue->simpleWave = NULL;
2672
2673
pCue->data->instanceCount -= 1;
2674
}
2675
else if (pCue->playingSound != NULL)
2676
{
2677
FACT_INTERNAL_DestroySound(pCue->playingSound);
2678
}
2679
}
2680
else
2681
{
2682
if (pCue->parentBank->cues[pCue->index].fadeOutMS > 0)
2683
{
2684
FACT_INTERNAL_BeginFadeOut(
2685
pCue->playingSound,
2686
pCue->parentBank->cues[pCue->index].fadeOutMS
2687
);
2688
}
2689
else if (pCue->maxRpcReleaseTime > 0)
2690
{
2691
FACT_INTERNAL_BeginReleaseRPC(
2692
pCue->playingSound,
2693
pCue->maxRpcReleaseTime
2694
);
2695
}
2696
else
2697
{
2698
/* Pretty sure this doesn't happen, but just in case? */
2699
pCue->state |= FACT_STATE_STOPPING;
2700
}
2701
}
2702
2703
FACT_INTERNAL_SendCueNotification(pCue, NOTIFY_CUESTOP, FACTNOTIFICATIONTYPE_CUESTOP);
2704
2705
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2706
return 0;
2707
}
2708
2709
uint32_t FACTCue_GetState(FACTCue *pCue, uint32_t *pdwState)
2710
{
2711
if (pCue == NULL)
2712
{
2713
*pdwState = 0;
2714
return 1;
2715
}
2716
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2717
*pdwState = pCue->state;
2718
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2719
return 0;
2720
}
2721
2722
uint32_t FACTCue_SetMatrixCoefficients(
2723
FACTCue *pCue,
2724
uint32_t uSrcChannelCount,
2725
uint32_t uDstChannelCount,
2726
float *pMatrixCoefficients
2727
) {
2728
uint8_t i;
2729
2730
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2731
2732
/* See FACTCue.matrixCoefficients declaration */
2733
FAudio_assert(uSrcChannelCount > 0 && uSrcChannelCount < 3);
2734
FAudio_assert(uDstChannelCount > 0 && uDstChannelCount < 9);
2735
2736
/* Local storage */
2737
pCue->srcChannels = uSrcChannelCount;
2738
pCue->dstChannels = uDstChannelCount;
2739
FAudio_memcpy(
2740
pCue->matrixCoefficients,
2741
pMatrixCoefficients,
2742
sizeof(float) * uSrcChannelCount * uDstChannelCount
2743
);
2744
pCue->active3D = 1;
2745
2746
/* Apply to Waves if they exist */
2747
if (pCue->simpleWave != NULL)
2748
{
2749
FACTWave_SetMatrixCoefficients(
2750
pCue->simpleWave,
2751
uSrcChannelCount,
2752
uDstChannelCount,
2753
pMatrixCoefficients
2754
);
2755
}
2756
else if (pCue->playingSound != NULL)
2757
{
2758
for (i = 0; i < pCue->playingSound->sound->trackCount; i += 1)
2759
{
2760
if (pCue->playingSound->tracks[i].activeWave.wave != NULL)
2761
{
2762
FACTWave_SetMatrixCoefficients(
2763
pCue->playingSound->tracks[i].activeWave.wave,
2764
uSrcChannelCount,
2765
uDstChannelCount,
2766
pMatrixCoefficients
2767
);
2768
}
2769
}
2770
}
2771
2772
FACT_INTERNAL_SendCueNotification(pCue, NOTIFY_CUESTOP, FACTNOTIFICATIONTYPE_CUESTOP);
2773
2774
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2775
return 0;
2776
}
2777
2778
uint16_t FACTCue_GetVariableIndex(
2779
FACTCue *pCue,
2780
const char *szFriendlyName
2781
) {
2782
uint16_t i;
2783
if (pCue == NULL)
2784
{
2785
return FACTVARIABLEINDEX_INVALID;
2786
}
2787
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2788
for (i = 0; i < pCue->parentBank->parentEngine->variableCount; i += 1)
2789
{
2790
if ( FAudio_strcmp(szFriendlyName, pCue->parentBank->parentEngine->variableNames[i]) == 0 &&
2791
pCue->parentBank->parentEngine->variables[i].accessibility & 0x04 )
2792
{
2793
FAudio_PlatformUnlockMutex(
2794
pCue->parentBank->parentEngine->apiLock
2795
);
2796
return i;
2797
}
2798
}
2799
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2800
return FACTVARIABLEINDEX_INVALID;
2801
}
2802
2803
uint32_t FACTCue_SetVariable(
2804
FACTCue *pCue,
2805
uint16_t nIndex,
2806
float nValue
2807
) {
2808
FACTVariable *var;
2809
if (pCue == NULL)
2810
{
2811
return 1;
2812
}
2813
2814
if (nIndex == FACTINDEX_INVALID)
2815
{
2816
return 1;
2817
}
2818
2819
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2820
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
2829
);
2830
2831
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2832
return 0;
2833
}
2834
2835
uint32_t FACTCue_GetVariable(
2836
FACTCue *pCue,
2837
uint16_t nIndex,
2838
float *nValue
2839
) {
2840
FACTVariable *var;
2841
if (pCue == NULL)
2842
{
2843
*nValue = 0.0f;
2844
return 1;
2845
}
2846
2847
if (nIndex == FACTINDEX_INVALID)
2848
{
2849
return 1;
2850
}
2851
2852
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2853
2854
var = &pCue->parentBank->parentEngine->variables[nIndex];
2855
FAudio_assert(var->accessibility & 0x01);
2856
FAudio_assert(var->accessibility & 0x04);
2857
2858
if (nIndex == 0) /* NumCueInstances */
2859
{
2860
*nValue = pCue->parentBank->cues[pCue->index].instanceCount;
2861
}
2862
else
2863
{
2864
*nValue = pCue->variableValues[nIndex];
2865
}
2866
2867
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2868
return 0;
2869
}
2870
2871
uint32_t FACTCue_Pause(FACTCue *pCue, int32_t fPause)
2872
{
2873
uint8_t i;
2874
if (pCue == NULL)
2875
{
2876
return 1;
2877
}
2878
2879
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2880
2881
/* "A stopping or stopped cue cannot be paused." */
2882
if (pCue->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))
2883
{
2884
FAudio_PlatformUnlockMutex(
2885
pCue->parentBank->parentEngine->apiLock
2886
);
2887
return 0;
2888
}
2889
2890
/* Store elapsed time */
2891
pCue->elapsed += FAudio_timems() - pCue->start;
2892
2893
/* All we do is set the flag, not much to see here */
2894
if (fPause)
2895
{
2896
pCue->state |= FACT_STATE_PAUSED;
2897
}
2898
else
2899
{
2900
pCue->state &= ~FACT_STATE_PAUSED;
2901
}
2902
2903
/* Pause the Waves */
2904
if (pCue->simpleWave != NULL)
2905
{
2906
FACTWave_Pause(pCue->simpleWave, fPause);
2907
}
2908
else if (pCue->playingSound != NULL)
2909
{
2910
for (i = 0; i < pCue->playingSound->sound->trackCount; i += 1)
2911
{
2912
if (pCue->playingSound->tracks[i].activeWave.wave != NULL)
2913
{
2914
FACTWave_Pause(
2915
pCue->playingSound->tracks[i].activeWave.wave,
2916
fPause
2917
);
2918
}
2919
}
2920
}
2921
2922
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2923
return 0;
2924
}
2925
2926
uint32_t FACTCue_GetProperties(
2927
FACTCue *pCue,
2928
FACTCueInstanceProperties **ppProperties
2929
) {
2930
uint32_t i;
2931
size_t allocSize;
2932
FACTCueInstanceProperties *cueProps;
2933
FACTVariationProperties *varProps;
2934
FACTSoundProperties *sndProps;
2935
FACTWaveInstanceProperties waveProps;
2936
if (pCue == NULL)
2937
{
2938
return 1;
2939
}
2940
2941
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2942
2943
/* Alloc container (including variable length array space) */
2944
allocSize = sizeof(FACTCueInstanceProperties);
2945
if (pCue->playingSound != NULL)
2946
{
2947
allocSize += (
2948
sizeof(FACTTrackProperties) *
2949
pCue->playingSound->sound->trackCount
2950
);
2951
}
2952
cueProps = (FACTCueInstanceProperties*) pCue->parentBank->parentEngine->pMalloc(
2953
allocSize
2954
);
2955
FAudio_zero(cueProps, allocSize);
2956
2957
/* Cue Properties */
2958
FACTSoundBank_GetCueProperties(
2959
pCue->parentBank,
2960
pCue->index,
2961
&cueProps->cueProperties
2962
);
2963
2964
/* Variation Properties */
2965
varProps = &cueProps->activeVariationProperties.variationProperties;
2966
if (pCue->playingVariation != NULL)
2967
{
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
2973
);
2974
if (pCue->variation->flags == 3)
2975
{
2976
varProps->iaVariableMin =
2977
pCue->playingVariation->minWeight;
2978
varProps->iaVariableMax =
2979
pCue->playingVariation->maxWeight;
2980
}
2981
else
2982
{
2983
varProps->iaVariableMin = 0;
2984
varProps->iaVariableMax = 0;
2985
}
2986
varProps->linger = pCue->playingVariation->linger;
2987
}
2988
2989
/* Sound Properties */
2990
sndProps = &cueProps->activeVariationProperties.soundProperties;
2991
if (pCue->playingSound != NULL)
2992
{
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;
2998
2999
for (i = 0; i < sndProps->numTracks; i += 1)
3000
{
3001
if (FACTWave_GetProperties(
3002
pCue->playingSound->tracks[i].activeWave.wave,
3003
&waveProps
3004
) == 0) {
3005
sndProps->arrTrackProperties[i].duration = (uint32_t) (
3006
(
3007
(float) waveProps.properties.durationInSamples /
3008
(float) waveProps.properties.format.nSamplesPerSec
3009
) / 1000.0f
3010
);
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;
3017
}
3018
}
3019
}
3020
3021
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
3022
3023
*ppProperties = cueProps;
3024
return 0;
3025
}
3026
3027
uint32_t FACTCue_SetOutputVoices(
3028
FACTCue *pCue,
3029
const FAudioVoiceSends *pSendList /* Optional XAUDIO2_VOICE_SENDS */
3030
) {
3031
/* TODO */
3032
return 0;
3033
}
3034
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 */
3041
) {
3042
/* TODO */
3043
return 0;
3044
}
3045
3046
/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
3047
3048