Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/faudio/src/FAudio.c
4389 views
1
/* FAudio - XAudio Reimplementation for FNA
2
*
3
* Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team
4
*
5
* This software is provided 'as-is', without any express or implied warranty.
6
* In no event will the authors be held liable for any damages arising from
7
* the use of this software.
8
*
9
* Permission is granted to anyone to use this software for any purpose,
10
* including commercial applications, and to alter it and redistribute it
11
* freely, subject to the following restrictions:
12
*
13
* 1. The origin of this software must not be misrepresented; you must not
14
* claim that you wrote the original software. If you use this software in a
15
* product, an acknowledgment in the product documentation would be
16
* appreciated but is not required.
17
*
18
* 2. Altered source versions must be plainly marked as such, and must not be
19
* misrepresented as being the original software.
20
*
21
* 3. This notice may not be removed or altered from any source distribution.
22
*
23
* Ethan "flibitijibibo" Lee <[email protected]>
24
*
25
*/
26
27
#include "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[EffectIndex] == NULL)
1618
{
1619
voice->effects.parameters[EffectIndex] = voice->audio->pMalloc(
1620
ParametersByteSize
1621
);
1622
voice->effects.parameterSizes[EffectIndex] = ParametersByteSize;
1623
}
1624
FAudio_PlatformLockMutex(voice->effectLock);
1625
LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
1626
if (voice->effects.parameterSizes[EffectIndex] < ParametersByteSize)
1627
{
1628
voice->effects.parameters[EffectIndex] = voice->audio->pRealloc(
1629
voice->effects.parameters[EffectIndex],
1630
ParametersByteSize
1631
);
1632
voice->effects.parameterSizes[EffectIndex] = ParametersByteSize;
1633
}
1634
FAudio_memcpy(
1635
voice->effects.parameters[EffectIndex],
1636
pParameters,
1637
ParametersByteSize
1638
);
1639
voice->effects.parameterUpdates[EffectIndex] = 1;
1640
FAudio_PlatformUnlockMutex(voice->effectLock);
1641
LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
1642
LOG_API_EXIT(voice->audio)
1643
return 0;
1644
}
1645
1646
uint32_t FAudioVoice_GetEffectParameters(
1647
FAudioVoice *voice,
1648
uint32_t EffectIndex,
1649
void *pParameters,
1650
uint32_t ParametersByteSize
1651
) {
1652
FAPO *fapo;
1653
LOG_API_ENTER(voice->audio)
1654
FAudio_PlatformLockMutex(voice->effectLock);
1655
LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
1656
fapo = voice->effects.desc[EffectIndex].pEffect;
1657
fapo->GetParameters(fapo, pParameters, ParametersByteSize);
1658
FAudio_PlatformUnlockMutex(voice->effectLock);
1659
LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
1660
LOG_API_EXIT(voice->audio)
1661
return 0;
1662
}
1663
1664
uint32_t FAudioVoice_SetFilterParametersEXT(
1665
FAudioVoice *voice,
1666
const FAudioFilterParametersEXT *pParameters,
1667
uint32_t OperationSet
1668
) {
1669
LOG_API_ENTER(voice->audio)
1670
1671
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
1672
{
1673
FAudio_OPERATIONSET_QueueSetFilterParameters(
1674
voice,
1675
pParameters,
1676
OperationSet
1677
);
1678
LOG_API_EXIT(voice->audio)
1679
return 0;
1680
}
1681
1682
/* MSDN: "This method is usable only on source and submix voices and
1683
* has no effect on mastering voices."
1684
*/
1685
if (voice->type == FAUDIO_VOICE_MASTER)
1686
{
1687
LOG_API_EXIT(voice->audio)
1688
return 0;
1689
}
1690
1691
if (!(voice->flags & FAUDIO_VOICE_USEFILTER))
1692
{
1693
LOG_API_EXIT(voice->audio)
1694
return 0;
1695
}
1696
1697
FAudio_PlatformLockMutex(voice->filterLock);
1698
LOG_MUTEX_LOCK(voice->audio, voice->filterLock)
1699
FAudio_memcpy(
1700
&voice->filter,
1701
pParameters,
1702
sizeof(FAudioFilterParametersEXT)
1703
);
1704
FAudio_PlatformUnlockMutex(voice->filterLock);
1705
LOG_MUTEX_UNLOCK(voice->audio, voice->filterLock)
1706
1707
LOG_API_EXIT(voice->audio)
1708
return 0;
1709
}
1710
1711
uint32_t FAudioVoice_SetFilterParameters(
1712
FAudioVoice* voice,
1713
const FAudioFilterParameters* pParameters,
1714
uint32_t OperationSet
1715
) {
1716
FAudioFilterParametersEXT ext_parameters;
1717
ext_parameters.Type = pParameters->Type;
1718
ext_parameters.OneOverQ = pParameters->OneOverQ;
1719
ext_parameters.Frequency = pParameters->Frequency;
1720
ext_parameters.WetDryMix = FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT;
1721
1722
return FAudioVoice_SetFilterParametersEXT(voice, &ext_parameters, OperationSet);
1723
}
1724
1725
void FAudioVoice_GetFilterParametersEXT(
1726
FAudioVoice *voice,
1727
FAudioFilterParametersEXT *pParameters
1728
) {
1729
LOG_API_ENTER(voice->audio)
1730
1731
/* MSDN: "This method is usable only on source and submix voices and
1732
* has no effect on mastering voices."
1733
*/
1734
if (voice->type == FAUDIO_VOICE_MASTER)
1735
{
1736
LOG_API_EXIT(voice->audio)
1737
return;
1738
}
1739
1740
if (!(voice->flags & FAUDIO_VOICE_USEFILTER))
1741
{
1742
LOG_API_EXIT(voice->audio)
1743
return;
1744
}
1745
1746
FAudio_PlatformLockMutex(voice->filterLock);
1747
LOG_MUTEX_LOCK(voice->audio, voice->filterLock)
1748
FAudio_memcpy(
1749
pParameters,
1750
&voice->filter,
1751
sizeof(FAudioFilterParametersEXT)
1752
);
1753
FAudio_PlatformUnlockMutex(voice->filterLock);
1754
LOG_MUTEX_UNLOCK(voice->audio, voice->filterLock)
1755
LOG_API_EXIT(voice->audio)
1756
}
1757
1758
void FAudioVoice_GetFilterParameters(
1759
FAudioVoice* voice,
1760
FAudioFilterParameters* pParameters
1761
) {
1762
FAudioFilterParametersEXT ext_parameters;
1763
ext_parameters.Type = pParameters->Type;
1764
ext_parameters.OneOverQ = pParameters->OneOverQ;
1765
ext_parameters.Frequency = pParameters->Frequency;
1766
ext_parameters.WetDryMix = FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT;
1767
1768
FAudioVoice_GetFilterParametersEXT(voice, &ext_parameters);
1769
1770
pParameters->Type = ext_parameters.Type;
1771
pParameters->Frequency = ext_parameters.Frequency;
1772
pParameters->OneOverQ = ext_parameters.OneOverQ;
1773
}
1774
1775
uint32_t FAudioVoice_SetOutputFilterParametersEXT(
1776
FAudioVoice *voice,
1777
FAudioVoice *pDestinationVoice,
1778
const FAudioFilterParametersEXT *pParameters,
1779
uint32_t OperationSet
1780
) {
1781
uint32_t i;
1782
LOG_API_ENTER(voice->audio)
1783
1784
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
1785
{
1786
FAudio_OPERATIONSET_QueueSetOutputFilterParameters(
1787
voice,
1788
pDestinationVoice,
1789
pParameters,
1790
OperationSet
1791
);
1792
LOG_API_EXIT(voice->audio)
1793
return 0;
1794
}
1795
1796
/* MSDN: "This method is usable only on source and submix voices and
1797
* has no effect on mastering voices."
1798
*/
1799
if (voice->type == FAUDIO_VOICE_MASTER)
1800
{
1801
LOG_API_EXIT(voice->audio)
1802
return 0;
1803
}
1804
1805
FAudio_PlatformLockMutex(voice->sendLock);
1806
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
1807
1808
/* Find the send index */
1809
if (pDestinationVoice == NULL && voice->sends.SendCount == 1)
1810
{
1811
pDestinationVoice = voice->sends.pSends[0].pOutputVoice;
1812
}
1813
for (i = 0; i < voice->sends.SendCount; i += 1)
1814
{
1815
if (pDestinationVoice == voice->sends.pSends[i].pOutputVoice)
1816
{
1817
break;
1818
}
1819
}
1820
if (i >= voice->sends.SendCount)
1821
{
1822
LOG_ERROR(
1823
voice->audio,
1824
"Destination not attached to source: %p %p",
1825
(void*) voice,
1826
(void*) pDestinationVoice
1827
)
1828
FAudio_PlatformUnlockMutex(voice->sendLock);
1829
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1830
LOG_API_EXIT(voice->audio)
1831
return FAUDIO_E_INVALID_CALL;
1832
}
1833
1834
if (!(voice->sends.pSends[i].Flags & FAUDIO_SEND_USEFILTER))
1835
{
1836
FAudio_PlatformUnlockMutex(voice->sendLock);
1837
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1838
LOG_API_EXIT(voice->audio)
1839
return 0;
1840
}
1841
1842
/* Set the filter parameters, finally. */
1843
FAudio_memcpy(
1844
&voice->sendFilter[i],
1845
pParameters,
1846
sizeof(FAudioFilterParametersEXT)
1847
);
1848
1849
FAudio_PlatformUnlockMutex(voice->sendLock);
1850
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1851
LOG_API_EXIT(voice->audio)
1852
return 0;
1853
}
1854
1855
uint32_t FAudioVoice_SetOutputFilterParameters(
1856
FAudioVoice* voice,
1857
FAudioVoice* pDestinationVoice,
1858
const FAudioFilterParameters* pParameters,
1859
uint32_t OperationSet
1860
) {
1861
FAudioFilterParametersEXT ext_parameters;
1862
ext_parameters.Type = pParameters->Type;
1863
ext_parameters.OneOverQ = pParameters->OneOverQ;
1864
ext_parameters.Frequency = pParameters->Frequency;
1865
ext_parameters.WetDryMix = FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT;
1866
1867
return FAudioVoice_SetOutputFilterParametersEXT(voice, pDestinationVoice, &ext_parameters, OperationSet);
1868
}
1869
1870
void FAudioVoice_GetOutputFilterParametersEXT(
1871
FAudioVoice *voice,
1872
FAudioVoice *pDestinationVoice,
1873
FAudioFilterParametersEXT *pParameters
1874
) {
1875
uint32_t i;
1876
1877
LOG_API_ENTER(voice->audio)
1878
1879
/* MSDN: "This method is usable only on source and submix voices and
1880
* has no effect on mastering voices."
1881
*/
1882
if (voice->type == FAUDIO_VOICE_MASTER)
1883
{
1884
LOG_API_EXIT(voice->audio)
1885
return;
1886
}
1887
1888
FAudio_PlatformLockMutex(voice->sendLock);
1889
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
1890
1891
/* Find the send index */
1892
if (pDestinationVoice == NULL && voice->sends.SendCount == 1)
1893
{
1894
pDestinationVoice = voice->sends.pSends[0].pOutputVoice;
1895
}
1896
for (i = 0; i < voice->sends.SendCount; i += 1)
1897
{
1898
if (pDestinationVoice == voice->sends.pSends[i].pOutputVoice)
1899
{
1900
break;
1901
}
1902
}
1903
if (i >= voice->sends.SendCount)
1904
{
1905
LOG_ERROR(
1906
voice->audio,
1907
"Destination not attached to source: %p %p",
1908
(void*) voice,
1909
(void*) pDestinationVoice
1910
)
1911
FAudio_PlatformUnlockMutex(voice->sendLock);
1912
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1913
LOG_API_EXIT(voice->audio)
1914
return;
1915
}
1916
1917
if (!(voice->sends.pSends[i].Flags & FAUDIO_SEND_USEFILTER))
1918
{
1919
FAudio_PlatformUnlockMutex(voice->sendLock);
1920
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1921
LOG_API_EXIT(voice->audio)
1922
return;
1923
}
1924
1925
/* Set the filter parameters, finally. */
1926
FAudio_memcpy(
1927
pParameters,
1928
&voice->sendFilter[i],
1929
sizeof(FAudioFilterParametersEXT)
1930
);
1931
1932
FAudio_PlatformUnlockMutex(voice->sendLock);
1933
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1934
LOG_API_EXIT(voice->audio)
1935
}
1936
1937
uint32_t FAudioVoice_SetVolume(
1938
FAudioVoice *voice,
1939
float Volume,
1940
uint32_t OperationSet
1941
) {
1942
uint32_t i;
1943
1944
LOG_API_ENTER(voice->audio)
1945
1946
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
1947
{
1948
FAudio_OPERATIONSET_QueueSetVolume(
1949
voice,
1950
Volume,
1951
OperationSet
1952
);
1953
LOG_API_EXIT(voice->audio)
1954
return 0;
1955
}
1956
1957
FAudio_PlatformLockMutex(voice->sendLock);
1958
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
1959
1960
FAudio_PlatformLockMutex(voice->volumeLock);
1961
LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
1962
1963
voice->volume = FAudio_clamp(
1964
Volume,
1965
-FAUDIO_MAX_VOLUME_LEVEL,
1966
FAUDIO_MAX_VOLUME_LEVEL
1967
);
1968
1969
for (i = 0; i < voice->sends.SendCount; i += 1)
1970
{
1971
FAudio_RecalcMixMatrix(voice, i);
1972
}
1973
1974
FAudio_PlatformUnlockMutex(voice->volumeLock);
1975
LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
1976
1977
FAudio_PlatformUnlockMutex(voice->sendLock);
1978
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1979
1980
LOG_API_EXIT(voice->audio)
1981
return 0;
1982
}
1983
1984
void FAudioVoice_GetOutputFilterParameters(
1985
FAudioVoice* voice,
1986
FAudioVoice* pDestinationVoice,
1987
FAudioFilterParameters* pParameters
1988
) {
1989
FAudioFilterParametersEXT ext_parameters;
1990
ext_parameters.Type = pParameters->Type;
1991
ext_parameters.OneOverQ = pParameters->OneOverQ;
1992
ext_parameters.Frequency = pParameters->Frequency;
1993
ext_parameters.WetDryMix = FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT;
1994
1995
FAudioVoice_GetOutputFilterParametersEXT(voice, pDestinationVoice, &ext_parameters);
1996
1997
pParameters->Type = ext_parameters.Type;
1998
pParameters->Frequency = ext_parameters.Frequency;
1999
pParameters->OneOverQ = ext_parameters.OneOverQ;
2000
}
2001
2002
void FAudioVoice_GetVolume(
2003
FAudioVoice *voice,
2004
float *pVolume
2005
) {
2006
LOG_API_ENTER(voice->audio)
2007
*pVolume = voice->volume;
2008
LOG_API_EXIT(voice->audio)
2009
}
2010
2011
uint32_t FAudioVoice_SetChannelVolumes(
2012
FAudioVoice *voice,
2013
uint32_t Channels,
2014
const float *pVolumes,
2015
uint32_t OperationSet
2016
) {
2017
uint32_t i;
2018
2019
LOG_API_ENTER(voice->audio)
2020
2021
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
2022
{
2023
FAudio_OPERATIONSET_QueueSetChannelVolumes(
2024
voice,
2025
Channels,
2026
pVolumes,
2027
OperationSet
2028
);
2029
LOG_API_EXIT(voice->audio)
2030
return 0;
2031
}
2032
2033
if (pVolumes == NULL)
2034
{
2035
LOG_API_EXIT(voice->audio)
2036
return FAUDIO_E_INVALID_CALL;
2037
}
2038
2039
if (voice->type == FAUDIO_VOICE_MASTER)
2040
{
2041
LOG_API_EXIT(voice->audio)
2042
return FAUDIO_E_INVALID_CALL;
2043
}
2044
2045
if (voice->audio->version > 7 && Channels != voice->outputChannels)
2046
{
2047
LOG_API_EXIT(voice->audio)
2048
return FAUDIO_E_INVALID_CALL;
2049
}
2050
2051
FAudio_PlatformLockMutex(voice->sendLock);
2052
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
2053
2054
FAudio_PlatformLockMutex(voice->volumeLock);
2055
LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
2056
2057
FAudio_memcpy(
2058
voice->channelVolume,
2059
pVolumes,
2060
sizeof(float) * Channels
2061
);
2062
2063
for (i = 0; i < voice->sends.SendCount; i += 1)
2064
{
2065
FAudio_RecalcMixMatrix(voice, i);
2066
}
2067
2068
FAudio_PlatformUnlockMutex(voice->volumeLock);
2069
LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
2070
2071
FAudio_PlatformUnlockMutex(voice->sendLock);
2072
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
2073
2074
LOG_API_EXIT(voice->audio)
2075
return 0;
2076
}
2077
2078
void FAudioVoice_GetChannelVolumes(
2079
FAudioVoice *voice,
2080
uint32_t Channels,
2081
float *pVolumes
2082
) {
2083
LOG_API_ENTER(voice->audio)
2084
FAudio_PlatformLockMutex(voice->volumeLock);
2085
LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
2086
FAudio_memcpy(
2087
pVolumes,
2088
voice->channelVolume,
2089
sizeof(float) * Channels
2090
);
2091
FAudio_PlatformUnlockMutex(voice->volumeLock);
2092
LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
2093
LOG_API_EXIT(voice->audio)
2094
}
2095
2096
uint32_t FAudioVoice_SetOutputMatrix(
2097
FAudioVoice *voice,
2098
FAudioVoice *pDestinationVoice,
2099
uint32_t SourceChannels,
2100
uint32_t DestinationChannels,
2101
const float *pLevelMatrix,
2102
uint32_t OperationSet
2103
) {
2104
uint32_t i, result = 0;
2105
LOG_API_ENTER(voice->audio)
2106
2107
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
2108
{
2109
FAudio_OPERATIONSET_QueueSetOutputMatrix(
2110
voice,
2111
pDestinationVoice,
2112
SourceChannels,
2113
DestinationChannels,
2114
pLevelMatrix,
2115
OperationSet
2116
);
2117
LOG_API_EXIT(voice->audio)
2118
return 0;
2119
}
2120
2121
FAudio_PlatformLockMutex(voice->sendLock);
2122
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
2123
2124
/* Find the send index */
2125
if (pDestinationVoice == NULL && voice->sends.SendCount == 1)
2126
{
2127
pDestinationVoice = voice->sends.pSends[0].pOutputVoice;
2128
}
2129
FAudio_assert(pDestinationVoice != NULL);
2130
for (i = 0; i < voice->sends.SendCount; i += 1)
2131
{
2132
if (pDestinationVoice == voice->sends.pSends[i].pOutputVoice)
2133
{
2134
break;
2135
}
2136
}
2137
if (i >= voice->sends.SendCount)
2138
{
2139
LOG_ERROR(
2140
voice->audio,
2141
"Destination not attached to source: %p %p",
2142
(void*) voice,
2143
(void*) pDestinationVoice
2144
)
2145
result = FAUDIO_E_INVALID_CALL;
2146
goto end;
2147
}
2148
2149
/* Verify the Source/Destination channel count */
2150
if (SourceChannels != voice->outputChannels)
2151
{
2152
LOG_ERROR(
2153
voice->audio,
2154
"SourceChannels not equal to voice channel count: %p %d %d",
2155
(void*) voice,
2156
SourceChannels,
2157
voice->outputChannels
2158
)
2159
result = FAUDIO_E_INVALID_CALL;
2160
goto end;
2161
}
2162
2163
if (pDestinationVoice->type == FAUDIO_VOICE_MASTER)
2164
{
2165
if (DestinationChannels != pDestinationVoice->master.inputChannels)
2166
{
2167
LOG_ERROR(
2168
voice->audio,
2169
"DestinationChannels not equal to master channel count: %p %d %d",
2170
(void*) pDestinationVoice,
2171
DestinationChannels,
2172
pDestinationVoice->master.inputChannels
2173
)
2174
result = FAUDIO_E_INVALID_CALL;
2175
goto end;
2176
}
2177
}
2178
else
2179
{
2180
if (DestinationChannels != pDestinationVoice->mix.inputChannels)
2181
{
2182
LOG_ERROR(
2183
voice->audio,
2184
"DestinationChannels not equal to submix channel count: %p %d %d",
2185
(void*) pDestinationVoice,
2186
DestinationChannels,
2187
pDestinationVoice->mix.inputChannels
2188
)
2189
result = FAUDIO_E_INVALID_CALL;
2190
goto end;
2191
}
2192
}
2193
2194
/* Set the matrix values, finally */
2195
FAudio_PlatformLockMutex(voice->volumeLock);
2196
LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
2197
2198
FAudio_memcpy(
2199
voice->sendCoefficients[i],
2200
pLevelMatrix,
2201
sizeof(float) * SourceChannels * DestinationChannels
2202
);
2203
2204
FAudio_RecalcMixMatrix(voice, i);
2205
2206
FAudio_PlatformUnlockMutex(voice->volumeLock);
2207
LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
2208
2209
end:
2210
FAudio_PlatformUnlockMutex(voice->sendLock);
2211
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
2212
LOG_API_EXIT(voice->audio)
2213
return result;
2214
}
2215
2216
void FAudioVoice_GetOutputMatrix(
2217
FAudioVoice *voice,
2218
FAudioVoice *pDestinationVoice,
2219
uint32_t SourceChannels,
2220
uint32_t DestinationChannels,
2221
float *pLevelMatrix
2222
) {
2223
uint32_t i;
2224
2225
LOG_API_ENTER(voice->audio)
2226
FAudio_PlatformLockMutex(voice->sendLock);
2227
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
2228
2229
/* Find the send index */
2230
for (i = 0; i < voice->sends.SendCount; i += 1)
2231
{
2232
if (pDestinationVoice == voice->sends.pSends[i].pOutputVoice)
2233
{
2234
break;
2235
}
2236
}
2237
if (i >= voice->sends.SendCount)
2238
{
2239
LOG_ERROR(
2240
voice->audio,
2241
"Destination not attached to source: %p %p",
2242
(void*) voice,
2243
(void*) pDestinationVoice
2244
)
2245
FAudio_PlatformUnlockMutex(voice->sendLock);
2246
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
2247
LOG_API_EXIT(voice->audio)
2248
return;
2249
}
2250
2251
/* Verify the Source/Destination channel count */
2252
if (voice->type == FAUDIO_VOICE_SOURCE)
2253
{
2254
FAudio_assert(SourceChannels == voice->src.format->nChannels);
2255
}
2256
else
2257
{
2258
FAudio_assert(SourceChannels == voice->mix.inputChannels);
2259
}
2260
if (pDestinationVoice->type == FAUDIO_VOICE_MASTER)
2261
{
2262
FAudio_assert(DestinationChannels == pDestinationVoice->master.inputChannels);
2263
}
2264
else
2265
{
2266
FAudio_assert(DestinationChannels == pDestinationVoice->mix.inputChannels);
2267
}
2268
2269
/* Get the matrix values, finally */
2270
FAudio_memcpy(
2271
pLevelMatrix,
2272
voice->sendCoefficients[i],
2273
sizeof(float) * SourceChannels * DestinationChannels
2274
);
2275
2276
FAudio_PlatformUnlockMutex(voice->sendLock);
2277
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
2278
LOG_API_EXIT(voice->audio)
2279
}
2280
2281
static uint32_t check_for_sends_to_voice(FAudioVoice *voice)
2282
{
2283
FAudio *audio = voice->audio;
2284
uint32_t ret = 0;
2285
FAudioSourceVoice *source;
2286
FAudioSubmixVoice *submix;
2287
LinkedList *list;
2288
uint32_t i;
2289
2290
FAudio_PlatformLockMutex(audio->sourceLock);
2291
list = audio->sources;
2292
while (list != NULL)
2293
{
2294
source = (FAudioSourceVoice*) list->entry;
2295
for (i = 0; i < source->sends.SendCount; i += 1)
2296
if (source->sends.pSends[i].pOutputVoice == voice)
2297
{
2298
ret = 0x80004005; /* E_FAIL */
2299
break;
2300
}
2301
if (ret)
2302
break;
2303
list = list->next;
2304
}
2305
FAudio_PlatformUnlockMutex(audio->sourceLock);
2306
2307
if (ret)
2308
return ret;
2309
2310
FAudio_PlatformLockMutex(audio->submixLock);
2311
list = audio->submixes;
2312
while (list != NULL)
2313
{
2314
submix = (FAudioSubmixVoice*) list->entry;
2315
for (i = 0; i < submix->sends.SendCount; i += 1)
2316
if (submix->sends.pSends[i].pOutputVoice == voice)
2317
{
2318
ret = 0x80004005; /* E_FAIL */
2319
break;
2320
}
2321
if (ret)
2322
break;
2323
list = list->next;
2324
}
2325
FAudio_PlatformUnlockMutex(audio->submixLock);
2326
2327
return ret;
2328
}
2329
2330
static void destroy_voice(FAudioVoice *voice)
2331
{
2332
uint32_t i;
2333
2334
/* TODO: Check for dependencies and remove from audio graph first! */
2335
FAudio_OPERATIONSET_ClearAllForVoice(voice);
2336
2337
if (voice->type == FAUDIO_VOICE_SOURCE)
2338
{
2339
FAudioBufferEntry *entry, *next;
2340
2341
#ifdef FAUDIO_DUMP_VOICES
2342
FAudio_DUMPVOICE_Finalize((FAudioSourceVoice*) voice);
2343
#endif /* FAUDIO_DUMP_VOICES */
2344
2345
FAudio_PlatformLockMutex(voice->audio->sourceLock);
2346
LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
2347
while (voice == voice->audio->processingSource)
2348
{
2349
FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
2350
LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
2351
FAudio_PlatformLockMutex(voice->audio->sourceLock);
2352
LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
2353
}
2354
LinkedList_RemoveEntry(
2355
&voice->audio->sources,
2356
voice,
2357
voice->audio->sourceLock,
2358
voice->audio->pFree
2359
);
2360
FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
2361
LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
2362
2363
entry = voice->src.bufferList;
2364
while (entry != NULL)
2365
{
2366
next = entry->next;
2367
voice->audio->pFree(entry);
2368
entry = next;
2369
}
2370
2371
entry = voice->src.flushList;
2372
while (entry != NULL)
2373
{
2374
next = entry->next;
2375
voice->audio->pFree(entry);
2376
entry = next;
2377
}
2378
2379
voice->audio->pFree(voice->src.format);
2380
LOG_MUTEX_DESTROY(voice->audio, voice->src.bufferLock)
2381
FAudio_PlatformDestroyMutex(voice->src.bufferLock);
2382
#ifdef HAVE_WMADEC
2383
if (voice->src.wmadec)
2384
{
2385
FAudio_WMADEC_free(voice);
2386
}
2387
#endif /* HAVE_WMADEC */
2388
}
2389
else if (voice->type == FAUDIO_VOICE_SUBMIX)
2390
{
2391
/* Remove submix from list */
2392
LinkedList_RemoveEntry(
2393
&voice->audio->submixes,
2394
voice,
2395
voice->audio->submixLock,
2396
voice->audio->pFree
2397
);
2398
2399
/* Delete submix data */
2400
voice->audio->pFree(voice->mix.inputCache);
2401
}
2402
else if (voice->type == FAUDIO_VOICE_MASTER)
2403
{
2404
if (voice->audio->platform != NULL)
2405
{
2406
FAudio_PlatformQuit(voice->audio->platform);
2407
voice->audio->platform = NULL;
2408
}
2409
if (voice->master.effectCache != NULL)
2410
{
2411
voice->audio->pFree(voice->master.effectCache);
2412
}
2413
voice->audio->master = NULL;
2414
}
2415
2416
if (voice->sendLock != NULL)
2417
{
2418
FAudio_PlatformLockMutex(voice->sendLock);
2419
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
2420
for (i = 0; i < voice->sends.SendCount; i += 1)
2421
{
2422
voice->audio->pFree(voice->sendCoefficients[i]);
2423
}
2424
if (voice->sendCoefficients != NULL)
2425
{
2426
voice->audio->pFree(voice->sendCoefficients);
2427
}
2428
for (i = 0; i < voice->sends.SendCount; i += 1)
2429
{
2430
voice->audio->pFree(voice->mixCoefficients[i]);
2431
}
2432
if (voice->mixCoefficients != NULL)
2433
{
2434
voice->audio->pFree(voice->mixCoefficients);
2435
}
2436
if (voice->sendMix != NULL)
2437
{
2438
voice->audio->pFree(voice->sendMix);
2439
}
2440
if (voice->sendFilter != NULL)
2441
{
2442
voice->audio->pFree(voice->sendFilter);
2443
}
2444
if (voice->sendFilterState != NULL)
2445
{
2446
for (i = 0; i < voice->sends.SendCount; i += 1)
2447
{
2448
if (voice->sendFilterState[i] != NULL)
2449
{
2450
voice->audio->pFree(voice->sendFilterState[i]);
2451
}
2452
}
2453
voice->audio->pFree(voice->sendFilterState);
2454
}
2455
if (voice->sends.pSends != NULL)
2456
{
2457
voice->audio->pFree(voice->sends.pSends);
2458
}
2459
FAudio_PlatformUnlockMutex(voice->sendLock);
2460
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
2461
LOG_MUTEX_DESTROY(voice->audio, voice->sendLock)
2462
FAudio_PlatformDestroyMutex(voice->sendLock);
2463
}
2464
2465
if (voice->effectLock != NULL)
2466
{
2467
FAudio_PlatformLockMutex(voice->effectLock);
2468
LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
2469
FAudio_INTERNAL_FreeEffectChain(voice);
2470
FAudio_PlatformUnlockMutex(voice->effectLock);
2471
LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
2472
LOG_MUTEX_DESTROY(voice->audio, voice->effectLock)
2473
FAudio_PlatformDestroyMutex(voice->effectLock);
2474
}
2475
2476
if (voice->filterLock != NULL)
2477
{
2478
FAudio_PlatformLockMutex(voice->filterLock);
2479
LOG_MUTEX_LOCK(voice->audio, voice->filterLock)
2480
if (voice->filterState != NULL)
2481
{
2482
voice->audio->pFree(voice->filterState);
2483
}
2484
FAudio_PlatformUnlockMutex(voice->filterLock);
2485
LOG_MUTEX_UNLOCK(voice->audio, voice->filterLock)
2486
LOG_MUTEX_DESTROY(voice->audio, voice->filterLock)
2487
FAudio_PlatformDestroyMutex(voice->filterLock);
2488
}
2489
2490
if (voice->volumeLock != NULL)
2491
{
2492
FAudio_PlatformLockMutex(voice->volumeLock);
2493
LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
2494
if (voice->channelVolume != NULL)
2495
{
2496
voice->audio->pFree(voice->channelVolume);
2497
}
2498
FAudio_PlatformUnlockMutex(voice->volumeLock);
2499
LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
2500
LOG_MUTEX_DESTROY(voice->audio, voice->volumeLock)
2501
FAudio_PlatformDestroyMutex(voice->volumeLock);
2502
}
2503
2504
voice->audio->pFree(voice);
2505
}
2506
2507
uint32_t FAudioVoice_DestroyVoiceSafeEXT(FAudioVoice *voice)
2508
{
2509
uint32_t ret;
2510
2511
FAudio* audio = voice->audio;
2512
2513
LOG_API_ENTER(audio)
2514
2515
if ((ret = check_for_sends_to_voice(voice)))
2516
{
2517
LOG_ERROR(
2518
audio,
2519
"Voice %p is an output for other voice(s)",
2520
voice
2521
)
2522
LOG_API_EXIT(audio)
2523
return ret;
2524
}
2525
destroy_voice(voice);
2526
LOG_API_EXIT(audio)
2527
return 0;
2528
}
2529
2530
void FAudioVoice_DestroyVoice(FAudioVoice *voice)
2531
{
2532
FAudioVoice_DestroyVoiceSafeEXT(voice);
2533
}
2534
2535
/* FAudioSourceVoice Interface */
2536
2537
uint32_t FAudioSourceVoice_Start(
2538
FAudioSourceVoice *voice,
2539
uint32_t Flags,
2540
uint32_t OperationSet
2541
) {
2542
LOG_API_ENTER(voice->audio)
2543
2544
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
2545
{
2546
FAudio_OPERATIONSET_QueueStart(
2547
voice,
2548
Flags,
2549
OperationSet
2550
);
2551
LOG_API_EXIT(voice->audio)
2552
return 0;
2553
}
2554
2555
2556
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2557
2558
FAudio_assert(Flags == 0);
2559
voice->src.active = 1;
2560
LOG_API_EXIT(voice->audio)
2561
return 0;
2562
}
2563
2564
uint32_t FAudioSourceVoice_Stop(
2565
FAudioSourceVoice *voice,
2566
uint32_t Flags,
2567
uint32_t OperationSet
2568
) {
2569
LOG_API_ENTER(voice->audio)
2570
2571
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
2572
{
2573
FAudio_OPERATIONSET_QueueStop(
2574
voice,
2575
Flags,
2576
OperationSet
2577
);
2578
LOG_API_EXIT(voice->audio)
2579
return 0;
2580
}
2581
2582
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2583
2584
if (Flags & FAUDIO_PLAY_TAILS)
2585
{
2586
voice->src.active = 2;
2587
}
2588
else
2589
{
2590
voice->src.active = 0;
2591
}
2592
LOG_API_EXIT(voice->audio)
2593
return 0;
2594
}
2595
2596
uint32_t FAudioSourceVoice_SubmitSourceBuffer(
2597
FAudioSourceVoice *voice,
2598
const FAudioBuffer *pBuffer,
2599
const FAudioBufferWMA *pBufferWMA
2600
) {
2601
uint32_t adpcmMask, *adpcmByteCount;
2602
uint32_t playBegin, playLength, loopBegin, loopLength, bufferLength;
2603
FAudioBufferEntry *entry, *list;
2604
2605
LOG_API_ENTER(voice->audio)
2606
LOG_INFO(
2607
voice->audio,
2608
"%p: {Flags: 0x%x, AudioBytes: %u, pAudioData: %p, Play: %u + %u, Loop: %u + %u x %u}",
2609
(void*) voice,
2610
pBuffer->Flags,
2611
pBuffer->AudioBytes,
2612
(const void*) pBuffer->pAudioData,
2613
pBuffer->PlayBegin,
2614
pBuffer->PlayLength,
2615
pBuffer->LoopBegin,
2616
pBuffer->LoopLength,
2617
pBuffer->LoopCount
2618
)
2619
2620
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2621
#ifdef HAVE_WMADEC
2622
FAudio_assert( (voice->src.wmadec != NULL && (pBufferWMA != NULL ||
2623
(voice->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2 ||
2624
voice->src.format->wFormatTag == FAUDIO_FORMAT_EXTENSIBLE))) ||
2625
(voice->src.wmadec == NULL && (pBufferWMA == NULL && voice->src.format->wFormatTag != FAUDIO_FORMAT_XMAUDIO2)) );
2626
#endif /* HAVE_WMADEC */
2627
2628
/* Start off with whatever they just sent us... */
2629
playBegin = pBuffer->PlayBegin;
2630
playLength = pBuffer->PlayLength;
2631
loopBegin = pBuffer->LoopBegin;
2632
loopLength = pBuffer->LoopLength;
2633
2634
/* "LoopBegin/LoopLength must be zero if LoopCount is 0" */
2635
if (pBuffer->LoopCount == 0 && (loopBegin > 0 || loopLength > 0))
2636
{
2637
LOG_API_EXIT(voice->audio)
2638
return FAUDIO_E_INVALID_CALL;
2639
}
2640
2641
if (voice->src.format->wFormatTag == FAUDIO_FORMAT_MSADPCM)
2642
{
2643
FAudioADPCMWaveFormat *fmtex = (FAudioADPCMWaveFormat*) voice->src.format;
2644
bufferLength =
2645
pBuffer->AudioBytes /
2646
fmtex->wfx.nBlockAlign *
2647
fmtex->wSamplesPerBlock;
2648
}
2649
else if (voice->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
2650
{
2651
FAudioXMA2WaveFormat *fmtex = (FAudioXMA2WaveFormat*) voice->src.format;
2652
bufferLength = fmtex->dwSamplesEncoded;
2653
}
2654
else if (pBufferWMA != NULL)
2655
{
2656
bufferLength =
2657
pBufferWMA->pDecodedPacketCumulativeBytes[pBufferWMA->PacketCount - 1] /
2658
(voice->src.format->nChannels * voice->src.format->wBitsPerSample / 8);
2659
}
2660
else
2661
{
2662
bufferLength =
2663
pBuffer->AudioBytes /
2664
voice->src.format->nBlockAlign;
2665
}
2666
2667
/* PlayLength Default */
2668
if (playLength == 0)
2669
{
2670
playLength = bufferLength - playBegin;
2671
}
2672
else if (playBegin + playLength > bufferLength || playBegin + playLength < playLength)
2673
{
2674
/* Reading past the end of the buffer, or begin + length overflow uint32_t, which
2675
* would also read past the end of the buffer. */
2676
LOG_API_EXIT(voice->audio)
2677
return FAUDIO_E_INVALID_CALL;
2678
}
2679
2680
if (pBuffer->LoopCount > 0 && pBufferWMA == NULL && voice->src.format->wFormatTag != FAUDIO_FORMAT_XMAUDIO2)
2681
{
2682
/* "The value of LoopBegin must be less than PlayBegin + PlayLength" */
2683
if (loopBegin >= (playBegin + playLength))
2684
{
2685
LOG_API_EXIT(voice->audio)
2686
return FAUDIO_E_INVALID_CALL;
2687
}
2688
2689
/* LoopLength Default */
2690
if (loopLength == 0)
2691
{
2692
loopLength = playBegin + playLength - loopBegin;
2693
}
2694
2695
/* "The value of LoopBegin + LoopLength must be greater than PlayBegin
2696
* and less than PlayBegin + PlayLength"
2697
*/
2698
if ( voice->audio->version > 7 && (
2699
(loopBegin + loopLength) <= playBegin ||
2700
(loopBegin + loopLength) > (playBegin + playLength)) )
2701
{
2702
LOG_API_EXIT(voice->audio)
2703
return FAUDIO_E_INVALID_CALL;
2704
}
2705
}
2706
2707
/* For ADPCM, round down to the nearest sample block size */
2708
if (voice->src.format->wFormatTag == FAUDIO_FORMAT_MSADPCM)
2709
{
2710
adpcmMask = ((FAudioADPCMWaveFormat*) voice->src.format)->wSamplesPerBlock;
2711
playBegin -= playBegin % adpcmMask;
2712
playLength -= playLength % adpcmMask;
2713
loopBegin -= loopBegin % adpcmMask;
2714
loopLength -= loopLength % adpcmMask;
2715
2716
/* This is basically a const_cast... */
2717
adpcmByteCount = (uint32_t*) &pBuffer->AudioBytes;
2718
*adpcmByteCount = (
2719
pBuffer->AudioBytes / voice->src.format->nBlockAlign
2720
) * voice->src.format->nBlockAlign;
2721
}
2722
else if (pBufferWMA != NULL || voice->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
2723
{
2724
/* WMA only supports looping the whole buffer */
2725
loopBegin = 0;
2726
loopLength = playBegin + playLength;
2727
}
2728
2729
/* Allocate, now that we have valid input */
2730
entry = (FAudioBufferEntry*) voice->audio->pMalloc(sizeof(FAudioBufferEntry));
2731
FAudio_memcpy(&entry->buffer, pBuffer, sizeof(FAudioBuffer));
2732
entry->buffer.PlayBegin = playBegin;
2733
entry->buffer.PlayLength = playLength;
2734
entry->buffer.LoopBegin = loopBegin;
2735
entry->buffer.LoopLength = loopLength;
2736
if (pBufferWMA != NULL)
2737
{
2738
FAudio_memcpy(&entry->bufferWMA, pBufferWMA, sizeof(FAudioBufferWMA));
2739
}
2740
entry->next = NULL;
2741
2742
if ( voice->audio->version <= 7 && (
2743
entry->buffer.LoopCount > 0 &&
2744
entry->buffer.LoopBegin + entry->buffer.LoopLength <= entry->buffer.PlayBegin))
2745
{
2746
entry->buffer.LoopCount = 0;
2747
}
2748
2749
#ifdef FAUDIO_DUMP_VOICES
2750
/* dumping current buffer, append into "data" section */
2751
if (pBuffer->pAudioData != NULL && playLength > 0)
2752
{
2753
FAudio_DUMPVOICE_WriteBuffer(voice, pBuffer, pBufferWMA, playBegin, playLength);
2754
}
2755
#endif /* FAUDIO_DUMP_VOICES */
2756
2757
/* Submit! */
2758
FAudio_PlatformLockMutex(voice->src.bufferLock);
2759
LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
2760
if (voice->src.bufferList == NULL)
2761
{
2762
voice->src.bufferList = entry;
2763
voice->src.curBufferOffset = entry->buffer.PlayBegin;
2764
voice->src.newBuffer = 1;
2765
}
2766
else
2767
{
2768
list = voice->src.bufferList;
2769
while (list->next != NULL)
2770
{
2771
list = list->next;
2772
}
2773
list->next = entry;
2774
2775
/* For some bizarre reason we get scenarios where a buffer is freed, only to
2776
* have the allocator give us the exact same address and somehow get a single
2777
* buffer referencing itself. I don't even know.
2778
*/
2779
FAudio_assert(list != entry);
2780
}
2781
LOG_INFO(
2782
voice->audio,
2783
"%p: appended buffer %p",
2784
(void*) voice,
2785
(void*) &entry->buffer
2786
)
2787
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
2788
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
2789
LOG_API_EXIT(voice->audio)
2790
return 0;
2791
}
2792
2793
uint32_t FAudioSourceVoice_FlushSourceBuffers(
2794
FAudioSourceVoice *voice
2795
) {
2796
FAudioBufferEntry *entry, *latest;
2797
2798
LOG_API_ENTER(voice->audio)
2799
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2800
2801
FAudio_PlatformLockMutex(voice->src.bufferLock);
2802
LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
2803
2804
/* If the source is playing, don't flush the active buffer */
2805
entry = voice->src.bufferList;
2806
if ((voice->src.active == 1) && entry != NULL && !voice->src.newBuffer)
2807
{
2808
entry = entry->next;
2809
voice->src.bufferList->next = NULL;
2810
}
2811
else
2812
{
2813
voice->src.curBufferOffset = 0;
2814
voice->src.bufferList = NULL;
2815
voice->src.newBuffer = 0;
2816
}
2817
2818
/* Move them to the pending flush list */
2819
if (entry != NULL)
2820
{
2821
if (voice->src.flushList == NULL)
2822
{
2823
voice->src.flushList = entry;
2824
}
2825
else
2826
{
2827
latest = voice->src.flushList;
2828
while (latest->next != NULL)
2829
{
2830
latest = latest->next;
2831
}
2832
latest->next = entry;
2833
}
2834
}
2835
2836
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
2837
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
2838
LOG_API_EXIT(voice->audio)
2839
return 0;
2840
}
2841
2842
uint32_t FAudioSourceVoice_Discontinuity(
2843
FAudioSourceVoice *voice
2844
) {
2845
FAudioBufferEntry *buf;
2846
2847
LOG_API_ENTER(voice->audio)
2848
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2849
2850
FAudio_PlatformLockMutex(voice->src.bufferLock);
2851
LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
2852
2853
if (voice->src.bufferList != NULL)
2854
{
2855
for (buf = voice->src.bufferList; buf->next != NULL; buf = buf->next);
2856
buf->buffer.Flags |= FAUDIO_END_OF_STREAM;
2857
}
2858
2859
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
2860
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
2861
LOG_API_EXIT(voice->audio)
2862
return 0;
2863
}
2864
2865
uint32_t FAudioSourceVoice_ExitLoop(
2866
FAudioSourceVoice *voice,
2867
uint32_t OperationSet
2868
) {
2869
LOG_API_ENTER(voice->audio)
2870
2871
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
2872
{
2873
FAudio_OPERATIONSET_QueueExitLoop(
2874
voice,
2875
OperationSet
2876
);
2877
LOG_API_EXIT(voice->audio)
2878
return 0;
2879
}
2880
2881
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2882
2883
FAudio_PlatformLockMutex(voice->src.bufferLock);
2884
LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
2885
2886
if (voice->src.bufferList != NULL)
2887
{
2888
voice->src.bufferList->buffer.LoopCount = 0;
2889
}
2890
2891
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
2892
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
2893
LOG_API_EXIT(voice->audio)
2894
return 0;
2895
}
2896
2897
void FAudioSourceVoice_GetState(
2898
FAudioSourceVoice *voice,
2899
FAudioVoiceState *pVoiceState,
2900
uint32_t Flags
2901
) {
2902
FAudioBufferEntry *entry;
2903
2904
LOG_API_ENTER(voice->audio)
2905
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2906
2907
FAudio_PlatformLockMutex(voice->src.bufferLock);
2908
LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
2909
2910
if (!(Flags & FAUDIO_VOICE_NOSAMPLESPLAYED))
2911
{
2912
pVoiceState->SamplesPlayed = voice->src.totalSamples;
2913
}
2914
2915
pVoiceState->BuffersQueued = 0;
2916
pVoiceState->pCurrentBufferContext = NULL;
2917
if (voice->src.bufferList != NULL)
2918
{
2919
entry = voice->src.bufferList;
2920
if (!voice->src.newBuffer)
2921
{
2922
pVoiceState->pCurrentBufferContext = entry->buffer.pContext;
2923
}
2924
do
2925
{
2926
pVoiceState->BuffersQueued += 1;
2927
entry = entry->next;
2928
} while (entry != NULL);
2929
}
2930
2931
/* Pending flushed buffers also count */
2932
entry = voice->src.flushList;
2933
while (entry != NULL)
2934
{
2935
pVoiceState->BuffersQueued += 1;
2936
entry = entry->next;
2937
}
2938
2939
LOG_INFO(
2940
voice->audio,
2941
"-> {pCurrentBufferContext: %p, BuffersQueued: %u, SamplesPlayed: %"FAudio_PRIu64"}",
2942
pVoiceState->pCurrentBufferContext, pVoiceState->BuffersQueued,
2943
pVoiceState->SamplesPlayed
2944
)
2945
2946
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
2947
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
2948
LOG_API_EXIT(voice->audio)
2949
}
2950
2951
uint32_t FAudioSourceVoice_SetFrequencyRatio(
2952
FAudioSourceVoice *voice,
2953
float Ratio,
2954
uint32_t OperationSet
2955
) {
2956
LOG_API_ENTER(voice->audio)
2957
2958
if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
2959
{
2960
FAudio_OPERATIONSET_QueueSetFrequencyRatio(
2961
voice,
2962
Ratio,
2963
OperationSet
2964
);
2965
LOG_API_EXIT(voice->audio)
2966
return 0;
2967
}
2968
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2969
2970
if (voice->flags & FAUDIO_VOICE_NOPITCH)
2971
{
2972
LOG_API_EXIT(voice->audio)
2973
return 0;
2974
}
2975
2976
voice->src.freqRatio = FAudio_clamp(
2977
Ratio,
2978
FAUDIO_MIN_FREQ_RATIO,
2979
voice->src.maxFreqRatio
2980
);
2981
LOG_API_EXIT(voice->audio)
2982
return 0;
2983
}
2984
2985
void FAudioSourceVoice_GetFrequencyRatio(
2986
FAudioSourceVoice *voice,
2987
float *pRatio
2988
) {
2989
LOG_API_ENTER(voice->audio)
2990
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2991
2992
*pRatio = voice->src.freqRatio;
2993
LOG_API_EXIT(voice->audio)
2994
}
2995
2996
uint32_t FAudioSourceVoice_SetSourceSampleRate(
2997
FAudioSourceVoice *voice,
2998
uint32_t NewSourceSampleRate
2999
) {
3000
uint32_t outSampleRate;
3001
uint32_t newDecodeSamples, newResampleSamples;
3002
3003
LOG_API_ENTER(voice->audio)
3004
FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
3005
FAudio_assert( NewSourceSampleRate >= FAUDIO_MIN_SAMPLE_RATE &&
3006
NewSourceSampleRate <= FAUDIO_MAX_SAMPLE_RATE );
3007
3008
FAudio_PlatformLockMutex(voice->src.bufferLock);
3009
LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
3010
if ( voice->audio->version > 7 &&
3011
voice->src.bufferList != NULL )
3012
{
3013
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
3014
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
3015
LOG_API_EXIT(voice->audio)
3016
return FAUDIO_E_INVALID_CALL;
3017
}
3018
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
3019
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
3020
3021
voice->src.format->nSamplesPerSec = NewSourceSampleRate;
3022
3023
/* Resize decode cache */
3024
newDecodeSamples = (uint32_t) FAudio_ceil(
3025
voice->audio->updateSize *
3026
(double) voice->src.maxFreqRatio *
3027
(double) NewSourceSampleRate /
3028
(double) voice->audio->master->master.inputSampleRate
3029
) + EXTRA_DECODE_PADDING * voice->src.format->nChannels;
3030
FAudio_INTERNAL_ResizeDecodeCache(
3031
voice->audio,
3032
(newDecodeSamples + EXTRA_DECODE_PADDING) * voice->src.format->nChannels
3033
);
3034
voice->src.decodeSamples = newDecodeSamples;
3035
3036
FAudio_PlatformLockMutex(voice->sendLock);
3037
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
3038
3039
if (voice->sends.SendCount == 0)
3040
{
3041
FAudio_PlatformUnlockMutex(voice->sendLock);
3042
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
3043
LOG_API_EXIT(voice->audio)
3044
return 0;
3045
}
3046
outSampleRate = voice->sends.pSends[0].pOutputVoice->type == FAUDIO_VOICE_MASTER ?
3047
voice->sends.pSends[0].pOutputVoice->master.inputSampleRate :
3048
voice->sends.pSends[0].pOutputVoice->mix.inputSampleRate;
3049
3050
newResampleSamples = (uint32_t) (FAudio_ceil(
3051
(double) voice->audio->updateSize *
3052
(double) outSampleRate /
3053
(double) voice->audio->master->master.inputSampleRate
3054
));
3055
voice->src.resampleSamples = newResampleSamples;
3056
3057
FAudio_PlatformUnlockMutex(voice->sendLock);
3058
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
3059
3060
LOG_API_EXIT(voice->audio)
3061
return 0;
3062
}
3063
3064
/* FAudioMasteringVoice Interface */
3065
3066
FAUDIOAPI uint32_t FAudioMasteringVoice_GetChannelMask(
3067
FAudioMasteringVoice *voice,
3068
uint32_t *pChannelMask
3069
) {
3070
LOG_API_ENTER(voice->audio)
3071
FAudio_assert(voice->type == FAUDIO_VOICE_MASTER);
3072
FAudio_assert(pChannelMask != NULL);
3073
3074
*pChannelMask = voice->audio->mixFormat.dwChannelMask;
3075
LOG_API_EXIT(voice->audio)
3076
return 0;
3077
}
3078
3079
#ifdef FAUDIO_DUMP_VOICES
3080
3081
static inline FAudioIOStreamOut *DumpVoices_fopen(
3082
const FAudioSourceVoice *voice,
3083
const FAudioWaveFormatEx *format,
3084
const char *mode,
3085
const char *ext
3086
) {
3087
char loc[64];
3088
uint16_t format_tag = format->wFormatTag;
3089
uint16_t format_ex_tag = 0;
3090
if (format->wFormatTag == FAUDIO_FORMAT_EXTENSIBLE)
3091
{
3092
/* get the GUID of the extended subformat */
3093
const FAudioWaveFormatExtensible *format_ex =
3094
(const FAudioWaveFormatExtensible*) format;
3095
format_ex_tag = (uint16_t) (format_ex->SubFormat.Data1);
3096
}
3097
FAudio_snprintf(
3098
loc,
3099
sizeof(loc),
3100
"FA_fmt_0x%04X_0x%04X_0x%016lX%s.wav",
3101
format_tag,
3102
format_ex_tag,
3103
(uint64_t) voice,
3104
ext
3105
);
3106
FAudioIOStreamOut *fileOut = FAudio_fopen_out(loc, mode);
3107
return fileOut;
3108
}
3109
3110
static inline void DumpVoices_finalize_section(
3111
const FAudioSourceVoice *voice,
3112
const FAudioWaveFormatEx *format,
3113
const char *section /* one of "data" or "dpds" */
3114
) {
3115
/* data file only contains the real data bytes */
3116
FAudioIOStreamOut *io_data = DumpVoices_fopen(voice, format, "rb", section);
3117
if (!io_data)
3118
{
3119
return;
3120
}
3121
FAudio_PlatformLockMutex((FAudioMutex) io_data->lock);
3122
size_t file_size_data = io_data->size(io_data->data);
3123
if (file_size_data == 0)
3124
{
3125
/* nothing to do */
3126
/* close data file */
3127
FAudio_PlatformUnlockMutex((FAudioMutex) io_data->lock);
3128
FAudio_close_out(io_data);
3129
return;
3130
}
3131
3132
/* we got some data: append data section to main file */
3133
FAudioIOStreamOut *io = DumpVoices_fopen(voice, format, "ab", "");
3134
if (!io)
3135
{
3136
/* close data file */
3137
FAudio_PlatformUnlockMutex((FAudioMutex) io_data->lock);
3138
FAudio_close_out(io_data);
3139
return;
3140
}
3141
3142
/* data sub-chunk - 8 bytes + data */
3143
/* SubChunk2ID - 4 --> "data" or "dpds" */
3144
io->write(io->data, section, 4, 1);
3145
/* Subchunk2Size - 4 */
3146
uint32_t chunk_size = (uint32_t)file_size_data;
3147
io->write(io->data, &chunk_size, 4, 1);
3148
/* data */
3149
/* fill in data bytes */
3150
uint8_t buffer[1024*1024];
3151
size_t count;
3152
while((count = io_data->read(io_data->data, (void*) buffer, 1, 1024*1024)) > 0)
3153
{
3154
io->write(io->data, (void*) buffer, 1, count);
3155
}
3156
3157
/* close data file */
3158
FAudio_PlatformUnlockMutex((FAudioMutex) io_data->lock);
3159
FAudio_close_out(io_data);
3160
/* close main file */
3161
FAudio_PlatformUnlockMutex((FAudioMutex) io->lock);
3162
FAudio_close_out(io);
3163
}
3164
3165
static void FAudio_DUMPVOICE_Init(const FAudioSourceVoice *voice)
3166
{
3167
const FAudioWaveFormatEx *format = voice->src.format;
3168
3169
FAudioIOStreamOut *io = DumpVoices_fopen(voice, format, "wb", "");
3170
if (!io)
3171
{
3172
return;
3173
}
3174
FAudio_PlatformLockMutex((FAudioMutex) io->lock);
3175
/* another GREAT ressource
3176
* https://wiki.multimedia.cx/index.php/Microsoft_xWMA
3177
*/
3178
3179
3180
/* wave file format taken from
3181
* http://soundfile.sapp.org/doc/WaveFormat
3182
* https://sites.google.com/site/musicgapi/technical-documents/wav-file-format
3183
* |52 49|46 46|52 4A|02 00|
3184
* |c1 sz|af|nc|sp rt|bt rt|
3185
* |ba|bs|da ta|c2 sz|
3186
3187
* | R I F F |chunk size |W A V E |f m t |
3188
* 19026
3189
* | 52 49 46 46 52 4A 02 00 57 41 56 45 66 6D 74 20 | RIFFRJ..WAVEfmt
3190
3191
* | subchnk size|fmt |nChan |samplerate |byte rate |
3192
* | 50 | 2 |2 |11025 |11289 |
3193
* | 32 00 00 00 02 00 02 00 11 2B 00 00 19 2C 00 00 | 2........+...,..
3194
3195
* |blkaln|bps |efmt |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|
3196
* | 512 |4 |32 |500 |7 |256 |0 |512 |
3197
* | 512 |4 |32 |459252 |256 |
3198
* | 00 02|04 00 20 00 F4 01 07 00 00 01 00 00 00 02 | .... .ô.........
3199
3200
* | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
3201
* |
3202
* | 00 FF 00 00 00 00 C0 00 40 00 F0 00 00 00 CC 01 | .ÿ....À.@.ð...Ì.
3203
3204
* | XXXXXXXXXXXXXXXXXX|d a t a |chunk size |XXXXX |
3205
* | | |18944 | |
3206
* | 30 FF 88 01 18 FF 64 61 74 61 00 4A 02 00 00 00 | 0ÿ...ÿdata.J....
3207
*/
3208
3209
uint16_t cbSize = format->cbSize;
3210
const char *formatFourcc = "WAVE";
3211
uint16_t wFormatTag = format->wFormatTag;
3212
/* special handling for WMAUDIO2 */
3213
if (wFormatTag == FAUDIO_FORMAT_EXTENSIBLE && cbSize >= 22)
3214
{
3215
const FAudioWaveFormatExtensible *format_ex =
3216
(const FAudioWaveFormatExtensible*) format;
3217
uint16_t format_ex_tag = (uint16_t) (format_ex->SubFormat.Data1);
3218
if (format_ex_tag == FAUDIO_FORMAT_WMAUDIO2)
3219
{
3220
cbSize = 0;
3221
formatFourcc = "XWMA";
3222
wFormatTag = FAUDIO_FORMAT_WMAUDIO2;
3223
}
3224
}
3225
3226
{ /* RIFF chunk descriptor - 12 byte */
3227
/* ChunkID - 4 */
3228
io->write(io->data, "RIFF", 4, 1);
3229
/* ChunkSize - 4 */
3230
uint32_t filesize = 0; /* the real file size is written in finalize step */
3231
io->write(io->data, &filesize, 4, 1);
3232
/* Format - 4 */
3233
io->write(io->data, formatFourcc, 4, 1);
3234
}
3235
{ /* fmt sub-chunk 24 */
3236
/* Subchunk1ID - 4 */
3237
io->write(io->data, "fmt ", 4, 1);
3238
/* Subchunk1Size - 4 */
3239
/* 18 byte for WAVEFORMATEX and cbSize for WAVEFORMATEXTENDED */
3240
uint32_t chunk_data_size = 18 + (uint32_t) cbSize;
3241
io->write(io->data, &chunk_data_size, 4, 1);
3242
/* AudioFormat - 2 */
3243
io->write(io->data, &wFormatTag, 2, 1);
3244
/* NumChannels - 2 */
3245
io->write(io->data, &format->nChannels, 2, 1);
3246
/* SampleRate - 4 */
3247
io->write(io->data, &format->nSamplesPerSec, 4, 1);
3248
/* ByteRate - 4 */
3249
/* SampleRate * NumChannels * BitsPerSample/8 */
3250
io->write(io->data, &format->nAvgBytesPerSec, 4, 1);
3251
/* BlockAlign - 2 */
3252
/* NumChannels * BitsPerSample/8 */
3253
io->write(io->data, &format->nBlockAlign, 2, 1);
3254
/* BitsPerSample - 2 */
3255
io->write(io->data, &format->wBitsPerSample, 2, 1);
3256
}
3257
/* in case of extensible audio format write the additional data to the file */
3258
{
3259
/* always write the cbSize */
3260
io->write(io->data, &cbSize, 2, 1);
3261
3262
if (cbSize >= 22)
3263
{
3264
/* we have a WAVEFORMATEXTENSIBLE struct to write */
3265
const FAudioWaveFormatExtensible *format_ex =
3266
(const FAudioWaveFormatExtensible*) format;
3267
io->write(io->data, &format_ex->Samples.wValidBitsPerSample, 2, 1);
3268
io->write(io->data, &format_ex->dwChannelMask, 4, 1);
3269
/* write FAudioGUID */
3270
io->write(io->data, &format_ex->SubFormat.Data1, 4, 1);
3271
io->write(io->data, &format_ex->SubFormat.Data2, 2, 1);
3272
io->write(io->data, &format_ex->SubFormat.Data3, 2, 1);
3273
io->write(io->data, &format_ex->SubFormat.Data4, 1, 8);
3274
}
3275
if (format->cbSize > 22)
3276
{
3277
/* fill up the remaining cbSize bytes with zeros */
3278
uint8_t zero = 0;
3279
for (uint16_t i=23; i<=format->cbSize; i++)
3280
{
3281
io->write(io->data, &zero, 1, 1);
3282
}
3283
}
3284
}
3285
{ /* dpds sub-chunk - optional - 8 bytes + bufferWMA uint32_t samples */
3286
/* create file to hold the bufferWMA samples */
3287
FAudioIOStreamOut *io_dpds = DumpVoices_fopen(voice, format, "wb", "dpds");
3288
FAudio_close_out(io_dpds);
3289
/* io_dpds file will be filled by SubmitBuffer */
3290
}
3291
{ /* data sub-chunk - 8 bytes + data */
3292
/* create file to hold the data samples */
3293
FAudioIOStreamOut *io_data = DumpVoices_fopen(voice, format, "wb", "data");
3294
FAudio_close_out(io_data);
3295
/* io_data file will be filled by SubmitBuffer */
3296
}
3297
FAudio_PlatformUnlockMutex((FAudioMutex) io->lock);
3298
FAudio_close_out(io);
3299
}
3300
3301
static void FAudio_DUMPVOICE_Finalize(const FAudioSourceVoice *voice)
3302
{
3303
const FAudioWaveFormatEx *format = voice->src.format;
3304
3305
/* add dpds subchunk - optional */
3306
DumpVoices_finalize_section(voice, format, "dpds");
3307
/* add data subchunk */
3308
DumpVoices_finalize_section(voice, format, "data");
3309
3310
/* open main file to update filesize */
3311
FAudioIOStreamOut *io = DumpVoices_fopen(voice, format, "r+b", "");
3312
if (!io)
3313
{
3314
return;
3315
}
3316
FAudio_PlatformLockMutex((FAudioMutex) io->lock);
3317
size_t file_size = io->size(io->data);
3318
if (file_size >= 44)
3319
{
3320
/* update filesize */
3321
uint32_t chunk_size = (uint32_t)(file_size - 8);
3322
io->seek(io->data, 4, FAUDIO_SEEK_SET);
3323
io->write(io->data, &chunk_size, 4, 1);
3324
}
3325
FAudio_PlatformUnlockMutex((FAudioMutex) io->lock);
3326
FAudio_close_out(io);
3327
}
3328
3329
static void FAudio_DUMPVOICE_WriteBuffer(
3330
const FAudioSourceVoice *voice,
3331
const FAudioBuffer *pBuffer,
3332
const FAudioBufferWMA *pBufferWMA,
3333
const uint32_t playBegin,
3334
const uint32_t playLength
3335
) {
3336
FAudioIOStreamOut *io_data = DumpVoices_fopen(voice, voice->src.format, "ab", "data");
3337
if (io_data == NULL)
3338
{
3339
return;
3340
}
3341
3342
FAudio_PlatformLockMutex((FAudioMutex) io_data->lock);
3343
if (pBufferWMA != NULL)
3344
{
3345
/* dump encoded buffer contents */
3346
if (pBufferWMA->PacketCount > 0)
3347
{
3348
FAudioIOStreamOut *io_dpds = DumpVoices_fopen(voice, voice->src.format, "ab", "dpds");
3349
if (io_dpds)
3350
{
3351
FAudio_PlatformLockMutex((FAudioMutex) io_dpds->lock);
3352
/* write to dpds file */
3353
io_dpds->write(io_dpds->data, pBufferWMA->pDecodedPacketCumulativeBytes, sizeof(uint32_t), pBufferWMA->PacketCount);
3354
FAudio_PlatformUnlockMutex((FAudioMutex) io_dpds->lock);
3355
FAudio_close_out(io_dpds);
3356
}
3357
/* write buffer contents to data file */
3358
io_data->write(io_data->data, pBuffer->pAudioData, sizeof(uint8_t), pBuffer->AudioBytes);
3359
}
3360
}
3361
else
3362
{
3363
/* dump unencoded buffer contents */
3364
uint16_t bytesPerFrame = (voice->src.format->nChannels * voice->src.format->wBitsPerSample / 8);
3365
FAudio_assert(bytesPerFrame > 0);
3366
const void *pAudioDataBegin = pBuffer->pAudioData + playBegin*bytesPerFrame;
3367
io_data->write(io_data->data, pAudioDataBegin, bytesPerFrame, playLength);
3368
}
3369
FAudio_PlatformUnlockMutex((FAudioMutex) io_data->lock);
3370
FAudio_close_out(io_data);
3371
}
3372
3373
#endif /* FAUDIO_DUMP_VOICES */
3374
3375
/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
3376
3377