1 /* FAudio - XAudio Reimplementation for FNA
3 * Copyright (c) 2011-2020 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 #ifdef FAUDIO_WIN32_PLATFORM
29 #include "FAudio_internal.h"
38 #include <mfreadwrite.h>
39 #include <propvarutil.h>
42 #include <audioclient.h>
43 #include <mmdeviceapi.h>
45 DEFINE_GUID(CLSID_CWMADecMediaObject
, 0x2eeb4adf, 0x4578, 0x4d10, 0xbc, 0xa7, 0xbb, 0x95, 0x5f, 0x56, 0x32, 0x0a);
46 DEFINE_MEDIATYPE_GUID(MFAudioFormat_XMAudio2
, FAUDIO_FORMAT_XMAUDIO2
);
48 static CRITICAL_SECTION faudio_cs
= { NULL
, -1, 0, 0, 0, 0 };
49 static IMMDeviceEnumerator
*device_enumerator
;
50 static HRESULT init_hr
;
52 struct FAudioWin32PlatformData
59 struct FAudioAudioClientThreadArgs
61 WAVEFORMATEXTENSIBLE format
;
68 void FAudio_Log(char const *msg
)
70 OutputDebugStringA(msg
);
73 static HMODULE kernelbase
= NULL
;
74 static HRESULT (WINAPI
*my_SetThreadDescription
)(HANDLE
, PCWSTR
) = NULL
;
76 static void FAudio_resolve_SetThreadDescription(void)
78 kernelbase
= LoadLibraryA("kernelbase.dll");
82 my_SetThreadDescription
= (HRESULT (WINAPI
*)(HANDLE
, PCWSTR
)) GetProcAddress(kernelbase
, "SetThreadDescription");
83 if (!my_SetThreadDescription
)
85 FreeLibrary(kernelbase
);
90 static void FAudio_set_thread_name(char const *name
)
95 if (!my_SetThreadDescription
)
98 ret
= MultiByteToWideChar(CP_UTF8
, 0, name
, -1, NULL
, 0);
100 nameW
= FAudio_malloc(ret
* sizeof(WCHAR
));
104 ret
= MultiByteToWideChar(CP_UTF8
, 0, name
, -1, nameW
, ret
);
106 my_SetThreadDescription(GetCurrentThread(), nameW
);
111 static HRESULT
FAudio_FillAudioClientBuffer(
112 struct FAudioAudioClientThreadArgs
*args
,
113 IAudioRenderClient
*client
,
120 while (padding
+ args
->updateSize
<= frames
)
122 hr
= IAudioRenderClient_GetBuffer(
127 if (FAILED(hr
)) return hr
;
131 args
->updateSize
* args
->format
.Format
.nBlockAlign
134 if (args
->audio
->active
)
136 FAudio_INTERNAL_UpdateEngine(
142 hr
= IAudioRenderClient_ReleaseBuffer(
147 if (FAILED(hr
)) return hr
;
149 padding
+= args
->updateSize
;
155 static DWORD WINAPI
FAudio_AudioClientThread(void *user
)
157 struct FAudioAudioClientThreadArgs
*args
= user
;
158 IAudioRenderClient
*render_client
;
160 UINT frames
, padding
= 0;
162 FAudio_set_thread_name(__func__
);
164 hr
= IAudioClient_GetService(
166 &IID_IAudioRenderClient
,
167 (void **)&render_client
169 FAudio_assert(!FAILED(hr
) && "Failed to get IAudioRenderClient service!");
171 hr
= IAudioClient_GetBufferSize(args
->client
, &frames
);
172 FAudio_assert(!FAILED(hr
) && "Failed to get IAudioClient buffer size!");
174 hr
= FAudio_FillAudioClientBuffer(args
, render_client
, frames
, 0);
175 FAudio_assert(!FAILED(hr
) && "Failed to initialize IAudioClient buffer!");
177 hr
= IAudioClient_Start(args
->client
);
178 FAudio_assert(!FAILED(hr
) && "Failed to start IAudioClient!");
180 while (WaitForMultipleObjects(2, args
->events
, FALSE
, INFINITE
) == WAIT_OBJECT_0
)
182 hr
= IAudioClient_GetCurrentPadding(args
->client
, &padding
);
183 FAudio_assert(!FAILED(hr
) && "Failed to get IAudioClient current padding!");
185 hr
= FAudio_FillAudioClientBuffer(args
, render_client
, frames
, padding
);
186 FAudio_assert(!FAILED(hr
) && "Failed to fill IAudioClient buffer!");
189 hr
= IAudioClient_Stop(args
->client
);
190 FAudio_assert(!FAILED(hr
) && "Failed to stop IAudioClient!");
192 IAudioRenderClient_Release(render_client
);
197 void FAudio_PlatformInit(
200 uint32_t deviceIndex
,
201 FAudioWaveFormatExtensible
*mixFormat
,
202 uint32_t *updateSize
,
203 void** platformDevice
205 struct FAudioAudioClientThreadArgs
*args
;
206 struct FAudioWin32PlatformData
*data
;
207 REFERENCE_TIME duration
;
208 WAVEFORMATEX
*closest
;
209 IMMDevice
*device
= NULL
;
211 HANDLE audioEvent
= NULL
;
212 BOOL has_sse2
= IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE
);
214 FAudio_INTERNAL_InitSIMDFunctions(has_sse2
, FALSE
);
215 FAudio_resolve_SetThreadDescription();
217 FAudio_PlatformAddRef();
219 *platformDevice
= NULL
;
220 if (deviceIndex
> 0) return;
222 args
= FAudio_malloc(sizeof(*args
));
223 FAudio_assert(!!args
&& "Failed to allocate FAudio thread args!");
225 data
= FAudio_malloc(sizeof(*data
));
226 FAudio_assert(!!data
&& "Failed to allocate FAudio platform data!");
227 FAudio_zero(data
, sizeof(*data
));
229 args
->format
.Format
.wFormatTag
= mixFormat
->Format
.wFormatTag
;
230 args
->format
.Format
.nChannels
= mixFormat
->Format
.nChannels
;
231 args
->format
.Format
.nSamplesPerSec
= mixFormat
->Format
.nSamplesPerSec
;
232 args
->format
.Format
.nAvgBytesPerSec
= mixFormat
->Format
.nAvgBytesPerSec
;
233 args
->format
.Format
.nBlockAlign
= mixFormat
->Format
.nBlockAlign
;
234 args
->format
.Format
.wBitsPerSample
= mixFormat
->Format
.wBitsPerSample
;
235 args
->format
.Format
.cbSize
= mixFormat
->Format
.cbSize
;
237 if (args
->format
.Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
239 args
->format
.Samples
.wValidBitsPerSample
= mixFormat
->Samples
.wValidBitsPerSample
;
240 args
->format
.dwChannelMask
= mixFormat
->dwChannelMask
;
242 &args
->format
.SubFormat
,
243 &mixFormat
->SubFormat
,
248 audioEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
249 FAudio_assert(!!audioEvent
&& "Failed to create FAudio thread buffer event!");
251 data
->stopEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
252 FAudio_assert(!!data
->stopEvent
&& "Failed to create FAudio thread stop event!");
254 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(
260 FAudio_assert(!FAILED(hr
) && "Failed to get default audio endpoint!");
262 hr
= IMMDevice_Activate(
267 (void **)&data
->client
269 FAudio_assert(!FAILED(hr
) && "Failed to create audio client!");
270 IMMDevice_Release(device
);
272 if (flags
& FAUDIO_1024_QUANTUM
) duration
= 213333;
273 else duration
= 100000;
275 hr
= IAudioClient_IsFormatSupported(
277 AUDCLNT_SHAREMODE_SHARED
,
278 &args
->format
.Format
,
281 FAudio_assert(!FAILED(hr
) && "Failed to find supported audio format!");
285 if (closest
->wFormatTag
!= WAVE_FORMAT_EXTENSIBLE
) args
->format
.Format
= *closest
;
286 else args
->format
= *(WAVEFORMATEXTENSIBLE
*)closest
;
287 CoTaskMemFree(closest
);
290 hr
= IAudioClient_Initialize(
292 AUDCLNT_SHAREMODE_SHARED
,
293 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
296 &args
->format
.Format
,
299 FAudio_assert(!FAILED(hr
) && "Failed to initialize audio client!");
301 hr
= IAudioClient_SetEventHandle(data
->client
, audioEvent
);
302 FAudio_assert(!FAILED(hr
) && "Failed to set audio client event!");
304 mixFormat
->Format
.wFormatTag
= args
->format
.Format
.wFormatTag
;
305 mixFormat
->Format
.nChannels
= args
->format
.Format
.nChannels
;
306 mixFormat
->Format
.nSamplesPerSec
= args
->format
.Format
.nSamplesPerSec
;
307 mixFormat
->Format
.nAvgBytesPerSec
= args
->format
.Format
.nAvgBytesPerSec
;
308 mixFormat
->Format
.nBlockAlign
= args
->format
.Format
.nBlockAlign
;
309 mixFormat
->Format
.wBitsPerSample
= args
->format
.Format
.wBitsPerSample
;
311 if (args
->format
.Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
313 mixFormat
->Format
.cbSize
= sizeof(FAudioWaveFormatExtensible
) - sizeof(FAudioWaveFormatEx
);
314 mixFormat
->Samples
.wValidBitsPerSample
= args
->format
.Samples
.wValidBitsPerSample
;
315 mixFormat
->dwChannelMask
= args
->format
.dwChannelMask
;
317 &mixFormat
->SubFormat
,
318 &args
->format
.SubFormat
,
324 mixFormat
->Format
.cbSize
= sizeof(FAudioWaveFormatEx
);
327 args
->client
= data
->client
;
328 args
->events
[0] = audioEvent
;
329 args
->events
[1] = data
->stopEvent
;
331 if (flags
& FAUDIO_1024_QUANTUM
) args
->updateSize
= args
->format
.Format
.nSamplesPerSec
/ (1000.0 / (64.0 / 3.0));
332 else args
->updateSize
= args
->format
.Format
.nSamplesPerSec
/ 100;
334 data
->audioThread
= CreateThread(NULL
, 0, &FAudio_AudioClientThread
, args
, 0, NULL
);
335 FAudio_assert(!!data
->audioThread
&& "Failed to create audio client thread!");
337 *updateSize
= args
->updateSize
;
338 *platformDevice
= data
;
342 void FAudio_PlatformQuit(void* platformDevice
)
344 struct FAudioWin32PlatformData
*data
= platformDevice
;
346 SetEvent(data
->stopEvent
);
347 WaitForSingleObject(data
->audioThread
, INFINITE
);
348 if (data
->client
) IAudioClient_Release(data
->client
);
351 my_SetThreadDescription
= NULL
;
352 FreeLibrary(kernelbase
);
355 FAudio_PlatformRelease();
358 void FAudio_PlatformAddRef()
361 EnterCriticalSection(&faudio_cs
);
362 if (!device_enumerator
)
364 init_hr
= CoInitialize(NULL
);
365 hr
= CoCreateInstance(
366 &CLSID_MMDeviceEnumerator
,
368 CLSCTX_INPROC_SERVER
,
369 &IID_IMMDeviceEnumerator
,
370 (void**)&device_enumerator
372 FAudio_assert(!FAILED(hr
) && "CoCreateInstance failed!");
374 else IMMDeviceEnumerator_AddRef(device_enumerator
);
375 LeaveCriticalSection(&faudio_cs
);
378 void FAudio_PlatformRelease()
380 EnterCriticalSection(&faudio_cs
);
381 if (!IMMDeviceEnumerator_Release(device_enumerator
))
383 device_enumerator
= NULL
;
384 if (SUCCEEDED(init_hr
)) CoUninitialize();
386 LeaveCriticalSection(&faudio_cs
);
389 uint32_t FAudio_PlatformGetDeviceCount(void)
395 FAudio_PlatformAddRef();
396 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(
403 if (hr
== E_NOTFOUND
) {
404 FAudio_PlatformRelease();
408 FAudio_assert(!FAILED(hr
) && "Failed to get default audio endpoint!");
410 IMMDevice_Release(device
);
411 FAudio_PlatformRelease();
416 uint32_t FAudio_PlatformGetDeviceDetails(
418 FAudioDeviceDetails
*details
420 WAVEFORMATEX
*format
, *obtained
;
421 WAVEFORMATEXTENSIBLE
*ext
;
422 IAudioClient
*client
;
429 FAudio_memset(details
, 0, sizeof(FAudioDeviceDetails
));
430 if (index
> 0) return FAUDIO_E_INVALID_CALL
;
432 FAudio_PlatformAddRef();
434 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(
440 FAudio_assert(!FAILED(hr
) && "Failed to get default audio endpoint!");
442 details
->Role
= FAudioGlobalDefaultDevice
;
444 hr
= IMMDevice_GetId(device
, &str
);
445 FAudio_assert(!FAILED(hr
) && "Failed to get audio endpoint id!");
447 lstrcpynW((WCHAR
*)details
->DeviceID
, str
, ARRAYSIZE(details
->DeviceID
) - 1);
448 lstrcpynW((WCHAR
*)details
->DisplayName
, str
, ARRAYSIZE(details
->DisplayName
) - 1);
451 hr
= IMMDevice_Activate(
458 FAudio_assert(!FAILED(hr
) && "Failed to activate audio client!");
460 hr
= IAudioClient_GetMixFormat(client
, &format
);
461 FAudio_assert(!FAILED(hr
) && "Failed to get audio client mix format!");
463 if (format
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
465 ext
= (WAVEFORMATEXTENSIBLE
*)format
;
466 sub
= ext
->SubFormat
;
469 &DATAFORMAT_SUBTYPE_PCM
,
473 hr
= IAudioClient_IsFormatSupported(client
, AUDCLNT_SHAREMODE_SHARED
, format
, &obtained
);
476 ext
->SubFormat
= sub
;
480 CoTaskMemFree(format
);
485 details
->OutputFormat
.Format
.wFormatTag
= format
->wFormatTag
;
486 details
->OutputFormat
.Format
.nChannels
= format
->nChannels
;
487 details
->OutputFormat
.Format
.nSamplesPerSec
= format
->nSamplesPerSec
;
488 details
->OutputFormat
.Format
.nAvgBytesPerSec
= format
->nAvgBytesPerSec
;
489 details
->OutputFormat
.Format
.nBlockAlign
= format
->nBlockAlign
;
490 details
->OutputFormat
.Format
.wBitsPerSample
= format
->wBitsPerSample
;
491 details
->OutputFormat
.Format
.cbSize
= format
->cbSize
;
493 if (format
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
495 ext
= (WAVEFORMATEXTENSIBLE
*)format
;
496 details
->OutputFormat
.Samples
.wValidBitsPerSample
= ext
->Samples
.wValidBitsPerSample
;
497 details
->OutputFormat
.dwChannelMask
= ext
->dwChannelMask
;
499 &details
->OutputFormat
.SubFormat
,
506 details
->OutputFormat
.dwChannelMask
= GetMask(format
->nChannels
);
509 CoTaskMemFree(format
);
511 IAudioClient_Release(client
);
513 IMMDevice_Release(device
);
515 FAudio_PlatformRelease();
520 FAudioMutex
FAudio_PlatformCreateMutex(void)
522 CRITICAL_SECTION
*cs
;
524 cs
= FAudio_malloc(sizeof(CRITICAL_SECTION
));
525 if (!cs
) return NULL
;
527 InitializeCriticalSection(cs
);
532 void FAudio_PlatformLockMutex(FAudioMutex mutex
)
534 if (mutex
) EnterCriticalSection(mutex
);
537 void FAudio_PlatformUnlockMutex(FAudioMutex mutex
)
539 if (mutex
) LeaveCriticalSection(mutex
);
542 void FAudio_PlatformDestroyMutex(FAudioMutex mutex
)
544 if (mutex
) DeleteCriticalSection(mutex
);
548 struct FAudioThreadArgs
550 FAudioThreadFunc func
;
555 static DWORD WINAPI
FaudioThreadWrapper(void *user
)
557 struct FAudioThreadArgs
*args
= user
;
560 FAudio_set_thread_name(args
->name
);
561 ret
= args
->func(args
->data
);
567 FAudioThread
FAudio_PlatformCreateThread(
568 FAudioThreadFunc func
,
572 struct FAudioThreadArgs
*args
;
574 if (!(args
= FAudio_malloc(sizeof(*args
)))) return NULL
;
579 return CreateThread(NULL
, 0, &FaudioThreadWrapper
, args
, 0, NULL
);
582 void FAudio_PlatformWaitThread(FAudioThread thread
, int32_t *retval
)
584 WaitForSingleObject(thread
, INFINITE
);
585 if (retval
!= NULL
) GetExitCodeThread(thread
, (DWORD
*)retval
);
588 void FAudio_PlatformThreadPriority(FAudioThreadPriority priority
)
593 uint64_t FAudio_PlatformGetThreadID(void)
595 return GetCurrentThreadId();
598 void FAudio_sleep(uint32_t ms
)
603 uint32_t FAudio_timems()
605 return GetTickCount();
610 static size_t FAUDIOCALL
FAudio_FILE_read(
617 return fread(dst
, size
, count
, data
);
620 static int64_t FAUDIOCALL
FAudio_FILE_seek(
625 if (!data
) return -1;
626 fseek(data
, offset
, whence
);
630 static int FAUDIOCALL
FAudio_FILE_close(void *data
)
637 FAudioIOStream
* FAudio_fopen(const char *path
)
641 io
= (FAudioIOStream
*) FAudio_malloc(sizeof(FAudioIOStream
));
642 if (!io
) return NULL
;
644 io
->data
= fopen(path
, "rb");
645 io
->read
= FAudio_FILE_read
;
646 io
->seek
= FAudio_FILE_seek
;
647 io
->close
= FAudio_FILE_close
;
648 io
->lock
= FAudio_PlatformCreateMutex();
660 static size_t FAUDIOCALL
FAudio_mem_read(
666 struct FAudio_mem
*io
= data
;
667 size_t len
= size
* count
;
671 while (len
&& len
> (io
->len
- io
->pos
)) len
-= size
;
672 FAudio_memcpy(dst
, io
->mem
+ io
->pos
, len
);
678 static int64_t FAUDIOCALL
FAudio_mem_seek(
683 struct FAudio_mem
*io
= data
;
684 if (!data
) return -1;
686 if (whence
== SEEK_SET
)
688 if (io
->len
> offset
) io
->pos
= offset
;
689 else io
->pos
= io
->len
;
691 if (whence
== SEEK_CUR
)
693 if (io
->len
> io
->pos
+ offset
) io
->pos
+= offset
;
694 else io
->pos
= io
->len
;
696 if (whence
== SEEK_END
)
698 if (io
->len
> offset
) io
->pos
= io
->len
- offset
;
705 static int FAUDIOCALL
FAudio_mem_close(void *data
)
712 FAudioIOStream
* FAudio_memopen(void *mem
, int len
)
714 struct FAudio_mem
*data
;
717 io
= (FAudioIOStream
*) FAudio_malloc(sizeof(FAudioIOStream
));
718 if (!io
) return NULL
;
720 data
= FAudio_malloc(sizeof(struct FAudio_mem
));
732 io
->read
= FAudio_mem_read
;
733 io
->seek
= FAudio_mem_seek
;
734 io
->close
= FAudio_mem_close
;
735 io
->lock
= FAudio_PlatformCreateMutex();
739 uint8_t* FAudio_memptr(FAudioIOStream
*io
, size_t offset
)
741 struct FAudio_mem
*memio
= io
->data
;
742 return (uint8_t *)memio
->mem
+ offset
;
745 void FAudio_close(FAudioIOStream
*io
)
748 FAudio_PlatformDestroyMutex((FAudioMutex
) io
->lock
);
752 /* XNA Song implementation over Win32 MF */
754 static FAudioWaveFormatEx activeSongFormat
;
755 IMFSourceReader
*activeSong
;
756 static uint8_t *songBuffer
;
757 static SIZE_T songBufferSize
;
759 static float songVolume
= 1.0f
;
760 static FAudio
*songAudio
= NULL
;
761 static FAudioMasteringVoice
*songMaster
= NULL
;
763 static FAudioSourceVoice
*songVoice
= NULL
;
764 static FAudioVoiceCallback callbacks
;
766 /* Internal Functions */
768 static void XNA_SongSubmitBuffer(FAudioVoiceCallback
*callback
, void *pBufferContext
)
770 IMFMediaBuffer
*media_buffer
;
774 DWORD flags
, buffer_size
= 0;
777 LOG_FUNC_ENTER(songAudio
);
779 FAudio_memset(&buffer
, 0, sizeof(buffer
));
781 hr
= IMFSourceReader_ReadSample(
783 MF_SOURCE_READER_FIRST_AUDIO_STREAM
,
790 FAudio_assert(!FAILED(hr
) && "Failed to read audio sample!");
792 if (flags
& MF_SOURCE_READERF_ENDOFSTREAM
)
794 buffer
.Flags
= FAUDIO_END_OF_STREAM
;
798 hr
= IMFSample_ConvertToContiguousBuffer(
802 FAudio_assert(!FAILED(hr
) && "Failed to get sample buffer!");
804 hr
= IMFMediaBuffer_Lock(
810 FAudio_assert(!FAILED(hr
) && "Failed to lock buffer bytes!");
812 if (songBufferSize
< buffer_size
)
814 songBufferSize
= buffer_size
;
815 songBuffer
= FAudio_realloc(songBuffer
, songBufferSize
);
816 FAudio_assert(songBuffer
!= NULL
&& "Failed to allocate song buffer!");
818 FAudio_memcpy(songBuffer
, buffer_ptr
, buffer_size
);
820 hr
= IMFMediaBuffer_Unlock(media_buffer
);
821 FAudio_assert(!FAILED(hr
) && "Failed to unlock buffer bytes!");
823 IMFMediaBuffer_Release(media_buffer
);
824 IMFSample_Release(sample
);
829 buffer
.AudioBytes
= buffer_size
;
830 buffer
.pAudioData
= songBuffer
;
831 buffer
.PlayBegin
= 0;
832 buffer
.PlayLength
= buffer_size
/ activeSongFormat
.nBlockAlign
;
833 buffer
.LoopBegin
= 0;
834 buffer
.LoopLength
= 0;
835 buffer
.LoopCount
= 0;
836 buffer
.pContext
= NULL
;
837 FAudioSourceVoice_SubmitSourceBuffer(
844 LOG_FUNC_EXIT(songAudio
);
847 static void XNA_SongKill()
849 if (songVoice
!= NULL
)
851 FAudioSourceVoice_Stop(songVoice
, 0, 0);
852 FAudioVoice_DestroyVoice(songVoice
);
857 IMFSourceReader_Release(activeSong
);
860 FAudio_free(songBuffer
);
867 FAUDIOAPI
void XNA_SongInit()
871 hr
= MFStartup(MF_VERSION
, MFSTARTUP_FULL
);
872 FAudio_assert(!FAILED(hr
) && "Failed to initialize Media Foundation!");
874 FAudioCreate(&songAudio
, 0, FAUDIO_DEFAULT_PROCESSOR
);
875 FAudio_CreateMasteringVoice(
878 FAUDIO_DEFAULT_CHANNELS
,
879 FAUDIO_DEFAULT_SAMPLERATE
,
886 FAUDIOAPI
void XNA_SongQuit()
889 FAudioVoice_DestroyVoice(songMaster
);
890 FAudio_Release(songAudio
);
894 FAUDIOAPI
float XNA_PlaySong(const char *name
)
896 IMFAttributes
*attributes
= NULL
;
897 IMFMediaType
*media_type
= NULL
;
898 UINT32 channels
, samplerate
;
902 WCHAR filename_w
[MAX_PATH
];
904 LOG_FUNC_ENTER(songAudio
);
905 LOG_INFO(songAudio
, "name %s\n", name
);
908 MultiByteToWideChar(CP_UTF8
, 0, name
, -1, filename_w
, MAX_PATH
);
910 hr
= MFCreateAttributes(&attributes
, 1);
911 FAudio_assert(!FAILED(hr
) && "Failed to create attributes!");
912 hr
= MFCreateSourceReaderFromURL(
917 FAudio_assert(!FAILED(hr
) && "Failed to create source reader!");
918 IMFAttributes_Release(attributes
);
920 hr
= MFCreateMediaType(&media_type
);
921 FAudio_assert(!FAILED(hr
) && "Failed to create media type!");
922 hr
= IMFMediaType_SetGUID(
927 FAudio_assert(!FAILED(hr
) && "Failed to set major type!");
928 hr
= IMFMediaType_SetGUID(
933 FAudio_assert(!FAILED(hr
) && "Failed to set sub type!");
934 hr
= IMFSourceReader_SetCurrentMediaType(
936 MF_SOURCE_READER_FIRST_AUDIO_STREAM
,
940 FAudio_assert(!FAILED(hr
) && "Failed to set source media type!");
941 hr
= IMFSourceReader_SetStreamSelection(
943 MF_SOURCE_READER_FIRST_AUDIO_STREAM
,
946 FAudio_assert(!FAILED(hr
) && "Failed to select source stream!");
947 IMFMediaType_Release(media_type
);
949 hr
= IMFSourceReader_GetCurrentMediaType(
951 MF_SOURCE_READER_FIRST_AUDIO_STREAM
,
954 FAudio_assert(!FAILED(hr
) && "Failed to get current media type!");
955 hr
= IMFMediaType_GetUINT32(
957 &MF_MT_AUDIO_NUM_CHANNELS
,
960 FAudio_assert(!FAILED(hr
) && "Failed to get channel count!");
961 hr
= IMFMediaType_GetUINT32(
963 &MF_MT_AUDIO_SAMPLES_PER_SECOND
,
966 FAudio_assert(!FAILED(hr
) && "Failed to get sample rate!");
967 IMFMediaType_Release(media_type
);
969 hr
= IMFSourceReader_GetPresentationAttribute(
971 MF_SOURCE_READER_MEDIASOURCE
,
975 FAudio_assert(!FAILED(hr
) && "Failed to get song duration!");
976 hr
= PropVariantToInt64(&var
, &duration
);
977 FAudio_assert(!FAILED(hr
) && "Failed to get song duration!");
978 PropVariantClear(&var
);
980 activeSongFormat
.wFormatTag
= FAUDIO_FORMAT_IEEE_FLOAT
;
981 activeSongFormat
.nChannels
= channels
;
982 activeSongFormat
.nSamplesPerSec
= samplerate
;
983 activeSongFormat
.wBitsPerSample
= sizeof(float) * 8;
984 activeSongFormat
.nBlockAlign
= activeSongFormat
.nChannels
* activeSongFormat
.wBitsPerSample
/ 8;
985 activeSongFormat
.nAvgBytesPerSec
= activeSongFormat
.nSamplesPerSec
* activeSongFormat
.nBlockAlign
;
986 activeSongFormat
.cbSize
= 0;
989 FAudio_zero(&callbacks
, sizeof(FAudioVoiceCallback
));
990 callbacks
.OnBufferEnd
= XNA_SongSubmitBuffer
;
991 FAudio_CreateSourceVoice(
996 1.0f
, /* No pitch shifting here! */
1001 FAudioVoice_SetVolume(songVoice
, songVolume
, 0);
1002 XNA_SongSubmitBuffer(NULL
, NULL
);
1005 FAudioSourceVoice_Start(songVoice
, 0, 0);
1006 LOG_FUNC_EXIT(songAudio
);
1007 return duration
/ 10000000.;
1010 FAUDIOAPI
void XNA_PauseSong()
1012 if (songVoice
== NULL
)
1016 FAudioSourceVoice_Stop(songVoice
, 0, 0);
1019 FAUDIOAPI
void XNA_ResumeSong()
1021 if (songVoice
== NULL
)
1025 FAudioSourceVoice_Start(songVoice
, 0, 0);
1028 FAUDIOAPI
void XNA_StopSong()
1033 FAUDIOAPI
void XNA_SetSongVolume(float volume
)
1035 songVolume
= volume
;
1036 if (songVoice
!= NULL
)
1038 FAudioVoice_SetVolume(songVoice
, songVolume
, 0);
1042 FAUDIOAPI
uint32_t XNA_GetSongEnded()
1044 FAudioVoiceState state
;
1045 if (songVoice
== NULL
|| activeSong
== NULL
)
1049 FAudioSourceVoice_GetState(songVoice
, &state
, 0);
1050 return state
.BuffersQueued
== 0;
1053 FAUDIOAPI
void XNA_EnableVisualization(uint32_t enable
)
1055 /* TODO: Enable/Disable FAPO effect */
1058 FAUDIOAPI
uint32_t XNA_VisualizationEnabled()
1060 /* TODO: Query FAPO effect enabled */
1064 FAUDIOAPI
void XNA_GetSongVisualizationData(
1069 /* TODO: Visualization FAPO that reads in Song samples, FFT analysis */
1072 /* FAudio WMADEC implementation over Win32 MF */
1076 IMFTransform
*decoder
;
1077 IMFSample
*output_sample
;
1086 static HRESULT
FAudio_WMAMF_ProcessInput(
1088 FAudioBuffer
*buffer
1090 struct FAudioWMADEC
*impl
= voice
->src
.wmadec
;
1091 IMFMediaBuffer
*media_buffer
;
1097 copy_size
= min(buffer
->AudioBytes
- impl
->input_pos
, impl
->input_size
);
1098 if (!copy_size
) return S_FALSE
;
1099 LOG_INFO(voice
->audio
, "pushing %lx bytes at %Ix", copy_size
, impl
->input_pos
);
1101 hr
= MFCreateSample(&sample
);
1102 FAudio_assert(!FAILED(hr
) && "Failed to create sample!");
1103 hr
= MFCreateMemoryBuffer(copy_size
, &media_buffer
);
1104 FAudio_assert(!FAILED(hr
) && "Failed to create buffer!");
1105 hr
= IMFMediaBuffer_SetCurrentLength(media_buffer
, copy_size
);
1106 FAudio_assert(!FAILED(hr
) && "Failed to set buffer length!");
1107 hr
= IMFMediaBuffer_Lock(
1113 FAudio_assert(!FAILED(hr
) && "Failed to lock buffer bytes!");
1114 FAudio_memcpy(copy_buf
, buffer
->pAudioData
+ impl
->input_pos
, copy_size
);
1115 hr
= IMFMediaBuffer_Unlock(media_buffer
);
1116 FAudio_assert(!FAILED(hr
) && "Failed to unlock buffer bytes!");
1118 hr
= IMFSample_AddBuffer(sample
, media_buffer
);
1119 FAudio_assert(!FAILED(hr
) && "Failed to buffer to sample!");
1120 IMFMediaBuffer_Release(media_buffer
);
1122 hr
= IMFTransform_ProcessInput(impl
->decoder
, 0, sample
, 0);
1123 IMFSample_Release(sample
);
1124 if (hr
== MF_E_NOTACCEPTING
) return S_OK
;
1127 LOG_ERROR(voice
->audio
, "IMFTransform_ProcessInput returned %#lx", hr
);
1131 impl
->input_pos
+= copy_size
;
1135 static HRESULT
FAudio_WMAMF_ProcessOutput(
1137 FAudioBuffer
*buffer
1139 struct FAudioWMADEC
*impl
= voice
->src
.wmadec
;
1140 MFT_OUTPUT_DATA_BUFFER output
;
1141 IMFMediaBuffer
*media_buffer
;
1142 DWORD status
, copy_size
;
1148 FAudio_memset(&output
, 0, sizeof(output
));
1149 output
.pSample
= impl
->output_sample
;
1150 hr
= IMFTransform_ProcessOutput(impl
->decoder
, 0, 1, &output
, &status
);
1151 if (hr
== MF_E_TRANSFORM_NEED_MORE_INPUT
) return S_FALSE
;
1154 LOG_ERROR(voice
->audio
, "IMFTransform_ProcessInput returned %#lx", hr
);
1158 if (output
.dwStatus
& MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE
) continue;
1160 hr
= IMFSample_ConvertToContiguousBuffer(
1164 FAudio_assert(!FAILED(hr
) && "Failed to get sample buffer!");
1165 hr
= IMFMediaBuffer_Lock(
1171 FAudio_assert(!FAILED(hr
) && "Failed to lock buffer bytes!");
1172 if (impl
->output_pos
+ copy_size
> impl
->output_size
)
1174 impl
->output_size
= max(
1175 impl
->output_pos
+ copy_size
,
1176 impl
->output_size
* 3 / 2
1178 impl
->output_buf
= voice
->audio
->pRealloc(
1182 FAudio_assert(impl
->output_buf
&& "Failed to resize output buffer!");
1184 FAudio_memcpy(impl
->output_buf
+ impl
->output_pos
, copy_buf
, copy_size
);
1185 impl
->output_pos
+= copy_size
;
1186 LOG_INFO(voice
->audio
, "pulled %lx bytes at %Ix", copy_size
, impl
->output_pos
);
1187 hr
= IMFMediaBuffer_Unlock(media_buffer
);
1188 FAudio_assert(!FAILED(hr
) && "Failed to unlock buffer bytes!");
1190 IMFMediaBuffer_Release(media_buffer
);
1191 if (!impl
->output_sample
) IMFSample_Release(output
.pSample
);
1197 static void FAudio_INTERNAL_DecodeWMAMF(
1199 FAudioBuffer
*buffer
,
1203 const FAudioWaveFormatExtensible
*wfx
= (FAudioWaveFormatExtensible
*)voice
->src
.format
;
1204 size_t samples_pos
, samples_size
, copy_size
= 0;
1205 struct FAudioWMADEC
*impl
= voice
->src
.wmadec
;
1208 LOG_FUNC_ENTER(voice
->audio
)
1210 if (!impl
->output_pos
)
1212 if (wfx
->Format
.wFormatTag
== FAUDIO_FORMAT_EXTENSIBLE
)
1214 const FAudioBufferWMA
*wma
= &voice
->src
.bufferList
->bufferWMA
;
1215 const UINT32
*output_sizes
= wma
->pDecodedPacketCumulativeBytes
;
1217 impl
->input_size
= wfx
->Format
.nBlockAlign
;
1218 impl
->output_size
= max(
1220 output_sizes
[wma
->PacketCount
- 1]
1225 const FAudioXMA2WaveFormat
*xwf
= (const FAudioXMA2WaveFormat
*)wfx
;
1227 impl
->input_size
= xwf
->dwBytesPerBlock
;
1228 impl
->output_size
= max(
1230 (size_t) xwf
->dwSamplesEncoded
*
1231 voice
->src
.format
->nChannels
*
1232 (voice
->src
.format
->wBitsPerSample
/ 8)
1236 impl
->output_buf
= voice
->audio
->pRealloc(
1240 FAudio_assert(impl
->output_buf
&& "Failed to allocate output buffer!");
1242 LOG_INFO(voice
->audio
, "sending BOS to %p", impl
->decoder
);
1243 hr
= IMFTransform_ProcessMessage(
1245 MFT_MESSAGE_NOTIFY_START_OF_STREAM
,
1248 FAudio_assert(!FAILED(hr
) && "Failed to notify decoder stream start!");
1249 FAudio_WMAMF_ProcessInput(voice
, buffer
);
1252 samples_pos
= voice
->src
.curBufferOffset
* voice
->src
.format
->nChannels
* sizeof(float);
1253 samples_size
= samples
* voice
->src
.format
->nChannels
* sizeof(float);
1255 while (impl
->output_pos
< samples_pos
+ samples_size
)
1257 hr
= FAudio_WMAMF_ProcessOutput(voice
, buffer
);
1258 if (FAILED(hr
)) goto error
;
1259 if (hr
== S_OK
) continue;
1261 hr
= FAudio_WMAMF_ProcessInput(voice
, buffer
);
1262 if (FAILED(hr
)) goto error
;
1263 if (hr
== S_OK
) continue;
1265 if (!impl
->input_size
) break;
1267 LOG_INFO(voice
->audio
, "sending EOS to %p", impl
->decoder
);
1268 hr
= IMFTransform_ProcessMessage(
1270 MFT_MESSAGE_NOTIFY_END_OF_STREAM
,
1273 FAudio_assert(!FAILED(hr
) && "Failed to send EOS!");
1274 impl
->input_size
= 0;
1277 if (impl
->output_pos
> samples_pos
)
1279 copy_size
= FAudio_min(impl
->output_pos
- samples_pos
, samples_size
);
1280 FAudio_memcpy(decodeCache
, impl
->output_buf
+ samples_pos
, copy_size
);
1282 FAudio_zero(decodeCache
+ copy_size
, samples_size
- copy_size
);
1285 "decoded %Ix / %Ix bytes, copied %Ix / %Ix bytes",
1292 LOG_FUNC_EXIT(voice
->audio
)
1296 FAudio_zero(decodeCache
, samples
* voice
->src
.format
->nChannels
* sizeof(float));
1297 LOG_FUNC_EXIT(voice
->audio
)
1300 uint32_t FAudio_WMADEC_init(FAudioSourceVoice
*voice
, uint32_t type
)
1302 static const uint8_t fake_codec_data
[16] = {0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1303 uint8_t fake_codec_data_wma3
[18] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 0, 0, 0};
1304 const FAudioWaveFormatExtensible
*wfx
= (FAudioWaveFormatExtensible
*)voice
->src
.format
;
1305 struct FAudioWMADEC
*impl
;
1306 MFT_OUTPUT_STREAM_INFO info
= {0};
1307 IMFMediaBuffer
*media_buffer
;
1308 IMFMediaType
*media_type
;
1309 IMFTransform
*decoder
;
1314 LOG_FUNC_ENTER(voice
->audio
)
1316 if (!(impl
= voice
->audio
->pMalloc(sizeof(*impl
)))) return -1;
1317 FAudio_memset(impl
, 0, sizeof(*impl
));
1319 hr
= CoCreateInstance(
1320 &CLSID_CWMADecMediaObject
,
1322 CLSCTX_INPROC_SERVER
,
1328 voice
->audio
->pFree(impl
->output_buf
);
1332 hr
= MFCreateMediaType(&media_type
);
1333 FAudio_assert(!FAILED(hr
) && "Failed create media type!");
1334 hr
= IMFMediaType_SetGUID(
1339 FAudio_assert(!FAILED(hr
) && "Failed set media major type!");
1343 case FAUDIO_FORMAT_WMAUDIO2
:
1344 hr
= IMFMediaType_SetBlob(
1347 (void *)fake_codec_data
,
1348 sizeof(fake_codec_data
)
1350 FAudio_assert(!FAILED(hr
) && "Failed set codec private data!");
1351 hr
= IMFMediaType_SetGUID(
1354 &MFAudioFormat_WMAudioV8
1356 FAudio_assert(!FAILED(hr
) && "Failed set media sub type!");
1357 hr
= IMFMediaType_SetUINT32(
1359 &MF_MT_AUDIO_BLOCK_ALIGNMENT
,
1360 wfx
->Format
.nBlockAlign
1362 FAudio_assert(!FAILED(hr
) && "Failed set input block align!");
1364 case FAUDIO_FORMAT_WMAUDIO3
:
1365 *(uint16_t *)fake_codec_data_wma3
= voice
->src
.format
->wBitsPerSample
;
1366 for (i
= 0; i
< voice
->src
.format
->nChannels
; i
++)
1368 fake_codec_data_wma3
[2] <<= 1;
1369 fake_codec_data_wma3
[2] |= 1;
1371 hr
= IMFMediaType_SetBlob(
1374 (void *)fake_codec_data_wma3
,
1375 sizeof(fake_codec_data_wma3
)
1377 FAudio_assert(!FAILED(hr
) && "Failed set codec private data!");
1378 hr
= IMFMediaType_SetGUID(
1381 &MFAudioFormat_WMAudioV9
1383 FAudio_assert(!FAILED(hr
) && "Failed set media sub type!");
1384 hr
= IMFMediaType_SetUINT32(
1386 &MF_MT_AUDIO_BLOCK_ALIGNMENT
,
1387 wfx
->Format
.nBlockAlign
1389 FAudio_assert(!FAILED(hr
) && "Failed set input block align!");
1391 case FAUDIO_FORMAT_WMAUDIO_LOSSLESS
:
1392 hr
= IMFMediaType_SetBlob(
1395 (void *)&wfx
->Samples
,
1398 FAudio_assert(!FAILED(hr
) && "Failed set codec private data!");
1399 hr
= IMFMediaType_SetGUID(
1402 &MFAudioFormat_WMAudio_Lossless
1404 FAudio_assert(!FAILED(hr
) && "Failed set media sub type!");
1405 hr
= IMFMediaType_SetUINT32(
1407 &MF_MT_AUDIO_BLOCK_ALIGNMENT
,
1408 wfx
->Format
.nBlockAlign
1410 FAudio_assert(!FAILED(hr
) && "Failed set input block align!");
1412 case FAUDIO_FORMAT_XMAUDIO2
:
1414 const FAudioXMA2WaveFormat
*xwf
= (const FAudioXMA2WaveFormat
*)wfx
;
1415 hr
= IMFMediaType_SetBlob(
1418 (void *)&wfx
->Samples
,
1421 FAudio_assert(!FAILED(hr
) && "Failed set codec private data!");
1422 hr
= IMFMediaType_SetGUID(
1425 &MFAudioFormat_XMAudio2
1427 FAudio_assert(!FAILED(hr
) && "Failed set media sub type!");
1428 hr
= IMFMediaType_SetUINT32(
1430 &MF_MT_AUDIO_BLOCK_ALIGNMENT
,
1431 xwf
->dwBytesPerBlock
1433 FAudio_assert(!FAILED(hr
) && "Failed set input block align!");
1437 FAudio_assert(0 && "Unsupported type!");
1441 hr
= IMFMediaType_SetUINT32(
1443 &MF_MT_AUDIO_BITS_PER_SAMPLE
,
1444 wfx
->Format
.wBitsPerSample
1446 FAudio_assert(!FAILED(hr
) && "Failed set input bits per sample!");
1447 hr
= IMFMediaType_SetUINT32(
1449 &MF_MT_AUDIO_AVG_BYTES_PER_SECOND
,
1450 wfx
->Format
.nAvgBytesPerSec
1452 FAudio_assert(!FAILED(hr
) && "Failed set input bytes per sample!");
1453 hr
= IMFMediaType_SetUINT32(
1455 &MF_MT_AUDIO_NUM_CHANNELS
,
1456 wfx
->Format
.nChannels
1458 FAudio_assert(!FAILED(hr
) && "Failed set input channel count!");
1459 hr
= IMFMediaType_SetUINT32(
1461 &MF_MT_AUDIO_SAMPLES_PER_SECOND
,
1462 wfx
->Format
.nSamplesPerSec
1464 FAudio_assert(!FAILED(hr
) && "Failed set input sample rate!");
1466 hr
= IMFTransform_SetInputType(
1472 FAudio_assert(!FAILED(hr
) && "Failed set decoder input type!");
1473 IMFMediaType_Release(media_type
);
1476 while (SUCCEEDED(hr
))
1478 hr
= IMFTransform_GetOutputAvailableType(
1484 FAudio_assert(!FAILED(hr
) && "Failed get output media type!");
1486 hr
= IMFMediaType_GetGUID(
1491 FAudio_assert(!FAILED(hr
) && "Failed get media major type!");
1492 if (!IsEqualGUID(&MFMediaType_Audio
, &guid
)) goto next
;
1494 hr
= IMFMediaType_GetGUID(
1499 FAudio_assert(!FAILED(hr
) && "Failed get media major type!");
1500 if (!IsEqualGUID(&MFAudioFormat_Float
, &guid
)) goto next
;
1502 hr
= IMFMediaType_GetUINT32(
1504 &MF_MT_AUDIO_BITS_PER_SAMPLE
,
1510 hr
= IMFMediaType_SetUINT32(
1512 &MF_MT_AUDIO_BITS_PER_SAMPLE
,
1516 FAudio_assert(!FAILED(hr
) && "Failed get bits per sample!");
1517 if (value
!= 32) goto next
;
1519 hr
= IMFMediaType_GetUINT32(
1521 &MF_MT_AUDIO_NUM_CHANNELS
,
1526 value
= wfx
->Format
.nChannels
;
1527 hr
= IMFMediaType_SetUINT32(
1529 &MF_MT_AUDIO_NUM_CHANNELS
,
1533 FAudio_assert(!FAILED(hr
) && "Failed get channel count!");
1534 if (value
!= wfx
->Format
.nChannels
) goto next
;
1536 hr
= IMFMediaType_GetUINT32(
1538 &MF_MT_AUDIO_SAMPLES_PER_SECOND
,
1543 value
= wfx
->Format
.nSamplesPerSec
;
1544 hr
= IMFMediaType_SetUINT32(
1546 &MF_MT_AUDIO_SAMPLES_PER_SECOND
,
1550 FAudio_assert(!FAILED(hr
) && "Failed get sample rate!");
1551 if (value
!= wfx
->Format
.nSamplesPerSec
) goto next
;
1553 hr
= IMFMediaType_GetUINT32(
1555 &MF_MT_AUDIO_BLOCK_ALIGNMENT
,
1560 value
= wfx
->Format
.nChannels
* sizeof(float);
1561 hr
= IMFMediaType_SetUINT32(
1563 &MF_MT_AUDIO_BLOCK_ALIGNMENT
,
1567 FAudio_assert(!FAILED(hr
) && "Failed get block align!");
1568 if (value
== wfx
->Format
.nChannels
* sizeof(float)) break;
1571 IMFMediaType_Release(media_type
);
1573 FAudio_assert(!FAILED(hr
) && "Failed to find output media type!");
1574 hr
= IMFTransform_SetOutputType(decoder
, 0, media_type
, 0);
1575 FAudio_assert(!FAILED(hr
) && "Failed set decoder output type!");
1576 IMFMediaType_Release(media_type
);
1578 hr
= IMFTransform_GetOutputStreamInfo(decoder
, 0, &info
);
1579 FAudio_assert(!FAILED(hr
) && "Failed to get output stream info!");
1581 impl
->decoder
= decoder
;
1582 if (!(info
.dwFlags
& MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES
))
1584 hr
= MFCreateSample(&impl
->output_sample
);
1585 FAudio_assert(!FAILED(hr
) && "Failed to create sample!");
1586 hr
= MFCreateMemoryBuffer(info
.cbSize
, &media_buffer
);
1587 FAudio_assert(!FAILED(hr
) && "Failed to create buffer!");
1588 hr
= IMFSample_AddBuffer(impl
->output_sample
, media_buffer
);
1589 FAudio_assert(!FAILED(hr
) && "Failed to buffer to sample!");
1590 IMFMediaBuffer_Release(media_buffer
);
1593 hr
= IMFTransform_ProcessMessage(
1595 MFT_MESSAGE_NOTIFY_BEGIN_STREAMING
,
1598 FAudio_assert(!FAILED(hr
) && "Failed to start decoder stream!");
1600 voice
->src
.wmadec
= impl
;
1601 voice
->src
.decode
= FAudio_INTERNAL_DecodeWMAMF
;
1603 LOG_FUNC_EXIT(voice
->audio
);
1607 void FAudio_WMADEC_free(FAudioSourceVoice
*voice
)
1609 struct FAudioWMADEC
*impl
= voice
->src
.wmadec
;
1612 LOG_FUNC_ENTER(voice
->audio
)
1613 FAudio_PlatformLockMutex(voice
->audio
->sourceLock
);
1614 LOG_MUTEX_LOCK(voice
->audio
, voice
->audio
->sourceLock
)
1616 if (impl
->input_size
)
1618 LOG_INFO(voice
->audio
, "sending EOS to %p", impl
->decoder
);
1619 hr
= IMFTransform_ProcessMessage(
1621 MFT_MESSAGE_NOTIFY_END_OF_STREAM
,
1624 FAudio_assert(!FAILED(hr
) && "Failed to send EOS!");
1625 impl
->input_size
= 0;
1627 if (impl
->output_pos
)
1629 LOG_INFO(voice
->audio
, "sending DRAIN to %p", impl
->decoder
);
1630 hr
= IMFTransform_ProcessMessage(
1632 MFT_MESSAGE_COMMAND_DRAIN
,
1635 FAudio_assert(!FAILED(hr
) && "Failed to send DRAIN!");
1636 impl
->output_pos
= 0;
1639 if (impl
->output_sample
) IMFSample_Release(impl
->output_sample
);
1640 IMFTransform_Release(impl
->decoder
);
1641 voice
->audio
->pFree(impl
->output_buf
);
1642 voice
->audio
->pFree(voice
->src
.wmadec
);
1643 voice
->src
.wmadec
= NULL
;
1644 voice
->src
.decode
= NULL
;
1646 FAudio_PlatformUnlockMutex(voice
->audio
->sourceLock
);
1647 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->audio
->sourceLock
)
1648 LOG_FUNC_EXIT(voice
->audio
)
1651 void FAudio_WMADEC_end_buffer(FAudioSourceVoice
*voice
)
1653 struct FAudioWMADEC
*impl
= voice
->src
.wmadec
;
1656 LOG_FUNC_ENTER(voice
->audio
)
1658 if (impl
->input_size
)
1660 LOG_INFO(voice
->audio
, "sending EOS to %p", impl
->decoder
);
1661 hr
= IMFTransform_ProcessMessage(
1663 MFT_MESSAGE_NOTIFY_END_OF_STREAM
,
1666 FAudio_assert(!FAILED(hr
) && "Failed to send EOS!");
1667 impl
->input_size
= 0;
1669 impl
->output_pos
= 0;
1670 impl
->input_pos
= 0;
1672 LOG_FUNC_EXIT(voice
->audio
)
1677 extern int this_tu_is_empty
;
1679 #endif /* FAUDIO_WIN32_PLATFORM */