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
);
213 #if defined(__aarch64__) || defined(_M_ARM64) || defined(__arm64ec__) || defined(_M_ARM64EC)
214 BOOL has_neon
= TRUE
;
215 #elif defined(__arm__) || defined(_M_ARM)
216 BOOL has_neon
= IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE
);
218 BOOL has_neon
= FALSE
;
220 FAudio_INTERNAL_InitSIMDFunctions(has_sse2
, has_neon
);
221 FAudio_resolve_SetThreadDescription();
223 FAudio_PlatformAddRef();
225 *platformDevice
= NULL
;
226 if (deviceIndex
> 0) return;
228 args
= FAudio_malloc(sizeof(*args
));
229 FAudio_assert(!!args
&& "Failed to allocate FAudio thread args!");
231 data
= FAudio_malloc(sizeof(*data
));
232 FAudio_assert(!!data
&& "Failed to allocate FAudio platform data!");
233 FAudio_zero(data
, sizeof(*data
));
235 args
->format
.Format
.wFormatTag
= mixFormat
->Format
.wFormatTag
;
236 args
->format
.Format
.nChannels
= mixFormat
->Format
.nChannels
;
237 args
->format
.Format
.nSamplesPerSec
= mixFormat
->Format
.nSamplesPerSec
;
238 args
->format
.Format
.nAvgBytesPerSec
= mixFormat
->Format
.nAvgBytesPerSec
;
239 args
->format
.Format
.nBlockAlign
= mixFormat
->Format
.nBlockAlign
;
240 args
->format
.Format
.wBitsPerSample
= mixFormat
->Format
.wBitsPerSample
;
241 args
->format
.Format
.cbSize
= mixFormat
->Format
.cbSize
;
243 if (args
->format
.Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
245 args
->format
.Samples
.wValidBitsPerSample
= mixFormat
->Samples
.wValidBitsPerSample
;
246 args
->format
.dwChannelMask
= mixFormat
->dwChannelMask
;
248 &args
->format
.SubFormat
,
249 &mixFormat
->SubFormat
,
254 audioEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
255 FAudio_assert(!!audioEvent
&& "Failed to create FAudio thread buffer event!");
257 data
->stopEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
258 FAudio_assert(!!data
->stopEvent
&& "Failed to create FAudio thread stop event!");
260 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(
266 FAudio_assert(!FAILED(hr
) && "Failed to get default audio endpoint!");
268 hr
= IMMDevice_Activate(
273 (void **)&data
->client
275 FAudio_assert(!FAILED(hr
) && "Failed to create audio client!");
276 IMMDevice_Release(device
);
278 if (flags
& FAUDIO_1024_QUANTUM
) duration
= 213333;
279 else duration
= 100000;
281 hr
= IAudioClient_IsFormatSupported(
283 AUDCLNT_SHAREMODE_SHARED
,
284 &args
->format
.Format
,
287 FAudio_assert(!FAILED(hr
) && "Failed to find supported audio format!");
291 if (closest
->wFormatTag
!= WAVE_FORMAT_EXTENSIBLE
) args
->format
.Format
= *closest
;
292 else args
->format
= *(WAVEFORMATEXTENSIBLE
*)closest
;
293 CoTaskMemFree(closest
);
296 hr
= IAudioClient_Initialize(
298 AUDCLNT_SHAREMODE_SHARED
,
299 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
302 &args
->format
.Format
,
305 FAudio_assert(!FAILED(hr
) && "Failed to initialize audio client!");
307 hr
= IAudioClient_SetEventHandle(data
->client
, audioEvent
);
308 FAudio_assert(!FAILED(hr
) && "Failed to set audio client event!");
310 mixFormat
->Format
.wFormatTag
= args
->format
.Format
.wFormatTag
;
311 mixFormat
->Format
.nChannels
= args
->format
.Format
.nChannels
;
312 mixFormat
->Format
.nSamplesPerSec
= args
->format
.Format
.nSamplesPerSec
;
313 mixFormat
->Format
.nAvgBytesPerSec
= args
->format
.Format
.nAvgBytesPerSec
;
314 mixFormat
->Format
.nBlockAlign
= args
->format
.Format
.nBlockAlign
;
315 mixFormat
->Format
.wBitsPerSample
= args
->format
.Format
.wBitsPerSample
;
317 if (args
->format
.Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
319 mixFormat
->Format
.cbSize
= sizeof(FAudioWaveFormatExtensible
) - sizeof(FAudioWaveFormatEx
);
320 mixFormat
->Samples
.wValidBitsPerSample
= args
->format
.Samples
.wValidBitsPerSample
;
321 mixFormat
->dwChannelMask
= args
->format
.dwChannelMask
;
323 &mixFormat
->SubFormat
,
324 &args
->format
.SubFormat
,
330 mixFormat
->Format
.cbSize
= sizeof(FAudioWaveFormatEx
);
333 args
->client
= data
->client
;
334 args
->events
[0] = audioEvent
;
335 args
->events
[1] = data
->stopEvent
;
337 if (flags
& FAUDIO_1024_QUANTUM
) args
->updateSize
= args
->format
.Format
.nSamplesPerSec
/ (1000.0 / (64.0 / 3.0));
338 else args
->updateSize
= args
->format
.Format
.nSamplesPerSec
/ 100;
340 data
->audioThread
= CreateThread(NULL
, 0, &FAudio_AudioClientThread
, args
, 0, NULL
);
341 FAudio_assert(!!data
->audioThread
&& "Failed to create audio client thread!");
343 *updateSize
= args
->updateSize
;
344 *platformDevice
= data
;
348 void FAudio_PlatformQuit(void* platformDevice
)
350 struct FAudioWin32PlatformData
*data
= platformDevice
;
352 SetEvent(data
->stopEvent
);
353 WaitForSingleObject(data
->audioThread
, INFINITE
);
354 if (data
->client
) IAudioClient_Release(data
->client
);
357 my_SetThreadDescription
= NULL
;
358 FreeLibrary(kernelbase
);
361 FAudio_PlatformRelease();
364 void FAudio_PlatformAddRef()
367 EnterCriticalSection(&faudio_cs
);
368 if (!device_enumerator
)
370 init_hr
= CoInitialize(NULL
);
371 hr
= CoCreateInstance(
372 &CLSID_MMDeviceEnumerator
,
374 CLSCTX_INPROC_SERVER
,
375 &IID_IMMDeviceEnumerator
,
376 (void**)&device_enumerator
378 FAudio_assert(!FAILED(hr
) && "CoCreateInstance failed!");
380 else IMMDeviceEnumerator_AddRef(device_enumerator
);
381 LeaveCriticalSection(&faudio_cs
);
384 void FAudio_PlatformRelease()
386 EnterCriticalSection(&faudio_cs
);
387 if (!IMMDeviceEnumerator_Release(device_enumerator
))
389 device_enumerator
= NULL
;
390 if (SUCCEEDED(init_hr
)) CoUninitialize();
392 LeaveCriticalSection(&faudio_cs
);
395 uint32_t FAudio_PlatformGetDeviceCount(void)
401 FAudio_PlatformAddRef();
402 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(
409 if (hr
== E_NOTFOUND
) {
410 FAudio_PlatformRelease();
414 FAudio_assert(!FAILED(hr
) && "Failed to get default audio endpoint!");
416 IMMDevice_Release(device
);
417 FAudio_PlatformRelease();
422 uint32_t FAudio_PlatformGetDeviceDetails(
424 FAudioDeviceDetails
*details
426 WAVEFORMATEX
*format
, *obtained
;
427 WAVEFORMATEXTENSIBLE
*ext
;
428 IAudioClient
*client
;
435 FAudio_memset(details
, 0, sizeof(FAudioDeviceDetails
));
436 if (index
> 0) return FAUDIO_E_INVALID_CALL
;
438 FAudio_PlatformAddRef();
440 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(
446 FAudio_assert(!FAILED(hr
) && "Failed to get default audio endpoint!");
448 details
->Role
= FAudioGlobalDefaultDevice
;
450 hr
= IMMDevice_GetId(device
, &str
);
451 FAudio_assert(!FAILED(hr
) && "Failed to get audio endpoint id!");
453 lstrcpynW((WCHAR
*)details
->DeviceID
, str
, ARRAYSIZE(details
->DeviceID
) - 1);
454 lstrcpynW((WCHAR
*)details
->DisplayName
, str
, ARRAYSIZE(details
->DisplayName
) - 1);
457 hr
= IMMDevice_Activate(
464 FAudio_assert(!FAILED(hr
) && "Failed to activate audio client!");
466 hr
= IAudioClient_GetMixFormat(client
, &format
);
467 FAudio_assert(!FAILED(hr
) && "Failed to get audio client mix format!");
469 if (format
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
471 ext
= (WAVEFORMATEXTENSIBLE
*)format
;
472 sub
= ext
->SubFormat
;
475 &DATAFORMAT_SUBTYPE_PCM
,
479 hr
= IAudioClient_IsFormatSupported(client
, AUDCLNT_SHAREMODE_SHARED
, format
, &obtained
);
482 ext
->SubFormat
= sub
;
486 CoTaskMemFree(format
);
491 details
->OutputFormat
.Format
.wFormatTag
= format
->wFormatTag
;
492 details
->OutputFormat
.Format
.nChannels
= format
->nChannels
;
493 details
->OutputFormat
.Format
.nSamplesPerSec
= format
->nSamplesPerSec
;
494 details
->OutputFormat
.Format
.nAvgBytesPerSec
= format
->nAvgBytesPerSec
;
495 details
->OutputFormat
.Format
.nBlockAlign
= format
->nBlockAlign
;
496 details
->OutputFormat
.Format
.wBitsPerSample
= format
->wBitsPerSample
;
497 details
->OutputFormat
.Format
.cbSize
= format
->cbSize
;
499 if (format
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
501 ext
= (WAVEFORMATEXTENSIBLE
*)format
;
502 details
->OutputFormat
.Samples
.wValidBitsPerSample
= ext
->Samples
.wValidBitsPerSample
;
503 details
->OutputFormat
.dwChannelMask
= ext
->dwChannelMask
;
505 &details
->OutputFormat
.SubFormat
,
512 details
->OutputFormat
.dwChannelMask
= GetMask(format
->nChannels
);
515 CoTaskMemFree(format
);
517 IAudioClient_Release(client
);
519 IMMDevice_Release(device
);
521 FAudio_PlatformRelease();
526 FAudioMutex
FAudio_PlatformCreateMutex(void)
528 CRITICAL_SECTION
*cs
;
530 cs
= FAudio_malloc(sizeof(CRITICAL_SECTION
));
531 if (!cs
) return NULL
;
533 InitializeCriticalSection(cs
);
538 void FAudio_PlatformLockMutex(FAudioMutex mutex
)
540 if (mutex
) EnterCriticalSection(mutex
);
543 void FAudio_PlatformUnlockMutex(FAudioMutex mutex
)
545 if (mutex
) LeaveCriticalSection(mutex
);
548 void FAudio_PlatformDestroyMutex(FAudioMutex mutex
)
550 if (mutex
) DeleteCriticalSection(mutex
);
554 struct FAudioThreadArgs
556 FAudioThreadFunc func
;
561 static DWORD WINAPI
FaudioThreadWrapper(void *user
)
563 struct FAudioThreadArgs
*args
= user
;
566 FAudio_set_thread_name(args
->name
);
567 ret
= args
->func(args
->data
);
573 FAudioThread
FAudio_PlatformCreateThread(
574 FAudioThreadFunc func
,
578 struct FAudioThreadArgs
*args
;
580 if (!(args
= FAudio_malloc(sizeof(*args
)))) return NULL
;
585 return CreateThread(NULL
, 0, &FaudioThreadWrapper
, args
, 0, NULL
);
588 void FAudio_PlatformWaitThread(FAudioThread thread
, int32_t *retval
)
590 WaitForSingleObject(thread
, INFINITE
);
591 if (retval
!= NULL
) GetExitCodeThread(thread
, (DWORD
*)retval
);
594 void FAudio_PlatformThreadPriority(FAudioThreadPriority priority
)
599 uint64_t FAudio_PlatformGetThreadID(void)
601 return GetCurrentThreadId();
604 void FAudio_sleep(uint32_t ms
)
609 uint32_t FAudio_timems()
611 return GetTickCount();
616 static size_t FAUDIOCALL
FAudio_FILE_read(
623 return fread(dst
, size
, count
, data
);
626 static int64_t FAUDIOCALL
FAudio_FILE_seek(
631 if (!data
) return -1;
632 fseek(data
, offset
, whence
);
636 static int FAUDIOCALL
FAudio_FILE_close(void *data
)
643 FAudioIOStream
* FAudio_fopen(const char *path
)
647 io
= (FAudioIOStream
*) FAudio_malloc(sizeof(FAudioIOStream
));
648 if (!io
) return NULL
;
650 io
->data
= fopen(path
, "rb");
651 io
->read
= FAudio_FILE_read
;
652 io
->seek
= FAudio_FILE_seek
;
653 io
->close
= FAudio_FILE_close
;
654 io
->lock
= FAudio_PlatformCreateMutex();
666 static size_t FAUDIOCALL
FAudio_mem_read(
672 struct FAudio_mem
*io
= data
;
673 size_t len
= size
* count
;
677 while (len
&& len
> (io
->len
- io
->pos
)) len
-= size
;
678 FAudio_memcpy(dst
, io
->mem
+ io
->pos
, len
);
684 static int64_t FAUDIOCALL
FAudio_mem_seek(
689 struct FAudio_mem
*io
= data
;
690 if (!data
) return -1;
692 if (whence
== SEEK_SET
)
694 if (io
->len
> offset
) io
->pos
= offset
;
695 else io
->pos
= io
->len
;
697 if (whence
== SEEK_CUR
)
699 if (io
->len
> io
->pos
+ offset
) io
->pos
+= offset
;
700 else io
->pos
= io
->len
;
702 if (whence
== SEEK_END
)
704 if (io
->len
> offset
) io
->pos
= io
->len
- offset
;
711 static int FAUDIOCALL
FAudio_mem_close(void *data
)
718 FAudioIOStream
* FAudio_memopen(void *mem
, int len
)
720 struct FAudio_mem
*data
;
723 io
= (FAudioIOStream
*) FAudio_malloc(sizeof(FAudioIOStream
));
724 if (!io
) return NULL
;
726 data
= FAudio_malloc(sizeof(struct FAudio_mem
));
738 io
->read
= FAudio_mem_read
;
739 io
->seek
= FAudio_mem_seek
;
740 io
->close
= FAudio_mem_close
;
741 io
->lock
= FAudio_PlatformCreateMutex();
745 uint8_t* FAudio_memptr(FAudioIOStream
*io
, size_t offset
)
747 struct FAudio_mem
*memio
= io
->data
;
748 return (uint8_t *)memio
->mem
+ offset
;
751 void FAudio_close(FAudioIOStream
*io
)
754 FAudio_PlatformDestroyMutex((FAudioMutex
) io
->lock
);
758 /* XNA Song implementation over Win32 MF */
760 static FAudioWaveFormatEx activeSongFormat
;
761 IMFSourceReader
*activeSong
;
762 static uint8_t *songBuffer
;
763 static SIZE_T songBufferSize
;
765 static float songVolume
= 1.0f
;
766 static FAudio
*songAudio
= NULL
;
767 static FAudioMasteringVoice
*songMaster
= NULL
;
769 static FAudioSourceVoice
*songVoice
= NULL
;
770 static FAudioVoiceCallback callbacks
;
772 /* Internal Functions */
774 static void XNA_SongSubmitBuffer(FAudioVoiceCallback
*callback
, void *pBufferContext
)
776 IMFMediaBuffer
*media_buffer
;
780 DWORD flags
, buffer_size
= 0;
783 LOG_FUNC_ENTER(songAudio
);
785 FAudio_memset(&buffer
, 0, sizeof(buffer
));
787 hr
= IMFSourceReader_ReadSample(
789 MF_SOURCE_READER_FIRST_AUDIO_STREAM
,
796 FAudio_assert(!FAILED(hr
) && "Failed to read audio sample!");
798 if (flags
& MF_SOURCE_READERF_ENDOFSTREAM
)
800 buffer
.Flags
= FAUDIO_END_OF_STREAM
;
804 hr
= IMFSample_ConvertToContiguousBuffer(
808 FAudio_assert(!FAILED(hr
) && "Failed to get sample buffer!");
810 hr
= IMFMediaBuffer_Lock(
816 FAudio_assert(!FAILED(hr
) && "Failed to lock buffer bytes!");
818 if (songBufferSize
< buffer_size
)
820 songBufferSize
= buffer_size
;
821 songBuffer
= FAudio_realloc(songBuffer
, songBufferSize
);
822 FAudio_assert(songBuffer
!= NULL
&& "Failed to allocate song buffer!");
824 FAudio_memcpy(songBuffer
, buffer_ptr
, buffer_size
);
826 hr
= IMFMediaBuffer_Unlock(media_buffer
);
827 FAudio_assert(!FAILED(hr
) && "Failed to unlock buffer bytes!");
829 IMFMediaBuffer_Release(media_buffer
);
830 IMFSample_Release(sample
);
835 buffer
.AudioBytes
= buffer_size
;
836 buffer
.pAudioData
= songBuffer
;
837 buffer
.PlayBegin
= 0;
838 buffer
.PlayLength
= buffer_size
/ activeSongFormat
.nBlockAlign
;
839 buffer
.LoopBegin
= 0;
840 buffer
.LoopLength
= 0;
841 buffer
.LoopCount
= 0;
842 buffer
.pContext
= NULL
;
843 FAudioSourceVoice_SubmitSourceBuffer(
850 LOG_FUNC_EXIT(songAudio
);
853 static void XNA_SongKill()
855 if (songVoice
!= NULL
)
857 FAudioSourceVoice_Stop(songVoice
, 0, 0);
858 FAudioVoice_DestroyVoice(songVoice
);
863 IMFSourceReader_Release(activeSong
);
866 FAudio_free(songBuffer
);
873 FAUDIOAPI
void XNA_SongInit()
877 hr
= MFStartup(MF_VERSION
, MFSTARTUP_FULL
);
878 FAudio_assert(!FAILED(hr
) && "Failed to initialize Media Foundation!");
880 FAudioCreate(&songAudio
, 0, FAUDIO_DEFAULT_PROCESSOR
);
881 FAudio_CreateMasteringVoice(
884 FAUDIO_DEFAULT_CHANNELS
,
885 FAUDIO_DEFAULT_SAMPLERATE
,
892 FAUDIOAPI
void XNA_SongQuit()
895 FAudioVoice_DestroyVoice(songMaster
);
896 FAudio_Release(songAudio
);
900 FAUDIOAPI
float XNA_PlaySong(const char *name
)
902 IMFAttributes
*attributes
= NULL
;
903 IMFMediaType
*media_type
= NULL
;
904 UINT32 channels
, samplerate
;
908 WCHAR filename_w
[MAX_PATH
];
910 LOG_FUNC_ENTER(songAudio
);
911 LOG_INFO(songAudio
, "name %s\n", name
);
914 MultiByteToWideChar(CP_UTF8
, 0, name
, -1, filename_w
, MAX_PATH
);
916 hr
= MFCreateAttributes(&attributes
, 1);
917 FAudio_assert(!FAILED(hr
) && "Failed to create attributes!");
918 hr
= MFCreateSourceReaderFromURL(
923 FAudio_assert(!FAILED(hr
) && "Failed to create source reader!");
924 IMFAttributes_Release(attributes
);
926 hr
= MFCreateMediaType(&media_type
);
927 FAudio_assert(!FAILED(hr
) && "Failed to create media type!");
928 hr
= IMFMediaType_SetGUID(
933 FAudio_assert(!FAILED(hr
) && "Failed to set major type!");
934 hr
= IMFMediaType_SetGUID(
939 FAudio_assert(!FAILED(hr
) && "Failed to set sub type!");
940 hr
= IMFSourceReader_SetCurrentMediaType(
942 MF_SOURCE_READER_FIRST_AUDIO_STREAM
,
946 FAudio_assert(!FAILED(hr
) && "Failed to set source media type!");
947 hr
= IMFSourceReader_SetStreamSelection(
949 MF_SOURCE_READER_FIRST_AUDIO_STREAM
,
952 FAudio_assert(!FAILED(hr
) && "Failed to select source stream!");
953 IMFMediaType_Release(media_type
);
955 hr
= IMFSourceReader_GetCurrentMediaType(
957 MF_SOURCE_READER_FIRST_AUDIO_STREAM
,
960 FAudio_assert(!FAILED(hr
) && "Failed to get current media type!");
961 hr
= IMFMediaType_GetUINT32(
963 &MF_MT_AUDIO_NUM_CHANNELS
,
966 FAudio_assert(!FAILED(hr
) && "Failed to get channel count!");
967 hr
= IMFMediaType_GetUINT32(
969 &MF_MT_AUDIO_SAMPLES_PER_SECOND
,
972 FAudio_assert(!FAILED(hr
) && "Failed to get sample rate!");
973 IMFMediaType_Release(media_type
);
975 hr
= IMFSourceReader_GetPresentationAttribute(
977 MF_SOURCE_READER_MEDIASOURCE
,
981 FAudio_assert(!FAILED(hr
) && "Failed to get song duration!");
982 hr
= PropVariantToInt64(&var
, &duration
);
983 FAudio_assert(!FAILED(hr
) && "Failed to get song duration!");
984 PropVariantClear(&var
);
986 activeSongFormat
.wFormatTag
= FAUDIO_FORMAT_IEEE_FLOAT
;
987 activeSongFormat
.nChannels
= channels
;
988 activeSongFormat
.nSamplesPerSec
= samplerate
;
989 activeSongFormat
.wBitsPerSample
= sizeof(float) * 8;
990 activeSongFormat
.nBlockAlign
= activeSongFormat
.nChannels
* activeSongFormat
.wBitsPerSample
/ 8;
991 activeSongFormat
.nAvgBytesPerSec
= activeSongFormat
.nSamplesPerSec
* activeSongFormat
.nBlockAlign
;
992 activeSongFormat
.cbSize
= 0;
995 FAudio_zero(&callbacks
, sizeof(FAudioVoiceCallback
));
996 callbacks
.OnBufferEnd
= XNA_SongSubmitBuffer
;
997 FAudio_CreateSourceVoice(
1002 1.0f
, /* No pitch shifting here! */
1007 FAudioVoice_SetVolume(songVoice
, songVolume
, 0);
1008 XNA_SongSubmitBuffer(NULL
, NULL
);
1011 FAudioSourceVoice_Start(songVoice
, 0, 0);
1012 LOG_FUNC_EXIT(songAudio
);
1013 return duration
/ 10000000.;
1016 FAUDIOAPI
void XNA_PauseSong()
1018 if (songVoice
== NULL
)
1022 FAudioSourceVoice_Stop(songVoice
, 0, 0);
1025 FAUDIOAPI
void XNA_ResumeSong()
1027 if (songVoice
== NULL
)
1031 FAudioSourceVoice_Start(songVoice
, 0, 0);
1034 FAUDIOAPI
void XNA_StopSong()
1039 FAUDIOAPI
void XNA_SetSongVolume(float volume
)
1041 songVolume
= volume
;
1042 if (songVoice
!= NULL
)
1044 FAudioVoice_SetVolume(songVoice
, songVolume
, 0);
1048 FAUDIOAPI
uint32_t XNA_GetSongEnded()
1050 FAudioVoiceState state
;
1051 if (songVoice
== NULL
|| activeSong
== NULL
)
1055 FAudioSourceVoice_GetState(songVoice
, &state
, 0);
1056 return state
.BuffersQueued
== 0;
1059 FAUDIOAPI
void XNA_EnableVisualization(uint32_t enable
)
1061 /* TODO: Enable/Disable FAPO effect */
1064 FAUDIOAPI
uint32_t XNA_VisualizationEnabled()
1066 /* TODO: Query FAPO effect enabled */
1070 FAUDIOAPI
void XNA_GetSongVisualizationData(
1075 /* TODO: Visualization FAPO that reads in Song samples, FFT analysis */
1078 /* FAudio WMADEC implementation over Win32 MF */
1082 IMFTransform
*decoder
;
1083 IMFSample
*output_sample
;
1092 static HRESULT
FAudio_WMAMF_ProcessInput(
1094 FAudioBuffer
*buffer
1096 struct FAudioWMADEC
*impl
= voice
->src
.wmadec
;
1097 IMFMediaBuffer
*media_buffer
;
1103 copy_size
= min(buffer
->AudioBytes
- impl
->input_pos
, impl
->input_size
);
1104 if (!copy_size
) return S_FALSE
;
1105 LOG_INFO(voice
->audio
, "pushing %lx bytes at %Ix", copy_size
, impl
->input_pos
);
1107 hr
= MFCreateSample(&sample
);
1108 FAudio_assert(!FAILED(hr
) && "Failed to create sample!");
1109 hr
= MFCreateMemoryBuffer(copy_size
, &media_buffer
);
1110 FAudio_assert(!FAILED(hr
) && "Failed to create buffer!");
1111 hr
= IMFMediaBuffer_SetCurrentLength(media_buffer
, copy_size
);
1112 FAudio_assert(!FAILED(hr
) && "Failed to set buffer length!");
1113 hr
= IMFMediaBuffer_Lock(
1119 FAudio_assert(!FAILED(hr
) && "Failed to lock buffer bytes!");
1120 FAudio_memcpy(copy_buf
, buffer
->pAudioData
+ impl
->input_pos
, copy_size
);
1121 hr
= IMFMediaBuffer_Unlock(media_buffer
);
1122 FAudio_assert(!FAILED(hr
) && "Failed to unlock buffer bytes!");
1124 hr
= IMFSample_AddBuffer(sample
, media_buffer
);
1125 FAudio_assert(!FAILED(hr
) && "Failed to buffer to sample!");
1126 IMFMediaBuffer_Release(media_buffer
);
1128 hr
= IMFTransform_ProcessInput(impl
->decoder
, 0, sample
, 0);
1129 IMFSample_Release(sample
);
1130 if (hr
== MF_E_NOTACCEPTING
) return S_OK
;
1133 LOG_ERROR(voice
->audio
, "IMFTransform_ProcessInput returned %#lx", hr
);
1137 impl
->input_pos
+= copy_size
;
1141 static HRESULT
FAudio_WMAMF_ProcessOutput(
1143 FAudioBuffer
*buffer
1145 struct FAudioWMADEC
*impl
= voice
->src
.wmadec
;
1146 MFT_OUTPUT_DATA_BUFFER output
;
1147 IMFMediaBuffer
*media_buffer
;
1148 DWORD status
, copy_size
;
1154 FAudio_memset(&output
, 0, sizeof(output
));
1155 output
.pSample
= impl
->output_sample
;
1156 hr
= IMFTransform_ProcessOutput(impl
->decoder
, 0, 1, &output
, &status
);
1157 if (hr
== MF_E_TRANSFORM_NEED_MORE_INPUT
) return S_FALSE
;
1160 LOG_ERROR(voice
->audio
, "IMFTransform_ProcessInput returned %#lx", hr
);
1164 if (output
.dwStatus
& MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE
) continue;
1166 hr
= IMFSample_ConvertToContiguousBuffer(
1170 FAudio_assert(!FAILED(hr
) && "Failed to get sample buffer!");
1171 hr
= IMFMediaBuffer_Lock(
1177 FAudio_assert(!FAILED(hr
) && "Failed to lock buffer bytes!");
1178 if (impl
->output_pos
+ copy_size
> impl
->output_size
)
1180 impl
->output_size
= max(
1181 impl
->output_pos
+ copy_size
,
1182 impl
->output_size
* 3 / 2
1184 impl
->output_buf
= voice
->audio
->pRealloc(
1188 FAudio_assert(impl
->output_buf
&& "Failed to resize output buffer!");
1190 FAudio_memcpy(impl
->output_buf
+ impl
->output_pos
, copy_buf
, copy_size
);
1191 impl
->output_pos
+= copy_size
;
1192 LOG_INFO(voice
->audio
, "pulled %lx bytes at %Ix", copy_size
, impl
->output_pos
);
1193 hr
= IMFMediaBuffer_Unlock(media_buffer
);
1194 FAudio_assert(!FAILED(hr
) && "Failed to unlock buffer bytes!");
1196 IMFMediaBuffer_Release(media_buffer
);
1197 if (!impl
->output_sample
) IMFSample_Release(output
.pSample
);
1203 static void FAudio_INTERNAL_DecodeWMAMF(
1205 FAudioBuffer
*buffer
,
1209 const FAudioWaveFormatExtensible
*wfx
= (FAudioWaveFormatExtensible
*)voice
->src
.format
;
1210 size_t samples_pos
, samples_size
, copy_size
= 0;
1211 struct FAudioWMADEC
*impl
= voice
->src
.wmadec
;
1214 LOG_FUNC_ENTER(voice
->audio
)
1216 if (!impl
->output_pos
)
1218 if (wfx
->Format
.wFormatTag
== FAUDIO_FORMAT_EXTENSIBLE
)
1220 const FAudioBufferWMA
*wma
= &voice
->src
.bufferList
->bufferWMA
;
1221 const UINT32
*output_sizes
= wma
->pDecodedPacketCumulativeBytes
;
1223 impl
->input_size
= wfx
->Format
.nBlockAlign
;
1224 impl
->output_size
= max(
1226 output_sizes
[wma
->PacketCount
- 1]
1231 const FAudioXMA2WaveFormat
*xwf
= (const FAudioXMA2WaveFormat
*)wfx
;
1233 impl
->input_size
= xwf
->dwBytesPerBlock
;
1234 impl
->output_size
= max(
1236 (size_t) xwf
->dwSamplesEncoded
*
1237 voice
->src
.format
->nChannels
*
1238 (voice
->src
.format
->wBitsPerSample
/ 8)
1242 impl
->output_buf
= voice
->audio
->pRealloc(
1246 FAudio_assert(impl
->output_buf
&& "Failed to allocate output buffer!");
1248 LOG_INFO(voice
->audio
, "sending BOS to %p", impl
->decoder
);
1249 hr
= IMFTransform_ProcessMessage(
1251 MFT_MESSAGE_NOTIFY_START_OF_STREAM
,
1254 FAudio_assert(!FAILED(hr
) && "Failed to notify decoder stream start!");
1255 FAudio_WMAMF_ProcessInput(voice
, buffer
);
1258 samples_pos
= voice
->src
.curBufferOffset
* voice
->src
.format
->nChannels
* sizeof(float);
1259 samples_size
= samples
* voice
->src
.format
->nChannels
* sizeof(float);
1261 while (impl
->output_pos
< samples_pos
+ samples_size
)
1263 hr
= FAudio_WMAMF_ProcessOutput(voice
, buffer
);
1264 if (FAILED(hr
)) goto error
;
1265 if (hr
== S_OK
) continue;
1267 hr
= FAudio_WMAMF_ProcessInput(voice
, buffer
);
1268 if (FAILED(hr
)) goto error
;
1269 if (hr
== S_OK
) continue;
1271 if (!impl
->input_size
) break;
1273 LOG_INFO(voice
->audio
, "sending EOS to %p", impl
->decoder
);
1274 hr
= IMFTransform_ProcessMessage(
1276 MFT_MESSAGE_NOTIFY_END_OF_STREAM
,
1279 FAudio_assert(!FAILED(hr
) && "Failed to send EOS!");
1280 impl
->input_size
= 0;
1283 if (impl
->output_pos
> samples_pos
)
1285 copy_size
= FAudio_min(impl
->output_pos
- samples_pos
, samples_size
);
1286 FAudio_memcpy(decodeCache
, impl
->output_buf
+ samples_pos
, copy_size
);
1288 FAudio_zero(decodeCache
+ copy_size
, samples_size
- copy_size
);
1291 "decoded %Ix / %Ix bytes, copied %Ix / %Ix bytes",
1298 LOG_FUNC_EXIT(voice
->audio
)
1302 FAudio_zero(decodeCache
, samples
* voice
->src
.format
->nChannels
* sizeof(float));
1303 LOG_FUNC_EXIT(voice
->audio
)
1306 uint32_t FAudio_WMADEC_init(FAudioSourceVoice
*voice
, uint32_t type
)
1308 static const uint8_t fake_codec_data
[16] = {0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1309 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};
1310 const FAudioWaveFormatExtensible
*wfx
= (FAudioWaveFormatExtensible
*)voice
->src
.format
;
1311 struct FAudioWMADEC
*impl
;
1312 MFT_OUTPUT_STREAM_INFO info
= {0};
1313 IMFMediaBuffer
*media_buffer
;
1314 IMFMediaType
*media_type
;
1315 IMFTransform
*decoder
;
1320 LOG_FUNC_ENTER(voice
->audio
)
1322 if (!(impl
= voice
->audio
->pMalloc(sizeof(*impl
)))) return -1;
1323 FAudio_memset(impl
, 0, sizeof(*impl
));
1325 hr
= CoCreateInstance(
1326 &CLSID_CWMADecMediaObject
,
1328 CLSCTX_INPROC_SERVER
,
1334 voice
->audio
->pFree(impl
->output_buf
);
1338 hr
= MFCreateMediaType(&media_type
);
1339 FAudio_assert(!FAILED(hr
) && "Failed create media type!");
1340 hr
= IMFMediaType_SetGUID(
1345 FAudio_assert(!FAILED(hr
) && "Failed set media major type!");
1349 case FAUDIO_FORMAT_WMAUDIO2
:
1350 hr
= IMFMediaType_SetBlob(
1353 (void *)fake_codec_data
,
1354 sizeof(fake_codec_data
)
1356 FAudio_assert(!FAILED(hr
) && "Failed set codec private data!");
1357 hr
= IMFMediaType_SetGUID(
1360 &MFAudioFormat_WMAudioV8
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_WMAUDIO3
:
1371 *(uint16_t *)fake_codec_data_wma3
= voice
->src
.format
->wBitsPerSample
;
1372 for (i
= 0; i
< voice
->src
.format
->nChannels
; i
++)
1374 fake_codec_data_wma3
[2] <<= 1;
1375 fake_codec_data_wma3
[2] |= 1;
1377 hr
= IMFMediaType_SetBlob(
1380 (void *)fake_codec_data_wma3
,
1381 sizeof(fake_codec_data_wma3
)
1383 FAudio_assert(!FAILED(hr
) && "Failed set codec private data!");
1384 hr
= IMFMediaType_SetGUID(
1387 &MFAudioFormat_WMAudioV9
1389 FAudio_assert(!FAILED(hr
) && "Failed set media sub type!");
1390 hr
= IMFMediaType_SetUINT32(
1392 &MF_MT_AUDIO_BLOCK_ALIGNMENT
,
1393 wfx
->Format
.nBlockAlign
1395 FAudio_assert(!FAILED(hr
) && "Failed set input block align!");
1397 case FAUDIO_FORMAT_WMAUDIO_LOSSLESS
:
1398 hr
= IMFMediaType_SetBlob(
1401 (void *)&wfx
->Samples
,
1404 FAudio_assert(!FAILED(hr
) && "Failed set codec private data!");
1405 hr
= IMFMediaType_SetGUID(
1408 &MFAudioFormat_WMAudio_Lossless
1410 FAudio_assert(!FAILED(hr
) && "Failed set media sub type!");
1411 hr
= IMFMediaType_SetUINT32(
1413 &MF_MT_AUDIO_BLOCK_ALIGNMENT
,
1414 wfx
->Format
.nBlockAlign
1416 FAudio_assert(!FAILED(hr
) && "Failed set input block align!");
1418 case FAUDIO_FORMAT_XMAUDIO2
:
1420 const FAudioXMA2WaveFormat
*xwf
= (const FAudioXMA2WaveFormat
*)wfx
;
1421 hr
= IMFMediaType_SetBlob(
1424 (void *)&wfx
->Samples
,
1427 FAudio_assert(!FAILED(hr
) && "Failed set codec private data!");
1428 hr
= IMFMediaType_SetGUID(
1431 &MFAudioFormat_XMAudio2
1433 FAudio_assert(!FAILED(hr
) && "Failed set media sub type!");
1434 hr
= IMFMediaType_SetUINT32(
1436 &MF_MT_AUDIO_BLOCK_ALIGNMENT
,
1437 xwf
->dwBytesPerBlock
1439 FAudio_assert(!FAILED(hr
) && "Failed set input block align!");
1443 FAudio_assert(0 && "Unsupported type!");
1447 hr
= IMFMediaType_SetUINT32(
1449 &MF_MT_AUDIO_BITS_PER_SAMPLE
,
1450 wfx
->Format
.wBitsPerSample
1452 FAudio_assert(!FAILED(hr
) && "Failed set input bits per sample!");
1453 hr
= IMFMediaType_SetUINT32(
1455 &MF_MT_AUDIO_AVG_BYTES_PER_SECOND
,
1456 wfx
->Format
.nAvgBytesPerSec
1458 FAudio_assert(!FAILED(hr
) && "Failed set input bytes per sample!");
1459 hr
= IMFMediaType_SetUINT32(
1461 &MF_MT_AUDIO_NUM_CHANNELS
,
1462 wfx
->Format
.nChannels
1464 FAudio_assert(!FAILED(hr
) && "Failed set input channel count!");
1465 hr
= IMFMediaType_SetUINT32(
1467 &MF_MT_AUDIO_SAMPLES_PER_SECOND
,
1468 wfx
->Format
.nSamplesPerSec
1470 FAudio_assert(!FAILED(hr
) && "Failed set input sample rate!");
1472 hr
= IMFTransform_SetInputType(
1478 FAudio_assert(!FAILED(hr
) && "Failed set decoder input type!");
1479 IMFMediaType_Release(media_type
);
1482 while (SUCCEEDED(hr
))
1484 hr
= IMFTransform_GetOutputAvailableType(
1490 FAudio_assert(!FAILED(hr
) && "Failed get output media type!");
1492 hr
= IMFMediaType_GetGUID(
1497 FAudio_assert(!FAILED(hr
) && "Failed get media major type!");
1498 if (!IsEqualGUID(&MFMediaType_Audio
, &guid
)) goto next
;
1500 hr
= IMFMediaType_GetGUID(
1505 FAudio_assert(!FAILED(hr
) && "Failed get media major type!");
1506 if (!IsEqualGUID(&MFAudioFormat_Float
, &guid
)) goto next
;
1508 hr
= IMFMediaType_GetUINT32(
1510 &MF_MT_AUDIO_BITS_PER_SAMPLE
,
1516 hr
= IMFMediaType_SetUINT32(
1518 &MF_MT_AUDIO_BITS_PER_SAMPLE
,
1522 FAudio_assert(!FAILED(hr
) && "Failed get bits per sample!");
1523 if (value
!= 32) goto next
;
1525 hr
= IMFMediaType_GetUINT32(
1527 &MF_MT_AUDIO_NUM_CHANNELS
,
1532 value
= wfx
->Format
.nChannels
;
1533 hr
= IMFMediaType_SetUINT32(
1535 &MF_MT_AUDIO_NUM_CHANNELS
,
1539 FAudio_assert(!FAILED(hr
) && "Failed get channel count!");
1540 if (value
!= wfx
->Format
.nChannels
) goto next
;
1542 hr
= IMFMediaType_GetUINT32(
1544 &MF_MT_AUDIO_SAMPLES_PER_SECOND
,
1549 value
= wfx
->Format
.nSamplesPerSec
;
1550 hr
= IMFMediaType_SetUINT32(
1552 &MF_MT_AUDIO_SAMPLES_PER_SECOND
,
1556 FAudio_assert(!FAILED(hr
) && "Failed get sample rate!");
1557 if (value
!= wfx
->Format
.nSamplesPerSec
) goto next
;
1559 hr
= IMFMediaType_GetUINT32(
1561 &MF_MT_AUDIO_BLOCK_ALIGNMENT
,
1566 value
= wfx
->Format
.nChannels
* sizeof(float);
1567 hr
= IMFMediaType_SetUINT32(
1569 &MF_MT_AUDIO_BLOCK_ALIGNMENT
,
1573 FAudio_assert(!FAILED(hr
) && "Failed get block align!");
1574 if (value
== wfx
->Format
.nChannels
* sizeof(float)) break;
1577 IMFMediaType_Release(media_type
);
1579 FAudio_assert(!FAILED(hr
) && "Failed to find output media type!");
1580 hr
= IMFTransform_SetOutputType(decoder
, 0, media_type
, 0);
1581 FAudio_assert(!FAILED(hr
) && "Failed set decoder output type!");
1582 IMFMediaType_Release(media_type
);
1584 hr
= IMFTransform_GetOutputStreamInfo(decoder
, 0, &info
);
1585 FAudio_assert(!FAILED(hr
) && "Failed to get output stream info!");
1587 impl
->decoder
= decoder
;
1588 if (!(info
.dwFlags
& MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES
))
1590 hr
= MFCreateSample(&impl
->output_sample
);
1591 FAudio_assert(!FAILED(hr
) && "Failed to create sample!");
1592 hr
= MFCreateMemoryBuffer(info
.cbSize
, &media_buffer
);
1593 FAudio_assert(!FAILED(hr
) && "Failed to create buffer!");
1594 hr
= IMFSample_AddBuffer(impl
->output_sample
, media_buffer
);
1595 FAudio_assert(!FAILED(hr
) && "Failed to buffer to sample!");
1596 IMFMediaBuffer_Release(media_buffer
);
1599 hr
= IMFTransform_ProcessMessage(
1601 MFT_MESSAGE_NOTIFY_BEGIN_STREAMING
,
1604 FAudio_assert(!FAILED(hr
) && "Failed to start decoder stream!");
1606 voice
->src
.wmadec
= impl
;
1607 voice
->src
.decode
= FAudio_INTERNAL_DecodeWMAMF
;
1609 LOG_FUNC_EXIT(voice
->audio
);
1613 void FAudio_WMADEC_free(FAudioSourceVoice
*voice
)
1615 struct FAudioWMADEC
*impl
= voice
->src
.wmadec
;
1618 LOG_FUNC_ENTER(voice
->audio
)
1619 FAudio_PlatformLockMutex(voice
->audio
->sourceLock
);
1620 LOG_MUTEX_LOCK(voice
->audio
, voice
->audio
->sourceLock
)
1622 if (impl
->input_size
)
1624 LOG_INFO(voice
->audio
, "sending EOS to %p", impl
->decoder
);
1625 hr
= IMFTransform_ProcessMessage(
1627 MFT_MESSAGE_NOTIFY_END_OF_STREAM
,
1630 FAudio_assert(!FAILED(hr
) && "Failed to send EOS!");
1631 impl
->input_size
= 0;
1633 if (impl
->output_pos
)
1635 LOG_INFO(voice
->audio
, "sending DRAIN to %p", impl
->decoder
);
1636 hr
= IMFTransform_ProcessMessage(
1638 MFT_MESSAGE_COMMAND_DRAIN
,
1641 FAudio_assert(!FAILED(hr
) && "Failed to send DRAIN!");
1642 impl
->output_pos
= 0;
1645 if (impl
->output_sample
) IMFSample_Release(impl
->output_sample
);
1646 IMFTransform_Release(impl
->decoder
);
1647 voice
->audio
->pFree(impl
->output_buf
);
1648 voice
->audio
->pFree(voice
->src
.wmadec
);
1649 voice
->src
.wmadec
= NULL
;
1650 voice
->src
.decode
= NULL
;
1652 FAudio_PlatformUnlockMutex(voice
->audio
->sourceLock
);
1653 LOG_MUTEX_UNLOCK(voice
->audio
, voice
->audio
->sourceLock
)
1654 LOG_FUNC_EXIT(voice
->audio
)
1657 void FAudio_WMADEC_end_buffer(FAudioSourceVoice
*voice
)
1659 struct FAudioWMADEC
*impl
= voice
->src
.wmadec
;
1662 LOG_FUNC_ENTER(voice
->audio
)
1664 if (impl
->input_size
)
1666 LOG_INFO(voice
->audio
, "sending EOS to %p", impl
->decoder
);
1667 hr
= IMFTransform_ProcessMessage(
1669 MFT_MESSAGE_NOTIFY_END_OF_STREAM
,
1672 FAudio_assert(!FAILED(hr
) && "Failed to send EOS!");
1673 impl
->input_size
= 0;
1675 impl
->output_pos
= 0;
1676 impl
->input_pos
= 0;
1678 LOG_FUNC_EXIT(voice
->audio
)
1683 extern int this_tu_is_empty
;
1685 #endif /* FAUDIO_WIN32_PLATFORM */