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(
402 FAudio_assert(!FAILED(hr
) && "Failed to get default audio endpoint!");
404 IMMDevice_Release(device
);
405 FAudio_PlatformRelease();
410 uint32_t FAudio_PlatformGetDeviceDetails(
412 FAudioDeviceDetails
*details
414 WAVEFORMATEX
*format
, *obtained
;
415 WAVEFORMATEXTENSIBLE
*ext
;
416 IAudioClient
*client
;
423 FAudio_memset(details
, 0, sizeof(FAudioDeviceDetails
));
424 if (index
> 0) return FAUDIO_E_INVALID_CALL
;
426 FAudio_PlatformAddRef();
428 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(
434 FAudio_assert(!FAILED(hr
) && "Failed to get default audio endpoint!");
436 details
->Role
= FAudioGlobalDefaultDevice
;
438 hr
= IMMDevice_GetId(device
, &str
);
439 FAudio_assert(!FAILED(hr
) && "Failed to get audio endpoint id!");
441 lstrcpynW(details
->DeviceID
, str
, ARRAYSIZE(details
->DeviceID
) - 1);
442 lstrcpynW(details
->DisplayName
, str
, ARRAYSIZE(details
->DisplayName
) - 1);
445 hr
= IMMDevice_Activate(
452 FAudio_assert(!FAILED(hr
) && "Failed to activate audio client!");
454 hr
= IAudioClient_GetMixFormat(client
, &format
);
455 FAudio_assert(!FAILED(hr
) && "Failed to get audio client mix format!");
457 if (format
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
459 ext
= (WAVEFORMATEXTENSIBLE
*)format
;
460 sub
= ext
->SubFormat
;
463 &DATAFORMAT_SUBTYPE_PCM
,
467 hr
= IAudioClient_IsFormatSupported(client
, AUDCLNT_SHAREMODE_SHARED
, format
, &obtained
);
470 ext
->SubFormat
= sub
;
474 CoTaskMemFree(format
);
479 details
->OutputFormat
.Format
.wFormatTag
= format
->wFormatTag
;
480 details
->OutputFormat
.Format
.nChannels
= format
->nChannels
;
481 details
->OutputFormat
.Format
.nSamplesPerSec
= format
->nSamplesPerSec
;
482 details
->OutputFormat
.Format
.nAvgBytesPerSec
= format
->nAvgBytesPerSec
;
483 details
->OutputFormat
.Format
.nBlockAlign
= format
->nBlockAlign
;
484 details
->OutputFormat
.Format
.wBitsPerSample
= format
->wBitsPerSample
;
485 details
->OutputFormat
.Format
.cbSize
= format
->cbSize
;
487 if (format
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
489 ext
= (WAVEFORMATEXTENSIBLE
*)format
;
490 details
->OutputFormat
.Samples
.wValidBitsPerSample
= ext
->Samples
.wValidBitsPerSample
;
491 details
->OutputFormat
.dwChannelMask
= ext
->dwChannelMask
;
493 &details
->OutputFormat
.SubFormat
,
499 CoTaskMemFree(format
);
501 IAudioClient_Release(client
);
503 IMMDevice_Release(device
);
505 FAudio_PlatformRelease();
510 FAudioMutex
FAudio_PlatformCreateMutex(void)
512 CRITICAL_SECTION
*cs
;
514 cs
= FAudio_malloc(sizeof(CRITICAL_SECTION
));
515 if (!cs
) return NULL
;
517 InitializeCriticalSection(cs
);
522 void FAudio_PlatformLockMutex(FAudioMutex mutex
)
524 if (mutex
) EnterCriticalSection(mutex
);
527 void FAudio_PlatformUnlockMutex(FAudioMutex mutex
)
529 if (mutex
) LeaveCriticalSection(mutex
);
532 void FAudio_PlatformDestroyMutex(FAudioMutex mutex
)
534 if (mutex
) DeleteCriticalSection(mutex
);
538 struct FAudioThreadArgs
540 FAudioThreadFunc func
;
545 static DWORD WINAPI
FaudioThreadWrapper(void *user
)
547 struct FAudioThreadArgs
*args
= user
;
550 FAudio_set_thread_name(args
->name
);
551 ret
= args
->func(args
->data
);
557 FAudioThread
FAudio_PlatformCreateThread(
558 FAudioThreadFunc func
,
562 struct FAudioThreadArgs
*args
;
564 if (!(args
= FAudio_malloc(sizeof(*args
)))) return NULL
;
569 return CreateThread(NULL
, 0, &FaudioThreadWrapper
, args
, 0, NULL
);
572 void FAudio_PlatformWaitThread(FAudioThread thread
, int32_t *retval
)
574 WaitForSingleObject(thread
, INFINITE
);
575 GetExitCodeThread(thread
, (DWORD
*)retval
);
578 void FAudio_PlatformThreadPriority(FAudioThreadPriority priority
)
583 uint64_t FAudio_PlatformGetThreadID(void)
585 return GetCurrentThreadId();
588 void FAudio_sleep(uint32_t ms
)
593 uint32_t FAudio_timems()
595 return GetTickCount();
600 static size_t FAUDIOCALL
FAudio_FILE_read(
607 return fread(dst
, size
, count
, data
);
610 static int64_t FAUDIOCALL
FAudio_FILE_seek(
615 if (!data
) return -1;
616 fseek(data
, offset
, whence
);
620 static int FAUDIOCALL
FAudio_FILE_close(void *data
)
627 FAudioIOStream
* FAudio_fopen(const char *path
)
631 io
= (FAudioIOStream
*) FAudio_malloc(sizeof(FAudioIOStream
));
632 if (!io
) return NULL
;
634 io
->data
= fopen(path
, "rb");
635 io
->read
= FAudio_FILE_read
;
636 io
->seek
= FAudio_FILE_seek
;
637 io
->close
= FAudio_FILE_close
;
638 io
->lock
= FAudio_PlatformCreateMutex();
650 static size_t FAUDIOCALL
FAudio_mem_read(
656 struct FAudio_mem
*io
= data
;
657 size_t len
= size
* count
;
661 while (len
&& len
> (io
->len
- io
->pos
)) len
-= size
;
662 FAudio_memcpy(dst
, io
->mem
+ io
->pos
, len
);
668 static int64_t FAUDIOCALL
FAudio_mem_seek(
673 struct FAudio_mem
*io
= data
;
674 if (!data
) return -1;
676 if (whence
== SEEK_SET
)
678 if (io
->len
> offset
) io
->pos
= offset
;
679 else io
->pos
= io
->len
;
681 if (whence
== SEEK_CUR
)
683 if (io
->len
> io
->pos
+ offset
) io
->pos
+= offset
;
684 else io
->pos
= io
->len
;
686 if (whence
== SEEK_END
)
688 if (io
->len
> offset
) io
->pos
= io
->len
- offset
;
695 static int FAUDIOCALL
FAudio_mem_close(void *data
)
702 FAudioIOStream
* FAudio_memopen(void *mem
, int len
)
704 struct FAudio_mem
*data
;
707 io
= (FAudioIOStream
*) FAudio_malloc(sizeof(FAudioIOStream
));
708 if (!io
) return NULL
;
710 data
= FAudio_malloc(sizeof(struct FAudio_mem
));
722 io
->read
= FAudio_mem_read
;
723 io
->seek
= FAudio_mem_seek
;
724 io
->close
= FAudio_mem_close
;
725 io
->lock
= FAudio_PlatformCreateMutex();
729 uint8_t* FAudio_memptr(FAudioIOStream
*io
, size_t offset
)
731 struct FAudio_mem
*memio
= io
->data
;
732 return memio
->mem
+ offset
;
735 void FAudio_close(FAudioIOStream
*io
)
738 FAudio_PlatformDestroyMutex((FAudioMutex
) io
->lock
);
742 /* XNA Song implementation over Win32 MF */
744 static FAudioWaveFormatEx activeSongFormat
;
745 IMFSourceReader
*activeSong
;
746 static uint8_t *songBuffer
;
747 static SIZE_T songBufferSize
;
749 static float songVolume
= 1.0f
;
750 static FAudio
*songAudio
= NULL
;
751 static FAudioMasteringVoice
*songMaster
= NULL
;
753 static FAudioSourceVoice
*songVoice
= NULL
;
754 static FAudioVoiceCallback callbacks
;
756 /* Internal Functions */
758 static void XNA_SongSubmitBuffer(FAudioVoiceCallback
*callback
, void *pBufferContext
)
760 IMFMediaBuffer
*media_buffer
;
764 DWORD flags
, buffer_size
= 0;
767 LOG_FUNC_ENTER(songAudio
);
769 FAudio_memset(&buffer
, 0, sizeof(buffer
));
771 hr
= IMFSourceReader_ReadSample(
773 MF_SOURCE_READER_FIRST_AUDIO_STREAM
,
780 FAudio_assert(!FAILED(hr
) && "Failed to read audio sample!");
782 if (flags
& MF_SOURCE_READERF_ENDOFSTREAM
)
784 buffer
.Flags
= FAUDIO_END_OF_STREAM
;
788 hr
= IMFSample_ConvertToContiguousBuffer(
792 FAudio_assert(!FAILED(hr
) && "Failed to get sample buffer!");
794 hr
= IMFMediaBuffer_Lock(
800 FAudio_assert(!FAILED(hr
) && "Failed to lock buffer bytes!");
802 if (songBufferSize
< buffer_size
)
804 songBufferSize
= buffer_size
;
805 songBuffer
= FAudio_realloc(songBuffer
, songBufferSize
);
806 FAudio_assert(songBuffer
!= NULL
&& "Failed to allocate song buffer!");
808 FAudio_memcpy(songBuffer
, buffer_ptr
, buffer_size
);
810 hr
= IMFMediaBuffer_Unlock(media_buffer
);
811 FAudio_assert(!FAILED(hr
) && "Failed to unlock buffer bytes!");
813 IMFMediaBuffer_Release(media_buffer
);
814 IMFSample_Release(sample
);
819 buffer
.AudioBytes
= buffer_size
;
820 buffer
.pAudioData
= songBuffer
;
821 buffer
.PlayBegin
= 0;
822 buffer
.PlayLength
= buffer_size
/ activeSongFormat
.nBlockAlign
;
823 buffer
.LoopBegin
= 0;
824 buffer
.LoopLength
= 0;
825 buffer
.LoopCount
= 0;
826 buffer
.pContext
= NULL
;
827 FAudioSourceVoice_SubmitSourceBuffer(
834 LOG_FUNC_EXIT(songAudio
);
837 static void XNA_SongKill()
839 if (songVoice
!= NULL
)
841 FAudioSourceVoice_Stop(songVoice
, 0, 0);
842 FAudioVoice_DestroyVoice(songVoice
);
847 IMFSourceReader_Release(activeSong
);
850 FAudio_free(songBuffer
);
857 FAUDIOAPI
void XNA_SongInit()
861 hr
= MFStartup(MF_VERSION
, MFSTARTUP_FULL
);
862 FAudio_assert(!FAILED(hr
) && "Failed to initialize Media Foundation!");
864 FAudioCreate(&songAudio
, 0, FAUDIO_DEFAULT_PROCESSOR
);
865 FAudio_CreateMasteringVoice(
868 FAUDIO_DEFAULT_CHANNELS
,
869 FAUDIO_DEFAULT_SAMPLERATE
,
876 FAUDIOAPI
void XNA_SongQuit()
879 FAudioVoice_DestroyVoice(songMaster
);
880 FAudio_Release(songAudio
);
884 FAUDIOAPI
float XNA_PlaySong(const char *name
)
886 IMFAttributes
*attributes
= NULL
;
887 IMFMediaType
*media_type
= NULL
;
888 UINT32 channels
, samplerate
;
892 WCHAR filename_w
[MAX_PATH
];
894 LOG_FUNC_ENTER(songAudio
);
895 LOG_INFO(songAudio
, "name %s\n", name
);
898 MultiByteToWideChar(CP_UTF8
, 0, name
, -1, filename_w
, MAX_PATH
);
900 hr
= MFCreateAttributes(&attributes
, 1);
901 FAudio_assert(!FAILED(hr
) && "Failed to create attributes!");
902 hr
= MFCreateSourceReaderFromURL(
907 FAudio_assert(!FAILED(hr
) && "Failed to create source reader!");
908 IMFAttributes_Release(attributes
);
910 hr
= MFCreateMediaType(&media_type
);
911 FAudio_assert(!FAILED(hr
) && "Failed to create media type!");
912 hr
= IMFMediaType_SetGUID(
917 FAudio_assert(!FAILED(hr
) && "Failed to set major type!");
918 hr
= IMFMediaType_SetGUID(
923 FAudio_assert(!FAILED(hr
) && "Failed to set sub type!");
924 hr
= IMFSourceReader_SetCurrentMediaType(
926 MF_SOURCE_READER_FIRST_AUDIO_STREAM
,
930 FAudio_assert(!FAILED(hr
) && "Failed to set source media type!");
931 hr
= IMFSourceReader_SetStreamSelection(
933 MF_SOURCE_READER_FIRST_AUDIO_STREAM
,
936 FAudio_assert(!FAILED(hr
) && "Failed to select source stream!");
937 IMFMediaType_Release(media_type
);
939 hr
= IMFSourceReader_GetCurrentMediaType(
941 MF_SOURCE_READER_FIRST_AUDIO_STREAM
,
944 FAudio_assert(!FAILED(hr
) && "Failed to get current media type!");
945 hr
= IMFMediaType_GetUINT32(
947 &MF_MT_AUDIO_NUM_CHANNELS
,
950 FAudio_assert(!FAILED(hr
) && "Failed to get channel count!");
951 hr
= IMFMediaType_GetUINT32(
953 &MF_MT_AUDIO_SAMPLES_PER_SECOND
,
956 FAudio_assert(!FAILED(hr
) && "Failed to get sample rate!");
957 IMFMediaType_Release(media_type
);
959 hr
= IMFSourceReader_GetPresentationAttribute(
961 MF_SOURCE_READER_MEDIASOURCE
,
965 FAudio_assert(!FAILED(hr
) && "Failed to get song duration!");
966 hr
= PropVariantToInt64(&var
, &duration
);
967 FAudio_assert(!FAILED(hr
) && "Failed to get song duration!");
968 PropVariantClear(&var
);
970 activeSongFormat
.wFormatTag
= FAUDIO_FORMAT_IEEE_FLOAT
;
971 activeSongFormat
.nChannels
= channels
;
972 activeSongFormat
.nSamplesPerSec
= samplerate
;
973 activeSongFormat
.wBitsPerSample
= sizeof(float) * 8;
974 activeSongFormat
.nBlockAlign
= activeSongFormat
.nChannels
* activeSongFormat
.wBitsPerSample
/ 8;
975 activeSongFormat
.nAvgBytesPerSec
= activeSongFormat
.nSamplesPerSec
* activeSongFormat
.nBlockAlign
;
976 activeSongFormat
.cbSize
= 0;
979 FAudio_zero(&callbacks
, sizeof(FAudioVoiceCallback
));
980 callbacks
.OnBufferEnd
= XNA_SongSubmitBuffer
;
981 FAudio_CreateSourceVoice(
986 1.0f
, /* No pitch shifting here! */
991 FAudioVoice_SetVolume(songVoice
, songVolume
, 0);
992 XNA_SongSubmitBuffer(NULL
, NULL
);
995 FAudioSourceVoice_Start(songVoice
, 0, 0);
996 LOG_FUNC_EXIT(songAudio
);
997 return duration
/ 10000000.;
1000 FAUDIOAPI
void XNA_PauseSong()
1002 if (songVoice
== NULL
)
1006 FAudioSourceVoice_Stop(songVoice
, 0, 0);
1009 FAUDIOAPI
void XNA_ResumeSong()
1011 if (songVoice
== NULL
)
1015 FAudioSourceVoice_Start(songVoice
, 0, 0);
1018 FAUDIOAPI
void XNA_StopSong()
1023 FAUDIOAPI
void XNA_SetSongVolume(float volume
)
1025 songVolume
= volume
;
1026 if (songVoice
!= NULL
)
1028 FAudioVoice_SetVolume(songVoice
, songVolume
, 0);
1032 FAUDIOAPI
uint32_t XNA_GetSongEnded()
1034 FAudioVoiceState state
;
1035 if (songVoice
== NULL
|| activeSong
== NULL
)
1039 FAudioSourceVoice_GetState(songVoice
, &state
, 0);
1040 return state
.BuffersQueued
== 0;
1043 FAUDIOAPI
void XNA_EnableVisualization(uint32_t enable
)
1045 /* TODO: Enable/Disable FAPO effect */
1048 FAUDIOAPI
uint32_t XNA_VisualizationEnabled()
1050 /* TODO: Query FAPO effect enabled */
1054 FAUDIOAPI
void XNA_GetSongVisualizationData(
1059 /* TODO: Visualization FAPO that reads in Song samples, FFT analysis */
1062 /* FAudio WMADEC implementation over Win32 MF */
1066 IMFTransform
*decoder
;
1067 IMFSample
*output_sample
;
1076 static HRESULT
FAudio_WMAMF_ProcessInput(
1078 FAudioBuffer
*buffer
1080 struct FAudioWMADEC
*impl
= voice
->src
.wmadec
;
1081 IMFMediaBuffer
*media_buffer
;
1087 copy_size
= min(buffer
->AudioBytes
- impl
->input_pos
, impl
->input_size
);
1088 if (!copy_size
) return S_FALSE
;
1089 LOG_INFO(voice
->audio
, "pushing %x bytes at %x", copy_size
, impl
->input_pos
);
1091 hr
= MFCreateSample(&sample
);
1092 FAudio_assert(!FAILED(hr
) && "Failed to create sample!");
1093 hr
= MFCreateMemoryBuffer(copy_size
, &media_buffer
);
1094 FAudio_assert(!FAILED(hr
) && "Failed to create buffer!");
1095 hr
= IMFMediaBuffer_SetCurrentLength(media_buffer
, copy_size
);
1096 FAudio_assert(!FAILED(hr
) && "Failed to set buffer length!");
1097 hr
= IMFMediaBuffer_Lock(
1103 FAudio_assert(!FAILED(hr
) && "Failed to lock buffer bytes!");
1104 FAudio_memcpy(copy_buf
, buffer
->pAudioData
+ impl
->input_pos
, copy_size
);
1105 hr
= IMFMediaBuffer_Unlock(media_buffer
);
1106 FAudio_assert(!FAILED(hr
) && "Failed to unlock buffer bytes!");
1108 hr
= IMFSample_AddBuffer(sample
, media_buffer
);
1109 FAudio_assert(!FAILED(hr
) && "Failed to buffer to sample!");
1110 IMFMediaBuffer_Release(media_buffer
);
1112 hr
= IMFTransform_ProcessInput(impl
->decoder
, 0, sample
, 0);
1113 IMFSample_Release(sample
);
1114 if (hr
== MF_E_NOTACCEPTING
) return S_OK
;
1117 LOG_ERROR(voice
->audio
, "IMFTransform_ProcessInput returned %#x", hr
);
1121 impl
->input_pos
+= copy_size
;
1125 static HRESULT
FAudio_WMAMF_ProcessOutput(
1127 FAudioBuffer
*buffer
1129 struct FAudioWMADEC
*impl
= voice
->src
.wmadec
;
1130 MFT_OUTPUT_DATA_BUFFER output
;
1131 IMFMediaBuffer
*media_buffer
;
1132 DWORD status
, copy_size
;
1138 FAudio_memset(&output
, 0, sizeof(output
));
1139 output
.pSample
= impl
->output_sample
;
1140 hr
= IMFTransform_ProcessOutput(impl
->decoder
, 0, 1, &output
, &status
);
1141 if (hr
== MF_E_TRANSFORM_NEED_MORE_INPUT
) return S_FALSE
;
1144 LOG_ERROR(voice
->audio
, "IMFTransform_ProcessInput returned %#x", hr
);
1148 if (output
.dwStatus
& MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE
) continue;
1150 hr
= IMFSample_ConvertToContiguousBuffer(
1154 FAudio_assert(!FAILED(hr
) && "Failed to get sample buffer!");
1155 hr
= IMFMediaBuffer_Lock(
1161 FAudio_assert(!FAILED(hr
) && "Failed to lock buffer bytes!");
1162 if (impl
->output_pos
+ copy_size
> impl
->output_size
)
1164 impl
->output_size
= max(
1165 impl
->output_pos
+ copy_size
,
1166 impl
->output_size
* 3 / 2
1168 impl
->output_buf
= voice
->audio
->pRealloc(
1172 FAudio_assert(impl
->output_buf
&& "Failed to resize output buffer!");
1174 FAudio_memcpy(impl
->output_buf
+ impl
->output_pos
, copy_buf
, copy_size
);
1175 impl
->output_pos
+= copy_size
;
1176 LOG_INFO(voice
->audio
, "pulled %x bytes at %x", copy_size
, impl
->output_pos
);
1177 hr
= IMFMediaBuffer_Unlock(media_buffer
);
1178 FAudio_assert(!FAILED(hr
) && "Failed to unlock buffer bytes!");
1180 IMFMediaBuffer_Release(media_buffer
);
1181 if (!impl
->output_sample
) IMFSample_Release(output
.pSample
);
1187 static void FAudio_INTERNAL_DecodeWMAMF(
1189 FAudioBuffer
*buffer
,
1193 const FAudioWaveFormatExtensible
*wfx
= (FAudioWaveFormatExtensible
*)voice
->src
.format
;
1194 struct FAudioWMADEC
*impl
= voice
->src
.wmadec
;
1195 size_t samples_pos
, samples_size
, copy_size
;
1198 LOG_FUNC_ENTER(voice
->audio
)
1200 if (!impl
->output_pos
)
1202 if (wfx
->Format
.wFormatTag
== FAUDIO_FORMAT_EXTENSIBLE
)
1204 const FAudioBufferWMA
*wma
= &voice
->src
.bufferList
->bufferWMA
;
1205 const UINT32
*output_sizes
= wma
->pDecodedPacketCumulativeBytes
;
1207 impl
->input_size
= wfx
->Format
.nBlockAlign
;
1208 impl
->output_size
= max(
1210 output_sizes
[wma
->PacketCount
- 1]
1215 const FAudioXMA2WaveFormat
*xwf
= (const FAudioXMA2WaveFormat
*)wfx
;
1217 impl
->input_size
= xwf
->dwBytesPerBlock
;
1218 impl
->output_size
= max(
1220 (size_t) xwf
->dwSamplesEncoded
*
1221 voice
->src
.format
->nChannels
*
1222 (voice
->src
.format
->wBitsPerSample
/ 8)
1226 impl
->output_buf
= voice
->audio
->pRealloc(
1230 FAudio_assert(impl
->output_buf
&& "Failed to allocate output buffer!");
1232 LOG_INFO(voice
->audio
, "sending BOS to %p", impl
->decoder
);
1233 hr
= IMFTransform_ProcessMessage(
1235 MFT_MESSAGE_NOTIFY_START_OF_STREAM
,
1238 FAudio_assert(!FAILED(hr
) && "Failed to notify decoder stream start!");
1239 FAudio_WMAMF_ProcessInput(voice
, buffer
);
1242 samples_pos
= voice
->src
.curBufferOffset
* voice
->src
.format
->nChannels
* sizeof(float);
1243 samples_size
= samples
* voice
->src
.format
->nChannels
* sizeof(float);
1245 while (impl
->output_pos
< samples_pos
+ samples_size
)
1247 hr
= FAudio_WMAMF_ProcessOutput(voice
, buffer
);
1248 if (FAILED(hr
)) goto error
;
1249 if (hr
== S_OK
) continue;
1251 hr
= FAudio_WMAMF_ProcessInput(voice
, buffer
);
1252 if (FAILED(hr
)) goto error
;
1253 if (hr
== S_OK
) continue;
1255 if (!impl
->input_size
) break;
1257 LOG_INFO(voice
->audio
, "sending EOS to %p", impl
->decoder
);
1258 hr
= IMFTransform_ProcessMessage(
1260 MFT_MESSAGE_NOTIFY_END_OF_STREAM
,
1263 FAudio_assert(!FAILED(hr
) && "Failed to send EOS!");
1264 impl
->input_size
= 0;
1267 copy_size
= FAudio_clamp(impl
->output_pos
- samples_pos
, 0, samples_size
);
1268 FAudio_memcpy(decodeCache
, impl
->output_buf
+ samples_pos
, copy_size
);
1271 "decoded %x / %x bytes, copied %x / %x bytes",
1278 LOG_FUNC_EXIT(voice
->audio
)
1282 FAudio_zero(decodeCache
, samples
* voice
->src
.format
->nChannels
* sizeof(float));
1283 LOG_FUNC_EXIT(voice
->audio
)
1286 uint32_t FAudio_WMADEC_init(FAudioSourceVoice
*voice
, uint32_t type
)
1288 static const uint8_t fake_codec_data
[16] = {0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1289 const FAudioWaveFormatExtensible
*wfx
= (FAudioWaveFormatExtensible
*)voice
->src
.format
;
1290 struct FAudioWMADEC
*impl
;
1291 MFT_OUTPUT_STREAM_INFO info
= {0};
1292 IMFMediaBuffer
*media_buffer
;
1293 IMFMediaType
*media_type
;
1294 IMFTransform
*decoder
;
1299 LOG_FUNC_ENTER(voice
->audio
)
1301 if (!(impl
= voice
->audio
->pMalloc(sizeof(*impl
)))) return -1;
1302 FAudio_memset(impl
, 0, sizeof(*impl
));
1304 hr
= CoCreateInstance(
1305 &CLSID_CWMADecMediaObject
,
1307 CLSCTX_INPROC_SERVER
,
1313 voice
->audio
->pFree(impl
->output_buf
);
1317 hr
= MFCreateMediaType(&media_type
);
1318 FAudio_assert(!FAILED(hr
) && "Failed create media type!");
1319 hr
= IMFMediaType_SetGUID(
1324 FAudio_assert(!FAILED(hr
) && "Failed set media major type!");
1328 case FAUDIO_FORMAT_WMAUDIO2
:
1329 hr
= IMFMediaType_SetBlob(
1332 (void *)fake_codec_data
,
1333 sizeof(fake_codec_data
)
1335 FAudio_assert(!FAILED(hr
) && "Failed set codec private data!");
1336 hr
= IMFMediaType_SetGUID(
1339 &MFAudioFormat_WMAudioV8
1341 FAudio_assert(!FAILED(hr
) && "Failed set media sub type!");
1342 hr
= IMFMediaType_SetUINT32(
1344 &MF_MT_AUDIO_BLOCK_ALIGNMENT
,
1345 wfx
->Format
.nBlockAlign
1347 FAudio_assert(!FAILED(hr
) && "Failed set input block align!");
1349 case FAUDIO_FORMAT_WMAUDIO3
:
1350 hr
= IMFMediaType_SetBlob(
1353 (void *)&wfx
->Samples
,
1356 FAudio_assert(!FAILED(hr
) && "Failed set codec private data!");
1357 hr
= IMFMediaType_SetGUID(
1360 &MFAudioFormat_WMAudioV9
1362 FAudio_assert(!FAILED(hr
) && "Failed set media sub type!");
1363 hr
= IMFMediaType_SetUINT32(
1365 &MF_MT_AUDIO_BLOCK_ALIGNMENT
,
1366 wfx
->Format
.nBlockAlign
1368 FAudio_assert(!FAILED(hr
) && "Failed set input block align!");
1370 case FAUDIO_FORMAT_WMAUDIO_LOSSLESS
:
1371 hr
= IMFMediaType_SetBlob(
1374 (void *)&wfx
->Samples
,
1377 FAudio_assert(!FAILED(hr
) && "Failed set codec private data!");
1378 hr
= IMFMediaType_SetGUID(
1381 &MFAudioFormat_WMAudio_Lossless
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_XMAUDIO2
:
1393 const FAudioXMA2WaveFormat
*xwf
= (const FAudioXMA2WaveFormat
*)wfx
;
1394 hr
= IMFMediaType_SetBlob(
1397 (void *)&wfx
->Samples
,
1400 FAudio_assert(!FAILED(hr
) && "Failed set codec private data!");
1401 hr
= IMFMediaType_SetGUID(
1404 &MFAudioFormat_XMAudio2
1406 FAudio_assert(!FAILED(hr
) && "Failed set media sub type!");
1407 hr
= IMFMediaType_SetUINT32(
1409 &MF_MT_AUDIO_BLOCK_ALIGNMENT
,
1410 xwf
->dwBytesPerBlock
1412 FAudio_assert(!FAILED(hr
) && "Failed set input block align!");
1416 FAudio_assert(0 && "Unsupported type!");
1420 hr
= IMFMediaType_SetUINT32(
1422 &MF_MT_AUDIO_BITS_PER_SAMPLE
,
1423 wfx
->Format
.wBitsPerSample
1425 FAudio_assert(!FAILED(hr
) && "Failed set input bits per sample!");
1426 hr
= IMFMediaType_SetUINT32(
1428 &MF_MT_AUDIO_AVG_BYTES_PER_SECOND
,
1429 wfx
->Format
.nAvgBytesPerSec
1431 FAudio_assert(!FAILED(hr
) && "Failed set input bytes per sample!");
1432 hr
= IMFMediaType_SetUINT32(
1434 &MF_MT_AUDIO_NUM_CHANNELS
,
1435 wfx
->Format
.nChannels
1437 FAudio_assert(!FAILED(hr
) && "Failed set input channel count!");
1438 hr
= IMFMediaType_SetUINT32(
1440 &MF_MT_AUDIO_SAMPLES_PER_SECOND
,
1441 wfx
->Format
.nSamplesPerSec
1443 FAudio_assert(!FAILED(hr
) && "Failed set input sample rate!");
1445 hr
= IMFTransform_SetInputType(
1451 FAudio_assert(!FAILED(hr
) && "Failed set decoder input type!");
1452 IMFMediaType_Release(media_type
);
1455 while (SUCCEEDED(hr
))
1457 hr
= IMFTransform_GetOutputAvailableType(
1463 FAudio_assert(!FAILED(hr
) && "Failed get output media type!");
1465 hr
= IMFMediaType_GetGUID(
1470 FAudio_assert(!FAILED(hr
) && "Failed get media major type!");
1471 if (!IsEqualGUID(&MFMediaType_Audio
, &guid
)) goto next
;
1473 hr
= IMFMediaType_GetGUID(
1478 FAudio_assert(!FAILED(hr
) && "Failed get media major type!");
1479 if (!IsEqualGUID(&MFAudioFormat_Float
, &guid
)) goto next
;
1481 hr
= IMFMediaType_GetUINT32(
1483 &MF_MT_AUDIO_BITS_PER_SAMPLE
,
1489 hr
= IMFMediaType_SetUINT32(
1491 &MF_MT_AUDIO_BITS_PER_SAMPLE
,
1495 FAudio_assert(!FAILED(hr
) && "Failed get bits per sample!");
1496 if (value
!= 32) goto next
;
1498 hr
= IMFMediaType_GetUINT32(
1500 &MF_MT_AUDIO_NUM_CHANNELS
,
1505 value
= wfx
->Format
.nChannels
;
1506 hr
= IMFMediaType_SetUINT32(
1508 &MF_MT_AUDIO_NUM_CHANNELS
,
1512 FAudio_assert(!FAILED(hr
) && "Failed get channel count!");
1513 if (value
!= wfx
->Format
.nChannels
) goto next
;
1515 hr
= IMFMediaType_GetUINT32(
1517 &MF_MT_AUDIO_SAMPLES_PER_SECOND
,
1522 value
= wfx
->Format
.nSamplesPerSec
;
1523 hr
= IMFMediaType_SetUINT32(
1525 &MF_MT_AUDIO_SAMPLES_PER_SECOND
,
1529 FAudio_assert(!FAILED(hr
) && "Failed get sample rate!");
1530 if (value
!= wfx
->Format
.nSamplesPerSec
) goto next
;
1532 hr
= IMFMediaType_GetUINT32(
1534 &MF_MT_AUDIO_BLOCK_ALIGNMENT
,
1539 value
= wfx
->Format
.nChannels
* sizeof(float);
1540 hr
= IMFMediaType_SetUINT32(
1542 &MF_MT_AUDIO_BLOCK_ALIGNMENT
,
1546 FAudio_assert(!FAILED(hr
) && "Failed get block align!");
1547 if (value
== wfx
->Format
.nChannels
* sizeof(float)) break;
1550 IMFMediaType_Release(media_type
);
1552 FAudio_assert(!FAILED(hr
) && "Failed to find output media type!");
1553 hr
= IMFTransform_SetOutputType(decoder
, 0, media_type
, 0);
1554 FAudio_assert(!FAILED(hr
) && "Failed set decoder output type!");
1555 IMFMediaType_Release(media_type
);
1557 hr
= IMFTransform_GetOutputStreamInfo(decoder
, 0, &info
);
1558 FAudio_assert(!FAILED(hr
) && "Failed to get output stream info!");
1560 impl
->decoder
= decoder
;
1561 if (!(info
.dwFlags
& MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES
))
1563 hr
= MFCreateSample(&impl
->output_sample
);
1564 FAudio_assert(!FAILED(hr
) && "Failed to create sample!");
1565 hr
= MFCreateMemoryBuffer(info
.cbSize
, &media_buffer
);
1566 FAudio_assert(!FAILED(hr
) && "Failed to create buffer!");
1567 hr
= IMFSample_AddBuffer(impl
->output_sample
, media_buffer
);
1568 FAudio_assert(!FAILED(hr
) && "Failed to buffer to sample!");
1569 IMFMediaBuffer_Release(media_buffer
);
1572 hr
= IMFTransform_ProcessMessage(
1574 MFT_MESSAGE_NOTIFY_BEGIN_STREAMING
,
1577 FAudio_assert(!FAILED(hr
) && "Failed to start decoder stream!");
1579 voice
->src
.wmadec
= impl
;
1580 voice
->src
.decode
= FAudio_INTERNAL_DecodeWMAMF
;
1582 LOG_FUNC_EXIT(voice
->audio
);
1586 void FAudio_WMADEC_free(FAudioSourceVoice
*voice
)
1588 struct FAudioWMADEC
*impl
= voice
->src
.wmadec
;
1591 LOG_FUNC_ENTER(voice
->audio
)
1592 FAudio_PlatformLockMutex(voice
->audio
->sourceLock
);
1593 LOG_MUTEX_LOCK(voice
->audio
, voice
->audio
->sourceLock
)
1595 if (impl
->input_size
)
1597 LOG_INFO(voice
->audio
, "sending EOS to %p", impl
->decoder
);
1598 hr
= IMFTransform_ProcessMessage(
1600 MFT_MESSAGE_NOTIFY_END_OF_STREAM
,
1603 FAudio_assert(!FAILED(hr
) && "Failed to send EOS!");
1604 impl
->input_size
= 0;
1606 if (impl
->output_pos
)
1608 LOG_INFO(voice
->audio
, "sending DRAIN to %p", impl
->decoder
);
1609 hr
= IMFTransform_ProcessMessage(
1611 MFT_MESSAGE_COMMAND_DRAIN
,
1614 FAudio_assert(!FAILED(hr
) && "Failed to send DRAIN!");
1615 impl
->output_pos
= 0;
1618 if (impl
->output_sample
) IMFSample_Release(impl
->output_sample
);
1619 IMFTransform_Release(impl
->decoder
);
1620 voice
->audio
->pFree(impl
->output_buf
);
1621 voice
->audio
->pFree(voice
->src
.wmadec
);
1622 voice
->src
.wmadec
= NULL
;
1623 voice
->src
.decode
= NULL
;
1625 FAudio_PlatformUnlockMutex(voice
->audio
->sourceLock
);
1626 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->audio
->sourceLock
)
1627 LOG_FUNC_EXIT(voice
->audio
)
1630 void FAudio_WMADEC_end_buffer(FAudioSourceVoice
*voice
)
1632 struct FAudioWMADEC
*impl
= voice
->src
.wmadec
;
1635 LOG_FUNC_ENTER(voice
->audio
)
1637 if (impl
->input_size
)
1639 LOG_INFO(voice
->audio
, "sending EOS to %p", impl
->decoder
);
1640 hr
= IMFTransform_ProcessMessage(
1642 MFT_MESSAGE_NOTIFY_END_OF_STREAM
,
1645 FAudio_assert(!FAILED(hr
) && "Failed to send EOS!");
1646 impl
->input_size
= 0;
1648 impl
->output_pos
= 0;
1649 impl
->input_pos
= 0;
1651 LOG_FUNC_EXIT(voice
->audio
)
1656 extern int this_tu_is_empty
;
1658 #endif /* FAUDIO_WIN32_PLATFORM */