Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/faudio/src/FAudio.c
8672 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 "FAudio_internal.h"
28
29
#define MAKE_SUBFORMAT_GUID(guid, fmt) \
30
FAudioGUID DATAFORMAT_SUBTYPE_##guid = \
31
{ \
32
(uint16_t) (fmt), \
33
0x0000, \
34
0x0010, \
35
{ \
36
0x80, \
37
0x00, \
38
0x00, \
39
0xAA, \
40
0x00, \
41
0x38, \
42
0x9B, \
43
0x71 \
44
} \
45
}
46
MAKE_SUBFORMAT_GUID(PCM, 1);
47
MAKE_SUBFORMAT_GUID(ADPCM, 2);
48
MAKE_SUBFORMAT_GUID(IEEE_FLOAT, 3);
49
MAKE_SUBFORMAT_GUID(XMAUDIO2, FAUDIO_FORMAT_XMAUDIO2);
50
MAKE_SUBFORMAT_GUID(WMAUDIO2, FAUDIO_FORMAT_WMAUDIO2);
51
MAKE_SUBFORMAT_GUID(WMAUDIO3, FAUDIO_FORMAT_WMAUDIO3);
52
MAKE_SUBFORMAT_GUID(WMAUDIO_LOSSLESS, FAUDIO_FORMAT_WMAUDIO_LOSSLESS);
53
#undef MAKE_SUBFORMAT_GUID
54
55
#ifdef FAUDIO_DUMP_VOICES
56
static void FAudio_DUMPVOICE_Init(const FAudioSourceVoice *voice);
57
static void FAudio_DUMPVOICE_Finalize(const FAudioSourceVoice *voice);
58
static void FAudio_DUMPVOICE_WriteBuffer(
59
const FAudioSourceVoice *voice,
60
const FAudioBuffer *pBuffer,
61
const FAudioBufferWMA *pBufferWMA,
62
const uint32_t playBegin,
63
const uint32_t playLength
64
);
65
#endif /* FAUDIO_DUMP_VOICES */
66
67
/* FAudio Version */
68
69
uint32_t FAudioLinkedVersion(void)
70
{
71
return FAUDIO_COMPILED_VERSION;
72
}
73
74
/* FAudio Interface */
75
76
uint32_t FAudioCreate(
77
FAudio **ppFAudio,
78
uint32_t Flags,
79
FAudioProcessor XAudio2Processor
80
) {
81
FAudioCOMConstructEXT(ppFAudio, FAUDIO_TARGET_VERSION);
82
FAudio_Initialize(*ppFAudio, Flags, XAudio2Processor);
83
return 0;
84
}
85
86
uint32_t FAudioCOMConstructEXT(FAudio **ppFAudio, uint8_t version)
87
{
88
return FAudioCOMConstructWithCustomAllocatorEXT(
89
ppFAudio,
90
version,
91
FAudio_malloc,
92
FAudio_free,
93
FAudio_realloc
94
);
95
}
96
97
uint32_t FAudioCreateWithCustomAllocatorEXT(
98
FAudio **ppFAudio,
99
uint32_t Flags,
100
FAudioProcessor XAudio2Processor,
101
FAudioMallocFunc customMalloc,
102
FAudioFreeFunc customFree,
103
FAudioReallocFunc customRealloc
104
) {
105
FAudioCOMConstructWithCustomAllocatorEXT(
106
ppFAudio,
107
FAUDIO_TARGET_VERSION,
108
customMalloc,
109
customFree,
110
customRealloc
111
);
112
FAudio_Initialize(*ppFAudio, Flags, XAudio2Processor);
113
return 0;
114
}
115
116
uint32_t FAudioCOMConstructWithCustomAllocatorEXT(
117
FAudio **ppFAudio,
118
uint8_t version,
119
FAudioMallocFunc customMalloc,
120
FAudioFreeFunc customFree,
121
FAudioReallocFunc customRealloc
122
) {
123
#ifndef FAUDIO_DISABLE_DEBUGCONFIGURATION
124
FAudioDebugConfiguration debugInit = {0};
125
#endif /* FAUDIO_DISABLE_DEBUGCONFIGURATION */
126
FAudio_PlatformAddRef();
127
*ppFAudio = (FAudio*) customMalloc(sizeof(FAudio));
128
FAudio_zero(*ppFAudio, sizeof(FAudio));
129
(*ppFAudio)->version = version;
130
#ifndef FAUDIO_DISABLE_DEBUGCONFIGURATION
131
FAudio_SetDebugConfiguration(*ppFAudio, &debugInit, NULL);
132
#endif /* FAUDIO_DISABLE_DEBUGCONFIGURATION */
133
(*ppFAudio)->sourceLock = FAudio_PlatformCreateMutex();
134
LOG_MUTEX_CREATE((*ppFAudio), (*ppFAudio)->sourceLock)
135
(*ppFAudio)->submixLock = FAudio_PlatformCreateMutex();
136
LOG_MUTEX_CREATE((*ppFAudio), (*ppFAudio)->submixLock)
137
(*ppFAudio)->callbackLock = FAudio_PlatformCreateMutex();
138
LOG_MUTEX_CREATE((*ppFAudio), (*ppFAudio)->callbackLock)
139
(*ppFAudio)->operationLock = FAudio_PlatformCreateMutex();
140
LOG_MUTEX_CREATE((*ppFAudio), (*ppFAudio)->operationLock)
141
(*ppFAudio)->pMalloc = customMalloc;
142
(*ppFAudio)->pFree = customFree;
143
(*ppFAudio)->pRealloc = customRealloc;
144
(*ppFAudio)->refcount = 1;
145
return 0;
146
}
147
148
uint32_t FAudio_AddRef(FAudio *audio)
149
{
150
LOG_API_ENTER(audio)
151
audio->refcount += 1;
152
LOG_API_EXIT(audio)
153
return audio->refcount;
154
}
155
156
static void destroy_voice(FAudioVoice *voice);
157
158
uint32_t FAudio_Release(FAudio *audio)
159
{
160
uint32_t refcount;
161
FAudioVoice *voice;
162
163
LOG_API_ENTER(audio)
164
audio->refcount -= 1;
165
refcount = audio->refcount;
166
if (audio->refcount == 0)
167
{
168
while (audio->sources)
169
{
170
voice = (FAudioSourceVoice*) audio->sources->entry;
171
destroy_voice(voice);
172
}
173
while (audio->submixes)
174
{
175
voice = (FAudioSourceVoice*) audio->submixes->entry;
176
destroy_voice(voice);
177
}
178
if (audio->master)
179
destroy_voice(audio->master);
180
FAudio_OPERATIONSET_ClearAll(audio);
181
FAudio_StopEngine(audio);
182
audio->pFree(audio->decodeCache);
183
audio->pFree(audio->resampleCache);
184
audio->pFree(audio->effectChainCache);
185
LOG_MUTEX_DESTROY(audio, audio->sourceLock)
186
FAudio_PlatformDestroyMutex(audio->sourceLock);
187
LOG_MUTEX_DESTROY(audio, audio->submixLock)
188
FAudio_PlatformDestroyMutex(audio->submixLock);
189
LOG_MUTEX_DESTROY(audio, audio->callbackLock)
190
FAudio_PlatformDestroyMutex(audio->callbackLock);
191
LOG_MUTEX_DESTROY(audio, audio->operationLock)
192
FAudio_PlatformDestroyMutex(audio->operationLock);
193
audio->pFree(audio);
194
FAudio_PlatformRelease();
195
}
196
else
197
{
198
LOG_API_EXIT(audio)
199
}
200
return refcount;
201
}
202
203
uint32_t FAudio_GetDeviceCount(FAudio *audio, uint32_t *pCount)
204
{
205
LOG_API_ENTER(audio)
206
*pCount = FAudio_PlatformGetDeviceCount();
207
LOG_API_EXIT(audio)
208
return 0;
209
}
210
211
uint32_t FAudio_GetDeviceDetails(
212
FAudio *audio,
213
uint32_t Index,
214
FAudioDeviceDetails *pDeviceDetails
215
) {
216
uint32_t result;
217
LOG_API_ENTER(audio)
218
result = FAudio_PlatformGetDeviceDetails(Index, pDeviceDetails);
219
LOG_API_EXIT(audio)
220
return result;
221
}
222
223
uint32_t FAudio_Initialize(
224
FAudio *audio,
225
uint32_t Flags,
226
FAudioProcessor XAudio2Processor
227
) {
228
LOG_API_ENTER(audio)
229
FAudio_assert((Flags & ~(FAUDIO_DEBUG_ENGINE | FAUDIO_1024_QUANTUM)) == 0);
230
FAudio_assert(XAudio2Processor == FAUDIO_DEFAULT_PROCESSOR);
231
232
audio->initFlags = Flags;
233
234
/* FIXME: This is lazy... */
235
audio->decodeCache = (float*) audio->pMalloc(sizeof(float));
236
audio->resampleCache = (float*) audio->pMalloc(sizeof(float));
237
audio->decodeSamples = 1;
238
audio->resampleSamples = 1;
239
240
FAudio_StartEngine(audio);
241
LOG_API_EXIT(audio)
242
return 0;
243
}
244
245
uint32_t FAudio_RegisterForCallbacks(
246
FAudio *audio,
247
FAudioEngineCallback *pCallback
248
) {
249
LOG_API_ENTER(audio)
250
LinkedList_AddEntry(
251
&audio->callbacks,
252
pCallback,
253
audio->callbackLock,
254
audio->pMalloc
255
);
256
LOG_API_EXIT(audio)
257
return 0;
258
}
259
260
void FAudio_UnregisterForCallbacks(
261
FAudio *audio,
262
FAudioEngineCallback *pCallback
263
) {
264
LOG_API_ENTER(audio)
265
LinkedList_RemoveEntry(
266
&audio->callbacks,
267
pCallback,
268
audio->callbackLock,
269
audio->pFree
270
);
271
LOG_API_EXIT(audio)
272
}
273
274
uint32_t FAudio_CreateSourceVoice(
275
FAudio *audio,
276
FAudioSourceVoice **ppSourceVoice,
277
const FAudioWaveFormatEx *pSourceFormat,
278
uint32_t Flags,
279
float MaxFrequencyRatio,
280
FAudioVoiceCallback *pCallback,
281
const FAudioVoiceSends *pSendList,
282
const FAudioEffectChain *pEffectChain
283
) {
284
uint32_t i;
285
286
LOG_API_ENTER(audio)
287
LOG_FORMAT(audio, pSourceFormat)
288
289
*ppSourceVoice = (FAudioSourceVoice*) audio->pMalloc(sizeof(FAudioVoice));
290
FAudio_zero(*ppSourceVoice, sizeof(FAudioSourceVoice));
291
(*ppSourceVoice)->audio = audio;
292
(*ppSourceVoice)->type = FAUDIO_VOICE_SOURCE;
293
(*ppSourceVoice)->flags = Flags;
294
(*ppSourceVoice)->filter.Type = FAUDIO_DEFAULT_FILTER_TYPE;
295
(*ppSourceVoice)->filter.Frequency = FAUDIO_DEFAULT_FILTER_FREQUENCY;
296
(*ppSourceVoice)->filter.OneOverQ = FAUDIO_DEFAULT_FILTER_ONEOVERQ;
297
(*ppSourceVoice)->filter.WetDryMix = FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT;
298
(*ppSourceVoice)->sendLock = FAudio_PlatformCreateMutex();
299
LOG_MUTEX_CREATE(audio, (*ppSourceVoice)->sendLock)
300
(*ppSourceVoice)->effectLock = FAudio_PlatformCreateMutex();
301
LOG_MUTEX_CREATE(audio, (*ppSourceVoice)->effectLock)
302
(*ppSourceVoice)->filterLock = FAudio_PlatformCreateMutex();
303
LOG_MUTEX_CREATE(audio, (*ppSourceVoice)->filterLock)
304
(*ppSourceVoice)->volumeLock = FAudio_PlatformCreateMutex();
305
LOG_MUTEX_CREATE(audio, (*ppSourceVoice)->volumeLock)
306
307
/* Source Properties */
308
FAudio_assert(MaxFrequencyRatio <= FAUDIO_MAX_FREQ_RATIO);
309
(*ppSourceVoice)->src.maxFreqRatio = MaxFrequencyRatio;
310
311
if ( pSourceFormat->wFormatTag == FAUDIO_FORMAT_PCM ||
312
pSourceFormat->wFormatTag == FAUDIO_FORMAT_IEEE_FLOAT ||
313
pSourceFormat->wFormatTag == FAUDIO_FORMAT_WMAUDIO2 ||
314
pSourceFormat->wFormatTag == FAUDIO_FORMAT_WMAUDIO3 )
315
{
316
FAudioWaveFormatExtensible *fmtex = (FAudioWaveFormatExtensible*) audio->pMalloc(
317
sizeof(FAudioWaveFormatExtensible)
318
);
319
/* convert PCM to EXTENSIBLE */
320
fmtex->Format.wFormatTag = FAUDIO_FORMAT_EXTENSIBLE;
321
fmtex->Format.nChannels = pSourceFormat->nChannels;
322
fmtex->Format.nSamplesPerSec = pSourceFormat->nSamplesPerSec;
323
fmtex->Format.nAvgBytesPerSec = pSourceFormat->nAvgBytesPerSec;
324
fmtex->Format.nBlockAlign = pSourceFormat->nBlockAlign;
325
fmtex->Format.wBitsPerSample = pSourceFormat->wBitsPerSample;
326
fmtex->Format.cbSize = sizeof(FAudioWaveFormatExtensible) - sizeof(FAudioWaveFormatEx);
327
fmtex->Samples.wValidBitsPerSample = pSourceFormat->wBitsPerSample;
328
fmtex->dwChannelMask = 0;
329
if (pSourceFormat->wFormatTag == FAUDIO_FORMAT_PCM)
330
{
331
FAudio_memcpy(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_PCM, sizeof(FAudioGUID));
332
}
333
else if (pSourceFormat->wFormatTag == FAUDIO_FORMAT_IEEE_FLOAT)
334
{
335
FAudio_memcpy(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(FAudioGUID));
336
}
337
else if (pSourceFormat->wFormatTag == FAUDIO_FORMAT_WMAUDIO2)
338
{
339
FAudio_memcpy(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_WMAUDIO2, sizeof(FAudioGUID));
340
}
341
else if (pSourceFormat->wFormatTag == FAUDIO_FORMAT_WMAUDIO3)
342
{
343
FAudio_memcpy(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_WMAUDIO3, sizeof(FAudioGUID));
344
}
345
(*ppSourceVoice)->src.format = &fmtex->Format;
346
}
347
else if (pSourceFormat->wFormatTag == FAUDIO_FORMAT_MSADPCM)
348
{
349
FAudioADPCMWaveFormat *fmtex = (FAudioADPCMWaveFormat*) audio->pMalloc(
350
sizeof(FAudioADPCMWaveFormat)
351
);
352
353
/* Copy what we can, ideally the sizes match! */
354
size_t cbSize = sizeof(FAudioWaveFormatEx) + pSourceFormat->cbSize;
355
FAudio_memcpy(
356
fmtex,
357
pSourceFormat,
358
FAudio_min(cbSize, sizeof(FAudioADPCMWaveFormat))
359
);
360
if (cbSize < sizeof(FAudioADPCMWaveFormat))
361
{
362
FAudio_zero(
363
((uint8_t*) fmtex) + cbSize,
364
sizeof(FAudioADPCMWaveFormat) - cbSize
365
);
366
}
367
368
/* XAudio2 does not validate this input! */
369
fmtex->wfx.cbSize = sizeof(FAudioADPCMWaveFormat) - sizeof(FAudioWaveFormatEx);
370
fmtex->wSamplesPerBlock = ((
371
fmtex->wfx.nBlockAlign / fmtex->wfx.nChannels
372
) - 6) * 2;
373
(*ppSourceVoice)->src.format = &fmtex->wfx;
374
}
375
else if (pSourceFormat->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
376
{
377
FAudioXMA2WaveFormat *fmtex = (FAudioXMA2WaveFormat*) audio->pMalloc(
378
sizeof(FAudioXMA2WaveFormat)
379
);
380
381
/* Copy what we can, ideally the sizes match! */
382
size_t cbSize = sizeof(FAudioWaveFormatEx) + pSourceFormat->cbSize;
383
FAudio_memcpy(
384
fmtex,
385
pSourceFormat,
386
FAudio_min(cbSize, sizeof(FAudioXMA2WaveFormat))
387
);
388
if (cbSize < sizeof(FAudioXMA2WaveFormat))
389
{
390
FAudio_zero(
391
((uint8_t*) fmtex) + cbSize,
392
sizeof(FAudioADPCMWaveFormat) - cbSize
393
);
394
}
395
396
/* Does XAudio2 validate this input?! */
397
fmtex->wfx.cbSize = sizeof(FAudioXMA2WaveFormat) - sizeof(FAudioWaveFormatEx);
398
(*ppSourceVoice)->src.format = &fmtex->wfx;
399
}
400
else
401
{
402
/* direct copy anything else */
403
(*ppSourceVoice)->src.format = (FAudioWaveFormatEx*) audio->pMalloc(
404
sizeof(FAudioWaveFormatEx) + pSourceFormat->cbSize
405
);
406
FAudio_memcpy(
407
(*ppSourceVoice)->src.format,
408
pSourceFormat,
409
sizeof(FAudioWaveFormatEx) + pSourceFormat->cbSize
410
);
411
}
412
413
(*ppSourceVoice)->src.callback = pCallback;
414
(*ppSourceVoice)->src.active = 0;
415
(*ppSourceVoice)->src.freqRatio = 1.0f;
416
(*ppSourceVoice)->src.totalSamples = 0;
417
(*ppSourceVoice)->src.bufferList = NULL;
418
(*ppSourceVoice)->src.flushList = NULL;
419
(*ppSourceVoice)->src.bufferLock = FAudio_PlatformCreateMutex();
420
LOG_MUTEX_CREATE(audio, (*ppSourceVoice)->src.bufferLock)
421
422
if ((*ppSourceVoice)->src.format->wFormatTag == FAUDIO_FORMAT_EXTENSIBLE)
423
{
424
FAudioWaveFormatExtensible *fmtex = (FAudioWaveFormatExtensible*) (*ppSourceVoice)->src.format;
425
426
#define COMPARE_GUID(type) \
427
(FAudio_memcmp( \
428
&fmtex->SubFormat, \
429
&DATAFORMAT_SUBTYPE_##type, \
430
sizeof(FAudioGUID) \
431
) == 0)
432
if (COMPARE_GUID(PCM))
433
{
434
#define DECODER(bit) \
435
if (fmtex->Format.wBitsPerSample == bit) \
436
{ \
437
(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodePCM##bit; \
438
}
439
DECODER(16)
440
else DECODER(8)
441
else DECODER(24)
442
else DECODER(32)
443
else
444
{
445
LOG_ERROR(
446
audio,
447
"Unrecognized wBitsPerSample: %d",
448
fmtex->Format.wBitsPerSample
449
)
450
FAudio_assert(0 && "Unrecognized wBitsPerSample!");
451
}
452
#undef DECODER
453
}
454
else if (COMPARE_GUID(IEEE_FLOAT))
455
{
456
/* FIXME: Weird behavior!
457
* Prototype creates a source with the IEEE_FLOAT tag,
458
* but it's actually PCM16. It seems to prioritize
459
* wBitsPerSample over the format tag. Not sure if we
460
* should fold this section into the section above...?
461
* -flibit
462
*/
463
if (fmtex->Format.wBitsPerSample == 16)
464
{
465
(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodePCM16;
466
}
467
else
468
{
469
(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodePCM32F;
470
}
471
}
472
else if ( COMPARE_GUID(WMAUDIO2) ||
473
COMPARE_GUID(WMAUDIO3) ||
474
COMPARE_GUID(WMAUDIO_LOSSLESS) )
475
{
476
#ifdef HAVE_WMADEC
477
if (FAudio_WMADEC_init(*ppSourceVoice, fmtex->SubFormat.Data1) != 0)
478
{
479
(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
480
}
481
#else
482
FAudio_assert(0 && "xWMA is not supported!");
483
(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
484
#endif /* HAVE_WMADEC */
485
}
486
else
487
{
488
FAudio_assert(0 && "Unsupported WAVEFORMATEXTENSIBLE subtype!");
489
}
490
#undef COMPARE_GUID
491
}
492
else if ((*ppSourceVoice)->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
493
{
494
#ifdef HAVE_WMADEC
495
if (FAudio_WMADEC_init(*ppSourceVoice, FAUDIO_FORMAT_XMAUDIO2) != 0)
496
{
497
(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
498
}
499
#else
500
FAudio_assert(0 && "XMA2 is not supported!");
501
(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
502
#endif /* HAVE_WMADEC */
503
}
504
else if ((*ppSourceVoice)->src.format->wFormatTag == FAUDIO_FORMAT_MSADPCM)
505
{
506
(*ppSourceVoice)->src.decode = ((*ppSourceVoice)->src.format->nChannels == 2) ?
507
FAudio_INTERNAL_DecodeStereoMSADPCM :
508
FAudio_INTERNAL_DecodeMonoMSADPCM;
509
}
510
else
511
{
512
FAudio_assert(0 && "Unsupported format tag!");
513
}
514
515
if ((*ppSourceVoice)->src.format->nChannels == 1)
516
{
517
(*ppSourceVoice)->src.resample = FAudio_INTERNAL_ResampleMono;
518
}
519
else if ((*ppSourceVoice)->src.format->nChannels == 2)
520
{
521
(*ppSourceVoice)->src.resample = FAudio_INTERNAL_ResampleStereo;
522
}
523
else
524
{
525
(*ppSourceVoice)->src.resample = FAudio_INTERNAL_ResampleGeneric;
526
}
527
528
(*ppSourceVoice)->src.curBufferOffset = 0;
529
530
/* Sends/Effects */
531
FAudio_INTERNAL_VoiceOutputFrequency(*ppSourceVoice, pSendList);
532
FAudioVoice_SetEffectChain(*ppSourceVoice, pEffectChain);
533
534
/* Default Levels */
535
(*ppSourceVoice)->volume = 1.0f;
536
(*ppSourceVoice)->channelVolume = (float*) audio->pMalloc(
537
sizeof(float) * (*ppSourceVoice)->outputChannels
538
);
539
for (i = 0; i < (*ppSourceVoice)->outputChannels; i += 1)
540
{
541
(*ppSourceVoice)->channelVolume[i] = 1.0f;
542
}
543
544
FAudioVoice_SetOutputVoices(*ppSourceVoice, pSendList);
545
546
/* Filters */
547
if (Flags & FAUDIO_VOICE_USEFILTER)
548
{
549
(*ppSourceVoice)->filterState = (FAudioFilterState*) audio->pMalloc(
550
sizeof(FAudioFilterState) * (*ppSourceVoice)->src.format->nChannels
551
);
552
FAudio_zero(
553
(*ppSourceVoice)->filterState,
554
sizeof(FAudioFilterState) * (*ppSourceVoice)->src.format->nChannels
555
);
556
}
557
558
/* Sample Storage */
559
(*ppSourceVoice)->src.decodeSamples = (uint32_t) (FAudio_ceil(
560
(double) audio->updateSize *
561
(double) MaxFrequencyRatio *
562
(double) (*ppSourceVoice)->src.format->nSamplesPerSec /
563
(double) audio->master->master.inputSampleRate
564
)) + EXTRA_DECODE_PADDING * (*ppSourceVoice)->src.format->nChannels;
565
FAudio_INTERNAL_ResizeDecodeCache(
566
audio,
567
((*ppSourceVoice)->src.decodeSamples + EXTRA_DECODE_PADDING) * (*ppSourceVoice)->src.format->nChannels
568
);
569
570
LOG_INFO(audio, "-> %p", (void*) (*ppSourceVoice))
571
572
/* Add to list, finally. */
573
LinkedList_PrependEntry(
574
&audio->sources,
575
*ppSourceVoice,
576
audio->sourceLock,
577
audio->pMalloc
578
);
579
580
#ifdef FAUDIO_DUMP_VOICES
581
FAudio_DUMPVOICE_Init(*ppSourceVoice);
582
#endif /* FAUDIO_DUMP_VOICES */
583
584
LOG_API_EXIT(audio)
585
return 0;
586
}
587
588
uint32_t FAudio_CreateSubmixVoice(
589
FAudio *audio,
590
FAudioSubmixVoice **ppSubmixVoice,
591
uint32_t InputChannels,
592
uint32_t InputSampleRate,
593
uint32_t Flags,
594
uint32_t ProcessingStage,
595
const FAudioVoiceSends *pSendList,
596
const FAudioEffectChain *pEffectChain
597
) {
598
uint32_t i;
599
600
LOG_API_ENTER(audio)
601
602
*ppSubmixVoice = (FAudioSubmixVoice*) audio->pMalloc(sizeof(FAudioVoice));
603
FAudio_zero(*ppSubmixVoice, sizeof(FAudioSubmixVoice));
604
(*ppSubmixVoice)->audio = audio;
605
(*ppSubmixVoice)->type = FAUDIO_VOICE_SUBMIX;
606
(*ppSubmixVoice)->flags = Flags;
607
(*ppSubmixVoice)->filter.Type = FAUDIO_DEFAULT_FILTER_TYPE;
608
(*ppSubmixVoice)->filter.Frequency = FAUDIO_DEFAULT_FILTER_FREQUENCY;
609
(*ppSubmixVoice)->filter.OneOverQ = FAUDIO_DEFAULT_FILTER_ONEOVERQ;
610
(*ppSubmixVoice)->filter.WetDryMix = FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT;
611
(*ppSubmixVoice)->sendLock = FAudio_PlatformCreateMutex();
612
LOG_MUTEX_CREATE(audio, (*ppSubmixVoice)->sendLock)
613
(*ppSubmixVoice)->effectLock = FAudio_PlatformCreateMutex();
614
LOG_MUTEX_CREATE(audio, (*ppSubmixVoice)->effectLock)
615
(*ppSubmixVoice)->filterLock = FAudio_PlatformCreateMutex();
616
LOG_MUTEX_CREATE(audio, (*ppSubmixVoice)->filterLock)
617
(*ppSubmixVoice)->volumeLock = FAudio_PlatformCreateMutex();
618
LOG_MUTEX_CREATE(audio, (*ppSubmixVoice)->volumeLock)
619
620
/* Submix Properties */
621
(*ppSubmixVoice)->mix.inputChannels = InputChannels;
622
(*ppSubmixVoice)->mix.inputSampleRate = InputSampleRate;
623
(*ppSubmixVoice)->mix.processingStage = ProcessingStage;
624
625
/* Resampler */
626
if (InputChannels == 1)
627
{
628
(*ppSubmixVoice)->mix.resample = FAudio_INTERNAL_ResampleMono;
629
}
630
else if (InputChannels == 2)
631
{
632
(*ppSubmixVoice)->mix.resample = FAudio_INTERNAL_ResampleStereo;
633
}
634
else
635
{
636
(*ppSubmixVoice)->mix.resample = FAudio_INTERNAL_ResampleGeneric;
637
}
638
639
/* Sample Storage */
640
(*ppSubmixVoice)->mix.inputSamples = ((uint32_t) FAudio_ceil(
641
audio->updateSize *
642
(double) InputSampleRate /
643
(double) audio->master->master.inputSampleRate
644
) + EXTRA_DECODE_PADDING) * InputChannels;
645
(*ppSubmixVoice)->mix.inputCache = (float*) audio->pMalloc(
646
sizeof(float) * (*ppSubmixVoice)->mix.inputSamples
647
);
648
FAudio_zero( /* Zero this now, for the first update */
649
(*ppSubmixVoice)->mix.inputCache,
650
sizeof(float) * (*ppSubmixVoice)->mix.inputSamples
651
);
652
653
/* Sends/Effects */
654
FAudio_INTERNAL_VoiceOutputFrequency(*ppSubmixVoice, pSendList);
655
FAudioVoice_SetEffectChain(*ppSubmixVoice, pEffectChain);
656
657
/* Default Levels */
658
(*ppSubmixVoice)->volume = 1.0f;
659
(*ppSubmixVoice)->channelVolume = (float*) audio->pMalloc(
660
sizeof(float) * (*ppSubmixVoice)->outputChannels
661
);
662
for (i = 0; i < (*ppSubmixVoice)->outputChannels; i += 1)
663
{
664
(*ppSubmixVoice)->channelVolume[i] = 1.0f;
665
}
666
667
FAudioVoice_SetOutputVoices(*ppSubmixVoice, pSendList);
668
669
/* Filters */
670
if (Flags & FAUDIO_VOICE_USEFILTER)
671
{
672
(*ppSubmixVoice)->filterState = (FAudioFilterState*) audio->pMalloc(
673
sizeof(FAudioFilterState) * InputChannels
674
);
675
FAudio_zero(
676
(*ppSubmixVoice)->filterState,
677
sizeof(FAudioFilterState) * InputChannels
678
);
679
}
680
681
/* Add to list, finally. */
682
FAudio_INTERNAL_InsertSubmixSorted(
683
&audio->submixes,
684
*ppSubmixVoice,
685
audio->submixLock,
686
audio->pMalloc
687
);
688
689
LOG_API_EXIT(audio)
690
return 0;
691
}
692
693
uint32_t FAudio_CreateMasteringVoice(
694
FAudio *audio,
695
FAudioMasteringVoice **ppMasteringVoice,
696
uint32_t InputChannels,
697
uint32_t InputSampleRate,
698
uint32_t Flags,
699
uint32_t DeviceIndex,
700
const FAudioEffectChain *pEffectChain
701
) {
702
LOG_API_ENTER(audio)
703
704
/* For now we only support one allocated master voice at a time */
705
FAudio_assert(audio->master == NULL);
706
707
if ( InputChannels == FAUDIO_DEFAULT_CHANNELS ||
708
InputSampleRate == FAUDIO_DEFAULT_SAMPLERATE )
709
{
710
FAudioDeviceDetails details;
711
if (FAudio_GetDeviceDetails(audio, DeviceIndex, &details) != 0)
712
{
713
return FAUDIO_E_INVALID_CALL;
714
}
715
if (InputChannels == FAUDIO_DEFAULT_CHANNELS)
716
{
717
InputChannels = details.OutputFormat.Format.nChannels;
718
}
719
if (InputSampleRate == FAUDIO_DEFAULT_SAMPLERATE)
720
{
721
InputSampleRate = details.OutputFormat.Format.nSamplesPerSec;
722
}
723
}
724
725
*ppMasteringVoice = (FAudioMasteringVoice*) audio->pMalloc(sizeof(FAudioVoice));
726
FAudio_zero(*ppMasteringVoice, sizeof(FAudioMasteringVoice));
727
(*ppMasteringVoice)->audio = audio;
728
(*ppMasteringVoice)->type = FAUDIO_VOICE_MASTER;
729
(*ppMasteringVoice)->flags = Flags;
730
(*ppMasteringVoice)->effectLock = FAudio_PlatformCreateMutex();
731
LOG_MUTEX_CREATE(audio, (*ppMasteringVoice)->effectLock)
732
(*ppMasteringVoice)->volumeLock = FAudio_PlatformCreateMutex();
733
LOG_MUTEX_CREATE(audio, (*ppMasteringVoice)->volumeLock)
734
735
/* Default Levels */
736
(*ppMasteringVoice)->volume = 1.0f;
737
738
/* Master Properties */
739
(*ppMasteringVoice)->master.inputChannels = InputChannels;
740
(*ppMasteringVoice)->master.inputSampleRate = InputSampleRate;
741
742
/* Sends/Effects */
743
FAudio_zero(&(*ppMasteringVoice)->sends, sizeof(FAudioVoiceSends));
744
FAudioVoice_SetEffectChain(*ppMasteringVoice, pEffectChain);
745
746
/* This is now safe enough to assign */
747
audio->master = *ppMasteringVoice;
748
749
/* Build the device format.
750
* The most unintuitive part of this is the use of outputChannels
751
* instead of master.inputChannels. Bizarrely, the effect chain can
752
* dictate the _actual_ output channel count, and when the channel count
753
* mismatches, we have to add a staging buffer for effects to process on
754
* before ultimately copying the final result to the device. ARGH.
755
*/
756
WriteWaveFormatExtensible(
757
&audio->mixFormat,
758
audio->master->outputChannels,
759
audio->master->master.inputSampleRate,
760
&DATAFORMAT_SUBTYPE_IEEE_FLOAT
761
);
762
763
/* Platform Device */
764
FAudio_PlatformInit(
765
audio,
766
audio->initFlags,
767
DeviceIndex,
768
&audio->mixFormat,
769
&audio->updateSize,
770
&audio->platform
771
);
772
if (audio->platform == NULL)
773
{
774
FAudioVoice_DestroyVoice(*ppMasteringVoice);
775
*ppMasteringVoice = NULL;
776
777
/* Not the best code, but it's probably true? */
778
return FAUDIO_E_DEVICE_INVALIDATED;
779
}
780
audio->master->outputChannels = audio->mixFormat.Format.nChannels;
781
audio->master->master.inputSampleRate = audio->mixFormat.Format.nSamplesPerSec;
782
783
/* Effect Chain Cache */
784
if ((*ppMasteringVoice)->master.inputChannels != (*ppMasteringVoice)->outputChannels)
785
{
786
(*ppMasteringVoice)->master.effectCache = (float*) audio->pMalloc(
787
sizeof(float) *
788
audio->updateSize *
789
(*ppMasteringVoice)->master.inputChannels
790
);
791
}
792
793
LOG_API_EXIT(audio)
794
return 0;
795
}
796
797
uint32_t FAudio_CreateMasteringVoice8(
798
FAudio *audio,
799
FAudioMasteringVoice **ppMasteringVoice,
800
uint32_t InputChannels,
801
uint32_t InputSampleRate,
802
uint32_t Flags,
803
uint16_t *szDeviceId,
804
const FAudioEffectChain *pEffectChain,
805
FAudioStreamCategory StreamCategory
806
) {
807
uint32_t DeviceIndex, retval;
808
809
LOG_API_ENTER(audio)
810
811
/* Eventually, we'll want the old CreateMastering to call the new one.
812
* That will depend on us being able to use DeviceID though.
813
* For now, use our little ID hack to turn szDeviceId into DeviceIndex.
814
* -flibit
815
*/
816
if (szDeviceId == NULL || szDeviceId[0] == 0)
817
{
818
DeviceIndex = 0;
819
}
820
else
821
{
822
DeviceIndex = szDeviceId[0] - L'0';
823
if (DeviceIndex > FAudio_PlatformGetDeviceCount())
824
{
825
DeviceIndex = 0;
826
}
827
}
828
829
/* Note that StreamCategory is ignored! */
830
retval = FAudio_CreateMasteringVoice(
831
audio,
832
ppMasteringVoice,
833
InputChannels,
834
InputSampleRate,
835
Flags,
836
DeviceIndex,
837
pEffectChain
838
);
839
840
LOG_API_EXIT(audio)
841
return retval;
842
}
843
844
void FAudio_SetEngineProcedureEXT(
845
FAudio *audio,
846
FAudioEngineProcedureEXT clientEngineProc,
847
void *user
848
) {
849
LOG_API_ENTER(audio)
850
audio->pClientEngineProc = clientEngineProc;
851
audio->clientEngineUser = user;
852
LOG_API_EXIT(audio)
853
}
854
855
uint32_t FAudio_StartEngine(FAudio *audio)
856
{
857
LOG_API_ENTER(audio)
858
audio->active = 1;
859
LOG_API_EXIT(audio)
860
return 0;
861
}
862
863
void FAudio_StopEngine(FAudio *audio)
864
{
865
LOG_API_ENTER(audio)
866
audio->active = 0;
867
FAudio_OPERATIONSET_CommitAll(audio);
868
FAudio_OPERATIONSET_Execute(audio);
869
LOG_API_EXIT(audio)
870
}
871
872
uint32_t FAudio_CommitOperationSet(FAudio *audio, uint32_t OperationSet)
873
{
874
LOG_API_ENTER(audio)
875
if (OperationSet == FAUDIO_COMMIT_ALL)
876
{
877
FAudio_OPERATIONSET_CommitAll(audio);
878
}
879
else
880
{
881
FAudio_OPERATIONSET_Commit(audio, OperationSet);
882
}
883
LOG_API_EXIT(audio)
884
return 0;
885
}
886
887
uint32_t FAudio_CommitChanges(FAudio *audio)
888
{
889
FAudio_Log(
890
"IF YOU CAN READ THIS, YOUR PROGRAM IS ABOUT TO BREAK!"
891
"\n\nEither you or somebody else is using FAudio_CommitChanges,"
892
"\nwhen they should be using FAudio_CommitOperationSet instead."
893
"\n\nIf your program calls this, move to CommitOperationSet."
894
"\n\nIf somebody else is calling this, find out who it is and"
895
"\nfile a bug report with them ASAP."
896
);
897
898
/* Seriously, this is like the worst possible thing short of no-oping.
899
* For the love-a Pete, just migrate, do it, what is wrong with you
900
*/
901
return FAudio_CommitOperationSet(audio, FAUDIO_COMMIT_ALL);
902
}
903
904
void FAudio_GetPerformanceData(
905
FAudio *audio,
906
FAudioPerformanceData *pPerfData
907
) {
908
LinkedList *list;
909
FAudioSourceVoice *source;
910
911
LOG_API_ENTER(audio)
912
913
FAudio_zero(pPerfData, sizeof(FAudioPerformanceData));
914
915
FAudio_PlatformLockMutex(audio->sourceLock);
916
LOG_MUTEX_LOCK(audio, audio->sourceLock)
917
list = audio->sources;
918
while (list != NULL)
919
{
920
source = (FAudioSourceVoice*) list->entry;
921
pPerfData->TotalSourceVoiceCount += 1;
922
if (source->src.active)
923
{
924
pPerfData->ActiveSourceVoiceCount += 1;
925
}
926
list = list->next;
927
}
928
FAudio_PlatformUnlockMutex(audio->sourceLock);
929
LOG_MUTEX_UNLOCK(audio, audio->sourceLock)
930
931
FAudio_PlatformLockMutex(audio->submixLock);
932
LOG_MUTEX_LOCK(audio, audio->submixLock)
933
list = audio->submixes;
934
while (list != NULL)
935
{
936
pPerfData->ActiveSubmixVoiceCount += 1;
937
list = list->next;
938
}
939
FAudio_PlatformUnlockMutex(audio->submixLock);
940
LOG_MUTEX_UNLOCK(audio, audio->submixLock)
941
942
if (audio->master != NULL)
943
{
944
/* estimate, should use real latency from platform */
945
pPerfData->CurrentLatencyInSamples = 2 * audio->updateSize;
946
}
947
948
LOG_API_EXIT(audio)
949
}
950
951
void FAudio_SetDebugConfiguration(
952
FAudio *audio,
953
FAudioDebugConfiguration *pDebugConfiguration,
954
void* pReserved
955
) {
956
#ifndef FAUDIO_DISABLE_DEBUGCONFIGURATION
957
char *env;
958
959
LOG_API_ENTER(audio)
960
961
FAudio_memcpy(
962
&audio->debug,
963
pDebugConfiguration,
964
sizeof(FAudioDebugConfiguration)
965
);
966
967
env = FAudio_getenv("FAUDIO_LOG_EVERYTHING");
968
if (env != NULL && *env == '1')
969
{
970
audio->debug.TraceMask = (
971
FAUDIO_LOG_ERRORS |
972
FAUDIO_LOG_WARNINGS |
973
FAUDIO_LOG_INFO |
974
FAUDIO_LOG_DETAIL |
975
FAUDIO_LOG_API_CALLS |
976
FAUDIO_LOG_FUNC_CALLS |
977
FAUDIO_LOG_TIMING |
978
FAUDIO_LOG_LOCKS |
979
FAUDIO_LOG_MEMORY |
980
FAUDIO_LOG_STREAMING
981
);
982
audio->debug.LogThreadID = 1;
983
audio->debug.LogFunctionName = 1;
984
audio->debug.LogTiming = 1;
985
}
986
987
#define CHECK_ENV(type) \
988
env = FAudio_getenv("FAUDIO_LOG_" #type); \
989
if (env != NULL) \
990
{ \
991
if (*env == '1') \
992
{ \
993
audio->debug.TraceMask |= FAUDIO_LOG_##type; \
994
} \
995
else \
996
{ \
997
audio->debug.TraceMask &= ~FAUDIO_LOG_##type; \
998
} \
999
}
1000
CHECK_ENV(ERRORS)
1001
CHECK_ENV(WARNINGS)
1002
CHECK_ENV(INFO)
1003
CHECK_ENV(DETAIL)
1004
CHECK_ENV(API_CALLS)
1005
CHECK_ENV(FUNC_CALLS)
1006
CHECK_ENV(TIMING)
1007
CHECK_ENV(LOCKS)
1008
CHECK_ENV(MEMORY)
1009
CHECK_ENV(STREAMING)
1010
#undef CHECK_ENV
1011
#define CHECK_ENV(envvar, boolvar) \
1012
env = FAudio_getenv("FAUDIO_LOG_LOG" #envvar); \
1013
if (env != NULL) \
1014
{ \
1015
audio->debug.Log##boolvar = (*env == '1'); \
1016
}
1017
CHECK_ENV(THREADID, ThreadID)
1018
CHECK_ENV(FILELINE, Fileline)
1019
CHECK_ENV(FUNCTIONNAME, FunctionName)
1020
CHECK_ENV(TIMING, Timing)
1021
#undef CHECK_ENV
1022
1023
LOG_API_EXIT(audio)
1024
#endif /* FAUDIO_DISABLE_DEBUGCONFIGURATION */
1025
}
1026
1027
void FAudio_GetProcessingQuantum(
1028
FAudio *audio,
1029
uint32_t *quantumNumerator,
1030
uint32_t *quantumDenominator
1031
) {
1032
FAudio_assert(audio->master != NULL);
1033
if (quantumNumerator != NULL)
1034
{
1035
*quantumNumerator = audio->updateSize;
1036
}
1037
if (quantumDenominator != NULL)
1038
{
1039
*quantumDenominator = audio->master->master.inputSampleRate;
1040
}
1041
}
1042
1043
/* FAudioVoice Interface */
1044
1045
static void FAudio_RecalcMixMatrix(FAudioVoice *voice, uint32_t sendIndex)
1046
{
1047
uint32_t oChan, s, d;
1048
FAudioVoice *out = voice->sends.pSends[sendIndex].pOutputVoice;
1049
float volume, *matrix = voice->mixCoefficients[sendIndex];
1050
1051
if (voice->type == FAUDIO_VOICE_SUBMIX)
1052
{
1053
volume = 1.f;
1054
}
1055
else
1056
{
1057
volume = voice->volume;
1058
}
1059
1060
if (out->type == FAUDIO_VOICE_MASTER)
1061
{
1062
oChan = out->master.inputChannels;
1063
}
1064
else
1065
{
1066
oChan = out->mix.inputChannels;
1067
}
1068
1069
for (d = 0; d < oChan; d += 1)
1070
{
1071
for (s = 0; s < voice->outputChannels; s += 1)
1072
{
1073
matrix[d * voice->outputChannels + s] = volume *
1074
voice->channelVolume[s] *
1075
voice->sendCoefficients[sendIndex][d * voice->outputChannels + s];
1076
}
1077
}
1078
}
1079
1080
void FAudioVoice_GetVoiceDetails(
1081
FAudioVoice *voice,
1082
FAudioVoiceDetails *pVoiceDetails
1083
) {
1084
LOG_API_ENTER(voice->audio)
1085
1086
pVoiceDetails->CreationFlags = voice->flags;
1087
pVoiceDetails->ActiveFlags = voice->flags;
1088
if (voice->type == FAUDIO_VOICE_SOURCE)
1089
{
1090
pVoiceDetails->InputChannels = voice->src.format->nChannels;
1091
pVoiceDetails->InputSampleRate = voice->src.format->nSamplesPerSec;
1092
}
1093
else if (voice->type == FAUDIO_VOICE_SUBMIX)
1094
{
1095
pVoiceDetails->InputChannels = voice->mix.inputChannels;
1096
pVoiceDetails->InputSampleRate = voice->mix.inputSampleRate;
1097
}
1098
else if (voice->type == FAUDIO_VOICE_MASTER)
1099
{
1100
pVoiceDetails->InputChannels = voice->master.inputChannels;
1101
pVoiceDetails->InputSampleRate = voice->master.inputSampleRate;
1102
}
1103
else
1104
{
1105
FAudio_assert(0 && "Unknown voice type!");
1106
}
1107
1108
LOG_API_EXIT(voice->audio)
1109
}
1110
1111
uint32_t FAudioVoice_SetOutputVoices(
1112
FAudioVoice *voice,
1113
const FAudioVoiceSends *pSendList
1114
) {
1115
uint32_t i;
1116
uint32_t outChannels;
1117
FAudioVoiceSends defaultSends;
1118
FAudioSendDescriptor defaultSend;
1119
1120
LOG_API_ENTER(voice->audio)
1121
1122
if (voice->type == FAUDIO_VOICE_MASTER)
1123
{
1124
LOG_API_EXIT(voice->audio)
1125
return FAUDIO_E_INVALID_CALL;
1126
}
1127
1128
FAudio_PlatformLockMutex(voice->sendLock);
1129
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
1130
1131
if (FAudio_INTERNAL_VoiceOutputFrequency(voice, pSendList) != 0)
1132
{
1133
LOG_ERROR(
1134
voice->audio,
1135
"%s",
1136
"Changing the sample rate while an effect chain is attached is invalid!"
1137
)
1138
FAudio_PlatformUnlockMutex(voice->sendLock);
1139
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1140
LOG_API_EXIT(voice->audio)
1141
return FAUDIO_E_INVALID_CALL;
1142
}
1143
1144
FAudio_PlatformLockMutex(voice->volumeLock);
1145
LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
1146
1147
/* FIXME: This is lazy... */
1148
for (i = 0; i < voice->sends.SendCount; i += 1)
1149
{
1150
voice->audio->pFree(voice->sendCoefficients[i]);
1151
}
1152
if (voice->sendCoefficients != NULL)
1153
{
1154
voice->audio->pFree(voice->sendCoefficients);
1155
}
1156
for (i = 0; i < voice->sends.SendCount; i += 1)
1157
{
1158
voice->audio->pFree(voice->mixCoefficients[i]);
1159
}
1160
if (voice->mixCoefficients != NULL)
1161
{
1162
voice->audio->pFree(voice->mixCoefficients);
1163
}
1164
if (voice->sendMix != NULL)
1165
{
1166
voice->audio->pFree(voice->sendMix);
1167
}
1168
if (voice->sendFilter != NULL)
1169
{
1170
voice->audio->pFree(voice->sendFilter);
1171
voice->sendFilter = NULL;
1172
}
1173
if (voice->sendFilterState != NULL)
1174
{
1175
for (i = 0; i < voice->sends.SendCount; i += 1)
1176
{
1177
if (voice->sendFilterState[i] != NULL)
1178
{
1179
voice->audio->pFree(voice->sendFilterState[i]);
1180
}
1181
}
1182
voice->audio->pFree(voice->sendFilterState);
1183
voice->sendFilterState = NULL;
1184
}
1185
if (voice->sends.pSends != NULL)
1186
{
1187
voice->audio->pFree(voice->sends.pSends);
1188
}
1189
1190
if (pSendList == NULL)
1191
{
1192
/* Default to the mastering voice as output */
1193
defaultSend.Flags = 0;
1194
defaultSend.pOutputVoice = voice->audio->master;
1195
defaultSends.SendCount = 1;
1196
defaultSends.pSends = &defaultSend;
1197
pSendList = &defaultSends;
1198
}
1199
else if (pSendList->SendCount == 0)
1200
{
1201
/* No sends? Nothing to do... */
1202
voice->sendCoefficients = NULL;
1203
voice->mixCoefficients = NULL;
1204
voice->sendMix = NULL;
1205
FAudio_zero(&voice->sends, sizeof(FAudioVoiceSends));
1206
1207
FAudio_PlatformUnlockMutex(voice->volumeLock);
1208
LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
1209
FAudio_PlatformUnlockMutex(voice->sendLock);
1210
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1211
LOG_API_EXIT(voice->audio)
1212
return 0;
1213
}
1214
1215
/* Copy send list */
1216
voice->sends.SendCount = pSendList->SendCount;
1217
voice->sends.pSends = (FAudioSendDescriptor*) voice->audio->pMalloc(
1218
pSendList->SendCount * sizeof(FAudioSendDescriptor)
1219
);
1220
FAudio_memcpy(
1221
voice->sends.pSends,
1222
pSendList->pSends,
1223
pSendList->SendCount * sizeof(FAudioSendDescriptor)
1224
);
1225
1226
/* Allocate/Reset default output matrix, mixer function, filters */
1227
voice->sendCoefficients = (float**) voice->audio->pMalloc(
1228
sizeof(float*) * pSendList->SendCount
1229
);
1230
voice->mixCoefficients = (float**) voice->audio->pMalloc(
1231
sizeof(float*) * pSendList->SendCount
1232
);
1233
voice->sendMix = (FAudioMixCallback*) voice->audio->pMalloc(
1234
sizeof(FAudioMixCallback) * pSendList->SendCount
1235
);
1236
1237
for (i = 0; i < pSendList->SendCount; i += 1)
1238
{
1239
if (pSendList->pSends[i].pOutputVoice->type == FAUDIO_VOICE_MASTER)
1240
{
1241
outChannels = pSendList->pSends[i].pOutputVoice->master.inputChannels;
1242
}
1243
else
1244
{
1245
outChannels = pSendList->pSends[i].pOutputVoice->mix.inputChannels;
1246
}
1247
voice->sendCoefficients[i] = (float*) voice->audio->pMalloc(
1248
sizeof(float) * voice->outputChannels * outChannels
1249
);
1250
voice->mixCoefficients[i] = (float*) voice->audio->pMalloc(
1251
sizeof(float) * voice->outputChannels * outChannels
1252
);
1253
1254
FAudio_assert(voice->outputChannels > 0 && voice->outputChannels < 9);
1255
FAudio_assert(outChannels > 0 && outChannels < 9);
1256
FAudio_memcpy(
1257
voice->sendCoefficients[i],
1258
FAUDIO_INTERNAL_MATRIX_DEFAULTS[voice->outputChannels - 1][outChannels - 1],
1259
voice->outputChannels * outChannels * sizeof(float)
1260
);
1261
FAudio_RecalcMixMatrix(voice, i);
1262
1263
if (voice->outputChannels == 1)
1264
{
1265
if (outChannels == 1)
1266
{
1267
voice->sendMix[i] = FAudio_INTERNAL_Mix_1in_1out_Scalar;
1268
}
1269
else if (outChannels == 2)
1270
{
1271
voice->sendMix[i] = FAudio_INTERNAL_Mix_1in_2out_Scalar;
1272
}
1273
else if (outChannels == 6)
1274
{
1275
voice->sendMix[i] = FAudio_INTERNAL_Mix_1in_6out_Scalar;
1276
}
1277
else if (outChannels == 8)
1278
{
1279
voice->sendMix[i] = FAudio_INTERNAL_Mix_1in_8out_Scalar;
1280
}
1281
else
1282
{
1283
voice->sendMix[i] = FAudio_INTERNAL_Mix_Generic;
1284
}
1285
}
1286
else if (voice->outputChannels == 2)
1287
{
1288
if (outChannels == 1)
1289
{
1290
voice->sendMix[i] = FAudio_INTERNAL_Mix_2in_1out_Scalar;
1291
}
1292
else if (outChannels == 2)
1293
{
1294
voice->sendMix[i] = FAudio_INTERNAL_Mix_2in_2out_Scalar;
1295
}
1296
else if (outChannels == 6)
1297
{
1298
voice->sendMix[i] = FAudio_INTERNAL_Mix_2in_6out_Scalar;
1299
}
1300
else if (outChannels == 8)
1301
{
1302
voice->sendMix[i] = FAudio_INTERNAL_Mix_2in_8out_Scalar;
1303
}
1304
else
1305
{
1306
voice->sendMix[i] = FAudio_INTERNAL_Mix_Generic;
1307
}
1308
}
1309
else
1310
{
1311
voice->sendMix[i] = FAudio_INTERNAL_Mix_Generic;
1312
}
1313
1314
if (pSendList->pSends[i].Flags & FAUDIO_SEND_USEFILTER)
1315
{
1316
/* Allocate the whole send filter array if needed... */
1317
if (voice->sendFilter == NULL)
1318
{
1319
voice->sendFilter = (FAudioFilterParametersEXT*) voice->audio->pMalloc(
1320
sizeof(FAudioFilterParametersEXT) * pSendList->SendCount
1321
);
1322
}
1323
if (voice->sendFilterState == NULL)
1324
{
1325
voice->sendFilterState = (FAudioFilterState**) voice->audio->pMalloc(
1326
sizeof(FAudioFilterState*) * pSendList->SendCount
1327
);
1328
FAudio_zero(
1329
voice->sendFilterState,
1330
sizeof(FAudioFilterState*) * pSendList->SendCount
1331
);
1332
}
1333
1334
/* ... then fill in this send's filter data */
1335
voice->sendFilter[i].Type = FAUDIO_DEFAULT_FILTER_TYPE;
1336
voice->sendFilter[i].Frequency = FAUDIO_DEFAULT_FILTER_FREQUENCY;
1337
voice->sendFilter[i].OneOverQ = FAUDIO_DEFAULT_FILTER_ONEOVERQ;
1338
voice->sendFilter[i].WetDryMix = FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT;
1339
voice->sendFilterState[i] = (FAudioFilterState*) voice->audio->pMalloc(
1340
sizeof(FAudioFilterState) * outChannels
1341
);
1342
FAudio_zero(
1343
voice->sendFilterState[i],
1344
sizeof(FAudioFilterState) * outChannels
1345
);
1346
}
1347
}
1348
1349
FAudio_PlatformUnlockMutex(voice->volumeLock);
1350
LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
1351
1352
FAudio_PlatformUnlockMutex(voice->sendLock);
1353
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1354
LOG_API_EXIT(voice->audio)
1355
return 0;
1356
}
1357
1358
uint32_t FAudioVoice_SetEffectChain(
1359
FAudioVoice *voice,
1360
const FAudioEffectChain *pEffectChain
1361
) {
1362
uint32_t i;
1363
FAPO *fapo;
1364
uint32_t channelCount;
1365
FAudioVoiceDetails voiceDetails;
1366
FAPORegistrationProperties *pProps;
1367
FAudioWaveFormatExtensible srcFmt, dstFmt;
1368
FAPOLockForProcessBufferParameters srcLockParams, dstLockParams;
1369
1370
LOG_API_ENTER(voice->audio)
1371
1372
FAudioVoice_GetVoiceDetails(voice, &voiceDetails);
1373
1374
/* SetEffectChain must not change the number of output channels once the voice has been created */
1375
if (pEffectChain == NULL && voice->outputChannels != 0)
1376
{
1377
/* cannot remove an effect chain that changes the number of channels */
1378
if (voice->outputChannels != voiceDetails.InputChannels)
1379
{
1380
LOG_ERROR(
1381
voice->audio,
1382
"%s",
1383
"Cannot remove effect chain that changes the number of channels"
1384
)
1385
FAudio_assert(0 && "Cannot remove effect chain that changes the number of channels");
1386
LOG_API_EXIT(voice->audio)
1387
return FAUDIO_E_INVALID_CALL;
1388
}
1389
}
1390
1391
if (pEffectChain != NULL && voice->outputChannels != 0)
1392
{
1393
uint32_t lst = pEffectChain->EffectCount - 1;
1394
1395
/* new effect chain must have same number of output channels */
1396
if (voice->outputChannels != pEffectChain->pEffectDescriptors[lst].OutputChannels)
1397
{
1398
LOG_ERROR(
1399
voice->audio,
1400
"%s",
1401
"New effect chain must have same number of output channels as the old chain"
1402
)
1403
FAudio_assert(0 && "New effect chain must have same number of output channels as the old chain");
1404
LOG_API_EXIT(voice->audio)
1405
return FAUDIO_E_INVALID_CALL;
1406
}
1407
}
1408
1409
FAudio_PlatformLockMutex(voice->effectLock);
1410
LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
1411
1412
if (pEffectChain == NULL)
1413
{
1414
FAudio_INTERNAL_FreeEffectChain(voice);
1415
FAudio_zero(&voice->effects, sizeof(voice->effects));
1416
voice->outputChannels = voiceDetails.InputChannels;
1417
}
1418
else
1419
{
1420
/* Validate incoming chain before changing the current chain */
1421
1422
/* These are always the same, so just write them now. */
1423
srcLockParams.pFormat = &srcFmt.Format;
1424
dstLockParams.pFormat = &dstFmt.Format;
1425
if (voice->type == FAUDIO_VOICE_SOURCE)
1426
{
1427
srcLockParams.MaxFrameCount = voice->src.resampleSamples;
1428
dstLockParams.MaxFrameCount = voice->src.resampleSamples;
1429
}
1430
else if (voice->type == FAUDIO_VOICE_SUBMIX)
1431
{
1432
srcLockParams.MaxFrameCount = voice->mix.outputSamples;
1433
dstLockParams.MaxFrameCount = voice->mix.outputSamples;
1434
}
1435
else if (voice->type == FAUDIO_VOICE_MASTER)
1436
{
1437
srcLockParams.MaxFrameCount = voice->audio->updateSize;
1438
dstLockParams.MaxFrameCount = voice->audio->updateSize;
1439
}
1440
1441
/* The first source is the voice input data... */
1442
srcFmt.Format.wBitsPerSample = 32;
1443
srcFmt.Format.wFormatTag = FAUDIO_FORMAT_EXTENSIBLE;
1444
srcFmt.Format.nChannels = voiceDetails.InputChannels;
1445
srcFmt.Format.nSamplesPerSec = voiceDetails.InputSampleRate;
1446
srcFmt.Format.nBlockAlign = srcFmt.Format.nChannels * (srcFmt.Format.wBitsPerSample / 8);
1447
srcFmt.Format.nAvgBytesPerSec = srcFmt.Format.nSamplesPerSec * srcFmt.Format.nBlockAlign;
1448
srcFmt.Format.cbSize = sizeof(FAudioWaveFormatExtensible) - sizeof(FAudioWaveFormatEx);
1449
srcFmt.Samples.wValidBitsPerSample = srcFmt.Format.wBitsPerSample;
1450
srcFmt.dwChannelMask = 0;
1451
FAudio_memcpy(&srcFmt.SubFormat, &DATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(FAudioGUID));
1452
FAudio_memcpy(&dstFmt, &srcFmt, sizeof(srcFmt));
1453
1454
for (i = 0; i < pEffectChain->EffectCount; i += 1)
1455
{
1456
fapo = pEffectChain->pEffectDescriptors[i].pEffect;
1457
1458
/* ... then we get this effect's format... */
1459
dstFmt.Format.nChannels = pEffectChain->pEffectDescriptors[i].OutputChannels;
1460
dstFmt.Format.nBlockAlign = dstFmt.Format.nChannels * (dstFmt.Format.wBitsPerSample / 8);
1461
dstFmt.Format.nAvgBytesPerSec = dstFmt.Format.nSamplesPerSec * dstFmt.Format.nBlockAlign;
1462
1463
/* FIXME: This error needs to be found _before_ we start
1464
* shredding the voice's state. This function is highly
1465
* destructive so any errors need to be found at the
1466
* beginning, not in the middle! We can't undo this!
1467
* -flibit
1468
*/
1469
if (fapo->LockForProcess(fapo, 1, &srcLockParams, 1, &dstLockParams))
1470
{
1471
LOG_ERROR(
1472
voice->audio,
1473
"%s",
1474
"Effect output format not supported"
1475
)
1476
FAudio_assert(0 && "Effect output format not supported");
1477
FAudio_PlatformUnlockMutex(voice->effectLock);
1478
LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
1479
LOG_API_EXIT(voice->audio)
1480
return FAUDIO_E_UNSUPPORTED_FORMAT;
1481
}
1482
1483
/* Okay, now this effect is the source and the next
1484
* effect will be the destination. Repeat until no
1485
* effects left.
1486
*/
1487
FAudio_memcpy(&srcFmt, &dstFmt, sizeof(srcFmt));
1488
}
1489
1490
FAudio_INTERNAL_FreeEffectChain(voice);
1491
FAudio_INTERNAL_AllocEffectChain(
1492
voice,
1493
pEffectChain
1494
);
1495
1496
/* check if in-place processing is supported */
1497
channelCount = voiceDetails.InputChannels;
1498
for (i = 0; i < voice->effects.count; i += 1)
1499
{
1500
fapo = voice->effects.desc[i].pEffect;
1501
if (fapo->GetRegistrationProperties(fapo, &pProps) == 0)
1502
{
1503
voice->effects.inPlaceProcessing[i] = (pProps->Flags & FAPO_FLAG_INPLACE_SUPPORTED) == FAPO_FLAG_INPLACE_SUPPORTED;
1504
voice->effects.inPlaceProcessing[i] &= (channelCount == voice->effects.desc[i].OutputChannels);
1505
channelCount = voice->effects.desc[i].OutputChannels;
1506
1507
/* Fails if in-place processing is mandatory and
1508
* the chain forces us to do otherwise...
1509
*/
1510
FAudio_assert(
1511
!(pProps->Flags & FAPO_FLAG_INPLACE_REQUIRED) ||
1512
voice->effects.inPlaceProcessing[i]
1513
);
1514
1515
voice->audio->pFree(pProps);
1516
}
1517
}
1518
voice->outputChannels = channelCount;
1519
}
1520
1521
FAudio_PlatformUnlockMutex(voice->effectLock);
1522
LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
1523
LOG_API_EXIT(voice->audio)
1524
return 0;
1525
}
1526
1527
uint32_t FAudioVoice_EnableEffect(
1528
FAudioVoice *voice,
1529
uint32_t EffectIndex,
1530
uint32_t OperationSet
1531
) {
1532
LOG_API_ENTER(voice->audio)
1533
1534
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
1535
{
1536
FAudio_OPERATIONSET_QueueEnableEffect(
1537
voice,
1538
EffectIndex,
1539
OperationSet
1540
);
1541
LOG_API_EXIT(voice->audio)
1542
return 0;
1543
}
1544
1545
FAudio_PlatformLockMutex(voice->effectLock);
1546
LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
1547
voice->effects.desc[EffectIndex].InitialState = 1;
1548
FAudio_PlatformUnlockMutex(voice->effectLock);
1549
LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
1550
LOG_API_EXIT(voice->audio)
1551
return 0;
1552
}
1553
1554
uint32_t FAudioVoice_DisableEffect(
1555
FAudioVoice *voice,
1556
uint32_t EffectIndex,
1557
uint32_t OperationSet
1558
) {
1559
LOG_API_ENTER(voice->audio)
1560
1561
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
1562
{
1563
FAudio_OPERATIONSET_QueueDisableEffect(
1564
voice,
1565
EffectIndex,
1566
OperationSet
1567
);
1568
LOG_API_EXIT(voice->audio)
1569
return 0;
1570
}
1571
1572
FAudio_PlatformLockMutex(voice->effectLock);
1573
LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
1574
voice->effects.desc[EffectIndex].InitialState = 0;
1575
FAudio_PlatformUnlockMutex(voice->effectLock);
1576
LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
1577
LOG_API_EXIT(voice->audio)
1578
return 0;
1579
}
1580
1581
void FAudioVoice_GetEffectState(
1582
FAudioVoice *voice,
1583
uint32_t EffectIndex,
1584
int32_t *pEnabled
1585
) {
1586
LOG_API_ENTER(voice->audio)
1587
FAudio_PlatformLockMutex(voice->effectLock);
1588
LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
1589
*pEnabled = voice->effects.desc[EffectIndex].InitialState;
1590
FAudio_PlatformUnlockMutex(voice->effectLock);
1591
LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
1592
LOG_API_EXIT(voice->audio)
1593
}
1594
1595
uint32_t FAudioVoice_SetEffectParameters(
1596
FAudioVoice *voice,
1597
uint32_t EffectIndex,
1598
const void *pParameters,
1599
uint32_t ParametersByteSize,
1600
uint32_t OperationSet
1601
) {
1602
LOG_API_ENTER(voice->audio)
1603
1604
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
1605
{
1606
FAudio_OPERATIONSET_QueueSetEffectParameters(
1607
voice,
1608
EffectIndex,
1609
pParameters,
1610
ParametersByteSize,
1611
OperationSet
1612
);
1613
LOG_API_EXIT(voice->audio)
1614
return 0;
1615
}
1616
1617
if (voice->effects.parameters == NULL)
1618
{
1619
/* Cannot set parameters if there is no effect chain! */
1620
LOG_ERROR(
1621
voice->audio,
1622
"Setting effect parameters on voice with no effect chain: %p",
1623
(void*) voice
1624
);
1625
return FAUDIO_E_INVALID_CALL;
1626
}
1627
1628
if (voice->effects.parameters[EffectIndex] == NULL)
1629
{
1630
voice->effects.parameters[EffectIndex] = voice->audio->pMalloc(
1631
ParametersByteSize
1632
);
1633
voice->effects.parameterSizes[EffectIndex] = ParametersByteSize;
1634
}
1635
FAudio_PlatformLockMutex(voice->effectLock);
1636
LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
1637
if (voice->effects.parameterSizes[EffectIndex] < ParametersByteSize)
1638
{
1639
voice->effects.parameters[EffectIndex] = voice->audio->pRealloc(
1640
voice->effects.parameters[EffectIndex],
1641
ParametersByteSize
1642
);
1643
voice->effects.parameterSizes[EffectIndex] = ParametersByteSize;
1644
}
1645
FAudio_memcpy(
1646
voice->effects.parameters[EffectIndex],
1647
pParameters,
1648
ParametersByteSize
1649
);
1650
voice->effects.parameterUpdates[EffectIndex] = 1;
1651
FAudio_PlatformUnlockMutex(voice->effectLock);
1652
LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
1653
LOG_API_EXIT(voice->audio)
1654
return 0;
1655
}
1656
1657
uint32_t FAudioVoice_GetEffectParameters(
1658
FAudioVoice *voice,
1659
uint32_t EffectIndex,
1660
void *pParameters,
1661
uint32_t ParametersByteSize
1662
) {
1663
FAPO *fapo;
1664
LOG_API_ENTER(voice->audio)
1665
FAudio_PlatformLockMutex(voice->effectLock);
1666
LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
1667
fapo = voice->effects.desc[EffectIndex].pEffect;
1668
fapo->GetParameters(fapo, pParameters, ParametersByteSize);
1669
FAudio_PlatformUnlockMutex(voice->effectLock);
1670
LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
1671
LOG_API_EXIT(voice->audio)
1672
return 0;
1673
}
1674
1675
uint32_t FAudioVoice_SetFilterParametersEXT(
1676
FAudioVoice *voice,
1677
const FAudioFilterParametersEXT *pParameters,
1678
uint32_t OperationSet
1679
) {
1680
LOG_API_ENTER(voice->audio)
1681
1682
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
1683
{
1684
FAudio_OPERATIONSET_QueueSetFilterParameters(
1685
voice,
1686
pParameters,
1687
OperationSet
1688
);
1689
LOG_API_EXIT(voice->audio)
1690
return 0;
1691
}
1692
1693
/* MSDN: "This method is usable only on source and submix voices and
1694
* has no effect on mastering voices."
1695
*/
1696
if (voice->type == FAUDIO_VOICE_MASTER)
1697
{
1698
LOG_API_EXIT(voice->audio)
1699
return 0;
1700
}
1701
1702
if (!(voice->flags & FAUDIO_VOICE_USEFILTER))
1703
{
1704
LOG_API_EXIT(voice->audio)
1705
return 0;
1706
}
1707
1708
FAudio_PlatformLockMutex(voice->filterLock);
1709
LOG_MUTEX_LOCK(voice->audio, voice->filterLock)
1710
FAudio_memcpy(
1711
&voice->filter,
1712
pParameters,
1713
sizeof(FAudioFilterParametersEXT)
1714
);
1715
FAudio_PlatformUnlockMutex(voice->filterLock);
1716
LOG_MUTEX_UNLOCK(voice->audio, voice->filterLock)
1717
1718
LOG_API_EXIT(voice->audio)
1719
return 0;
1720
}
1721
1722
uint32_t FAudioVoice_SetFilterParameters(
1723
FAudioVoice* voice,
1724
const FAudioFilterParameters* pParameters,
1725
uint32_t OperationSet
1726
) {
1727
FAudioFilterParametersEXT ext_parameters;
1728
ext_parameters.Type = pParameters->Type;
1729
ext_parameters.OneOverQ = pParameters->OneOverQ;
1730
ext_parameters.Frequency = pParameters->Frequency;
1731
ext_parameters.WetDryMix = FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT;
1732
1733
return FAudioVoice_SetFilterParametersEXT(voice, &ext_parameters, OperationSet);
1734
}
1735
1736
void FAudioVoice_GetFilterParametersEXT(
1737
FAudioVoice *voice,
1738
FAudioFilterParametersEXT *pParameters
1739
) {
1740
LOG_API_ENTER(voice->audio)
1741
1742
/* MSDN: "This method is usable only on source and submix voices and
1743
* has no effect on mastering voices."
1744
*/
1745
if (voice->type == FAUDIO_VOICE_MASTER)
1746
{
1747
LOG_API_EXIT(voice->audio)
1748
return;
1749
}
1750
1751
if (!(voice->flags & FAUDIO_VOICE_USEFILTER))
1752
{
1753
LOG_API_EXIT(voice->audio)
1754
return;
1755
}
1756
1757
FAudio_PlatformLockMutex(voice->filterLock);
1758
LOG_MUTEX_LOCK(voice->audio, voice->filterLock)
1759
FAudio_memcpy(
1760
pParameters,
1761
&voice->filter,
1762
sizeof(FAudioFilterParametersEXT)
1763
);
1764
FAudio_PlatformUnlockMutex(voice->filterLock);
1765
LOG_MUTEX_UNLOCK(voice->audio, voice->filterLock)
1766
LOG_API_EXIT(voice->audio)
1767
}
1768
1769
void FAudioVoice_GetFilterParameters(
1770
FAudioVoice* voice,
1771
FAudioFilterParameters* pParameters
1772
) {
1773
FAudioFilterParametersEXT ext_parameters;
1774
ext_parameters.Type = pParameters->Type;
1775
ext_parameters.OneOverQ = pParameters->OneOverQ;
1776
ext_parameters.Frequency = pParameters->Frequency;
1777
ext_parameters.WetDryMix = FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT;
1778
1779
FAudioVoice_GetFilterParametersEXT(voice, &ext_parameters);
1780
1781
pParameters->Type = ext_parameters.Type;
1782
pParameters->Frequency = ext_parameters.Frequency;
1783
pParameters->OneOverQ = ext_parameters.OneOverQ;
1784
}
1785
1786
uint32_t FAudioVoice_SetOutputFilterParametersEXT(
1787
FAudioVoice *voice,
1788
FAudioVoice *pDestinationVoice,
1789
const FAudioFilterParametersEXT *pParameters,
1790
uint32_t OperationSet
1791
) {
1792
uint32_t i;
1793
LOG_API_ENTER(voice->audio)
1794
1795
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
1796
{
1797
FAudio_OPERATIONSET_QueueSetOutputFilterParameters(
1798
voice,
1799
pDestinationVoice,
1800
pParameters,
1801
OperationSet
1802
);
1803
LOG_API_EXIT(voice->audio)
1804
return 0;
1805
}
1806
1807
/* MSDN: "This method is usable only on source and submix voices and
1808
* has no effect on mastering voices."
1809
*/
1810
if (voice->type == FAUDIO_VOICE_MASTER)
1811
{
1812
LOG_API_EXIT(voice->audio)
1813
return 0;
1814
}
1815
1816
FAudio_PlatformLockMutex(voice->sendLock);
1817
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
1818
1819
/* Find the send index */
1820
if (pDestinationVoice == NULL && voice->sends.SendCount == 1)
1821
{
1822
pDestinationVoice = voice->sends.pSends[0].pOutputVoice;
1823
}
1824
for (i = 0; i < voice->sends.SendCount; i += 1)
1825
{
1826
if (pDestinationVoice == voice->sends.pSends[i].pOutputVoice)
1827
{
1828
break;
1829
}
1830
}
1831
if (i >= voice->sends.SendCount)
1832
{
1833
LOG_ERROR(
1834
voice->audio,
1835
"Destination not attached to source: %p %p",
1836
(void*) voice,
1837
(void*) pDestinationVoice
1838
)
1839
FAudio_PlatformUnlockMutex(voice->sendLock);
1840
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1841
LOG_API_EXIT(voice->audio)
1842
return FAUDIO_E_INVALID_CALL;
1843
}
1844
1845
if (!(voice->sends.pSends[i].Flags & FAUDIO_SEND_USEFILTER))
1846
{
1847
FAudio_PlatformUnlockMutex(voice->sendLock);
1848
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1849
LOG_API_EXIT(voice->audio)
1850
return 0;
1851
}
1852
1853
/* Set the filter parameters, finally. */
1854
FAudio_memcpy(
1855
&voice->sendFilter[i],
1856
pParameters,
1857
sizeof(FAudioFilterParametersEXT)
1858
);
1859
1860
FAudio_PlatformUnlockMutex(voice->sendLock);
1861
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1862
LOG_API_EXIT(voice->audio)
1863
return 0;
1864
}
1865
1866
uint32_t FAudioVoice_SetOutputFilterParameters(
1867
FAudioVoice* voice,
1868
FAudioVoice* pDestinationVoice,
1869
const FAudioFilterParameters* pParameters,
1870
uint32_t OperationSet
1871
) {
1872
FAudioFilterParametersEXT ext_parameters;
1873
ext_parameters.Type = pParameters->Type;
1874
ext_parameters.OneOverQ = pParameters->OneOverQ;
1875
ext_parameters.Frequency = pParameters->Frequency;
1876
ext_parameters.WetDryMix = FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT;
1877
1878
return FAudioVoice_SetOutputFilterParametersEXT(voice, pDestinationVoice, &ext_parameters, OperationSet);
1879
}
1880
1881
void FAudioVoice_GetOutputFilterParametersEXT(
1882
FAudioVoice *voice,
1883
FAudioVoice *pDestinationVoice,
1884
FAudioFilterParametersEXT *pParameters
1885
) {
1886
uint32_t i;
1887
1888
LOG_API_ENTER(voice->audio)
1889
1890
/* MSDN: "This method is usable only on source and submix voices and
1891
* has no effect on mastering voices."
1892
*/
1893
if (voice->type == FAUDIO_VOICE_MASTER)
1894
{
1895
LOG_API_EXIT(voice->audio)
1896
return;
1897
}
1898
1899
FAudio_PlatformLockMutex(voice->sendLock);
1900
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
1901
1902
/* Find the send index */
1903
if (pDestinationVoice == NULL && voice->sends.SendCount == 1)
1904
{
1905
pDestinationVoice = voice->sends.pSends[0].pOutputVoice;
1906
}
1907
for (i = 0; i < voice->sends.SendCount; i += 1)
1908
{
1909
if (pDestinationVoice == voice->sends.pSends[i].pOutputVoice)
1910
{
1911
break;
1912
}
1913
}
1914
if (i >= voice->sends.SendCount)
1915
{
1916
LOG_ERROR(
1917
voice->audio,
1918
"Destination not attached to source: %p %p",
1919
(void*) voice,
1920
(void*) pDestinationVoice
1921
)
1922
FAudio_PlatformUnlockMutex(voice->sendLock);
1923
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1924
LOG_API_EXIT(voice->audio)
1925
return;
1926
}
1927
1928
if (!(voice->sends.pSends[i].Flags & FAUDIO_SEND_USEFILTER))
1929
{
1930
FAudio_PlatformUnlockMutex(voice->sendLock);
1931
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1932
LOG_API_EXIT(voice->audio)
1933
return;
1934
}
1935
1936
/* Set the filter parameters, finally. */
1937
FAudio_memcpy(
1938
pParameters,
1939
&voice->sendFilter[i],
1940
sizeof(FAudioFilterParametersEXT)
1941
);
1942
1943
FAudio_PlatformUnlockMutex(voice->sendLock);
1944
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1945
LOG_API_EXIT(voice->audio)
1946
}
1947
1948
uint32_t FAudioVoice_SetVolume(
1949
FAudioVoice *voice,
1950
float Volume,
1951
uint32_t OperationSet
1952
) {
1953
uint32_t i;
1954
1955
LOG_API_ENTER(voice->audio)
1956
1957
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
1958
{
1959
FAudio_OPERATIONSET_QueueSetVolume(
1960
voice,
1961
Volume,
1962
OperationSet
1963
);
1964
LOG_API_EXIT(voice->audio)
1965
return 0;
1966
}
1967
1968
FAudio_PlatformLockMutex(voice->sendLock);
1969
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
1970
1971
FAudio_PlatformLockMutex(voice->volumeLock);
1972
LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
1973
1974
voice->volume = FAudio_clamp(
1975
Volume,
1976
-FAUDIO_MAX_VOLUME_LEVEL,
1977
FAUDIO_MAX_VOLUME_LEVEL
1978
);
1979
1980
for (i = 0; i < voice->sends.SendCount; i += 1)
1981
{
1982
FAudio_RecalcMixMatrix(voice, i);
1983
}
1984
1985
FAudio_PlatformUnlockMutex(voice->volumeLock);
1986
LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
1987
1988
FAudio_PlatformUnlockMutex(voice->sendLock);
1989
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1990
1991
LOG_API_EXIT(voice->audio)
1992
return 0;
1993
}
1994
1995
void FAudioVoice_GetOutputFilterParameters(
1996
FAudioVoice* voice,
1997
FAudioVoice* pDestinationVoice,
1998
FAudioFilterParameters* pParameters
1999
) {
2000
FAudioFilterParametersEXT ext_parameters;
2001
ext_parameters.Type = pParameters->Type;
2002
ext_parameters.OneOverQ = pParameters->OneOverQ;
2003
ext_parameters.Frequency = pParameters->Frequency;
2004
ext_parameters.WetDryMix = FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT;
2005
2006
FAudioVoice_GetOutputFilterParametersEXT(voice, pDestinationVoice, &ext_parameters);
2007
2008
pParameters->Type = ext_parameters.Type;
2009
pParameters->Frequency = ext_parameters.Frequency;
2010
pParameters->OneOverQ = ext_parameters.OneOverQ;
2011
}
2012
2013
void FAudioVoice_GetVolume(
2014
FAudioVoice *voice,
2015
float *pVolume
2016
) {
2017
LOG_API_ENTER(voice->audio)
2018
*pVolume = voice->volume;
2019
LOG_API_EXIT(voice->audio)
2020
}
2021
2022
uint32_t FAudioVoice_SetChannelVolumes(
2023
FAudioVoice *voice,
2024
uint32_t Channels,
2025
const float *pVolumes,
2026
uint32_t OperationSet
2027
) {
2028
uint32_t i;
2029
2030
LOG_API_ENTER(voice->audio)
2031
2032
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
2033
{
2034
FAudio_OPERATIONSET_QueueSetChannelVolumes(
2035
voice,
2036
Channels,
2037
pVolumes,
2038
OperationSet
2039
);
2040
LOG_API_EXIT(voice->audio)
2041
return 0;
2042
}
2043
2044
if (pVolumes == NULL)
2045
{
2046
LOG_API_EXIT(voice->audio)
2047
return FAUDIO_E_INVALID_CALL;
2048
}
2049
2050
if (voice->type == FAUDIO_VOICE_MASTER)
2051
{
2052
LOG_API_EXIT(voice->audio)
2053
return FAUDIO_E_INVALID_CALL;
2054
}
2055
2056
if (voice->audio->version > 7 && Channels != voice->outputChannels)
2057
{
2058
LOG_API_EXIT(voice->audio)
2059
return FAUDIO_E_INVALID_CALL;
2060
}
2061
2062
FAudio_PlatformLockMutex(voice->sendLock);
2063
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
2064
2065
FAudio_PlatformLockMutex(voice->volumeLock);
2066
LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
2067
2068
FAudio_memcpy(
2069
voice->channelVolume,
2070
pVolumes,
2071
sizeof(float) * Channels
2072
);
2073
2074
for (i = 0; i < voice->sends.SendCount; i += 1)
2075
{
2076
FAudio_RecalcMixMatrix(voice, i);
2077
}
2078
2079
FAudio_PlatformUnlockMutex(voice->volumeLock);
2080
LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
2081
2082
FAudio_PlatformUnlockMutex(voice->sendLock);
2083
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
2084
2085
LOG_API_EXIT(voice->audio)
2086
return 0;
2087
}
2088
2089
void FAudioVoice_GetChannelVolumes(
2090
FAudioVoice *voice,
2091
uint32_t Channels,
2092
float *pVolumes
2093
) {
2094
LOG_API_ENTER(voice->audio)
2095
FAudio_PlatformLockMutex(voice->volumeLock);
2096
LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
2097
FAudio_memcpy(
2098
pVolumes,
2099
voice->channelVolume,
2100
sizeof(float) * Channels
2101
);
2102
FAudio_PlatformUnlockMutex(voice->volumeLock);
2103
LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
2104
LOG_API_EXIT(voice->audio)
2105
}
2106
2107
uint32_t FAudioVoice_SetOutputMatrix(
2108
FAudioVoice *voice,
2109
FAudioVoice *pDestinationVoice,
2110
uint32_t SourceChannels,
2111
uint32_t DestinationChannels,
2112
const float *pLevelMatrix,
2113
uint32_t OperationSet
2114
) {
2115
uint32_t i, result = 0;
2116
LOG_API_ENTER(voice->audio)
2117
2118
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
2119
{
2120
FAudio_OPERATIONSET_QueueSetOutputMatrix(
2121
voice,
2122
pDestinationVoice,
2123
SourceChannels,
2124
DestinationChannels,
2125
pLevelMatrix,
2126
OperationSet
2127
);
2128
LOG_API_EXIT(voice->audio)
2129
return 0;
2130
}
2131
2132
FAudio_PlatformLockMutex(voice->sendLock);
2133
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
2134
2135
/* Find the send index */
2136
if (pDestinationVoice == NULL && voice->sends.SendCount == 1)
2137
{
2138
pDestinationVoice = voice->sends.pSends[0].pOutputVoice;
2139
}
2140
FAudio_assert(pDestinationVoice != NULL);
2141
for (i = 0; i < voice->sends.SendCount; i += 1)
2142
{
2143
if (pDestinationVoice == voice->sends.pSends[i].pOutputVoice)
2144
{
2145
break;
2146
}
2147
}
2148
if (i >= voice->sends.SendCount)
2149
{
2150
LOG_ERROR(
2151
voice->audio,
2152
"Destination not attached to source: %p %p",
2153
(void*) voice,
2154
(void*) pDestinationVoice
2155
)
2156
result = FAUDIO_E_INVALID_CALL;
2157
goto end;
2158
}
2159
2160
/* Verify the Source/Destination channel count */
2161
if (SourceChannels != voice->outputChannels)
2162
{
2163
LOG_ERROR(
2164
voice->audio,
2165
"SourceChannels not equal to voice channel count: %p %d %d",
2166
(void*) voice,
2167
SourceChannels,
2168
voice->outputChannels
2169
)
2170
result = FAUDIO_E_INVALID_CALL;
2171
goto end;
2172
}
2173
2174
if (pDestinationVoice->type == FAUDIO_VOICE_MASTER)
2175
{
2176
if (DestinationChannels != pDestinationVoice->master.inputChannels)
2177
{
2178
LOG_ERROR(
2179
voice->audio,
2180
"DestinationChannels not equal to master channel count: %p %d %d",
2181
(void*) pDestinationVoice,
2182
DestinationChannels,
2183
pDestinationVoice->master.inputChannels
2184
)
2185
result = FAUDIO_E_INVALID_CALL;
2186
goto end;
2187
}
2188
}
2189
else
2190
{
2191
if (DestinationChannels != pDestinationVoice->mix.inputChannels)
2192
{
2193
LOG_ERROR(
2194
voice->audio,
2195
"DestinationChannels not equal to submix channel count: %p %d %d",
2196
(void*) pDestinationVoice,
2197
DestinationChannels,
2198
pDestinationVoice->mix.inputChannels
2199
)
2200
result = FAUDIO_E_INVALID_CALL;
2201
goto end;
2202
}
2203
}
2204
2205
/* Set the matrix values, finally */
2206
FAudio_PlatformLockMutex(voice->volumeLock);
2207
LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
2208
2209
FAudio_memcpy(
2210
voice->sendCoefficients[i],
2211
pLevelMatrix,
2212
sizeof(float) * SourceChannels * DestinationChannels
2213
);
2214
2215
FAudio_RecalcMixMatrix(voice, i);
2216
2217
FAudio_PlatformUnlockMutex(voice->volumeLock);
2218
LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
2219
2220
end:
2221
FAudio_PlatformUnlockMutex(voice->sendLock);
2222
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
2223
LOG_API_EXIT(voice->audio)
2224
return result;
2225
}
2226
2227
void FAudioVoice_GetOutputMatrix(
2228
FAudioVoice *voice,
2229
FAudioVoice *pDestinationVoice,
2230
uint32_t SourceChannels,
2231
uint32_t DestinationChannels,
2232
float *pLevelMatrix
2233
) {
2234
uint32_t i;
2235
2236
LOG_API_ENTER(voice->audio)
2237
FAudio_PlatformLockMutex(voice->sendLock);
2238
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
2239
2240
/* Find the send index */
2241
for (i = 0; i < voice->sends.SendCount; i += 1)
2242
{
2243
if (pDestinationVoice == voice->sends.pSends[i].pOutputVoice)
2244
{
2245
break;
2246
}
2247
}
2248
if (i >= voice->sends.SendCount)
2249
{
2250
LOG_ERROR(
2251
voice->audio,
2252
"Destination not attached to source: %p %p",
2253
(void*) voice,
2254
(void*) pDestinationVoice
2255
)
2256
FAudio_PlatformUnlockMutex(voice->sendLock);
2257
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
2258
LOG_API_EXIT(voice->audio)
2259
return;
2260
}
2261
2262
/* Verify the Source/Destination channel count */
2263
if (voice->type == FAUDIO_VOICE_SOURCE)
2264
{
2265
FAudio_assert(SourceChannels == voice->src.format->nChannels);
2266
}
2267
else
2268
{
2269
FAudio_assert(SourceChannels == voice->mix.inputChannels);
2270
}
2271
if (pDestinationVoice->type == FAUDIO_VOICE_MASTER)
2272
{
2273
FAudio_assert(DestinationChannels == pDestinationVoice->master.inputChannels);
2274
}
2275
else
2276
{
2277
FAudio_assert(DestinationChannels == pDestinationVoice->mix.inputChannels);
2278
}
2279
2280
/* Get the matrix values, finally */
2281
FAudio_memcpy(
2282
pLevelMatrix,
2283
voice->sendCoefficients[i],
2284
sizeof(float) * SourceChannels * DestinationChannels
2285
);
2286
2287
FAudio_PlatformUnlockMutex(voice->sendLock);
2288
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
2289
LOG_API_EXIT(voice->audio)
2290
}
2291
2292
static uint32_t check_for_sends_to_voice(FAudioVoice *voice)
2293
{
2294
FAudio *audio = voice->audio;
2295
uint32_t ret = 0;
2296
FAudioSourceVoice *source;
2297
FAudioSubmixVoice *submix;
2298
LinkedList *list;
2299
uint32_t i;
2300
2301
FAudio_PlatformLockMutex(audio->sourceLock);
2302
list = audio->sources;
2303
while (list != NULL)
2304
{
2305
source = (FAudioSourceVoice*) list->entry;
2306
for (i = 0; i < source->sends.SendCount; i += 1)
2307
if (source->sends.pSends[i].pOutputVoice == voice)
2308
{
2309
ret = 0x80004005; /* E_FAIL */
2310
break;
2311
}
2312
if (ret)
2313
break;
2314
list = list->next;
2315
}
2316
FAudio_PlatformUnlockMutex(audio->sourceLock);
2317
2318
if (ret)
2319
return ret;
2320
2321
FAudio_PlatformLockMutex(audio->submixLock);
2322
list = audio->submixes;
2323
while (list != NULL)
2324
{
2325
submix = (FAudioSubmixVoice*) list->entry;
2326
for (i = 0; i < submix->sends.SendCount; i += 1)
2327
if (submix->sends.pSends[i].pOutputVoice == voice)
2328
{
2329
ret = 0x80004005; /* E_FAIL */
2330
break;
2331
}
2332
if (ret)
2333
break;
2334
list = list->next;
2335
}
2336
FAudio_PlatformUnlockMutex(audio->submixLock);
2337
2338
return ret;
2339
}
2340
2341
static void destroy_voice(FAudioVoice *voice)
2342
{
2343
uint32_t i;
2344
2345
/* TODO: Check for dependencies and remove from audio graph first! */
2346
FAudio_OPERATIONSET_ClearAllForVoice(voice);
2347
2348
if (voice->type == FAUDIO_VOICE_SOURCE)
2349
{
2350
FAudioBufferEntry *entry, *next;
2351
2352
#ifdef FAUDIO_DUMP_VOICES
2353
FAudio_DUMPVOICE_Finalize((FAudioSourceVoice*) voice);
2354
#endif /* FAUDIO_DUMP_VOICES */
2355
2356
FAudio_PlatformLockMutex(voice->audio->sourceLock);
2357
LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
2358
while (voice == voice->audio->processingSource)
2359
{
2360
FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
2361
LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
2362
FAudio_PlatformLockMutex(voice->audio->sourceLock);
2363
LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
2364
}
2365
LinkedList_RemoveEntry(
2366
&voice->audio->sources,
2367
voice,
2368
voice->audio->sourceLock,
2369
voice->audio->pFree
2370
);
2371
FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
2372
LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
2373
2374
entry = voice->src.bufferList;
2375
while (entry != NULL)
2376
{
2377
next = entry->next;
2378
voice->audio->pFree(entry);
2379
entry = next;
2380
}
2381
2382
entry = voice->src.flushList;
2383
while (entry != NULL)
2384
{
2385
next = entry->next;
2386
voice->audio->pFree(entry);
2387
entry = next;
2388
}
2389
2390
voice->audio->pFree(voice->src.format);
2391
LOG_MUTEX_DESTROY(voice->audio, voice->src.bufferLock)
2392
FAudio_PlatformDestroyMutex(voice->src.bufferLock);
2393
#ifdef HAVE_WMADEC
2394
if (voice->src.wmadec)
2395
{
2396
FAudio_WMADEC_free(voice);
2397
}
2398
#endif /* HAVE_WMADEC */
2399
}
2400
else if (voice->type == FAUDIO_VOICE_SUBMIX)
2401
{
2402
/* Remove submix from list */
2403
LinkedList_RemoveEntry(
2404
&voice->audio->submixes,
2405
voice,
2406
voice->audio->submixLock,
2407
voice->audio->pFree
2408
);
2409
2410
/* Delete submix data */
2411
voice->audio->pFree(voice->mix.inputCache);
2412
}
2413
else if (voice->type == FAUDIO_VOICE_MASTER)
2414
{
2415
if (voice->audio->platform != NULL)
2416
{
2417
FAudio_PlatformQuit(voice->audio->platform);
2418
voice->audio->platform = NULL;
2419
}
2420
if (voice->master.effectCache != NULL)
2421
{
2422
voice->audio->pFree(voice->master.effectCache);
2423
}
2424
voice->audio->master = NULL;
2425
}
2426
2427
if (voice->sendLock != NULL)
2428
{
2429
FAudio_PlatformLockMutex(voice->sendLock);
2430
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
2431
for (i = 0; i < voice->sends.SendCount; i += 1)
2432
{
2433
voice->audio->pFree(voice->sendCoefficients[i]);
2434
}
2435
if (voice->sendCoefficients != NULL)
2436
{
2437
voice->audio->pFree(voice->sendCoefficients);
2438
}
2439
for (i = 0; i < voice->sends.SendCount; i += 1)
2440
{
2441
voice->audio->pFree(voice->mixCoefficients[i]);
2442
}
2443
if (voice->mixCoefficients != NULL)
2444
{
2445
voice->audio->pFree(voice->mixCoefficients);
2446
}
2447
if (voice->sendMix != NULL)
2448
{
2449
voice->audio->pFree(voice->sendMix);
2450
}
2451
if (voice->sendFilter != NULL)
2452
{
2453
voice->audio->pFree(voice->sendFilter);
2454
}
2455
if (voice->sendFilterState != NULL)
2456
{
2457
for (i = 0; i < voice->sends.SendCount; i += 1)
2458
{
2459
if (voice->sendFilterState[i] != NULL)
2460
{
2461
voice->audio->pFree(voice->sendFilterState[i]);
2462
}
2463
}
2464
voice->audio->pFree(voice->sendFilterState);
2465
}
2466
if (voice->sends.pSends != NULL)
2467
{
2468
voice->audio->pFree(voice->sends.pSends);
2469
}
2470
FAudio_PlatformUnlockMutex(voice->sendLock);
2471
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
2472
LOG_MUTEX_DESTROY(voice->audio, voice->sendLock)
2473
FAudio_PlatformDestroyMutex(voice->sendLock);
2474
}
2475
2476
if (voice->effectLock != NULL)
2477
{
2478
FAudio_PlatformLockMutex(voice->effectLock);
2479
LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
2480
FAudio_INTERNAL_FreeEffectChain(voice);
2481
FAudio_PlatformUnlockMutex(voice->effectLock);
2482
LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
2483
LOG_MUTEX_DESTROY(voice->audio, voice->effectLock)
2484
FAudio_PlatformDestroyMutex(voice->effectLock);
2485
}
2486
2487
if (voice->filterLock != NULL)
2488
{
2489
FAudio_PlatformLockMutex(voice->filterLock);
2490
LOG_MUTEX_LOCK(voice->audio, voice->filterLock)
2491
if (voice->filterState != NULL)
2492
{
2493
voice->audio->pFree(voice->filterState);
2494
}
2495
FAudio_PlatformUnlockMutex(voice->filterLock);
2496
LOG_MUTEX_UNLOCK(voice->audio, voice->filterLock)
2497
LOG_MUTEX_DESTROY(voice->audio, voice->filterLock)
2498
FAudio_PlatformDestroyMutex(voice->filterLock);
2499
}
2500
2501
if (voice->volumeLock != NULL)
2502
{
2503
FAudio_PlatformLockMutex(voice->volumeLock);
2504
LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
2505
if (voice->channelVolume != NULL)
2506
{
2507
voice->audio->pFree(voice->channelVolume);
2508
}
2509
FAudio_PlatformUnlockMutex(voice->volumeLock);
2510
LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
2511
LOG_MUTEX_DESTROY(voice->audio, voice->volumeLock)
2512
FAudio_PlatformDestroyMutex(voice->volumeLock);
2513
}
2514
2515
voice->audio->pFree(voice);
2516
}
2517
2518
uint32_t FAudioVoice_DestroyVoiceSafeEXT(FAudioVoice *voice)
2519
{
2520
uint32_t ret;
2521
2522
FAudio* audio = voice->audio;
2523
2524
LOG_API_ENTER(audio)
2525
2526
if ((ret = check_for_sends_to_voice(voice)))
2527
{
2528
LOG_ERROR(
2529
audio,
2530
"Voice %p is an output for other voice(s)",
2531
voice
2532
)
2533
LOG_API_EXIT(audio)
2534
return ret;
2535
}
2536
destroy_voice(voice);
2537
LOG_API_EXIT(audio)
2538
return 0;
2539
}
2540
2541
void FAudioVoice_DestroyVoice(FAudioVoice *voice)
2542
{
2543
FAudioVoice_DestroyVoiceSafeEXT(voice);
2544
}
2545
2546
/* FAudioSourceVoice Interface */
2547
2548
uint32_t FAudioSourceVoice_Start(
2549
FAudioSourceVoice *voice,
2550
uint32_t Flags,
2551
uint32_t OperationSet
2552
) {
2553
LOG_API_ENTER(voice->audio)
2554
2555
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
2556
{
2557
FAudio_OPERATIONSET_QueueStart(
2558
voice,
2559
Flags,
2560
OperationSet
2561
);
2562
LOG_API_EXIT(voice->audio)
2563
return 0;
2564
}
2565
2566
2567
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2568
2569
FAudio_assert(Flags == 0);
2570
voice->src.active = 1;
2571
LOG_API_EXIT(voice->audio)
2572
return 0;
2573
}
2574
2575
uint32_t FAudioSourceVoice_Stop(
2576
FAudioSourceVoice *voice,
2577
uint32_t Flags,
2578
uint32_t OperationSet
2579
) {
2580
LOG_API_ENTER(voice->audio)
2581
2582
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
2583
{
2584
FAudio_OPERATIONSET_QueueStop(
2585
voice,
2586
Flags,
2587
OperationSet
2588
);
2589
LOG_API_EXIT(voice->audio)
2590
return 0;
2591
}
2592
2593
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2594
2595
if (Flags & FAUDIO_PLAY_TAILS)
2596
{
2597
voice->src.active = 2;
2598
}
2599
else
2600
{
2601
voice->src.active = 0;
2602
}
2603
LOG_API_EXIT(voice->audio)
2604
return 0;
2605
}
2606
2607
uint32_t FAudioSourceVoice_SubmitSourceBuffer(
2608
FAudioSourceVoice *voice,
2609
const FAudioBuffer *pBuffer,
2610
const FAudioBufferWMA *pBufferWMA
2611
) {
2612
uint32_t adpcmMask, *adpcmByteCount;
2613
uint32_t playBegin, playLength, loopBegin, loopLength, bufferLength;
2614
FAudioBufferEntry *entry, *list;
2615
2616
LOG_API_ENTER(voice->audio)
2617
LOG_INFO(
2618
voice->audio,
2619
"%p: {Flags: 0x%x, AudioBytes: %u, pAudioData: %p, Play: %u + %u, Loop: %u + %u x %u}",
2620
(void*) voice,
2621
pBuffer->Flags,
2622
pBuffer->AudioBytes,
2623
(const void*) pBuffer->pAudioData,
2624
pBuffer->PlayBegin,
2625
pBuffer->PlayLength,
2626
pBuffer->LoopBegin,
2627
pBuffer->LoopLength,
2628
pBuffer->LoopCount
2629
)
2630
2631
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2632
#ifdef HAVE_WMADEC
2633
FAudio_assert( (voice->src.wmadec != NULL && (pBufferWMA != NULL ||
2634
(voice->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2 ||
2635
voice->src.format->wFormatTag == FAUDIO_FORMAT_EXTENSIBLE))) ||
2636
(voice->src.wmadec == NULL && (pBufferWMA == NULL && voice->src.format->wFormatTag != FAUDIO_FORMAT_XMAUDIO2)) );
2637
#endif /* HAVE_WMADEC */
2638
2639
/* Start off with whatever they just sent us... */
2640
playBegin = pBuffer->PlayBegin;
2641
playLength = pBuffer->PlayLength;
2642
loopBegin = pBuffer->LoopBegin;
2643
loopLength = pBuffer->LoopLength;
2644
2645
/* "LoopBegin/LoopLength must be zero if LoopCount is 0" */
2646
if (pBuffer->LoopCount == 0 && (loopBegin > 0 || loopLength > 0))
2647
{
2648
LOG_API_EXIT(voice->audio)
2649
return FAUDIO_E_INVALID_CALL;
2650
}
2651
2652
if (voice->src.format->wFormatTag == FAUDIO_FORMAT_MSADPCM)
2653
{
2654
FAudioADPCMWaveFormat *fmtex = (FAudioADPCMWaveFormat*) voice->src.format;
2655
bufferLength =
2656
pBuffer->AudioBytes /
2657
fmtex->wfx.nBlockAlign *
2658
fmtex->wSamplesPerBlock;
2659
}
2660
else if (voice->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
2661
{
2662
FAudioXMA2WaveFormat *fmtex = (FAudioXMA2WaveFormat*) voice->src.format;
2663
bufferLength = fmtex->dwSamplesEncoded;
2664
}
2665
else if (pBufferWMA != NULL)
2666
{
2667
bufferLength =
2668
pBufferWMA->pDecodedPacketCumulativeBytes[pBufferWMA->PacketCount - 1] /
2669
(voice->src.format->nChannels * voice->src.format->wBitsPerSample / 8);
2670
}
2671
else
2672
{
2673
bufferLength =
2674
pBuffer->AudioBytes /
2675
voice->src.format->nBlockAlign;
2676
}
2677
2678
/* PlayLength Default */
2679
if (playLength == 0)
2680
{
2681
playLength = bufferLength - playBegin;
2682
}
2683
else if (playBegin + playLength > bufferLength || playBegin + playLength < playLength)
2684
{
2685
/* Reading past the end of the buffer, or begin + length overflow uint32_t, which
2686
* would also read past the end of the buffer. */
2687
LOG_API_EXIT(voice->audio)
2688
return FAUDIO_E_INVALID_CALL;
2689
}
2690
2691
if (pBuffer->LoopCount > 0 && pBufferWMA == NULL && voice->src.format->wFormatTag != FAUDIO_FORMAT_XMAUDIO2)
2692
{
2693
/* "The value of LoopBegin must be less than PlayBegin + PlayLength" */
2694
if (loopBegin >= (playBegin + playLength))
2695
{
2696
LOG_API_EXIT(voice->audio)
2697
return FAUDIO_E_INVALID_CALL;
2698
}
2699
2700
/* LoopLength Default */
2701
if (loopLength == 0)
2702
{
2703
loopLength = playBegin + playLength - loopBegin;
2704
}
2705
2706
/* "The value of LoopBegin + LoopLength must be greater than PlayBegin
2707
* and less than PlayBegin + PlayLength"
2708
*/
2709
if ( voice->audio->version > 7 && (
2710
(loopBegin + loopLength) <= playBegin ||
2711
(loopBegin + loopLength) > (playBegin + playLength)) )
2712
{
2713
LOG_API_EXIT(voice->audio)
2714
return FAUDIO_E_INVALID_CALL;
2715
}
2716
}
2717
2718
/* For ADPCM, round down to the nearest sample block size */
2719
if (voice->src.format->wFormatTag == FAUDIO_FORMAT_MSADPCM)
2720
{
2721
adpcmMask = ((FAudioADPCMWaveFormat*) voice->src.format)->wSamplesPerBlock;
2722
playBegin -= playBegin % adpcmMask;
2723
playLength -= playLength % adpcmMask;
2724
loopBegin -= loopBegin % adpcmMask;
2725
loopLength -= loopLength % adpcmMask;
2726
2727
/* This is basically a const_cast... */
2728
adpcmByteCount = (uint32_t*) &pBuffer->AudioBytes;
2729
*adpcmByteCount = (
2730
pBuffer->AudioBytes / voice->src.format->nBlockAlign
2731
) * voice->src.format->nBlockAlign;
2732
}
2733
else if (pBufferWMA != NULL || voice->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
2734
{
2735
/* WMA only supports looping the whole buffer */
2736
loopBegin = 0;
2737
loopLength = playBegin + playLength;
2738
}
2739
2740
/* Allocate, now that we have valid input */
2741
entry = (FAudioBufferEntry*) voice->audio->pMalloc(sizeof(FAudioBufferEntry));
2742
FAudio_memcpy(&entry->buffer, pBuffer, sizeof(FAudioBuffer));
2743
entry->buffer.PlayBegin = playBegin;
2744
entry->buffer.PlayLength = playLength;
2745
entry->buffer.LoopBegin = loopBegin;
2746
entry->buffer.LoopLength = loopLength;
2747
if (pBufferWMA != NULL)
2748
{
2749
FAudio_memcpy(&entry->bufferWMA, pBufferWMA, sizeof(FAudioBufferWMA));
2750
}
2751
entry->next = NULL;
2752
2753
if ( voice->audio->version <= 7 && (
2754
entry->buffer.LoopCount > 0 &&
2755
entry->buffer.LoopBegin + entry->buffer.LoopLength <= entry->buffer.PlayBegin))
2756
{
2757
entry->buffer.LoopCount = 0;
2758
}
2759
2760
#ifdef FAUDIO_DUMP_VOICES
2761
/* dumping current buffer, append into "data" section */
2762
if (pBuffer->pAudioData != NULL && playLength > 0)
2763
{
2764
FAudio_DUMPVOICE_WriteBuffer(voice, pBuffer, pBufferWMA, playBegin, playLength);
2765
}
2766
#endif /* FAUDIO_DUMP_VOICES */
2767
2768
/* Submit! */
2769
FAudio_PlatformLockMutex(voice->src.bufferLock);
2770
LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
2771
if (voice->src.bufferList == NULL)
2772
{
2773
voice->src.bufferList = entry;
2774
voice->src.curBufferOffset = entry->buffer.PlayBegin;
2775
voice->src.newBuffer = 1;
2776
}
2777
else
2778
{
2779
list = voice->src.bufferList;
2780
while (list->next != NULL)
2781
{
2782
list = list->next;
2783
}
2784
list->next = entry;
2785
2786
/* For some bizarre reason we get scenarios where a buffer is freed, only to
2787
* have the allocator give us the exact same address and somehow get a single
2788
* buffer referencing itself. I don't even know.
2789
*/
2790
FAudio_assert(list != entry);
2791
}
2792
LOG_INFO(
2793
voice->audio,
2794
"%p: appended buffer %p",
2795
(void*) voice,
2796
(void*) &entry->buffer
2797
)
2798
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
2799
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
2800
LOG_API_EXIT(voice->audio)
2801
return 0;
2802
}
2803
2804
uint32_t FAudioSourceVoice_FlushSourceBuffers(
2805
FAudioSourceVoice *voice
2806
) {
2807
FAudioBufferEntry *entry, *latest;
2808
2809
LOG_API_ENTER(voice->audio)
2810
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2811
2812
FAudio_PlatformLockMutex(voice->src.bufferLock);
2813
LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
2814
2815
/* If the source is playing, don't flush the active buffer */
2816
entry = voice->src.bufferList;
2817
if ((voice->src.active == 1) && entry != NULL && !voice->src.newBuffer)
2818
{
2819
entry = entry->next;
2820
voice->src.bufferList->next = NULL;
2821
}
2822
else
2823
{
2824
voice->src.curBufferOffset = 0;
2825
voice->src.bufferList = NULL;
2826
voice->src.newBuffer = 0;
2827
}
2828
2829
/* Move them to the pending flush list */
2830
if (entry != NULL)
2831
{
2832
if (voice->src.flushList == NULL)
2833
{
2834
voice->src.flushList = entry;
2835
}
2836
else
2837
{
2838
latest = voice->src.flushList;
2839
while (latest->next != NULL)
2840
{
2841
latest = latest->next;
2842
}
2843
latest->next = entry;
2844
}
2845
}
2846
2847
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
2848
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
2849
LOG_API_EXIT(voice->audio)
2850
return 0;
2851
}
2852
2853
uint32_t FAudioSourceVoice_Discontinuity(
2854
FAudioSourceVoice *voice
2855
) {
2856
FAudioBufferEntry *buf;
2857
2858
LOG_API_ENTER(voice->audio)
2859
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2860
2861
FAudio_PlatformLockMutex(voice->src.bufferLock);
2862
LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
2863
2864
if (voice->src.bufferList != NULL)
2865
{
2866
for (buf = voice->src.bufferList; buf->next != NULL; buf = buf->next);
2867
buf->buffer.Flags |= FAUDIO_END_OF_STREAM;
2868
}
2869
2870
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
2871
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
2872
LOG_API_EXIT(voice->audio)
2873
return 0;
2874
}
2875
2876
uint32_t FAudioSourceVoice_ExitLoop(
2877
FAudioSourceVoice *voice,
2878
uint32_t OperationSet
2879
) {
2880
LOG_API_ENTER(voice->audio)
2881
2882
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
2883
{
2884
FAudio_OPERATIONSET_QueueExitLoop(
2885
voice,
2886
OperationSet
2887
);
2888
LOG_API_EXIT(voice->audio)
2889
return 0;
2890
}
2891
2892
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2893
2894
FAudio_PlatformLockMutex(voice->src.bufferLock);
2895
LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
2896
2897
if (voice->src.bufferList != NULL)
2898
{
2899
voice->src.bufferList->buffer.LoopCount = 0;
2900
}
2901
2902
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
2903
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
2904
LOG_API_EXIT(voice->audio)
2905
return 0;
2906
}
2907
2908
void FAudioSourceVoice_GetState(
2909
FAudioSourceVoice *voice,
2910
FAudioVoiceState *pVoiceState,
2911
uint32_t Flags
2912
) {
2913
FAudioBufferEntry *entry;
2914
2915
LOG_API_ENTER(voice->audio)
2916
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2917
2918
FAudio_PlatformLockMutex(voice->src.bufferLock);
2919
LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
2920
2921
if (!(Flags & FAUDIO_VOICE_NOSAMPLESPLAYED))
2922
{
2923
pVoiceState->SamplesPlayed = voice->src.totalSamples;
2924
}
2925
2926
pVoiceState->BuffersQueued = 0;
2927
pVoiceState->pCurrentBufferContext = NULL;
2928
if (voice->src.bufferList != NULL)
2929
{
2930
entry = voice->src.bufferList;
2931
if (!voice->src.newBuffer)
2932
{
2933
pVoiceState->pCurrentBufferContext = entry->buffer.pContext;
2934
}
2935
do
2936
{
2937
pVoiceState->BuffersQueued += 1;
2938
entry = entry->next;
2939
} while (entry != NULL);
2940
}
2941
2942
/* Pending flushed buffers also count */
2943
entry = voice->src.flushList;
2944
while (entry != NULL)
2945
{
2946
pVoiceState->BuffersQueued += 1;
2947
entry = entry->next;
2948
}
2949
2950
LOG_INFO(
2951
voice->audio,
2952
"-> {pCurrentBufferContext: %p, BuffersQueued: %u, SamplesPlayed: %"FAudio_PRIu64"}",
2953
pVoiceState->pCurrentBufferContext, pVoiceState->BuffersQueued,
2954
pVoiceState->SamplesPlayed
2955
)
2956
2957
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
2958
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
2959
LOG_API_EXIT(voice->audio)
2960
}
2961
2962
uint32_t FAudioSourceVoice_SetFrequencyRatio(
2963
FAudioSourceVoice *voice,
2964
float Ratio,
2965
uint32_t OperationSet
2966
) {
2967
LOG_API_ENTER(voice->audio)
2968
2969
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
2970
{
2971
FAudio_OPERATIONSET_QueueSetFrequencyRatio(
2972
voice,
2973
Ratio,
2974
OperationSet
2975
);
2976
LOG_API_EXIT(voice->audio)
2977
return 0;
2978
}
2979
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2980
2981
if (voice->flags & FAUDIO_VOICE_NOPITCH)
2982
{
2983
LOG_API_EXIT(voice->audio)
2984
return 0;
2985
}
2986
2987
voice->src.freqRatio = FAudio_clamp(
2988
Ratio,
2989
FAUDIO_MIN_FREQ_RATIO,
2990
voice->src.maxFreqRatio
2991
);
2992
LOG_API_EXIT(voice->audio)
2993
return 0;
2994
}
2995
2996
void FAudioSourceVoice_GetFrequencyRatio(
2997
FAudioSourceVoice *voice,
2998
float *pRatio
2999
) {
3000
LOG_API_ENTER(voice->audio)
3001
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
3002
3003
*pRatio = voice->src.freqRatio;
3004
LOG_API_EXIT(voice->audio)
3005
}
3006
3007
uint32_t FAudioSourceVoice_SetSourceSampleRate(
3008
FAudioSourceVoice *voice,
3009
uint32_t NewSourceSampleRate
3010
) {
3011
uint32_t outSampleRate;
3012
uint32_t newDecodeSamples, newResampleSamples;
3013
3014
LOG_API_ENTER(voice->audio)
3015
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
3016
FAudio_assert( NewSourceSampleRate >= FAUDIO_MIN_SAMPLE_RATE &&
3017
NewSourceSampleRate <= FAUDIO_MAX_SAMPLE_RATE );
3018
3019
FAudio_PlatformLockMutex(voice->src.bufferLock);
3020
LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
3021
if ( voice->audio->version > 7 &&
3022
voice->src.bufferList != NULL )
3023
{
3024
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
3025
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
3026
LOG_API_EXIT(voice->audio)
3027
return FAUDIO_E_INVALID_CALL;
3028
}
3029
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
3030
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
3031
3032
voice->src.format->nSamplesPerSec = NewSourceSampleRate;
3033
3034
/* Resize decode cache */
3035
newDecodeSamples = (uint32_t) FAudio_ceil(
3036
voice->audio->updateSize *
3037
(double) voice->src.maxFreqRatio *
3038
(double) NewSourceSampleRate /
3039
(double) voice->audio->master->master.inputSampleRate
3040
) + EXTRA_DECODE_PADDING * voice->src.format->nChannels;
3041
FAudio_INTERNAL_ResizeDecodeCache(
3042
voice->audio,
3043
(newDecodeSamples + EXTRA_DECODE_PADDING) * voice->src.format->nChannels
3044
);
3045
voice->src.decodeSamples = newDecodeSamples;
3046
3047
FAudio_PlatformLockMutex(voice->sendLock);
3048
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
3049
3050
if (voice->sends.SendCount == 0)
3051
{
3052
FAudio_PlatformUnlockMutex(voice->sendLock);
3053
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
3054
LOG_API_EXIT(voice->audio)
3055
return 0;
3056
}
3057
outSampleRate = voice->sends.pSends[0].pOutputVoice->type == FAUDIO_VOICE_MASTER ?
3058
voice->sends.pSends[0].pOutputVoice->master.inputSampleRate :
3059
voice->sends.pSends[0].pOutputVoice->mix.inputSampleRate;
3060
3061
newResampleSamples = (uint32_t) (FAudio_ceil(
3062
(double) voice->audio->updateSize *
3063
(double) outSampleRate /
3064
(double) voice->audio->master->master.inputSampleRate
3065
));
3066
voice->src.resampleSamples = newResampleSamples;
3067
3068
FAudio_PlatformUnlockMutex(voice->sendLock);
3069
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
3070
3071
LOG_API_EXIT(voice->audio)
3072
return 0;
3073
}
3074
3075
/* FAudioMasteringVoice Interface */
3076
3077
FAUDIOAPI uint32_t FAudioMasteringVoice_GetChannelMask(
3078
FAudioMasteringVoice *voice,
3079
uint32_t *pChannelMask
3080
) {
3081
LOG_API_ENTER(voice->audio)
3082
FAudio_assert(voice->type == FAUDIO_VOICE_MASTER);
3083
FAudio_assert(pChannelMask != NULL);
3084
3085
*pChannelMask = voice->audio->mixFormat.dwChannelMask;
3086
LOG_API_EXIT(voice->audio)
3087
return 0;
3088
}
3089
3090
#ifdef FAUDIO_DUMP_VOICES
3091
3092
static inline FAudioIOStreamOut *DumpVoices_fopen(
3093
const FAudioSourceVoice *voice,
3094
const FAudioWaveFormatEx *format,
3095
const char *mode,
3096
const char *ext
3097
) {
3098
char loc[64];
3099
uint16_t format_tag = format->wFormatTag;
3100
uint16_t format_ex_tag = 0;
3101
if (format->wFormatTag == FAUDIO_FORMAT_EXTENSIBLE)
3102
{
3103
/* get the GUID of the extended subformat */
3104
const FAudioWaveFormatExtensible *format_ex =
3105
(const FAudioWaveFormatExtensible*) format;
3106
format_ex_tag = (uint16_t) (format_ex->SubFormat.Data1);
3107
}
3108
FAudio_snprintf(
3109
loc,
3110
sizeof(loc),
3111
"FA_fmt_0x%04X_0x%04X_0x%016lX%s.wav",
3112
format_tag,
3113
format_ex_tag,
3114
(uint64_t) voice,
3115
ext
3116
);
3117
FAudioIOStreamOut *fileOut = FAudio_fopen_out(loc, mode);
3118
return fileOut;
3119
}
3120
3121
static inline void DumpVoices_finalize_section(
3122
const FAudioSourceVoice *voice,
3123
const FAudioWaveFormatEx *format,
3124
const char *section /* one of "data" or "dpds" */
3125
) {
3126
/* data file only contains the real data bytes */
3127
FAudioIOStreamOut *io_data = DumpVoices_fopen(voice, format, "rb", section);
3128
if (!io_data)
3129
{
3130
return;
3131
}
3132
FAudio_PlatformLockMutex((FAudioMutex) io_data->lock);
3133
size_t file_size_data = io_data->size(io_data->data);
3134
if (file_size_data == 0)
3135
{
3136
/* nothing to do */
3137
/* close data file */
3138
FAudio_PlatformUnlockMutex((FAudioMutex) io_data->lock);
3139
FAudio_close_out(io_data);
3140
return;
3141
}
3142
3143
/* we got some data: append data section to main file */
3144
FAudioIOStreamOut *io = DumpVoices_fopen(voice, format, "ab", "");
3145
if (!io)
3146
{
3147
/* close data file */
3148
FAudio_PlatformUnlockMutex((FAudioMutex) io_data->lock);
3149
FAudio_close_out(io_data);
3150
return;
3151
}
3152
3153
/* data sub-chunk - 8 bytes + data */
3154
/* SubChunk2ID - 4 --> "data" or "dpds" */
3155
io->write(io->data, section, 4, 1);
3156
/* Subchunk2Size - 4 */
3157
uint32_t chunk_size = (uint32_t)file_size_data;
3158
io->write(io->data, &chunk_size, 4, 1);
3159
/* data */
3160
/* fill in data bytes */
3161
uint8_t buffer[1024*1024];
3162
size_t count;
3163
while((count = io_data->read(io_data->data, (void*) buffer, 1, 1024*1024)) > 0)
3164
{
3165
io->write(io->data, (void*) buffer, 1, count);
3166
}
3167
3168
/* close data file */
3169
FAudio_PlatformUnlockMutex((FAudioMutex) io_data->lock);
3170
FAudio_close_out(io_data);
3171
/* close main file */
3172
FAudio_PlatformUnlockMutex((FAudioMutex) io->lock);
3173
FAudio_close_out(io);
3174
}
3175
3176
static void FAudio_DUMPVOICE_Init(const FAudioSourceVoice *voice)
3177
{
3178
const FAudioWaveFormatEx *format = voice->src.format;
3179
3180
FAudioIOStreamOut *io = DumpVoices_fopen(voice, format, "wb", "");
3181
if (!io)
3182
{
3183
return;
3184
}
3185
FAudio_PlatformLockMutex((FAudioMutex) io->lock);
3186
/* another GREAT ressource
3187
* https://wiki.multimedia.cx/index.php/Microsoft_xWMA
3188
*/
3189
3190
3191
/* wave file format taken from
3192
* http://soundfile.sapp.org/doc/WaveFormat
3193
* https://sites.google.com/site/musicgapi/technical-documents/wav-file-format
3194
* |52 49|46 46|52 4A|02 00|
3195
* |c1 sz|af|nc|sp rt|bt rt|
3196
* |ba|bs|da ta|c2 sz|
3197
3198
* | R I F F |chunk size |W A V E |f m t |
3199
* 19026
3200
* | 52 49 46 46 52 4A 02 00 57 41 56 45 66 6D 74 20 | RIFFRJ..WAVEfmt
3201
3202
* | subchnk size|fmt |nChan |samplerate |byte rate |
3203
* | 50 | 2 |2 |11025 |11289 |
3204
* | 32 00 00 00 02 00 02 00 11 2B 00 00 19 2C 00 00 | 2........+...,..
3205
3206
* |blkaln|bps |efmt |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|
3207
* | 512 |4 |32 |500 |7 |256 |0 |512 |
3208
* | 512 |4 |32 |459252 |256 |
3209
* | 00 02|04 00 20 00 F4 01 07 00 00 01 00 00 00 02 | .... .ô.........
3210
3211
* | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
3212
* |
3213
* | 00 FF 00 00 00 00 C0 00 40 00 F0 00 00 00 CC 01 | .ÿ....À.@.ð...Ì.
3214
3215
* | XXXXXXXXXXXXXXXXXX|d a t a |chunk size |XXXXX |
3216
* | | |18944 | |
3217
* | 30 FF 88 01 18 FF 64 61 74 61 00 4A 02 00 00 00 | 0ÿ...ÿdata.J....
3218
*/
3219
3220
uint16_t cbSize = format->cbSize;
3221
const char *formatFourcc = "WAVE";
3222
uint16_t wFormatTag = format->wFormatTag;
3223
/* special handling for WMAUDIO2 */
3224
if (wFormatTag == FAUDIO_FORMAT_EXTENSIBLE && cbSize >= 22)
3225
{
3226
const FAudioWaveFormatExtensible *format_ex =
3227
(const FAudioWaveFormatExtensible*) format;
3228
uint16_t format_ex_tag = (uint16_t) (format_ex->SubFormat.Data1);
3229
if (format_ex_tag == FAUDIO_FORMAT_WMAUDIO2)
3230
{
3231
cbSize = 0;
3232
formatFourcc = "XWMA";
3233
wFormatTag = FAUDIO_FORMAT_WMAUDIO2;
3234
}
3235
}
3236
3237
{ /* RIFF chunk descriptor - 12 byte */
3238
/* ChunkID - 4 */
3239
io->write(io->data, "RIFF", 4, 1);
3240
/* ChunkSize - 4 */
3241
uint32_t filesize = 0; /* the real file size is written in finalize step */
3242
io->write(io->data, &filesize, 4, 1);
3243
/* Format - 4 */
3244
io->write(io->data, formatFourcc, 4, 1);
3245
}
3246
{ /* fmt sub-chunk 24 */
3247
/* Subchunk1ID - 4 */
3248
io->write(io->data, "fmt ", 4, 1);
3249
/* Subchunk1Size - 4 */
3250
/* 18 byte for WAVEFORMATEX and cbSize for WAVEFORMATEXTENDED */
3251
uint32_t chunk_data_size = 18 + (uint32_t) cbSize;
3252
io->write(io->data, &chunk_data_size, 4, 1);
3253
/* AudioFormat - 2 */
3254
io->write(io->data, &wFormatTag, 2, 1);
3255
/* NumChannels - 2 */
3256
io->write(io->data, &format->nChannels, 2, 1);
3257
/* SampleRate - 4 */
3258
io->write(io->data, &format->nSamplesPerSec, 4, 1);
3259
/* ByteRate - 4 */
3260
/* SampleRate * NumChannels * BitsPerSample/8 */
3261
io->write(io->data, &format->nAvgBytesPerSec, 4, 1);
3262
/* BlockAlign - 2 */
3263
/* NumChannels * BitsPerSample/8 */
3264
io->write(io->data, &format->nBlockAlign, 2, 1);
3265
/* BitsPerSample - 2 */
3266
io->write(io->data, &format->wBitsPerSample, 2, 1);
3267
}
3268
/* in case of extensible audio format write the additional data to the file */
3269
{
3270
/* always write the cbSize */
3271
io->write(io->data, &cbSize, 2, 1);
3272
3273
if (cbSize >= 22)
3274
{
3275
/* we have a WAVEFORMATEXTENSIBLE struct to write */
3276
const FAudioWaveFormatExtensible *format_ex =
3277
(const FAudioWaveFormatExtensible*) format;
3278
io->write(io->data, &format_ex->Samples.wValidBitsPerSample, 2, 1);
3279
io->write(io->data, &format_ex->dwChannelMask, 4, 1);
3280
/* write FAudioGUID */
3281
io->write(io->data, &format_ex->SubFormat.Data1, 4, 1);
3282
io->write(io->data, &format_ex->SubFormat.Data2, 2, 1);
3283
io->write(io->data, &format_ex->SubFormat.Data3, 2, 1);
3284
io->write(io->data, &format_ex->SubFormat.Data4, 1, 8);
3285
}
3286
if (format->cbSize > 22)
3287
{
3288
/* fill up the remaining cbSize bytes with zeros */
3289
uint8_t zero = 0;
3290
for (uint16_t i=23; i<=format->cbSize; i++)
3291
{
3292
io->write(io->data, &zero, 1, 1);
3293
}
3294
}
3295
}
3296
{ /* dpds sub-chunk - optional - 8 bytes + bufferWMA uint32_t samples */
3297
/* create file to hold the bufferWMA samples */
3298
FAudioIOStreamOut *io_dpds = DumpVoices_fopen(voice, format, "wb", "dpds");
3299
FAudio_close_out(io_dpds);
3300
/* io_dpds file will be filled by SubmitBuffer */
3301
}
3302
{ /* data sub-chunk - 8 bytes + data */
3303
/* create file to hold the data samples */
3304
FAudioIOStreamOut *io_data = DumpVoices_fopen(voice, format, "wb", "data");
3305
FAudio_close_out(io_data);
3306
/* io_data file will be filled by SubmitBuffer */
3307
}
3308
FAudio_PlatformUnlockMutex((FAudioMutex) io->lock);
3309
FAudio_close_out(io);
3310
}
3311
3312
static void FAudio_DUMPVOICE_Finalize(const FAudioSourceVoice *voice)
3313
{
3314
const FAudioWaveFormatEx *format = voice->src.format;
3315
3316
/* add dpds subchunk - optional */
3317
DumpVoices_finalize_section(voice, format, "dpds");
3318
/* add data subchunk */
3319
DumpVoices_finalize_section(voice, format, "data");
3320
3321
/* open main file to update filesize */
3322
FAudioIOStreamOut *io = DumpVoices_fopen(voice, format, "r+b", "");
3323
if (!io)
3324
{
3325
return;
3326
}
3327
FAudio_PlatformLockMutex((FAudioMutex) io->lock);
3328
size_t file_size = io->size(io->data);
3329
if (file_size >= 44)
3330
{
3331
/* update filesize */
3332
uint32_t chunk_size = (uint32_t)(file_size - 8);
3333
io->seek(io->data, 4, FAUDIO_SEEK_SET);
3334
io->write(io->data, &chunk_size, 4, 1);
3335
}
3336
FAudio_PlatformUnlockMutex((FAudioMutex) io->lock);
3337
FAudio_close_out(io);
3338
}
3339
3340
static void FAudio_DUMPVOICE_WriteBuffer(
3341
const FAudioSourceVoice *voice,
3342
const FAudioBuffer *pBuffer,
3343
const FAudioBufferWMA *pBufferWMA,
3344
const uint32_t playBegin,
3345
const uint32_t playLength
3346
) {
3347
FAudioIOStreamOut *io_data = DumpVoices_fopen(voice, voice->src.format, "ab", "data");
3348
if (io_data == NULL)
3349
{
3350
return;
3351
}
3352
3353
FAudio_PlatformLockMutex((FAudioMutex) io_data->lock);
3354
if (pBufferWMA != NULL)
3355
{
3356
/* dump encoded buffer contents */
3357
if (pBufferWMA->PacketCount > 0)
3358
{
3359
FAudioIOStreamOut *io_dpds = DumpVoices_fopen(voice, voice->src.format, "ab", "dpds");
3360
if (io_dpds)
3361
{
3362
FAudio_PlatformLockMutex((FAudioMutex) io_dpds->lock);
3363
/* write to dpds file */
3364
io_dpds->write(io_dpds->data, pBufferWMA->pDecodedPacketCumulativeBytes, sizeof(uint32_t), pBufferWMA->PacketCount);
3365
FAudio_PlatformUnlockMutex((FAudioMutex) io_dpds->lock);
3366
FAudio_close_out(io_dpds);
3367
}
3368
/* write buffer contents to data file */
3369
io_data->write(io_data->data, pBuffer->pAudioData, sizeof(uint8_t), pBuffer->AudioBytes);
3370
}
3371
}
3372
else
3373
{
3374
/* dump unencoded buffer contents */
3375
uint16_t bytesPerFrame = (voice->src.format->nChannels * voice->src.format->wBitsPerSample / 8);
3376
FAudio_assert(bytesPerFrame > 0);
3377
const void *pAudioDataBegin = pBuffer->pAudioData + playBegin*bytesPerFrame;
3378
io_data->write(io_data->data, pAudioDataBegin, bytesPerFrame, playLength);
3379
}
3380
FAudio_PlatformUnlockMutex((FAudioMutex) io_data->lock);
3381
FAudio_close_out(io_data);
3382
}
3383
3384
#endif /* FAUDIO_DUMP_VOICES */
3385
3386
/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
3387
3388