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