1 /* FAudio - XAudio Reimplementation for FNA
3 * Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team
5 * This software is provided 'as-is', without any express or implied warranty.
6 * In no event will the authors be held liable for any damages arising from
7 * the use of this software.
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software in a
15 * product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
18 * 2. Altered source versions must be plainly marked as such, and must not be
19 * misrepresented as being the original software.
21 * 3. This notice may not be removed or altered from any source distribution.
23 * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
27 #include "FAudio_internal.h"
29 #define MAKE_SUBFORMAT_GUID(guid, fmt) \
30 FAudioGUID DATAFORMAT_SUBTYPE_##guid = \
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
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
65 #endif /* FAUDIO_DUMP_VOICES */
69 uint32_t FAudioLinkedVersion(void)
71 return FAUDIO_COMPILED_VERSION
;
74 /* FAudio Interface */
76 uint32_t FAudioCreate(
79 FAudioProcessor XAudio2Processor
81 FAudioCOMConstructEXT(ppFAudio
, FAUDIO_TARGET_VERSION
);
82 FAudio_Initialize(*ppFAudio
, Flags
, XAudio2Processor
);
86 uint32_t FAudioCOMConstructEXT(FAudio
**ppFAudio
, uint8_t version
)
88 return FAudioCOMConstructWithCustomAllocatorEXT(
97 uint32_t FAudioCreateWithCustomAllocatorEXT(
100 FAudioProcessor XAudio2Processor
,
101 FAudioMallocFunc customMalloc
,
102 FAudioFreeFunc customFree
,
103 FAudioReallocFunc customRealloc
105 FAudioCOMConstructWithCustomAllocatorEXT(
107 FAUDIO_TARGET_VERSION
,
112 FAudio_Initialize(*ppFAudio
, Flags
, XAudio2Processor
);
116 uint32_t FAudioCOMConstructWithCustomAllocatorEXT(
119 FAudioMallocFunc customMalloc
,
120 FAudioFreeFunc customFree
,
121 FAudioReallocFunc customRealloc
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;
148 uint32_t FAudio_AddRef(FAudio
*audio
)
151 audio
->refcount
+= 1;
153 return audio
->refcount
;
156 uint32_t FAudio_Release(FAudio
*audio
)
160 audio
->refcount
-= 1;
161 refcount
= audio
->refcount
;
162 if (audio
->refcount
== 0)
164 FAudio_OPERATIONSET_ClearAll(audio
);
165 FAudio_StopEngine(audio
);
166 audio
->pFree(audio
->decodeCache
);
167 audio
->pFree(audio
->resampleCache
);
168 audio
->pFree(audio
->effectChainCache
);
169 LOG_MUTEX_DESTROY(audio
, audio
->sourceLock
)
170 FAudio_PlatformDestroyMutex(audio
->sourceLock
);
171 LOG_MUTEX_DESTROY(audio
, audio
->submixLock
)
172 FAudio_PlatformDestroyMutex(audio
->submixLock
);
173 LOG_MUTEX_DESTROY(audio
, audio
->callbackLock
)
174 FAudio_PlatformDestroyMutex(audio
->callbackLock
);
175 LOG_MUTEX_DESTROY(audio
, audio
->operationLock
)
176 FAudio_PlatformDestroyMutex(audio
->operationLock
);
178 FAudio_PlatformRelease();
187 uint32_t FAudio_GetDeviceCount(FAudio
*audio
, uint32_t *pCount
)
190 *pCount
= FAudio_PlatformGetDeviceCount();
195 uint32_t FAudio_GetDeviceDetails(
198 FAudioDeviceDetails
*pDeviceDetails
202 result
= FAudio_PlatformGetDeviceDetails(Index
, pDeviceDetails
);
207 uint32_t FAudio_Initialize(
210 FAudioProcessor XAudio2Processor
213 FAudio_assert(Flags
== 0 || Flags
== FAUDIO_DEBUG_ENGINE
);
214 FAudio_assert(XAudio2Processor
== FAUDIO_DEFAULT_PROCESSOR
);
216 audio
->initFlags
= Flags
;
218 /* FIXME: This is lazy... */
219 audio
->decodeCache
= (float*) audio
->pMalloc(sizeof(float));
220 audio
->resampleCache
= (float*) audio
->pMalloc(sizeof(float));
221 audio
->decodeSamples
= 1;
222 audio
->resampleSamples
= 1;
224 FAudio_StartEngine(audio
);
229 uint32_t FAudio_RegisterForCallbacks(
231 FAudioEngineCallback
*pCallback
244 void FAudio_UnregisterForCallbacks(
246 FAudioEngineCallback
*pCallback
249 LinkedList_RemoveEntry(
258 uint32_t FAudio_CreateSourceVoice(
260 FAudioSourceVoice
**ppSourceVoice
,
261 const FAudioWaveFormatEx
*pSourceFormat
,
263 float MaxFrequencyRatio
,
264 FAudioVoiceCallback
*pCallback
,
265 const FAudioVoiceSends
*pSendList
,
266 const FAudioEffectChain
*pEffectChain
271 LOG_FORMAT(audio
, pSourceFormat
)
273 *ppSourceVoice
= (FAudioSourceVoice
*) audio
->pMalloc(sizeof(FAudioVoice
));
274 FAudio_zero(*ppSourceVoice
, sizeof(FAudioSourceVoice
));
275 (*ppSourceVoice
)->audio
= audio
;
276 (*ppSourceVoice
)->type
= FAUDIO_VOICE_SOURCE
;
277 (*ppSourceVoice
)->flags
= Flags
;
278 (*ppSourceVoice
)->filter
.Type
= FAUDIO_DEFAULT_FILTER_TYPE
;
279 (*ppSourceVoice
)->filter
.Frequency
= FAUDIO_DEFAULT_FILTER_FREQUENCY
;
280 (*ppSourceVoice
)->filter
.OneOverQ
= FAUDIO_DEFAULT_FILTER_ONEOVERQ
;
281 (*ppSourceVoice
)->filter
.WetDryMix
= FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT
;
282 (*ppSourceVoice
)->sendLock
= FAudio_PlatformCreateMutex();
283 LOG_MUTEX_CREATE(audio
, (*ppSourceVoice
)->sendLock
)
284 (*ppSourceVoice
)->effectLock
= FAudio_PlatformCreateMutex();
285 LOG_MUTEX_CREATE(audio
, (*ppSourceVoice
)->effectLock
)
286 (*ppSourceVoice
)->filterLock
= FAudio_PlatformCreateMutex();
287 LOG_MUTEX_CREATE(audio
, (*ppSourceVoice
)->filterLock
)
288 (*ppSourceVoice
)->volumeLock
= FAudio_PlatformCreateMutex();
289 LOG_MUTEX_CREATE(audio
, (*ppSourceVoice
)->volumeLock
)
291 /* Source Properties */
292 FAudio_assert(MaxFrequencyRatio
<= FAUDIO_MAX_FREQ_RATIO
);
293 (*ppSourceVoice
)->src
.maxFreqRatio
= MaxFrequencyRatio
;
295 if ( pSourceFormat
->wFormatTag
== FAUDIO_FORMAT_PCM
||
296 pSourceFormat
->wFormatTag
== FAUDIO_FORMAT_IEEE_FLOAT
||
297 pSourceFormat
->wFormatTag
== FAUDIO_FORMAT_WMAUDIO2
||
298 pSourceFormat
->wFormatTag
== FAUDIO_FORMAT_WMAUDIO3
)
300 FAudioWaveFormatExtensible
*fmtex
= (FAudioWaveFormatExtensible
*) audio
->pMalloc(
301 sizeof(FAudioWaveFormatExtensible
)
303 /* convert PCM to EXTENSIBLE */
304 fmtex
->Format
.wFormatTag
= FAUDIO_FORMAT_EXTENSIBLE
;
305 fmtex
->Format
.nChannels
= pSourceFormat
->nChannels
;
306 fmtex
->Format
.nSamplesPerSec
= pSourceFormat
->nSamplesPerSec
;
307 fmtex
->Format
.nAvgBytesPerSec
= pSourceFormat
->nAvgBytesPerSec
;
308 fmtex
->Format
.nBlockAlign
= pSourceFormat
->nBlockAlign
;
309 fmtex
->Format
.wBitsPerSample
= pSourceFormat
->wBitsPerSample
;
310 fmtex
->Format
.cbSize
= sizeof(FAudioWaveFormatExtensible
) - sizeof(FAudioWaveFormatEx
);
311 fmtex
->Samples
.wValidBitsPerSample
= pSourceFormat
->wBitsPerSample
;
312 fmtex
->dwChannelMask
= 0;
313 if (pSourceFormat
->wFormatTag
== FAUDIO_FORMAT_PCM
)
315 FAudio_memcpy(&fmtex
->SubFormat
, &DATAFORMAT_SUBTYPE_PCM
, sizeof(FAudioGUID
));
317 else if (pSourceFormat
->wFormatTag
== FAUDIO_FORMAT_IEEE_FLOAT
)
319 FAudio_memcpy(&fmtex
->SubFormat
, &DATAFORMAT_SUBTYPE_IEEE_FLOAT
, sizeof(FAudioGUID
));
321 else if (pSourceFormat
->wFormatTag
== FAUDIO_FORMAT_WMAUDIO2
)
323 FAudio_memcpy(&fmtex
->SubFormat
, &DATAFORMAT_SUBTYPE_WMAUDIO2
, sizeof(FAudioGUID
));
325 else if (pSourceFormat
->wFormatTag
== FAUDIO_FORMAT_WMAUDIO3
)
327 FAudio_memcpy(&fmtex
->SubFormat
, &DATAFORMAT_SUBTYPE_WMAUDIO3
, sizeof(FAudioGUID
));
329 (*ppSourceVoice
)->src
.format
= &fmtex
->Format
;
331 else if (pSourceFormat
->wFormatTag
== FAUDIO_FORMAT_MSADPCM
)
333 FAudioADPCMWaveFormat
*fmtex
= (FAudioADPCMWaveFormat
*) audio
->pMalloc(
334 sizeof(FAudioADPCMWaveFormat
)
337 /* Copy what we can, ideally the sizes match! */
338 size_t cbSize
= sizeof(FAudioWaveFormatEx
) + pSourceFormat
->cbSize
;
342 FAudio_min(cbSize
, sizeof(FAudioADPCMWaveFormat
))
344 if (cbSize
< sizeof(FAudioADPCMWaveFormat
))
347 ((uint8_t*) fmtex
) + cbSize
,
348 sizeof(FAudioADPCMWaveFormat
) - cbSize
352 /* XAudio2 does not validate this input! */
353 fmtex
->wfx
.cbSize
= sizeof(FAudioADPCMWaveFormat
) - sizeof(FAudioWaveFormatEx
);
354 fmtex
->wSamplesPerBlock
= ((
355 fmtex
->wfx
.nBlockAlign
/ fmtex
->wfx
.nChannels
357 (*ppSourceVoice
)->src
.format
= &fmtex
->wfx
;
359 else if (pSourceFormat
->wFormatTag
== FAUDIO_FORMAT_XMAUDIO2
)
361 FAudioXMA2WaveFormat
*fmtex
= (FAudioXMA2WaveFormat
*) audio
->pMalloc(
362 sizeof(FAudioXMA2WaveFormat
)
365 /* Copy what we can, ideally the sizes match! */
366 size_t cbSize
= sizeof(FAudioWaveFormatEx
) + pSourceFormat
->cbSize
;
370 FAudio_min(cbSize
, sizeof(FAudioXMA2WaveFormat
))
372 if (cbSize
< sizeof(FAudioXMA2WaveFormat
))
375 ((uint8_t*) fmtex
) + cbSize
,
376 sizeof(FAudioADPCMWaveFormat
) - cbSize
380 /* Does XAudio2 validate this input?! */
381 fmtex
->wfx
.cbSize
= sizeof(FAudioXMA2WaveFormat
) - sizeof(FAudioWaveFormatEx
);
382 (*ppSourceVoice
)->src
.format
= &fmtex
->wfx
;
386 /* direct copy anything else */
387 (*ppSourceVoice
)->src
.format
= (FAudioWaveFormatEx
*) audio
->pMalloc(
388 sizeof(FAudioWaveFormatEx
) + pSourceFormat
->cbSize
391 (*ppSourceVoice
)->src
.format
,
393 sizeof(FAudioWaveFormatEx
) + pSourceFormat
->cbSize
397 (*ppSourceVoice
)->src
.callback
= pCallback
;
398 (*ppSourceVoice
)->src
.active
= 0;
399 (*ppSourceVoice
)->src
.freqRatio
= 1.0f
;
400 (*ppSourceVoice
)->src
.totalSamples
= 0;
401 (*ppSourceVoice
)->src
.bufferList
= NULL
;
402 (*ppSourceVoice
)->src
.flushList
= NULL
;
403 (*ppSourceVoice
)->src
.bufferLock
= FAudio_PlatformCreateMutex();
404 LOG_MUTEX_CREATE(audio
, (*ppSourceVoice
)->src
.bufferLock
)
406 if ((*ppSourceVoice
)->src
.format
->wFormatTag
== FAUDIO_FORMAT_EXTENSIBLE
)
408 FAudioWaveFormatExtensible
*fmtex
= (FAudioWaveFormatExtensible
*) (*ppSourceVoice
)->src
.format
;
410 #define COMPARE_GUID(type) \
413 &DATAFORMAT_SUBTYPE_##type, \
416 if (COMPARE_GUID(PCM
))
418 #define DECODER(bit) \
419 if (fmtex->Format.wBitsPerSample == bit) \
421 (*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodePCM##bit; \
431 "Unrecognized wBitsPerSample: %d",
432 fmtex
->Format
.wBitsPerSample
434 FAudio_assert(0 && "Unrecognized wBitsPerSample!");
438 else if (COMPARE_GUID(IEEE_FLOAT
))
440 /* FIXME: Weird behavior!
441 * Prototype creates a source with the IEEE_FLOAT tag,
442 * but it's actually PCM16. It seems to prioritize
443 * wBitsPerSample over the format tag. Not sure if we
444 * should fold this section into the section above...?
447 if (fmtex
->Format
.wBitsPerSample
== 16)
449 (*ppSourceVoice
)->src
.decode
= FAudio_INTERNAL_DecodePCM16
;
453 (*ppSourceVoice
)->src
.decode
= FAudio_INTERNAL_DecodePCM32F
;
456 else if ( COMPARE_GUID(WMAUDIO2
) ||
457 COMPARE_GUID(WMAUDIO3
) ||
458 COMPARE_GUID(WMAUDIO_LOSSLESS
) )
461 if (FAudio_WMADEC_init(*ppSourceVoice
, fmtex
->SubFormat
.Data1
) != 0)
463 (*ppSourceVoice
)->src
.decode
= FAudio_INTERNAL_DecodeWMAERROR
;
466 FAudio_assert(0 && "xWMA is not supported!");
467 (*ppSourceVoice
)->src
.decode
= FAudio_INTERNAL_DecodeWMAERROR
;
468 #endif /* HAVE_WMADEC */
472 FAudio_assert(0 && "Unsupported WAVEFORMATEXTENSIBLE subtype!");
476 else if ((*ppSourceVoice
)->src
.format
->wFormatTag
== FAUDIO_FORMAT_XMAUDIO2
)
479 if (FAudio_WMADEC_init(*ppSourceVoice
, FAUDIO_FORMAT_XMAUDIO2
) != 0)
481 (*ppSourceVoice
)->src
.decode
= FAudio_INTERNAL_DecodeWMAERROR
;
484 FAudio_assert(0 && "XMA2 is not supported!");
485 (*ppSourceVoice
)->src
.decode
= FAudio_INTERNAL_DecodeWMAERROR
;
486 #endif /* HAVE_WMADEC */
488 else if ((*ppSourceVoice
)->src
.format
->wFormatTag
== FAUDIO_FORMAT_MSADPCM
)
490 (*ppSourceVoice
)->src
.decode
= ((*ppSourceVoice
)->src
.format
->nChannels
== 2) ?
491 FAudio_INTERNAL_DecodeStereoMSADPCM
:
492 FAudio_INTERNAL_DecodeMonoMSADPCM
;
496 FAudio_assert(0 && "Unsupported format tag!");
499 if ((*ppSourceVoice
)->src
.format
->nChannels
== 1)
501 (*ppSourceVoice
)->src
.resample
= FAudio_INTERNAL_ResampleMono
;
503 else if ((*ppSourceVoice
)->src
.format
->nChannels
== 2)
505 (*ppSourceVoice
)->src
.resample
= FAudio_INTERNAL_ResampleStereo
;
509 (*ppSourceVoice
)->src
.resample
= FAudio_INTERNAL_ResampleGeneric
;
512 (*ppSourceVoice
)->src
.curBufferOffset
= 0;
515 FAudio_INTERNAL_VoiceOutputFrequency(*ppSourceVoice
, pSendList
);
516 FAudioVoice_SetEffectChain(*ppSourceVoice
, pEffectChain
);
519 (*ppSourceVoice
)->volume
= 1.0f
;
520 (*ppSourceVoice
)->channelVolume
= (float*) audio
->pMalloc(
521 sizeof(float) * (*ppSourceVoice
)->outputChannels
523 for (i
= 0; i
< (*ppSourceVoice
)->outputChannels
; i
+= 1)
525 (*ppSourceVoice
)->channelVolume
[i
] = 1.0f
;
528 FAudioVoice_SetOutputVoices(*ppSourceVoice
, pSendList
);
531 if (Flags
& FAUDIO_VOICE_USEFILTER
)
533 (*ppSourceVoice
)->filterState
= (FAudioFilterState
*) audio
->pMalloc(
534 sizeof(FAudioFilterState
) * (*ppSourceVoice
)->src
.format
->nChannels
537 (*ppSourceVoice
)->filterState
,
538 sizeof(FAudioFilterState
) * (*ppSourceVoice
)->src
.format
->nChannels
543 (*ppSourceVoice
)->src
.decodeSamples
= (uint32_t) (FAudio_ceil(
544 (double) audio
->updateSize
*
545 (double) MaxFrequencyRatio
*
546 (double) (*ppSourceVoice
)->src
.format
->nSamplesPerSec
/
547 (double) audio
->master
->master
.inputSampleRate
548 )) + EXTRA_DECODE_PADDING
* (*ppSourceVoice
)->src
.format
->nChannels
;
549 FAudio_INTERNAL_ResizeDecodeCache(
551 ((*ppSourceVoice
)->src
.decodeSamples
+ EXTRA_DECODE_PADDING
) * (*ppSourceVoice
)->src
.format
->nChannels
554 LOG_INFO(audio
, "-> %p", (void*) (*ppSourceVoice
))
556 /* Add to list, finally. */
557 LinkedList_PrependEntry(
563 FAudio_AddRef(audio
);
565 #ifdef FAUDIO_DUMP_VOICES
566 FAudio_DUMPVOICE_Init(*ppSourceVoice
);
567 #endif /* FAUDIO_DUMP_VOICES */
573 uint32_t FAudio_CreateSubmixVoice(
575 FAudioSubmixVoice
**ppSubmixVoice
,
576 uint32_t InputChannels
,
577 uint32_t InputSampleRate
,
579 uint32_t ProcessingStage
,
580 const FAudioVoiceSends
*pSendList
,
581 const FAudioEffectChain
*pEffectChain
587 *ppSubmixVoice
= (FAudioSubmixVoice
*) audio
->pMalloc(sizeof(FAudioVoice
));
588 FAudio_zero(*ppSubmixVoice
, sizeof(FAudioSubmixVoice
));
589 (*ppSubmixVoice
)->audio
= audio
;
590 (*ppSubmixVoice
)->type
= FAUDIO_VOICE_SUBMIX
;
591 (*ppSubmixVoice
)->flags
= Flags
;
592 (*ppSubmixVoice
)->filter
.Type
= FAUDIO_DEFAULT_FILTER_TYPE
;
593 (*ppSubmixVoice
)->filter
.Frequency
= FAUDIO_DEFAULT_FILTER_FREQUENCY
;
594 (*ppSubmixVoice
)->filter
.OneOverQ
= FAUDIO_DEFAULT_FILTER_ONEOVERQ
;
595 (*ppSubmixVoice
)->filter
.WetDryMix
= FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT
;
596 (*ppSubmixVoice
)->sendLock
= FAudio_PlatformCreateMutex();
597 LOG_MUTEX_CREATE(audio
, (*ppSubmixVoice
)->sendLock
)
598 (*ppSubmixVoice
)->effectLock
= FAudio_PlatformCreateMutex();
599 LOG_MUTEX_CREATE(audio
, (*ppSubmixVoice
)->effectLock
)
600 (*ppSubmixVoice
)->filterLock
= FAudio_PlatformCreateMutex();
601 LOG_MUTEX_CREATE(audio
, (*ppSubmixVoice
)->filterLock
)
602 (*ppSubmixVoice
)->volumeLock
= FAudio_PlatformCreateMutex();
603 LOG_MUTEX_CREATE(audio
, (*ppSubmixVoice
)->volumeLock
)
605 /* Submix Properties */
606 (*ppSubmixVoice
)->mix
.inputChannels
= InputChannels
;
607 (*ppSubmixVoice
)->mix
.inputSampleRate
= InputSampleRate
;
608 (*ppSubmixVoice
)->mix
.processingStage
= ProcessingStage
;
611 if (InputChannels
== 1)
613 (*ppSubmixVoice
)->mix
.resample
= FAudio_INTERNAL_ResampleMono
;
615 else if (InputChannels
== 2)
617 (*ppSubmixVoice
)->mix
.resample
= FAudio_INTERNAL_ResampleStereo
;
621 (*ppSubmixVoice
)->mix
.resample
= FAudio_INTERNAL_ResampleGeneric
;
625 (*ppSubmixVoice
)->mix
.inputSamples
= ((uint32_t) FAudio_ceil(
627 (double) InputSampleRate
/
628 (double) audio
->master
->master
.inputSampleRate
629 ) + EXTRA_DECODE_PADDING
) * InputChannels
;
630 (*ppSubmixVoice
)->mix
.inputCache
= (float*) audio
->pMalloc(
631 sizeof(float) * (*ppSubmixVoice
)->mix
.inputSamples
633 FAudio_zero( /* Zero this now, for the first update */
634 (*ppSubmixVoice
)->mix
.inputCache
,
635 sizeof(float) * (*ppSubmixVoice
)->mix
.inputSamples
639 FAudio_INTERNAL_VoiceOutputFrequency(*ppSubmixVoice
, pSendList
);
640 FAudioVoice_SetEffectChain(*ppSubmixVoice
, pEffectChain
);
643 (*ppSubmixVoice
)->volume
= 1.0f
;
644 (*ppSubmixVoice
)->channelVolume
= (float*) audio
->pMalloc(
645 sizeof(float) * (*ppSubmixVoice
)->outputChannels
647 for (i
= 0; i
< (*ppSubmixVoice
)->outputChannels
; i
+= 1)
649 (*ppSubmixVoice
)->channelVolume
[i
] = 1.0f
;
652 FAudioVoice_SetOutputVoices(*ppSubmixVoice
, pSendList
);
655 if (Flags
& FAUDIO_VOICE_USEFILTER
)
657 (*ppSubmixVoice
)->filterState
= (FAudioFilterState
*) audio
->pMalloc(
658 sizeof(FAudioFilterState
) * InputChannels
661 (*ppSubmixVoice
)->filterState
,
662 sizeof(FAudioFilterState
) * InputChannels
666 /* Add to list, finally. */
667 FAudio_INTERNAL_InsertSubmixSorted(
673 FAudio_AddRef(audio
);
679 uint32_t FAudio_CreateMasteringVoice(
681 FAudioMasteringVoice
**ppMasteringVoice
,
682 uint32_t InputChannels
,
683 uint32_t InputSampleRate
,
685 uint32_t DeviceIndex
,
686 const FAudioEffectChain
*pEffectChain
690 /* For now we only support one allocated master voice at a time */
691 FAudio_assert(audio
->master
== NULL
);
693 if ( InputChannels
== FAUDIO_DEFAULT_CHANNELS
||
694 InputSampleRate
== FAUDIO_DEFAULT_SAMPLERATE
)
696 FAudioDeviceDetails details
;
697 if (FAudio_GetDeviceDetails(audio
, DeviceIndex
, &details
) != 0)
699 return FAUDIO_E_INVALID_CALL
;
701 if (InputChannels
== FAUDIO_DEFAULT_CHANNELS
)
703 InputChannels
= details
.OutputFormat
.Format
.nChannels
;
705 if (InputSampleRate
== FAUDIO_DEFAULT_SAMPLERATE
)
707 InputSampleRate
= details
.OutputFormat
.Format
.nSamplesPerSec
;
711 *ppMasteringVoice
= (FAudioMasteringVoice
*) audio
->pMalloc(sizeof(FAudioVoice
));
712 FAudio_zero(*ppMasteringVoice
, sizeof(FAudioMasteringVoice
));
713 (*ppMasteringVoice
)->audio
= audio
;
714 (*ppMasteringVoice
)->type
= FAUDIO_VOICE_MASTER
;
715 (*ppMasteringVoice
)->flags
= Flags
;
716 (*ppMasteringVoice
)->effectLock
= FAudio_PlatformCreateMutex();
717 LOG_MUTEX_CREATE(audio
, (*ppMasteringVoice
)->effectLock
)
718 (*ppMasteringVoice
)->volumeLock
= FAudio_PlatformCreateMutex();
719 LOG_MUTEX_CREATE(audio
, (*ppMasteringVoice
)->volumeLock
)
722 (*ppMasteringVoice
)->volume
= 1.0f
;
724 /* Master Properties */
725 (*ppMasteringVoice
)->master
.inputChannels
= InputChannels
;
726 (*ppMasteringVoice
)->master
.inputSampleRate
= InputSampleRate
;
729 FAudio_zero(&(*ppMasteringVoice
)->sends
, sizeof(FAudioVoiceSends
));
730 FAudioVoice_SetEffectChain(*ppMasteringVoice
, pEffectChain
);
732 /* This is now safe enough to assign */
733 audio
->master
= *ppMasteringVoice
;
735 /* Build the device format.
736 * The most unintuitive part of this is the use of outputChannels
737 * instead of master.inputChannels. Bizarrely, the effect chain can
738 * dictate the _actual_ output channel count, and when the channel count
739 * mismatches, we have to add a staging buffer for effects to process on
740 * before ultimately copying the final result to the device. ARGH.
742 WriteWaveFormatExtensible(
744 audio
->master
->outputChannels
,
745 audio
->master
->master
.inputSampleRate
,
746 &DATAFORMAT_SUBTYPE_IEEE_FLOAT
749 /* Platform Device */
750 FAudio_AddRef(audio
);
759 if (audio
->platform
== NULL
)
761 FAudioVoice_DestroyVoice(*ppMasteringVoice
);
762 *ppMasteringVoice
= NULL
;
764 /* Not the best code, but it's probably true? */
765 return FAUDIO_E_DEVICE_INVALIDATED
;
767 audio
->master
->outputChannels
= audio
->mixFormat
.Format
.nChannels
;
768 audio
->master
->master
.inputSampleRate
= audio
->mixFormat
.Format
.nSamplesPerSec
;
770 /* Effect Chain Cache */
771 if ((*ppMasteringVoice
)->master
.inputChannels
!= (*ppMasteringVoice
)->outputChannels
)
773 (*ppMasteringVoice
)->master
.effectCache
= (float*) audio
->pMalloc(
776 (*ppMasteringVoice
)->master
.inputChannels
784 uint32_t FAudio_CreateMasteringVoice8(
786 FAudioMasteringVoice
**ppMasteringVoice
,
787 uint32_t InputChannels
,
788 uint32_t InputSampleRate
,
790 uint16_t *szDeviceId
,
791 const FAudioEffectChain
*pEffectChain
,
792 FAudioStreamCategory StreamCategory
794 uint32_t DeviceIndex
, retval
;
798 /* Eventually, we'll want the old CreateMastering to call the new one.
799 * That will depend on us being able to use DeviceID though.
800 * For now, use our little ID hack to turn szDeviceId into DeviceIndex.
803 if (szDeviceId
== NULL
|| szDeviceId
[0] == 0)
809 DeviceIndex
= szDeviceId
[0] - L
'0';
810 if (DeviceIndex
> FAudio_PlatformGetDeviceCount())
816 /* Note that StreamCategory is ignored! */
817 retval
= FAudio_CreateMasteringVoice(
831 void FAudio_SetEngineProcedureEXT(
833 FAudioEngineProcedureEXT clientEngineProc
,
837 audio
->pClientEngineProc
= clientEngineProc
;
838 audio
->clientEngineUser
= user
;
842 uint32_t FAudio_StartEngine(FAudio
*audio
)
850 void FAudio_StopEngine(FAudio
*audio
)
854 FAudio_OPERATIONSET_CommitAll(audio
);
855 FAudio_OPERATIONSET_Execute(audio
);
859 uint32_t FAudio_CommitOperationSet(FAudio
*audio
, uint32_t OperationSet
)
862 if (OperationSet
== FAUDIO_COMMIT_ALL
)
864 FAudio_OPERATIONSET_CommitAll(audio
);
868 FAudio_OPERATIONSET_Commit(audio
, OperationSet
);
874 uint32_t FAudio_CommitChanges(FAudio
*audio
)
877 "IF YOU CAN READ THIS, YOUR PROGRAM IS ABOUT TO BREAK!"
878 "\n\nEither you or somebody else is using FAudio_CommitChanges,"
879 "\nwhen they should be using FAudio_CommitOperationSet instead."
880 "\n\nIf your program calls this, move to CommitOperationSet."
881 "\n\nIf somebody else is calling this, find out who it is and"
882 "\nfile a bug report with them ASAP."
885 /* Seriously, this is like the worst possible thing short of no-oping.
886 * For the love-a Pete, just migrate, do it, what is wrong with you
888 return FAudio_CommitOperationSet(audio
, FAUDIO_COMMIT_ALL
);
891 void FAudio_GetPerformanceData(
893 FAudioPerformanceData
*pPerfData
896 FAudioSourceVoice
*source
;
900 FAudio_zero(pPerfData
, sizeof(FAudioPerformanceData
));
902 FAudio_PlatformLockMutex(audio
->sourceLock
);
903 LOG_MUTEX_LOCK(audio
, audio
->sourceLock
)
904 list
= audio
->sources
;
907 source
= (FAudioSourceVoice
*) list
->entry
;
908 pPerfData
->TotalSourceVoiceCount
+= 1;
909 if (source
->src
.active
)
911 pPerfData
->ActiveSourceVoiceCount
+= 1;
915 FAudio_PlatformUnlockMutex(audio
->sourceLock
);
916 LOG_MUTEX_UNLOCK(audio
, audio
->sourceLock
)
918 FAudio_PlatformLockMutex(audio
->submixLock
);
919 LOG_MUTEX_LOCK(audio
, audio
->submixLock
)
920 list
= audio
->submixes
;
923 pPerfData
->ActiveSubmixVoiceCount
+= 1;
926 FAudio_PlatformUnlockMutex(audio
->submixLock
);
927 LOG_MUTEX_UNLOCK(audio
, audio
->submixLock
)
929 if (audio
->master
!= NULL
)
931 /* estimate, should use real latency from platform */
932 pPerfData
->CurrentLatencyInSamples
= 2 * audio
->updateSize
;
938 void FAudio_SetDebugConfiguration(
940 FAudioDebugConfiguration
*pDebugConfiguration
,
943 #ifndef FAUDIO_DISABLE_DEBUGCONFIGURATION
951 sizeof(FAudioDebugConfiguration
)
954 env
= FAudio_getenv("FAUDIO_LOG_EVERYTHING");
955 if (env
!= NULL
&& *env
== '1')
957 audio
->debug
.TraceMask
= (
959 FAUDIO_LOG_WARNINGS
|
962 FAUDIO_LOG_API_CALLS
|
963 FAUDIO_LOG_FUNC_CALLS
|
969 audio
->debug
.LogThreadID
= 1;
970 audio
->debug
.LogFunctionName
= 1;
971 audio
->debug
.LogTiming
= 1;
974 #define CHECK_ENV(type) \
975 env = FAudio_getenv("FAUDIO_LOG_" #type); \
980 audio->debug.TraceMask |= FAUDIO_LOG_##type; \
984 audio->debug.TraceMask &= ~FAUDIO_LOG_##type; \
992 CHECK_ENV(FUNC_CALLS
)
998 #define CHECK_ENV(envvar, boolvar) \
999 env = FAudio_getenv("FAUDIO_LOG_LOG" #envvar); \
1002 audio->debug.Log##boolvar = (*env == '1'); \
1004 CHECK_ENV(THREADID
, ThreadID
)
1005 CHECK_ENV(FILELINE
, Fileline
)
1006 CHECK_ENV(FUNCTIONNAME
, FunctionName
)
1007 CHECK_ENV(TIMING
, Timing
)
1011 #endif /* FAUDIO_DISABLE_DEBUGCONFIGURATION */
1014 void FAudio_GetProcessingQuantum(
1016 uint32_t *quantumNumerator
,
1017 uint32_t *quantumDenominator
1019 FAudio_assert(audio
->master
!= NULL
);
1020 if (quantumNumerator
!= NULL
)
1022 *quantumNumerator
= audio
->updateSize
;
1024 if (quantumDenominator
!= NULL
)
1026 *quantumDenominator
= audio
->master
->master
.inputSampleRate
;
1030 /* FAudioVoice Interface */
1032 static void FAudio_RecalcMixMatrix(FAudioVoice
*voice
, uint32_t sendIndex
)
1034 uint32_t oChan
, s
, d
;
1035 FAudioVoice
*out
= voice
->sends
.pSends
[sendIndex
].pOutputVoice
;
1036 float volume
, *matrix
= voice
->mixCoefficients
[sendIndex
];
1038 if (voice
->type
== FAUDIO_VOICE_SUBMIX
)
1044 volume
= voice
->volume
;
1047 if (out
->type
== FAUDIO_VOICE_MASTER
)
1049 oChan
= out
->master
.inputChannels
;
1053 oChan
= out
->mix
.inputChannels
;
1056 for (d
= 0; d
< oChan
; d
+= 1)
1058 for (s
= 0; s
< voice
->outputChannels
; s
+= 1)
1060 matrix
[d
* voice
->outputChannels
+ s
] = volume
*
1061 voice
->channelVolume
[s
] *
1062 voice
->sendCoefficients
[sendIndex
][d
* voice
->outputChannels
+ s
];
1067 void FAudioVoice_GetVoiceDetails(
1069 FAudioVoiceDetails
*pVoiceDetails
1071 LOG_API_ENTER(voice
->audio
)
1073 pVoiceDetails
->CreationFlags
= voice
->flags
;
1074 pVoiceDetails
->ActiveFlags
= voice
->flags
;
1075 if (voice
->type
== FAUDIO_VOICE_SOURCE
)
1077 pVoiceDetails
->InputChannels
= voice
->src
.format
->nChannels
;
1078 pVoiceDetails
->InputSampleRate
= voice
->src
.format
->nSamplesPerSec
;
1080 else if (voice
->type
== FAUDIO_VOICE_SUBMIX
)
1082 pVoiceDetails
->InputChannels
= voice
->mix
.inputChannels
;
1083 pVoiceDetails
->InputSampleRate
= voice
->mix
.inputSampleRate
;
1085 else if (voice
->type
== FAUDIO_VOICE_MASTER
)
1087 pVoiceDetails
->InputChannels
= voice
->master
.inputChannels
;
1088 pVoiceDetails
->InputSampleRate
= voice
->master
.inputSampleRate
;
1092 FAudio_assert(0 && "Unknown voice type!");
1095 LOG_API_EXIT(voice
->audio
)
1098 uint32_t FAudioVoice_SetOutputVoices(
1100 const FAudioVoiceSends
*pSendList
1103 uint32_t outChannels
;
1104 FAudioVoiceSends defaultSends
;
1105 FAudioSendDescriptor defaultSend
;
1107 LOG_API_ENTER(voice
->audio
)
1109 if (voice
->type
== FAUDIO_VOICE_MASTER
)
1111 LOG_API_EXIT(voice
->audio
)
1112 return FAUDIO_E_INVALID_CALL
;
1115 FAudio_PlatformLockMutex(voice
->sendLock
);
1116 LOG_MUTEX_LOCK(voice
->audio
, voice
->sendLock
)
1118 if (FAudio_INTERNAL_VoiceOutputFrequency(voice
, pSendList
) != 0)
1123 "Changing the sample rate while an effect chain is attached is invalid!"
1125 FAudio_PlatformUnlockMutex(voice
->sendLock
);
1126 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->sendLock
)
1127 LOG_API_EXIT(voice
->audio
)
1128 return FAUDIO_E_INVALID_CALL
;
1131 FAudio_PlatformLockMutex(voice
->volumeLock
);
1132 LOG_MUTEX_LOCK(voice
->audio
, voice
->volumeLock
)
1134 /* FIXME: This is lazy... */
1135 for (i
= 0; i
< voice
->sends
.SendCount
; i
+= 1)
1137 voice
->audio
->pFree(voice
->sendCoefficients
[i
]);
1139 if (voice
->sendCoefficients
!= NULL
)
1141 voice
->audio
->pFree(voice
->sendCoefficients
);
1143 for (i
= 0; i
< voice
->sends
.SendCount
; i
+= 1)
1145 voice
->audio
->pFree(voice
->mixCoefficients
[i
]);
1147 if (voice
->mixCoefficients
!= NULL
)
1149 voice
->audio
->pFree(voice
->mixCoefficients
);
1151 if (voice
->sendMix
!= NULL
)
1153 voice
->audio
->pFree(voice
->sendMix
);
1155 if (voice
->sendFilter
!= NULL
)
1157 voice
->audio
->pFree(voice
->sendFilter
);
1158 voice
->sendFilter
= NULL
;
1160 if (voice
->sendFilterState
!= NULL
)
1162 for (i
= 0; i
< voice
->sends
.SendCount
; i
+= 1)
1164 if (voice
->sendFilterState
[i
] != NULL
)
1166 voice
->audio
->pFree(voice
->sendFilterState
[i
]);
1169 voice
->audio
->pFree(voice
->sendFilterState
);
1170 voice
->sendFilterState
= NULL
;
1172 if (voice
->sends
.pSends
!= NULL
)
1174 voice
->audio
->pFree(voice
->sends
.pSends
);
1177 if (pSendList
== NULL
)
1179 /* Default to the mastering voice as output */
1180 defaultSend
.Flags
= 0;
1181 defaultSend
.pOutputVoice
= voice
->audio
->master
;
1182 defaultSends
.SendCount
= 1;
1183 defaultSends
.pSends
= &defaultSend
;
1184 pSendList
= &defaultSends
;
1186 else if (pSendList
->SendCount
== 0)
1188 /* No sends? Nothing to do... */
1189 voice
->sendCoefficients
= NULL
;
1190 voice
->mixCoefficients
= NULL
;
1191 voice
->sendMix
= NULL
;
1192 FAudio_zero(&voice
->sends
, sizeof(FAudioVoiceSends
));
1194 FAudio_PlatformUnlockMutex(voice
->volumeLock
);
1195 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->volumeLock
)
1196 FAudio_PlatformUnlockMutex(voice
->sendLock
);
1197 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->sendLock
)
1198 LOG_API_EXIT(voice
->audio
)
1202 /* Copy send list */
1203 voice
->sends
.SendCount
= pSendList
->SendCount
;
1204 voice
->sends
.pSends
= (FAudioSendDescriptor
*) voice
->audio
->pMalloc(
1205 pSendList
->SendCount
* sizeof(FAudioSendDescriptor
)
1208 voice
->sends
.pSends
,
1210 pSendList
->SendCount
* sizeof(FAudioSendDescriptor
)
1213 /* Allocate/Reset default output matrix, mixer function, filters */
1214 voice
->sendCoefficients
= (float**) voice
->audio
->pMalloc(
1215 sizeof(float*) * pSendList
->SendCount
1217 voice
->mixCoefficients
= (float**) voice
->audio
->pMalloc(
1218 sizeof(float*) * pSendList
->SendCount
1220 voice
->sendMix
= (FAudioMixCallback
*) voice
->audio
->pMalloc(
1221 sizeof(FAudioMixCallback
) * pSendList
->SendCount
1224 for (i
= 0; i
< pSendList
->SendCount
; i
+= 1)
1226 if (pSendList
->pSends
[i
].pOutputVoice
->type
== FAUDIO_VOICE_MASTER
)
1228 outChannels
= pSendList
->pSends
[i
].pOutputVoice
->master
.inputChannels
;
1232 outChannels
= pSendList
->pSends
[i
].pOutputVoice
->mix
.inputChannels
;
1234 voice
->sendCoefficients
[i
] = (float*) voice
->audio
->pMalloc(
1235 sizeof(float) * voice
->outputChannels
* outChannels
1237 voice
->mixCoefficients
[i
] = (float*) voice
->audio
->pMalloc(
1238 sizeof(float) * voice
->outputChannels
* outChannels
1241 FAudio_assert(voice
->outputChannels
> 0 && voice
->outputChannels
< 9);
1242 FAudio_assert(outChannels
> 0 && outChannels
< 9);
1244 voice
->sendCoefficients
[i
],
1245 FAUDIO_INTERNAL_MATRIX_DEFAULTS
[voice
->outputChannels
- 1][outChannels
- 1],
1246 voice
->outputChannels
* outChannels
* sizeof(float)
1248 FAudio_RecalcMixMatrix(voice
, i
);
1250 if (voice
->outputChannels
== 1)
1252 if (outChannels
== 1)
1254 voice
->sendMix
[i
] = FAudio_INTERNAL_Mix_1in_1out_Scalar
;
1256 else if (outChannels
== 2)
1258 voice
->sendMix
[i
] = FAudio_INTERNAL_Mix_1in_2out_Scalar
;
1260 else if (outChannels
== 6)
1262 voice
->sendMix
[i
] = FAudio_INTERNAL_Mix_1in_6out_Scalar
;
1264 else if (outChannels
== 8)
1266 voice
->sendMix
[i
] = FAudio_INTERNAL_Mix_1in_8out_Scalar
;
1270 voice
->sendMix
[i
] = FAudio_INTERNAL_Mix_Generic
;
1273 else if (voice
->outputChannels
== 2)
1275 if (outChannels
== 1)
1277 voice
->sendMix
[i
] = FAudio_INTERNAL_Mix_2in_1out_Scalar
;
1279 else if (outChannels
== 2)
1281 voice
->sendMix
[i
] = FAudio_INTERNAL_Mix_2in_2out_Scalar
;
1283 else if (outChannels
== 6)
1285 voice
->sendMix
[i
] = FAudio_INTERNAL_Mix_2in_6out_Scalar
;
1287 else if (outChannels
== 8)
1289 voice
->sendMix
[i
] = FAudio_INTERNAL_Mix_2in_8out_Scalar
;
1293 voice
->sendMix
[i
] = FAudio_INTERNAL_Mix_Generic
;
1298 voice
->sendMix
[i
] = FAudio_INTERNAL_Mix_Generic
;
1301 if (pSendList
->pSends
[i
].Flags
& FAUDIO_SEND_USEFILTER
)
1303 /* Allocate the whole send filter array if needed... */
1304 if (voice
->sendFilter
== NULL
)
1306 voice
->sendFilter
= (FAudioFilterParametersEXT
*) voice
->audio
->pMalloc(
1307 sizeof(FAudioFilterParametersEXT
) * pSendList
->SendCount
1310 if (voice
->sendFilterState
== NULL
)
1312 voice
->sendFilterState
= (FAudioFilterState
**) voice
->audio
->pMalloc(
1313 sizeof(FAudioFilterState
*) * pSendList
->SendCount
1316 voice
->sendFilterState
,
1317 sizeof(FAudioFilterState
*) * pSendList
->SendCount
1321 /* ... then fill in this send's filter data */
1322 voice
->sendFilter
[i
].Type
= FAUDIO_DEFAULT_FILTER_TYPE
;
1323 voice
->sendFilter
[i
].Frequency
= FAUDIO_DEFAULT_FILTER_FREQUENCY
;
1324 voice
->sendFilter
[i
].OneOverQ
= FAUDIO_DEFAULT_FILTER_ONEOVERQ
;
1325 voice
->sendFilter
[i
].WetDryMix
= FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT
;
1326 voice
->sendFilterState
[i
] = (FAudioFilterState
*) voice
->audio
->pMalloc(
1327 sizeof(FAudioFilterState
) * outChannels
1330 voice
->sendFilterState
[i
],
1331 sizeof(FAudioFilterState
) * outChannels
1336 FAudio_PlatformUnlockMutex(voice
->volumeLock
);
1337 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->volumeLock
)
1339 FAudio_PlatformUnlockMutex(voice
->sendLock
);
1340 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->sendLock
)
1341 LOG_API_EXIT(voice
->audio
)
1345 uint32_t FAudioVoice_SetEffectChain(
1347 const FAudioEffectChain
*pEffectChain
1351 uint32_t channelCount
;
1352 FAudioVoiceDetails voiceDetails
;
1353 FAPORegistrationProperties
*pProps
;
1354 FAudioWaveFormatExtensible srcFmt
, dstFmt
;
1355 FAPOLockForProcessBufferParameters srcLockParams
, dstLockParams
;
1357 LOG_API_ENTER(voice
->audio
)
1359 FAudioVoice_GetVoiceDetails(voice
, &voiceDetails
);
1361 /* SetEffectChain must not change the number of output channels once the voice has been created */
1362 if (pEffectChain
== NULL
&& voice
->outputChannels
!= 0)
1364 /* cannot remove an effect chain that changes the number of channels */
1365 if (voice
->outputChannels
!= voiceDetails
.InputChannels
)
1370 "Cannot remove effect chain that changes the number of channels"
1372 FAudio_assert(0 && "Cannot remove effect chain that changes the number of channels");
1373 LOG_API_EXIT(voice
->audio
)
1374 return FAUDIO_E_INVALID_CALL
;
1378 if (pEffectChain
!= NULL
&& voice
->outputChannels
!= 0)
1380 uint32_t lst
= pEffectChain
->EffectCount
- 1;
1382 /* new effect chain must have same number of output channels */
1383 if (voice
->outputChannels
!= pEffectChain
->pEffectDescriptors
[lst
].OutputChannels
)
1388 "New effect chain must have same number of output channels as the old chain"
1390 FAudio_assert(0 && "New effect chain must have same number of output channels as the old chain");
1391 LOG_API_EXIT(voice
->audio
)
1392 return FAUDIO_E_INVALID_CALL
;
1396 FAudio_PlatformLockMutex(voice
->effectLock
);
1397 LOG_MUTEX_LOCK(voice
->audio
, voice
->effectLock
)
1399 if (pEffectChain
== NULL
)
1401 FAudio_INTERNAL_FreeEffectChain(voice
);
1402 FAudio_zero(&voice
->effects
, sizeof(voice
->effects
));
1403 voice
->outputChannels
= voiceDetails
.InputChannels
;
1407 /* Validate incoming chain before changing the current chain */
1409 /* These are always the same, so just write them now. */
1410 srcLockParams
.pFormat
= &srcFmt
.Format
;
1411 dstLockParams
.pFormat
= &dstFmt
.Format
;
1412 if (voice
->type
== FAUDIO_VOICE_SOURCE
)
1414 srcLockParams
.MaxFrameCount
= voice
->src
.resampleSamples
;
1415 dstLockParams
.MaxFrameCount
= voice
->src
.resampleSamples
;
1417 else if (voice
->type
== FAUDIO_VOICE_SUBMIX
)
1419 srcLockParams
.MaxFrameCount
= voice
->mix
.outputSamples
;
1420 dstLockParams
.MaxFrameCount
= voice
->mix
.outputSamples
;
1422 else if (voice
->type
== FAUDIO_VOICE_MASTER
)
1424 srcLockParams
.MaxFrameCount
= voice
->audio
->updateSize
;
1425 dstLockParams
.MaxFrameCount
= voice
->audio
->updateSize
;
1428 /* The first source is the voice input data... */
1429 srcFmt
.Format
.wBitsPerSample
= 32;
1430 srcFmt
.Format
.wFormatTag
= FAUDIO_FORMAT_EXTENSIBLE
;
1431 srcFmt
.Format
.nChannels
= voiceDetails
.InputChannels
;
1432 srcFmt
.Format
.nSamplesPerSec
= voiceDetails
.InputSampleRate
;
1433 srcFmt
.Format
.nBlockAlign
= srcFmt
.Format
.nChannels
* (srcFmt
.Format
.wBitsPerSample
/ 8);
1434 srcFmt
.Format
.nAvgBytesPerSec
= srcFmt
.Format
.nSamplesPerSec
* srcFmt
.Format
.nBlockAlign
;
1435 srcFmt
.Format
.cbSize
= sizeof(FAudioWaveFormatExtensible
) - sizeof(FAudioWaveFormatEx
);
1436 srcFmt
.Samples
.wValidBitsPerSample
= srcFmt
.Format
.wBitsPerSample
;
1437 srcFmt
.dwChannelMask
= 0;
1438 FAudio_memcpy(&srcFmt
.SubFormat
, &DATAFORMAT_SUBTYPE_IEEE_FLOAT
, sizeof(FAudioGUID
));
1439 FAudio_memcpy(&dstFmt
, &srcFmt
, sizeof(srcFmt
));
1441 for (i
= 0; i
< pEffectChain
->EffectCount
; i
+= 1)
1443 fapo
= pEffectChain
->pEffectDescriptors
[i
].pEffect
;
1445 /* ... then we get this effect's format... */
1446 dstFmt
.Format
.nChannels
= pEffectChain
->pEffectDescriptors
[i
].OutputChannels
;
1447 dstFmt
.Format
.nBlockAlign
= dstFmt
.Format
.nChannels
* (dstFmt
.Format
.wBitsPerSample
/ 8);
1448 dstFmt
.Format
.nAvgBytesPerSec
= dstFmt
.Format
.nSamplesPerSec
* dstFmt
.Format
.nBlockAlign
;
1450 /* FIXME: This error needs to be found _before_ we start
1451 * shredding the voice's state. This function is highly
1452 * destructive so any errors need to be found at the
1453 * beginning, not in the middle! We can't undo this!
1456 if (fapo
->LockForProcess(fapo
, 1, &srcLockParams
, 1, &dstLockParams
))
1461 "Effect output format not supported"
1463 FAudio_assert(0 && "Effect output format not supported");
1464 FAudio_PlatformUnlockMutex(voice
->effectLock
);
1465 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->effectLock
)
1466 LOG_API_EXIT(voice
->audio
)
1467 return FAUDIO_E_UNSUPPORTED_FORMAT
;
1470 /* Okay, now this effect is the source and the next
1471 * effect will be the destination. Repeat until no
1474 FAudio_memcpy(&srcFmt
, &dstFmt
, sizeof(srcFmt
));
1477 FAudio_INTERNAL_FreeEffectChain(voice
);
1478 FAudio_INTERNAL_AllocEffectChain(
1483 /* check if in-place processing is supported */
1484 channelCount
= voiceDetails
.InputChannels
;
1485 for (i
= 0; i
< voice
->effects
.count
; i
+= 1)
1487 fapo
= voice
->effects
.desc
[i
].pEffect
;
1488 if (fapo
->GetRegistrationProperties(fapo
, &pProps
) == 0)
1490 voice
->effects
.inPlaceProcessing
[i
] = (pProps
->Flags
& FAPO_FLAG_INPLACE_SUPPORTED
) == FAPO_FLAG_INPLACE_SUPPORTED
;
1491 voice
->effects
.inPlaceProcessing
[i
] &= (channelCount
== voice
->effects
.desc
[i
].OutputChannels
);
1492 channelCount
= voice
->effects
.desc
[i
].OutputChannels
;
1494 /* Fails if in-place processing is mandatory and
1495 * the chain forces us to do otherwise...
1498 !(pProps
->Flags
& FAPO_FLAG_INPLACE_REQUIRED
) ||
1499 voice
->effects
.inPlaceProcessing
[i
]
1502 voice
->audio
->pFree(pProps
);
1505 voice
->outputChannels
= channelCount
;
1508 FAudio_PlatformUnlockMutex(voice
->effectLock
);
1509 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->effectLock
)
1510 LOG_API_EXIT(voice
->audio
)
1514 uint32_t FAudioVoice_EnableEffect(
1516 uint32_t EffectIndex
,
1517 uint32_t OperationSet
1519 LOG_API_ENTER(voice
->audio
)
1521 if (OperationSet
!= FAUDIO_COMMIT_NOW
&& voice
->audio
->active
)
1523 FAudio_OPERATIONSET_QueueEnableEffect(
1528 LOG_API_EXIT(voice
->audio
)
1532 FAudio_PlatformLockMutex(voice
->effectLock
);
1533 LOG_MUTEX_LOCK(voice
->audio
, voice
->effectLock
)
1534 voice
->effects
.desc
[EffectIndex
].InitialState
= 1;
1535 FAudio_PlatformUnlockMutex(voice
->effectLock
);
1536 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->effectLock
)
1537 LOG_API_EXIT(voice
->audio
)
1541 uint32_t FAudioVoice_DisableEffect(
1543 uint32_t EffectIndex
,
1544 uint32_t OperationSet
1546 LOG_API_ENTER(voice
->audio
)
1548 if (OperationSet
!= FAUDIO_COMMIT_NOW
&& voice
->audio
->active
)
1550 FAudio_OPERATIONSET_QueueDisableEffect(
1555 LOG_API_EXIT(voice
->audio
)
1559 FAudio_PlatformLockMutex(voice
->effectLock
);
1560 LOG_MUTEX_LOCK(voice
->audio
, voice
->effectLock
)
1561 voice
->effects
.desc
[EffectIndex
].InitialState
= 0;
1562 FAudio_PlatformUnlockMutex(voice
->effectLock
);
1563 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->effectLock
)
1564 LOG_API_EXIT(voice
->audio
)
1568 void FAudioVoice_GetEffectState(
1570 uint32_t EffectIndex
,
1573 LOG_API_ENTER(voice
->audio
)
1574 FAudio_PlatformLockMutex(voice
->effectLock
);
1575 LOG_MUTEX_LOCK(voice
->audio
, voice
->effectLock
)
1576 *pEnabled
= voice
->effects
.desc
[EffectIndex
].InitialState
;
1577 FAudio_PlatformUnlockMutex(voice
->effectLock
);
1578 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->effectLock
)
1579 LOG_API_EXIT(voice
->audio
)
1582 uint32_t FAudioVoice_SetEffectParameters(
1584 uint32_t EffectIndex
,
1585 const void *pParameters
,
1586 uint32_t ParametersByteSize
,
1587 uint32_t OperationSet
1589 LOG_API_ENTER(voice
->audio
)
1591 if (OperationSet
!= FAUDIO_COMMIT_NOW
&& voice
->audio
->active
)
1593 FAudio_OPERATIONSET_QueueSetEffectParameters(
1600 LOG_API_EXIT(voice
->audio
)
1604 if (voice
->effects
.parameters
[EffectIndex
] == NULL
)
1606 voice
->effects
.parameters
[EffectIndex
] = voice
->audio
->pMalloc(
1609 voice
->effects
.parameterSizes
[EffectIndex
] = ParametersByteSize
;
1611 FAudio_PlatformLockMutex(voice
->effectLock
);
1612 LOG_MUTEX_LOCK(voice
->audio
, voice
->effectLock
)
1613 if (voice
->effects
.parameterSizes
[EffectIndex
] < ParametersByteSize
)
1615 voice
->effects
.parameters
[EffectIndex
] = voice
->audio
->pRealloc(
1616 voice
->effects
.parameters
[EffectIndex
],
1619 voice
->effects
.parameterSizes
[EffectIndex
] = ParametersByteSize
;
1622 voice
->effects
.parameters
[EffectIndex
],
1626 voice
->effects
.parameterUpdates
[EffectIndex
] = 1;
1627 FAudio_PlatformUnlockMutex(voice
->effectLock
);
1628 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->effectLock
)
1629 LOG_API_EXIT(voice
->audio
)
1633 uint32_t FAudioVoice_GetEffectParameters(
1635 uint32_t EffectIndex
,
1637 uint32_t ParametersByteSize
1640 LOG_API_ENTER(voice
->audio
)
1641 FAudio_PlatformLockMutex(voice
->effectLock
);
1642 LOG_MUTEX_LOCK(voice
->audio
, voice
->effectLock
)
1643 fapo
= voice
->effects
.desc
[EffectIndex
].pEffect
;
1644 fapo
->GetParameters(fapo
, pParameters
, ParametersByteSize
);
1645 FAudio_PlatformUnlockMutex(voice
->effectLock
);
1646 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->effectLock
)
1647 LOG_API_EXIT(voice
->audio
)
1651 uint32_t FAudioVoice_SetFilterParametersEXT(
1653 const FAudioFilterParametersEXT
*pParameters
,
1654 uint32_t OperationSet
1656 LOG_API_ENTER(voice
->audio
)
1658 if (OperationSet
!= FAUDIO_COMMIT_NOW
&& voice
->audio
->active
)
1660 FAudio_OPERATIONSET_QueueSetFilterParameters(
1665 LOG_API_EXIT(voice
->audio
)
1669 /* MSDN: "This method is usable only on source and submix voices and
1670 * has no effect on mastering voices."
1672 if (voice
->type
== FAUDIO_VOICE_MASTER
)
1674 LOG_API_EXIT(voice
->audio
)
1678 if (!(voice
->flags
& FAUDIO_VOICE_USEFILTER
))
1680 LOG_API_EXIT(voice
->audio
)
1684 FAudio_PlatformLockMutex(voice
->filterLock
);
1685 LOG_MUTEX_LOCK(voice
->audio
, voice
->filterLock
)
1689 sizeof(FAudioFilterParametersEXT
)
1691 FAudio_PlatformUnlockMutex(voice
->filterLock
);
1692 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->filterLock
)
1694 LOG_API_EXIT(voice
->audio
)
1698 uint32_t FAudioVoice_SetFilterParameters(
1700 const FAudioFilterParameters
* pParameters
,
1701 uint32_t OperationSet
1703 FAudioFilterParametersEXT ext_parameters
;
1704 ext_parameters
.Type
= pParameters
->Type
;
1705 ext_parameters
.OneOverQ
= pParameters
->OneOverQ
;
1706 ext_parameters
.Frequency
= pParameters
->Frequency
;
1707 ext_parameters
.WetDryMix
= FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT
;
1709 return FAudioVoice_SetFilterParametersEXT(voice
, &ext_parameters
, OperationSet
);
1712 void FAudioVoice_GetFilterParametersEXT(
1714 FAudioFilterParametersEXT
*pParameters
1716 LOG_API_ENTER(voice
->audio
)
1718 /* MSDN: "This method is usable only on source and submix voices and
1719 * has no effect on mastering voices."
1721 if (voice
->type
== FAUDIO_VOICE_MASTER
)
1723 LOG_API_EXIT(voice
->audio
)
1727 if (!(voice
->flags
& FAUDIO_VOICE_USEFILTER
))
1729 LOG_API_EXIT(voice
->audio
)
1733 FAudio_PlatformLockMutex(voice
->filterLock
);
1734 LOG_MUTEX_LOCK(voice
->audio
, voice
->filterLock
)
1738 sizeof(FAudioFilterParametersEXT
)
1740 FAudio_PlatformUnlockMutex(voice
->filterLock
);
1741 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->filterLock
)
1742 LOG_API_EXIT(voice
->audio
)
1745 void FAudioVoice_GetFilterParameters(
1747 FAudioFilterParameters
* pParameters
1749 FAudioFilterParametersEXT ext_parameters
;
1750 ext_parameters
.Type
= pParameters
->Type
;
1751 ext_parameters
.OneOverQ
= pParameters
->OneOverQ
;
1752 ext_parameters
.Frequency
= pParameters
->Frequency
;
1753 ext_parameters
.WetDryMix
= FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT
;
1755 FAudioVoice_GetFilterParametersEXT(voice
, &ext_parameters
);
1757 pParameters
->Type
= ext_parameters
.Type
;
1758 pParameters
->Frequency
= ext_parameters
.Frequency
;
1759 pParameters
->OneOverQ
= ext_parameters
.OneOverQ
;
1762 uint32_t FAudioVoice_SetOutputFilterParametersEXT(
1764 FAudioVoice
*pDestinationVoice
,
1765 const FAudioFilterParametersEXT
*pParameters
,
1766 uint32_t OperationSet
1769 LOG_API_ENTER(voice
->audio
)
1771 if (OperationSet
!= FAUDIO_COMMIT_NOW
&& voice
->audio
->active
)
1773 FAudio_OPERATIONSET_QueueSetOutputFilterParameters(
1779 LOG_API_EXIT(voice
->audio
)
1783 /* MSDN: "This method is usable only on source and submix voices and
1784 * has no effect on mastering voices."
1786 if (voice
->type
== FAUDIO_VOICE_MASTER
)
1788 LOG_API_EXIT(voice
->audio
)
1792 FAudio_PlatformLockMutex(voice
->sendLock
);
1793 LOG_MUTEX_LOCK(voice
->audio
, voice
->sendLock
)
1795 /* Find the send index */
1796 if (pDestinationVoice
== NULL
&& voice
->sends
.SendCount
== 1)
1798 pDestinationVoice
= voice
->sends
.pSends
[0].pOutputVoice
;
1800 for (i
= 0; i
< voice
->sends
.SendCount
; i
+= 1)
1802 if (pDestinationVoice
== voice
->sends
.pSends
[i
].pOutputVoice
)
1807 if (i
>= voice
->sends
.SendCount
)
1811 "Destination not attached to source: %p %p",
1813 (void*) pDestinationVoice
1815 FAudio_PlatformUnlockMutex(voice
->sendLock
);
1816 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->sendLock
)
1817 LOG_API_EXIT(voice
->audio
)
1818 return FAUDIO_E_INVALID_CALL
;
1821 if (!(voice
->sends
.pSends
[i
].Flags
& FAUDIO_SEND_USEFILTER
))
1823 FAudio_PlatformUnlockMutex(voice
->sendLock
);
1824 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->sendLock
)
1825 LOG_API_EXIT(voice
->audio
)
1829 /* Set the filter parameters, finally. */
1831 &voice
->sendFilter
[i
],
1833 sizeof(FAudioFilterParametersEXT
)
1836 FAudio_PlatformUnlockMutex(voice
->sendLock
);
1837 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->sendLock
)
1838 LOG_API_EXIT(voice
->audio
)
1842 uint32_t FAudioVoice_SetOutputFilterParameters(
1844 FAudioVoice
* pDestinationVoice
,
1845 const FAudioFilterParameters
* pParameters
,
1846 uint32_t OperationSet
1848 FAudioFilterParametersEXT ext_parameters
;
1849 ext_parameters
.Type
= pParameters
->Type
;
1850 ext_parameters
.OneOverQ
= pParameters
->OneOverQ
;
1851 ext_parameters
.Frequency
= pParameters
->Frequency
;
1852 ext_parameters
.WetDryMix
= FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT
;
1854 return FAudioVoice_SetOutputFilterParametersEXT(voice
, pDestinationVoice
, &ext_parameters
, OperationSet
);
1857 void FAudioVoice_GetOutputFilterParametersEXT(
1859 FAudioVoice
*pDestinationVoice
,
1860 FAudioFilterParametersEXT
*pParameters
1864 LOG_API_ENTER(voice
->audio
)
1866 /* MSDN: "This method is usable only on source and submix voices and
1867 * has no effect on mastering voices."
1869 if (voice
->type
== FAUDIO_VOICE_MASTER
)
1871 LOG_API_EXIT(voice
->audio
)
1875 FAudio_PlatformLockMutex(voice
->sendLock
);
1876 LOG_MUTEX_LOCK(voice
->audio
, voice
->sendLock
)
1878 /* Find the send index */
1879 if (pDestinationVoice
== NULL
&& voice
->sends
.SendCount
== 1)
1881 pDestinationVoice
= voice
->sends
.pSends
[0].pOutputVoice
;
1883 for (i
= 0; i
< voice
->sends
.SendCount
; i
+= 1)
1885 if (pDestinationVoice
== voice
->sends
.pSends
[i
].pOutputVoice
)
1890 if (i
>= voice
->sends
.SendCount
)
1894 "Destination not attached to source: %p %p",
1896 (void*) pDestinationVoice
1898 FAudio_PlatformUnlockMutex(voice
->sendLock
);
1899 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->sendLock
)
1900 LOG_API_EXIT(voice
->audio
)
1904 if (!(voice
->sends
.pSends
[i
].Flags
& FAUDIO_SEND_USEFILTER
))
1906 FAudio_PlatformUnlockMutex(voice
->sendLock
);
1907 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->sendLock
)
1908 LOG_API_EXIT(voice
->audio
)
1912 /* Set the filter parameters, finally. */
1915 &voice
->sendFilter
[i
],
1916 sizeof(FAudioFilterParametersEXT
)
1919 FAudio_PlatformUnlockMutex(voice
->sendLock
);
1920 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->sendLock
)
1921 LOG_API_EXIT(voice
->audio
)
1924 uint32_t FAudioVoice_SetVolume(
1927 uint32_t OperationSet
1931 LOG_API_ENTER(voice
->audio
)
1933 if (OperationSet
!= FAUDIO_COMMIT_NOW
&& voice
->audio
->active
)
1935 FAudio_OPERATIONSET_QueueSetVolume(
1940 LOG_API_EXIT(voice
->audio
)
1944 FAudio_PlatformLockMutex(voice
->sendLock
);
1945 LOG_MUTEX_LOCK(voice
->audio
, voice
->sendLock
)
1947 FAudio_PlatformLockMutex(voice
->volumeLock
);
1948 LOG_MUTEX_LOCK(voice
->audio
, voice
->volumeLock
)
1950 voice
->volume
= FAudio_clamp(
1952 -FAUDIO_MAX_VOLUME_LEVEL
,
1953 FAUDIO_MAX_VOLUME_LEVEL
1956 for (i
= 0; i
< voice
->sends
.SendCount
; i
+= 1)
1958 FAudio_RecalcMixMatrix(voice
, i
);
1961 FAudio_PlatformUnlockMutex(voice
->volumeLock
);
1962 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->volumeLock
)
1964 FAudio_PlatformUnlockMutex(voice
->sendLock
);
1965 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->sendLock
)
1967 LOG_API_EXIT(voice
->audio
)
1971 void FAudioVoice_GetOutputFilterParameters(
1973 FAudioVoice
* pDestinationVoice
,
1974 FAudioFilterParameters
* pParameters
1976 FAudioFilterParametersEXT ext_parameters
;
1977 ext_parameters
.Type
= pParameters
->Type
;
1978 ext_parameters
.OneOverQ
= pParameters
->OneOverQ
;
1979 ext_parameters
.Frequency
= pParameters
->Frequency
;
1980 ext_parameters
.WetDryMix
= FAUDIO_DEFAULT_FILTER_WETDRYMIX_EXT
;
1982 FAudioVoice_GetOutputFilterParametersEXT(voice
, pDestinationVoice
, &ext_parameters
);
1984 pParameters
->Type
= ext_parameters
.Type
;
1985 pParameters
->Frequency
= ext_parameters
.Frequency
;
1986 pParameters
->OneOverQ
= ext_parameters
.OneOverQ
;
1989 void FAudioVoice_GetVolume(
1993 LOG_API_ENTER(voice
->audio
)
1994 *pVolume
= voice
->volume
;
1995 LOG_API_EXIT(voice
->audio
)
1998 uint32_t FAudioVoice_SetChannelVolumes(
2001 const float *pVolumes
,
2002 uint32_t OperationSet
2006 LOG_API_ENTER(voice
->audio
)
2008 if (OperationSet
!= FAUDIO_COMMIT_NOW
&& voice
->audio
->active
)
2010 FAudio_OPERATIONSET_QueueSetChannelVolumes(
2016 LOG_API_EXIT(voice
->audio
)
2020 if (pVolumes
== NULL
)
2022 LOG_API_EXIT(voice
->audio
)
2023 return FAUDIO_E_INVALID_CALL
;
2026 if (voice
->type
== FAUDIO_VOICE_MASTER
)
2028 LOG_API_EXIT(voice
->audio
)
2029 return FAUDIO_E_INVALID_CALL
;
2032 if (voice
->audio
->version
> 7 && Channels
!= voice
->outputChannels
)
2034 LOG_API_EXIT(voice
->audio
)
2035 return FAUDIO_E_INVALID_CALL
;
2038 FAudio_PlatformLockMutex(voice
->sendLock
);
2039 LOG_MUTEX_LOCK(voice
->audio
, voice
->sendLock
)
2041 FAudio_PlatformLockMutex(voice
->volumeLock
);
2042 LOG_MUTEX_LOCK(voice
->audio
, voice
->volumeLock
)
2045 voice
->channelVolume
,
2047 sizeof(float) * Channels
2050 for (i
= 0; i
< voice
->sends
.SendCount
; i
+= 1)
2052 FAudio_RecalcMixMatrix(voice
, i
);
2055 FAudio_PlatformUnlockMutex(voice
->volumeLock
);
2056 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->volumeLock
)
2058 FAudio_PlatformUnlockMutex(voice
->sendLock
);
2059 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->sendLock
)
2061 LOG_API_EXIT(voice
->audio
)
2065 void FAudioVoice_GetChannelVolumes(
2070 LOG_API_ENTER(voice
->audio
)
2071 FAudio_PlatformLockMutex(voice
->volumeLock
);
2072 LOG_MUTEX_LOCK(voice
->audio
, voice
->volumeLock
)
2075 voice
->channelVolume
,
2076 sizeof(float) * Channels
2078 FAudio_PlatformUnlockMutex(voice
->volumeLock
);
2079 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->volumeLock
)
2080 LOG_API_EXIT(voice
->audio
)
2083 uint32_t FAudioVoice_SetOutputMatrix(
2085 FAudioVoice
*pDestinationVoice
,
2086 uint32_t SourceChannels
,
2087 uint32_t DestinationChannels
,
2088 const float *pLevelMatrix
,
2089 uint32_t OperationSet
2091 uint32_t i
, result
= 0;
2092 LOG_API_ENTER(voice
->audio
)
2094 if (OperationSet
!= FAUDIO_COMMIT_NOW
&& voice
->audio
->active
)
2096 FAudio_OPERATIONSET_QueueSetOutputMatrix(
2100 DestinationChannels
,
2104 LOG_API_EXIT(voice
->audio
)
2108 FAudio_PlatformLockMutex(voice
->sendLock
);
2109 LOG_MUTEX_LOCK(voice
->audio
, voice
->sendLock
)
2111 /* Find the send index */
2112 if (pDestinationVoice
== NULL
&& voice
->sends
.SendCount
== 1)
2114 pDestinationVoice
= voice
->sends
.pSends
[0].pOutputVoice
;
2116 FAudio_assert(pDestinationVoice
!= NULL
);
2117 for (i
= 0; i
< voice
->sends
.SendCount
; i
+= 1)
2119 if (pDestinationVoice
== voice
->sends
.pSends
[i
].pOutputVoice
)
2124 if (i
>= voice
->sends
.SendCount
)
2128 "Destination not attached to source: %p %p",
2130 (void*) pDestinationVoice
2132 result
= FAUDIO_E_INVALID_CALL
;
2136 /* Verify the Source/Destination channel count */
2137 if (SourceChannels
!= voice
->outputChannels
)
2141 "SourceChannels not equal to voice channel count: %p %d %d",
2144 voice
->outputChannels
2146 result
= FAUDIO_E_INVALID_CALL
;
2150 if (pDestinationVoice
->type
== FAUDIO_VOICE_MASTER
)
2152 if (DestinationChannels
!= pDestinationVoice
->master
.inputChannels
)
2156 "DestinationChannels not equal to master channel count: %p %d %d",
2157 (void*) pDestinationVoice
,
2158 DestinationChannels
,
2159 pDestinationVoice
->master
.inputChannels
2161 result
= FAUDIO_E_INVALID_CALL
;
2167 if (DestinationChannels
!= pDestinationVoice
->mix
.inputChannels
)
2171 "DestinationChannels not equal to submix channel count: %p %d %d",
2172 (void*) pDestinationVoice
,
2173 DestinationChannels
,
2174 pDestinationVoice
->mix
.inputChannels
2176 result
= FAUDIO_E_INVALID_CALL
;
2181 /* Set the matrix values, finally */
2182 FAudio_PlatformLockMutex(voice
->volumeLock
);
2183 LOG_MUTEX_LOCK(voice
->audio
, voice
->volumeLock
)
2186 voice
->sendCoefficients
[i
],
2188 sizeof(float) * SourceChannels
* DestinationChannels
2191 FAudio_RecalcMixMatrix(voice
, i
);
2193 FAudio_PlatformUnlockMutex(voice
->volumeLock
);
2194 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->volumeLock
)
2197 FAudio_PlatformUnlockMutex(voice
->sendLock
);
2198 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->sendLock
)
2199 LOG_API_EXIT(voice
->audio
)
2203 void FAudioVoice_GetOutputMatrix(
2205 FAudioVoice
*pDestinationVoice
,
2206 uint32_t SourceChannels
,
2207 uint32_t DestinationChannels
,
2212 LOG_API_ENTER(voice
->audio
)
2213 FAudio_PlatformLockMutex(voice
->sendLock
);
2214 LOG_MUTEX_LOCK(voice
->audio
, voice
->sendLock
)
2216 /* Find the send index */
2217 for (i
= 0; i
< voice
->sends
.SendCount
; i
+= 1)
2219 if (pDestinationVoice
== voice
->sends
.pSends
[i
].pOutputVoice
)
2224 if (i
>= voice
->sends
.SendCount
)
2228 "Destination not attached to source: %p %p",
2230 (void*) pDestinationVoice
2232 FAudio_PlatformUnlockMutex(voice
->sendLock
);
2233 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->sendLock
)
2234 LOG_API_EXIT(voice
->audio
)
2238 /* Verify the Source/Destination channel count */
2239 if (voice
->type
== FAUDIO_VOICE_SOURCE
)
2241 FAudio_assert(SourceChannels
== voice
->src
.format
->nChannels
);
2245 FAudio_assert(SourceChannels
== voice
->mix
.inputChannels
);
2247 if (pDestinationVoice
->type
== FAUDIO_VOICE_MASTER
)
2249 FAudio_assert(DestinationChannels
== pDestinationVoice
->master
.inputChannels
);
2253 FAudio_assert(DestinationChannels
== pDestinationVoice
->mix
.inputChannels
);
2256 /* Get the matrix values, finally */
2259 voice
->sendCoefficients
[i
],
2260 sizeof(float) * SourceChannels
* DestinationChannels
2263 FAudio_PlatformUnlockMutex(voice
->sendLock
);
2264 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->sendLock
)
2265 LOG_API_EXIT(voice
->audio
)
2268 static uint32_t check_for_sends_to_voice(FAudioVoice
*voice
)
2270 FAudio
*audio
= voice
->audio
;
2272 FAudioSourceVoice
*source
;
2273 FAudioSubmixVoice
*submix
;
2277 FAudio_PlatformLockMutex(audio
->sourceLock
);
2278 list
= audio
->sources
;
2279 while (list
!= NULL
)
2281 source
= (FAudioSourceVoice
*) list
->entry
;
2282 for (i
= 0; i
< source
->sends
.SendCount
; i
+= 1)
2283 if (source
->sends
.pSends
[i
].pOutputVoice
== voice
)
2285 ret
= 0x80004005; /* E_FAIL */
2292 FAudio_PlatformUnlockMutex(audio
->sourceLock
);
2297 FAudio_PlatformLockMutex(audio
->submixLock
);
2298 list
= audio
->submixes
;
2299 while (list
!= NULL
)
2301 submix
= (FAudioSubmixVoice
*) list
->entry
;
2302 for (i
= 0; i
< submix
->sends
.SendCount
; i
+= 1)
2303 if (submix
->sends
.pSends
[i
].pOutputVoice
== voice
)
2305 ret
= 0x80004005; /* E_FAIL */
2312 FAudio_PlatformUnlockMutex(audio
->submixLock
);
2317 uint32_t FAudioVoice_DestroyVoiceSafeEXT(FAudioVoice
*voice
)
2320 LOG_API_ENTER(voice
->audio
)
2322 if ((ret
= check_for_sends_to_voice(voice
)))
2326 "Voice %p is an output for other voice(s)",
2329 LOG_API_EXIT(voice
->audio
)
2333 /* TODO: Check for dependencies and remove from audio graph first! */
2334 FAudio_OPERATIONSET_ClearAllForVoice(voice
);
2336 if (voice
->type
== FAUDIO_VOICE_SOURCE
)
2338 FAudioBufferEntry
*entry
, *next
;
2340 #ifdef FAUDIO_DUMP_VOICES
2341 FAudio_DUMPVOICE_Finalize((FAudioSourceVoice
*) voice
);
2342 #endif /* FAUDIO_DUMP_VOICES */
2344 FAudio_PlatformLockMutex(voice
->audio
->sourceLock
);
2345 LOG_MUTEX_LOCK(voice
->audio
, voice
->audio
->sourceLock
)
2346 while (voice
== voice
->audio
->processingSource
)
2348 FAudio_PlatformUnlockMutex(voice
->audio
->sourceLock
);
2349 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->audio
->sourceLock
)
2350 FAudio_PlatformLockMutex(voice
->audio
->sourceLock
);
2351 LOG_MUTEX_LOCK(voice
->audio
, voice
->audio
->sourceLock
)
2353 LinkedList_RemoveEntry(
2354 &voice
->audio
->sources
,
2356 voice
->audio
->sourceLock
,
2359 FAudio_PlatformUnlockMutex(voice
->audio
->sourceLock
);
2360 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->audio
->sourceLock
)
2362 entry
= voice
->src
.bufferList
;
2363 while (entry
!= NULL
)
2366 voice
->audio
->pFree(entry
);
2370 entry
= voice
->src
.flushList
;
2371 while (entry
!= NULL
)
2374 voice
->audio
->pFree(entry
);
2378 voice
->audio
->pFree(voice
->src
.format
);
2379 LOG_MUTEX_DESTROY(voice
->audio
, voice
->src
.bufferLock
)
2380 FAudio_PlatformDestroyMutex(voice
->src
.bufferLock
);
2382 if (voice
->src
.wmadec
)
2384 FAudio_WMADEC_free(voice
);
2386 #endif /* HAVE_WMADEC */
2388 else if (voice
->type
== FAUDIO_VOICE_SUBMIX
)
2390 /* Remove submix from list */
2391 LinkedList_RemoveEntry(
2392 &voice
->audio
->submixes
,
2394 voice
->audio
->submixLock
,
2398 /* Delete submix data */
2399 voice
->audio
->pFree(voice
->mix
.inputCache
);
2401 else if (voice
->type
== FAUDIO_VOICE_MASTER
)
2403 if (voice
->audio
->platform
!= NULL
)
2405 FAudio_PlatformQuit(voice
->audio
->platform
);
2406 voice
->audio
->platform
= NULL
;
2408 if (voice
->master
.effectCache
!= NULL
)
2410 voice
->audio
->pFree(voice
->master
.effectCache
);
2412 voice
->audio
->master
= NULL
;
2415 if (voice
->sendLock
!= NULL
)
2417 FAudio_PlatformLockMutex(voice
->sendLock
);
2418 LOG_MUTEX_LOCK(voice
->audio
, voice
->sendLock
)
2419 for (i
= 0; i
< voice
->sends
.SendCount
; i
+= 1)
2421 voice
->audio
->pFree(voice
->sendCoefficients
[i
]);
2423 if (voice
->sendCoefficients
!= NULL
)
2425 voice
->audio
->pFree(voice
->sendCoefficients
);
2427 for (i
= 0; i
< voice
->sends
.SendCount
; i
+= 1)
2429 voice
->audio
->pFree(voice
->mixCoefficients
[i
]);
2431 if (voice
->mixCoefficients
!= NULL
)
2433 voice
->audio
->pFree(voice
->mixCoefficients
);
2435 if (voice
->sendMix
!= NULL
)
2437 voice
->audio
->pFree(voice
->sendMix
);
2439 if (voice
->sendFilter
!= NULL
)
2441 voice
->audio
->pFree(voice
->sendFilter
);
2443 if (voice
->sendFilterState
!= NULL
)
2445 for (i
= 0; i
< voice
->sends
.SendCount
; i
+= 1)
2447 if (voice
->sendFilterState
[i
] != NULL
)
2449 voice
->audio
->pFree(voice
->sendFilterState
[i
]);
2452 voice
->audio
->pFree(voice
->sendFilterState
);
2454 if (voice
->sends
.pSends
!= NULL
)
2456 voice
->audio
->pFree(voice
->sends
.pSends
);
2458 FAudio_PlatformUnlockMutex(voice
->sendLock
);
2459 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->sendLock
)
2460 LOG_MUTEX_DESTROY(voice
->audio
, voice
->sendLock
)
2461 FAudio_PlatformDestroyMutex(voice
->sendLock
);
2464 if (voice
->effectLock
!= NULL
)
2466 FAudio_PlatformLockMutex(voice
->effectLock
);
2467 LOG_MUTEX_LOCK(voice
->audio
, voice
->effectLock
)
2468 FAudio_INTERNAL_FreeEffectChain(voice
);
2469 FAudio_PlatformUnlockMutex(voice
->effectLock
);
2470 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->effectLock
)
2471 LOG_MUTEX_DESTROY(voice
->audio
, voice
->effectLock
)
2472 FAudio_PlatformDestroyMutex(voice
->effectLock
);
2475 if (voice
->filterLock
!= NULL
)
2477 FAudio_PlatformLockMutex(voice
->filterLock
);
2478 LOG_MUTEX_LOCK(voice
->audio
, voice
->filterLock
)
2479 if (voice
->filterState
!= NULL
)
2481 voice
->audio
->pFree(voice
->filterState
);
2483 FAudio_PlatformUnlockMutex(voice
->filterLock
);
2484 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->filterLock
)
2485 LOG_MUTEX_DESTROY(voice
->audio
, voice
->filterLock
)
2486 FAudio_PlatformDestroyMutex(voice
->filterLock
);
2489 if (voice
->volumeLock
!= NULL
)
2491 FAudio_PlatformLockMutex(voice
->volumeLock
);
2492 LOG_MUTEX_LOCK(voice
->audio
, voice
->volumeLock
)
2493 if (voice
->channelVolume
!= NULL
)
2495 voice
->audio
->pFree(voice
->channelVolume
);
2497 FAudio_PlatformUnlockMutex(voice
->volumeLock
);
2498 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->volumeLock
)
2499 LOG_MUTEX_DESTROY(voice
->audio
, voice
->volumeLock
)
2500 FAudio_PlatformDestroyMutex(voice
->volumeLock
);
2503 LOG_API_EXIT(voice
->audio
)
2504 FAudio_Release(voice
->audio
);
2505 voice
->audio
->pFree(voice
);
2509 void FAudioVoice_DestroyVoice(FAudioVoice
*voice
)
2511 FAudioVoice_DestroyVoiceSafeEXT(voice
);
2514 /* FAudioSourceVoice Interface */
2516 uint32_t FAudioSourceVoice_Start(
2517 FAudioSourceVoice
*voice
,
2519 uint32_t OperationSet
2521 LOG_API_ENTER(voice
->audio
)
2523 if (OperationSet
!= FAUDIO_COMMIT_NOW
&& voice
->audio
->active
)
2525 FAudio_OPERATIONSET_QueueStart(
2530 LOG_API_EXIT(voice
->audio
)
2535 FAudio_assert(voice
->type
== FAUDIO_VOICE_SOURCE
);
2537 FAudio_assert(Flags
== 0);
2538 voice
->src
.active
= 1;
2539 LOG_API_EXIT(voice
->audio
)
2543 uint32_t FAudioSourceVoice_Stop(
2544 FAudioSourceVoice
*voice
,
2546 uint32_t OperationSet
2548 LOG_API_ENTER(voice
->audio
)
2550 if (OperationSet
!= FAUDIO_COMMIT_NOW
&& voice
->audio
->active
)
2552 FAudio_OPERATIONSET_QueueStop(
2557 LOG_API_EXIT(voice
->audio
)
2561 FAudio_assert(voice
->type
== FAUDIO_VOICE_SOURCE
);
2563 if (Flags
& FAUDIO_PLAY_TAILS
)
2565 voice
->src
.active
= 2;
2569 voice
->src
.active
= 0;
2571 LOG_API_EXIT(voice
->audio
)
2575 uint32_t FAudioSourceVoice_SubmitSourceBuffer(
2576 FAudioSourceVoice
*voice
,
2577 const FAudioBuffer
*pBuffer
,
2578 const FAudioBufferWMA
*pBufferWMA
2580 uint32_t adpcmMask
, *adpcmByteCount
;
2581 uint32_t playBegin
, playLength
, loopBegin
, loopLength
;
2582 FAudioBufferEntry
*entry
, *list
;
2584 LOG_API_ENTER(voice
->audio
)
2587 "%p: {Flags: 0x%x, AudioBytes: %u, pAudioData: %p, Play: %u + %u, Loop: %u + %u x %u}",
2590 pBuffer
->AudioBytes
,
2591 (const void*) pBuffer
->pAudioData
,
2593 pBuffer
->PlayLength
,
2595 pBuffer
->LoopLength
,
2599 FAudio_assert(voice
->type
== FAUDIO_VOICE_SOURCE
);
2601 FAudio_assert( (voice
->src
.wmadec
!= NULL
&& (pBufferWMA
!= NULL
||
2602 (voice
->src
.format
->wFormatTag
== FAUDIO_FORMAT_XMAUDIO2
||
2603 voice
->src
.format
->wFormatTag
== FAUDIO_FORMAT_EXTENSIBLE
))) ||
2604 (voice
->src
.wmadec
== NULL
&& (pBufferWMA
== NULL
&& voice
->src
.format
->wFormatTag
!= FAUDIO_FORMAT_XMAUDIO2
)) );
2605 #endif /* HAVE_WMADEC */
2607 /* Start off with whatever they just sent us... */
2608 playBegin
= pBuffer
->PlayBegin
;
2609 playLength
= pBuffer
->PlayLength
;
2610 loopBegin
= pBuffer
->LoopBegin
;
2611 loopLength
= pBuffer
->LoopLength
;
2613 /* "LoopBegin/LoopLength must be zero if LoopCount is 0" */
2614 if (pBuffer
->LoopCount
== 0 && (loopBegin
> 0 || loopLength
> 0))
2616 LOG_API_EXIT(voice
->audio
)
2617 return FAUDIO_E_INVALID_CALL
;
2620 /* PlayLength Default */
2621 if (playLength
== 0)
2623 if (voice
->src
.format
->wFormatTag
== FAUDIO_FORMAT_MSADPCM
)
2625 FAudioADPCMWaveFormat
*fmtex
= (FAudioADPCMWaveFormat
*) voice
->src
.format
;
2627 pBuffer
->AudioBytes
/
2628 fmtex
->wfx
.nBlockAlign
*
2629 fmtex
->wSamplesPerBlock
2632 else if (voice
->src
.format
->wFormatTag
== FAUDIO_FORMAT_XMAUDIO2
)
2634 FAudioXMA2WaveFormat
*fmtex
= (FAudioXMA2WaveFormat
*) voice
->src
.format
;
2635 playLength
= fmtex
->dwSamplesEncoded
- playBegin
;
2637 else if (pBufferWMA
!= NULL
)
2640 pBufferWMA
->pDecodedPacketCumulativeBytes
[pBufferWMA
->PacketCount
- 1] /
2641 (voice
->src
.format
->nChannels
* voice
->src
.format
->wBitsPerSample
/ 8)
2647 pBuffer
->AudioBytes
/
2648 voice
->src
.format
->nBlockAlign
2653 if (pBuffer
->LoopCount
> 0 && pBufferWMA
== NULL
&& voice
->src
.format
->wFormatTag
!= FAUDIO_FORMAT_XMAUDIO2
)
2655 /* "The value of LoopBegin must be less than PlayBegin + PlayLength" */
2656 if (loopBegin
>= (playBegin
+ playLength
))
2658 LOG_API_EXIT(voice
->audio
)
2659 return FAUDIO_E_INVALID_CALL
;
2662 /* LoopLength Default */
2663 if (loopLength
== 0)
2665 loopLength
= playBegin
+ playLength
- loopBegin
;
2668 /* "The value of LoopBegin + LoopLength must be greater than PlayBegin
2669 * and less than PlayBegin + PlayLength"
2671 if ( voice
->audio
->version
> 7 && (
2672 (loopBegin
+ loopLength
) <= playBegin
||
2673 (loopBegin
+ loopLength
) > (playBegin
+ playLength
)) )
2675 LOG_API_EXIT(voice
->audio
)
2676 return FAUDIO_E_INVALID_CALL
;
2680 /* For ADPCM, round down to the nearest sample block size */
2681 if (voice
->src
.format
->wFormatTag
== FAUDIO_FORMAT_MSADPCM
)
2683 adpcmMask
= ((FAudioADPCMWaveFormat
*) voice
->src
.format
)->wSamplesPerBlock
;
2684 playBegin
-= playBegin
% adpcmMask
;
2685 playLength
-= playLength
% adpcmMask
;
2686 loopBegin
-= loopBegin
% adpcmMask
;
2687 loopLength
-= loopLength
% adpcmMask
;
2689 /* This is basically a const_cast... */
2690 adpcmByteCount
= (uint32_t*) &pBuffer
->AudioBytes
;
2692 pBuffer
->AudioBytes
/ voice
->src
.format
->nBlockAlign
2693 ) * voice
->src
.format
->nBlockAlign
;
2695 else if (pBufferWMA
!= NULL
|| voice
->src
.format
->wFormatTag
== FAUDIO_FORMAT_XMAUDIO2
)
2697 /* WMA only supports looping the whole buffer */
2699 loopLength
= playBegin
+ playLength
;
2702 /* Allocate, now that we have valid input */
2703 entry
= (FAudioBufferEntry
*) voice
->audio
->pMalloc(sizeof(FAudioBufferEntry
));
2704 FAudio_memcpy(&entry
->buffer
, pBuffer
, sizeof(FAudioBuffer
));
2705 entry
->buffer
.PlayBegin
= playBegin
;
2706 entry
->buffer
.PlayLength
= playLength
;
2707 entry
->buffer
.LoopBegin
= loopBegin
;
2708 entry
->buffer
.LoopLength
= loopLength
;
2709 if (pBufferWMA
!= NULL
)
2711 FAudio_memcpy(&entry
->bufferWMA
, pBufferWMA
, sizeof(FAudioBufferWMA
));
2715 if ( voice
->audio
->version
<= 7 && (
2716 entry
->buffer
.LoopCount
> 0 &&
2717 entry
->buffer
.LoopBegin
+ entry
->buffer
.LoopLength
<= entry
->buffer
.PlayBegin
))
2719 entry
->buffer
.LoopCount
= 0;
2722 #ifdef FAUDIO_DUMP_VOICES
2723 /* dumping current buffer, append into "data" section */
2724 if (pBuffer
->pAudioData
!= NULL
&& playLength
> 0)
2726 FAudio_DUMPVOICE_WriteBuffer(voice
, pBuffer
, pBufferWMA
, playBegin
, playLength
);
2728 #endif /* FAUDIO_DUMP_VOICES */
2731 FAudio_PlatformLockMutex(voice
->src
.bufferLock
);
2732 LOG_MUTEX_LOCK(voice
->audio
, voice
->src
.bufferLock
)
2733 if (voice
->src
.bufferList
== NULL
)
2735 voice
->src
.bufferList
= entry
;
2736 voice
->src
.curBufferOffset
= entry
->buffer
.PlayBegin
;
2737 voice
->src
.newBuffer
= 1;
2741 list
= voice
->src
.bufferList
;
2742 while (list
->next
!= NULL
)
2748 /* For some bizarre reason we get scenarios where a buffer is freed, only to
2749 * have the allocator give us the exact same address and somehow get a single
2750 * buffer referencing itself. I don't even know.
2752 FAudio_assert(list
!= entry
);
2756 "%p: appended buffer %p",
2758 (void*) &entry
->buffer
2760 FAudio_PlatformUnlockMutex(voice
->src
.bufferLock
);
2761 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->src
.bufferLock
)
2762 LOG_API_EXIT(voice
->audio
)
2766 uint32_t FAudioSourceVoice_FlushSourceBuffers(
2767 FAudioSourceVoice
*voice
2769 FAudioBufferEntry
*entry
, *latest
;
2771 LOG_API_ENTER(voice
->audio
)
2772 FAudio_assert(voice
->type
== FAUDIO_VOICE_SOURCE
);
2774 FAudio_PlatformLockMutex(voice
->src
.bufferLock
);
2775 LOG_MUTEX_LOCK(voice
->audio
, voice
->src
.bufferLock
)
2777 /* If the source is playing, don't flush the active buffer */
2778 entry
= voice
->src
.bufferList
;
2779 if ((voice
->src
.active
== 1) && entry
!= NULL
&& !voice
->src
.newBuffer
)
2781 entry
= entry
->next
;
2782 voice
->src
.bufferList
->next
= NULL
;
2786 voice
->src
.curBufferOffset
= 0;
2787 voice
->src
.bufferList
= NULL
;
2788 voice
->src
.newBuffer
= 0;
2791 /* Move them to the pending flush list */
2794 if (voice
->src
.flushList
== NULL
)
2796 voice
->src
.flushList
= entry
;
2800 latest
= voice
->src
.flushList
;
2801 while (latest
->next
!= NULL
)
2803 latest
= latest
->next
;
2805 latest
->next
= entry
;
2809 FAudio_PlatformUnlockMutex(voice
->src
.bufferLock
);
2810 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->src
.bufferLock
)
2811 LOG_API_EXIT(voice
->audio
)
2815 uint32_t FAudioSourceVoice_Discontinuity(
2816 FAudioSourceVoice
*voice
2818 FAudioBufferEntry
*buf
;
2820 LOG_API_ENTER(voice
->audio
)
2821 FAudio_assert(voice
->type
== FAUDIO_VOICE_SOURCE
);
2823 FAudio_PlatformLockMutex(voice
->src
.bufferLock
);
2824 LOG_MUTEX_LOCK(voice
->audio
, voice
->src
.bufferLock
)
2826 if (voice
->src
.bufferList
!= NULL
)
2828 for (buf
= voice
->src
.bufferList
; buf
->next
!= NULL
; buf
= buf
->next
);
2829 buf
->buffer
.Flags
|= FAUDIO_END_OF_STREAM
;
2832 FAudio_PlatformUnlockMutex(voice
->src
.bufferLock
);
2833 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->src
.bufferLock
)
2834 LOG_API_EXIT(voice
->audio
)
2838 uint32_t FAudioSourceVoice_ExitLoop(
2839 FAudioSourceVoice
*voice
,
2840 uint32_t OperationSet
2842 LOG_API_ENTER(voice
->audio
)
2844 if (OperationSet
!= FAUDIO_COMMIT_NOW
&& voice
->audio
->active
)
2846 FAudio_OPERATIONSET_QueueExitLoop(
2850 LOG_API_EXIT(voice
->audio
)
2854 FAudio_assert(voice
->type
== FAUDIO_VOICE_SOURCE
);
2856 FAudio_PlatformLockMutex(voice
->src
.bufferLock
);
2857 LOG_MUTEX_LOCK(voice
->audio
, voice
->src
.bufferLock
)
2859 if (voice
->src
.bufferList
!= NULL
)
2861 voice
->src
.bufferList
->buffer
.LoopCount
= 0;
2864 FAudio_PlatformUnlockMutex(voice
->src
.bufferLock
);
2865 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->src
.bufferLock
)
2866 LOG_API_EXIT(voice
->audio
)
2870 void FAudioSourceVoice_GetState(
2871 FAudioSourceVoice
*voice
,
2872 FAudioVoiceState
*pVoiceState
,
2875 FAudioBufferEntry
*entry
;
2877 LOG_API_ENTER(voice
->audio
)
2878 FAudio_assert(voice
->type
== FAUDIO_VOICE_SOURCE
);
2880 FAudio_PlatformLockMutex(voice
->src
.bufferLock
);
2881 LOG_MUTEX_LOCK(voice
->audio
, voice
->src
.bufferLock
)
2883 if (!(Flags
& FAUDIO_VOICE_NOSAMPLESPLAYED
))
2885 pVoiceState
->SamplesPlayed
= voice
->src
.totalSamples
;
2888 pVoiceState
->BuffersQueued
= 0;
2889 pVoiceState
->pCurrentBufferContext
= NULL
;
2890 if (voice
->src
.bufferList
!= NULL
)
2892 entry
= voice
->src
.bufferList
;
2893 if (!voice
->src
.newBuffer
)
2895 pVoiceState
->pCurrentBufferContext
= entry
->buffer
.pContext
;
2899 pVoiceState
->BuffersQueued
+= 1;
2900 entry
= entry
->next
;
2901 } while (entry
!= NULL
);
2904 /* Pending flushed buffers also count */
2905 entry
= voice
->src
.flushList
;
2906 while (entry
!= NULL
)
2908 pVoiceState
->BuffersQueued
+= 1;
2909 entry
= entry
->next
;
2914 "-> {pCurrentBufferContext: %p, BuffersQueued: %u, SamplesPlayed: %"FAudio_PRIu64
"}",
2915 pVoiceState
->pCurrentBufferContext
, pVoiceState
->BuffersQueued
,
2916 pVoiceState
->SamplesPlayed
2919 FAudio_PlatformUnlockMutex(voice
->src
.bufferLock
);
2920 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->src
.bufferLock
)
2921 LOG_API_EXIT(voice
->audio
)
2924 uint32_t FAudioSourceVoice_SetFrequencyRatio(
2925 FAudioSourceVoice
*voice
,
2927 uint32_t OperationSet
2929 LOG_API_ENTER(voice
->audio
)
2931 if (OperationSet
!= FAUDIO_COMMIT_NOW
&& voice
->audio
->active
)
2933 FAudio_OPERATIONSET_QueueSetFrequencyRatio(
2938 LOG_API_EXIT(voice
->audio
)
2941 FAudio_assert(voice
->type
== FAUDIO_VOICE_SOURCE
);
2943 if (voice
->flags
& FAUDIO_VOICE_NOPITCH
)
2945 LOG_API_EXIT(voice
->audio
)
2949 voice
->src
.freqRatio
= FAudio_clamp(
2951 FAUDIO_MIN_FREQ_RATIO
,
2952 voice
->src
.maxFreqRatio
2954 LOG_API_EXIT(voice
->audio
)
2958 void FAudioSourceVoice_GetFrequencyRatio(
2959 FAudioSourceVoice
*voice
,
2962 LOG_API_ENTER(voice
->audio
)
2963 FAudio_assert(voice
->type
== FAUDIO_VOICE_SOURCE
);
2965 *pRatio
= voice
->src
.freqRatio
;
2966 LOG_API_EXIT(voice
->audio
)
2969 uint32_t FAudioSourceVoice_SetSourceSampleRate(
2970 FAudioSourceVoice
*voice
,
2971 uint32_t NewSourceSampleRate
2973 uint32_t outSampleRate
;
2974 uint32_t newDecodeSamples
, newResampleSamples
;
2976 LOG_API_ENTER(voice
->audio
)
2977 FAudio_assert(voice
->type
== FAUDIO_VOICE_SOURCE
);
2978 FAudio_assert( NewSourceSampleRate
>= FAUDIO_MIN_SAMPLE_RATE
&&
2979 NewSourceSampleRate
<= FAUDIO_MAX_SAMPLE_RATE
);
2981 FAudio_PlatformLockMutex(voice
->src
.bufferLock
);
2982 LOG_MUTEX_LOCK(voice
->audio
, voice
->src
.bufferLock
)
2983 if ( voice
->audio
->version
> 7 &&
2984 voice
->src
.bufferList
!= NULL
)
2986 FAudio_PlatformUnlockMutex(voice
->src
.bufferLock
);
2987 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->src
.bufferLock
)
2988 LOG_API_EXIT(voice
->audio
)
2989 return FAUDIO_E_INVALID_CALL
;
2991 FAudio_PlatformUnlockMutex(voice
->src
.bufferLock
);
2992 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->src
.bufferLock
)
2994 voice
->src
.format
->nSamplesPerSec
= NewSourceSampleRate
;
2996 /* Resize decode cache */
2997 newDecodeSamples
= (uint32_t) FAudio_ceil(
2998 voice
->audio
->updateSize
*
2999 (double) voice
->src
.maxFreqRatio
*
3000 (double) NewSourceSampleRate
/
3001 (double) voice
->audio
->master
->master
.inputSampleRate
3002 ) + EXTRA_DECODE_PADDING
* voice
->src
.format
->nChannels
;
3003 FAudio_INTERNAL_ResizeDecodeCache(
3005 (newDecodeSamples
+ EXTRA_DECODE_PADDING
) * voice
->src
.format
->nChannels
3007 voice
->src
.decodeSamples
= newDecodeSamples
;
3009 FAudio_PlatformLockMutex(voice
->sendLock
);
3010 LOG_MUTEX_LOCK(voice
->audio
, voice
->sendLock
)
3012 if (voice
->sends
.SendCount
== 0)
3014 FAudio_PlatformUnlockMutex(voice
->sendLock
);
3015 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->sendLock
)
3016 LOG_API_EXIT(voice
->audio
)
3019 outSampleRate
= voice
->sends
.pSends
[0].pOutputVoice
->type
== FAUDIO_VOICE_MASTER
?
3020 voice
->sends
.pSends
[0].pOutputVoice
->master
.inputSampleRate
:
3021 voice
->sends
.pSends
[0].pOutputVoice
->mix
.inputSampleRate
;
3023 newResampleSamples
= (uint32_t) (FAudio_ceil(
3024 (double) voice
->audio
->updateSize
*
3025 (double) outSampleRate
/
3026 (double) voice
->audio
->master
->master
.inputSampleRate
3028 voice
->src
.resampleSamples
= newResampleSamples
;
3030 FAudio_PlatformUnlockMutex(voice
->sendLock
);
3031 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->sendLock
)
3033 LOG_API_EXIT(voice
->audio
)
3037 /* FAudioMasteringVoice Interface */
3039 FAUDIOAPI
uint32_t FAudioMasteringVoice_GetChannelMask(
3040 FAudioMasteringVoice
*voice
,
3041 uint32_t *pChannelMask
3043 LOG_API_ENTER(voice
->audio
)
3044 FAudio_assert(voice
->type
== FAUDIO_VOICE_MASTER
);
3045 FAudio_assert(pChannelMask
!= NULL
);
3047 *pChannelMask
= voice
->audio
->mixFormat
.dwChannelMask
;
3048 LOG_API_EXIT(voice
->audio
)
3052 #ifdef FAUDIO_DUMP_VOICES
3054 static inline FAudioIOStreamOut
*DumpVoices_fopen(
3055 const FAudioSourceVoice
*voice
,
3056 const FAudioWaveFormatEx
*format
,
3061 uint16_t format_tag
= format
->wFormatTag
;
3062 uint16_t format_ex_tag
= 0;
3063 if (format
->wFormatTag
== FAUDIO_FORMAT_EXTENSIBLE
)
3065 /* get the GUID of the extended subformat */
3066 const FAudioWaveFormatExtensible
*format_ex
=
3067 (const FAudioWaveFormatExtensible
*) format
;
3068 format_ex_tag
= (uint16_t) (format_ex
->SubFormat
.Data1
);
3073 "FA_fmt_0x%04X_0x%04X_0x%016lX%s.wav",
3079 FAudioIOStreamOut
*fileOut
= FAudio_fopen_out(loc
, mode
);
3083 static inline void DumpVoices_finalize_section(
3084 const FAudioSourceVoice
*voice
,
3085 const FAudioWaveFormatEx
*format
,
3086 const char *section
/* one of "data" or "dpds" */
3088 /* data file only contains the real data bytes */
3089 FAudioIOStreamOut
*io_data
= DumpVoices_fopen(voice
, format
, "rb", section
);
3094 FAudio_PlatformLockMutex((FAudioMutex
) io_data
->lock
);
3095 size_t file_size_data
= io_data
->size(io_data
->data
);
3096 if (file_size_data
== 0)
3099 /* close data file */
3100 FAudio_PlatformUnlockMutex((FAudioMutex
) io_data
->lock
);
3101 FAudio_close_out(io_data
);
3105 /* we got some data: append data section to main file */
3106 FAudioIOStreamOut
*io
= DumpVoices_fopen(voice
, format
, "ab", "");
3109 /* close data file */
3110 FAudio_PlatformUnlockMutex((FAudioMutex
) io_data
->lock
);
3111 FAudio_close_out(io_data
);
3115 /* data sub-chunk - 8 bytes + data */
3116 /* SubChunk2ID - 4 --> "data" or "dpds" */
3117 io
->write(io
->data
, section
, 4, 1);
3118 /* Subchunk2Size - 4 */
3119 uint32_t chunk_size
= (uint32_t)file_size_data
;
3120 io
->write(io
->data
, &chunk_size
, 4, 1);
3122 /* fill in data bytes */
3123 uint8_t buffer
[1024*1024];
3125 while((count
= io_data
->read(io_data
->data
, (void*) buffer
, 1, 1024*1024)) > 0)
3127 io
->write(io
->data
, (void*) buffer
, 1, count
);
3130 /* close data file */
3131 FAudio_PlatformUnlockMutex((FAudioMutex
) io_data
->lock
);
3132 FAudio_close_out(io_data
);
3133 /* close main file */
3134 FAudio_PlatformUnlockMutex((FAudioMutex
) io
->lock
);
3135 FAudio_close_out(io
);
3138 static void FAudio_DUMPVOICE_Init(const FAudioSourceVoice
*voice
)
3140 const FAudioWaveFormatEx
*format
= voice
->src
.format
;
3142 FAudioIOStreamOut
*io
= DumpVoices_fopen(voice
, format
, "wb", "");
3147 FAudio_PlatformLockMutex((FAudioMutex
) io
->lock
);
3148 /* another GREAT ressource
3149 * https://wiki.multimedia.cx/index.php/Microsoft_xWMA
3153 /* wave file format taken from
3154 * http://soundfile.sapp.org/doc/WaveFormat
3155 * https://sites.google.com/site/musicgapi/technical-documents/wav-file-format
3156 * |52 49|46 46|52 4A|02 00|
3157 * |c1 sz|af|nc|sp rt|bt rt|
3158 * |ba|bs|da ta|c2 sz|
3160 * | R I F F |chunk size |W A V E |f m t |
3162 * | 52 49 46 46 52 4A 02 00 57 41 56 45 66 6D 74 20 | RIFFRJ..WAVEfmt
3164 * | subchnk size|fmt |nChan |samplerate |byte rate |
3165 * | 50 | 2 |2 |11025 |11289 |
3166 * | 32 00 00 00 02 00 02 00 11 2B 00 00 19 2C 00 00 | 2........+...,..
3168 * |blkaln|bps |efmt |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|
3169 * | 512 |4 |32 |500 |7 |256 |0 |512 |
3170 * | 512 |4 |32 |459252 |256 |
3171 * | 00 02|04 00 20 00 F4 01 07 00 00 01 00 00 00 02 | .... .ô.........
3173 * | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
3175 * | 00 FF 00 00 00 00 C0 00 40 00 F0 00 00 00 CC 01 | .ÿ....À.@.ð...Ì.
3177 * | XXXXXXXXXXXXXXXXXX|d a t a |chunk size |XXXXX |
3179 * | 30 FF 88 01 18 FF 64 61 74 61 00 4A 02 00 00 00 | 0ÿ...ÿdata.J....
3182 uint16_t cbSize
= format
->cbSize
;
3183 const char *formatFourcc
= "WAVE";
3184 uint16_t wFormatTag
= format
->wFormatTag
;
3185 /* special handling for WMAUDIO2 */
3186 if (wFormatTag
== FAUDIO_FORMAT_EXTENSIBLE
&& cbSize
>= 22)
3188 const FAudioWaveFormatExtensible
*format_ex
=
3189 (const FAudioWaveFormatExtensible
*) format
;
3190 uint16_t format_ex_tag
= (uint16_t) (format_ex
->SubFormat
.Data1
);
3191 if (format_ex_tag
== FAUDIO_FORMAT_WMAUDIO2
)
3194 formatFourcc
= "XWMA";
3195 wFormatTag
= FAUDIO_FORMAT_WMAUDIO2
;
3199 { /* RIFF chunk descriptor - 12 byte */
3201 io
->write(io
->data
, "RIFF", 4, 1);
3203 uint32_t filesize
= 0; /* the real file size is written in finalize step */
3204 io
->write(io
->data
, &filesize
, 4, 1);
3206 io
->write(io
->data
, formatFourcc
, 4, 1);
3208 { /* fmt sub-chunk 24 */
3209 /* Subchunk1ID - 4 */
3210 io
->write(io
->data
, "fmt ", 4, 1);
3211 /* Subchunk1Size - 4 */
3212 /* 18 byte for WAVEFORMATEX and cbSize for WAVEFORMATEXTENDED */
3213 uint32_t chunk_data_size
= 18 + (uint32_t) cbSize
;
3214 io
->write(io
->data
, &chunk_data_size
, 4, 1);
3215 /* AudioFormat - 2 */
3216 io
->write(io
->data
, &wFormatTag
, 2, 1);
3217 /* NumChannels - 2 */
3218 io
->write(io
->data
, &format
->nChannels
, 2, 1);
3219 /* SampleRate - 4 */
3220 io
->write(io
->data
, &format
->nSamplesPerSec
, 4, 1);
3222 /* SampleRate * NumChannels * BitsPerSample/8 */
3223 io
->write(io
->data
, &format
->nAvgBytesPerSec
, 4, 1);
3224 /* BlockAlign - 2 */
3225 /* NumChannels * BitsPerSample/8 */
3226 io
->write(io
->data
, &format
->nBlockAlign
, 2, 1);
3227 /* BitsPerSample - 2 */
3228 io
->write(io
->data
, &format
->wBitsPerSample
, 2, 1);
3230 /* in case of extensible audio format write the additional data to the file */
3232 /* always write the cbSize */
3233 io
->write(io
->data
, &cbSize
, 2, 1);
3237 /* we have a WAVEFORMATEXTENSIBLE struct to write */
3238 const FAudioWaveFormatExtensible
*format_ex
=
3239 (const FAudioWaveFormatExtensible
*) format
;
3240 io
->write(io
->data
, &format_ex
->Samples
.wValidBitsPerSample
, 2, 1);
3241 io
->write(io
->data
, &format_ex
->dwChannelMask
, 4, 1);
3242 /* write FAudioGUID */
3243 io
->write(io
->data
, &format_ex
->SubFormat
.Data1
, 4, 1);
3244 io
->write(io
->data
, &format_ex
->SubFormat
.Data2
, 2, 1);
3245 io
->write(io
->data
, &format_ex
->SubFormat
.Data3
, 2, 1);
3246 io
->write(io
->data
, &format_ex
->SubFormat
.Data4
, 1, 8);
3248 if (format
->cbSize
> 22)
3250 /* fill up the remaining cbSize bytes with zeros */
3252 for (uint16_t i
=23; i
<=format
->cbSize
; i
++)
3254 io
->write(io
->data
, &zero
, 1, 1);
3258 { /* dpds sub-chunk - optional - 8 bytes + bufferWMA uint32_t samples */
3259 /* create file to hold the bufferWMA samples */
3260 FAudioIOStreamOut
*io_dpds
= DumpVoices_fopen(voice
, format
, "wb", "dpds");
3261 FAudio_close_out(io_dpds
);
3262 /* io_dpds file will be filled by SubmitBuffer */
3264 { /* data sub-chunk - 8 bytes + data */
3265 /* create file to hold the data samples */
3266 FAudioIOStreamOut
*io_data
= DumpVoices_fopen(voice
, format
, "wb", "data");
3267 FAudio_close_out(io_data
);
3268 /* io_data file will be filled by SubmitBuffer */
3270 FAudio_PlatformUnlockMutex((FAudioMutex
) io
->lock
);
3271 FAudio_close_out(io
);
3274 static void FAudio_DUMPVOICE_Finalize(const FAudioSourceVoice
*voice
)
3276 const FAudioWaveFormatEx
*format
= voice
->src
.format
;
3278 /* add dpds subchunk - optional */
3279 DumpVoices_finalize_section(voice
, format
, "dpds");
3280 /* add data subchunk */
3281 DumpVoices_finalize_section(voice
, format
, "data");
3283 /* open main file to update filesize */
3284 FAudioIOStreamOut
*io
= DumpVoices_fopen(voice
, format
, "r+b", "");
3289 FAudio_PlatformLockMutex((FAudioMutex
) io
->lock
);
3290 size_t file_size
= io
->size(io
->data
);
3291 if (file_size
>= 44)
3293 /* update filesize */
3294 uint32_t chunk_size
= (uint32_t)(file_size
- 8);
3295 io
->seek(io
->data
, 4, FAUDIO_SEEK_SET
);
3296 io
->write(io
->data
, &chunk_size
, 4, 1);
3298 FAudio_PlatformUnlockMutex((FAudioMutex
) io
->lock
);
3299 FAudio_close_out(io
);
3302 static void FAudio_DUMPVOICE_WriteBuffer(
3303 const FAudioSourceVoice
*voice
,
3304 const FAudioBuffer
*pBuffer
,
3305 const FAudioBufferWMA
*pBufferWMA
,
3306 const uint32_t playBegin
,
3307 const uint32_t playLength
3309 FAudioIOStreamOut
*io_data
= DumpVoices_fopen(voice
, voice
->src
.format
, "ab", "data");
3310 if (io_data
== NULL
)
3315 FAudio_PlatformLockMutex((FAudioMutex
) io_data
->lock
);
3316 if (pBufferWMA
!= NULL
)
3318 /* dump encoded buffer contents */
3319 if (pBufferWMA
->PacketCount
> 0)
3321 FAudioIOStreamOut
*io_dpds
= DumpVoices_fopen(voice
, voice
->src
.format
, "ab", "dpds");
3324 FAudio_PlatformLockMutex((FAudioMutex
) io_dpds
->lock
);
3325 /* write to dpds file */
3326 io_dpds
->write(io_dpds
->data
, pBufferWMA
->pDecodedPacketCumulativeBytes
, sizeof(uint32_t), pBufferWMA
->PacketCount
);
3327 FAudio_PlatformUnlockMutex((FAudioMutex
) io_dpds
->lock
);
3328 FAudio_close_out(io_dpds
);
3330 /* write buffer contents to data file */
3331 io_data
->write(io_data
->data
, pBuffer
->pAudioData
, sizeof(uint8_t), pBuffer
->AudioBytes
);
3336 /* dump unencoded buffer contents */
3337 uint16_t bytesPerFrame
= (voice
->src
.format
->nChannels
* voice
->src
.format
->wBitsPerSample
/ 8);
3338 FAudio_assert(bytesPerFrame
> 0);
3339 const void *pAudioDataBegin
= pBuffer
->pAudioData
+ playBegin
*bytesPerFrame
;
3340 io_data
->write(io_data
->data
, pAudioDataBegin
, bytesPerFrame
, playLength
);
3342 FAudio_PlatformUnlockMutex((FAudioMutex
) io_data
->lock
);
3343 FAudio_close_out(io_data
);
3346 #endif /* FAUDIO_DUMP_VOICES */
3348 /* vim: set noexpandtab shiftwidth=8 tabstop=8: */