Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/faudio/src/FACT.c
8612 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
static void remove_single_notification(FACTAudioEngine *engine, size_t index)
33
{
34
--engine->notification_count;
35
if (index < engine->notification_count)
36
FAudio_memmove(&engine->notifications[index], &engine->notifications[index + 1],
37
(engine->notification_count - index) * sizeof(FACTNotificationDescription));
38
}
39
40
static void send_wavebank_notification(FACTAudioEngine *engine, uint8_t type, FACTWaveBank *wavebank)
41
{
42
FACTNotification notification;
43
44
notification.type = type;
45
notification.waveBank.pWaveBank = wavebank;
46
47
for (ptrdiff_t i = engine->notification_count - 1; i >= 0; --i)
48
{
49
FACTNotificationDescription *desc = &engine->notifications[i];
50
51
if (desc->type == type && (!desc->pWaveBank || desc->pWaveBank == wavebank))
52
{
53
notification.pvContext = desc->pvContext;
54
if (!(desc->flags & FACT_FLAG_NOTIFICATION_PERSIST))
55
remove_single_notification(engine, i);
56
engine->notificationCallback(&notification);
57
break;
58
}
59
}
60
}
61
62
static void send_wave_notification(FACTAudioEngine *engine, uint8_t type, const FACTNotificationWave *wave)
63
{
64
FACTNotification notification;
65
66
notification.type = type;
67
notification.wave = *wave;
68
69
for (ptrdiff_t i = engine->notification_count - 1; i >= 0; --i)
70
{
71
FACTNotificationDescription *desc = &engine->notifications[i];
72
73
if (desc->type == type && (!desc->pWave || desc->pWave == wave->pWave))
74
{
75
notification.pvContext = desc->pvContext;
76
if (!(desc->flags & FACT_FLAG_NOTIFICATION_PERSIST))
77
remove_single_notification(engine, i);
78
engine->notificationCallback(&notification);
79
break;
80
}
81
}
82
}
83
84
static void send_soundbank_notification(FACTAudioEngine *engine, FACTSoundBank *soundbank)
85
{
86
FACTNotification notification;
87
88
notification.type = FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED;
89
notification.soundBank.pSoundBank = soundbank;
90
91
for (ptrdiff_t i = engine->notification_count - 1; i >= 0; --i)
92
{
93
FACTNotificationDescription *desc = &engine->notifications[i];
94
95
if (desc->type == FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED && (!desc->pSoundBank || desc->pSoundBank == soundbank))
96
{
97
notification.pvContext = desc->pvContext;
98
if (!(desc->flags & FACT_FLAG_NOTIFICATION_PERSIST))
99
remove_single_notification(engine, i);
100
engine->notificationCallback(&notification);
101
break;
102
}
103
}
104
}
105
106
void FACT_INTERNAL_SendCueNotification(FACTCue *cue, uint8_t type)
107
{
108
FACTAudioEngine *engine = cue->parentBank->parentEngine;
109
FACTNotification notification;
110
111
notification.type = type;
112
notification.cue.cueIndex = cue->index;
113
notification.cue.pSoundBank = cue->parentBank;
114
notification.cue.pCue = cue;
115
116
for (ptrdiff_t i = engine->notification_count - 1; i >= 0; --i)
117
{
118
FACTNotificationDescription *desc = &engine->notifications[i];
119
120
if (desc->type == type && (!desc->pCue || desc->pCue == cue))
121
{
122
notification.pvContext = desc->pvContext;
123
if (!(desc->flags & FACT_FLAG_NOTIFICATION_PERSIST))
124
remove_single_notification(engine, i);
125
engine->notificationCallback(&notification);
126
break;
127
}
128
}
129
}
130
131
uint32_t FACTCreateEngine(
132
uint32_t dwCreationFlags,
133
FACTAudioEngine **ppEngine
134
) {
135
return FACTCreateEngineWithCustomAllocatorEXT(
136
dwCreationFlags,
137
ppEngine,
138
FAudio_malloc,
139
FAudio_free,
140
FAudio_realloc
141
);
142
}
143
144
uint32_t FACTCreateEngineWithCustomAllocatorEXT(
145
uint32_t dwCreationFlags,
146
FACTAudioEngine **ppEngine,
147
FAudioMallocFunc customMalloc,
148
FAudioFreeFunc customFree,
149
FAudioReallocFunc customRealloc
150
) {
151
/* TODO: Anything fun with dwCreationFlags? */
152
FAudio_PlatformAddRef();
153
*ppEngine = (FACTAudioEngine*) customMalloc(sizeof(FACTAudioEngine));
154
if (*ppEngine == NULL)
155
{
156
return FAUDIO_E_OUT_OF_MEMORY;
157
}
158
FAudio_zero(*ppEngine, sizeof(FACTAudioEngine));
159
(*ppEngine)->sbLock = FAudio_PlatformCreateMutex();
160
(*ppEngine)->wbLock = FAudio_PlatformCreateMutex();
161
(*ppEngine)->apiLock = FAudio_PlatformCreateMutex();
162
(*ppEngine)->pMalloc = customMalloc;
163
(*ppEngine)->pFree = customFree;
164
(*ppEngine)->pRealloc = customRealloc;
165
(*ppEngine)->refcount = 1;
166
return FAUDIO_OK;
167
}
168
169
uint32_t FACTAudioEngine_AddRef(FACTAudioEngine *pEngine)
170
{
171
FAudio_PlatformLockMutex(pEngine->apiLock);
172
pEngine->refcount += 1;
173
FAudio_PlatformUnlockMutex(pEngine->apiLock);
174
return pEngine->refcount;
175
}
176
177
uint32_t FACTAudioEngine_Release(FACTAudioEngine *pEngine)
178
{
179
FAudio_PlatformLockMutex(pEngine->apiLock);
180
pEngine->refcount -= 1;
181
if (pEngine->refcount > 0)
182
{
183
FAudio_PlatformUnlockMutex(pEngine->apiLock);
184
return pEngine->refcount;
185
}
186
FACTAudioEngine_ShutDown(pEngine);
187
FAudio_PlatformDestroyMutex(pEngine->sbLock);
188
FAudio_PlatformDestroyMutex(pEngine->wbLock);
189
FAudio_PlatformUnlockMutex(pEngine->apiLock);
190
FAudio_PlatformDestroyMutex(pEngine->apiLock);
191
if (pEngine->settings != NULL)
192
{
193
pEngine->pFree(pEngine->settings);
194
}
195
pEngine->pFree(pEngine);
196
FAudio_PlatformRelease();
197
return 0;
198
}
199
200
uint32_t FACTAudioEngine_GetRendererCount(
201
FACTAudioEngine *pEngine,
202
uint16_t *pnRendererCount
203
) {
204
FAudio_PlatformLockMutex(pEngine->apiLock);
205
*pnRendererCount = (uint16_t) FAudio_PlatformGetDeviceCount();
206
FAudio_PlatformUnlockMutex(pEngine->apiLock);
207
return FAUDIO_OK;
208
}
209
210
uint32_t FACTAudioEngine_GetRendererDetails(
211
FACTAudioEngine *pEngine,
212
uint16_t nRendererIndex,
213
FACTRendererDetails *pRendererDetails
214
) {
215
FAudioDeviceDetails deviceDetails;
216
217
FAudio_PlatformLockMutex(pEngine->apiLock);
218
219
FAudio_PlatformGetDeviceDetails(
220
nRendererIndex,
221
&deviceDetails
222
);
223
FAudio_memcpy(
224
pRendererDetails->rendererID,
225
deviceDetails.DeviceID,
226
sizeof(int16_t) * 0xFF
227
);
228
FAudio_memcpy(
229
pRendererDetails->displayName,
230
deviceDetails.DisplayName,
231
sizeof(int16_t) * 0xFF
232
);
233
/* FIXME: Which defaults does it care about...? */
234
pRendererDetails->defaultDevice = (deviceDetails.Role & (
235
FAudioGlobalDefaultDevice |
236
FAudioDefaultGameDevice
237
)) != 0;
238
239
FAudio_PlatformUnlockMutex(pEngine->apiLock);
240
return FAUDIO_OK;
241
}
242
243
uint32_t FACTAudioEngine_GetFinalMixFormat(
244
FACTAudioEngine *pEngine,
245
FAudioWaveFormatExtensible *pFinalMixFormat
246
) {
247
*pFinalMixFormat = pEngine->output_format;
248
return FAUDIO_OK;
249
}
250
251
uint32_t FACTAudioEngine_Initialize(
252
FACTAudioEngine *pEngine,
253
const FACTRuntimeParameters *pParams
254
) {
255
FAudioDeviceDetails device_details;
256
uint32_t parseRet;
257
uint32_t deviceIndex;
258
FAudioVoiceDetails masterDetails;
259
FAudioEffectDescriptor reverbDesc;
260
FAudioEffectChain reverbChain;
261
262
FAudio_PlatformLockMutex(pEngine->apiLock);
263
264
if (!pParams->pGlobalSettingsBuffer || pParams->globalSettingsBufferSize == 0)
265
{
266
/* No file? Just go with a safe default. (Also why are you using XACT) */
267
pEngine->categoryCount = 3;
268
pEngine->variableCount = 0;
269
pEngine->rpcCount = 0;
270
pEngine->dspPresetCount = 0;
271
pEngine->dspParameterCount = 0;
272
273
pEngine->categories = (FACTAudioCategory*) pEngine->pMalloc(
274
sizeof(FACTAudioCategory) * pEngine->categoryCount
275
);
276
pEngine->categoryNames = (char**) pEngine->pMalloc(
277
sizeof(char*) * pEngine->categoryCount
278
);
279
280
pEngine->categoryNames[0] = pEngine->pMalloc(7);
281
FAudio_strlcpy(pEngine->categoryNames[0], "Global", 7);
282
pEngine->categories[0].instanceLimit = 255;
283
pEngine->categories[0].fadeInMS = 0;
284
pEngine->categories[0].fadeOutMS = 0;
285
pEngine->categories[0].maxInstanceBehavior = MAX_INSTANCE_BEHAVIOR_FAIL;
286
pEngine->categories[0].parentCategory = -1;
287
pEngine->categories[0].volume = 1.0f;
288
pEngine->categories[0].visibility = 1;
289
pEngine->categories[0].instanceCount = 0;
290
pEngine->categories[0].currentVolume = 1.0f;
291
292
pEngine->categoryNames[1] = pEngine->pMalloc(8);
293
FAudio_strlcpy(pEngine->categoryNames[1], "Default", 8);
294
pEngine->categories[1].instanceLimit = 255;
295
pEngine->categories[1].fadeInMS = 0;
296
pEngine->categories[1].fadeOutMS = 0;
297
pEngine->categories[1].maxInstanceBehavior = MAX_INSTANCE_BEHAVIOR_FAIL;
298
pEngine->categories[1].parentCategory = 0;
299
pEngine->categories[1].volume = 1.0f;
300
pEngine->categories[1].visibility = 1;
301
pEngine->categories[1].instanceCount = 0;
302
pEngine->categories[1].currentVolume = 1.0f;
303
304
pEngine->categoryNames[2] = pEngine->pMalloc(6);
305
FAudio_strlcpy(pEngine->categoryNames[2], "Music", 6);
306
pEngine->categories[2].instanceLimit = 255;
307
pEngine->categories[2].fadeInMS = 0;
308
pEngine->categories[2].fadeOutMS = 0;
309
pEngine->categories[2].maxInstanceBehavior = MAX_INSTANCE_BEHAVIOR_FAIL;
310
pEngine->categories[2].parentCategory = 0;
311
pEngine->categories[2].volume = 1.0f;
312
pEngine->categories[2].visibility = 1;
313
pEngine->categories[2].instanceCount = 0;
314
pEngine->categories[2].currentVolume = 1.0f;
315
316
pEngine->variables = NULL;
317
pEngine->variableNames = NULL;
318
pEngine->globalVariableValues = NULL;
319
pEngine->rpcs = NULL;
320
pEngine->dspPresets = NULL;
321
}
322
else
323
{
324
/* Parse the file */
325
parseRet = FACT_INTERNAL_ParseAudioEngine(pEngine, pParams);
326
if (parseRet != 0)
327
{
328
FAudio_PlatformUnlockMutex(pEngine->apiLock);
329
return parseRet;
330
}
331
}
332
333
pEngine->notifications = NULL;
334
pEngine->notification_count = 0;
335
pEngine->notifications_capacity = 0;
336
337
/* Assign the callbacks */
338
pEngine->notificationCallback = pParams->fnNotificationCallback;
339
pEngine->pReadFile = pParams->fileIOCallbacks.readFileCallback;
340
pEngine->pGetOverlappedResult = pParams->fileIOCallbacks.getOverlappedResultCallback;
341
if (pEngine->pReadFile == NULL)
342
{
343
pEngine->pReadFile = FACT_INTERNAL_DefaultReadFile;
344
}
345
if (pEngine->pGetOverlappedResult == NULL)
346
{
347
pEngine->pGetOverlappedResult = FACT_INTERNAL_DefaultGetOverlappedResult;
348
}
349
350
/* Init the FAudio subsystem */
351
pEngine->audio = pParams->pXAudio2;
352
if (pEngine->audio == NULL)
353
{
354
FAudio_assert(pParams->pMasteringVoice == NULL);
355
FAudioCreate(&pEngine->audio, 0, FAUDIO_DEFAULT_PROCESSOR);
356
}
357
358
if (!pParams->pRendererID || !pParams->pRendererID[0])
359
{
360
deviceIndex = 0;
361
}
362
else
363
{
364
deviceIndex = pParams->pRendererID[0] - L'0';
365
if (deviceIndex > FAudio_PlatformGetDeviceCount())
366
deviceIndex = 0;
367
}
368
369
FAudio_GetDeviceDetails(pEngine->audio, deviceIndex, &device_details);
370
pEngine->output_format = device_details.OutputFormat;
371
372
/* Create the audio device */
373
pEngine->master = pParams->pMasteringVoice;
374
if (pEngine->master == NULL)
375
{
376
if (FAudio_CreateMasteringVoice(
377
pEngine->audio,
378
&pEngine->master,
379
FAUDIO_DEFAULT_CHANNELS,
380
FAUDIO_DEFAULT_SAMPLERATE,
381
0,
382
deviceIndex,
383
NULL
384
) != 0) {
385
FAudio_Release(pEngine->audio);
386
FAudio_PlatformUnlockMutex(pEngine->apiLock);
387
return FAUDIO_E_INVALID_CALL;
388
}
389
}
390
391
/* Create the reverb effect, if applicable */
392
if (pEngine->dspPresetCount > 0) /* Never more than 1...? */
393
{
394
FAudioVoice_GetVoiceDetails(pEngine->master, &masterDetails);
395
396
/* Reverb effect chain... */
397
FAudioCreateReverb(&reverbDesc.pEffect, 0);
398
reverbDesc.InitialState = 1;
399
reverbDesc.OutputChannels = (masterDetails.InputChannels == 6) ? 6 : 1;
400
reverbChain.EffectCount = 1;
401
reverbChain.pEffectDescriptors = &reverbDesc;
402
403
/* Reverb submix voice... */
404
FAudio_CreateSubmixVoice(
405
pEngine->audio,
406
&pEngine->reverbVoice,
407
1, /* Reverb will be omnidirectional */
408
masterDetails.InputSampleRate,
409
0,
410
0,
411
NULL,
412
&reverbChain
413
);
414
415
/* We can release now, the submix owns this! */
416
FAPOBase_Release((FAPOBase*) reverbDesc.pEffect);
417
}
418
419
pEngine->initialized = true;
420
pEngine->apiThread = FAudio_PlatformCreateThread(
421
FACT_INTERNAL_APIThread,
422
"FACT Thread",
423
pEngine
424
);
425
426
FAudio_PlatformUnlockMutex(pEngine->apiLock);
427
return FAUDIO_OK;
428
}
429
430
static void send_queued_wavebank_notifications(FACTAudioEngine *engine)
431
{
432
for (size_t i = 0; i < engine->prepared_wavebank_count; ++i)
433
send_wavebank_notification(engine, FACTNOTIFICATIONTYPE_WAVEBANKPREPARED, engine->prepared_wavebanks[i]);
434
engine->prepared_wavebank_count = 0;
435
}
436
437
uint32_t FACTAudioEngine_ShutDown(FACTAudioEngine *pEngine)
438
{
439
uint32_t i, refcount;
440
FAudioMutex mutex;
441
FAudioMallocFunc pMalloc;
442
FAudioFreeFunc pFree;
443
FAudioReallocFunc pRealloc;
444
445
/* Close thread, then lock ASAP */
446
pEngine->initialized = false;
447
FAudio_PlatformWaitThread(pEngine->apiThread, NULL);
448
FAudio_PlatformLockMutex(pEngine->apiLock);
449
450
/* Stop the platform stream before freeing stuff! */
451
if (pEngine->audio != NULL)
452
{
453
FAudio_StopEngine(pEngine->audio);
454
}
455
456
send_queued_wavebank_notifications(pEngine);
457
pEngine->pFree(pEngine->prepared_wavebanks);
458
459
/* This method destroys all existing cues, sound banks, and wave banks.
460
* It blocks until all cues are destroyed.
461
*/
462
while (pEngine->wbList != NULL)
463
{
464
FACTWaveBank_Destroy((FACTWaveBank*) pEngine->wbList->entry);
465
}
466
while (pEngine->sbList != NULL)
467
{
468
FACTSoundBank_Destroy((FACTSoundBank*) pEngine->sbList->entry);
469
}
470
471
pEngine->pFree(pEngine->notifications);
472
pEngine->notification_count = 0;
473
pEngine->notifications_capacity = 0;
474
475
/* Category data */
476
for (i = 0; i < pEngine->categoryCount; i += 1)
477
{
478
pEngine->pFree(pEngine->categoryNames[i]);
479
}
480
pEngine->pFree(pEngine->categoryNames);
481
pEngine->pFree(pEngine->categories);
482
483
/* Variable data */
484
for (i = 0; i < pEngine->variableCount; i += 1)
485
{
486
pEngine->pFree(pEngine->variableNames[i]);
487
}
488
pEngine->pFree(pEngine->variableNames);
489
pEngine->pFree(pEngine->variables);
490
pEngine->pFree(pEngine->globalVariableValues);
491
492
/* RPC data */
493
for (i = 0; i < pEngine->rpcCount; i += 1)
494
{
495
pEngine->pFree(pEngine->rpcs[i].points);
496
}
497
pEngine->pFree(pEngine->rpcs);
498
pEngine->pFree(pEngine->rpcCodes);
499
500
/* DSP data */
501
for (i = 0; i < pEngine->dspPresetCount; i += 1)
502
{
503
pEngine->pFree(pEngine->dspPresets[i].parameters);
504
}
505
pEngine->pFree(pEngine->dspPresets);
506
pEngine->pFree(pEngine->dspPresetCodes);
507
508
/* Audio resources */
509
if (pEngine->reverbVoice != NULL)
510
{
511
FAudioVoice_DestroyVoice(pEngine->reverbVoice);
512
}
513
if (pEngine->master != NULL)
514
{
515
FAudioVoice_DestroyVoice(pEngine->master);
516
}
517
if (pEngine->audio != NULL)
518
{
519
FAudio_Release(pEngine->audio);
520
}
521
522
/* Finally. */
523
refcount = pEngine->refcount;
524
mutex = pEngine->apiLock;
525
pMalloc = pEngine->pMalloc;
526
pFree = pEngine->pFree;
527
pRealloc = pEngine->pRealloc;
528
FAudio_zero(pEngine, sizeof(FACTAudioEngine));
529
pEngine->pMalloc = pMalloc;
530
pEngine->pFree = pFree;
531
pEngine->pRealloc = pRealloc;
532
pEngine->refcount = refcount;
533
pEngine->apiLock = mutex;
534
535
FAudio_PlatformUnlockMutex(pEngine->apiLock);
536
return FAUDIO_OK;
537
}
538
539
uint32_t FACTAudioEngine_DoWork(FACTAudioEngine *pEngine)
540
{
541
uint8_t i;
542
FACTCue *cue;
543
LinkedList *list;
544
FACTNotification *note;
545
546
FAudio_PlatformLockMutex(pEngine->apiLock);
547
548
send_queued_wavebank_notifications(pEngine);
549
550
list = pEngine->sbList;
551
while (list != NULL)
552
{
553
cue = ((FACTSoundBank*) list->entry)->cueList;
554
while (cue != NULL)
555
{
556
if (cue->playingSound != NULL)
557
for (i = 0; i < cue->playingSound->sound->trackCount; i += 1)
558
{
559
if ( cue->playingSound->tracks[i].upcomingWave.wave == NULL &&
560
cue->playingSound->tracks[i].waveEvtInst->loopCount > 0 )
561
{
562
FACT_INTERNAL_GetNextWave(
563
cue,
564
cue->playingSound->sound,
565
&cue->playingSound->sound->tracks[i],
566
&cue->playingSound->tracks[i],
567
cue->playingSound->tracks[i].waveEvt,
568
cue->playingSound->tracks[i].waveEvtInst
569
);
570
}
571
}
572
cue = cue->next;
573
}
574
list = list->next;
575
}
576
577
FAudio_PlatformUnlockMutex(pEngine->apiLock);
578
return FAUDIO_OK;
579
}
580
581
uint32_t FACTAudioEngine_CreateSoundBank(
582
FACTAudioEngine *pEngine,
583
const void *pvBuffer,
584
uint32_t dwSize,
585
uint32_t dwFlags,
586
uint32_t dwAllocAttributes,
587
FACTSoundBank **ppSoundBank
588
) {
589
uint32_t retval;
590
FAudio_PlatformLockMutex(pEngine->apiLock);
591
retval = FACT_INTERNAL_ParseSoundBank(
592
pEngine,
593
pvBuffer,
594
dwSize,
595
ppSoundBank
596
);
597
FAudio_PlatformUnlockMutex(pEngine->apiLock);
598
return retval;
599
}
600
601
uint32_t FACTAudioEngine_CreateInMemoryWaveBank(
602
FACTAudioEngine *pEngine,
603
const void *pvBuffer,
604
uint32_t dwSize,
605
uint32_t dwFlags,
606
uint32_t dwAllocAttributes,
607
FACTWaveBank **ppWaveBank
608
) {
609
uint32_t retval;
610
FAudio_PlatformLockMutex(pEngine->apiLock);
611
retval = FACT_INTERNAL_ParseWaveBank(
612
pEngine,
613
FAudio_memopen((void*) pvBuffer, dwSize),
614
0,
615
0,
616
FACT_INTERNAL_DefaultReadFile,
617
FACT_INTERNAL_DefaultGetOverlappedResult,
618
false,
619
ppWaveBank
620
);
621
if (pEngine->prepared_wavebank_count == pEngine->prepared_wavebanks_capacity)
622
{
623
pEngine->prepared_wavebanks_capacity = FAudio_max(pEngine->prepared_wavebanks_capacity * 2, 8);
624
pEngine->prepared_wavebanks = pEngine->pRealloc(pEngine->prepared_wavebanks,
625
pEngine->prepared_wavebanks_capacity * sizeof(FACTWaveBank *));
626
}
627
pEngine->prepared_wavebanks[pEngine->prepared_wavebank_count++] = *ppWaveBank;
628
FAudio_PlatformUnlockMutex(pEngine->apiLock);
629
return retval;
630
}
631
632
uint32_t FACTAudioEngine_CreateStreamingWaveBank(
633
FACTAudioEngine *pEngine,
634
const FACTStreamingParameters *pParms,
635
FACTWaveBank **ppWaveBank
636
) {
637
FACTNotification *note;
638
uint32_t retval, packetSize;
639
FAudio_PlatformLockMutex(pEngine->apiLock);
640
if ( pEngine->pReadFile == FACT_INTERNAL_DefaultReadFile &&
641
pEngine->pGetOverlappedResult == FACT_INTERNAL_DefaultGetOverlappedResult )
642
{
643
/* Our I/O doesn't care about packets, set to 0 as an optimization */
644
packetSize = 0;
645
}
646
else
647
{
648
packetSize = pParms->packetSize * 2048;
649
}
650
retval = FACT_INTERNAL_ParseWaveBank(
651
pEngine,
652
pParms->file,
653
pParms->offset,
654
packetSize,
655
pEngine->pReadFile,
656
pEngine->pGetOverlappedResult,
657
true,
658
ppWaveBank
659
);
660
if (pEngine->prepared_wavebank_count == pEngine->prepared_wavebanks_capacity)
661
{
662
pEngine->prepared_wavebanks_capacity = FAudio_max(pEngine->prepared_wavebanks_capacity * 2, 8);
663
pEngine->prepared_wavebanks = pEngine->pRealloc(pEngine->prepared_wavebanks,
664
pEngine->prepared_wavebanks_capacity * sizeof(FACTWaveBank *));
665
}
666
pEngine->prepared_wavebanks[pEngine->prepared_wavebank_count++] = *ppWaveBank;
667
FAudio_PlatformUnlockMutex(pEngine->apiLock);
668
return retval;
669
}
670
671
uint32_t FACTAudioEngine_PrepareWave(
672
FACTAudioEngine *pEngine,
673
uint32_t dwFlags,
674
const char *szWavePath,
675
uint32_t wStreamingPacketSize,
676
uint32_t dwAlignment,
677
uint32_t dwPlayOffset,
678
uint8_t nLoopCount,
679
FACTWave **ppWave
680
) {
681
/* TODO: FACTWave */
682
return FAUDIO_OK;
683
}
684
685
uint32_t FACTAudioEngine_PrepareInMemoryWave(
686
FACTAudioEngine *pEngine,
687
uint32_t dwFlags,
688
FACTWaveBankEntry entry,
689
uint32_t *pdwSeekTable, /* Optional! */
690
uint8_t *pbWaveData,
691
uint32_t dwPlayOffset,
692
uint8_t nLoopCount,
693
FACTWave **ppWave
694
) {
695
/* TODO: FACTWave */
696
return FAUDIO_OK;
697
}
698
699
uint32_t FACTAudioEngine_PrepareStreamingWave(
700
FACTAudioEngine *pEngine,
701
uint32_t dwFlags,
702
FACTWaveBankEntry entry,
703
FACTStreamingParameters streamingParams,
704
uint32_t dwAlignment,
705
uint32_t *pdwSeekTable, /* Optional! */
706
uint8_t *pbWaveData, /* ABI bug, do not use! */
707
uint32_t dwPlayOffset,
708
uint8_t nLoopCount,
709
FACTWave **ppWave
710
) {
711
/* TODO: FACTWave */
712
return FAUDIO_OK;
713
}
714
715
uint32_t FACTAudioEngine_RegisterNotification(FACTAudioEngine *engine, const FACTNotificationDescription *desc)
716
{
717
if (!desc)
718
return FAUDIO_E_INVALID_ARG;
719
720
FAudio_assert(engine != NULL);
721
722
if (!engine->notificationCallback)
723
return FACTENGINE_E_NONOTIFICATIONCALLBACK;
724
725
if (desc->type == 0 || desc->type > FACTNOTIFICATIONTYPE_WAVEBANKSTREAMING_INVALIDCONTENT)
726
return FAUDIO_E_INVALID_ARG;
727
728
FAudio_PlatformLockMutex(engine->apiLock);
729
730
if (engine->notification_count == engine->notifications_capacity)
731
{
732
engine->notifications_capacity = FAudio_max(engine->notifications_capacity * 2, 8);
733
engine->notifications = engine->pRealloc(engine->notifications,
734
engine->notifications_capacity * sizeof(FACTNotification));
735
}
736
engine->notifications[engine->notification_count++] = *desc;
737
738
FAudio_PlatformUnlockMutex(engine->apiLock);
739
return FAUDIO_OK;
740
}
741
742
static void unregister_notification(FACTAudioEngine *engine, const FACTNotificationDescription *desc)
743
{
744
if (!desc)
745
{
746
/* Unregister all notifications. This behaviour is not documented. */
747
engine->notification_count = 0;
748
return;
749
}
750
751
for (size_t i = 0; i < engine->notification_count; ++i)
752
{
753
if (!memcmp(desc, &engine->notifications[i], sizeof(*desc)))
754
{
755
remove_single_notification(engine, i);
756
return;
757
}
758
}
759
760
FAudio_Log("No matching notification found.\n");
761
}
762
763
uint32_t FACTAudioEngine_UnRegisterNotification(FACTAudioEngine *engine, const FACTNotificationDescription *desc)
764
{
765
FAudio_assert(engine != NULL);
766
767
if (!engine->notificationCallback)
768
return FACTENGINE_E_NONOTIFICATIONCALLBACK;
769
770
FAudio_PlatformLockMutex(engine->apiLock);
771
unregister_notification(engine, desc);
772
FAudio_PlatformUnlockMutex(engine->apiLock);
773
return FAUDIO_OK;
774
}
775
776
uint16_t FACTAudioEngine_GetCategory(
777
FACTAudioEngine *pEngine,
778
const char *szFriendlyName
779
) {
780
uint16_t i;
781
FAudio_PlatformLockMutex(pEngine->apiLock);
782
for (i = 0; i < pEngine->categoryCount; i += 1)
783
{
784
if (FAudio_strcmp(szFriendlyName, pEngine->categoryNames[i]) == 0)
785
{
786
FAudio_PlatformUnlockMutex(pEngine->apiLock);
787
return i;
788
}
789
}
790
FAudio_PlatformUnlockMutex(pEngine->apiLock);
791
return FACTCATEGORY_INVALID;
792
}
793
794
static bool FACT_INTERNAL_IsInCategory(
795
FACTAudioEngine *engine,
796
uint16_t target,
797
uint16_t category
798
) {
799
FACTAudioCategory *cat;
800
801
/* Same category, no need to go on a crazy hunt */
802
if (category == target)
803
{
804
return true;
805
}
806
807
/* Right, on with the crazy hunt */
808
cat = &engine->categories[category];
809
while (cat->parentCategory != -1)
810
{
811
if (cat->parentCategory == target)
812
{
813
return true;
814
}
815
cat = &engine->categories[cat->parentCategory];
816
}
817
return false;
818
}
819
820
uint32_t FACTAudioEngine_Stop(
821
FACTAudioEngine *pEngine,
822
uint16_t nCategory,
823
uint32_t dwFlags
824
) {
825
FACTCue *cue, *backup;
826
LinkedList *list;
827
828
FAudio_PlatformLockMutex(pEngine->apiLock);
829
list = pEngine->sbList;
830
while (list != NULL)
831
{
832
cue = ((FACTSoundBank*) list->entry)->cueList;
833
while (cue != NULL)
834
{
835
if ( cue->playingSound != NULL &&
836
FACT_INTERNAL_IsInCategory(
837
pEngine,
838
nCategory,
839
cue->playingSound->sound->category
840
) )
841
{
842
if ( dwFlags == FACT_FLAG_STOP_IMMEDIATE &&
843
cue->managed )
844
{
845
/* Just blow this up now */
846
backup = cue->next;
847
FACTCue_Destroy(cue);
848
cue = backup;
849
}
850
else
851
{
852
/* If managed, the mixer will destroy for us */
853
FACTCue_Stop(cue, dwFlags);
854
cue = cue->next;
855
}
856
}
857
else
858
{
859
cue = cue->next;
860
}
861
}
862
list = list->next;
863
}
864
FAudio_PlatformUnlockMutex(pEngine->apiLock);
865
return FAUDIO_OK;
866
}
867
868
uint32_t FACTAudioEngine_SetVolume(
869
FACTAudioEngine *pEngine,
870
uint16_t nCategory,
871
float volume
872
) {
873
uint16_t i;
874
FAudio_PlatformLockMutex(pEngine->apiLock);
875
pEngine->categories[nCategory].currentVolume = (
876
pEngine->categories[nCategory].volume *
877
volume
878
);
879
for (i = 0; i < pEngine->categoryCount; i += 1)
880
{
881
if (pEngine->categories[i].parentCategory == nCategory)
882
{
883
FACTAudioEngine_SetVolume(
884
pEngine,
885
i,
886
pEngine->categories[i].currentVolume
887
);
888
}
889
}
890
FAudio_PlatformUnlockMutex(pEngine->apiLock);
891
return FAUDIO_OK;
892
}
893
894
uint32_t FACTAudioEngine_Pause(
895
FACTAudioEngine *pEngine,
896
uint16_t nCategory,
897
int32_t fPause
898
) {
899
FACTCue *cue;
900
LinkedList *list;
901
902
FAudio_PlatformLockMutex(pEngine->apiLock);
903
list = pEngine->sbList;
904
while (list != NULL)
905
{
906
cue = ((FACTSoundBank*) list->entry)->cueList;
907
while (cue != NULL)
908
{
909
if ( cue->playingSound != NULL &&
910
FACT_INTERNAL_IsInCategory(
911
pEngine,
912
nCategory,
913
cue->playingSound->sound->category
914
) )
915
{
916
FACTCue_Pause(cue, fPause);
917
}
918
cue = cue->next;
919
}
920
list = list->next;
921
}
922
FAudio_PlatformUnlockMutex(pEngine->apiLock);
923
return FAUDIO_OK;
924
}
925
926
uint16_t FACTAudioEngine_GetGlobalVariableIndex(
927
FACTAudioEngine *pEngine,
928
const char *szFriendlyName
929
) {
930
uint16_t i;
931
for (i = 0; i < pEngine->variableCount; i += 1)
932
{
933
if (!FAudio_strcmp(szFriendlyName, pEngine->variableNames[i]) &&
934
!(pEngine->variables[i].accessibility & ACCESSIBILITY_CUE) &&
935
(pEngine->variables[i].accessibility & ACCESSIBILITY_PUBLIC))
936
return i;
937
}
938
return FACTVARIABLEINDEX_INVALID;
939
}
940
941
uint32_t FACTAudioEngine_SetGlobalVariable(
942
FACTAudioEngine *pEngine,
943
uint16_t nIndex,
944
float nValue
945
) {
946
FACTVariable *var;
947
948
if (nIndex >= pEngine->variableCount)
949
return FACTENGINE_E_INVALIDVARIABLEINDEX;
950
var = &pEngine->variables[nIndex];
951
if (!(var->accessibility & ACCESSIBILITY_PUBLIC) || (var->accessibility & ACCESSIBILITY_CUE)
952
|| (var->accessibility & ACCESSIBILITY_READONLY))
953
return FACTENGINE_E_INVALIDVARIABLEINDEX;
954
955
FAudio_PlatformLockMutex(pEngine->apiLock);
956
pEngine->globalVariableValues[nIndex] = FAudio_clamp(
957
nValue,
958
var->minValue,
959
var->maxValue
960
);
961
FAudio_PlatformUnlockMutex(pEngine->apiLock);
962
return FAUDIO_OK;
963
}
964
965
uint32_t FACTAudioEngine_GetGlobalVariable(
966
FACTAudioEngine *pEngine,
967
uint16_t nIndex,
968
float *pnValue
969
) {
970
FACTVariable *var;
971
972
if (nIndex >= pEngine->variableCount)
973
return FACTENGINE_E_INVALIDVARIABLEINDEX;
974
var = &pEngine->variables[nIndex];
975
if (!(var->accessibility & ACCESSIBILITY_PUBLIC) || (var->accessibility & ACCESSIBILITY_CUE))
976
return FACTENGINE_E_INVALIDVARIABLEINDEX;
977
978
FAudio_PlatformLockMutex(pEngine->apiLock);
979
*pnValue = pEngine->globalVariableValues[nIndex];
980
FAudio_PlatformUnlockMutex(pEngine->apiLock);
981
return FAUDIO_OK;
982
}
983
984
/* SoundBank implementation */
985
986
uint16_t FACTSoundBank_GetCueIndex(
987
FACTSoundBank *pSoundBank,
988
const char *szFriendlyName
989
) {
990
uint16_t i;
991
if (pSoundBank == NULL)
992
{
993
return FACTINDEX_INVALID;
994
}
995
996
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
997
if (pSoundBank->cueNames != NULL)
998
for (i = 0; i < pSoundBank->cueCount; i += 1)
999
{
1000
if (FAudio_strcmp(szFriendlyName, pSoundBank->cueNames[i]) == 0)
1001
{
1002
FAudio_PlatformUnlockMutex(
1003
pSoundBank->parentEngine->apiLock
1004
);
1005
return i;
1006
}
1007
}
1008
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1009
return FACTINDEX_INVALID;
1010
}
1011
1012
uint32_t FACTSoundBank_GetNumCues(
1013
FACTSoundBank *pSoundBank,
1014
uint16_t *pnNumCues
1015
) {
1016
if (pSoundBank == NULL)
1017
{
1018
*pnNumCues = 0;
1019
return FAUDIO_OK;
1020
}
1021
1022
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1023
*pnNumCues = pSoundBank->cueCount;
1024
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1025
return FAUDIO_OK;
1026
}
1027
1028
uint32_t FACTSoundBank_GetCueProperties(
1029
FACTSoundBank *pSoundBank,
1030
uint16_t nCueIndex,
1031
FACTCueProperties *pProperties
1032
) {
1033
uint16_t i;
1034
if (pSoundBank == NULL)
1035
{
1036
return 1;
1037
}
1038
1039
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1040
1041
if (pSoundBank->cueNames == NULL)
1042
{
1043
FAudio_zero(pProperties->friendlyName, 0xFF);
1044
}
1045
else
1046
{
1047
FAudio_strlcpy(
1048
pProperties->friendlyName,
1049
pSoundBank->cueNames[nCueIndex],
1050
0xFF
1051
);
1052
}
1053
if (!(pSoundBank->cues[nCueIndex].flags & CUE_FLAG_SINGLE_SOUND))
1054
{
1055
for (i = 0; i < pSoundBank->variationCount; i += 1)
1056
{
1057
if (pSoundBank->variations[i].code == pSoundBank->cues[nCueIndex].sbCode)
1058
{
1059
break;
1060
}
1061
}
1062
1063
FAudio_assert(i < pSoundBank->variationCount && "Variation table not found!");
1064
1065
if (pSoundBank->variations[i].type == VARIATION_TABLE_TYPE_INTERACTIVE)
1066
{
1067
pProperties->interactive = 1;
1068
pProperties->iaVariableIndex = pSoundBank->variations[i].variable;
1069
}
1070
else
1071
{
1072
pProperties->interactive = 0;
1073
pProperties->iaVariableIndex = FACTINDEX_INVALID;
1074
}
1075
pProperties->numVariations = pSoundBank->variations[i].entryCount;
1076
}
1077
else
1078
{
1079
pProperties->interactive = 0;
1080
pProperties->iaVariableIndex = FACTINDEX_INVALID;
1081
pProperties->numVariations = 1;
1082
}
1083
pProperties->maxInstances = pSoundBank->cues[nCueIndex].instanceLimit;
1084
pProperties->currentInstances = pSoundBank->cues[nCueIndex].instanceCount;
1085
1086
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1087
return FAUDIO_OK;
1088
}
1089
1090
uint32_t FACTSoundBank_Prepare(
1091
FACTSoundBank *pSoundBank,
1092
uint16_t nCueIndex,
1093
uint32_t dwFlags,
1094
int32_t timeOffset,
1095
FACTCue** ppCue
1096
) {
1097
bool interactive = false;
1098
uint16_t i;
1099
FACTCue *latest;
1100
1101
if (pSoundBank == NULL)
1102
{
1103
*ppCue = NULL;
1104
return 1;
1105
}
1106
1107
if (nCueIndex >= pSoundBank->cueCount)
1108
return FACTENGINE_E_INVALIDVARIABLEINDEX;
1109
1110
*ppCue = (FACTCue*) pSoundBank->parentEngine->pMalloc(sizeof(FACTCue));
1111
FAudio_zero(*ppCue, sizeof(FACTCue));
1112
1113
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1114
1115
/* Engine references */
1116
(*ppCue)->parentBank = pSoundBank;
1117
(*ppCue)->index = nCueIndex;
1118
1119
/* Sound data */
1120
(*ppCue)->data = &pSoundBank->cues[nCueIndex];
1121
if ((*ppCue)->data->flags & CUE_FLAG_SINGLE_SOUND)
1122
{
1123
for (i = 0; i < pSoundBank->soundCount; i += 1)
1124
{
1125
if ((*ppCue)->data->sbCode == pSoundBank->soundCodes[i])
1126
{
1127
(*ppCue)->sound = &pSoundBank->sounds[i];
1128
break;
1129
}
1130
}
1131
}
1132
else
1133
{
1134
for (i = 0; i < pSoundBank->variationCount; i += 1)
1135
{
1136
if ((*ppCue)->data->sbCode == pSoundBank->variations[i].code)
1137
{
1138
(*ppCue)->variation = &pSoundBank->variations[i];
1139
break;
1140
}
1141
}
1142
if ((*ppCue)->variation && (*ppCue)->variation->type == VARIATION_TABLE_TYPE_INTERACTIVE)
1143
{
1144
interactive = true;
1145
(*ppCue)->interactive = pSoundBank->parentEngine->variables[
1146
(*ppCue)->variation->variable
1147
].initialValue;
1148
}
1149
}
1150
1151
/* Instance data */
1152
(*ppCue)->variableValues = (float*) pSoundBank->parentEngine->pMalloc(
1153
sizeof(float) * pSoundBank->parentEngine->variableCount
1154
);
1155
for (i = 0; i < pSoundBank->parentEngine->variableCount; i += 1)
1156
{
1157
(*ppCue)->variableValues[i] =
1158
pSoundBank->parentEngine->variables[i].initialValue;
1159
}
1160
1161
/* Playback */
1162
(*ppCue)->state = FACT_STATE_PREPARED;
1163
1164
/* Add to the SoundBank Cue list */
1165
if (pSoundBank->cueList == NULL)
1166
{
1167
pSoundBank->cueList = *ppCue;
1168
}
1169
else
1170
{
1171
latest = pSoundBank->cueList;
1172
while (latest->next != NULL)
1173
{
1174
latest = latest->next;
1175
}
1176
latest->next = *ppCue;
1177
}
1178
1179
if (!interactive)
1180
create_sound(*ppCue);
1181
1182
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1183
return FAUDIO_OK;
1184
}
1185
1186
uint32_t FACTSoundBank_Play(
1187
FACTSoundBank *pSoundBank,
1188
uint16_t nCueIndex,
1189
uint32_t dwFlags,
1190
int32_t timeOffset,
1191
FACTCue** ppCue /* Optional! */
1192
) {
1193
FACTCue *result;
1194
if (pSoundBank == NULL)
1195
{
1196
if (ppCue != NULL)
1197
{
1198
*ppCue = NULL;
1199
}
1200
return 1;
1201
}
1202
1203
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1204
1205
FACTSoundBank_Prepare(
1206
pSoundBank,
1207
nCueIndex,
1208
dwFlags,
1209
timeOffset,
1210
&result
1211
);
1212
if (ppCue != NULL)
1213
{
1214
*ppCue = result;
1215
}
1216
else
1217
{
1218
/* AKA we get to Destroy() this ourselves */
1219
result->managed = true;
1220
}
1221
FACTCue_Play(result);
1222
1223
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1224
return FAUDIO_OK;
1225
}
1226
1227
uint32_t FACTSoundBank_Play3D(
1228
FACTSoundBank *pSoundBank,
1229
uint16_t nCueIndex,
1230
uint32_t dwFlags,
1231
int32_t timeOffset,
1232
F3DAUDIO_DSP_SETTINGS *pDSPSettings,
1233
FACTCue** ppCue /* Optional! */
1234
) {
1235
FACTCue *result;
1236
if (pSoundBank == NULL)
1237
{
1238
if (ppCue != NULL)
1239
{
1240
*ppCue = NULL;
1241
}
1242
return 1;
1243
}
1244
1245
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1246
1247
FACTSoundBank_Prepare(
1248
pSoundBank,
1249
nCueIndex,
1250
dwFlags,
1251
timeOffset,
1252
&result
1253
);
1254
if (ppCue != NULL)
1255
{
1256
*ppCue = result;
1257
}
1258
else
1259
{
1260
/* AKA we get to Destroy() this ourselves */
1261
result->managed = true;
1262
}
1263
FACT3DApply(pDSPSettings, result);
1264
FACTCue_Play(result);
1265
1266
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1267
return FAUDIO_OK;
1268
}
1269
1270
uint32_t FACTSoundBank_Stop(
1271
FACTSoundBank *pSoundBank,
1272
uint16_t nCueIndex,
1273
uint32_t dwFlags
1274
) {
1275
FACTCue *backup, *cue;
1276
if (pSoundBank == NULL)
1277
{
1278
return 1;
1279
}
1280
1281
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1282
cue = pSoundBank->cueList;
1283
while (cue != NULL)
1284
{
1285
if (cue->index == nCueIndex)
1286
{
1287
if ( dwFlags == FACT_FLAG_STOP_IMMEDIATE &&
1288
cue->managed )
1289
{
1290
/* Just blow this up now */
1291
backup = cue->next;
1292
FACTCue_Destroy(cue);
1293
cue = backup;
1294
}
1295
else
1296
{
1297
/* If managed, the mixer will destroy for us */
1298
FACTCue_Stop(cue, dwFlags);
1299
cue = cue->next;
1300
}
1301
}
1302
else
1303
{
1304
cue = cue->next;
1305
}
1306
}
1307
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1308
return FAUDIO_OK;
1309
}
1310
1311
uint32_t FACTSoundBank_Destroy(FACTSoundBank *pSoundBank)
1312
{
1313
uint16_t i, j, k;
1314
FAudioMutex mutex;
1315
1316
if (pSoundBank == NULL)
1317
{
1318
return 1;
1319
}
1320
1321
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1322
1323
/* Synchronously destroys all cues that are associated */
1324
while (pSoundBank->cueList != NULL)
1325
{
1326
FACTCue_Destroy(pSoundBank->cueList);
1327
}
1328
1329
/* Remove this SoundBank from the Engine list */
1330
LinkedList_RemoveEntry(
1331
&pSoundBank->parentEngine->sbList,
1332
pSoundBank,
1333
pSoundBank->parentEngine->sbLock,
1334
pSoundBank->parentEngine->pFree
1335
);
1336
1337
/* SoundBank Name */
1338
pSoundBank->parentEngine->pFree(pSoundBank->name);
1339
1340
/* Cue data */
1341
pSoundBank->parentEngine->pFree(pSoundBank->cues);
1342
1343
/* WaveBank Name data */
1344
for (i = 0; i < pSoundBank->wavebankCount; i += 1)
1345
{
1346
pSoundBank->parentEngine->pFree(pSoundBank->wavebankNames[i]);
1347
}
1348
pSoundBank->parentEngine->pFree(pSoundBank->wavebankNames);
1349
1350
/* Sound data */
1351
for (i = 0; i < pSoundBank->soundCount; i += 1)
1352
{
1353
for (j = 0; j < pSoundBank->sounds[i].trackCount; j += 1)
1354
{
1355
for (k = 0; k < pSoundBank->sounds[i].tracks[j].eventCount; k += 1)
1356
{
1357
#define MATCH(t) \
1358
pSoundBank->sounds[i].tracks[j].events[k].type == t
1359
if ( MATCH(FACTEVENT_PLAYWAVE) ||
1360
MATCH(FACTEVENT_PLAYWAVETRACKVARIATION) ||
1361
MATCH(FACTEVENT_PLAYWAVEEFFECTVARIATION) ||
1362
MATCH(FACTEVENT_PLAYWAVETRACKEFFECTVARIATION) )
1363
{
1364
if (pSoundBank->sounds[i].tracks[j].events[k].wave.isComplex)
1365
{
1366
pSoundBank->parentEngine->pFree(
1367
pSoundBank->sounds[i].tracks[j].events[k].wave.complex.wave_indices
1368
);
1369
pSoundBank->parentEngine->pFree(
1370
pSoundBank->sounds[i].tracks[j].events[k].wave.complex.wavebanks
1371
);
1372
pSoundBank->parentEngine->pFree(
1373
pSoundBank->sounds[i].tracks[j].events[k].wave.complex.weights
1374
);
1375
}
1376
}
1377
#undef MATCH
1378
}
1379
pSoundBank->parentEngine->pFree((void *)pSoundBank->sounds[i].tracks[j].events);
1380
}
1381
pSoundBank->parentEngine->pFree((void *)pSoundBank->sounds[i].tracks);
1382
pSoundBank->parentEngine->pFree((void *)pSoundBank->sounds[i].rpc_codes.codes);
1383
pSoundBank->parentEngine->pFree(pSoundBank->sounds[i].dspCodes);
1384
}
1385
pSoundBank->parentEngine->pFree((void *)pSoundBank->sounds);
1386
pSoundBank->parentEngine->pFree(pSoundBank->soundCodes);
1387
1388
/* Variation data */
1389
for (i = 0; i < pSoundBank->variationCount; i += 1)
1390
{
1391
pSoundBank->parentEngine->pFree(
1392
pSoundBank->variations[i].entries
1393
);
1394
}
1395
pSoundBank->parentEngine->pFree(pSoundBank->variations);
1396
1397
/* Transition data */
1398
for (i = 0; i < pSoundBank->transitionCount; i += 1)
1399
{
1400
pSoundBank->parentEngine->pFree(
1401
pSoundBank->transitions[i].entries
1402
);
1403
}
1404
pSoundBank->parentEngine->pFree(pSoundBank->transitions);
1405
pSoundBank->parentEngine->pFree(pSoundBank->transitionCodes);
1406
1407
/* Cue Name data */
1408
if (pSoundBank->cueNames != NULL)
1409
{
1410
for (i = 0; i < pSoundBank->cueCount; i += 1)
1411
{
1412
pSoundBank->parentEngine->pFree(pSoundBank->cueNames[i]);
1413
}
1414
pSoundBank->parentEngine->pFree(pSoundBank->cueNames);
1415
}
1416
1417
send_soundbank_notification(pSoundBank->parentEngine, pSoundBank);
1418
1419
mutex = pSoundBank->parentEngine->apiLock;
1420
pSoundBank->parentEngine->pFree(pSoundBank);
1421
FAudio_PlatformUnlockMutex(mutex);
1422
return FAUDIO_OK;
1423
}
1424
1425
uint32_t FACTSoundBank_GetState(
1426
FACTSoundBank *pSoundBank,
1427
uint32_t *pdwState
1428
) {
1429
uint16_t i;
1430
if (pSoundBank == NULL)
1431
{
1432
*pdwState = 0;
1433
return 1;
1434
}
1435
1436
FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
1437
1438
*pdwState = 0;
1439
for (i = 0; i < pSoundBank->cueCount; i += 1)
1440
{
1441
if (pSoundBank->cues[i].instanceCount > 0)
1442
{
1443
*pdwState |= FACT_STATE_INUSE;
1444
FAudio_PlatformUnlockMutex(
1445
pSoundBank->parentEngine->apiLock
1446
);
1447
return FAUDIO_OK;
1448
}
1449
}
1450
1451
FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
1452
return FAUDIO_OK;
1453
}
1454
1455
/* WaveBank implementation */
1456
1457
uint32_t FACTWaveBank_Destroy(FACTWaveBank *pWaveBank)
1458
{
1459
uint32_t i;
1460
FACTWave *wave;
1461
FAudioMutex mutex;
1462
FACTNotification note;
1463
if (pWaveBank == NULL)
1464
{
1465
return 1;
1466
}
1467
1468
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1469
1470
/* Synchronously destroys any cues that are using the wavebank */
1471
while (pWaveBank->waveList != NULL)
1472
{
1473
wave = (FACTWave*) pWaveBank->waveList->entry;
1474
if (wave->parentCue != NULL)
1475
{
1476
/* Destroying this Cue destroys the Wave */
1477
FACTCue_Destroy(wave->parentCue);
1478
}
1479
else
1480
{
1481
FACTWave_Destroy(wave);
1482
}
1483
}
1484
1485
/* Remove this WaveBank from the Engine list */
1486
LinkedList_RemoveEntry(
1487
&pWaveBank->parentEngine->wbList,
1488
pWaveBank,
1489
pWaveBank->parentEngine->wbLock,
1490
pWaveBank->parentEngine->pFree
1491
);
1492
1493
/* Free everything, finally. */
1494
pWaveBank->parentEngine->pFree(pWaveBank->name);
1495
pWaveBank->parentEngine->pFree(pWaveBank->entries);
1496
pWaveBank->parentEngine->pFree(pWaveBank->entryRefs);
1497
if (pWaveBank->seekTables != NULL)
1498
{
1499
for (i = 0; i < pWaveBank->entryCount; i += 1)
1500
{
1501
if (pWaveBank->seekTables[i].entries != NULL)
1502
{
1503
pWaveBank->parentEngine->pFree(
1504
pWaveBank->seekTables[i].entries
1505
);
1506
}
1507
}
1508
pWaveBank->parentEngine->pFree(pWaveBank->seekTables);
1509
}
1510
1511
if (!pWaveBank->streaming)
1512
{
1513
FAudio_close(pWaveBank->io);
1514
}
1515
1516
if (pWaveBank->packetBuffer != NULL)
1517
{
1518
pWaveBank->parentEngine->pFree(pWaveBank->packetBuffer);
1519
}
1520
send_wavebank_notification(pWaveBank->parentEngine, FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED, pWaveBank);
1521
FAudio_PlatformDestroyMutex(pWaveBank->waveLock);
1522
1523
if (pWaveBank->waveBankNames != NULL)
1524
{
1525
pWaveBank->parentEngine->pFree(pWaveBank->waveBankNames);
1526
}
1527
1528
mutex = pWaveBank->parentEngine->apiLock;
1529
pWaveBank->parentEngine->pFree(pWaveBank);
1530
FAudio_PlatformUnlockMutex(mutex);
1531
return FAUDIO_OK;
1532
}
1533
1534
uint32_t FACTWaveBank_GetState(
1535
FACTWaveBank *pWaveBank,
1536
uint32_t *pdwState
1537
) {
1538
uint32_t i;
1539
if (pWaveBank == NULL)
1540
{
1541
*pdwState = 0;
1542
return 1;
1543
}
1544
1545
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1546
1547
*pdwState = FACT_STATE_PREPARED;
1548
for (i = 0; i < pWaveBank->entryCount; i += 1)
1549
{
1550
if (pWaveBank->entryRefs[i] > 0)
1551
{
1552
*pdwState |= FACT_STATE_INUSE;
1553
FAudio_PlatformUnlockMutex(
1554
pWaveBank->parentEngine->apiLock
1555
);
1556
return FAUDIO_OK;
1557
}
1558
}
1559
1560
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1561
return FAUDIO_OK;
1562
}
1563
1564
uint32_t FACTWaveBank_GetNumWaves(
1565
FACTWaveBank *pWaveBank,
1566
uint16_t *pnNumWaves
1567
) {
1568
if (pWaveBank == NULL)
1569
{
1570
*pnNumWaves = 0;
1571
return 1;
1572
}
1573
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1574
*pnNumWaves = pWaveBank->entryCount;
1575
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1576
return FAUDIO_OK;
1577
}
1578
1579
uint16_t FACTWaveBank_GetWaveIndex(
1580
FACTWaveBank *pWaveBank,
1581
const char *szFriendlyName
1582
) {
1583
uint16_t i;
1584
char *curName;
1585
if (pWaveBank == NULL || pWaveBank->waveBankNames == NULL)
1586
{
1587
return FACTINDEX_INVALID;
1588
}
1589
1590
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1591
curName = pWaveBank->waveBankNames;
1592
for (i = 0; i < pWaveBank->entryCount; i += 1, curName += 64)
1593
{
1594
if (FAudio_strncmp(szFriendlyName, curName, 64) == 0)
1595
{
1596
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1597
return i;
1598
}
1599
}
1600
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1601
1602
return FACTINDEX_INVALID;
1603
}
1604
1605
uint32_t FACTWaveBank_GetWaveProperties(
1606
FACTWaveBank *pWaveBank,
1607
uint16_t nWaveIndex,
1608
FACTWaveProperties *pWaveProperties
1609
) {
1610
FACTWaveBankEntry *entry;
1611
if (pWaveBank == NULL)
1612
{
1613
return 1;
1614
}
1615
1616
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1617
1618
entry = &pWaveBank->entries[nWaveIndex];
1619
1620
if (pWaveBank->waveBankNames)
1621
{
1622
FAudio_memcpy(
1623
pWaveProperties->friendlyName,
1624
&pWaveBank->waveBankNames[nWaveIndex * 64],
1625
sizeof(pWaveProperties->friendlyName)
1626
);
1627
}
1628
else
1629
{
1630
FAudio_zero(
1631
pWaveProperties->friendlyName,
1632
sizeof(pWaveProperties->friendlyName)
1633
);
1634
}
1635
1636
pWaveProperties->format = entry->Format;
1637
pWaveProperties->durationInSamples = entry->PlayRegion.dwLength;
1638
if (entry->Format.wFormatTag == FACT_WAVEBANKMINIFORMAT_TAG_PCM)
1639
{
1640
pWaveProperties->durationInSamples /= (8 << entry->Format.wBitsPerSample) / 8;
1641
pWaveProperties->durationInSamples /= entry->Format.nChannels;
1642
}
1643
else if (entry->Format.wFormatTag == FACT_WAVEBANKMINIFORMAT_TAG_ADPCM)
1644
{
1645
pWaveProperties->durationInSamples = (
1646
pWaveProperties->durationInSamples /
1647
((entry->Format.wBlockAlign + 22) * entry->Format.nChannels) *
1648
((entry->Format.wBlockAlign + 16) * 2)
1649
);
1650
}
1651
else
1652
{
1653
FAudio_assert(0 && "Unrecognized wFormatTag!");
1654
}
1655
1656
pWaveProperties->loopRegion = entry->LoopRegion;
1657
pWaveProperties->streaming = pWaveBank->streaming;
1658
1659
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1660
return FAUDIO_OK;
1661
}
1662
1663
uint32_t FACTWaveBank_Prepare(
1664
FACTWaveBank *pWaveBank,
1665
uint16_t nWaveIndex,
1666
uint32_t dwFlags,
1667
uint32_t dwPlayOffset,
1668
uint8_t nLoopCount,
1669
FACTWave **ppWave
1670
) {
1671
FAudioBuffer buffer;
1672
FAudioBufferWMA bufferWMA;
1673
FAudioVoiceSends sends;
1674
FAudioSendDescriptor send;
1675
union
1676
{
1677
FAudioWaveFormatEx pcm;
1678
FAudioADPCMWaveFormat adpcm;
1679
FAudioXMA2WaveFormat xma2;
1680
} format;
1681
FACTWaveBankEntry *entry;
1682
FACTSeekTable *seek;
1683
if (pWaveBank == NULL)
1684
{
1685
*ppWave = NULL;
1686
return 1;
1687
}
1688
1689
*ppWave = (FACTWave*) pWaveBank->parentEngine->pMalloc(sizeof(FACTWave));
1690
1691
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1692
1693
entry = &pWaveBank->entries[nWaveIndex];
1694
1695
/* Engine references */
1696
(*ppWave)->parentBank = pWaveBank;
1697
(*ppWave)->parentCue = NULL;
1698
(*ppWave)->index = nWaveIndex;
1699
1700
(*ppWave)->background_music = (dwFlags & FACT_FLAG_BACKGROUND_MUSIC);
1701
1702
/* Playback */
1703
(*ppWave)->state = FACT_STATE_PREPARED;
1704
(*ppWave)->volume = 1.0f;
1705
(*ppWave)->pitch = 0;
1706
(*ppWave)->loopCount = nLoopCount;
1707
1708
if (dwPlayOffset)
1709
FAudio_Log("Unhandled play offset.\n");
1710
#if 0
1711
if (dwFlags & FACT_FLAG_UNITS_MS)
1712
{
1713
dwPlayOffset = (uint32_t) (
1714
( /* Samples per millisecond... */
1715
(float) entry->Format.nSamplesPerSec /
1716
1000.0f
1717
) * (float) dwPlayOffset
1718
);
1719
}
1720
#endif
1721
1722
/* Create the voice */
1723
send.Flags = 0;
1724
send.pOutputVoice = pWaveBank->parentEngine->master;
1725
sends.SendCount = 1;
1726
sends.pSends = &send;
1727
format.pcm.nChannels = entry->Format.nChannels;
1728
format.pcm.nSamplesPerSec = entry->Format.nSamplesPerSec;
1729
if (entry->Format.wFormatTag == FACT_WAVEBANKMINIFORMAT_TAG_PCM)
1730
{
1731
format.pcm.wFormatTag = FAUDIO_FORMAT_PCM;
1732
format.pcm.wBitsPerSample = 8 << entry->Format.wBitsPerSample;
1733
format.pcm.nBlockAlign = format.pcm.nChannels * format.pcm.wBitsPerSample / 8;
1734
format.pcm.nAvgBytesPerSec = format.pcm.nBlockAlign * format.pcm.nSamplesPerSec;
1735
format.pcm.cbSize = 0;
1736
}
1737
else if (entry->Format.wFormatTag == FACT_WAVEBANKMINIFORMAT_TAG_XMA)
1738
{
1739
/* XMA2 is quite similar to WMA Pro... is what everyone thought.
1740
* What a great way to start this comment.
1741
*
1742
* Let's reconstruct the extra data because who knows what decoder we're dealing with in <present year>.
1743
* It's also a good exercise in understanding XMA2 metadata and feeding blocks into the decoder properly.
1744
* At the time of writing this patch, it's FFmpeg via gstreamer which doesn't even respect most of this.
1745
* ... which means: good luck to whoever ends up finding inaccuracies here in the future!
1746
*
1747
* dwLoopLength seems to match dwPlayLength in everything I've seen that had bLoopCount == 0.
1748
* dwLoopBegin can be > 0 even with bLoopCount == 0 because why not. Let's ignore that.
1749
*
1750
* dwSamplesEncoded is usually close to dwPlayLength but not always (if ever?) equal. Let's assume equality.
1751
* The XMA2 seek table uses sample indices as opposed to WMA's byte index seek table.
1752
*
1753
* nBlockAlign uses aWMABlockAlign given the entire WMA Pro thing BUT it's expected to be the block size for decoding.
1754
* The XMA2 block size MUST be a multiple of 2048 BUT entry->PlayRegion.dwLength / seek->entryCount doesn't respect that.
1755
* And even when correctly guesstimating the block size, we sometimes end up with block sizes >= 64k BYTES. nBlockAlign IS 16-BIT!
1756
* Scrap nBlockAlign. I've given up and made all FAudio gstreamer functions use dwBytesPerBlock if available.
1757
* Still though, if we don't want FAudio_INTERNAL_DecodeGSTREAMER to hang, the total data length must match (see SoundEffect.cs in FNA).
1758
* As such, we round up when guessing the block size, feed GStreamer with zeroes^Wundersized blocks and hope for the best.
1759
*
1760
* This is FUN.
1761
* -ade
1762
*/
1763
FAudio_assert(entry->Format.wBitsPerSample != 0);
1764
1765
seek = &pWaveBank->seekTables[nWaveIndex];
1766
format.pcm.wFormatTag = FAUDIO_FORMAT_XMAUDIO2;
1767
format.pcm.wBitsPerSample = 16;
1768
format.pcm.nAvgBytesPerSec = aWMAAvgBytesPerSec[entry->Format.wBlockAlign >> 5];
1769
format.pcm.nBlockAlign = aWMABlockAlign[entry->Format.wBlockAlign & 0x1F];
1770
format.pcm.cbSize = (
1771
sizeof(FAudioXMA2WaveFormat) -
1772
sizeof(FAudioWaveFormatEx)
1773
);
1774
format.xma2.wNumStreams = (format.pcm.nChannels + 1) / 2;
1775
format.xma2.dwChannelMask = format.pcm.nChannels > 1 ? 0xFFFFFFFF >> (32 - format.pcm.nChannels) : 0;
1776
format.xma2.dwSamplesEncoded = seek->entries[seek->entryCount - 1];
1777
format.xma2.dwBytesPerBlock = (uint16_t) FAudio_ceil(
1778
(double) entry->PlayRegion.dwLength /
1779
(double) seek->entryCount /
1780
2048.0
1781
) * 2048;
1782
format.xma2.dwPlayBegin = format.xma2.dwLoopBegin = 0;
1783
format.xma2.dwPlayLength = format.xma2.dwLoopLength = format.xma2.dwSamplesEncoded;
1784
format.xma2.bLoopCount = 0;
1785
format.xma2.bEncoderVersion = 4;
1786
format.xma2.wBlockCount = seek->entryCount;
1787
}
1788
else if (entry->Format.wFormatTag == FACT_WAVEBANKMINIFORMAT_TAG_ADPCM)
1789
{
1790
format.pcm.wFormatTag = FAUDIO_FORMAT_MSADPCM;
1791
format.pcm.nBlockAlign = (entry->Format.wBlockAlign + 22) * format.pcm.nChannels;
1792
format.pcm.wBitsPerSample = 16;
1793
format.pcm.cbSize = (
1794
sizeof(FAudioADPCMWaveFormat) -
1795
sizeof(FAudioWaveFormatEx)
1796
);
1797
format.adpcm.wSamplesPerBlock = (
1798
((format.pcm.nBlockAlign / format.pcm.nChannels) - 6) * 2
1799
);
1800
}
1801
else if (entry->Format.wFormatTag == FACT_WAVEBANKMINIFORMAT_TAG_WMA)
1802
{
1803
/* Apparently this is used to detect WMA Pro...? */
1804
FAudio_assert(entry->Format.wBitsPerSample == 0);
1805
1806
format.pcm.wFormatTag = FAUDIO_FORMAT_WMAUDIO2;
1807
format.pcm.nAvgBytesPerSec = aWMAAvgBytesPerSec[entry->Format.wBlockAlign >> 5];
1808
format.pcm.nBlockAlign = aWMABlockAlign[entry->Format.wBlockAlign & 0x1F];
1809
format.pcm.wBitsPerSample = 16;
1810
format.pcm.cbSize = 0;
1811
}
1812
(*ppWave)->callback.callback.OnBufferEnd = pWaveBank->streaming ?
1813
FACT_INTERNAL_OnBufferEnd :
1814
NULL;
1815
(*ppWave)->callback.callback.OnBufferStart = NULL;
1816
(*ppWave)->callback.callback.OnLoopEnd = NULL;
1817
(*ppWave)->callback.callback.OnStreamEnd = FACT_INTERNAL_OnStreamEnd;
1818
(*ppWave)->callback.callback.OnVoiceError = NULL;
1819
(*ppWave)->callback.callback.OnVoiceProcessingPassEnd = NULL;
1820
(*ppWave)->callback.callback.OnVoiceProcessingPassStart = NULL;
1821
(*ppWave)->callback.wave = *ppWave;
1822
(*ppWave)->srcChannels = format.pcm.nChannels;
1823
FAudio_CreateSourceVoice(
1824
pWaveBank->parentEngine->audio,
1825
&(*ppWave)->voice,
1826
&format.pcm,
1827
FAUDIO_VOICE_USEFILTER, /* FIXME: Can this be optional? */
1828
4.0f,
1829
(FAudioVoiceCallback*) &(*ppWave)->callback,
1830
&sends,
1831
NULL
1832
);
1833
if (pWaveBank->streaming)
1834
{
1835
/* Init stream cache info */
1836
if (format.pcm.wFormatTag == FAUDIO_FORMAT_PCM)
1837
{
1838
(*ppWave)->streamSize = (
1839
format.pcm.nSamplesPerSec *
1840
format.pcm.nBlockAlign
1841
);
1842
}
1843
else if (format.pcm.wFormatTag == FAUDIO_FORMAT_MSADPCM)
1844
{
1845
(*ppWave)->streamSize = (
1846
format.pcm.nSamplesPerSec /
1847
format.adpcm.wSamplesPerBlock *
1848
format.pcm.nBlockAlign
1849
);
1850
}
1851
else
1852
{
1853
/* Screw it, load the whole thing */
1854
(*ppWave)->streamSize = entry->PlayRegion.dwLength;
1855
1856
/* XACT does NOT support loop subregions for these formats */
1857
FAudio_assert(entry->LoopRegion.dwStartSample == 0);
1858
FAudio_assert(entry->LoopRegion.dwTotalSamples == 0 || entry->LoopRegion.dwTotalSamples == entry->Duration);
1859
}
1860
(*ppWave)->streamCache = (uint8_t*) pWaveBank->parentEngine->pMalloc(
1861
(*ppWave)->streamSize
1862
);
1863
(*ppWave)->streamOffset = entry->PlayRegion.dwOffset;
1864
1865
/* Read and submit first buffer from the WaveBank */
1866
FACT_INTERNAL_OnBufferEnd(&(*ppWave)->callback.callback, NULL);
1867
}
1868
else
1869
{
1870
(*ppWave)->streamCache = NULL;
1871
1872
buffer.Flags = FAUDIO_END_OF_STREAM;
1873
buffer.AudioBytes = entry->PlayRegion.dwLength;
1874
buffer.pAudioData = FAudio_memptr(
1875
pWaveBank->io,
1876
entry->PlayRegion.dwOffset
1877
);
1878
buffer.PlayBegin = 0;
1879
buffer.PlayLength = entry->Duration;
1880
if (nLoopCount == 0)
1881
{
1882
buffer.LoopBegin = 0;
1883
buffer.LoopLength = 0;
1884
buffer.LoopCount = 0;
1885
}
1886
else
1887
{
1888
buffer.LoopBegin = entry->LoopRegion.dwStartSample;
1889
buffer.LoopLength = entry->LoopRegion.dwTotalSamples;
1890
buffer.LoopCount = nLoopCount;
1891
}
1892
buffer.pContext = NULL;
1893
if (format.pcm.wFormatTag == FAUDIO_FORMAT_WMAUDIO2)
1894
{
1895
bufferWMA.pDecodedPacketCumulativeBytes =
1896
pWaveBank->seekTables[nWaveIndex].entries;
1897
bufferWMA.PacketCount =
1898
pWaveBank->seekTables[nWaveIndex].entryCount;
1899
FAudioSourceVoice_SubmitSourceBuffer(
1900
(*ppWave)->voice,
1901
&buffer,
1902
&bufferWMA
1903
);
1904
}
1905
else
1906
{
1907
FAudioSourceVoice_SubmitSourceBuffer(
1908
(*ppWave)->voice,
1909
&buffer,
1910
NULL
1911
);
1912
}
1913
}
1914
1915
/* Add to the WaveBank Wave list */
1916
LinkedList_AddEntry(
1917
&pWaveBank->waveList,
1918
*ppWave,
1919
pWaveBank->waveLock,
1920
pWaveBank->parentEngine->pMalloc
1921
);
1922
1923
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1924
return FAUDIO_OK;
1925
}
1926
1927
uint32_t FACTWaveBank_Play(
1928
FACTWaveBank *pWaveBank,
1929
uint16_t nWaveIndex,
1930
uint32_t dwFlags,
1931
uint32_t dwPlayOffset,
1932
uint8_t nLoopCount,
1933
FACTWave **ppWave
1934
) {
1935
if (pWaveBank == NULL)
1936
{
1937
*ppWave = NULL;
1938
return 1;
1939
}
1940
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1941
FACTWaveBank_Prepare(
1942
pWaveBank,
1943
nWaveIndex,
1944
dwFlags,
1945
dwPlayOffset,
1946
nLoopCount,
1947
ppWave
1948
);
1949
FACTWave_Play(*ppWave);
1950
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1951
return FAUDIO_OK;
1952
}
1953
1954
uint32_t FACTWaveBank_Stop(
1955
FACTWaveBank *pWaveBank,
1956
uint16_t nWaveIndex,
1957
uint32_t dwFlags
1958
) {
1959
FACTWave *wave;
1960
LinkedList *list;
1961
if (pWaveBank == NULL)
1962
{
1963
return 1;
1964
}
1965
FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
1966
list = pWaveBank->waveList;
1967
while (list != NULL)
1968
{
1969
wave = (FACTWave*) list->entry;
1970
if (wave->index == nWaveIndex)
1971
{
1972
FACTWave_Stop(wave, dwFlags);
1973
}
1974
list = list->next;
1975
}
1976
FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
1977
return FAUDIO_OK;
1978
}
1979
1980
/* Wave implementation */
1981
1982
uint32_t FACTWave_Destroy(FACTWave *pWave)
1983
{
1984
FACTNotificationWave notification;
1985
FAudioMutex mutex;
1986
1987
if (pWave == NULL)
1988
{
1989
return 1;
1990
}
1991
1992
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
1993
1994
/* Stop before we start deleting everything */
1995
FACTWave_Stop(pWave, FACT_FLAG_STOP_IMMEDIATE);
1996
1997
LinkedList_RemoveEntry(
1998
&pWave->parentBank->waveList,
1999
pWave,
2000
pWave->parentBank->waveLock,
2001
pWave->parentBank->parentEngine->pFree
2002
);
2003
2004
FAudioVoice_DestroyVoice(pWave->voice);
2005
if (pWave->streamCache != NULL)
2006
{
2007
pWave->parentBank->parentEngine->pFree(pWave->streamCache);
2008
}
2009
notification.pWave = pWave;
2010
send_wave_notification(pWave->parentBank->parentEngine, FACTNOTIFICATIONTYPE_WAVEDESTROYED, &notification);
2011
2012
mutex = pWave->parentBank->parentEngine->apiLock;
2013
pWave->parentBank->parentEngine->pFree(pWave);
2014
FAudio_PlatformUnlockMutex(mutex);
2015
return FAUDIO_OK;
2016
}
2017
2018
uint32_t FACTWave_Play(FACTWave *pWave)
2019
{
2020
if (pWave == NULL)
2021
{
2022
return 1;
2023
}
2024
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2025
2026
if (pWave->state & (FACT_STATE_PLAYING | FACT_STATE_STOPPING | FACT_STATE_STOPPED))
2027
{
2028
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2029
return FACTENGINE_E_INVALIDUSAGE;
2030
}
2031
2032
if (!(pWave->state & FACT_STATE_PAUSED))
2033
{
2034
pWave->state |= FACT_STATE_PLAYING;
2035
pWave->state &= ~FACT_STATE_PREPARED;
2036
FAudioSourceVoice_Start(pWave->voice, 0, 0);
2037
}
2038
2039
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2040
return FAUDIO_OK;
2041
}
2042
2043
uint32_t FACTWave_Stop(FACTWave *pWave, uint32_t dwFlags)
2044
{
2045
FACTNotificationWave notification;
2046
2047
if (pWave == NULL)
2048
{
2049
return 1;
2050
}
2051
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2052
2053
/* There are two ways that a Wave might be stopped immediately:
2054
* 1. The program explicitly asks for it
2055
* 2. The Wave is paused and therefore we can't do fade/release effects
2056
*/
2057
if ( dwFlags & FACT_FLAG_STOP_IMMEDIATE ||
2058
pWave->state & FACT_STATE_PAUSED )
2059
{
2060
pWave->state |= FACT_STATE_STOPPED;
2061
pWave->state &= ~(
2062
FACT_STATE_PLAYING |
2063
FACT_STATE_STOPPING |
2064
FACT_STATE_PAUSED
2065
);
2066
FAudioSourceVoice_Stop(pWave->voice, 0, 0);
2067
FAudioSourceVoice_FlushSourceBuffers(pWave->voice);
2068
}
2069
else
2070
{
2071
pWave->state |= FACT_STATE_STOPPING;
2072
FAudioSourceVoice_ExitLoop(pWave->voice, 0);
2073
}
2074
2075
notification.pCue = pWave->parentCue;
2076
if (pWave->parentCue != NULL)
2077
{
2078
notification.cueIndex = pWave->parentCue->index;
2079
notification.pSoundBank = pWave->parentCue->parentBank;
2080
}
2081
else
2082
{
2083
notification.cueIndex = FACTINDEX_INVALID;
2084
notification.pSoundBank = NULL;
2085
}
2086
notification.pWave = pWave;
2087
notification.pWaveBank = pWave->parentBank;
2088
send_wave_notification(pWave->parentBank->parentEngine, FACTNOTIFICATIONTYPE_WAVESTOP, &notification);
2089
2090
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2091
return FAUDIO_OK;
2092
}
2093
2094
uint32_t FACTWave_Pause(FACTWave *pWave, int32_t fPause)
2095
{
2096
if (pWave == NULL)
2097
{
2098
return 1;
2099
}
2100
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2101
2102
/* FIXME: Does the Cue STOPPING/STOPPED rule apply here too? */
2103
if (pWave->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))
2104
{
2105
FAudio_PlatformUnlockMutex(
2106
pWave->parentBank->parentEngine->apiLock
2107
);
2108
return FAUDIO_OK;
2109
}
2110
2111
if (fPause)
2112
{
2113
pWave->state |= FACT_STATE_PAUSED;
2114
FAudioSourceVoice_Stop(pWave->voice, 0, 0);
2115
}
2116
else
2117
{
2118
pWave->state &= ~FACT_STATE_PAUSED;
2119
FAudioSourceVoice_Start(pWave->voice, 0, 0);
2120
}
2121
2122
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2123
return FAUDIO_OK;
2124
}
2125
2126
uint32_t FACTWave_GetState(FACTWave *pWave, uint32_t *pdwState)
2127
{
2128
if (pWave == NULL)
2129
{
2130
*pdwState = 0;
2131
return 1;
2132
}
2133
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2134
*pdwState = pWave->state;
2135
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2136
return FAUDIO_OK;
2137
}
2138
2139
uint32_t FACTWave_SetPitch(FACTWave *pWave, int16_t pitch)
2140
{
2141
if (pWave == NULL)
2142
{
2143
return 1;
2144
}
2145
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2146
pWave->pitch = FAudio_clamp(
2147
pitch,
2148
FACTPITCH_MIN_TOTAL,
2149
FACTPITCH_MAX_TOTAL
2150
);
2151
FAudioSourceVoice_SetFrequencyRatio(
2152
pWave->voice,
2153
(float) FAudio_pow(2.0, pWave->pitch / 1200.0),
2154
0
2155
);
2156
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2157
return FAUDIO_OK;
2158
}
2159
2160
uint32_t FACTWave_SetVolume(FACTWave *pWave, float volume)
2161
{
2162
if (pWave == NULL)
2163
{
2164
return 1;
2165
}
2166
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2167
pWave->volume = FAudio_clamp(
2168
volume,
2169
FACTVOLUME_MIN,
2170
FACTVOLUME_MAX
2171
);
2172
FAudioVoice_SetVolume(
2173
pWave->voice,
2174
pWave->volume,
2175
0
2176
);
2177
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2178
return FAUDIO_OK;
2179
}
2180
2181
uint32_t FACTWave_SetMatrixCoefficients(
2182
FACTWave *pWave,
2183
uint32_t uSrcChannelCount,
2184
uint32_t uDstChannelCount,
2185
float *pMatrixCoefficients
2186
) {
2187
uint32_t i;
2188
float *mtxDst, *mtxSrc, *mtxTmp = NULL;
2189
if (pWave == NULL)
2190
{
2191
return 1;
2192
}
2193
2194
/* There seems to be this weird feature in XACT where the channel count
2195
* can be completely wrong and it'll go to the right place.
2196
* I guess these XACT functions do some extra work to merge coefficients
2197
* but I have no idea where it really happens and XAudio2 definitely
2198
* does NOT like it when this is wrong, so here it goes...
2199
* -flibit
2200
*/
2201
if (uSrcChannelCount == 1 && pWave->srcChannels == 2)
2202
{
2203
mtxTmp = (float*) FAudio_alloca(
2204
sizeof(float) *
2205
pWave->srcChannels *
2206
uDstChannelCount
2207
);
2208
mtxDst = mtxTmp;
2209
mtxSrc = pMatrixCoefficients;
2210
for (i = 0; i < uDstChannelCount; i += 1)
2211
{
2212
mtxDst[0] = *mtxSrc;
2213
mtxDst[1] = *mtxSrc;
2214
mtxDst += 2;
2215
mtxSrc += 1;
2216
}
2217
uSrcChannelCount = 2;
2218
pMatrixCoefficients = mtxTmp;
2219
}
2220
else if (uSrcChannelCount == 2 && pWave->srcChannels == 1)
2221
{
2222
mtxTmp = (float*) FAudio_alloca(
2223
sizeof(float) *
2224
pWave->srcChannels *
2225
uDstChannelCount
2226
);
2227
mtxDst = mtxTmp;
2228
mtxSrc = pMatrixCoefficients;
2229
for (i = 0; i < uDstChannelCount; i += 1)
2230
{
2231
*mtxDst = (mtxSrc[0] + mtxSrc[1]) / 2.0f;
2232
mtxDst += 1;
2233
mtxSrc += 2;
2234
}
2235
uSrcChannelCount = 1;
2236
pMatrixCoefficients = mtxTmp;
2237
}
2238
2239
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2240
2241
FAudioVoice_SetOutputMatrix(
2242
pWave->voice,
2243
pWave->voice->sends.pSends->pOutputVoice,
2244
uSrcChannelCount,
2245
uDstChannelCount,
2246
pMatrixCoefficients,
2247
0
2248
);
2249
2250
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2251
if (mtxTmp != NULL)
2252
{
2253
FAudio_dealloca(mtxTmp);
2254
}
2255
return FAUDIO_OK;
2256
}
2257
2258
uint32_t FACTWave_GetProperties(
2259
FACTWave *pWave,
2260
FACTWaveInstanceProperties *pProperties
2261
) {
2262
if (pWave == NULL)
2263
{
2264
return 1;
2265
}
2266
FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
2267
2268
FACTWaveBank_GetWaveProperties(
2269
pWave->parentBank,
2270
pWave->index,
2271
&pProperties->properties
2272
);
2273
2274
pProperties->backgroundMusic = pWave->background_music;
2275
2276
FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
2277
return FAUDIO_OK;
2278
}
2279
2280
/* Cue implementation */
2281
2282
uint32_t FACTCue_Destroy(FACTCue *pCue)
2283
{
2284
FACTCue *cue, *prev;
2285
FAudioMutex mutex;
2286
if (pCue == NULL)
2287
{
2288
return 1;
2289
}
2290
2291
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2292
2293
/* Stop before we start deleting everything */
2294
FACTCue_Stop(pCue, FACT_FLAG_STOP_IMMEDIATE);
2295
2296
/* Remove this Cue from the SoundBank list */
2297
cue = pCue->parentBank->cueList;
2298
prev = cue;
2299
while (cue != NULL)
2300
{
2301
if (cue == pCue)
2302
{
2303
if (cue == prev) /* First in list */
2304
{
2305
pCue->parentBank->cueList = cue->next;
2306
}
2307
else
2308
{
2309
prev->next = cue->next;
2310
}
2311
break;
2312
}
2313
prev = cue;
2314
cue = cue->next;
2315
}
2316
FAudio_assert(cue != NULL && "Could not find Cue reference!");
2317
2318
pCue->parentBank->parentEngine->pFree(pCue->variableValues);
2319
FACT_INTERNAL_SendCueNotification(pCue, FACTNOTIFICATIONTYPE_CUEDESTROYED);
2320
2321
mutex = pCue->parentBank->parentEngine->apiLock;
2322
pCue->parentBank->parentEngine->pFree(pCue);
2323
FAudio_PlatformUnlockMutex(mutex);
2324
return FAUDIO_OK;
2325
}
2326
2327
uint32_t FACTCue_Play(FACTCue *pCue)
2328
{
2329
if (pCue == NULL)
2330
{
2331
return 1;
2332
}
2333
2334
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2335
2336
if (pCue->state & (FACT_STATE_PLAYING | FACT_STATE_STOPPING | FACT_STATE_STOPPED))
2337
{
2338
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2339
return FACTENGINE_E_INVALIDUSAGE;
2340
}
2341
2342
if (!play_sound(pCue))
2343
{
2344
FAudio_PlatformUnlockMutex(
2345
pCue->parentBank->parentEngine->apiLock
2346
);
2347
return FACTENGINE_E_INSTANCELIMITFAILTOPLAY;
2348
}
2349
2350
pCue->state |= FACT_STATE_PLAYING;
2351
pCue->state &= ~FACT_STATE_PREPARED;
2352
2353
FACT_INTERNAL_SendCueNotification(pCue, FACTNOTIFICATIONTYPE_CUEPLAY);
2354
2355
pCue->start = FAudio_timems();
2356
2357
/* If it's a simple wave, just play it! */
2358
if (pCue->simpleWave != NULL)
2359
{
2360
if (pCue->active3D)
2361
{
2362
FACTWave_SetMatrixCoefficients(
2363
pCue->simpleWave,
2364
pCue->srcChannels,
2365
pCue->dstChannels,
2366
pCue->matrixCoefficients
2367
);
2368
}
2369
FACTWave_Play(pCue->simpleWave);
2370
}
2371
2372
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2373
return FAUDIO_OK;
2374
}
2375
2376
uint32_t FACTCue_Stop(FACTCue *pCue, uint32_t dwFlags)
2377
{
2378
if (pCue == NULL)
2379
{
2380
return 1;
2381
}
2382
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2383
2384
/* If we're already stopped, there's nothing to do... */
2385
if (pCue->state & FACT_STATE_STOPPED)
2386
{
2387
FAudio_PlatformUnlockMutex(
2388
pCue->parentBank->parentEngine->apiLock
2389
);
2390
return FAUDIO_OK;
2391
}
2392
2393
/* If we're stopping and we haven't asked for IMMEDIATE, we're already
2394
* doing what the application is asking us to do...
2395
*/
2396
if ( (pCue->state & FACT_STATE_STOPPING) &&
2397
!(dwFlags & FACT_FLAG_STOP_IMMEDIATE) )
2398
{
2399
FAudio_PlatformUnlockMutex(
2400
pCue->parentBank->parentEngine->apiLock
2401
);
2402
return FAUDIO_OK;
2403
}
2404
2405
/* There are three ways that a Cue might be stopped immediately:
2406
* 1. The program explicitly asks for it
2407
* 2. The Cue is paused and therefore we can't do fade/release effects
2408
* 3. The Cue is stopped "as authored" and has no fade effects
2409
*/
2410
if ( dwFlags & FACT_FLAG_STOP_IMMEDIATE ||
2411
pCue->state & FACT_STATE_PAUSED ||
2412
pCue->playingSound == NULL ||
2413
( pCue->parentBank->cues[pCue->index].fadeOutMS == 0 &&
2414
pCue->maxRpcReleaseTime == 0 ) )
2415
{
2416
pCue->start = 0;
2417
pCue->elapsed = 0;
2418
pCue->state |= FACT_STATE_STOPPED;
2419
pCue->state &= ~(
2420
FACT_STATE_PLAYING |
2421
FACT_STATE_STOPPING |
2422
FACT_STATE_PAUSED
2423
);
2424
2425
if (pCue->simpleWave != NULL)
2426
{
2427
FACTWave_Destroy(pCue->simpleWave);
2428
pCue->simpleWave = NULL;
2429
2430
pCue->data->instanceCount -= 1;
2431
}
2432
else if (pCue->playingSound != NULL)
2433
{
2434
FACT_INTERNAL_DestroySound(pCue->playingSound);
2435
}
2436
}
2437
else
2438
{
2439
if (pCue->parentBank->cues[pCue->index].fadeOutMS > 0)
2440
{
2441
FACT_INTERNAL_BeginFadeOut(
2442
pCue->playingSound,
2443
pCue->parentBank->cues[pCue->index].fadeOutMS
2444
);
2445
}
2446
else if (pCue->maxRpcReleaseTime > 0)
2447
{
2448
FACT_INTERNAL_BeginReleaseRPC(
2449
pCue->playingSound,
2450
pCue->maxRpcReleaseTime
2451
);
2452
}
2453
else
2454
{
2455
/* Pretty sure this doesn't happen, but just in case? */
2456
pCue->state |= FACT_STATE_STOPPING;
2457
}
2458
}
2459
2460
FACT_INTERNAL_SendCueNotification(pCue, FACTNOTIFICATIONTYPE_CUESTOP);
2461
2462
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2463
return FAUDIO_OK;
2464
}
2465
2466
uint32_t FACTCue_GetState(FACTCue *pCue, uint32_t *pdwState)
2467
{
2468
if (pCue == NULL)
2469
{
2470
*pdwState = 0;
2471
return 1;
2472
}
2473
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2474
*pdwState = pCue->state;
2475
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2476
return FAUDIO_OK;
2477
}
2478
2479
uint32_t FACTCue_SetMatrixCoefficients(
2480
FACTCue *pCue,
2481
uint32_t uSrcChannelCount,
2482
uint32_t uDstChannelCount,
2483
float *pMatrixCoefficients
2484
) {
2485
uint8_t i;
2486
2487
if (uSrcChannelCount < 1 || uSrcChannelCount > 8)
2488
return FAUDIO_E_FAIL;
2489
if (uDstChannelCount != pCue->parentBank->parentEngine->output_format.Format.nChannels)
2490
return FAUDIO_E_INVALID_ARG;
2491
2492
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2493
2494
/* See FACTCue.matrixCoefficients declaration */
2495
FAudio_assert(uSrcChannelCount <= 2);
2496
2497
/* Local storage */
2498
pCue->srcChannels = uSrcChannelCount;
2499
pCue->dstChannels = uDstChannelCount;
2500
FAudio_memcpy(
2501
pCue->matrixCoefficients,
2502
pMatrixCoefficients,
2503
sizeof(float) * uSrcChannelCount * uDstChannelCount
2504
);
2505
pCue->active3D = true;
2506
2507
/* Apply to Waves if they exist */
2508
if (pCue->simpleWave != NULL)
2509
{
2510
FACTWave_SetMatrixCoefficients(
2511
pCue->simpleWave,
2512
uSrcChannelCount,
2513
uDstChannelCount,
2514
pMatrixCoefficients
2515
);
2516
}
2517
else if (pCue->playingSound != NULL)
2518
{
2519
for (i = 0; i < pCue->playingSound->sound->trackCount; i += 1)
2520
{
2521
if (pCue->playingSound->tracks[i].activeWave.wave != NULL)
2522
{
2523
FACTWave_SetMatrixCoefficients(
2524
pCue->playingSound->tracks[i].activeWave.wave,
2525
uSrcChannelCount,
2526
uDstChannelCount,
2527
pMatrixCoefficients
2528
);
2529
}
2530
}
2531
}
2532
2533
FACT_INTERNAL_SendCueNotification(pCue, FACTNOTIFICATIONTYPE_CUESTOP);
2534
2535
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2536
return FAUDIO_OK;
2537
}
2538
2539
uint16_t FACTCue_GetVariableIndex(
2540
FACTCue *pCue,
2541
const char *szFriendlyName
2542
) {
2543
FACTAudioEngine *engine = pCue->parentBank->parentEngine;
2544
2545
if (pCue == NULL)
2546
{
2547
return FACTVARIABLEINDEX_INVALID;
2548
}
2549
for (uint16_t i = 0; i < engine->variableCount; ++i)
2550
{
2551
if (!FAudio_strcmp(szFriendlyName, engine->variableNames[i]) &&
2552
(engine->variables[i].accessibility & ACCESSIBILITY_CUE) &&
2553
(engine->variables[i].accessibility & ACCESSIBILITY_PUBLIC))
2554
return i;
2555
}
2556
return FACTVARIABLEINDEX_INVALID;
2557
}
2558
2559
uint32_t FACTCue_SetVariable(
2560
FACTCue *pCue,
2561
uint16_t nIndex,
2562
float nValue
2563
) {
2564
FACTVariable *var;
2565
if (pCue == NULL)
2566
{
2567
return 1;
2568
}
2569
2570
if (nIndex >= pCue->parentBank->parentEngine->variableCount)
2571
return FACTENGINE_E_INVALIDVARIABLEINDEX;
2572
var = &pCue->parentBank->parentEngine->variables[nIndex];
2573
if (!(var->accessibility & ACCESSIBILITY_PUBLIC) || !(var->accessibility & ACCESSIBILITY_CUE)
2574
|| (var->accessibility & ACCESSIBILITY_READONLY))
2575
return FACTENGINE_E_INVALIDVARIABLEINDEX;
2576
2577
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2578
2579
pCue->variableValues[nIndex] = FAudio_clamp(
2580
nValue,
2581
var->minValue,
2582
var->maxValue
2583
);
2584
2585
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2586
return FAUDIO_OK;
2587
}
2588
2589
uint32_t FACTCue_GetVariable(
2590
FACTCue *pCue,
2591
uint16_t nIndex,
2592
float *nValue
2593
) {
2594
FACTVariable *var;
2595
if (pCue == NULL)
2596
{
2597
*nValue = 0.0f;
2598
return 1;
2599
}
2600
2601
if (nIndex >= pCue->parentBank->parentEngine->variableCount)
2602
return FACTENGINE_E_INVALIDVARIABLEINDEX;
2603
var = &pCue->parentBank->parentEngine->variables[nIndex];
2604
if (!(var->accessibility & ACCESSIBILITY_PUBLIC) || !(var->accessibility & ACCESSIBILITY_CUE))
2605
return FACTENGINE_E_INVALIDVARIABLEINDEX;
2606
2607
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2608
2609
if (nIndex == 0) /* NumCueInstances */
2610
{
2611
*nValue = pCue->parentBank->cues[pCue->index].instanceCount;
2612
}
2613
else
2614
{
2615
*nValue = pCue->variableValues[nIndex];
2616
}
2617
2618
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2619
return FAUDIO_OK;
2620
}
2621
2622
uint32_t FACTCue_Pause(FACTCue *pCue, int32_t fPause)
2623
{
2624
uint8_t i;
2625
if (pCue == NULL)
2626
{
2627
return 1;
2628
}
2629
2630
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2631
2632
/* "A stopping or stopped cue cannot be paused." */
2633
if (pCue->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))
2634
{
2635
FAudio_PlatformUnlockMutex(
2636
pCue->parentBank->parentEngine->apiLock
2637
);
2638
return FAUDIO_OK;
2639
}
2640
2641
/* Store elapsed time */
2642
pCue->elapsed += FAudio_timems() - pCue->start;
2643
2644
/* All we do is set the flag, not much to see here */
2645
if (fPause)
2646
{
2647
pCue->state |= FACT_STATE_PAUSED;
2648
}
2649
else
2650
{
2651
pCue->state &= ~FACT_STATE_PAUSED;
2652
}
2653
2654
/* Pause the Waves */
2655
if (pCue->simpleWave != NULL)
2656
{
2657
FACTWave_Pause(pCue->simpleWave, fPause);
2658
}
2659
else if (pCue->playingSound != NULL)
2660
{
2661
for (i = 0; i < pCue->playingSound->sound->trackCount; i += 1)
2662
{
2663
if (pCue->playingSound->tracks[i].activeWave.wave != NULL)
2664
{
2665
FACTWave_Pause(
2666
pCue->playingSound->tracks[i].activeWave.wave,
2667
fPause
2668
);
2669
}
2670
}
2671
}
2672
2673
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2674
return FAUDIO_OK;
2675
}
2676
2677
uint32_t FACTCue_GetProperties(
2678
FACTCue *pCue,
2679
FACTCueInstanceProperties **ppProperties
2680
) {
2681
uint32_t i;
2682
size_t allocSize;
2683
FACTCueInstanceProperties *cueProps;
2684
FACTVariationProperties *varProps;
2685
FACTSoundProperties *sndProps;
2686
FACTWaveInstanceProperties waveProps;
2687
if (pCue == NULL)
2688
{
2689
return 1;
2690
}
2691
2692
FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
2693
2694
/* Alloc container (including variable length array space) */
2695
allocSize = sizeof(FACTCueInstanceProperties);
2696
if (pCue->playingSound != NULL)
2697
{
2698
allocSize += (
2699
sizeof(FACTTrackProperties) *
2700
pCue->playingSound->sound->trackCount
2701
);
2702
}
2703
cueProps = (FACTCueInstanceProperties*) pCue->parentBank->parentEngine->pMalloc(
2704
allocSize
2705
);
2706
FAudio_zero(cueProps, allocSize);
2707
2708
/* Cue Properties */
2709
FACTSoundBank_GetCueProperties(
2710
pCue->parentBank,
2711
pCue->index,
2712
&cueProps->cueProperties
2713
);
2714
2715
/* Variation Properties */
2716
varProps = &cueProps->activeVariationProperties.variationProperties;
2717
if (pCue->playingSound)
2718
{
2719
if (pCue->data->flags & CUE_FLAG_SINGLE_SOUND)
2720
{
2721
varProps->weight = 0xff;
2722
}
2723
else
2724
{
2725
const FACTVariation *variation = &pCue->variation->entries[pCue->playingSound->variation_index];
2726
2727
varProps->index = pCue->playingSound->variation_index;
2728
2729
if (pCue->variation->type == VARIATION_TABLE_TYPE_INTERACTIVE)
2730
{
2731
varProps->iaVariableMin = variation->interactive.var_min;
2732
varProps->iaVariableMax = variation->interactive.var_max;
2733
varProps->weight = 0xff;
2734
}
2735
else
2736
{
2737
varProps->weight = variation->noninteractive.weight_max;
2738
}
2739
2740
varProps->linger = variation->linger;
2741
}
2742
}
2743
2744
/* Sound Properties */
2745
sndProps = &cueProps->activeVariationProperties.soundProperties;
2746
if (pCue->playingSound != NULL)
2747
{
2748
sndProps->category = pCue->playingSound->sound->category;
2749
sndProps->priority = pCue->playingSound->sound->priority;
2750
sndProps->pitch = pCue->playingSound->sound->pitch;
2751
sndProps->volume = pCue->playingSound->sound->volume;
2752
sndProps->numTracks = pCue->playingSound->sound->trackCount;
2753
2754
for (i = 0; i < sndProps->numTracks; i += 1)
2755
{
2756
FACTTrackProperties *track_props = &sndProps->arrTrackProperties[i];
2757
FACTTrackInstance *track = &pCue->playingSound->tracks[i];
2758
2759
FAudio_assert(track->activeWave.wave);
2760
FACTWave_GetProperties(track->activeWave.wave, &waveProps);
2761
2762
track_props->duration = (waveProps.properties.durationInSamples * 1000)
2763
/ waveProps.properties.format.nSamplesPerSec;
2764
track_props->numChannels = waveProps.properties.format.nChannels;
2765
if (track->waveEvt->wave.isComplex)
2766
{
2767
track_props->numVariations = track->waveEvt->wave.complex.wave_count;
2768
track_props->waveVariation = track->waveEvtInst->valuei;
2769
}
2770
else
2771
{
2772
track_props->numVariations = 1;
2773
track_props->waveVariation = 0;
2774
}
2775
track_props->loopCount = pCue->playingSound->tracks[i].waveEvt->wave.loopCount;
2776
/* Native doesn't take variation into account here. */
2777
track_props->duration *= (track_props->loopCount + 1);
2778
}
2779
}
2780
2781
FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
2782
2783
*ppProperties = cueProps;
2784
return FAUDIO_OK;
2785
}
2786
2787
uint32_t FACTCue_SetOutputVoices(
2788
FACTCue *pCue,
2789
const FAudioVoiceSends *pSendList /* Optional XAUDIO2_VOICE_SENDS */
2790
) {
2791
/* TODO */
2792
return FAUDIO_OK;
2793
}
2794
2795
uint32_t FACTCue_SetOutputVoiceMatrix(
2796
FACTCue *pCue,
2797
const FAudioVoice *pDestinationVoice, /* Optional! */
2798
uint32_t SourceChannels,
2799
uint32_t DestinationChannels,
2800
const float *pLevelMatrix /* SourceChannels * DestinationChannels */
2801
) {
2802
/* TODO */
2803
return FAUDIO_OK;
2804
}
2805
2806
/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
2807
2808