1 /* RetroArch - A frontend for libretro.
2 * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
3 * Copyright (C) 2011-2017 - Daniel De Matteis
4 *
5 * RetroArch is free software: you can redistribute it and/or modify it under the terms
6 * of the GNU General Public License as published by the Free Software Found-
7 * ation, either version 3 of the License, or (at your option) any later version.
8 *
9 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along with RetroArch.
14 * If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 #if !defined(_XBOX) && (_MSC_VER == 1310)
18 #ifndef _WIN32_DCOM
19 #define _WIN32_DCOM
20 #endif
21 #endif
22
23 #include <stdint.h>
24 #include <stddef.h>
25 #include <stdlib.h>
26 #include <boolean.h>
27
28 #include <compat/msvc.h>
29 #include <retro_miscellaneous.h>
30 #include <string/stdstring.h>
31 #include <encodings/utf.h>
32 #include <lists/string_list.h>
33
34 #if defined(_MSC_VER) && (_WIN32_WINNT <= _WIN32_WINNT_WIN2K)
35 /* needed for CoInitializeEx */
36 #define _WIN32_DCOM
37 #endif
38
39 #include "xaudio.h"
40
41 #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
42 #include "../common/mmdevice_common.h"
43 #endif
44
45 #include "../../retroarch.h"
46 #include "../../verbosity.h"
47
48 typedef struct xaudio2 xaudio2_t;
49
50 #define MAX_BUFFERS 16
51
52 #define MAX_BUFFERS_MASK (MAX_BUFFERS - 1)
53
54 #ifndef COINIT_MULTITHREADED
55 #define COINIT_MULTITHREADED 0x00
56 #endif
57
58 #define XAUDIO2_WRITE_AVAILABLE(handle) ((handle)->bufsize * (MAX_BUFFERS - (handle)->buffers - 1))
59
60 typedef struct
61 {
62 xaudio2_t *xa;
63 size_t bufsize;
64 bool nonblock;
65 bool is_paused;
66 } xa_t;
67
68 /* Forward declarations */
69 static void *xa_list_new(void *u);
70
71 #if defined(__cplusplus) && !defined(CINTERFACE)
72 struct xaudio2 : public IXAudio2VoiceCallback
73 #else
74 struct xaudio2
75 #endif
76 {
77 #if defined(__cplusplus) && !defined(CINTERFACE)
xaudio2xaudio278 xaudio2() :
79 buf(0), pXAudio2(0), pMasterVoice(0),
80 pSourceVoice(0), hEvent(0), buffers(0), bufsize(0),
81 bufptr(0), write_buffer(0)
82 {}
83
~xaudio2xaudio284 virtual ~xaudio2() {}
85
STDMETHOD_xaudio286 STDMETHOD_(void, OnBufferStart) (void *) {}
STDMETHOD_xaudio287 STDMETHOD_(void, OnBufferEnd) (void *)
88 {
89 InterlockedDecrement((LONG volatile*)&buffers);
90 SetEvent(hEvent);
91 }
STDMETHOD_xaudio292 STDMETHOD_(void, OnLoopEnd) (void *) {}
STDMETHOD_xaudio293 STDMETHOD_(void, OnStreamEnd) () {}
STDMETHOD_xaudio294 STDMETHOD_(void, OnVoiceError) (void *, HRESULT) {}
STDMETHOD_xaudio295 STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
STDMETHOD_xaudio296 STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) {}
97 #else
98 const IXAudio2VoiceCallbackVtbl *lpVtbl;
99 #endif
100
101 uint8_t *buf;
102 IXAudio2 *pXAudio2;
103 IXAudio2MasteringVoice *pMasterVoice;
104 IXAudio2SourceVoice *pSourceVoice;
105 HANDLE hEvent;
106
107 unsigned long volatile buffers;
108 unsigned bufsize;
109 unsigned bufptr;
110 unsigned write_buffer;
111 };
112
113 #if !defined(__cplusplus) || defined(CINTERFACE)
voice_on_buffer_end(IXAudio2VoiceCallback * handle_,void * data)114 static void WINAPI voice_on_buffer_end(IXAudio2VoiceCallback *handle_, void *data)
115 {
116 xaudio2_t *handle = (xaudio2_t*)handle_;
117 (void)data;
118 InterlockedDecrement((LONG volatile*)&handle->buffers);
119 SetEvent(handle->hEvent);
120 }
121
dummy_voidp(IXAudio2VoiceCallback * handle,void * data)122 static void WINAPI dummy_voidp(IXAudio2VoiceCallback *handle, void *data) { (void)handle; (void)data; }
dummy_nil(IXAudio2VoiceCallback * handle)123 static void WINAPI dummy_nil(IXAudio2VoiceCallback *handle) { (void)handle; }
dummy_uint32(IXAudio2VoiceCallback * handle,UINT32 dummy)124 static void WINAPI dummy_uint32(IXAudio2VoiceCallback *handle, UINT32 dummy) { (void)handle; (void)dummy; }
dummy_voidp_hresult(IXAudio2VoiceCallback * handle,void * data,HRESULT dummy)125 static void WINAPI dummy_voidp_hresult(IXAudio2VoiceCallback *handle, void *data, HRESULT dummy) { (void)handle; (void)data; (void)dummy; }
126
127 const struct IXAudio2VoiceCallbackVtbl voice_vtable = {
128 dummy_uint32,
129 dummy_nil,
130 dummy_nil,
131 dummy_voidp,
132 voice_on_buffer_end,
133 dummy_voidp,
134 dummy_voidp_hresult,
135 };
136 #endif
137
xaudio2_set_wavefmt(WAVEFORMATEX * wfx,unsigned channels,unsigned samplerate)138 static void xaudio2_set_wavefmt(WAVEFORMATEX *wfx,
139 unsigned channels, unsigned samplerate)
140 {
141 wfx->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
142 wfx->nBlockAlign = channels * sizeof(float);
143 wfx->wBitsPerSample = sizeof(float) * 8;
144
145 wfx->nChannels = channels;
146 wfx->nSamplesPerSec = samplerate;
147 wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
148 wfx->cbSize = 0;
149 }
150
xaudio2_free(xaudio2_t * handle)151 static void xaudio2_free(xaudio2_t *handle)
152 {
153 if (!handle)
154 return;
155
156 if (handle->pSourceVoice)
157 {
158 IXAudio2SourceVoice_Stop(handle->pSourceVoice,
159 0, XAUDIO2_COMMIT_NOW);
160 IXAudio2SourceVoice_DestroyVoice(handle->pSourceVoice);
161 }
162
163 if (handle->pMasterVoice)
164 {
165 IXAudio2MasteringVoice_DestroyVoice(handle->pMasterVoice);
166 }
167
168 if (handle->pXAudio2)
169 {
170 IXAudio2_Release(handle->pXAudio2);
171 }
172
173 if (handle->hEvent)
174 CloseHandle(handle->hEvent);
175
176 free(handle->buf);
177
178 #if defined(__cplusplus) && !defined(CINTERFACE)
179 delete handle;
180 #else
181 free(handle);
182 #endif
183
184 #if !defined(_XBOX) && !defined(__WINRT__)
185 CoUninitialize();
186 #endif
187 }
188
xaudio2_new(unsigned samplerate,unsigned channels,size_t size,const char * device)189 static xaudio2_t *xaudio2_new(unsigned samplerate, unsigned channels,
190 size_t size, const char *device)
191 {
192 int32_t idx_found = -1;
193 WAVEFORMATEX wfx = {0};
194 struct string_list *list = NULL;
195 xaudio2_t *handle = NULL;
196
197 #if !defined(_XBOX) && !defined(__WINRT__)
198 if (FAILED(CoInitialize(NULL)))
199 goto error;
200 #endif
201
202 #if defined(__cplusplus) && !defined(CINTERFACE)
203 handle = new xaudio2;
204 #else
205 handle = (xaudio2_t*)calloc(1, sizeof(*handle));
206 #endif
207
208 if (!handle)
209 {
210 #if !defined(_XBOX) && !defined(__WINRT__)
211 CoUninitialize();
212 #endif
213 goto error;
214 }
215
216 list = (struct string_list*)xa_list_new(NULL);
217
218 #if !defined(__cplusplus) || defined(CINTERFACE)
219 handle->lpVtbl = &voice_vtable;
220 #endif
221
222 if (FAILED(XAudio2Create(&handle->pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR)))
223 goto error;
224
225 if (device)
226 {
227 /* Search for device name first */
228 if (list && list->elems)
229 {
230 if (list->elems)
231 {
232 unsigned i;
233 for (i = 0; i < list->size; i++)
234 {
235 if (string_is_equal(device, list->elems[i].data))
236 {
237 idx_found = i;
238 break;
239 }
240 }
241 /* Index was not found yet based on name string,
242 * just assume id is a one-character number index. */
243
244 if (idx_found == -1)
245 {
246 if (isdigit(device[0]))
247 {
248 RARCH_LOG("[XAudio2]: Fallback, device index is a single number index instead: %d.\n", idx_found);
249 idx_found = strtoul(device, NULL, 0);
250 }
251 }
252 }
253 }
254 }
255
256 if (idx_found == -1)
257 idx_found = 0;
258
259 #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
260 {
261 wchar_t *temp = NULL;
262 if (device)
263 temp = utf8_to_utf16_string_alloc((const char*)list->elems[idx_found].userdata);
264
265 if (FAILED(IXAudio2_CreateMasteringVoice(handle->pXAudio2, &handle->pMasterVoice, channels, samplerate, 0, (LPCWSTR)(uintptr_t)temp, NULL, AudioCategory_GameEffects)))
266 {
267 free(temp);
268 goto error;
269 }
270 if (temp)
271 free(temp);
272 }
273 #else
274 if (FAILED(IXAudio2_CreateMasteringVoice(handle->pXAudio2, &handle->pMasterVoice, channels, samplerate, 0, idx_found, NULL)))
275 goto error;
276 #endif
277
278 xaudio2_set_wavefmt(&wfx, channels, samplerate);
279
280 if (FAILED(IXAudio2_CreateSourceVoice(handle->pXAudio2,
281 &handle->pSourceVoice, &wfx,
282 XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO,
283 (IXAudio2VoiceCallback*)handle, 0, 0)))
284 goto error;
285
286 handle->hEvent = CreateEvent(0, FALSE, FALSE, 0);
287 if (!handle->hEvent)
288 goto error;
289
290 handle->bufsize = size / MAX_BUFFERS;
291 handle->buf = (uint8_t*)calloc(1, handle->bufsize * MAX_BUFFERS);
292 if (!handle->buf)
293 goto error;
294
295 if (FAILED(IXAudio2SourceVoice_Start(handle->pSourceVoice, 0,
296 XAUDIO2_COMMIT_NOW)))
297 goto error;
298
299 if (list)
300 string_list_free(list);
301 return handle;
302
303 error:
304 if (list)
305 string_list_free(list);
306 xaudio2_free(handle);
307 return NULL;
308 }
309
xa_init(const char * device,unsigned rate,unsigned latency,unsigned block_frames,unsigned * new_rate)310 static void *xa_init(const char *device, unsigned rate, unsigned latency,
311 unsigned block_frames,
312 unsigned *new_rate)
313 {
314 size_t bufsize;
315 xa_t *xa = (xa_t*)calloc(1, sizeof(*xa));
316
317 if (!xa)
318 return NULL;
319
320 if (latency < 8)
321 latency = 8; /* Do not allow shenanigans. */
322
323 bufsize = latency * rate / 1000;
324 xa->bufsize = bufsize * 2 * sizeof(float);
325
326 xa->xa = xaudio2_new(rate, 2, xa->bufsize, device);
327 if (!xa->xa)
328 {
329 RARCH_ERR("[XAudio2] Failed to init driver.\n");
330 free(xa);
331 return NULL;
332 }
333
334 RARCH_LOG("[XAudio2]: Requesting %u ms latency, using %d ms latency.\n",
335 latency, (int)bufsize * 1000 / rate);
336
337 return xa;
338 }
339
xa_write(void * data,const void * buf,size_t size)340 static ssize_t xa_write(void *data, const void *buf, size_t size)
341 {
342 unsigned bytes = size;
343 xa_t *xa = (xa_t*)data;
344 xaudio2_t *handle = xa->xa;
345 const uint8_t *buffer = (const uint8_t*)buf;
346
347 if (xa->nonblock)
348 {
349 size_t avail = XAUDIO2_WRITE_AVAILABLE(xa->xa);
350
351 if (avail == 0)
352 return 0;
353 if (avail < size)
354 bytes = size = avail;
355 }
356
357 while (bytes)
358 {
359 unsigned need = MIN(bytes, handle->bufsize - handle->bufptr);
360
361 memcpy(handle->buf + handle->write_buffer *
362 handle->bufsize + handle->bufptr,
363 buffer, need);
364
365 handle->bufptr += need;
366 buffer += need;
367 bytes -= need;
368
369 if (handle->bufptr == handle->bufsize)
370 {
371 XAUDIO2_BUFFER xa2buffer;
372
373 while (handle->buffers == MAX_BUFFERS - 1)
374 if (!(WaitForSingleObject(handle->hEvent, 50) == WAIT_OBJECT_0))
375 return -1;
376
377 xa2buffer.Flags = 0;
378 xa2buffer.AudioBytes = handle->bufsize;
379 xa2buffer.pAudioData = handle->buf + handle->write_buffer * handle->bufsize;
380 xa2buffer.PlayBegin = 0;
381 xa2buffer.PlayLength = 0;
382 xa2buffer.LoopBegin = 0;
383 xa2buffer.LoopLength = 0;
384 xa2buffer.LoopCount = 0;
385 xa2buffer.pContext = NULL;
386
387 if (FAILED(IXAudio2SourceVoice_SubmitSourceBuffer(
388 handle->pSourceVoice, &xa2buffer, NULL)))
389 {
390 if (size > 0)
391 return -1;
392 return 0;
393 }
394
395 InterlockedIncrement((LONG volatile*)&handle->buffers);
396 handle->bufptr = 0;
397 handle->write_buffer = (handle->write_buffer + 1) & MAX_BUFFERS_MASK;
398 }
399 }
400
401 return size;
402 }
403
xa_stop(void * data)404 static bool xa_stop(void *data)
405 {
406 xa_t *xa = (xa_t*)data;
407 xa->is_paused = true;
408 return true;
409 }
410
xa_alive(void * data)411 static bool xa_alive(void *data)
412 {
413 xa_t *xa = (xa_t*)data;
414 if (!xa)
415 return false;
416 return !xa->is_paused;
417 }
418
xa_set_nonblock_state(void * data,bool state)419 static void xa_set_nonblock_state(void *data, bool state)
420 {
421 xa_t *xa = (xa_t*)data;
422 if (xa)
423 xa->nonblock = state;
424 }
425
xa_start(void * data,bool is_shutdown)426 static bool xa_start(void *data, bool is_shutdown)
427 {
428 xa_t *xa = (xa_t*)data;
429 xa->is_paused = false;
430 return true;
431 }
432
xa_use_float(void * data)433 static bool xa_use_float(void *data) { return true; }
434
xa_free(void * data)435 static void xa_free(void *data)
436 {
437 xa_t *xa = (xa_t*)data;
438
439 if (!xa)
440 return;
441
442 if (xa->xa)
443 xaudio2_free(xa->xa);
444 free(xa);
445 }
446
xa_write_avail(void * data)447 static size_t xa_write_avail(void *data)
448 {
449 xa_t *xa = (xa_t*)data;
450 return XAUDIO2_WRITE_AVAILABLE(xa->xa);
451 }
452
xa_buffer_size(void * data)453 static size_t xa_buffer_size(void *data)
454 {
455 xa_t *xa = (xa_t*)data;
456 return xa->bufsize;
457 }
458
xa_device_list_free(void * u,void * slp)459 static void xa_device_list_free(void *u, void *slp)
460 {
461 struct string_list *sl = (struct string_list*)slp;
462
463 if (sl)
464 string_list_free(sl);
465 }
466
xa_list_new(void * u)467 static void *xa_list_new(void *u)
468 {
469 #if defined(_XBOX) || !(_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
470 unsigned i;
471 union string_list_elem_attr attr;
472 uint32_t dev_count = 0;
473 IXAudio2 *ixa2 = NULL;
474 struct string_list *sl = string_list_new();
475
476 if (!sl)
477 return NULL;
478
479 attr.i = 0;
480
481 if (FAILED(XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR)))
482 return NULL;
483
484 IXAudio2_GetDeviceCount(ixa2, &dev_count);
485
486 for (i = 0; i < dev_count; i++)
487 {
488 XAUDIO2_DEVICE_DETAILS dev_detail;
489 if (IXAudio2_GetDeviceDetails(ixa2, i, &dev_detail) == S_OK)
490 {
491 char *str = utf16_to_utf8_string_alloc(dev_detail.DisplayName);
492
493 if (str)
494 {
495 string_list_append(sl, str, attr);
496 free(str);
497 }
498 }
499 }
500
501 IXAudio2_Release(ixa2);
502
503 return sl;
504 #elif defined(__WINRT__)
505 return NULL;
506 #else
507 return mmdevice_list_new(u);
508 #endif
509 }
510
511 audio_driver_t audio_xa = {
512 xa_init,
513 xa_write,
514 xa_stop,
515 xa_start,
516 xa_alive,
517 xa_set_nonblock_state,
518 xa_free,
519 xa_use_float,
520 "xaudio",
521 xa_list_new,
522 xa_device_list_free,
523 xa_write_avail,
524 xa_buffer_size,
525 };
526