1 /* MikMod sound library
2 (c) 1998-2005 Miodrag Vallat and others - see file AUTHORS for
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.
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.
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
21 /*==============================================================================
25 Driver for output on win32 platforms using DirectSound
27 ==============================================================================*/
31 Written by Brian McKinney <Brian.McKinney@colorado.edu>
39 #include "mikmod_internals.h"
47 #if !defined(__cplusplus) && !defined(CINTERFACE)
53 /* If you encounter build failures from Open Watcom's dsound.h,
54 * see: https://github.com/open-watcom/open-watcom-v2/pull/313
56 /* Open Watcom has broken __cdecl (leading underscore) name mangling for Windows
57 * internal var names. It is fixed in Open Watcom V2 fork as of May/2014:
58 * https://github.com/open-watcom/open-watcom-v2/commit/961ef1ff756f3ec5a7248cefcae00a6ecaa97ff4
59 * Therefore, we define and use a local copy of IID_IDirectSoundNotify here.
62 DEFINE_GUID(IID_IDirectSoundNotify,0xB0210783,0x89cd,0x11d0,0xAF,0x08,0x00,0xA0,0xC9,0x25,0xCD,0x16);
65 /* PF_XMMI64_INSTRUCTIONS_AVAILABLE not in all SDKs */
66 #ifndef PF_XMMI64_INSTRUCTIONS_AVAILABLE
67 #define PF_XMMI64_INSTRUCTIONS_AVAILABLE 10
70 /* DSBCAPS_CTRLALL is not defined anymore with DirectX 7. Of course DirectSound
71 is a coherent, backwards compatible API... */
72 #ifndef DSBCAPS_CTRLALL
73 #define DSBCAPS_CTRLALL ( DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_CTRLVOLUME | \
74 DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY | \
78 #ifndef WAVE_FORMAT_IEEE_FLOAT
79 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
82 /* size of each buffer */
87 static LPDIRECTSOUND pSoundCard = NULL;
88 static LPDIRECTSOUNDBUFFER pPrimarySoundBuffer = NULL, pSoundBuffer = NULL;
89 static LPDIRECTSOUNDNOTIFY pSoundBufferNotify = NULL;
91 static HANDLE notifyUpdateHandle = NULL, updateBufferHandle = NULL;
92 static BOOL threadInUse = FALSE;
93 static int fragsize=1<<FRAGSIZE;
94 static DWORD controlflags = DSBCAPS_CTRLALL & ~DSBCAPS_GLOBALFOCUS;
96 static DWORD WINAPI updateBufferProc(LPVOID lpParameter)
98 LPVOID pBlock1 = NULL, pBlock2 = NULL;
99 DWORD soundBufferCurrentPosition, blockBytes1, blockBytes2;
102 while (threadInUse) {
103 if (WaitForSingleObject(notifyUpdateHandle,INFINITE)==WAIT_OBJECT_0) {
105 if (!threadInUse) break;
107 IDirectSoundBuffer_GetCurrentPosition
108 (pSoundBuffer,&soundBufferCurrentPosition,NULL);
110 if (soundBufferCurrentPosition < fragsize)
115 if (IDirectSoundBuffer_Lock
116 (pSoundBuffer,start,fragsize,&pBlock1,&blockBytes1,
117 &pBlock2,&blockBytes2,0)==DSERR_BUFFERLOST) {
118 IDirectSoundBuffer_Restore(pSoundBuffer);
119 IDirectSoundBuffer_Lock
120 (pSoundBuffer,start,fragsize,&pBlock1,&blockBytes1,
121 &pBlock2,&blockBytes2,0);
125 if (Player_Paused_internal()) {
126 VC_SilenceBytes((SBYTE*)pBlock1,(ULONG)blockBytes1);
128 VC_SilenceBytes((SBYTE*)pBlock2,(ULONG)blockBytes2);
130 VC_WriteBytes((SBYTE*)pBlock1,(ULONG)blockBytes1);
132 VC_WriteBytes((SBYTE*)pBlock2,(ULONG)blockBytes2);
136 IDirectSoundBuffer_Unlock
137 (pSoundBuffer,pBlock1,blockBytes1,pBlock2,blockBytes2);
143 static void DS_CommandLine(const CHAR *cmdline)
145 CHAR *ptr=MD_GetAtom("buffer",cmdline,0);
150 if ((buf<12)||(buf>19)) buf=FRAGSIZE;
156 ptr=MD_GetAtom("globalfocus",cmdline,1);
158 controlflags |= DSBCAPS_GLOBALFOCUS;
161 controlflags &= ~DSBCAPS_GLOBALFOCUS;
164 static BOOL DS_IsPresent(void)
166 if(DirectSoundCreate(NULL,&pSoundCard,NULL)!=DS_OK)
169 IDirectSound_Release(pSoundCard);
175 static int DS_Init(void)
177 DSBUFFERDESC soundBufferFormat;
179 DSBPOSITIONNOTIFY positionNotifications[2];
180 DWORD updateBufferThreadID;
183 if (DirectSoundCreate(NULL,&pSoundCard,NULL)!=DS_OK) {
184 _mm_errno=MMERR_OPENING_AUDIO;
188 if (IDirectSound_SetCooperativeLevel
189 (pSoundCard,GetForegroundWindow(),DSSCL_PRIORITY)!=DS_OK) {
190 _mm_errno=MMERR_DS_PRIORITY;
194 memset(&soundBufferFormat,0,sizeof(DSBUFFERDESC));
195 soundBufferFormat.dwSize = sizeof(DSBUFFERDESC);
196 soundBufferFormat.dwFlags = DSBCAPS_PRIMARYBUFFER;
197 soundBufferFormat.dwBufferBytes = 0;
198 soundBufferFormat.lpwfxFormat = NULL;
200 if (IDirectSound_CreateSoundBuffer
201 (pSoundCard,&soundBufferFormat,&pPrimarySoundBuffer,NULL)!=DS_OK) {
202 _mm_errno=MMERR_DS_BUFFER;
206 memset(&pcmwf,0,sizeof(WAVEFORMATEX));
207 pcmwf.wFormatTag =(md_mode&DMODE_FLOAT)? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
208 pcmwf.nChannels =(md_mode&DMODE_STEREO)?2:1;
209 pcmwf.nSamplesPerSec =md_mixfreq;
210 pcmwf.wBitsPerSample =(md_mode&DMODE_FLOAT)?32:(md_mode&DMODE_16BITS)?16:8;
211 pcmwf.nBlockAlign =(pcmwf.wBitsPerSample * pcmwf.nChannels) / 8;
212 pcmwf.nAvgBytesPerSec=pcmwf.nSamplesPerSec*pcmwf.nBlockAlign;
214 if (IDirectSoundBuffer_SetFormat(pPrimarySoundBuffer,&pcmwf)!=DS_OK) {
215 _mm_errno=MMERR_DS_FORMAT;
218 IDirectSoundBuffer_Play(pPrimarySoundBuffer,0,0,DSBPLAY_LOOPING);
220 memset(&soundBufferFormat,0,sizeof(DSBUFFERDESC));
221 soundBufferFormat.dwSize =sizeof(DSBUFFERDESC);
222 soundBufferFormat.dwFlags =controlflags|DSBCAPS_GETCURRENTPOSITION2 ;
223 soundBufferFormat.dwBufferBytes =fragsize*UPDATES;
224 soundBufferFormat.lpwfxFormat =&pcmwf;
226 if (IDirectSound_CreateSoundBuffer
227 (pSoundCard,&soundBufferFormat,&pSoundBuffer,NULL)!=DS_OK) {
228 _mm_errno=MMERR_DS_BUFFER;
233 IDirectSoundBuffer_QueryInterface(pSoundBuffer, IID_IDirectSoundNotify,&p);
235 IDirectSoundBuffer_QueryInterface(pSoundBuffer,&IID_IDirectSoundNotify,&p);
238 _mm_errno=MMERR_DS_NOTIFY;
241 pSoundBufferNotify = (LPDIRECTSOUNDNOTIFY) p;
243 notifyUpdateHandle=CreateEvent
244 (NULL,FALSE,FALSE,TEXT("libmikmod DirectSound Driver positionNotify Event"));
245 if (!notifyUpdateHandle) {
246 _mm_errno=MMERR_DS_EVENT;
250 updateBufferHandle=CreateThread
251 (NULL,0,updateBufferProc,NULL,CREATE_SUSPENDED,&updateBufferThreadID);
252 if (!updateBufferHandle) {
253 _mm_errno=MMERR_DS_THREAD;
257 memset(positionNotifications,0,2*sizeof(DSBPOSITIONNOTIFY));
258 positionNotifications[0].dwOffset =0;
259 positionNotifications[0].hEventNotify=notifyUpdateHandle;
260 positionNotifications[1].dwOffset =fragsize;
261 positionNotifications[1].hEventNotify=notifyUpdateHandle;
262 if (IDirectSoundNotify_SetNotificationPositions
263 (pSoundBufferNotify,2,positionNotifications) != DS_OK) {
264 _mm_errno=MMERR_DS_UPDATE;
268 #if defined HAVE_SSE2
269 /* this test only works on Windows XP or later */
270 if (IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE)) {
271 md_mode|=DMODE_SIMDMIXER;
277 static void DS_Exit(void)
281 if(updateBufferHandle) {
282 /* Signal thread to exit and wait for the exit */
286 SetEvent (notifyUpdateHandle);
287 WaitForSingleObject (updateBufferHandle, INFINITE);
291 CloseHandle(updateBufferHandle),
292 updateBufferHandle = NULL;
294 if (notifyUpdateHandle) {
295 CloseHandle(notifyUpdateHandle),
296 notifyUpdateHandle = NULL;
299 if (pSoundBufferNotify) {
300 IDirectSoundNotify_Release(pSoundBufferNotify);
301 pSoundBufferNotify = NULL;
304 if(IDirectSoundBuffer_GetStatus(pSoundBuffer,&statusInfo)==DS_OK)
305 if(statusInfo&DSBSTATUS_PLAYING)
306 IDirectSoundBuffer_Stop(pSoundBuffer);
307 IDirectSoundBuffer_Release(pSoundBuffer);
311 if(pPrimarySoundBuffer) {
312 if(IDirectSoundBuffer_GetStatus(pPrimarySoundBuffer,&statusInfo)==DS_OK)
313 if(statusInfo&DSBSTATUS_PLAYING)
314 IDirectSoundBuffer_Stop(pPrimarySoundBuffer);
315 IDirectSoundBuffer_Release(pPrimarySoundBuffer);
316 pPrimarySoundBuffer = NULL;
320 IDirectSound_Release(pSoundCard);
327 static BOOL do_update = 0;
329 static void DS_Update(void)
334 /* Do first update in DS_Update() to be consistent with other
335 non threaded drivers. */
336 if (do_update && pSoundBuffer) {
339 if (IDirectSoundBuffer_Lock(pSoundBuffer, 0, fragsize, &block, &bBytes, NULL, NULL, 0)
340 == DSERR_BUFFERLOST) {
341 IDirectSoundBuffer_Restore (pSoundBuffer);
342 IDirectSoundBuffer_Lock (pSoundBuffer, 0, fragsize, &block, &bBytes, NULL, NULL, 0);
345 if (Player_Paused_internal()) {
346 VC_SilenceBytes ((SBYTE *)block, (ULONG)bBytes);
348 VC_WriteBytes ((SBYTE *)block, (ULONG)bBytes);
351 IDirectSoundBuffer_Unlock (pSoundBuffer, block, bBytes, NULL, 0);
353 IDirectSoundBuffer_SetCurrentPosition(pSoundBuffer, 0);
354 IDirectSoundBuffer_Play(pSoundBuffer, 0, 0, DSBPLAY_LOOPING);
357 ResumeThread (updateBufferHandle);
361 static void DS_PlayStop(void)
365 IDirectSoundBuffer_Stop(pSoundBuffer);
369 static int DS_PlayStart(void)
372 return VC_PlayStart();
375 MIKMODAPI MDRIVER drv_ds=
379 "DirectSound Driver (DX6+) v0.6",
382 "buffer:r:12,19,16:Audio buffer log2 size\n"
383 "globalfocus:b:0:Play if window does not have the focus\n",
400 VC_VoiceSetFrequency,
401 VC_VoiceGetFrequency,