1 /* MikMod sound library
2 (c) 1998-2005 Miodrag Vallat and others - see file AUTHORS for
3 complete list.
4
5 This library is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of
8 the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18 02111-1307, USA.
19 */
20
21 /*==============================================================================
22
23 $Id$
24
25 Driver for output on win32 platforms using XAudio2
26
27 ==============================================================================*/
28
29 /*
30 Originally written by 'honza.c' <honzac@users.sourceforge.net>
31 Fixes, C-only conversion and float support by O.Sezer
32 <sezero@users.sourceforge.net>
33 */
34
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38
39 #ifdef DRV_XAUDIO2
40
41 #ifndef _WIN32_WINNT
42 /* 0x0501 for WinXP, 0x0602 for Win8 */
43 #define _WIN32_WINNT 0x0501
44 #endif
45 #if defined(DRV_XAUDIO28) && (_WIN32_WINNT < 0x0602)
46 #undef _WIN32_WINNT
47 #define _WIN32_WINNT 0x0602
48 #endif
49 #include "mikmod_internals.h"
50
51 #define INITGUID
52 #include <xaudio2.h>
53
54 #if defined(_MSC_VER)
55 #ifdef DRV_XAUDIO28
56 #pragma comment(lib,"xaudio2.lib")
57 #endif
58 #pragma comment(lib,"ole32.lib")
59 #endif
60
61 /* PF_XMMI64_INSTRUCTIONS_AVAILABLE not in all SDKs */
62 #ifndef PF_XMMI64_INSTRUCTIONS_AVAILABLE
63 #define PF_XMMI64_INSTRUCTIONS_AVAILABLE 10
64 #endif
65
66 #ifndef WAVE_FORMAT_IEEE_FLOAT
67 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
68 #endif
69
70
71 #define XAUDIO2_NUM_BUFFERS 4
72 #define XAUDIO2_BUFFER_SIZE 32768
73
74 #if defined(MIKMOD_DEBUG)
75 # define dbgprintf fprintf
76 #elif defined (__GNUC__) && !(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
77 # define dbgprintf(f, fmt, args...) do {} while (0)
78 #else
79 # define dbgprintf(f, ...) do {} while (0)
80 #endif
81
82 #ifndef __cplusplus
83 /* doing things C-only .. */
84 static HANDLE hBufferEvent;
85
cb_OnVoiceProcessPassStart(IXAudio2VoiceCallback * p,UINT32 SamplesRequired)86 static void STDMETHODCALLTYPE cb_OnVoiceProcessPassStart(IXAudio2VoiceCallback* p,
87 UINT32 SamplesRequired) {
88 dbgprintf(stderr, "\n>XAudio2: OnVoiceProcessingPassStart<\n");
89 }
cb_OnVoiceProcessPassEnd(IXAudio2VoiceCallback * p)90 static void STDMETHODCALLTYPE cb_OnVoiceProcessPassEnd(IXAudio2VoiceCallback* p) {
91 dbgprintf(stderr, "\n>XAudio2: OnVoiceProcessingPassEnd<\n");
92 }
cb_OnStreamEnd(IXAudio2VoiceCallback * p)93 static void STDMETHODCALLTYPE cb_OnStreamEnd(IXAudio2VoiceCallback* p) {
94 dbgprintf(stderr, "\n>XAudio2: OnStreamEnd<\n");
95 }
cb_OnBufferStart(IXAudio2VoiceCallback * p,void * pBufferContext)96 static void STDMETHODCALLTYPE cb_OnBufferStart(IXAudio2VoiceCallback* p,
97 void* pBufferContext) {
98 dbgprintf(stderr, "\n>XAudio2: OnBufferStart<\n");
99 }
cb_OnBufferEnd(IXAudio2VoiceCallback * p,void * pBufferContext)100 static void STDMETHODCALLTYPE cb_OnBufferEnd(IXAudio2VoiceCallback* p,
101 void* pBufferContext) {
102 SetEvent(hBufferEvent);
103 dbgprintf(stderr, "\n>XAudio2: OnBufferEnd<\n");
104 }
cb_OnLoopEnd(IXAudio2VoiceCallback * p,void * pBufferContext)105 static void STDMETHODCALLTYPE cb_OnLoopEnd(IXAudio2VoiceCallback* p,
106 void* pBufferContext) {
107 dbgprintf(stderr, "\n>XAudio2: OnLoopEnd<\n");
108 }
cb_OnVoiceError(IXAudio2VoiceCallback * p,void * pBufferContext,HRESULT Error)109 static void STDMETHODCALLTYPE cb_OnVoiceError(IXAudio2VoiceCallback* p,
110 void* pBufferContext,
111 HRESULT Error) {
112 dbgprintf(stderr, "\n>XAudio2: OnVoiceError: %ld <\n", Error);
113 }
114 static IXAudio2VoiceCallbackVtbl cbVoice_vtbl = {
115 cb_OnVoiceProcessPassStart,
116 cb_OnVoiceProcessPassEnd,
117 cb_OnStreamEnd,
118 cb_OnBufferStart,
119 cb_OnBufferEnd,
120 cb_OnLoopEnd,
121 cb_OnVoiceError
122 };
123 static IXAudio2VoiceCallback cbVoice = {
124 &cbVoice_vtbl
125 };
126 #define pcbVoice &cbVoice
127
128 #else /* __cplusplus: */
129 class XAudio2VoiceCallback: public IXAudio2VoiceCallback {
130 public:
131 HANDLE hBufferEvent;
132 public:
XAudio2VoiceCallback()133 XAudio2VoiceCallback(): hBufferEvent(CreateEvent(NULL, FALSE, FALSE, TEXT("libmikmod XAudio2 Driver buffer Event"))) { }
~XAudio2VoiceCallback()134 virtual ~XAudio2VoiceCallback() { CloseHandle(hBufferEvent); }
STDMETHOD_(void,cb_OnVoiceProcessPassStart)135 STDMETHOD_(void, cb_OnVoiceProcessPassStart)(UINT32 SamplesRequired) {
136 dbgprintf(stderr, "\n>XAudio2: OnVoiceProcessingPassStart<\n");
137 }
STDMETHOD_(void,cb_OnVoiceProcessPassEnd)138 STDMETHOD_(void, cb_OnVoiceProcessPassEnd)() {
139 dbgprintf(stderr, "\n>XAudio2: OnVoiceProcessingPassEnd<\n");
140 }
STDMETHOD_(void,cb_OnStreamEnd)141 STDMETHOD_(void, cb_OnStreamEnd)() {
142 dbgprintf(stderr, "\n>XAudio2: OnStreamEnd<\n");
143 }
STDMETHOD_(void,cb_OnBufferStart)144 STDMETHOD_(void, cb_OnBufferStart)(void* pBufferContext) {
145 dbgprintf(stderr, "\n>XAudio2: OnBufferStart<\n");
146 }
STDMETHOD_(void,cb_OnBufferEnd)147 STDMETHOD_(void, cb_OnBufferEnd)(void* pBufferContext) {
148 SetEvent(hBufferEvent);
149 dbgprintf(stderr, "\n>XAudio2: OnBufferStart<\n");
150 }
STDMETHOD_(void,cb_OnLoopEnd)151 STDMETHOD_(void, cb_OnLoopEnd)(void* pBufferContext) {
152 dbgprintf(stderr, "\n>XAudio2: OnLoopEnd<\n");
153 }
STDMETHOD_(void,cb_OnVoiceError)154 STDMETHOD_(void, cb_OnVoiceError)(void* pBufferContext, HRESULT Error) {
155 dbgprintf(stderr, "\n>XAudio2: OnVoiceError: %ld <\n", Error);
156 }
157 };
158 static XAudio2VoiceCallback *pcbVoice = NULL;
159 #define hBufferEvent pcbVoice->hBufferEvent
160 #define IXAudio2_Release(p) ((p)->Release())
161 #ifndef DRV_XAUDIO28
162 #define IXAudio2_CreateMasteringVoice(p,a,b,c,d,e,f) ((p)->CreateMasteringVoice(a,b,c,d,e,f))
163 #define IXAudio2SourceVoice_GetState(p,a) ((p)->GetState(a))
164 #else
165 #define IXAudio2_CreateMasteringVoice(p,a,b,c,d,e,f,g) ((p)->CreateMasteringVoice(a,b,c,d,e,f,g))
166 #define IXAudio2SourceVoice_GetState(p,a,b) ((p)->GetState(a,b))
167 #endif
168 #define IXAudio2SourceVoice_SubmitSourceBuffer(p,a,b) ((p)->SubmitSourceBuffer(a,b))
169 #define IXAudio2_CreateSourceVoice(p,a,b,c,d,e,f,g) ((p)->CreateSourceVoice(a,b,c,d,e,f,g))
170 #define IXAudio2SourceVoice_DestroyVoice(p) ((p)->DestroyVoice())
171 #define IXAudio2MasteringVoice_DestroyVoice(p) ((p)->DestroyVoice())
172 #define IXAudio2SourceVoice_Start(p,a,b) ((p)->Start(a,b))
173 #define IXAudio2SourceVoice_Stop( p,a,b) ((p)->Stop(a,b))
174 #endif /* __cplusplus */
175
176 static IXAudio2* pXAudio2 = NULL;
177 static IXAudio2MasteringVoice* pMasterVoice = NULL;
178 static IXAudio2SourceVoice* pSourceVoice = NULL;
179 static HANDLE UpdateBufferHandle = NULL;
180 static BOOL threadInUse = FALSE;
181 static BYTE buffers[XAUDIO2_NUM_BUFFERS][XAUDIO2_BUFFER_SIZE];
182 static DWORD current_buf = 0;
183
184
UpdateBufferProc(LPVOID lpParameter)185 static DWORD WINAPI UpdateBufferProc(LPVOID lpParameter) {
186 while (threadInUse) {
187 while (1) {
188 XAUDIO2_VOICE_STATE state;
189 XAUDIO2_BUFFER audio_buf;
190
191 #ifndef DRV_XAUDIO28
192 IXAudio2SourceVoice_GetState(pSourceVoice, &state);
193 #else
194 IXAudio2SourceVoice_GetState(pSourceVoice, &state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
195 #endif
196 if (state.BuffersQueued >= XAUDIO2_NUM_BUFFERS - 1)
197 break;
198 MUTEX_LOCK(vars);
199 if (Player_Paused_internal())
200 VC_SilenceBytes((SBYTE *) buffers[current_buf], (ULONG) XAUDIO2_BUFFER_SIZE);
201 else
202 VC_WriteBytes((SBYTE *) buffers[current_buf], (ULONG) XAUDIO2_BUFFER_SIZE);
203 MUTEX_UNLOCK(vars);
204 memset(&audio_buf, 0, sizeof(XAUDIO2_BUFFER));
205 audio_buf.AudioBytes = XAUDIO2_BUFFER_SIZE;
206 audio_buf.pAudioData = buffers[current_buf];
207 IXAudio2SourceVoice_SubmitSourceBuffer(pSourceVoice, &audio_buf, NULL);
208 current_buf++;
209 current_buf %= XAUDIO2_NUM_BUFFERS;
210 }
211 WaitForSingleObject(hBufferEvent, INFINITE);
212 }
213 return 0;
214 }
215
XAudio2_CommandLine(const CHAR * cmdline)216 static void XAudio2_CommandLine(const CHAR *cmdline) {
217 /* no options */
218 }
219
XAudio2_IsPresent(void)220 static BOOL XAudio2_IsPresent(void) {
221 HRESULT r;
222
223 if (pXAudio2 == NULL) {
224 #ifndef _XBOX
225 CoInitializeEx(NULL, COINIT_MULTITHREADED);
226 #endif
227 r = XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
228 if (pXAudio2) {
229 IXAudio2_Release(pXAudio2);
230 pXAudio2 = NULL;
231 }
232 #ifndef _XBOX
233 CoUninitialize();
234 #endif
235 if (FAILED(r))
236 return 0;
237 }
238 return 1;
239 }
240
XAudio2_Init(void)241 static int XAudio2_Init(void) {
242 UINT32 flags;
243 DWORD thread_id;
244 WAVEFORMATEX wfmt;
245
246 memset(&wfmt, 0, sizeof(WAVEFORMATEX));
247 wfmt.wFormatTag= (md_mode & DMODE_FLOAT)? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
248 wfmt.nChannels = (md_mode & DMODE_STEREO)? 2: 1;
249 wfmt.nSamplesPerSec = md_mixfreq;
250 wfmt.wBitsPerSample = (md_mode & DMODE_FLOAT)? 32: (md_mode & DMODE_16BITS)? 16: 8;
251 wfmt.nBlockAlign = (wfmt.wBitsPerSample * wfmt.nChannels) / 8;
252 wfmt.nAvgBytesPerSec = wfmt.nSamplesPerSec * wfmt.nBlockAlign;
253 if (wfmt.nSamplesPerSec < XAUDIO2_MIN_SAMPLE_RATE ||
254 wfmt.nSamplesPerSec > XAUDIO2_MAX_SAMPLE_RATE ||
255 wfmt.nChannels > XAUDIO2_MAX_AUDIO_CHANNELS) {
256 return 1;
257 }
258
259 current_buf = 0;
260 flags = 0;
261 #if defined(_DEBUG) && !defined(DRV_XAUDIO28)
262 /* flags |= XAUDIO2_DEBUG_ENGINE;*/
263 #endif
264 #ifndef _XBOX
265 CoInitializeEx(NULL, COINIT_MULTITHREADED);
266 #endif
267 if (FAILED(XAudio2Create(&pXAudio2, flags, XAUDIO2_DEFAULT_PROCESSOR))) {
268 goto fail;
269 }
270 #ifdef DRV_XAUDIO28
271 if (FAILED(IXAudio2_CreateMasteringVoice(pXAudio2, &pMasterVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE,
272 0, NULL, NULL, AudioCategory_Other))) {
273 goto fail;
274 }
275 #else
276 if (FAILED(IXAudio2_CreateMasteringVoice(pXAudio2, &pMasterVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, 0, NULL))) {
277 goto fail;
278 }
279 #endif
280 if (FAILED(IXAudio2_CreateSourceVoice(pXAudio2, &pSourceVoice, &wfmt, 0, 1.0f, pcbVoice, NULL, NULL))) {
281 goto fail;
282 }
283 #ifndef __cplusplus
284 if ((hBufferEvent = CreateEvent(NULL, FALSE, FALSE, TEXT("libmikmod XAudio2 Driver buffer Event"))) == NULL) {
285 goto fail;
286 }
287 #endif
288 if ((UpdateBufferHandle = CreateThread(NULL, 0, UpdateBufferProc, NULL, CREATE_SUSPENDED, &thread_id)) == NULL) {
289 goto fail;
290 }
291 #if defined HAVE_SSE2
292 /* this test only works on Windows XP or later */
293 if (IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE)) {
294 md_mode|=DMODE_SIMDMIXER;
295 }
296 #endif
297 return VC_Init();
298
299 fail:
300 if (pSourceVoice) {
301 IXAudio2SourceVoice_DestroyVoice(pSourceVoice);
302 pSourceVoice = NULL;
303 }
304 if (pMasterVoice) {
305 IXAudio2MasteringVoice_DestroyVoice(pMasterVoice);
306 pMasterVoice = NULL;
307 }
308 if (pXAudio2) {
309 IXAudio2_Release(pXAudio2);
310 pXAudio2 = NULL;
311 }
312 #ifndef _XBOX
313 CoUninitialize();
314 #endif
315 return 1;
316 }
317
XAudio2_Exit(void)318 static void XAudio2_Exit(void) {
319 if (UpdateBufferHandle != NULL) {
320 /* signal thread to exit and wait for the exit */
321 if (threadInUse) {
322 threadInUse = 0;
323 MUTEX_UNLOCK(vars);
324 SetEvent(hBufferEvent);
325 WaitForSingleObject(UpdateBufferHandle, INFINITE);
326 MUTEX_LOCK(vars);
327 }
328 CloseHandle(UpdateBufferHandle);
329 UpdateBufferHandle = NULL;
330 }
331 IXAudio2SourceVoice_Stop(pSourceVoice, 0, 0);
332 if (pSourceVoice) {
333 IXAudio2SourceVoice_DestroyVoice(pSourceVoice);
334 pSourceVoice = NULL;
335 }
336 if (pMasterVoice) {
337 IXAudio2MasteringVoice_DestroyVoice(pMasterVoice);
338 pMasterVoice = NULL;
339 }
340 if (pXAudio2) {
341 IXAudio2_Release(pXAudio2);
342 pXAudio2 = NULL;
343 }
344 #ifndef __cplusplus
345 if (hBufferEvent != NULL) {
346 CloseHandle(hBufferEvent);
347 hBufferEvent = NULL;
348 }
349 #endif
350 #ifndef _XBOX
351 CoUninitialize();
352 #endif
353 VC_Exit();
354 }
355
356 static BOOL do_update = 0;
357
XAudio2_Update(void)358 static void XAudio2_Update(void) {
359 if (do_update && pSourceVoice) {
360 do_update = 0;
361
362 while (1) {
363 XAUDIO2_VOICE_STATE state;
364 XAUDIO2_BUFFER audio_buf;
365
366 #ifndef DRV_XAUDIO28
367 IXAudio2SourceVoice_GetState(pSourceVoice, &state);
368 #else
369 IXAudio2SourceVoice_GetState(pSourceVoice, &state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
370 #endif
371 if (state.BuffersQueued > 0)
372 break;
373 current_buf %= XAUDIO2_NUM_BUFFERS;
374 if (Player_Paused_internal())
375 VC_SilenceBytes((SBYTE *) buffers[current_buf], (ULONG) XAUDIO2_BUFFER_SIZE);
376 else
377 VC_WriteBytes((SBYTE *) buffers[current_buf], (ULONG) XAUDIO2_BUFFER_SIZE);
378 memset(&audio_buf, 0, sizeof(XAUDIO2_BUFFER));
379 audio_buf.AudioBytes = XAUDIO2_BUFFER_SIZE;
380 audio_buf.pAudioData = buffers[current_buf];
381 IXAudio2SourceVoice_SubmitSourceBuffer(pSourceVoice, &audio_buf, NULL);
382 current_buf++;
383 current_buf %= XAUDIO2_NUM_BUFFERS;
384 }
385 IXAudio2SourceVoice_Start(pSourceVoice, 0, 0);
386 threadInUse = 1;
387 ResumeThread(UpdateBufferHandle);
388 }
389 }
390
XAudio2_PlayStop(void)391 static void XAudio2_PlayStop(void) {
392 do_update = 0;
393 if (pSourceVoice)
394 IXAudio2SourceVoice_Stop(pSourceVoice, 0, 0);
395 VC_PlayStop();
396 }
397
XAudio2_PlayStart(void)398 static int XAudio2_PlayStart(void) {
399 do_update = 1;
400 return VC_PlayStart();
401 }
402
403 MIKMODAPI MDRIVER drv_xaudio2 = {
404 NULL,
405 "XAudio2",
406 "DirectX XAudio2 Driver v0.3",
407 0,255,
408 "xaudio2",
409 "",
410 XAudio2_CommandLine,
411 XAudio2_IsPresent,
412 VC_SampleLoad,
413 VC_SampleUnload,
414 VC_SampleSpace,
415 VC_SampleLength,
416 XAudio2_Init,
417 XAudio2_Exit,
418 NULL,
419 VC_SetNumVoices,
420 XAudio2_PlayStart,
421 XAudio2_PlayStop,
422 XAudio2_Update,
423 NULL,
424 VC_VoiceSetVolume,
425 VC_VoiceGetVolume,
426 VC_VoiceSetFrequency,
427 VC_VoiceGetFrequency,
428 VC_VoiceSetPanning,
429 VC_VoiceGetPanning,
430 VC_VoicePlay,
431 VC_VoiceStop,
432 VC_VoiceStopped,
433 VC_VoiceGetPosition,
434 VC_VoiceRealVolume
435 };
436
437 #else
438
439 #include "mikmod_internals.h"
440 MISSING(drv_xaudio2);
441
442 #endif
443
444 /* ex:set ts=8: */
445