faudio: Import upstream release 24.05.
[wine.git] / libs / faudio / src / FAudio.c
blob8a4ad9a4b034aadb4e34face33f76ec6f9e453ca
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 = \
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 } \
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 */
67 /* FAudio Version */
69 uint32_t FAudioLinkedVersion(void)
71 return FAUDIO_COMPILED_VERSION;
74 /* FAudio Interface */
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;
86 uint32_t FAudioCOMConstructEXT(FAudio **ppFAudio, uint8_t version)
88 return FAudioCOMConstructWithCustomAllocatorEXT(
89 ppFAudio,
90 version,
91 FAudio_malloc,
92 FAudio_free,
93 FAudio_realloc
97 uint32_t FAudioCreateWithCustomAllocatorEXT(
98 FAudio **ppFAudio,
99 uint32_t Flags,
100 FAudioProcessor XAudio2Processor,
101 FAudioMallocFunc customMalloc,
102 FAudioFreeFunc customFree,
103 FAudioReallocFunc customRealloc
105 FAudioCOMConstructWithCustomAllocatorEXT(
106 ppFAudio,
107 FAUDIO_TARGET_VERSION,
108 customMalloc,
109 customFree,
110 customRealloc
112 FAudio_Initialize(*ppFAudio, Flags, XAudio2Processor);
113 return 0;
116 uint32_t FAudioCOMConstructWithCustomAllocatorEXT(
117 FAudio **ppFAudio,
118 uint8_t version,
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;
145 return 0;
148 uint32_t FAudio_AddRef(FAudio *audio)
150 LOG_API_ENTER(audio)
151 audio->refcount += 1;
152 LOG_API_EXIT(audio)
153 return audio->refcount;
156 uint32_t FAudio_Release(FAudio *audio)
158 uint32_t refcount;
159 LOG_API_ENTER(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);
177 audio->pFree(audio);
178 FAudio_PlatformRelease();
180 else
182 LOG_API_EXIT(audio)
184 return refcount;
187 uint32_t FAudio_GetDeviceCount(FAudio *audio, uint32_t *pCount)
189 LOG_API_ENTER(audio)
190 *pCount = FAudio_PlatformGetDeviceCount();
191 LOG_API_EXIT(audio)
192 return 0;
195 uint32_t FAudio_GetDeviceDetails(
196 FAudio *audio,
197 uint32_t Index,
198 FAudioDeviceDetails *pDeviceDetails
200 uint32_t result;
201 LOG_API_ENTER(audio)
202 result = FAudio_PlatformGetDeviceDetails(Index, pDeviceDetails);
203 LOG_API_EXIT(audio)
204 return result;
207 uint32_t FAudio_Initialize(
208 FAudio *audio,
209 uint32_t Flags,
210 FAudioProcessor XAudio2Processor
212 LOG_API_ENTER(audio)
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);
225 LOG_API_EXIT(audio)
226 return 0;
229 uint32_t FAudio_RegisterForCallbacks(
230 FAudio *audio,
231 FAudioEngineCallback *pCallback
233 LOG_API_ENTER(audio)
234 LinkedList_AddEntry(
235 &audio->callbacks,
236 pCallback,
237 audio->callbackLock,
238 audio->pMalloc
240 LOG_API_EXIT(audio)
241 return 0;
244 void FAudio_UnregisterForCallbacks(
245 FAudio *audio,
246 FAudioEngineCallback *pCallback
248 LOG_API_ENTER(audio)
249 LinkedList_RemoveEntry(
250 &audio->callbacks,
251 pCallback,
252 audio->callbackLock,
253 audio->pFree
255 LOG_API_EXIT(audio)
258 uint32_t FAudio_CreateSourceVoice(
259 FAudio *audio,
260 FAudioSourceVoice **ppSourceVoice,
261 const FAudioWaveFormatEx *pSourceFormat,
262 uint32_t Flags,
263 float MaxFrequencyRatio,
264 FAudioVoiceCallback *pCallback,
265 const FAudioVoiceSends *pSendList,
266 const FAudioEffectChain *pEffectChain
268 uint32_t i;
270 LOG_API_ENTER(audio)
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;
339 FAudio_memcpy(
340 fmtex,
341 pSourceFormat,
342 FAudio_min(cbSize, sizeof(FAudioADPCMWaveFormat))
344 if (cbSize < sizeof(FAudioADPCMWaveFormat))
346 FAudio_zero(
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
356 ) - 6) * 2;
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;
367 FAudio_memcpy(
368 fmtex,
369 pSourceFormat,
370 FAudio_min(cbSize, sizeof(FAudioXMA2WaveFormat))
372 if (cbSize < sizeof(FAudioXMA2WaveFormat))
374 FAudio_zero(
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;
384 else
386 /* direct copy anything else */
387 (*ppSourceVoice)->src.format = (FAudioWaveFormatEx*) audio->pMalloc(
388 sizeof(FAudioWaveFormatEx) + pSourceFormat->cbSize
390 FAudio_memcpy(
391 (*ppSourceVoice)->src.format,
392 pSourceFormat,
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) \
411 (FAudio_memcmp( \
412 &fmtex->SubFormat, \
413 &DATAFORMAT_SUBTYPE_##type, \
414 sizeof(FAudioGUID) \
415 ) == 0)
416 if (COMPARE_GUID(PCM))
418 #define DECODER(bit) \
419 if (fmtex->Format.wBitsPerSample == bit) \
421 (*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodePCM##bit; \
423 DECODER(16)
424 else DECODER(8)
425 else DECODER(24)
426 else DECODER(32)
427 else
429 LOG_ERROR(
430 audio,
431 "Unrecognized wBitsPerSample: %d",
432 fmtex->Format.wBitsPerSample
434 FAudio_assert(0 && "Unrecognized wBitsPerSample!");
436 #undef DECODER
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...?
445 * -flibit
447 if (fmtex->Format.wBitsPerSample == 16)
449 (*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodePCM16;
451 else
453 (*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodePCM32F;
456 else if ( COMPARE_GUID(WMAUDIO2) ||
457 COMPARE_GUID(WMAUDIO3) ||
458 COMPARE_GUID(WMAUDIO_LOSSLESS) )
460 #ifdef HAVE_WMADEC
461 if (FAudio_WMADEC_init(*ppSourceVoice, fmtex->SubFormat.Data1) != 0)
463 (*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
465 #else
466 FAudio_assert(0 && "xWMA is not supported!");
467 (*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
468 #endif /* HAVE_WMADEC */
470 else
472 FAudio_assert(0 && "Unsupported WAVEFORMATEXTENSIBLE subtype!");
474 #undef COMPARE_GUID
476 else if ((*ppSourceVoice)->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
478 #ifdef HAVE_WMADEC
479 if (FAudio_WMADEC_init(*ppSourceVoice, FAUDIO_FORMAT_XMAUDIO2) != 0)
481 (*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
483 #else
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;
494 else
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;
507 else
509 (*ppSourceVoice)->src.resample = FAudio_INTERNAL_ResampleGeneric;
512 (*ppSourceVoice)->src.curBufferOffset = 0;
514 /* Sends/Effects */
515 FAudio_INTERNAL_VoiceOutputFrequency(*ppSourceVoice, pSendList);
516 FAudioVoice_SetEffectChain(*ppSourceVoice, pEffectChain);
518 /* Default Levels */
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);
530 /* Filters */
531 if (Flags & FAUDIO_VOICE_USEFILTER)
533 (*ppSourceVoice)->filterState = (FAudioFilterState*) audio->pMalloc(
534 sizeof(FAudioFilterState) * (*ppSourceVoice)->src.format->nChannels
536 FAudio_zero(
537 (*ppSourceVoice)->filterState,
538 sizeof(FAudioFilterState) * (*ppSourceVoice)->src.format->nChannels
542 /* Sample Storage */
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(
550 audio,
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(
558 &audio->sources,
559 *ppSourceVoice,
560 audio->sourceLock,
561 audio->pMalloc
563 FAudio_AddRef(audio);
565 #ifdef FAUDIO_DUMP_VOICES
566 FAudio_DUMPVOICE_Init(*ppSourceVoice);
567 #endif /* FAUDIO_DUMP_VOICES */
569 LOG_API_EXIT(audio)
570 return 0;
573 uint32_t FAudio_CreateSubmixVoice(
574 FAudio *audio,
575 FAudioSubmixVoice **ppSubmixVoice,
576 uint32_t InputChannels,
577 uint32_t InputSampleRate,
578 uint32_t Flags,
579 uint32_t ProcessingStage,
580 const FAudioVoiceSends *pSendList,
581 const FAudioEffectChain *pEffectChain
583 uint32_t i;
585 LOG_API_ENTER(audio)
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;
610 /* Resampler */
611 if (InputChannels == 1)
613 (*ppSubmixVoice)->mix.resample = FAudio_INTERNAL_ResampleMono;
615 else if (InputChannels == 2)
617 (*ppSubmixVoice)->mix.resample = FAudio_INTERNAL_ResampleStereo;
619 else
621 (*ppSubmixVoice)->mix.resample = FAudio_INTERNAL_ResampleGeneric;
624 /* Sample Storage */
625 (*ppSubmixVoice)->mix.inputSamples = ((uint32_t) FAudio_ceil(
626 audio->updateSize *
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
638 /* Sends/Effects */
639 FAudio_INTERNAL_VoiceOutputFrequency(*ppSubmixVoice, pSendList);
640 FAudioVoice_SetEffectChain(*ppSubmixVoice, pEffectChain);
642 /* Default Levels */
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);
654 /* Filters */
655 if (Flags & FAUDIO_VOICE_USEFILTER)
657 (*ppSubmixVoice)->filterState = (FAudioFilterState*) audio->pMalloc(
658 sizeof(FAudioFilterState) * InputChannels
660 FAudio_zero(
661 (*ppSubmixVoice)->filterState,
662 sizeof(FAudioFilterState) * InputChannels
666 /* Add to list, finally. */
667 FAudio_INTERNAL_InsertSubmixSorted(
668 &audio->submixes,
669 *ppSubmixVoice,
670 audio->submixLock,
671 audio->pMalloc
673 FAudio_AddRef(audio);
675 LOG_API_EXIT(audio)
676 return 0;
679 uint32_t FAudio_CreateMasteringVoice(
680 FAudio *audio,
681 FAudioMasteringVoice **ppMasteringVoice,
682 uint32_t InputChannels,
683 uint32_t InputSampleRate,
684 uint32_t Flags,
685 uint32_t DeviceIndex,
686 const FAudioEffectChain *pEffectChain
688 LOG_API_ENTER(audio)
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)
721 /* Default Levels */
722 (*ppMasteringVoice)->volume = 1.0f;
724 /* Master Properties */
725 (*ppMasteringVoice)->master.inputChannels = InputChannels;
726 (*ppMasteringVoice)->master.inputSampleRate = InputSampleRate;
728 /* Sends/Effects */
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(
743 &audio->mixFormat,
744 audio->master->outputChannels,
745 audio->master->master.inputSampleRate,
746 &DATAFORMAT_SUBTYPE_IEEE_FLOAT
749 /* Platform Device */
750 FAudio_AddRef(audio);
751 FAudio_PlatformInit(
752 audio,
753 audio->initFlags,
754 DeviceIndex,
755 &audio->mixFormat,
756 &audio->updateSize,
757 &audio->platform
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(
774 sizeof(float) *
775 audio->updateSize *
776 (*ppMasteringVoice)->master.inputChannels
780 LOG_API_EXIT(audio)
781 return 0;
784 uint32_t FAudio_CreateMasteringVoice8(
785 FAudio *audio,
786 FAudioMasteringVoice **ppMasteringVoice,
787 uint32_t InputChannels,
788 uint32_t InputSampleRate,
789 uint32_t Flags,
790 uint16_t *szDeviceId,
791 const FAudioEffectChain *pEffectChain,
792 FAudioStreamCategory StreamCategory
794 uint32_t DeviceIndex, retval;
796 LOG_API_ENTER(audio)
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.
801 * -flibit
803 if (szDeviceId == NULL || szDeviceId[0] == 0)
805 DeviceIndex = 0;
807 else
809 DeviceIndex = szDeviceId[0] - L'0';
810 if (DeviceIndex > FAudio_PlatformGetDeviceCount())
812 DeviceIndex = 0;
816 /* Note that StreamCategory is ignored! */
817 retval = FAudio_CreateMasteringVoice(
818 audio,
819 ppMasteringVoice,
820 InputChannels,
821 InputSampleRate,
822 Flags,
823 DeviceIndex,
824 pEffectChain
827 LOG_API_EXIT(audio)
828 return retval;
831 void FAudio_SetEngineProcedureEXT(
832 FAudio *audio,
833 FAudioEngineProcedureEXT clientEngineProc,
834 void *user
836 LOG_API_ENTER(audio)
837 audio->pClientEngineProc = clientEngineProc;
838 audio->clientEngineUser = user;
839 LOG_API_EXIT(audio)
842 uint32_t FAudio_StartEngine(FAudio *audio)
844 LOG_API_ENTER(audio)
845 audio->active = 1;
846 LOG_API_EXIT(audio)
847 return 0;
850 void FAudio_StopEngine(FAudio *audio)
852 LOG_API_ENTER(audio)
853 audio->active = 0;
854 FAudio_OPERATIONSET_CommitAll(audio);
855 FAudio_OPERATIONSET_Execute(audio);
856 LOG_API_EXIT(audio)
859 uint32_t FAudio_CommitOperationSet(FAudio *audio, uint32_t OperationSet)
861 LOG_API_ENTER(audio)
862 if (OperationSet == FAUDIO_COMMIT_ALL)
864 FAudio_OPERATIONSET_CommitAll(audio);
866 else
868 FAudio_OPERATIONSET_Commit(audio, OperationSet);
870 LOG_API_EXIT(audio)
871 return 0;
874 uint32_t FAudio_CommitChanges(FAudio *audio)
876 FAudio_Log(
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(
892 FAudio *audio,
893 FAudioPerformanceData *pPerfData
895 LinkedList *list;
896 FAudioSourceVoice *source;
898 LOG_API_ENTER(audio)
900 FAudio_zero(pPerfData, sizeof(FAudioPerformanceData));
902 FAudio_PlatformLockMutex(audio->sourceLock);
903 LOG_MUTEX_LOCK(audio, audio->sourceLock)
904 list = audio->sources;
905 while (list != NULL)
907 source = (FAudioSourceVoice*) list->entry;
908 pPerfData->TotalSourceVoiceCount += 1;
909 if (source->src.active)
911 pPerfData->ActiveSourceVoiceCount += 1;
913 list = list->next;
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;
921 while (list != NULL)
923 pPerfData->ActiveSubmixVoiceCount += 1;
924 list = list->next;
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;
935 LOG_API_EXIT(audio)
938 void FAudio_SetDebugConfiguration(
939 FAudio *audio,
940 FAudioDebugConfiguration *pDebugConfiguration,
941 void* pReserved
943 #ifndef FAUDIO_DISABLE_DEBUGCONFIGURATION
944 char *env;
946 LOG_API_ENTER(audio)
948 FAudio_memcpy(
949 &audio->debug,
950 pDebugConfiguration,
951 sizeof(FAudioDebugConfiguration)
954 env = FAudio_getenv("FAUDIO_LOG_EVERYTHING");
955 if (env != NULL && *env == '1')
957 audio->debug.TraceMask = (
958 FAUDIO_LOG_ERRORS |
959 FAUDIO_LOG_WARNINGS |
960 FAUDIO_LOG_INFO |
961 FAUDIO_LOG_DETAIL |
962 FAUDIO_LOG_API_CALLS |
963 FAUDIO_LOG_FUNC_CALLS |
964 FAUDIO_LOG_TIMING |
965 FAUDIO_LOG_LOCKS |
966 FAUDIO_LOG_MEMORY |
967 FAUDIO_LOG_STREAMING
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); \
976 if (env != NULL) \
978 if (*env == '1') \
980 audio->debug.TraceMask |= FAUDIO_LOG_##type; \
982 else \
984 audio->debug.TraceMask &= ~FAUDIO_LOG_##type; \
987 CHECK_ENV(ERRORS)
988 CHECK_ENV(WARNINGS)
989 CHECK_ENV(INFO)
990 CHECK_ENV(DETAIL)
991 CHECK_ENV(API_CALLS)
992 CHECK_ENV(FUNC_CALLS)
993 CHECK_ENV(TIMING)
994 CHECK_ENV(LOCKS)
995 CHECK_ENV(MEMORY)
996 CHECK_ENV(STREAMING)
997 #undef CHECK_ENV
998 #define CHECK_ENV(envvar, boolvar) \
999 env = FAudio_getenv("FAUDIO_LOG_LOG" #envvar); \
1000 if (env != NULL) \
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)
1008 #undef CHECK_ENV
1010 LOG_API_EXIT(audio)
1011 #endif /* FAUDIO_DISABLE_DEBUGCONFIGURATION */
1014 void FAudio_GetProcessingQuantum(
1015 FAudio *audio,
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)
1040 volume = 1.f;
1042 else
1044 volume = voice->volume;
1047 if (out->type == FAUDIO_VOICE_MASTER)
1049 oChan = out->master.inputChannels;
1051 else
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(
1068 FAudioVoice *voice,
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;
1090 else
1092 FAudio_assert(0 && "Unknown voice type!");
1095 LOG_API_EXIT(voice->audio)
1098 uint32_t FAudioVoice_SetOutputVoices(
1099 FAudioVoice *voice,
1100 const FAudioVoiceSends *pSendList
1102 uint32_t i;
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)
1120 LOG_ERROR(
1121 voice->audio,
1122 "%s",
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)
1199 return 0;
1202 /* Copy send list */
1203 voice->sends.SendCount = pSendList->SendCount;
1204 voice->sends.pSends = (FAudioSendDescriptor*) voice->audio->pMalloc(
1205 pSendList->SendCount * sizeof(FAudioSendDescriptor)
1207 FAudio_memcpy(
1208 voice->sends.pSends,
1209 pSendList->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;
1230 else
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);
1243 FAudio_memcpy(
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;
1268 else
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;
1291 else
1293 voice->sendMix[i] = FAudio_INTERNAL_Mix_Generic;
1296 else
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
1315 FAudio_zero(
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
1329 FAudio_zero(
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)
1342 return 0;
1345 uint32_t FAudioVoice_SetEffectChain(
1346 FAudioVoice *voice,
1347 const FAudioEffectChain *pEffectChain
1349 uint32_t i;
1350 FAPO *fapo;
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)
1367 LOG_ERROR(
1368 voice->audio,
1369 "%s",
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)
1385 LOG_ERROR(
1386 voice->audio,
1387 "%s",
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;
1405 else
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!
1454 * -flibit
1456 if (fapo->LockForProcess(fapo, 1, &srcLockParams, 1, &dstLockParams))
1458 LOG_ERROR(
1459 voice->audio,
1460 "%s",
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
1472 * effects left.
1474 FAudio_memcpy(&srcFmt, &dstFmt, sizeof(srcFmt));
1477 FAudio_INTERNAL_FreeEffectChain(voice);
1478 FAudio_INTERNAL_AllocEffectChain(
1479 voice,
1480 pEffectChain
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...
1497 FAudio_assert(
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)
1511 return 0;
1514 uint32_t FAudioVoice_EnableEffect(
1515 FAudioVoice *voice,
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(
1524 voice,
1525 EffectIndex,
1526 OperationSet
1528 LOG_API_EXIT(voice->audio)
1529 return 0;
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)
1538 return 0;
1541 uint32_t FAudioVoice_DisableEffect(
1542 FAudioVoice *voice,
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(
1551 voice,
1552 EffectIndex,
1553 OperationSet
1555 LOG_API_EXIT(voice->audio)
1556 return 0;
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)
1565 return 0;
1568 void FAudioVoice_GetEffectState(
1569 FAudioVoice *voice,
1570 uint32_t EffectIndex,
1571 int32_t *pEnabled
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(
1583 FAudioVoice *voice,
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(
1594 voice,
1595 EffectIndex,
1596 pParameters,
1597 ParametersByteSize,
1598 OperationSet
1600 LOG_API_EXIT(voice->audio)
1601 return 0;
1604 if (voice->effects.parameters[EffectIndex] == NULL)
1606 voice->effects.parameters[EffectIndex] = voice->audio->pMalloc(
1607 ParametersByteSize
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],
1617 ParametersByteSize
1619 voice->effects.parameterSizes[EffectIndex] = ParametersByteSize;
1621 FAudio_memcpy(
1622 voice->effects.parameters[EffectIndex],
1623 pParameters,
1624 ParametersByteSize
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)
1630 return 0;
1633 uint32_t FAudioVoice_GetEffectParameters(
1634 FAudioVoice *voice,
1635 uint32_t EffectIndex,
1636 void *pParameters,
1637 uint32_t ParametersByteSize
1639 FAPO *fapo;
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)
1648 return 0;
1651 uint32_t FAudioVoice_SetFilterParametersEXT(
1652 FAudioVoice *voice,
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(
1661 voice,
1662 pParameters,
1663 OperationSet
1665 LOG_API_EXIT(voice->audio)
1666 return 0;
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)
1675 return 0;
1678 if (!(voice->flags & FAUDIO_VOICE_USEFILTER))
1680 LOG_API_EXIT(voice->audio)
1681 return 0;
1684 FAudio_PlatformLockMutex(voice->filterLock);
1685 LOG_MUTEX_LOCK(voice->audio, voice->filterLock)
1686 FAudio_memcpy(
1687 &voice->filter,
1688 pParameters,
1689 sizeof(FAudioFilterParametersEXT)
1691 FAudio_PlatformUnlockMutex(voice->filterLock);
1692 LOG_MUTEX_UNLOCK(voice->audio, voice->filterLock)
1694 LOG_API_EXIT(voice->audio)
1695 return 0;
1698 uint32_t FAudioVoice_SetFilterParameters(
1699 FAudioVoice* voice,
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(
1713 FAudioVoice *voice,
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)
1724 return;
1727 if (!(voice->flags & FAUDIO_VOICE_USEFILTER))
1729 LOG_API_EXIT(voice->audio)
1730 return;
1733 FAudio_PlatformLockMutex(voice->filterLock);
1734 LOG_MUTEX_LOCK(voice->audio, voice->filterLock)
1735 FAudio_memcpy(
1736 pParameters,
1737 &voice->filter,
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(
1746 FAudioVoice* voice,
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(
1763 FAudioVoice *voice,
1764 FAudioVoice *pDestinationVoice,
1765 const FAudioFilterParametersEXT *pParameters,
1766 uint32_t OperationSet
1768 uint32_t i;
1769 LOG_API_ENTER(voice->audio)
1771 if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
1773 FAudio_OPERATIONSET_QueueSetOutputFilterParameters(
1774 voice,
1775 pDestinationVoice,
1776 pParameters,
1777 OperationSet
1779 LOG_API_EXIT(voice->audio)
1780 return 0;
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)
1789 return 0;
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)
1804 break;
1807 if (i >= voice->sends.SendCount)
1809 LOG_ERROR(
1810 voice->audio,
1811 "Destination not attached to source: %p %p",
1812 (void*) voice,
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)
1826 return 0;
1829 /* Set the filter parameters, finally. */
1830 FAudio_memcpy(
1831 &voice->sendFilter[i],
1832 pParameters,
1833 sizeof(FAudioFilterParametersEXT)
1836 FAudio_PlatformUnlockMutex(voice->sendLock);
1837 LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1838 LOG_API_EXIT(voice->audio)
1839 return 0;
1842 uint32_t FAudioVoice_SetOutputFilterParameters(
1843 FAudioVoice* voice,
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(
1858 FAudioVoice *voice,
1859 FAudioVoice *pDestinationVoice,
1860 FAudioFilterParametersEXT *pParameters
1862 uint32_t i;
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)
1872 return;
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)
1887 break;
1890 if (i >= voice->sends.SendCount)
1892 LOG_ERROR(
1893 voice->audio,
1894 "Destination not attached to source: %p %p",
1895 (void*) voice,
1896 (void*) pDestinationVoice
1898 FAudio_PlatformUnlockMutex(voice->sendLock);
1899 LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1900 LOG_API_EXIT(voice->audio)
1901 return;
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)
1909 return;
1912 /* Set the filter parameters, finally. */
1913 FAudio_memcpy(
1914 pParameters,
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(
1925 FAudioVoice *voice,
1926 float Volume,
1927 uint32_t OperationSet
1929 uint32_t i;
1931 LOG_API_ENTER(voice->audio)
1933 if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
1935 FAudio_OPERATIONSET_QueueSetVolume(
1936 voice,
1937 Volume,
1938 OperationSet
1940 LOG_API_EXIT(voice->audio)
1941 return 0;
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(
1951 Volume,
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)
1968 return 0;
1971 void FAudioVoice_GetOutputFilterParameters(
1972 FAudioVoice* voice,
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(
1990 FAudioVoice *voice,
1991 float *pVolume
1993 LOG_API_ENTER(voice->audio)
1994 *pVolume = voice->volume;
1995 LOG_API_EXIT(voice->audio)
1998 uint32_t FAudioVoice_SetChannelVolumes(
1999 FAudioVoice *voice,
2000 uint32_t Channels,
2001 const float *pVolumes,
2002 uint32_t OperationSet
2004 uint32_t i;
2006 LOG_API_ENTER(voice->audio)
2008 if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
2010 FAudio_OPERATIONSET_QueueSetChannelVolumes(
2011 voice,
2012 Channels,
2013 pVolumes,
2014 OperationSet
2016 LOG_API_EXIT(voice->audio)
2017 return 0;
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)
2044 FAudio_memcpy(
2045 voice->channelVolume,
2046 pVolumes,
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)
2062 return 0;
2065 void FAudioVoice_GetChannelVolumes(
2066 FAudioVoice *voice,
2067 uint32_t Channels,
2068 float *pVolumes
2070 LOG_API_ENTER(voice->audio)
2071 FAudio_PlatformLockMutex(voice->volumeLock);
2072 LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
2073 FAudio_memcpy(
2074 pVolumes,
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(
2084 FAudioVoice *voice,
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(
2097 voice,
2098 pDestinationVoice,
2099 SourceChannels,
2100 DestinationChannels,
2101 pLevelMatrix,
2102 OperationSet
2104 LOG_API_EXIT(voice->audio)
2105 return 0;
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)
2121 break;
2124 if (i >= voice->sends.SendCount)
2126 LOG_ERROR(
2127 voice->audio,
2128 "Destination not attached to source: %p %p",
2129 (void*) voice,
2130 (void*) pDestinationVoice
2132 result = FAUDIO_E_INVALID_CALL;
2133 goto end;
2136 /* Verify the Source/Destination channel count */
2137 if (SourceChannels != voice->outputChannels)
2139 LOG_ERROR(
2140 voice->audio,
2141 "SourceChannels not equal to voice channel count: %p %d %d",
2142 (void*) voice,
2143 SourceChannels,
2144 voice->outputChannels
2146 result = FAUDIO_E_INVALID_CALL;
2147 goto end;
2150 if (pDestinationVoice->type == FAUDIO_VOICE_MASTER)
2152 if (DestinationChannels != pDestinationVoice->master.inputChannels)
2154 LOG_ERROR(
2155 voice->audio,
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;
2162 goto end;
2165 else
2167 if (DestinationChannels != pDestinationVoice->mix.inputChannels)
2169 LOG_ERROR(
2170 voice->audio,
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;
2177 goto end;
2181 /* Set the matrix values, finally */
2182 FAudio_PlatformLockMutex(voice->volumeLock);
2183 LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
2185 FAudio_memcpy(
2186 voice->sendCoefficients[i],
2187 pLevelMatrix,
2188 sizeof(float) * SourceChannels * DestinationChannels
2191 FAudio_RecalcMixMatrix(voice, i);
2193 FAudio_PlatformUnlockMutex(voice->volumeLock);
2194 LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
2196 end:
2197 FAudio_PlatformUnlockMutex(voice->sendLock);
2198 LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
2199 LOG_API_EXIT(voice->audio)
2200 return result;
2203 void FAudioVoice_GetOutputMatrix(
2204 FAudioVoice *voice,
2205 FAudioVoice *pDestinationVoice,
2206 uint32_t SourceChannels,
2207 uint32_t DestinationChannels,
2208 float *pLevelMatrix
2210 uint32_t i;
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)
2221 break;
2224 if (i >= voice->sends.SendCount)
2226 LOG_ERROR(
2227 voice->audio,
2228 "Destination not attached to source: %p %p",
2229 (void*) voice,
2230 (void*) pDestinationVoice
2232 FAudio_PlatformUnlockMutex(voice->sendLock);
2233 LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
2234 LOG_API_EXIT(voice->audio)
2235 return;
2238 /* Verify the Source/Destination channel count */
2239 if (voice->type == FAUDIO_VOICE_SOURCE)
2241 FAudio_assert(SourceChannels == voice->src.format->nChannels);
2243 else
2245 FAudio_assert(SourceChannels == voice->mix.inputChannels);
2247 if (pDestinationVoice->type == FAUDIO_VOICE_MASTER)
2249 FAudio_assert(DestinationChannels == pDestinationVoice->master.inputChannels);
2251 else
2253 FAudio_assert(DestinationChannels == pDestinationVoice->mix.inputChannels);
2256 /* Get the matrix values, finally */
2257 FAudio_memcpy(
2258 pLevelMatrix,
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;
2271 uint32_t ret = 0;
2272 FAudioSourceVoice *source;
2273 FAudioSubmixVoice *submix;
2274 LinkedList *list;
2275 uint32_t i;
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 */
2286 break;
2288 if (ret)
2289 break;
2290 list = list->next;
2292 FAudio_PlatformUnlockMutex(audio->sourceLock);
2294 if (ret)
2295 return ret;
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 */
2306 break;
2308 if (ret)
2309 break;
2310 list = list->next;
2312 FAudio_PlatformUnlockMutex(audio->submixLock);
2314 return ret;
2317 uint32_t FAudioVoice_DestroyVoiceSafeEXT(FAudioVoice *voice)
2319 uint32_t i, ret;
2320 LOG_API_ENTER(voice->audio)
2322 if ((ret = check_for_sends_to_voice(voice)))
2324 LOG_ERROR(
2325 voice->audio,
2326 "Voice %p is an output for other voice(s)",
2327 voice
2329 LOG_API_EXIT(voice->audio)
2330 return ret;
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,
2355 voice,
2356 voice->audio->sourceLock,
2357 voice->audio->pFree
2359 FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
2360 LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
2362 entry = voice->src.bufferList;
2363 while (entry != NULL)
2365 next = entry->next;
2366 voice->audio->pFree(entry);
2367 entry = next;
2370 entry = voice->src.flushList;
2371 while (entry != NULL)
2373 next = entry->next;
2374 voice->audio->pFree(entry);
2375 entry = next;
2378 voice->audio->pFree(voice->src.format);
2379 LOG_MUTEX_DESTROY(voice->audio, voice->src.bufferLock)
2380 FAudio_PlatformDestroyMutex(voice->src.bufferLock);
2381 #ifdef HAVE_WMADEC
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,
2393 voice,
2394 voice->audio->submixLock,
2395 voice->audio->pFree
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);
2506 return 0;
2509 void FAudioVoice_DestroyVoice(FAudioVoice *voice)
2511 FAudioVoice_DestroyVoiceSafeEXT(voice);
2514 /* FAudioSourceVoice Interface */
2516 uint32_t FAudioSourceVoice_Start(
2517 FAudioSourceVoice *voice,
2518 uint32_t Flags,
2519 uint32_t OperationSet
2521 LOG_API_ENTER(voice->audio)
2523 if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
2525 FAudio_OPERATIONSET_QueueStart(
2526 voice,
2527 Flags,
2528 OperationSet
2530 LOG_API_EXIT(voice->audio)
2531 return 0;
2535 FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2537 FAudio_assert(Flags == 0);
2538 voice->src.active = 1;
2539 LOG_API_EXIT(voice->audio)
2540 return 0;
2543 uint32_t FAudioSourceVoice_Stop(
2544 FAudioSourceVoice *voice,
2545 uint32_t Flags,
2546 uint32_t OperationSet
2548 LOG_API_ENTER(voice->audio)
2550 if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
2552 FAudio_OPERATIONSET_QueueStop(
2553 voice,
2554 Flags,
2555 OperationSet
2557 LOG_API_EXIT(voice->audio)
2558 return 0;
2561 FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2563 if (Flags & FAUDIO_PLAY_TAILS)
2565 voice->src.active = 2;
2567 else
2569 voice->src.active = 0;
2571 LOG_API_EXIT(voice->audio)
2572 return 0;
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)
2585 LOG_INFO(
2586 voice->audio,
2587 "%p: {Flags: 0x%x, AudioBytes: %u, pAudioData: %p, Play: %u + %u, Loop: %u + %u x %u}",
2588 (void*) voice,
2589 pBuffer->Flags,
2590 pBuffer->AudioBytes,
2591 (const void*) pBuffer->pAudioData,
2592 pBuffer->PlayBegin,
2593 pBuffer->PlayLength,
2594 pBuffer->LoopBegin,
2595 pBuffer->LoopLength,
2596 pBuffer->LoopCount
2599 FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2600 #ifdef HAVE_WMADEC
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;
2626 playLength = (
2627 pBuffer->AudioBytes /
2628 fmtex->wfx.nBlockAlign *
2629 fmtex->wSamplesPerBlock
2630 ) - playBegin;
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)
2639 playLength = (
2640 pBufferWMA->pDecodedPacketCumulativeBytes[pBufferWMA->PacketCount - 1] /
2641 (voice->src.format->nChannels * voice->src.format->wBitsPerSample / 8)
2642 ) - playBegin;
2644 else
2646 playLength = (
2647 pBuffer->AudioBytes /
2648 voice->src.format->nBlockAlign
2649 ) - playBegin;
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;
2691 *adpcmByteCount = (
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 */
2698 loopBegin = 0;
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));
2713 entry->next = NULL;
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 */
2730 /* Submit! */
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;
2739 else
2741 list = voice->src.bufferList;
2742 while (list->next != NULL)
2744 list = list->next;
2746 list->next = entry;
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);
2754 LOG_INFO(
2755 voice->audio,
2756 "%p: appended buffer %p",
2757 (void*) voice,
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)
2763 return 0;
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;
2784 else
2786 voice->src.curBufferOffset = 0;
2787 voice->src.bufferList = NULL;
2788 voice->src.newBuffer = 0;
2791 /* Move them to the pending flush list */
2792 if (entry != NULL)
2794 if (voice->src.flushList == NULL)
2796 voice->src.flushList = entry;
2798 else
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)
2812 return 0;
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)
2835 return 0;
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(
2847 voice,
2848 OperationSet
2850 LOG_API_EXIT(voice->audio)
2851 return 0;
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)
2867 return 0;
2870 void FAudioSourceVoice_GetState(
2871 FAudioSourceVoice *voice,
2872 FAudioVoiceState *pVoiceState,
2873 uint32_t Flags
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;
2912 LOG_INFO(
2913 voice->audio,
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,
2926 float Ratio,
2927 uint32_t OperationSet
2929 LOG_API_ENTER(voice->audio)
2931 if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
2933 FAudio_OPERATIONSET_QueueSetFrequencyRatio(
2934 voice,
2935 Ratio,
2936 OperationSet
2938 LOG_API_EXIT(voice->audio)
2939 return 0;
2941 FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
2943 if (voice->flags & FAUDIO_VOICE_NOPITCH)
2945 LOG_API_EXIT(voice->audio)
2946 return 0;
2949 voice->src.freqRatio = FAudio_clamp(
2950 Ratio,
2951 FAUDIO_MIN_FREQ_RATIO,
2952 voice->src.maxFreqRatio
2954 LOG_API_EXIT(voice->audio)
2955 return 0;
2958 void FAudioSourceVoice_GetFrequencyRatio(
2959 FAudioSourceVoice *voice,
2960 float *pRatio
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(
3004 voice->audio,
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)
3017 return 0;
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)
3034 return 0;
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)
3049 return 0;
3052 #ifdef FAUDIO_DUMP_VOICES
3054 static inline FAudioIOStreamOut *DumpVoices_fopen(
3055 const FAudioSourceVoice *voice,
3056 const FAudioWaveFormatEx *format,
3057 const char *mode,
3058 const char *ext
3060 char loc[64];
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);
3070 FAudio_snprintf(
3071 loc,
3072 sizeof(loc),
3073 "FA_fmt_0x%04X_0x%04X_0x%016lX%s.wav",
3074 format_tag,
3075 format_ex_tag,
3076 (uint64_t) voice,
3079 FAudioIOStreamOut *fileOut = FAudio_fopen_out(loc, mode);
3080 return fileOut;
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);
3090 if (!io_data)
3092 return;
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)
3098 /* nothing to do */
3099 /* close data file */
3100 FAudio_PlatformUnlockMutex((FAudioMutex) io_data->lock);
3101 FAudio_close_out(io_data);
3102 return;
3105 /* we got some data: append data section to main file */
3106 FAudioIOStreamOut *io = DumpVoices_fopen(voice, format, "ab", "");
3107 if (!io)
3109 /* close data file */
3110 FAudio_PlatformUnlockMutex((FAudioMutex) io_data->lock);
3111 FAudio_close_out(io_data);
3112 return;
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);
3121 /* data */
3122 /* fill in data bytes */
3123 uint8_t buffer[1024*1024];
3124 size_t count;
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", "");
3143 if (!io)
3145 return;
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 |
3161 * 19026
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 |
3178 * | | |18944 | |
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)
3193 cbSize = 0;
3194 formatFourcc = "XWMA";
3195 wFormatTag = FAUDIO_FORMAT_WMAUDIO2;
3199 { /* RIFF chunk descriptor - 12 byte */
3200 /* ChunkID - 4 */
3201 io->write(io->data, "RIFF", 4, 1);
3202 /* ChunkSize - 4 */
3203 uint32_t filesize = 0; /* the real file size is written in finalize step */
3204 io->write(io->data, &filesize, 4, 1);
3205 /* Format - 4 */
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);
3221 /* ByteRate - 4 */
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);
3235 if (cbSize >= 22)
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 */
3251 uint8_t zero = 0;
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", "");
3285 if (!io)
3287 return;
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)
3312 return;
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");
3322 if (io_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);
3334 else
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: */