faudio: Import upstream release 24.02.
[wine.git] / libs / faudio / src / FAudio_platform_win32.c
blob01a3a84d5e0b1065915a6603eb73732457f3eb1e
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"
31 #include <stddef.h>
33 #define COBJMACROS
34 #include <windows.h>
35 #include <mfidl.h>
36 #include <mfapi.h>
37 #include <mferror.h>
38 #include <mfreadwrite.h>
39 #include <propvarutil.h>
41 #include <initguid.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
54 IAudioClient *client;
55 HANDLE audioThread;
56 HANDLE stopEvent;
59 struct FAudioAudioClientThreadArgs
61 WAVEFORMATEXTENSIBLE format;
62 IAudioClient *client;
63 HANDLE events[2];
64 FAudio *audio;
65 UINT updateSize;
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");
79 if (!kernelbase)
80 return;
82 my_SetThreadDescription = (HRESULT (WINAPI *)(HANDLE, PCWSTR)) GetProcAddress(kernelbase, "SetThreadDescription");
83 if (!my_SetThreadDescription)
85 FreeLibrary(kernelbase);
86 kernelbase = NULL;
90 static void FAudio_set_thread_name(char const *name)
92 int ret;
93 WCHAR *nameW;
95 if (!my_SetThreadDescription)
96 return;
98 ret = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0);
100 nameW = FAudio_malloc(ret * sizeof(WCHAR));
101 if (!nameW)
102 return;
104 ret = MultiByteToWideChar(CP_UTF8, 0, name, -1, nameW, ret);
105 if (ret)
106 my_SetThreadDescription(GetCurrentThread(), nameW);
108 FAudio_free(nameW);
111 static HRESULT FAudio_FillAudioClientBuffer(
112 struct FAudioAudioClientThreadArgs *args,
113 IAudioRenderClient *client,
114 UINT frames,
115 UINT padding
117 HRESULT hr = S_OK;
118 BYTE *buffer;
120 while (padding + args->updateSize <= frames)
122 hr = IAudioRenderClient_GetBuffer(
123 client,
124 frames - padding,
125 &buffer
127 if (FAILED(hr)) return hr;
129 FAudio_zero(
130 buffer,
131 args->updateSize * args->format.Format.nBlockAlign
134 if (args->audio->active)
136 FAudio_INTERNAL_UpdateEngine(
137 args->audio,
138 (float*) buffer
142 hr = IAudioRenderClient_ReleaseBuffer(
143 client,
144 args->updateSize,
147 if (FAILED(hr)) return hr;
149 padding += args->updateSize;
152 return hr;
155 static DWORD WINAPI FAudio_AudioClientThread(void *user)
157 struct FAudioAudioClientThreadArgs *args = user;
158 IAudioRenderClient *render_client;
159 HRESULT hr = S_OK;
160 UINT frames, padding = 0;
162 FAudio_set_thread_name(__func__);
164 hr = IAudioClient_GetService(
165 args->client,
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);
193 FAudio_free(args);
194 return 0;
197 void FAudio_PlatformInit(
198 FAudio *audio,
199 uint32_t flags,
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;
210 HRESULT hr;
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;
241 FAudio_memcpy(
242 &args->format.SubFormat,
243 &mixFormat->SubFormat,
244 sizeof(GUID)
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(
255 device_enumerator,
256 eRender,
257 eConsole,
258 &device
260 FAudio_assert(!FAILED(hr) && "Failed to get default audio endpoint!");
262 hr = IMMDevice_Activate(
263 device,
264 &IID_IAudioClient,
265 CLSCTX_ALL,
266 NULL,
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(
276 data->client,
277 AUDCLNT_SHAREMODE_SHARED,
278 &args->format.Format,
279 &closest
281 FAudio_assert(!FAILED(hr) && "Failed to find supported audio format!");
283 if (closest)
285 if (closest->wFormatTag != WAVE_FORMAT_EXTENSIBLE) args->format.Format = *closest;
286 else args->format = *(WAVEFORMATEXTENSIBLE *)closest;
287 CoTaskMemFree(closest);
290 hr = IAudioClient_Initialize(
291 data->client,
292 AUDCLNT_SHAREMODE_SHARED,
293 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
294 duration * 3,
296 &args->format.Format,
297 &GUID_NULL
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;
316 FAudio_memcpy(
317 &mixFormat->SubFormat,
318 &args->format.SubFormat,
319 sizeof(GUID)
322 else
324 mixFormat->Format.cbSize = sizeof(FAudioWaveFormatEx);
327 args->client = data->client;
328 args->events[0] = audioEvent;
329 args->events[1] = data->stopEvent;
330 args->audio = audio;
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;
339 return;
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);
349 if (kernelbase)
351 my_SetThreadDescription = NULL;
352 FreeLibrary(kernelbase);
353 kernelbase = NULL;
355 FAudio_PlatformRelease();
358 void FAudio_PlatformAddRef()
360 HRESULT hr;
361 EnterCriticalSection(&faudio_cs);
362 if (!device_enumerator)
364 init_hr = CoInitialize(NULL);
365 hr = CoCreateInstance(
366 &CLSID_MMDeviceEnumerator,
367 NULL,
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)
391 IMMDevice *device;
392 uint32_t count;
393 HRESULT hr;
395 FAudio_PlatformAddRef();
396 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(
397 device_enumerator,
398 eRender,
399 eConsole,
400 &device
403 if (hr == E_NOTFOUND) {
404 FAudio_PlatformRelease();
405 return 0;
408 FAudio_assert(!FAILED(hr) && "Failed to get default audio endpoint!");
410 IMMDevice_Release(device);
411 FAudio_PlatformRelease();
413 return 1;
416 uint32_t FAudio_PlatformGetDeviceDetails(
417 uint32_t index,
418 FAudioDeviceDetails *details
420 WAVEFORMATEX *format, *obtained;
421 WAVEFORMATEXTENSIBLE *ext;
422 IAudioClient *client;
423 IMMDevice *device;
424 uint32_t ret = 0;
425 HRESULT hr;
426 WCHAR *str;
427 GUID sub;
429 FAudio_memset(details, 0, sizeof(FAudioDeviceDetails));
430 if (index > 0) return FAUDIO_E_INVALID_CALL;
432 FAudio_PlatformAddRef();
434 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(
435 device_enumerator,
436 eRender,
437 eConsole,
438 &device
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);
449 CoTaskMemFree(str);
451 hr = IMMDevice_Activate(
452 device,
453 &IID_IAudioClient,
454 CLSCTX_ALL,
455 NULL,
456 (void **)&client
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;
467 FAudio_memcpy(
468 &ext->SubFormat,
469 &DATAFORMAT_SUBTYPE_PCM,
470 sizeof(GUID)
473 hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, format, &obtained);
474 if (FAILED(hr))
476 ext->SubFormat = sub;
478 else if (obtained)
480 CoTaskMemFree(format);
481 format = obtained;
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;
498 FAudio_memcpy(
499 &details->OutputFormat.SubFormat,
500 &ext->SubFormat,
501 sizeof(GUID)
504 else
506 details->OutputFormat.dwChannelMask = GetMask(format->nChannels);
509 CoTaskMemFree(format);
511 IAudioClient_Release(client);
513 IMMDevice_Release(device);
515 FAudio_PlatformRelease();
517 return ret;
520 FAudioMutex FAudio_PlatformCreateMutex(void)
522 CRITICAL_SECTION *cs;
524 cs = FAudio_malloc(sizeof(CRITICAL_SECTION));
525 if (!cs) return NULL;
527 InitializeCriticalSection(cs);
529 return 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);
545 FAudio_free(mutex);
548 struct FAudioThreadArgs
550 FAudioThreadFunc func;
551 const char *name;
552 void* data;
555 static DWORD WINAPI FaudioThreadWrapper(void *user)
557 struct FAudioThreadArgs *args = user;
558 DWORD ret;
560 FAudio_set_thread_name(args->name);
561 ret = args->func(args->data);
563 FAudio_free(args);
564 return ret;
567 FAudioThread FAudio_PlatformCreateThread(
568 FAudioThreadFunc func,
569 const char *name,
570 void* data
572 struct FAudioThreadArgs *args;
574 if (!(args = FAudio_malloc(sizeof(*args)))) return NULL;
575 args->func = func;
576 args->name = name;
577 args->data = data;
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)
590 /* FIXME */
593 uint64_t FAudio_PlatformGetThreadID(void)
595 return GetCurrentThreadId();
598 void FAudio_sleep(uint32_t ms)
600 Sleep(ms);
603 uint32_t FAudio_timems()
605 return GetTickCount();
608 /* FAudio I/O */
610 static size_t FAUDIOCALL FAudio_FILE_read(
611 void *data,
612 void *dst,
613 size_t size,
614 size_t count
616 if (!data) return 0;
617 return fread(dst, size, count, data);
620 static int64_t FAUDIOCALL FAudio_FILE_seek(
621 void *data,
622 int64_t offset,
623 int whence
625 if (!data) return -1;
626 fseek(data, offset, whence);
627 return ftell(data);
630 static int FAUDIOCALL FAudio_FILE_close(void *data)
632 if (!data) return 0;
633 fclose(data);
634 return 0;
637 FAudioIOStream* FAudio_fopen(const char *path)
639 FAudioIOStream *io;
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();
650 return io;
653 struct FAudio_mem
655 char *mem;
656 int64_t len;
657 int64_t pos;
660 static size_t FAUDIOCALL FAudio_mem_read(
661 void *data,
662 void *dst,
663 size_t size,
664 size_t count
666 struct FAudio_mem *io = data;
667 size_t len = size * count;
669 if (!data) return 0;
671 while (len && len > (io->len - io->pos)) len -= size;
672 FAudio_memcpy(dst, io->mem + io->pos, len);
673 io->pos += len;
675 return len;
678 static int64_t FAUDIOCALL FAudio_mem_seek(
679 void *data,
680 int64_t offset,
681 int whence
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;
699 else io->pos = 0;
702 return io->pos;
705 static int FAUDIOCALL FAudio_mem_close(void *data)
707 if (!data) return 0;
708 FAudio_free(data);
709 return 0;
712 FAudioIOStream* FAudio_memopen(void *mem, int len)
714 struct FAudio_mem *data;
715 FAudioIOStream *io;
717 io = (FAudioIOStream*) FAudio_malloc(sizeof(FAudioIOStream));
718 if (!io) return NULL;
720 data = FAudio_malloc(sizeof(struct FAudio_mem));
721 if (!data)
723 FAudio_free(io);
724 return NULL;
727 data->mem = mem;
728 data->len = len;
729 data->pos = 0;
731 io->data = data;
732 io->read = FAudio_mem_read;
733 io->seek = FAudio_mem_seek;
734 io->close = FAudio_mem_close;
735 io->lock = FAudio_PlatformCreateMutex();
736 return io;
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)
747 io->close(io->data);
748 FAudio_PlatformDestroyMutex((FAudioMutex) io->lock);
749 FAudio_free(io);
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;
771 FAudioBuffer buffer;
772 IMFSample *sample;
773 HRESULT hr;
774 DWORD flags, buffer_size = 0;
775 BYTE *buffer_ptr;
777 LOG_FUNC_ENTER(songAudio);
779 FAudio_memset(&buffer, 0, sizeof(buffer));
781 hr = IMFSourceReader_ReadSample(
782 activeSong,
783 MF_SOURCE_READER_FIRST_AUDIO_STREAM,
785 NULL,
786 &flags,
787 NULL,
788 &sample
790 FAudio_assert(!FAILED(hr) && "Failed to read audio sample!");
792 if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
794 buffer.Flags = FAUDIO_END_OF_STREAM;
796 else
798 hr = IMFSample_ConvertToContiguousBuffer(
799 sample,
800 &media_buffer
802 FAudio_assert(!FAILED(hr) && "Failed to get sample buffer!");
804 hr = IMFMediaBuffer_Lock(
805 media_buffer,
806 &buffer_ptr,
807 NULL,
808 &buffer_size
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);
827 if (buffer_size > 0)
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(
838 songVoice,
839 &buffer,
840 NULL
844 LOG_FUNC_EXIT(songAudio);
847 static void XNA_SongKill()
849 if (songVoice != NULL)
851 FAudioSourceVoice_Stop(songVoice, 0, 0);
852 FAudioVoice_DestroyVoice(songVoice);
853 songVoice = NULL;
855 if (activeSong)
857 IMFSourceReader_Release(activeSong);
858 activeSong = NULL;
860 FAudio_free(songBuffer);
861 songBuffer = NULL;
862 songBufferSize = 0;
865 /* "Public" API */
867 FAUDIOAPI void XNA_SongInit()
869 HRESULT hr;
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(
876 songAudio,
877 &songMaster,
878 FAUDIO_DEFAULT_CHANNELS,
879 FAUDIO_DEFAULT_SAMPLERATE,
882 NULL
886 FAUDIOAPI void XNA_SongQuit()
888 XNA_SongKill();
889 FAudioVoice_DestroyVoice(songMaster);
890 FAudio_Release(songAudio);
891 MFShutdown();
894 FAUDIOAPI float XNA_PlaySong(const char *name)
896 IMFAttributes *attributes = NULL;
897 IMFMediaType *media_type = NULL;
898 UINT32 channels, samplerate;
899 INT64 duration;
900 PROPVARIANT var;
901 HRESULT hr;
902 WCHAR filename_w[MAX_PATH];
904 LOG_FUNC_ENTER(songAudio);
905 LOG_INFO(songAudio, "name %s\n", name);
906 XNA_SongKill();
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(
913 filename_w,
914 attributes,
915 &activeSong
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(
923 media_type,
924 &MF_MT_MAJOR_TYPE,
925 &MFMediaType_Audio
927 FAudio_assert(!FAILED(hr) && "Failed to set major type!");
928 hr = IMFMediaType_SetGUID(
929 media_type,
930 &MF_MT_SUBTYPE,
931 &MFAudioFormat_Float
933 FAudio_assert(!FAILED(hr) && "Failed to set sub type!");
934 hr = IMFSourceReader_SetCurrentMediaType(
935 activeSong,
936 MF_SOURCE_READER_FIRST_AUDIO_STREAM,
937 NULL,
938 media_type
940 FAudio_assert(!FAILED(hr) && "Failed to set source media type!");
941 hr = IMFSourceReader_SetStreamSelection(
942 activeSong,
943 MF_SOURCE_READER_FIRST_AUDIO_STREAM,
944 TRUE
946 FAudio_assert(!FAILED(hr) && "Failed to select source stream!");
947 IMFMediaType_Release(media_type);
949 hr = IMFSourceReader_GetCurrentMediaType(
950 activeSong,
951 MF_SOURCE_READER_FIRST_AUDIO_STREAM,
952 &media_type
954 FAudio_assert(!FAILED(hr) && "Failed to get current media type!");
955 hr = IMFMediaType_GetUINT32(
956 media_type,
957 &MF_MT_AUDIO_NUM_CHANNELS,
958 &channels
960 FAudio_assert(!FAILED(hr) && "Failed to get channel count!");
961 hr = IMFMediaType_GetUINT32(
962 media_type,
963 &MF_MT_AUDIO_SAMPLES_PER_SECOND,
964 &samplerate
966 FAudio_assert(!FAILED(hr) && "Failed to get sample rate!");
967 IMFMediaType_Release(media_type);
969 hr = IMFSourceReader_GetPresentationAttribute(
970 activeSong,
971 MF_SOURCE_READER_MEDIASOURCE,
972 &MF_PD_DURATION,
973 &var
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;
988 /* Init voice */
989 FAudio_zero(&callbacks, sizeof(FAudioVoiceCallback));
990 callbacks.OnBufferEnd = XNA_SongSubmitBuffer;
991 FAudio_CreateSourceVoice(
992 songAudio,
993 &songVoice,
994 &activeSongFormat,
996 1.0f, /* No pitch shifting here! */
997 &callbacks,
998 NULL,
999 NULL
1001 FAudioVoice_SetVolume(songVoice, songVolume, 0);
1002 XNA_SongSubmitBuffer(NULL, NULL);
1004 /* Finally. */
1005 FAudioSourceVoice_Start(songVoice, 0, 0);
1006 LOG_FUNC_EXIT(songAudio);
1007 return duration / 10000000.;
1010 FAUDIOAPI void XNA_PauseSong()
1012 if (songVoice == NULL)
1014 return;
1016 FAudioSourceVoice_Stop(songVoice, 0, 0);
1019 FAUDIOAPI void XNA_ResumeSong()
1021 if (songVoice == NULL)
1023 return;
1025 FAudioSourceVoice_Start(songVoice, 0, 0);
1028 FAUDIOAPI void XNA_StopSong()
1030 XNA_SongKill();
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)
1047 return 1;
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 */
1061 return 0;
1064 FAUDIOAPI void XNA_GetSongVisualizationData(
1065 float *frequencies,
1066 float *samples,
1067 uint32_t count
1069 /* TODO: Visualization FAPO that reads in Song samples, FFT analysis */
1072 /* FAudio WMADEC implementation over Win32 MF */
1074 struct FAudioWMADEC
1076 IMFTransform *decoder;
1077 IMFSample *output_sample;
1079 char *output_buf;
1080 size_t output_pos;
1081 size_t output_size;
1082 size_t input_pos;
1083 size_t input_size;
1086 static HRESULT FAudio_WMAMF_ProcessInput(
1087 FAudioVoice *voice,
1088 FAudioBuffer *buffer
1090 struct FAudioWMADEC *impl = voice->src.wmadec;
1091 IMFMediaBuffer *media_buffer;
1092 IMFSample *sample;
1093 DWORD copy_size;
1094 BYTE *copy_buf;
1095 HRESULT hr;
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(
1108 media_buffer,
1109 &copy_buf,
1110 NULL,
1111 &copy_size
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;
1125 if (FAILED(hr))
1127 LOG_ERROR(voice->audio, "IMFTransform_ProcessInput returned %#lx", hr);
1128 return hr;
1131 impl->input_pos += copy_size;
1132 return S_OK;
1135 static HRESULT FAudio_WMAMF_ProcessOutput(
1136 FAudioVoice *voice,
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;
1143 BYTE *copy_buf;
1144 HRESULT hr;
1146 while (1)
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;
1152 if (FAILED(hr))
1154 LOG_ERROR(voice->audio, "IMFTransform_ProcessInput returned %#lx", hr);
1155 return hr;
1158 if (output.dwStatus & MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE) continue;
1160 hr = IMFSample_ConvertToContiguousBuffer(
1161 output.pSample,
1162 &media_buffer
1164 FAudio_assert(!FAILED(hr) && "Failed to get sample buffer!");
1165 hr = IMFMediaBuffer_Lock(
1166 media_buffer,
1167 &copy_buf,
1168 NULL,
1169 &copy_size
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(
1179 impl->output_buf,
1180 impl->output_size
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);
1194 return S_OK;
1197 static void FAudio_INTERNAL_DecodeWMAMF(
1198 FAudioVoice *voice,
1199 FAudioBuffer *buffer,
1200 float *decodeCache,
1201 uint32_t samples
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;
1206 HRESULT hr;
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(
1219 impl->output_size,
1220 output_sizes[wma->PacketCount - 1]
1223 else
1225 const FAudioXMA2WaveFormat *xwf = (const FAudioXMA2WaveFormat *)wfx;
1227 impl->input_size = xwf->dwBytesPerBlock;
1228 impl->output_size = max(
1229 impl->output_size,
1230 (size_t) xwf->dwSamplesEncoded *
1231 voice->src.format->nChannels *
1232 (voice->src.format->wBitsPerSample / 8)
1236 impl->output_buf = voice->audio->pRealloc(
1237 impl->output_buf,
1238 impl->output_size
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(
1244 impl->decoder,
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(
1269 impl->decoder,
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);
1283 LOG_INFO(
1284 voice->audio,
1285 "decoded %Ix / %Ix bytes, copied %Ix / %Ix bytes",
1286 impl->output_pos,
1287 impl->output_size,
1288 copy_size,
1289 samples_size
1292 LOG_FUNC_EXIT(voice->audio)
1293 return;
1295 error:
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;
1310 HRESULT hr;
1311 UINT32 i, value;
1312 GUID guid;
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,
1323 &IID_IMFTransform,
1324 (void **)&decoder
1326 if (FAILED(hr))
1328 voice->audio->pFree(impl->output_buf);
1329 return -2;
1332 hr = MFCreateMediaType(&media_type);
1333 FAudio_assert(!FAILED(hr) && "Failed create media type!");
1334 hr = IMFMediaType_SetGUID(
1335 media_type,
1336 &MF_MT_MAJOR_TYPE,
1337 &MFMediaType_Audio
1339 FAudio_assert(!FAILED(hr) && "Failed set media major type!");
1341 switch (type)
1343 case FAUDIO_FORMAT_WMAUDIO2:
1344 hr = IMFMediaType_SetBlob(
1345 media_type,
1346 &MF_MT_USER_DATA,
1347 (void *)fake_codec_data,
1348 sizeof(fake_codec_data)
1350 FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
1351 hr = IMFMediaType_SetGUID(
1352 media_type,
1353 &MF_MT_SUBTYPE,
1354 &MFAudioFormat_WMAudioV8
1356 FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
1357 hr = IMFMediaType_SetUINT32(
1358 media_type,
1359 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1360 wfx->Format.nBlockAlign
1362 FAudio_assert(!FAILED(hr) && "Failed set input block align!");
1363 break;
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(
1372 media_type,
1373 &MF_MT_USER_DATA,
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(
1379 media_type,
1380 &MF_MT_SUBTYPE,
1381 &MFAudioFormat_WMAudioV9
1383 FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
1384 hr = IMFMediaType_SetUINT32(
1385 media_type,
1386 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1387 wfx->Format.nBlockAlign
1389 FAudio_assert(!FAILED(hr) && "Failed set input block align!");
1390 break;
1391 case FAUDIO_FORMAT_WMAUDIO_LOSSLESS:
1392 hr = IMFMediaType_SetBlob(
1393 media_type,
1394 &MF_MT_USER_DATA,
1395 (void *)&wfx->Samples,
1396 wfx->Format.cbSize
1398 FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
1399 hr = IMFMediaType_SetGUID(
1400 media_type,
1401 &MF_MT_SUBTYPE,
1402 &MFAudioFormat_WMAudio_Lossless
1404 FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
1405 hr = IMFMediaType_SetUINT32(
1406 media_type,
1407 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1408 wfx->Format.nBlockAlign
1410 FAudio_assert(!FAILED(hr) && "Failed set input block align!");
1411 break;
1412 case FAUDIO_FORMAT_XMAUDIO2:
1414 const FAudioXMA2WaveFormat *xwf = (const FAudioXMA2WaveFormat *)wfx;
1415 hr = IMFMediaType_SetBlob(
1416 media_type,
1417 &MF_MT_USER_DATA,
1418 (void *)&wfx->Samples,
1419 wfx->Format.cbSize
1421 FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
1422 hr = IMFMediaType_SetGUID(
1423 media_type,
1424 &MF_MT_SUBTYPE,
1425 &MFAudioFormat_XMAudio2
1427 FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
1428 hr = IMFMediaType_SetUINT32(
1429 media_type,
1430 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1431 xwf->dwBytesPerBlock
1433 FAudio_assert(!FAILED(hr) && "Failed set input block align!");
1434 break;
1436 default:
1437 FAudio_assert(0 && "Unsupported type!");
1438 break;
1441 hr = IMFMediaType_SetUINT32(
1442 media_type,
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(
1448 media_type,
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(
1454 media_type,
1455 &MF_MT_AUDIO_NUM_CHANNELS,
1456 wfx->Format.nChannels
1458 FAudio_assert(!FAILED(hr) && "Failed set input channel count!");
1459 hr = IMFMediaType_SetUINT32(
1460 media_type,
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(
1467 decoder,
1469 media_type,
1472 FAudio_assert(!FAILED(hr) && "Failed set decoder input type!");
1473 IMFMediaType_Release(media_type);
1475 i = 0;
1476 while (SUCCEEDED(hr))
1478 hr = IMFTransform_GetOutputAvailableType(
1479 decoder,
1481 i++,
1482 &media_type
1484 FAudio_assert(!FAILED(hr) && "Failed get output media type!");
1486 hr = IMFMediaType_GetGUID(
1487 media_type,
1488 &MF_MT_MAJOR_TYPE,
1489 &guid
1491 FAudio_assert(!FAILED(hr) && "Failed get media major type!");
1492 if (!IsEqualGUID(&MFMediaType_Audio, &guid)) goto next;
1494 hr = IMFMediaType_GetGUID(
1495 media_type,
1496 &MF_MT_SUBTYPE,
1497 &guid
1499 FAudio_assert(!FAILED(hr) && "Failed get media major type!");
1500 if (!IsEqualGUID(&MFAudioFormat_Float, &guid)) goto next;
1502 hr = IMFMediaType_GetUINT32(
1503 media_type,
1504 &MF_MT_AUDIO_BITS_PER_SAMPLE,
1505 &value
1507 if (FAILED(hr))
1509 value = 32;
1510 hr = IMFMediaType_SetUINT32(
1511 media_type,
1512 &MF_MT_AUDIO_BITS_PER_SAMPLE,
1513 value
1516 FAudio_assert(!FAILED(hr) && "Failed get bits per sample!");
1517 if (value != 32) goto next;
1519 hr = IMFMediaType_GetUINT32(
1520 media_type,
1521 &MF_MT_AUDIO_NUM_CHANNELS,
1522 &value
1524 if (FAILED(hr))
1526 value = wfx->Format.nChannels;
1527 hr = IMFMediaType_SetUINT32(
1528 media_type,
1529 &MF_MT_AUDIO_NUM_CHANNELS,
1530 value
1533 FAudio_assert(!FAILED(hr) && "Failed get channel count!");
1534 if (value != wfx->Format.nChannels) goto next;
1536 hr = IMFMediaType_GetUINT32(
1537 media_type,
1538 &MF_MT_AUDIO_SAMPLES_PER_SECOND,
1539 &value
1541 if (FAILED(hr))
1543 value = wfx->Format.nSamplesPerSec;
1544 hr = IMFMediaType_SetUINT32(
1545 media_type,
1546 &MF_MT_AUDIO_SAMPLES_PER_SECOND,
1547 value
1550 FAudio_assert(!FAILED(hr) && "Failed get sample rate!");
1551 if (value != wfx->Format.nSamplesPerSec) goto next;
1553 hr = IMFMediaType_GetUINT32(
1554 media_type,
1555 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1556 &value
1558 if (FAILED(hr))
1560 value = wfx->Format.nChannels * sizeof(float);
1561 hr = IMFMediaType_SetUINT32(
1562 media_type,
1563 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1564 value
1567 FAudio_assert(!FAILED(hr) && "Failed get block align!");
1568 if (value == wfx->Format.nChannels * sizeof(float)) break;
1570 next:
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(
1594 decoder,
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);
1604 return 0;
1607 void FAudio_WMADEC_free(FAudioSourceVoice *voice)
1609 struct FAudioWMADEC *impl = voice->src.wmadec;
1610 HRESULT hr;
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(
1620 impl->decoder,
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(
1631 impl->decoder,
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;
1654 HRESULT hr;
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(
1662 impl->decoder,
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)
1675 #else
1677 extern int this_tu_is_empty;
1679 #endif /* FAUDIO_WIN32_PLATFORM */