faudio: Import upstream release 24.05.
[wine.git] / libs / faudio / src / FAudio_platform_win32.c
blob5de6536a39b8615b0309c52fc09567ac2820c094
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);
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);
217 #else
218 BOOL has_neon = FALSE;
219 #endif
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;
247 FAudio_memcpy(
248 &args->format.SubFormat,
249 &mixFormat->SubFormat,
250 sizeof(GUID)
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(
261 device_enumerator,
262 eRender,
263 eConsole,
264 &device
266 FAudio_assert(!FAILED(hr) && "Failed to get default audio endpoint!");
268 hr = IMMDevice_Activate(
269 device,
270 &IID_IAudioClient,
271 CLSCTX_ALL,
272 NULL,
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(
282 data->client,
283 AUDCLNT_SHAREMODE_SHARED,
284 &args->format.Format,
285 &closest
287 FAudio_assert(!FAILED(hr) && "Failed to find supported audio format!");
289 if (closest)
291 if (closest->wFormatTag != WAVE_FORMAT_EXTENSIBLE) args->format.Format = *closest;
292 else args->format = *(WAVEFORMATEXTENSIBLE *)closest;
293 CoTaskMemFree(closest);
296 hr = IAudioClient_Initialize(
297 data->client,
298 AUDCLNT_SHAREMODE_SHARED,
299 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
300 duration * 3,
302 &args->format.Format,
303 &GUID_NULL
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;
322 FAudio_memcpy(
323 &mixFormat->SubFormat,
324 &args->format.SubFormat,
325 sizeof(GUID)
328 else
330 mixFormat->Format.cbSize = sizeof(FAudioWaveFormatEx);
333 args->client = data->client;
334 args->events[0] = audioEvent;
335 args->events[1] = data->stopEvent;
336 args->audio = audio;
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;
345 return;
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);
355 if (kernelbase)
357 my_SetThreadDescription = NULL;
358 FreeLibrary(kernelbase);
359 kernelbase = NULL;
361 FAudio_PlatformRelease();
364 void FAudio_PlatformAddRef()
366 HRESULT hr;
367 EnterCriticalSection(&faudio_cs);
368 if (!device_enumerator)
370 init_hr = CoInitialize(NULL);
371 hr = CoCreateInstance(
372 &CLSID_MMDeviceEnumerator,
373 NULL,
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)
397 IMMDevice *device;
398 uint32_t count;
399 HRESULT hr;
401 FAudio_PlatformAddRef();
402 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(
403 device_enumerator,
404 eRender,
405 eConsole,
406 &device
409 if (hr == E_NOTFOUND) {
410 FAudio_PlatformRelease();
411 return 0;
414 FAudio_assert(!FAILED(hr) && "Failed to get default audio endpoint!");
416 IMMDevice_Release(device);
417 FAudio_PlatformRelease();
419 return 1;
422 uint32_t FAudio_PlatformGetDeviceDetails(
423 uint32_t index,
424 FAudioDeviceDetails *details
426 WAVEFORMATEX *format, *obtained;
427 WAVEFORMATEXTENSIBLE *ext;
428 IAudioClient *client;
429 IMMDevice *device;
430 uint32_t ret = 0;
431 HRESULT hr;
432 WCHAR *str;
433 GUID sub;
435 FAudio_memset(details, 0, sizeof(FAudioDeviceDetails));
436 if (index > 0) return FAUDIO_E_INVALID_CALL;
438 FAudio_PlatformAddRef();
440 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(
441 device_enumerator,
442 eRender,
443 eConsole,
444 &device
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);
455 CoTaskMemFree(str);
457 hr = IMMDevice_Activate(
458 device,
459 &IID_IAudioClient,
460 CLSCTX_ALL,
461 NULL,
462 (void **)&client
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;
473 FAudio_memcpy(
474 &ext->SubFormat,
475 &DATAFORMAT_SUBTYPE_PCM,
476 sizeof(GUID)
479 hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, format, &obtained);
480 if (FAILED(hr))
482 ext->SubFormat = sub;
484 else if (obtained)
486 CoTaskMemFree(format);
487 format = obtained;
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;
504 FAudio_memcpy(
505 &details->OutputFormat.SubFormat,
506 &ext->SubFormat,
507 sizeof(GUID)
510 else
512 details->OutputFormat.dwChannelMask = GetMask(format->nChannels);
515 CoTaskMemFree(format);
517 IAudioClient_Release(client);
519 IMMDevice_Release(device);
521 FAudio_PlatformRelease();
523 return ret;
526 FAudioMutex FAudio_PlatformCreateMutex(void)
528 CRITICAL_SECTION *cs;
530 cs = FAudio_malloc(sizeof(CRITICAL_SECTION));
531 if (!cs) return NULL;
533 InitializeCriticalSection(cs);
535 return 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);
551 FAudio_free(mutex);
554 struct FAudioThreadArgs
556 FAudioThreadFunc func;
557 const char *name;
558 void* data;
561 static DWORD WINAPI FaudioThreadWrapper(void *user)
563 struct FAudioThreadArgs *args = user;
564 DWORD ret;
566 FAudio_set_thread_name(args->name);
567 ret = args->func(args->data);
569 FAudio_free(args);
570 return ret;
573 FAudioThread FAudio_PlatformCreateThread(
574 FAudioThreadFunc func,
575 const char *name,
576 void* data
578 struct FAudioThreadArgs *args;
580 if (!(args = FAudio_malloc(sizeof(*args)))) return NULL;
581 args->func = func;
582 args->name = name;
583 args->data = data;
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)
596 /* FIXME */
599 uint64_t FAudio_PlatformGetThreadID(void)
601 return GetCurrentThreadId();
604 void FAudio_sleep(uint32_t ms)
606 Sleep(ms);
609 uint32_t FAudio_timems()
611 return GetTickCount();
614 /* FAudio I/O */
616 static size_t FAUDIOCALL FAudio_FILE_read(
617 void *data,
618 void *dst,
619 size_t size,
620 size_t count
622 if (!data) return 0;
623 return fread(dst, size, count, data);
626 static int64_t FAUDIOCALL FAudio_FILE_seek(
627 void *data,
628 int64_t offset,
629 int whence
631 if (!data) return -1;
632 fseek(data, offset, whence);
633 return ftell(data);
636 static int FAUDIOCALL FAudio_FILE_close(void *data)
638 if (!data) return 0;
639 fclose(data);
640 return 0;
643 FAudioIOStream* FAudio_fopen(const char *path)
645 FAudioIOStream *io;
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();
656 return io;
659 struct FAudio_mem
661 char *mem;
662 int64_t len;
663 int64_t pos;
666 static size_t FAUDIOCALL FAudio_mem_read(
667 void *data,
668 void *dst,
669 size_t size,
670 size_t count
672 struct FAudio_mem *io = data;
673 size_t len = size * count;
675 if (!data) return 0;
677 while (len && len > (io->len - io->pos)) len -= size;
678 FAudio_memcpy(dst, io->mem + io->pos, len);
679 io->pos += len;
681 return len;
684 static int64_t FAUDIOCALL FAudio_mem_seek(
685 void *data,
686 int64_t offset,
687 int whence
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;
705 else io->pos = 0;
708 return io->pos;
711 static int FAUDIOCALL FAudio_mem_close(void *data)
713 if (!data) return 0;
714 FAudio_free(data);
715 return 0;
718 FAudioIOStream* FAudio_memopen(void *mem, int len)
720 struct FAudio_mem *data;
721 FAudioIOStream *io;
723 io = (FAudioIOStream*) FAudio_malloc(sizeof(FAudioIOStream));
724 if (!io) return NULL;
726 data = FAudio_malloc(sizeof(struct FAudio_mem));
727 if (!data)
729 FAudio_free(io);
730 return NULL;
733 data->mem = mem;
734 data->len = len;
735 data->pos = 0;
737 io->data = data;
738 io->read = FAudio_mem_read;
739 io->seek = FAudio_mem_seek;
740 io->close = FAudio_mem_close;
741 io->lock = FAudio_PlatformCreateMutex();
742 return io;
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)
753 io->close(io->data);
754 FAudio_PlatformDestroyMutex((FAudioMutex) io->lock);
755 FAudio_free(io);
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;
777 FAudioBuffer buffer;
778 IMFSample *sample;
779 HRESULT hr;
780 DWORD flags, buffer_size = 0;
781 BYTE *buffer_ptr;
783 LOG_FUNC_ENTER(songAudio);
785 FAudio_memset(&buffer, 0, sizeof(buffer));
787 hr = IMFSourceReader_ReadSample(
788 activeSong,
789 MF_SOURCE_READER_FIRST_AUDIO_STREAM,
791 NULL,
792 &flags,
793 NULL,
794 &sample
796 FAudio_assert(!FAILED(hr) && "Failed to read audio sample!");
798 if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
800 buffer.Flags = FAUDIO_END_OF_STREAM;
802 else
804 hr = IMFSample_ConvertToContiguousBuffer(
805 sample,
806 &media_buffer
808 FAudio_assert(!FAILED(hr) && "Failed to get sample buffer!");
810 hr = IMFMediaBuffer_Lock(
811 media_buffer,
812 &buffer_ptr,
813 NULL,
814 &buffer_size
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);
833 if (buffer_size > 0)
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(
844 songVoice,
845 &buffer,
846 NULL
850 LOG_FUNC_EXIT(songAudio);
853 static void XNA_SongKill()
855 if (songVoice != NULL)
857 FAudioSourceVoice_Stop(songVoice, 0, 0);
858 FAudioVoice_DestroyVoice(songVoice);
859 songVoice = NULL;
861 if (activeSong)
863 IMFSourceReader_Release(activeSong);
864 activeSong = NULL;
866 FAudio_free(songBuffer);
867 songBuffer = NULL;
868 songBufferSize = 0;
871 /* "Public" API */
873 FAUDIOAPI void XNA_SongInit()
875 HRESULT hr;
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(
882 songAudio,
883 &songMaster,
884 FAUDIO_DEFAULT_CHANNELS,
885 FAUDIO_DEFAULT_SAMPLERATE,
888 NULL
892 FAUDIOAPI void XNA_SongQuit()
894 XNA_SongKill();
895 FAudioVoice_DestroyVoice(songMaster);
896 FAudio_Release(songAudio);
897 MFShutdown();
900 FAUDIOAPI float XNA_PlaySong(const char *name)
902 IMFAttributes *attributes = NULL;
903 IMFMediaType *media_type = NULL;
904 UINT32 channels, samplerate;
905 INT64 duration;
906 PROPVARIANT var;
907 HRESULT hr;
908 WCHAR filename_w[MAX_PATH];
910 LOG_FUNC_ENTER(songAudio);
911 LOG_INFO(songAudio, "name %s\n", name);
912 XNA_SongKill();
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(
919 filename_w,
920 attributes,
921 &activeSong
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(
929 media_type,
930 &MF_MT_MAJOR_TYPE,
931 &MFMediaType_Audio
933 FAudio_assert(!FAILED(hr) && "Failed to set major type!");
934 hr = IMFMediaType_SetGUID(
935 media_type,
936 &MF_MT_SUBTYPE,
937 &MFAudioFormat_Float
939 FAudio_assert(!FAILED(hr) && "Failed to set sub type!");
940 hr = IMFSourceReader_SetCurrentMediaType(
941 activeSong,
942 MF_SOURCE_READER_FIRST_AUDIO_STREAM,
943 NULL,
944 media_type
946 FAudio_assert(!FAILED(hr) && "Failed to set source media type!");
947 hr = IMFSourceReader_SetStreamSelection(
948 activeSong,
949 MF_SOURCE_READER_FIRST_AUDIO_STREAM,
950 TRUE
952 FAudio_assert(!FAILED(hr) && "Failed to select source stream!");
953 IMFMediaType_Release(media_type);
955 hr = IMFSourceReader_GetCurrentMediaType(
956 activeSong,
957 MF_SOURCE_READER_FIRST_AUDIO_STREAM,
958 &media_type
960 FAudio_assert(!FAILED(hr) && "Failed to get current media type!");
961 hr = IMFMediaType_GetUINT32(
962 media_type,
963 &MF_MT_AUDIO_NUM_CHANNELS,
964 &channels
966 FAudio_assert(!FAILED(hr) && "Failed to get channel count!");
967 hr = IMFMediaType_GetUINT32(
968 media_type,
969 &MF_MT_AUDIO_SAMPLES_PER_SECOND,
970 &samplerate
972 FAudio_assert(!FAILED(hr) && "Failed to get sample rate!");
973 IMFMediaType_Release(media_type);
975 hr = IMFSourceReader_GetPresentationAttribute(
976 activeSong,
977 MF_SOURCE_READER_MEDIASOURCE,
978 &MF_PD_DURATION,
979 &var
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;
994 /* Init voice */
995 FAudio_zero(&callbacks, sizeof(FAudioVoiceCallback));
996 callbacks.OnBufferEnd = XNA_SongSubmitBuffer;
997 FAudio_CreateSourceVoice(
998 songAudio,
999 &songVoice,
1000 &activeSongFormat,
1002 1.0f, /* No pitch shifting here! */
1003 &callbacks,
1004 NULL,
1005 NULL
1007 FAudioVoice_SetVolume(songVoice, songVolume, 0);
1008 XNA_SongSubmitBuffer(NULL, NULL);
1010 /* Finally. */
1011 FAudioSourceVoice_Start(songVoice, 0, 0);
1012 LOG_FUNC_EXIT(songAudio);
1013 return duration / 10000000.;
1016 FAUDIOAPI void XNA_PauseSong()
1018 if (songVoice == NULL)
1020 return;
1022 FAudioSourceVoice_Stop(songVoice, 0, 0);
1025 FAUDIOAPI void XNA_ResumeSong()
1027 if (songVoice == NULL)
1029 return;
1031 FAudioSourceVoice_Start(songVoice, 0, 0);
1034 FAUDIOAPI void XNA_StopSong()
1036 XNA_SongKill();
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)
1053 return 1;
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 */
1067 return 0;
1070 FAUDIOAPI void XNA_GetSongVisualizationData(
1071 float *frequencies,
1072 float *samples,
1073 uint32_t count
1075 /* TODO: Visualization FAPO that reads in Song samples, FFT analysis */
1078 /* FAudio WMADEC implementation over Win32 MF */
1080 struct FAudioWMADEC
1082 IMFTransform *decoder;
1083 IMFSample *output_sample;
1085 char *output_buf;
1086 size_t output_pos;
1087 size_t output_size;
1088 size_t input_pos;
1089 size_t input_size;
1092 static HRESULT FAudio_WMAMF_ProcessInput(
1093 FAudioVoice *voice,
1094 FAudioBuffer *buffer
1096 struct FAudioWMADEC *impl = voice->src.wmadec;
1097 IMFMediaBuffer *media_buffer;
1098 IMFSample *sample;
1099 DWORD copy_size;
1100 BYTE *copy_buf;
1101 HRESULT hr;
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(
1114 media_buffer,
1115 &copy_buf,
1116 NULL,
1117 &copy_size
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;
1131 if (FAILED(hr))
1133 LOG_ERROR(voice->audio, "IMFTransform_ProcessInput returned %#lx", hr);
1134 return hr;
1137 impl->input_pos += copy_size;
1138 return S_OK;
1141 static HRESULT FAudio_WMAMF_ProcessOutput(
1142 FAudioVoice *voice,
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;
1149 BYTE *copy_buf;
1150 HRESULT hr;
1152 while (1)
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;
1158 if (FAILED(hr))
1160 LOG_ERROR(voice->audio, "IMFTransform_ProcessInput returned %#lx", hr);
1161 return hr;
1164 if (output.dwStatus & MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE) continue;
1166 hr = IMFSample_ConvertToContiguousBuffer(
1167 output.pSample,
1168 &media_buffer
1170 FAudio_assert(!FAILED(hr) && "Failed to get sample buffer!");
1171 hr = IMFMediaBuffer_Lock(
1172 media_buffer,
1173 &copy_buf,
1174 NULL,
1175 &copy_size
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(
1185 impl->output_buf,
1186 impl->output_size
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);
1200 return S_OK;
1203 static void FAudio_INTERNAL_DecodeWMAMF(
1204 FAudioVoice *voice,
1205 FAudioBuffer *buffer,
1206 float *decodeCache,
1207 uint32_t samples
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;
1212 HRESULT hr;
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(
1225 impl->output_size,
1226 output_sizes[wma->PacketCount - 1]
1229 else
1231 const FAudioXMA2WaveFormat *xwf = (const FAudioXMA2WaveFormat *)wfx;
1233 impl->input_size = xwf->dwBytesPerBlock;
1234 impl->output_size = max(
1235 impl->output_size,
1236 (size_t) xwf->dwSamplesEncoded *
1237 voice->src.format->nChannels *
1238 (voice->src.format->wBitsPerSample / 8)
1242 impl->output_buf = voice->audio->pRealloc(
1243 impl->output_buf,
1244 impl->output_size
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(
1250 impl->decoder,
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(
1275 impl->decoder,
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);
1289 LOG_INFO(
1290 voice->audio,
1291 "decoded %Ix / %Ix bytes, copied %Ix / %Ix bytes",
1292 impl->output_pos,
1293 impl->output_size,
1294 copy_size,
1295 samples_size
1298 LOG_FUNC_EXIT(voice->audio)
1299 return;
1301 error:
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;
1316 HRESULT hr;
1317 UINT32 i, value;
1318 GUID guid;
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,
1329 &IID_IMFTransform,
1330 (void **)&decoder
1332 if (FAILED(hr))
1334 voice->audio->pFree(impl->output_buf);
1335 return -2;
1338 hr = MFCreateMediaType(&media_type);
1339 FAudio_assert(!FAILED(hr) && "Failed create media type!");
1340 hr = IMFMediaType_SetGUID(
1341 media_type,
1342 &MF_MT_MAJOR_TYPE,
1343 &MFMediaType_Audio
1345 FAudio_assert(!FAILED(hr) && "Failed set media major type!");
1347 switch (type)
1349 case FAUDIO_FORMAT_WMAUDIO2:
1350 hr = IMFMediaType_SetBlob(
1351 media_type,
1352 &MF_MT_USER_DATA,
1353 (void *)fake_codec_data,
1354 sizeof(fake_codec_data)
1356 FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
1357 hr = IMFMediaType_SetGUID(
1358 media_type,
1359 &MF_MT_SUBTYPE,
1360 &MFAudioFormat_WMAudioV8
1362 FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
1363 hr = IMFMediaType_SetUINT32(
1364 media_type,
1365 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1366 wfx->Format.nBlockAlign
1368 FAudio_assert(!FAILED(hr) && "Failed set input block align!");
1369 break;
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(
1378 media_type,
1379 &MF_MT_USER_DATA,
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(
1385 media_type,
1386 &MF_MT_SUBTYPE,
1387 &MFAudioFormat_WMAudioV9
1389 FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
1390 hr = IMFMediaType_SetUINT32(
1391 media_type,
1392 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1393 wfx->Format.nBlockAlign
1395 FAudio_assert(!FAILED(hr) && "Failed set input block align!");
1396 break;
1397 case FAUDIO_FORMAT_WMAUDIO_LOSSLESS:
1398 hr = IMFMediaType_SetBlob(
1399 media_type,
1400 &MF_MT_USER_DATA,
1401 (void *)&wfx->Samples,
1402 wfx->Format.cbSize
1404 FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
1405 hr = IMFMediaType_SetGUID(
1406 media_type,
1407 &MF_MT_SUBTYPE,
1408 &MFAudioFormat_WMAudio_Lossless
1410 FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
1411 hr = IMFMediaType_SetUINT32(
1412 media_type,
1413 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1414 wfx->Format.nBlockAlign
1416 FAudio_assert(!FAILED(hr) && "Failed set input block align!");
1417 break;
1418 case FAUDIO_FORMAT_XMAUDIO2:
1420 const FAudioXMA2WaveFormat *xwf = (const FAudioXMA2WaveFormat *)wfx;
1421 hr = IMFMediaType_SetBlob(
1422 media_type,
1423 &MF_MT_USER_DATA,
1424 (void *)&wfx->Samples,
1425 wfx->Format.cbSize
1427 FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
1428 hr = IMFMediaType_SetGUID(
1429 media_type,
1430 &MF_MT_SUBTYPE,
1431 &MFAudioFormat_XMAudio2
1433 FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
1434 hr = IMFMediaType_SetUINT32(
1435 media_type,
1436 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1437 xwf->dwBytesPerBlock
1439 FAudio_assert(!FAILED(hr) && "Failed set input block align!");
1440 break;
1442 default:
1443 FAudio_assert(0 && "Unsupported type!");
1444 break;
1447 hr = IMFMediaType_SetUINT32(
1448 media_type,
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(
1454 media_type,
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(
1460 media_type,
1461 &MF_MT_AUDIO_NUM_CHANNELS,
1462 wfx->Format.nChannels
1464 FAudio_assert(!FAILED(hr) && "Failed set input channel count!");
1465 hr = IMFMediaType_SetUINT32(
1466 media_type,
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(
1473 decoder,
1475 media_type,
1478 FAudio_assert(!FAILED(hr) && "Failed set decoder input type!");
1479 IMFMediaType_Release(media_type);
1481 i = 0;
1482 while (SUCCEEDED(hr))
1484 hr = IMFTransform_GetOutputAvailableType(
1485 decoder,
1487 i++,
1488 &media_type
1490 FAudio_assert(!FAILED(hr) && "Failed get output media type!");
1492 hr = IMFMediaType_GetGUID(
1493 media_type,
1494 &MF_MT_MAJOR_TYPE,
1495 &guid
1497 FAudio_assert(!FAILED(hr) && "Failed get media major type!");
1498 if (!IsEqualGUID(&MFMediaType_Audio, &guid)) goto next;
1500 hr = IMFMediaType_GetGUID(
1501 media_type,
1502 &MF_MT_SUBTYPE,
1503 &guid
1505 FAudio_assert(!FAILED(hr) && "Failed get media major type!");
1506 if (!IsEqualGUID(&MFAudioFormat_Float, &guid)) goto next;
1508 hr = IMFMediaType_GetUINT32(
1509 media_type,
1510 &MF_MT_AUDIO_BITS_PER_SAMPLE,
1511 &value
1513 if (FAILED(hr))
1515 value = 32;
1516 hr = IMFMediaType_SetUINT32(
1517 media_type,
1518 &MF_MT_AUDIO_BITS_PER_SAMPLE,
1519 value
1522 FAudio_assert(!FAILED(hr) && "Failed get bits per sample!");
1523 if (value != 32) goto next;
1525 hr = IMFMediaType_GetUINT32(
1526 media_type,
1527 &MF_MT_AUDIO_NUM_CHANNELS,
1528 &value
1530 if (FAILED(hr))
1532 value = wfx->Format.nChannels;
1533 hr = IMFMediaType_SetUINT32(
1534 media_type,
1535 &MF_MT_AUDIO_NUM_CHANNELS,
1536 value
1539 FAudio_assert(!FAILED(hr) && "Failed get channel count!");
1540 if (value != wfx->Format.nChannels) goto next;
1542 hr = IMFMediaType_GetUINT32(
1543 media_type,
1544 &MF_MT_AUDIO_SAMPLES_PER_SECOND,
1545 &value
1547 if (FAILED(hr))
1549 value = wfx->Format.nSamplesPerSec;
1550 hr = IMFMediaType_SetUINT32(
1551 media_type,
1552 &MF_MT_AUDIO_SAMPLES_PER_SECOND,
1553 value
1556 FAudio_assert(!FAILED(hr) && "Failed get sample rate!");
1557 if (value != wfx->Format.nSamplesPerSec) goto next;
1559 hr = IMFMediaType_GetUINT32(
1560 media_type,
1561 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1562 &value
1564 if (FAILED(hr))
1566 value = wfx->Format.nChannels * sizeof(float);
1567 hr = IMFMediaType_SetUINT32(
1568 media_type,
1569 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1570 value
1573 FAudio_assert(!FAILED(hr) && "Failed get block align!");
1574 if (value == wfx->Format.nChannels * sizeof(float)) break;
1576 next:
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(
1600 decoder,
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);
1610 return 0;
1613 void FAudio_WMADEC_free(FAudioSourceVoice *voice)
1615 struct FAudioWMADEC *impl = voice->src.wmadec;
1616 HRESULT hr;
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(
1626 impl->decoder,
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(
1637 impl->decoder,
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;
1660 HRESULT hr;
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(
1668 impl->decoder,
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)
1681 #else
1683 extern int this_tu_is_empty;
1685 #endif /* FAUDIO_WIN32_PLATFORM */