Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/faudio/src/FAudio_platform_win32.c
4389 views
1
/* FAudio - XAudio Reimplementation for FNA
2
*
3
* Copyright (c) 2011-2020 Ethan Lee, Luigi Auriemma, and the MonoGame Team
4
*
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.
8
*
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:
12
*
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.
17
*
18
* 2. Altered source versions must be plainly marked as such, and must not be
19
* misrepresented as being the original software.
20
*
21
* 3. This notice may not be removed or altered from any source distribution.
22
*
23
* Ethan "flibitijibibo" Lee <[email protected]>
24
*
25
*/
26
27
#ifdef FAUDIO_WIN32_PLATFORM
28
29
#include <stddef.h>
30
31
#define COBJMACROS
32
#include <windows.h>
33
#include <mfidl.h>
34
#include <mfapi.h>
35
#include <mfreadwrite.h>
36
#include <propvarutil.h>
37
38
#include <initguid.h>
39
#include <audioclient.h>
40
#include <mmdeviceapi.h>
41
#include <devpkey.h>
42
43
#define _SPEAKER_POSITIONS_ /* Defined by SDK. */
44
#include "FAudio_internal.h"
45
46
static CRITICAL_SECTION faudio_cs = { NULL, -1, 0, 0, 0, 0 };
47
static IMMDeviceEnumerator *device_enumerator;
48
static HRESULT init_hr;
49
50
struct FAudioWin32PlatformData
51
{
52
IAudioClient *client;
53
HANDLE audioThread;
54
HANDLE stopEvent;
55
};
56
57
struct FAudioAudioClientThreadArgs
58
{
59
WAVEFORMATEXTENSIBLE format;
60
IAudioClient *client;
61
HANDLE events[2];
62
FAudio *audio;
63
UINT updateSize;
64
};
65
66
void FAudio_Log(char const *msg)
67
{
68
OutputDebugStringA(msg);
69
}
70
71
static HMODULE kernelbase = NULL;
72
static HRESULT (WINAPI *my_SetThreadDescription)(HANDLE, PCWSTR) = NULL;
73
74
static void FAudio_resolve_SetThreadDescription(void)
75
{
76
kernelbase = LoadLibraryA("kernelbase.dll");
77
if (!kernelbase)
78
return;
79
80
my_SetThreadDescription = (HRESULT (WINAPI *)(HANDLE, PCWSTR)) GetProcAddress(kernelbase, "SetThreadDescription");
81
if (!my_SetThreadDescription)
82
{
83
FreeLibrary(kernelbase);
84
kernelbase = NULL;
85
}
86
}
87
88
static void FAudio_set_thread_name(char const *name)
89
{
90
int ret;
91
WCHAR *nameW;
92
93
if (!my_SetThreadDescription)
94
return;
95
96
ret = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0);
97
98
nameW = FAudio_malloc(ret * sizeof(WCHAR));
99
if (!nameW)
100
return;
101
102
ret = MultiByteToWideChar(CP_UTF8, 0, name, -1, nameW, ret);
103
if (ret)
104
my_SetThreadDescription(GetCurrentThread(), nameW);
105
106
FAudio_free(nameW);
107
}
108
109
static HRESULT FAudio_FillAudioClientBuffer(
110
struct FAudioAudioClientThreadArgs *args,
111
IAudioRenderClient *client,
112
UINT frames,
113
UINT padding
114
) {
115
HRESULT hr = S_OK;
116
BYTE *buffer;
117
118
while (padding + args->updateSize <= frames)
119
{
120
hr = IAudioRenderClient_GetBuffer(
121
client,
122
frames - padding,
123
&buffer
124
);
125
if (FAILED(hr)) return hr;
126
127
FAudio_zero(
128
buffer,
129
args->updateSize * args->format.Format.nBlockAlign
130
);
131
132
if (args->audio->active)
133
{
134
FAudio_INTERNAL_UpdateEngine(
135
args->audio,
136
(float*) buffer
137
);
138
}
139
140
hr = IAudioRenderClient_ReleaseBuffer(
141
client,
142
args->updateSize,
143
0
144
);
145
if (FAILED(hr)) return hr;
146
147
padding += args->updateSize;
148
}
149
150
return hr;
151
}
152
153
static DWORD WINAPI FAudio_AudioClientThread(void *user)
154
{
155
struct FAudioAudioClientThreadArgs *args = user;
156
IAudioRenderClient *render_client;
157
HRESULT hr = S_OK;
158
UINT frames, padding = 0;
159
160
FAudio_set_thread_name(__func__);
161
162
hr = IAudioClient_GetService(
163
args->client,
164
&IID_IAudioRenderClient,
165
(void **)&render_client
166
);
167
FAudio_assert(!FAILED(hr) && "Failed to get IAudioRenderClient service!");
168
169
hr = IAudioClient_GetBufferSize(args->client, &frames);
170
FAudio_assert(!FAILED(hr) && "Failed to get IAudioClient buffer size!");
171
172
hr = FAudio_FillAudioClientBuffer(args, render_client, frames, 0);
173
FAudio_assert(!FAILED(hr) && "Failed to initialize IAudioClient buffer!");
174
175
hr = IAudioClient_Start(args->client);
176
FAudio_assert(!FAILED(hr) && "Failed to start IAudioClient!");
177
178
while (WaitForMultipleObjects(2, args->events, FALSE, INFINITE) == WAIT_OBJECT_0)
179
{
180
hr = IAudioClient_GetCurrentPadding(args->client, &padding);
181
if (hr == AUDCLNT_E_DEVICE_INVALIDATED)
182
{
183
/* Device was removed, just exit */
184
break;
185
}
186
FAudio_assert(!FAILED(hr) && "Failed to get IAudioClient current padding!");
187
188
hr = FAudio_FillAudioClientBuffer(args, render_client, frames, padding);
189
FAudio_assert(!FAILED(hr) && "Failed to fill IAudioClient buffer!");
190
}
191
192
hr = IAudioClient_Stop(args->client);
193
FAudio_assert(!FAILED(hr) && "Failed to stop IAudioClient!");
194
195
IAudioRenderClient_Release(render_client);
196
FAudio_free(args);
197
return 0;
198
}
199
200
/* Sets `defaultDeviceIndex` to the default audio device index in
201
* `deviceCollection`.
202
* On failure, `defaultDeviceIndex` is not modified and the latest error is
203
* returned. */
204
static HRESULT FAudio_DefaultDeviceIndex(
205
IMMDeviceCollection *deviceCollection,
206
uint32_t* defaultDeviceIndex
207
) {
208
IMMDevice *device;
209
HRESULT hr;
210
uint32_t i, count;
211
WCHAR *default_guid;
212
WCHAR *device_guid;
213
214
/* Open the default device and get its GUID. */
215
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(
216
device_enumerator,
217
eRender,
218
eConsole,
219
&device
220
);
221
if (FAILED(hr))
222
{
223
return hr;
224
}
225
hr = IMMDevice_GetId(device, &default_guid);
226
if (FAILED(hr))
227
{
228
IMMDevice_Release(device);
229
return hr;
230
}
231
232
/* Free the default device. */
233
IMMDevice_Release(device);
234
235
hr = IMMDeviceCollection_GetCount(deviceCollection, &count);
236
if (FAILED(hr))
237
{
238
CoTaskMemFree(default_guid);
239
return hr;
240
}
241
242
for (i = 0; i < count; i += 1)
243
{
244
/* Open the device and get its GUID. */
245
hr = IMMDeviceCollection_Item(deviceCollection, i, &device);
246
if (FAILED(hr)) {
247
CoTaskMemFree(default_guid);
248
return hr;
249
}
250
hr = IMMDevice_GetId(device, &device_guid);
251
if (FAILED(hr))
252
{
253
CoTaskMemFree(default_guid);
254
IMMDevice_Release(device);
255
return hr;
256
}
257
258
if (lstrcmpW(default_guid, device_guid) == 0)
259
{
260
/* Device found. */
261
CoTaskMemFree(default_guid);
262
CoTaskMemFree(device_guid);
263
IMMDevice_Release(device);
264
*defaultDeviceIndex = i;
265
return S_OK;
266
}
267
268
CoTaskMemFree(device_guid);
269
IMMDevice_Release(device);
270
}
271
272
/* This should probably never happen. Just in case, set
273
* `defaultDeviceIndex` to 0 and return S_OK. */
274
CoTaskMemFree(default_guid);
275
*defaultDeviceIndex = 0;
276
return S_OK;
277
}
278
279
/* Open `device`, corresponding to `deviceIndex`. `deviceIndex` 0 always
280
* corresponds to the default device. XAudio reorders the devices so that the
281
* default device is always at index 0, so we mimick this behavior here by
282
* swapping the devices at indexes 0 and `defaultDeviceIndex`.
283
*/
284
static HRESULT FAudio_OpenDevice(uint32_t deviceIndex, IMMDevice **device)
285
{
286
IMMDeviceCollection *deviceCollection;
287
HRESULT hr;
288
uint32_t defaultDeviceIndex;
289
uint32_t actualIndex;
290
291
*device = NULL;
292
293
hr = IMMDeviceEnumerator_EnumAudioEndpoints(
294
device_enumerator,
295
eRender,
296
DEVICE_STATE_ACTIVE,
297
&deviceCollection
298
);
299
if (FAILED(hr))
300
{
301
return hr;
302
}
303
304
/* Get the default device index. */
305
hr = FAudio_DefaultDeviceIndex(deviceCollection, &defaultDeviceIndex);
306
if (FAILED(hr))
307
{
308
IMMDeviceCollection_Release(deviceCollection);
309
return hr;
310
}
311
312
if (deviceIndex == 0) {
313
/* Default device. */
314
actualIndex = defaultDeviceIndex;
315
} else if (deviceIndex == defaultDeviceIndex) {
316
/* Open the device at index 0 instead of the "correct" one. */
317
actualIndex = 0;
318
} else {
319
/* Otherwise, just open the device. */
320
actualIndex = deviceIndex;
321
322
}
323
hr = IMMDeviceCollection_Item(deviceCollection, actualIndex, device);
324
if (FAILED(hr))
325
{
326
IMMDeviceCollection_Release(deviceCollection);
327
return hr;
328
}
329
330
IMMDeviceCollection_Release(deviceCollection);
331
332
return hr;
333
}
334
335
void FAudio_PlatformInit(
336
FAudio *audio,
337
uint32_t flags,
338
uint32_t deviceIndex,
339
FAudioWaveFormatExtensible *mixFormat,
340
uint32_t *updateSize,
341
void** platformDevice
342
) {
343
struct FAudioAudioClientThreadArgs *args;
344
struct FAudioWin32PlatformData *data;
345
REFERENCE_TIME duration;
346
WAVEFORMATEX *closest;
347
IMMDevice *device = NULL;
348
HRESULT hr;
349
HANDLE audioEvent = NULL;
350
BOOL has_sse2 = IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE);
351
#if defined(__aarch64__) || defined(_M_ARM64) || defined(__arm64ec__) || defined(_M_ARM64EC)
352
BOOL has_neon = TRUE;
353
#elif defined(__arm__) || defined(_M_ARM)
354
BOOL has_neon = IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE);
355
#else
356
BOOL has_neon = FALSE;
357
#endif
358
FAudio_INTERNAL_InitSIMDFunctions(has_sse2, has_neon);
359
FAudio_resolve_SetThreadDescription();
360
361
FAudio_PlatformAddRef();
362
363
*platformDevice = NULL;
364
365
args = FAudio_malloc(sizeof(*args));
366
FAudio_assert(!!args && "Failed to allocate FAudio thread args!");
367
368
data = FAudio_malloc(sizeof(*data));
369
FAudio_assert(!!data && "Failed to allocate FAudio platform data!");
370
FAudio_zero(data, sizeof(*data));
371
372
args->format.Format.wFormatTag = mixFormat->Format.wFormatTag;
373
args->format.Format.nChannels = mixFormat->Format.nChannels;
374
args->format.Format.nSamplesPerSec = mixFormat->Format.nSamplesPerSec;
375
args->format.Format.nAvgBytesPerSec = mixFormat->Format.nAvgBytesPerSec;
376
args->format.Format.nBlockAlign = mixFormat->Format.nBlockAlign;
377
args->format.Format.wBitsPerSample = mixFormat->Format.wBitsPerSample;
378
args->format.Format.cbSize = mixFormat->Format.cbSize;
379
380
if (args->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
381
{
382
args->format.Samples.wValidBitsPerSample = mixFormat->Samples.wValidBitsPerSample;
383
args->format.dwChannelMask = mixFormat->dwChannelMask;
384
FAudio_memcpy(
385
&args->format.SubFormat,
386
&mixFormat->SubFormat,
387
sizeof(GUID)
388
);
389
}
390
391
audioEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
392
FAudio_assert(!!audioEvent && "Failed to create FAudio thread buffer event!");
393
394
data->stopEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
395
FAudio_assert(!!data->stopEvent && "Failed to create FAudio thread stop event!");
396
397
hr = FAudio_OpenDevice(deviceIndex, &device);
398
FAudio_assert(!FAILED(hr) && "Failed to get audio device!");
399
400
hr = IMMDevice_Activate(
401
device,
402
&IID_IAudioClient,
403
CLSCTX_ALL,
404
NULL,
405
(void **)&data->client
406
);
407
FAudio_assert(!FAILED(hr) && "Failed to create audio client!");
408
IMMDevice_Release(device);
409
410
if (flags & FAUDIO_1024_QUANTUM) duration = 213333;
411
else duration = 100000;
412
413
hr = IAudioClient_IsFormatSupported(
414
data->client,
415
AUDCLNT_SHAREMODE_SHARED,
416
&args->format.Format,
417
&closest
418
);
419
FAudio_assert(!FAILED(hr) && "Failed to find supported audio format!");
420
421
if (closest)
422
{
423
if (closest->wFormatTag != WAVE_FORMAT_EXTENSIBLE) args->format.Format = *closest;
424
else args->format = *(WAVEFORMATEXTENSIBLE *)closest;
425
CoTaskMemFree(closest);
426
}
427
428
hr = IAudioClient_Initialize(
429
data->client,
430
AUDCLNT_SHAREMODE_SHARED,
431
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
432
duration * 3,
433
0,
434
&args->format.Format,
435
&GUID_NULL
436
);
437
FAudio_assert(!FAILED(hr) && "Failed to initialize audio client!");
438
439
hr = IAudioClient_SetEventHandle(data->client, audioEvent);
440
FAudio_assert(!FAILED(hr) && "Failed to set audio client event!");
441
442
mixFormat->Format.wFormatTag = args->format.Format.wFormatTag;
443
mixFormat->Format.nChannels = args->format.Format.nChannels;
444
mixFormat->Format.nSamplesPerSec = args->format.Format.nSamplesPerSec;
445
mixFormat->Format.nAvgBytesPerSec = args->format.Format.nAvgBytesPerSec;
446
mixFormat->Format.nBlockAlign = args->format.Format.nBlockAlign;
447
mixFormat->Format.wBitsPerSample = args->format.Format.wBitsPerSample;
448
449
if (args->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
450
{
451
mixFormat->Format.cbSize = sizeof(FAudioWaveFormatExtensible) - sizeof(FAudioWaveFormatEx);
452
mixFormat->Samples.wValidBitsPerSample = args->format.Samples.wValidBitsPerSample;
453
mixFormat->dwChannelMask = args->format.dwChannelMask;
454
FAudio_memcpy(
455
&mixFormat->SubFormat,
456
&args->format.SubFormat,
457
sizeof(GUID)
458
);
459
}
460
else
461
{
462
mixFormat->Format.cbSize = sizeof(FAudioWaveFormatEx);
463
}
464
465
args->client = data->client;
466
args->events[0] = audioEvent;
467
args->events[1] = data->stopEvent;
468
args->audio = audio;
469
if (flags & FAUDIO_1024_QUANTUM) args->updateSize = (UINT)(args->format.Format.nSamplesPerSec / (1000.0 / (64.0 / 3.0)));
470
else args->updateSize = args->format.Format.nSamplesPerSec / 100;
471
472
data->audioThread = CreateThread(NULL, 0, &FAudio_AudioClientThread, args, 0, NULL);
473
FAudio_assert(!!data->audioThread && "Failed to create audio client thread!");
474
475
*updateSize = args->updateSize;
476
*platformDevice = data;
477
return;
478
}
479
480
void FAudio_PlatformQuit(void* platformDevice)
481
{
482
struct FAudioWin32PlatformData *data = platformDevice;
483
484
SetEvent(data->stopEvent);
485
WaitForSingleObject(data->audioThread, INFINITE);
486
if (data->client) IAudioClient_Release(data->client);
487
if (kernelbase)
488
{
489
my_SetThreadDescription = NULL;
490
FreeLibrary(kernelbase);
491
kernelbase = NULL;
492
}
493
FAudio_PlatformRelease();
494
}
495
496
void FAudio_PlatformAddRef()
497
{
498
HRESULT hr;
499
EnterCriticalSection(&faudio_cs);
500
if (!device_enumerator)
501
{
502
init_hr = CoInitialize(NULL);
503
hr = CoCreateInstance(
504
&CLSID_MMDeviceEnumerator,
505
NULL,
506
CLSCTX_INPROC_SERVER,
507
&IID_IMMDeviceEnumerator,
508
(void**)&device_enumerator
509
);
510
FAudio_assert(!FAILED(hr) && "CoCreateInstance failed!");
511
}
512
else IMMDeviceEnumerator_AddRef(device_enumerator);
513
LeaveCriticalSection(&faudio_cs);
514
}
515
516
void FAudio_PlatformRelease()
517
{
518
EnterCriticalSection(&faudio_cs);
519
if (!IMMDeviceEnumerator_Release(device_enumerator))
520
{
521
device_enumerator = NULL;
522
if (SUCCEEDED(init_hr)) CoUninitialize();
523
}
524
LeaveCriticalSection(&faudio_cs);
525
}
526
527
uint32_t FAudio_PlatformGetDeviceCount(void)
528
{
529
IMMDeviceCollection *device_collection;
530
uint32_t count;
531
HRESULT hr;
532
533
FAudio_PlatformAddRef();
534
535
hr = IMMDeviceEnumerator_EnumAudioEndpoints(
536
device_enumerator,
537
eRender,
538
DEVICE_STATE_ACTIVE,
539
&device_collection
540
);
541
if (FAILED(hr)) {
542
FAudio_PlatformRelease();
543
return 0;
544
}
545
546
hr = IMMDeviceCollection_GetCount(device_collection, &count);
547
if (FAILED(hr)) {
548
IMMDeviceCollection_Release(device_collection);
549
FAudio_PlatformRelease();
550
return 0;
551
}
552
553
IMMDeviceCollection_Release(device_collection);
554
555
FAudio_PlatformRelease();
556
557
return count;
558
}
559
560
uint32_t FAudio_PlatformGetDeviceDetails(
561
uint32_t index,
562
FAudioDeviceDetails *details
563
) {
564
WAVEFORMATEX *format, *obtained;
565
WAVEFORMATEXTENSIBLE *ext;
566
IAudioClient *client;
567
IMMDevice *device;
568
IPropertyStore* properties;
569
PROPVARIANT deviceName;
570
uint32_t count = 0;
571
uint32_t ret = 0;
572
HRESULT hr;
573
WCHAR *str;
574
GUID sub;
575
576
FAudio_memset(details, 0, sizeof(FAudioDeviceDetails));
577
578
FAudio_PlatformAddRef();
579
580
count = FAudio_PlatformGetDeviceCount();
581
if (index >= count)
582
{
583
FAudio_PlatformRelease();
584
return FAUDIO_E_INVALID_CALL;
585
}
586
587
hr = FAudio_OpenDevice(index, &device);
588
FAudio_assert(!FAILED(hr) && "Failed to get audio endpoint!");
589
590
if (index == 0)
591
{
592
details->Role = FAudioGlobalDefaultDevice;
593
}
594
else
595
{
596
details->Role = FAudioNotDefaultDevice;
597
}
598
599
/* Set the Device Display Name */
600
hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &properties);
601
FAudio_assert(!FAILED(hr) && "Failed to open device property store!");
602
hr = IPropertyStore_GetValue(properties, (PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &deviceName);
603
FAudio_assert(!FAILED(hr) && "Failed to get audio device friendly name!");
604
lstrcpynW((LPWSTR)details->DisplayName, deviceName.pwszVal, ARRAYSIZE(details->DisplayName) - 1);
605
PropVariantClear(&deviceName);
606
IPropertyStore_Release(properties);
607
608
/* Set the Device ID */
609
hr = IMMDevice_GetId(device, &str);
610
FAudio_assert(!FAILED(hr) && "Failed to get audio endpoint id!");
611
lstrcpynW((LPWSTR)details->DeviceID, str, ARRAYSIZE(details->DeviceID) - 1);
612
CoTaskMemFree(str);
613
614
hr = IMMDevice_Activate(
615
device,
616
&IID_IAudioClient,
617
CLSCTX_ALL,
618
NULL,
619
(void **)&client
620
);
621
FAudio_assert(!FAILED(hr) && "Failed to activate audio client!");
622
623
hr = IAudioClient_GetMixFormat(client, &format);
624
FAudio_assert(!FAILED(hr) && "Failed to get audio client mix format!");
625
626
if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
627
{
628
ext = (WAVEFORMATEXTENSIBLE *)format;
629
sub = ext->SubFormat;
630
FAudio_memcpy(
631
&ext->SubFormat,
632
&DATAFORMAT_SUBTYPE_PCM,
633
sizeof(GUID)
634
);
635
636
hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, format, &obtained);
637
if (FAILED(hr))
638
{
639
ext->SubFormat = sub;
640
}
641
else if (obtained)
642
{
643
CoTaskMemFree(format);
644
format = obtained;
645
}
646
}
647
648
details->OutputFormat.Format.wFormatTag = format->wFormatTag;
649
details->OutputFormat.Format.nChannels = format->nChannels;
650
details->OutputFormat.Format.nSamplesPerSec = format->nSamplesPerSec;
651
details->OutputFormat.Format.nAvgBytesPerSec = format->nAvgBytesPerSec;
652
details->OutputFormat.Format.nBlockAlign = format->nBlockAlign;
653
details->OutputFormat.Format.wBitsPerSample = format->wBitsPerSample;
654
details->OutputFormat.Format.cbSize = format->cbSize;
655
656
if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
657
{
658
ext = (WAVEFORMATEXTENSIBLE *)format;
659
details->OutputFormat.Samples.wValidBitsPerSample = ext->Samples.wValidBitsPerSample;
660
details->OutputFormat.dwChannelMask = ext->dwChannelMask;
661
FAudio_memcpy(
662
&details->OutputFormat.SubFormat,
663
&ext->SubFormat,
664
sizeof(GUID)
665
);
666
}
667
else
668
{
669
details->OutputFormat.dwChannelMask = GetMask(format->nChannels);
670
}
671
672
CoTaskMemFree(format);
673
674
IAudioClient_Release(client);
675
676
IMMDevice_Release(device);
677
678
FAudio_PlatformRelease();
679
680
return ret;
681
}
682
683
FAudioMutex FAudio_PlatformCreateMutex(void)
684
{
685
CRITICAL_SECTION *cs;
686
687
cs = FAudio_malloc(sizeof(CRITICAL_SECTION));
688
if (!cs) return NULL;
689
690
InitializeCriticalSection(cs);
691
692
return cs;
693
}
694
695
void FAudio_PlatformLockMutex(FAudioMutex mutex)
696
{
697
if (mutex) EnterCriticalSection(mutex);
698
}
699
700
void FAudio_PlatformUnlockMutex(FAudioMutex mutex)
701
{
702
if (mutex) LeaveCriticalSection(mutex);
703
}
704
705
void FAudio_PlatformDestroyMutex(FAudioMutex mutex)
706
{
707
if (mutex) DeleteCriticalSection(mutex);
708
FAudio_free(mutex);
709
}
710
711
struct FAudioThreadArgs
712
{
713
FAudioThreadFunc func;
714
const char *name;
715
void* data;
716
};
717
718
static DWORD WINAPI FaudioThreadWrapper(void *user)
719
{
720
struct FAudioThreadArgs *args = user;
721
DWORD ret;
722
723
FAudio_set_thread_name(args->name);
724
ret = args->func(args->data);
725
726
FAudio_free(args);
727
return ret;
728
}
729
730
FAudioThread FAudio_PlatformCreateThread(
731
FAudioThreadFunc func,
732
const char *name,
733
void* data
734
) {
735
struct FAudioThreadArgs *args;
736
737
if (!(args = FAudio_malloc(sizeof(*args)))) return NULL;
738
args->func = func;
739
args->name = name;
740
args->data = data;
741
742
return CreateThread(NULL, 0, &FaudioThreadWrapper, args, 0, NULL);
743
}
744
745
void FAudio_PlatformWaitThread(FAudioThread thread, int32_t *retval)
746
{
747
WaitForSingleObject(thread, INFINITE);
748
if (retval != NULL) GetExitCodeThread(thread, (DWORD *)retval);
749
}
750
751
void FAudio_PlatformThreadPriority(FAudioThreadPriority priority)
752
{
753
/* FIXME */
754
}
755
756
uint64_t FAudio_PlatformGetThreadID(void)
757
{
758
return GetCurrentThreadId();
759
}
760
761
void FAudio_sleep(uint32_t ms)
762
{
763
Sleep(ms);
764
}
765
766
uint32_t FAudio_timems()
767
{
768
return GetTickCount();
769
}
770
771
/* FAudio I/O */
772
773
static size_t FAUDIOCALL FAudio_FILE_read(
774
void *data,
775
void *dst,
776
size_t size,
777
size_t count
778
) {
779
if (!data) return 0;
780
return fread(dst, size, count, data);
781
}
782
783
static int64_t FAUDIOCALL FAudio_FILE_seek(
784
void *data,
785
int64_t offset,
786
int whence
787
) {
788
if (!data) return -1;
789
fseek(data, (long)offset, whence);
790
return ftell(data);
791
}
792
793
static int FAUDIOCALL FAudio_FILE_close(void *data)
794
{
795
if (!data) return 0;
796
fclose(data);
797
return 0;
798
}
799
800
FAudioIOStream* FAudio_fopen(const char *path)
801
{
802
FAudioIOStream *io;
803
errno_t err = -1;
804
805
io = (FAudioIOStream*) FAudio_malloc(sizeof(FAudioIOStream));
806
if (!io) return NULL;
807
808
err = fopen_s((FILE**)&(io->data), path, "rb");
809
810
if (err != 0 || io->data == NULL)
811
{
812
FAudio_free(io);
813
return NULL;
814
}
815
816
io->read = FAudio_FILE_read;
817
io->seek = FAudio_FILE_seek;
818
io->close = FAudio_FILE_close;
819
io->lock = FAudio_PlatformCreateMutex();
820
821
return io;
822
}
823
824
struct FAudio_mem
825
{
826
char *mem;
827
int64_t len;
828
int64_t pos;
829
};
830
831
static size_t FAUDIOCALL FAudio_mem_read(
832
void *data,
833
void *dst,
834
size_t size,
835
size_t count
836
) {
837
struct FAudio_mem *io = data;
838
size_t len = size * count;
839
840
if (!data) return 0;
841
842
while (len && len > (UINT)(io->len - io->pos)) len -= size;
843
FAudio_memcpy(dst, io->mem + io->pos, len);
844
io->pos += len;
845
846
return len;
847
}
848
849
static int64_t FAUDIOCALL FAudio_mem_seek(
850
void *data,
851
int64_t offset,
852
int whence
853
) {
854
struct FAudio_mem *io = data;
855
if (!data) return -1;
856
857
if (whence == SEEK_SET)
858
{
859
if (io->len > offset) io->pos = offset;
860
else io->pos = io->len;
861
}
862
if (whence == SEEK_CUR)
863
{
864
if (io->len > io->pos + offset) io->pos += offset;
865
else io->pos = io->len;
866
}
867
if (whence == SEEK_END)
868
{
869
if (io->len > offset) io->pos = io->len - offset;
870
else io->pos = 0;
871
}
872
873
return io->pos;
874
}
875
876
static int FAUDIOCALL FAudio_mem_close(void *data)
877
{
878
if (!data) return 0;
879
FAudio_free(data);
880
return 0;
881
}
882
883
FAudioIOStream* FAudio_memopen(void *mem, int len)
884
{
885
struct FAudio_mem *data;
886
FAudioIOStream *io;
887
888
io = (FAudioIOStream*) FAudio_malloc(sizeof(FAudioIOStream));
889
if (!io) return NULL;
890
891
data = FAudio_malloc(sizeof(struct FAudio_mem));
892
if (!data)
893
{
894
FAudio_free(io);
895
return NULL;
896
}
897
898
data->mem = mem;
899
data->len = len;
900
data->pos = 0;
901
902
io->data = data;
903
io->read = FAudio_mem_read;
904
io->seek = FAudio_mem_seek;
905
io->close = FAudio_mem_close;
906
io->lock = FAudio_PlatformCreateMutex();
907
return io;
908
}
909
910
uint8_t* FAudio_memptr(FAudioIOStream *io, size_t offset)
911
{
912
struct FAudio_mem *memio = io->data;
913
return (uint8_t *)memio->mem + offset;
914
}
915
916
void FAudio_close(FAudioIOStream *io)
917
{
918
io->close(io->data);
919
FAudio_PlatformDestroyMutex((FAudioMutex) io->lock);
920
FAudio_free(io);
921
}
922
923
/* XNA Song implementation over Win32 MF */
924
925
static FAudioWaveFormatEx activeSongFormat;
926
IMFSourceReader *activeSong;
927
static uint8_t *songBuffer;
928
static SIZE_T songBufferSize;
929
930
static float songVolume = 1.0f;
931
static FAudio *songAudio = NULL;
932
static FAudioMasteringVoice *songMaster = NULL;
933
934
static FAudioSourceVoice *songVoice = NULL;
935
static FAudioVoiceCallback callbacks;
936
937
/* Internal Functions */
938
939
static void XNA_SongSubmitBuffer(FAudioVoiceCallback *callback, void *pBufferContext)
940
{
941
IMFMediaBuffer *media_buffer;
942
FAudioBuffer buffer;
943
IMFSample *sample;
944
HRESULT hr;
945
DWORD flags, buffer_size = 0;
946
BYTE *buffer_ptr;
947
948
LOG_FUNC_ENTER(songAudio);
949
950
FAudio_memset(&buffer, 0, sizeof(buffer));
951
952
hr = IMFSourceReader_ReadSample(
953
activeSong,
954
MF_SOURCE_READER_FIRST_AUDIO_STREAM,
955
0,
956
NULL,
957
&flags,
958
NULL,
959
&sample
960
);
961
FAudio_assert(!FAILED(hr) && "Failed to read audio sample!");
962
963
if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
964
{
965
buffer.Flags = FAUDIO_END_OF_STREAM;
966
}
967
else
968
{
969
hr = IMFSample_ConvertToContiguousBuffer(
970
sample,
971
&media_buffer
972
);
973
FAudio_assert(!FAILED(hr) && "Failed to get sample buffer!");
974
975
hr = IMFMediaBuffer_Lock(
976
media_buffer,
977
&buffer_ptr,
978
NULL,
979
&buffer_size
980
);
981
FAudio_assert(!FAILED(hr) && "Failed to lock buffer bytes!");
982
983
if (songBufferSize < buffer_size)
984
{
985
songBufferSize = buffer_size;
986
songBuffer = FAudio_realloc(songBuffer, songBufferSize);
987
FAudio_assert(songBuffer != NULL && "Failed to allocate song buffer!");
988
}
989
FAudio_memcpy(songBuffer, buffer_ptr, buffer_size);
990
991
hr = IMFMediaBuffer_Unlock(media_buffer);
992
FAudio_assert(!FAILED(hr) && "Failed to unlock buffer bytes!");
993
994
IMFMediaBuffer_Release(media_buffer);
995
IMFSample_Release(sample);
996
}
997
998
if (buffer_size > 0)
999
{
1000
buffer.AudioBytes = buffer_size;
1001
buffer.pAudioData = songBuffer;
1002
buffer.PlayBegin = 0;
1003
buffer.PlayLength = buffer_size / activeSongFormat.nBlockAlign;
1004
buffer.LoopBegin = 0;
1005
buffer.LoopLength = 0;
1006
buffer.LoopCount = 0;
1007
buffer.pContext = NULL;
1008
FAudioSourceVoice_SubmitSourceBuffer(
1009
songVoice,
1010
&buffer,
1011
NULL
1012
);
1013
}
1014
1015
LOG_FUNC_EXIT(songAudio);
1016
}
1017
1018
static void XNA_SongKill()
1019
{
1020
if (songVoice != NULL)
1021
{
1022
FAudioSourceVoice_Stop(songVoice, 0, 0);
1023
FAudioVoice_DestroyVoice(songVoice);
1024
songVoice = NULL;
1025
}
1026
if (activeSong)
1027
{
1028
IMFSourceReader_Release(activeSong);
1029
activeSong = NULL;
1030
}
1031
FAudio_free(songBuffer);
1032
songBuffer = NULL;
1033
songBufferSize = 0;
1034
}
1035
1036
/* "Public" API */
1037
1038
FAUDIOAPI void XNA_SongInit()
1039
{
1040
HRESULT hr;
1041
1042
hr = MFStartup(MF_VERSION, MFSTARTUP_FULL);
1043
FAudio_assert(!FAILED(hr) && "Failed to initialize Media Foundation!");
1044
1045
FAudioCreate(&songAudio, 0, FAUDIO_DEFAULT_PROCESSOR);
1046
FAudio_CreateMasteringVoice(
1047
songAudio,
1048
&songMaster,
1049
FAUDIO_DEFAULT_CHANNELS,
1050
FAUDIO_DEFAULT_SAMPLERATE,
1051
0,
1052
0,
1053
NULL
1054
);
1055
}
1056
1057
FAUDIOAPI void XNA_SongQuit()
1058
{
1059
XNA_SongKill();
1060
FAudioVoice_DestroyVoice(songMaster);
1061
FAudio_Release(songAudio);
1062
MFShutdown();
1063
}
1064
1065
FAUDIOAPI float XNA_PlaySong(const char *name)
1066
{
1067
IMFAttributes *attributes = NULL;
1068
IMFMediaType *media_type = NULL;
1069
UINT32 channels, samplerate;
1070
INT64 duration;
1071
PROPVARIANT var;
1072
HRESULT hr;
1073
WCHAR filename_w[MAX_PATH];
1074
1075
LOG_FUNC_ENTER(songAudio);
1076
LOG_INFO(songAudio, "name %s\n", name);
1077
XNA_SongKill();
1078
1079
MultiByteToWideChar(CP_UTF8, 0, name, -1, filename_w, MAX_PATH);
1080
1081
hr = MFCreateAttributes(&attributes, 1);
1082
FAudio_assert(!FAILED(hr) && "Failed to create attributes!");
1083
hr = MFCreateSourceReaderFromURL(
1084
filename_w,
1085
attributes,
1086
&activeSong
1087
);
1088
FAudio_assert(!FAILED(hr) && "Failed to create source reader!");
1089
IMFAttributes_Release(attributes);
1090
1091
hr = MFCreateMediaType(&media_type);
1092
FAudio_assert(!FAILED(hr) && "Failed to create media type!");
1093
hr = IMFMediaType_SetGUID(
1094
media_type,
1095
&MF_MT_MAJOR_TYPE,
1096
&MFMediaType_Audio
1097
);
1098
FAudio_assert(!FAILED(hr) && "Failed to set major type!");
1099
hr = IMFMediaType_SetGUID(
1100
media_type,
1101
&MF_MT_SUBTYPE,
1102
&MFAudioFormat_Float
1103
);
1104
FAudio_assert(!FAILED(hr) && "Failed to set sub type!");
1105
hr = IMFSourceReader_SetCurrentMediaType(
1106
activeSong,
1107
MF_SOURCE_READER_FIRST_AUDIO_STREAM,
1108
NULL,
1109
media_type
1110
);
1111
FAudio_assert(!FAILED(hr) && "Failed to set source media type!");
1112
hr = IMFSourceReader_SetStreamSelection(
1113
activeSong,
1114
MF_SOURCE_READER_FIRST_AUDIO_STREAM,
1115
TRUE
1116
);
1117
FAudio_assert(!FAILED(hr) && "Failed to select source stream!");
1118
IMFMediaType_Release(media_type);
1119
1120
hr = IMFSourceReader_GetCurrentMediaType(
1121
activeSong,
1122
MF_SOURCE_READER_FIRST_AUDIO_STREAM,
1123
&media_type
1124
);
1125
FAudio_assert(!FAILED(hr) && "Failed to get current media type!");
1126
hr = IMFMediaType_GetUINT32(
1127
media_type,
1128
&MF_MT_AUDIO_NUM_CHANNELS,
1129
&channels
1130
);
1131
FAudio_assert(!FAILED(hr) && "Failed to get channel count!");
1132
hr = IMFMediaType_GetUINT32(
1133
media_type,
1134
&MF_MT_AUDIO_SAMPLES_PER_SECOND,
1135
&samplerate
1136
);
1137
FAudio_assert(!FAILED(hr) && "Failed to get sample rate!");
1138
IMFMediaType_Release(media_type);
1139
1140
hr = IMFSourceReader_GetPresentationAttribute(
1141
activeSong,
1142
MF_SOURCE_READER_MEDIASOURCE,
1143
&MF_PD_DURATION,
1144
&var
1145
);
1146
FAudio_assert(!FAILED(hr) && "Failed to get song duration!");
1147
hr = PropVariantToInt64(&var, &duration);
1148
FAudio_assert(!FAILED(hr) && "Failed to get song duration!");
1149
PropVariantClear(&var);
1150
1151
activeSongFormat.wFormatTag = FAUDIO_FORMAT_IEEE_FLOAT;
1152
activeSongFormat.nChannels = channels;
1153
activeSongFormat.nSamplesPerSec = samplerate;
1154
activeSongFormat.wBitsPerSample = sizeof(float) * 8;
1155
activeSongFormat.nBlockAlign = activeSongFormat.nChannels * activeSongFormat.wBitsPerSample / 8;
1156
activeSongFormat.nAvgBytesPerSec = activeSongFormat.nSamplesPerSec * activeSongFormat.nBlockAlign;
1157
activeSongFormat.cbSize = 0;
1158
1159
/* Init voice */
1160
FAudio_zero(&callbacks, sizeof(FAudioVoiceCallback));
1161
callbacks.OnBufferEnd = XNA_SongSubmitBuffer;
1162
FAudio_CreateSourceVoice(
1163
songAudio,
1164
&songVoice,
1165
&activeSongFormat,
1166
0,
1167
1.0f, /* No pitch shifting here! */
1168
&callbacks,
1169
NULL,
1170
NULL
1171
);
1172
FAudioVoice_SetVolume(songVoice, songVolume, 0);
1173
XNA_SongSubmitBuffer(NULL, NULL);
1174
1175
/* Finally. */
1176
FAudioSourceVoice_Start(songVoice, 0, 0);
1177
LOG_FUNC_EXIT(songAudio);
1178
return (float)(duration / 10000000.);
1179
}
1180
1181
FAUDIOAPI void XNA_PauseSong()
1182
{
1183
if (songVoice == NULL)
1184
{
1185
return;
1186
}
1187
FAudioSourceVoice_Stop(songVoice, 0, 0);
1188
}
1189
1190
FAUDIOAPI void XNA_ResumeSong()
1191
{
1192
if (songVoice == NULL)
1193
{
1194
return;
1195
}
1196
FAudioSourceVoice_Start(songVoice, 0, 0);
1197
}
1198
1199
FAUDIOAPI void XNA_StopSong()
1200
{
1201
XNA_SongKill();
1202
}
1203
1204
FAUDIOAPI void XNA_SetSongVolume(float volume)
1205
{
1206
songVolume = volume;
1207
if (songVoice != NULL)
1208
{
1209
FAudioVoice_SetVolume(songVoice, songVolume, 0);
1210
}
1211
}
1212
1213
FAUDIOAPI uint32_t XNA_GetSongEnded()
1214
{
1215
FAudioVoiceState state;
1216
if (songVoice == NULL || activeSong == NULL)
1217
{
1218
return 1;
1219
}
1220
FAudioSourceVoice_GetState(songVoice, &state, 0);
1221
return state.BuffersQueued == 0 && state.SamplesPlayed == 0;
1222
}
1223
1224
FAUDIOAPI void XNA_EnableVisualization(uint32_t enable)
1225
{
1226
/* TODO: Enable/Disable FAPO effect */
1227
}
1228
1229
FAUDIOAPI uint32_t XNA_VisualizationEnabled()
1230
{
1231
/* TODO: Query FAPO effect enabled */
1232
return 0;
1233
}
1234
1235
FAUDIOAPI void XNA_GetSongVisualizationData(
1236
float *frequencies,
1237
float *samples,
1238
uint32_t count
1239
) {
1240
/* TODO: Visualization FAPO that reads in Song samples, FFT analysis */
1241
}
1242
1243
#else
1244
1245
extern int this_tu_is_empty;
1246
1247
#endif /* FAUDIO_WIN32_PLATFORM */
1248
1249