faudio: Import upstream release 22.01.
[wine.git] / libs / faudio / src / FAudio_platform_win32.c
blob89101a6cf7a460d6899e2bc78ea9ea1e938b367f
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
402 FAudio_assert(!FAILED(hr) && "Failed to get default audio endpoint!");
404 IMMDevice_Release(device);
405 FAudio_PlatformRelease();
407 return 1;
410 uint32_t FAudio_PlatformGetDeviceDetails(
411 uint32_t index,
412 FAudioDeviceDetails *details
414 WAVEFORMATEX *format, *obtained;
415 WAVEFORMATEXTENSIBLE *ext;
416 IAudioClient *client;
417 IMMDevice *device;
418 uint32_t ret = 0;
419 HRESULT hr;
420 WCHAR *str;
421 GUID sub;
423 FAudio_memset(details, 0, sizeof(FAudioDeviceDetails));
424 if (index > 0) return FAUDIO_E_INVALID_CALL;
426 FAudio_PlatformAddRef();
428 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(
429 device_enumerator,
430 eRender,
431 eConsole,
432 &device
434 FAudio_assert(!FAILED(hr) && "Failed to get default audio endpoint!");
436 details->Role = FAudioGlobalDefaultDevice;
438 hr = IMMDevice_GetId(device, &str);
439 FAudio_assert(!FAILED(hr) && "Failed to get audio endpoint id!");
441 lstrcpynW(details->DeviceID, str, ARRAYSIZE(details->DeviceID) - 1);
442 lstrcpynW(details->DisplayName, str, ARRAYSIZE(details->DisplayName) - 1);
443 CoTaskMemFree(str);
445 hr = IMMDevice_Activate(
446 device,
447 &IID_IAudioClient,
448 CLSCTX_ALL,
449 NULL,
450 (void **)&client
452 FAudio_assert(!FAILED(hr) && "Failed to activate audio client!");
454 hr = IAudioClient_GetMixFormat(client, &format);
455 FAudio_assert(!FAILED(hr) && "Failed to get audio client mix format!");
457 if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
459 ext = (WAVEFORMATEXTENSIBLE *)format;
460 sub = ext->SubFormat;
461 FAudio_memcpy(
462 &ext->SubFormat,
463 &DATAFORMAT_SUBTYPE_PCM,
464 sizeof(GUID)
467 hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, format, &obtained);
468 if (FAILED(hr))
470 ext->SubFormat = sub;
472 else if (obtained)
474 CoTaskMemFree(format);
475 format = obtained;
479 details->OutputFormat.Format.wFormatTag = format->wFormatTag;
480 details->OutputFormat.Format.nChannels = format->nChannels;
481 details->OutputFormat.Format.nSamplesPerSec = format->nSamplesPerSec;
482 details->OutputFormat.Format.nAvgBytesPerSec = format->nAvgBytesPerSec;
483 details->OutputFormat.Format.nBlockAlign = format->nBlockAlign;
484 details->OutputFormat.Format.wBitsPerSample = format->wBitsPerSample;
485 details->OutputFormat.Format.cbSize = format->cbSize;
487 if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
489 ext = (WAVEFORMATEXTENSIBLE *)format;
490 details->OutputFormat.Samples.wValidBitsPerSample = ext->Samples.wValidBitsPerSample;
491 details->OutputFormat.dwChannelMask = ext->dwChannelMask;
492 FAudio_memcpy(
493 &details->OutputFormat.SubFormat,
494 &ext->SubFormat,
495 sizeof(GUID)
499 CoTaskMemFree(format);
501 IAudioClient_Release(client);
503 IMMDevice_Release(device);
505 FAudio_PlatformRelease();
507 return ret;
510 FAudioMutex FAudio_PlatformCreateMutex(void)
512 CRITICAL_SECTION *cs;
514 cs = FAudio_malloc(sizeof(CRITICAL_SECTION));
515 if (!cs) return NULL;
517 InitializeCriticalSection(cs);
519 return cs;
522 void FAudio_PlatformLockMutex(FAudioMutex mutex)
524 if (mutex) EnterCriticalSection(mutex);
527 void FAudio_PlatformUnlockMutex(FAudioMutex mutex)
529 if (mutex) LeaveCriticalSection(mutex);
532 void FAudio_PlatformDestroyMutex(FAudioMutex mutex)
534 if (mutex) DeleteCriticalSection(mutex);
535 FAudio_free(mutex);
538 struct FAudioThreadArgs
540 FAudioThreadFunc func;
541 const char *name;
542 void* data;
545 static DWORD WINAPI FaudioThreadWrapper(void *user)
547 struct FAudioThreadArgs *args = user;
548 DWORD ret;
550 FAudio_set_thread_name(args->name);
551 ret = args->func(args->data);
553 FAudio_free(args);
554 return ret;
557 FAudioThread FAudio_PlatformCreateThread(
558 FAudioThreadFunc func,
559 const char *name,
560 void* data
562 struct FAudioThreadArgs *args;
564 if (!(args = FAudio_malloc(sizeof(*args)))) return NULL;
565 args->func = func;
566 args->name = name;
567 args->data = data;
569 return CreateThread(NULL, 0, &FaudioThreadWrapper, args, 0, NULL);
572 void FAudio_PlatformWaitThread(FAudioThread thread, int32_t *retval)
574 WaitForSingleObject(thread, INFINITE);
575 GetExitCodeThread(thread, (DWORD *)retval);
578 void FAudio_PlatformThreadPriority(FAudioThreadPriority priority)
580 /* FIXME */
583 uint64_t FAudio_PlatformGetThreadID(void)
585 return GetCurrentThreadId();
588 void FAudio_sleep(uint32_t ms)
590 Sleep(ms);
593 uint32_t FAudio_timems()
595 return GetTickCount();
598 /* FAudio I/O */
600 static size_t FAUDIOCALL FAudio_FILE_read(
601 void *data,
602 void *dst,
603 size_t size,
604 size_t count
606 if (!data) return 0;
607 return fread(dst, size, count, data);
610 static int64_t FAUDIOCALL FAudio_FILE_seek(
611 void *data,
612 int64_t offset,
613 int whence
615 if (!data) return -1;
616 fseek(data, offset, whence);
617 return ftell(data);
620 static int FAUDIOCALL FAudio_FILE_close(void *data)
622 if (!data) return 0;
623 fclose(data);
624 return 0;
627 FAudioIOStream* FAudio_fopen(const char *path)
629 FAudioIOStream *io;
631 io = (FAudioIOStream*) FAudio_malloc(sizeof(FAudioIOStream));
632 if (!io) return NULL;
634 io->data = fopen(path, "rb");
635 io->read = FAudio_FILE_read;
636 io->seek = FAudio_FILE_seek;
637 io->close = FAudio_FILE_close;
638 io->lock = FAudio_PlatformCreateMutex();
640 return io;
643 struct FAudio_mem
645 char *mem;
646 int64_t len;
647 int64_t pos;
650 static size_t FAUDIOCALL FAudio_mem_read(
651 void *data,
652 void *dst,
653 size_t size,
654 size_t count
656 struct FAudio_mem *io = data;
657 size_t len = size * count;
659 if (!data) return 0;
661 while (len && len > (io->len - io->pos)) len -= size;
662 FAudio_memcpy(dst, io->mem + io->pos, len);
663 io->pos += len;
665 return len;
668 static int64_t FAUDIOCALL FAudio_mem_seek(
669 void *data,
670 int64_t offset,
671 int whence
673 struct FAudio_mem *io = data;
674 if (!data) return -1;
676 if (whence == SEEK_SET)
678 if (io->len > offset) io->pos = offset;
679 else io->pos = io->len;
681 if (whence == SEEK_CUR)
683 if (io->len > io->pos + offset) io->pos += offset;
684 else io->pos = io->len;
686 if (whence == SEEK_END)
688 if (io->len > offset) io->pos = io->len - offset;
689 else io->pos = 0;
692 return io->pos;
695 static int FAUDIOCALL FAudio_mem_close(void *data)
697 if (!data) return 0;
698 FAudio_free(data);
699 return 0;
702 FAudioIOStream* FAudio_memopen(void *mem, int len)
704 struct FAudio_mem *data;
705 FAudioIOStream *io;
707 io = (FAudioIOStream*) FAudio_malloc(sizeof(FAudioIOStream));
708 if (!io) return NULL;
710 data = FAudio_malloc(sizeof(struct FAudio_mem));
711 if (!data)
713 FAudio_free(io);
714 return NULL;
717 data->mem = mem;
718 data->len = len;
719 data->pos = 0;
721 io->data = data;
722 io->read = FAudio_mem_read;
723 io->seek = FAudio_mem_seek;
724 io->close = FAudio_mem_close;
725 io->lock = FAudio_PlatformCreateMutex();
726 return io;
729 uint8_t* FAudio_memptr(FAudioIOStream *io, size_t offset)
731 struct FAudio_mem *memio = io->data;
732 return memio->mem + offset;
735 void FAudio_close(FAudioIOStream *io)
737 io->close(io->data);
738 FAudio_PlatformDestroyMutex((FAudioMutex) io->lock);
739 FAudio_free(io);
742 /* XNA Song implementation over Win32 MF */
744 static FAudioWaveFormatEx activeSongFormat;
745 IMFSourceReader *activeSong;
746 static uint8_t *songBuffer;
747 static SIZE_T songBufferSize;
749 static float songVolume = 1.0f;
750 static FAudio *songAudio = NULL;
751 static FAudioMasteringVoice *songMaster = NULL;
753 static FAudioSourceVoice *songVoice = NULL;
754 static FAudioVoiceCallback callbacks;
756 /* Internal Functions */
758 static void XNA_SongSubmitBuffer(FAudioVoiceCallback *callback, void *pBufferContext)
760 IMFMediaBuffer *media_buffer;
761 FAudioBuffer buffer;
762 IMFSample *sample;
763 HRESULT hr;
764 DWORD flags, buffer_size = 0;
765 BYTE *buffer_ptr;
767 LOG_FUNC_ENTER(songAudio);
769 FAudio_memset(&buffer, 0, sizeof(buffer));
771 hr = IMFSourceReader_ReadSample(
772 activeSong,
773 MF_SOURCE_READER_FIRST_AUDIO_STREAM,
775 NULL,
776 &flags,
777 NULL,
778 &sample
780 FAudio_assert(!FAILED(hr) && "Failed to read audio sample!");
782 if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
784 buffer.Flags = FAUDIO_END_OF_STREAM;
786 else
788 hr = IMFSample_ConvertToContiguousBuffer(
789 sample,
790 &media_buffer
792 FAudio_assert(!FAILED(hr) && "Failed to get sample buffer!");
794 hr = IMFMediaBuffer_Lock(
795 media_buffer,
796 &buffer_ptr,
797 NULL,
798 &buffer_size
800 FAudio_assert(!FAILED(hr) && "Failed to lock buffer bytes!");
802 if (songBufferSize < buffer_size)
804 songBufferSize = buffer_size;
805 songBuffer = FAudio_realloc(songBuffer, songBufferSize);
806 FAudio_assert(songBuffer != NULL && "Failed to allocate song buffer!");
808 FAudio_memcpy(songBuffer, buffer_ptr, buffer_size);
810 hr = IMFMediaBuffer_Unlock(media_buffer);
811 FAudio_assert(!FAILED(hr) && "Failed to unlock buffer bytes!");
813 IMFMediaBuffer_Release(media_buffer);
814 IMFSample_Release(sample);
817 if (buffer_size > 0)
819 buffer.AudioBytes = buffer_size;
820 buffer.pAudioData = songBuffer;
821 buffer.PlayBegin = 0;
822 buffer.PlayLength = buffer_size / activeSongFormat.nBlockAlign;
823 buffer.LoopBegin = 0;
824 buffer.LoopLength = 0;
825 buffer.LoopCount = 0;
826 buffer.pContext = NULL;
827 FAudioSourceVoice_SubmitSourceBuffer(
828 songVoice,
829 &buffer,
830 NULL
834 LOG_FUNC_EXIT(songAudio);
837 static void XNA_SongKill()
839 if (songVoice != NULL)
841 FAudioSourceVoice_Stop(songVoice, 0, 0);
842 FAudioVoice_DestroyVoice(songVoice);
843 songVoice = NULL;
845 if (activeSong)
847 IMFSourceReader_Release(activeSong);
848 activeSong = NULL;
850 FAudio_free(songBuffer);
851 songBuffer = NULL;
852 songBufferSize = 0;
855 /* "Public" API */
857 FAUDIOAPI void XNA_SongInit()
859 HRESULT hr;
861 hr = MFStartup(MF_VERSION, MFSTARTUP_FULL);
862 FAudio_assert(!FAILED(hr) && "Failed to initialize Media Foundation!");
864 FAudioCreate(&songAudio, 0, FAUDIO_DEFAULT_PROCESSOR);
865 FAudio_CreateMasteringVoice(
866 songAudio,
867 &songMaster,
868 FAUDIO_DEFAULT_CHANNELS,
869 FAUDIO_DEFAULT_SAMPLERATE,
872 NULL
876 FAUDIOAPI void XNA_SongQuit()
878 XNA_SongKill();
879 FAudioVoice_DestroyVoice(songMaster);
880 FAudio_Release(songAudio);
881 MFShutdown();
884 FAUDIOAPI float XNA_PlaySong(const char *name)
886 IMFAttributes *attributes = NULL;
887 IMFMediaType *media_type = NULL;
888 UINT32 channels, samplerate;
889 UINT64 duration;
890 PROPVARIANT var;
891 HRESULT hr;
892 WCHAR filename_w[MAX_PATH];
894 LOG_FUNC_ENTER(songAudio);
895 LOG_INFO(songAudio, "name %s\n", name);
896 XNA_SongKill();
898 MultiByteToWideChar(CP_UTF8, 0, name, -1, filename_w, MAX_PATH);
900 hr = MFCreateAttributes(&attributes, 1);
901 FAudio_assert(!FAILED(hr) && "Failed to create attributes!");
902 hr = MFCreateSourceReaderFromURL(
903 filename_w,
904 attributes,
905 &activeSong
907 FAudio_assert(!FAILED(hr) && "Failed to create source reader!");
908 IMFAttributes_Release(attributes);
910 hr = MFCreateMediaType(&media_type);
911 FAudio_assert(!FAILED(hr) && "Failed to create media type!");
912 hr = IMFMediaType_SetGUID(
913 media_type,
914 &MF_MT_MAJOR_TYPE,
915 &MFMediaType_Audio
917 FAudio_assert(!FAILED(hr) && "Failed to set major type!");
918 hr = IMFMediaType_SetGUID(
919 media_type,
920 &MF_MT_SUBTYPE,
921 &MFAudioFormat_Float
923 FAudio_assert(!FAILED(hr) && "Failed to set sub type!");
924 hr = IMFSourceReader_SetCurrentMediaType(
925 activeSong,
926 MF_SOURCE_READER_FIRST_AUDIO_STREAM,
927 NULL,
928 media_type
930 FAudio_assert(!FAILED(hr) && "Failed to set source media type!");
931 hr = IMFSourceReader_SetStreamSelection(
932 activeSong,
933 MF_SOURCE_READER_FIRST_AUDIO_STREAM,
934 TRUE
936 FAudio_assert(!FAILED(hr) && "Failed to select source stream!");
937 IMFMediaType_Release(media_type);
939 hr = IMFSourceReader_GetCurrentMediaType(
940 activeSong,
941 MF_SOURCE_READER_FIRST_AUDIO_STREAM,
942 &media_type
944 FAudio_assert(!FAILED(hr) && "Failed to get current media type!");
945 hr = IMFMediaType_GetUINT32(
946 media_type,
947 &MF_MT_AUDIO_NUM_CHANNELS,
948 &channels
950 FAudio_assert(!FAILED(hr) && "Failed to get channel count!");
951 hr = IMFMediaType_GetUINT32(
952 media_type,
953 &MF_MT_AUDIO_SAMPLES_PER_SECOND,
954 &samplerate
956 FAudio_assert(!FAILED(hr) && "Failed to get sample rate!");
957 IMFMediaType_Release(media_type);
959 hr = IMFSourceReader_GetPresentationAttribute(
960 activeSong,
961 MF_SOURCE_READER_MEDIASOURCE,
962 &MF_PD_DURATION,
963 &var
965 FAudio_assert(!FAILED(hr) && "Failed to get song duration!");
966 hr = PropVariantToInt64(&var, &duration);
967 FAudio_assert(!FAILED(hr) && "Failed to get song duration!");
968 PropVariantClear(&var);
970 activeSongFormat.wFormatTag = FAUDIO_FORMAT_IEEE_FLOAT;
971 activeSongFormat.nChannels = channels;
972 activeSongFormat.nSamplesPerSec = samplerate;
973 activeSongFormat.wBitsPerSample = sizeof(float) * 8;
974 activeSongFormat.nBlockAlign = activeSongFormat.nChannels * activeSongFormat.wBitsPerSample / 8;
975 activeSongFormat.nAvgBytesPerSec = activeSongFormat.nSamplesPerSec * activeSongFormat.nBlockAlign;
976 activeSongFormat.cbSize = 0;
978 /* Init voice */
979 FAudio_zero(&callbacks, sizeof(FAudioVoiceCallback));
980 callbacks.OnBufferEnd = XNA_SongSubmitBuffer;
981 FAudio_CreateSourceVoice(
982 songAudio,
983 &songVoice,
984 &activeSongFormat,
986 1.0f, /* No pitch shifting here! */
987 &callbacks,
988 NULL,
989 NULL
991 FAudioVoice_SetVolume(songVoice, songVolume, 0);
992 XNA_SongSubmitBuffer(NULL, NULL);
994 /* Finally. */
995 FAudioSourceVoice_Start(songVoice, 0, 0);
996 LOG_FUNC_EXIT(songAudio);
997 return duration / 10000000.;
1000 FAUDIOAPI void XNA_PauseSong()
1002 if (songVoice == NULL)
1004 return;
1006 FAudioSourceVoice_Stop(songVoice, 0, 0);
1009 FAUDIOAPI void XNA_ResumeSong()
1011 if (songVoice == NULL)
1013 return;
1015 FAudioSourceVoice_Start(songVoice, 0, 0);
1018 FAUDIOAPI void XNA_StopSong()
1020 XNA_SongKill();
1023 FAUDIOAPI void XNA_SetSongVolume(float volume)
1025 songVolume = volume;
1026 if (songVoice != NULL)
1028 FAudioVoice_SetVolume(songVoice, songVolume, 0);
1032 FAUDIOAPI uint32_t XNA_GetSongEnded()
1034 FAudioVoiceState state;
1035 if (songVoice == NULL || activeSong == NULL)
1037 return 1;
1039 FAudioSourceVoice_GetState(songVoice, &state, 0);
1040 return state.BuffersQueued == 0;
1043 FAUDIOAPI void XNA_EnableVisualization(uint32_t enable)
1045 /* TODO: Enable/Disable FAPO effect */
1048 FAUDIOAPI uint32_t XNA_VisualizationEnabled()
1050 /* TODO: Query FAPO effect enabled */
1051 return 0;
1054 FAUDIOAPI void XNA_GetSongVisualizationData(
1055 float *frequencies,
1056 float *samples,
1057 uint32_t count
1059 /* TODO: Visualization FAPO that reads in Song samples, FFT analysis */
1062 /* FAudio WMADEC implementation over Win32 MF */
1064 struct FAudioWMADEC
1066 IMFTransform *decoder;
1067 IMFSample *output_sample;
1069 char *output_buf;
1070 size_t output_pos;
1071 size_t output_size;
1072 size_t input_pos;
1073 size_t input_size;
1076 static HRESULT FAudio_WMAMF_ProcessInput(
1077 FAudioVoice *voice,
1078 FAudioBuffer *buffer
1080 struct FAudioWMADEC *impl = voice->src.wmadec;
1081 IMFMediaBuffer *media_buffer;
1082 IMFSample *sample;
1083 DWORD copy_size;
1084 BYTE *copy_buf;
1085 HRESULT hr;
1087 copy_size = min(buffer->AudioBytes - impl->input_pos, impl->input_size);
1088 if (!copy_size) return S_FALSE;
1089 LOG_INFO(voice->audio, "pushing %x bytes at %x", copy_size, impl->input_pos);
1091 hr = MFCreateSample(&sample);
1092 FAudio_assert(!FAILED(hr) && "Failed to create sample!");
1093 hr = MFCreateMemoryBuffer(copy_size, &media_buffer);
1094 FAudio_assert(!FAILED(hr) && "Failed to create buffer!");
1095 hr = IMFMediaBuffer_SetCurrentLength(media_buffer, copy_size);
1096 FAudio_assert(!FAILED(hr) && "Failed to set buffer length!");
1097 hr = IMFMediaBuffer_Lock(
1098 media_buffer,
1099 &copy_buf,
1100 NULL,
1101 &copy_size
1103 FAudio_assert(!FAILED(hr) && "Failed to lock buffer bytes!");
1104 FAudio_memcpy(copy_buf, buffer->pAudioData + impl->input_pos, copy_size);
1105 hr = IMFMediaBuffer_Unlock(media_buffer);
1106 FAudio_assert(!FAILED(hr) && "Failed to unlock buffer bytes!");
1108 hr = IMFSample_AddBuffer(sample, media_buffer);
1109 FAudio_assert(!FAILED(hr) && "Failed to buffer to sample!");
1110 IMFMediaBuffer_Release(media_buffer);
1112 hr = IMFTransform_ProcessInput(impl->decoder, 0, sample, 0);
1113 IMFSample_Release(sample);
1114 if (hr == MF_E_NOTACCEPTING) return S_OK;
1115 if (FAILED(hr))
1117 LOG_ERROR(voice->audio, "IMFTransform_ProcessInput returned %#x", hr);
1118 return hr;
1121 impl->input_pos += copy_size;
1122 return S_OK;
1125 static HRESULT FAudio_WMAMF_ProcessOutput(
1126 FAudioVoice *voice,
1127 FAudioBuffer *buffer
1129 struct FAudioWMADEC *impl = voice->src.wmadec;
1130 MFT_OUTPUT_DATA_BUFFER output;
1131 IMFMediaBuffer *media_buffer;
1132 DWORD status, copy_size;
1133 BYTE *copy_buf;
1134 HRESULT hr;
1136 while (1)
1138 FAudio_memset(&output, 0, sizeof(output));
1139 output.pSample = impl->output_sample;
1140 hr = IMFTransform_ProcessOutput(impl->decoder, 0, 1, &output, &status);
1141 if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) return S_FALSE;
1142 if (FAILED(hr))
1144 LOG_ERROR(voice->audio, "IMFTransform_ProcessInput returned %#x", hr);
1145 return hr;
1148 if (output.dwStatus & MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE) continue;
1150 hr = IMFSample_ConvertToContiguousBuffer(
1151 output.pSample,
1152 &media_buffer
1154 FAudio_assert(!FAILED(hr) && "Failed to get sample buffer!");
1155 hr = IMFMediaBuffer_Lock(
1156 media_buffer,
1157 &copy_buf,
1158 NULL,
1159 &copy_size
1161 FAudio_assert(!FAILED(hr) && "Failed to lock buffer bytes!");
1162 if (impl->output_pos + copy_size > impl->output_size)
1164 impl->output_size = max(
1165 impl->output_pos + copy_size,
1166 impl->output_size * 3 / 2
1168 impl->output_buf = voice->audio->pRealloc(
1169 impl->output_buf,
1170 impl->output_size
1172 FAudio_assert(impl->output_buf && "Failed to resize output buffer!");
1174 FAudio_memcpy(impl->output_buf + impl->output_pos, copy_buf, copy_size);
1175 impl->output_pos += copy_size;
1176 LOG_INFO(voice->audio, "pulled %x bytes at %x", copy_size, impl->output_pos);
1177 hr = IMFMediaBuffer_Unlock(media_buffer);
1178 FAudio_assert(!FAILED(hr) && "Failed to unlock buffer bytes!");
1180 IMFMediaBuffer_Release(media_buffer);
1181 if (!impl->output_sample) IMFSample_Release(output.pSample);
1184 return S_OK;
1187 static void FAudio_INTERNAL_DecodeWMAMF(
1188 FAudioVoice *voice,
1189 FAudioBuffer *buffer,
1190 float *decodeCache,
1191 uint32_t samples
1193 const FAudioWaveFormatExtensible *wfx = (FAudioWaveFormatExtensible *)voice->src.format;
1194 struct FAudioWMADEC *impl = voice->src.wmadec;
1195 size_t samples_pos, samples_size, copy_size;
1196 HRESULT hr;
1198 LOG_FUNC_ENTER(voice->audio)
1200 if (!impl->output_pos)
1202 if (wfx->Format.wFormatTag == FAUDIO_FORMAT_EXTENSIBLE)
1204 const FAudioBufferWMA *wma = &voice->src.bufferList->bufferWMA;
1205 const UINT32 *output_sizes = wma->pDecodedPacketCumulativeBytes;
1207 impl->input_size = wfx->Format.nBlockAlign;
1208 impl->output_size = max(
1209 impl->output_size,
1210 output_sizes[wma->PacketCount - 1]
1213 else
1215 const FAudioXMA2WaveFormat *xwf = (const FAudioXMA2WaveFormat *)wfx;
1217 impl->input_size = xwf->dwBytesPerBlock;
1218 impl->output_size = max(
1219 impl->output_size,
1220 (size_t) xwf->dwSamplesEncoded *
1221 voice->src.format->nChannels *
1222 (voice->src.format->wBitsPerSample / 8)
1226 impl->output_buf = voice->audio->pRealloc(
1227 impl->output_buf,
1228 impl->output_size
1230 FAudio_assert(impl->output_buf && "Failed to allocate output buffer!");
1232 LOG_INFO(voice->audio, "sending BOS to %p", impl->decoder);
1233 hr = IMFTransform_ProcessMessage(
1234 impl->decoder,
1235 MFT_MESSAGE_NOTIFY_START_OF_STREAM,
1238 FAudio_assert(!FAILED(hr) && "Failed to notify decoder stream start!");
1239 FAudio_WMAMF_ProcessInput(voice, buffer);
1242 samples_pos = voice->src.curBufferOffset * voice->src.format->nChannels * sizeof(float);
1243 samples_size = samples * voice->src.format->nChannels * sizeof(float);
1245 while (impl->output_pos < samples_pos + samples_size)
1247 hr = FAudio_WMAMF_ProcessOutput(voice, buffer);
1248 if (FAILED(hr)) goto error;
1249 if (hr == S_OK) continue;
1251 hr = FAudio_WMAMF_ProcessInput(voice, buffer);
1252 if (FAILED(hr)) goto error;
1253 if (hr == S_OK) continue;
1255 if (!impl->input_size) break;
1257 LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);
1258 hr = IMFTransform_ProcessMessage(
1259 impl->decoder,
1260 MFT_MESSAGE_NOTIFY_END_OF_STREAM,
1263 FAudio_assert(!FAILED(hr) && "Failed to send EOS!");
1264 impl->input_size = 0;
1267 copy_size = FAudio_clamp(impl->output_pos - samples_pos, 0, samples_size);
1268 FAudio_memcpy(decodeCache, impl->output_buf + samples_pos, copy_size);
1269 LOG_INFO(
1270 voice->audio,
1271 "decoded %x / %x bytes, copied %x / %x bytes",
1272 impl->output_pos,
1273 impl->output_size,
1274 copy_size,
1275 samples_size
1278 LOG_FUNC_EXIT(voice->audio)
1279 return;
1281 error:
1282 FAudio_zero(decodeCache, samples * voice->src.format->nChannels * sizeof(float));
1283 LOG_FUNC_EXIT(voice->audio)
1286 uint32_t FAudio_WMADEC_init(FAudioSourceVoice *voice, uint32_t type)
1288 static const uint8_t fake_codec_data[16] = {0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1289 const FAudioWaveFormatExtensible *wfx = (FAudioWaveFormatExtensible *)voice->src.format;
1290 struct FAudioWMADEC *impl;
1291 MFT_OUTPUT_STREAM_INFO info = {0};
1292 IMFMediaBuffer *media_buffer;
1293 IMFMediaType *media_type;
1294 IMFTransform *decoder;
1295 HRESULT hr;
1296 UINT32 i, value;
1297 GUID guid;
1299 LOG_FUNC_ENTER(voice->audio)
1301 if (!(impl = voice->audio->pMalloc(sizeof(*impl)))) return -1;
1302 FAudio_memset(impl, 0, sizeof(*impl));
1304 hr = CoCreateInstance(
1305 &CLSID_CWMADecMediaObject,
1307 CLSCTX_INPROC_SERVER,
1308 &IID_IMFTransform,
1309 (void **)&decoder
1311 if (FAILED(hr))
1313 voice->audio->pFree(impl->output_buf);
1314 return -2;
1317 hr = MFCreateMediaType(&media_type);
1318 FAudio_assert(!FAILED(hr) && "Failed create media type!");
1319 hr = IMFMediaType_SetGUID(
1320 media_type,
1321 &MF_MT_MAJOR_TYPE,
1322 &MFMediaType_Audio
1324 FAudio_assert(!FAILED(hr) && "Failed set media major type!");
1326 switch (type)
1328 case FAUDIO_FORMAT_WMAUDIO2:
1329 hr = IMFMediaType_SetBlob(
1330 media_type,
1331 &MF_MT_USER_DATA,
1332 (void *)fake_codec_data,
1333 sizeof(fake_codec_data)
1335 FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
1336 hr = IMFMediaType_SetGUID(
1337 media_type,
1338 &MF_MT_SUBTYPE,
1339 &MFAudioFormat_WMAudioV8
1341 FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
1342 hr = IMFMediaType_SetUINT32(
1343 media_type,
1344 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1345 wfx->Format.nBlockAlign
1347 FAudio_assert(!FAILED(hr) && "Failed set input block align!");
1348 break;
1349 case FAUDIO_FORMAT_WMAUDIO3:
1350 hr = IMFMediaType_SetBlob(
1351 media_type,
1352 &MF_MT_USER_DATA,
1353 (void *)&wfx->Samples,
1354 wfx->Format.cbSize
1356 FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
1357 hr = IMFMediaType_SetGUID(
1358 media_type,
1359 &MF_MT_SUBTYPE,
1360 &MFAudioFormat_WMAudioV9
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_WMAUDIO_LOSSLESS:
1371 hr = IMFMediaType_SetBlob(
1372 media_type,
1373 &MF_MT_USER_DATA,
1374 (void *)&wfx->Samples,
1375 wfx->Format.cbSize
1377 FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
1378 hr = IMFMediaType_SetGUID(
1379 media_type,
1380 &MF_MT_SUBTYPE,
1381 &MFAudioFormat_WMAudio_Lossless
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_XMAUDIO2:
1393 const FAudioXMA2WaveFormat *xwf = (const FAudioXMA2WaveFormat *)wfx;
1394 hr = IMFMediaType_SetBlob(
1395 media_type,
1396 &MF_MT_USER_DATA,
1397 (void *)&wfx->Samples,
1398 wfx->Format.cbSize
1400 FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
1401 hr = IMFMediaType_SetGUID(
1402 media_type,
1403 &MF_MT_SUBTYPE,
1404 &MFAudioFormat_XMAudio2
1406 FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
1407 hr = IMFMediaType_SetUINT32(
1408 media_type,
1409 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1410 xwf->dwBytesPerBlock
1412 FAudio_assert(!FAILED(hr) && "Failed set input block align!");
1413 break;
1415 default:
1416 FAudio_assert(0 && "Unsupported type!");
1417 break;
1420 hr = IMFMediaType_SetUINT32(
1421 media_type,
1422 &MF_MT_AUDIO_BITS_PER_SAMPLE,
1423 wfx->Format.wBitsPerSample
1425 FAudio_assert(!FAILED(hr) && "Failed set input bits per sample!");
1426 hr = IMFMediaType_SetUINT32(
1427 media_type,
1428 &MF_MT_AUDIO_AVG_BYTES_PER_SECOND,
1429 wfx->Format.nAvgBytesPerSec
1431 FAudio_assert(!FAILED(hr) && "Failed set input bytes per sample!");
1432 hr = IMFMediaType_SetUINT32(
1433 media_type,
1434 &MF_MT_AUDIO_NUM_CHANNELS,
1435 wfx->Format.nChannels
1437 FAudio_assert(!FAILED(hr) && "Failed set input channel count!");
1438 hr = IMFMediaType_SetUINT32(
1439 media_type,
1440 &MF_MT_AUDIO_SAMPLES_PER_SECOND,
1441 wfx->Format.nSamplesPerSec
1443 FAudio_assert(!FAILED(hr) && "Failed set input sample rate!");
1445 hr = IMFTransform_SetInputType(
1446 decoder,
1448 media_type,
1451 FAudio_assert(!FAILED(hr) && "Failed set decoder input type!");
1452 IMFMediaType_Release(media_type);
1454 i = 0;
1455 while (SUCCEEDED(hr))
1457 hr = IMFTransform_GetOutputAvailableType(
1458 decoder,
1460 i++,
1461 &media_type
1463 FAudio_assert(!FAILED(hr) && "Failed get output media type!");
1465 hr = IMFMediaType_GetGUID(
1466 media_type,
1467 &MF_MT_MAJOR_TYPE,
1468 &guid
1470 FAudio_assert(!FAILED(hr) && "Failed get media major type!");
1471 if (!IsEqualGUID(&MFMediaType_Audio, &guid)) goto next;
1473 hr = IMFMediaType_GetGUID(
1474 media_type,
1475 &MF_MT_SUBTYPE,
1476 &guid
1478 FAudio_assert(!FAILED(hr) && "Failed get media major type!");
1479 if (!IsEqualGUID(&MFAudioFormat_Float, &guid)) goto next;
1481 hr = IMFMediaType_GetUINT32(
1482 media_type,
1483 &MF_MT_AUDIO_BITS_PER_SAMPLE,
1484 &value
1486 if (FAILED(hr))
1488 value = 32;
1489 hr = IMFMediaType_SetUINT32(
1490 media_type,
1491 &MF_MT_AUDIO_BITS_PER_SAMPLE,
1492 value
1495 FAudio_assert(!FAILED(hr) && "Failed get bits per sample!");
1496 if (value != 32) goto next;
1498 hr = IMFMediaType_GetUINT32(
1499 media_type,
1500 &MF_MT_AUDIO_NUM_CHANNELS,
1501 &value
1503 if (FAILED(hr))
1505 value = wfx->Format.nChannels;
1506 hr = IMFMediaType_SetUINT32(
1507 media_type,
1508 &MF_MT_AUDIO_NUM_CHANNELS,
1509 value
1512 FAudio_assert(!FAILED(hr) && "Failed get channel count!");
1513 if (value != wfx->Format.nChannels) goto next;
1515 hr = IMFMediaType_GetUINT32(
1516 media_type,
1517 &MF_MT_AUDIO_SAMPLES_PER_SECOND,
1518 &value
1520 if (FAILED(hr))
1522 value = wfx->Format.nSamplesPerSec;
1523 hr = IMFMediaType_SetUINT32(
1524 media_type,
1525 &MF_MT_AUDIO_SAMPLES_PER_SECOND,
1526 value
1529 FAudio_assert(!FAILED(hr) && "Failed get sample rate!");
1530 if (value != wfx->Format.nSamplesPerSec) goto next;
1532 hr = IMFMediaType_GetUINT32(
1533 media_type,
1534 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1535 &value
1537 if (FAILED(hr))
1539 value = wfx->Format.nChannels * sizeof(float);
1540 hr = IMFMediaType_SetUINT32(
1541 media_type,
1542 &MF_MT_AUDIO_BLOCK_ALIGNMENT,
1543 value
1546 FAudio_assert(!FAILED(hr) && "Failed get block align!");
1547 if (value == wfx->Format.nChannels * sizeof(float)) break;
1549 next:
1550 IMFMediaType_Release(media_type);
1552 FAudio_assert(!FAILED(hr) && "Failed to find output media type!");
1553 hr = IMFTransform_SetOutputType(decoder, 0, media_type, 0);
1554 FAudio_assert(!FAILED(hr) && "Failed set decoder output type!");
1555 IMFMediaType_Release(media_type);
1557 hr = IMFTransform_GetOutputStreamInfo(decoder, 0, &info);
1558 FAudio_assert(!FAILED(hr) && "Failed to get output stream info!");
1560 impl->decoder = decoder;
1561 if (!(info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES))
1563 hr = MFCreateSample(&impl->output_sample);
1564 FAudio_assert(!FAILED(hr) && "Failed to create sample!");
1565 hr = MFCreateMemoryBuffer(info.cbSize, &media_buffer);
1566 FAudio_assert(!FAILED(hr) && "Failed to create buffer!");
1567 hr = IMFSample_AddBuffer(impl->output_sample, media_buffer);
1568 FAudio_assert(!FAILED(hr) && "Failed to buffer to sample!");
1569 IMFMediaBuffer_Release(media_buffer);
1572 hr = IMFTransform_ProcessMessage(
1573 decoder,
1574 MFT_MESSAGE_NOTIFY_BEGIN_STREAMING,
1577 FAudio_assert(!FAILED(hr) && "Failed to start decoder stream!");
1579 voice->src.wmadec = impl;
1580 voice->src.decode = FAudio_INTERNAL_DecodeWMAMF;
1582 LOG_FUNC_EXIT(voice->audio);
1583 return 0;
1586 void FAudio_WMADEC_free(FAudioSourceVoice *voice)
1588 struct FAudioWMADEC *impl = voice->src.wmadec;
1589 HRESULT hr;
1591 LOG_FUNC_ENTER(voice->audio)
1592 FAudio_PlatformLockMutex(voice->audio->sourceLock);
1593 LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
1595 if (impl->input_size)
1597 LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);
1598 hr = IMFTransform_ProcessMessage(
1599 impl->decoder,
1600 MFT_MESSAGE_NOTIFY_END_OF_STREAM,
1603 FAudio_assert(!FAILED(hr) && "Failed to send EOS!");
1604 impl->input_size = 0;
1606 if (impl->output_pos)
1608 LOG_INFO(voice->audio, "sending DRAIN to %p", impl->decoder);
1609 hr = IMFTransform_ProcessMessage(
1610 impl->decoder,
1611 MFT_MESSAGE_COMMAND_DRAIN,
1614 FAudio_assert(!FAILED(hr) && "Failed to send DRAIN!");
1615 impl->output_pos = 0;
1618 if (impl->output_sample) IMFSample_Release(impl->output_sample);
1619 IMFTransform_Release(impl->decoder);
1620 voice->audio->pFree(impl->output_buf);
1621 voice->audio->pFree(voice->src.wmadec);
1622 voice->src.wmadec = NULL;
1623 voice->src.decode = NULL;
1625 FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
1626 LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
1627 LOG_FUNC_EXIT(voice->audio)
1630 void FAudio_WMADEC_end_buffer(FAudioSourceVoice *voice)
1632 struct FAudioWMADEC *impl = voice->src.wmadec;
1633 HRESULT hr;
1635 LOG_FUNC_ENTER(voice->audio)
1637 if (impl->input_size)
1639 LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);
1640 hr = IMFTransform_ProcessMessage(
1641 impl->decoder,
1642 MFT_MESSAGE_NOTIFY_END_OF_STREAM,
1645 FAudio_assert(!FAILED(hr) && "Failed to send EOS!");
1646 impl->input_size = 0;
1648 impl->output_pos = 0;
1649 impl->input_pos = 0;
1651 LOG_FUNC_EXIT(voice->audio)
1654 #else
1656 extern int this_tu_is_empty;
1658 #endif /* FAUDIO_WIN32_PLATFORM */