dropping SDL for the cross-platform version almost done
[dosdemo] / libs / mikmod / drivers / drv_ds.c
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 DirectSound
26
27 ==============================================================================*/
28
29 /*
30
31         Written by Brian McKinney <Brian.McKinney@colorado.edu>
32
33 */
34
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38
39 #include "mikmod_internals.h"
40
41 #ifdef DRV_DS
42
43 #include <memory.h>
44 #include <string.h>
45
46 #define INITGUID
47 #if !defined(__cplusplus) && !defined(CINTERFACE)
48 #define CINTERFACE
49 #endif
50 #include <dsound.h>
51
52 #ifdef __WATCOMC__
53 /* If you encounter build failures from Open Watcom's dsound.h,
54  * see: https://github.com/open-watcom/open-watcom-v2/pull/313
55  */
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.
60  */
61 #include <guiddef.h>
62 DEFINE_GUID(IID_IDirectSoundNotify,0xB0210783,0x89cd,0x11d0,0xAF,0x08,0x00,0xA0,0xC9,0x25,0xCD,0x16);
63 #endif
64
65 /* PF_XMMI64_INSTRUCTIONS_AVAILABLE not in all SDKs */
66 #ifndef PF_XMMI64_INSTRUCTIONS_AVAILABLE
67 #define PF_XMMI64_INSTRUCTIONS_AVAILABLE 10
68 #endif
69
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 | \
75                                                   DSBCAPS_CTRL3D )
76 #endif
77
78 #ifndef WAVE_FORMAT_IEEE_FLOAT
79 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
80 #endif
81
82 /* size of each buffer */
83 #define FRAGSIZE 16
84 /* buffer count */
85 #define UPDATES  2
86
87 static LPDIRECTSOUND pSoundCard = NULL;
88 static LPDIRECTSOUNDBUFFER pPrimarySoundBuffer = NULL, pSoundBuffer = NULL;
89 static LPDIRECTSOUNDNOTIFY pSoundBufferNotify = NULL;
90
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;
95
96 static DWORD WINAPI updateBufferProc(LPVOID lpParameter)
97 {
98         LPVOID pBlock1 = NULL, pBlock2 = NULL;
99         DWORD soundBufferCurrentPosition, blockBytes1, blockBytes2;
100         DWORD start;
101
102         while (threadInUse) {
103                 if (WaitForSingleObject(notifyUpdateHandle,INFINITE)==WAIT_OBJECT_0) {
104
105                         if (!threadInUse) break;
106
107                         IDirectSoundBuffer_GetCurrentPosition
108                                                 (pSoundBuffer,&soundBufferCurrentPosition,NULL);
109
110                         if (soundBufferCurrentPosition < fragsize)
111                                 start = fragsize;
112                         else
113                                 start = 0;
114
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);
122                         }
123
124                         MUTEX_LOCK(vars);
125                         if (Player_Paused_internal()) {
126                                 VC_SilenceBytes((SBYTE*)pBlock1,(ULONG)blockBytes1);
127                                 if (pBlock2)
128                                         VC_SilenceBytes((SBYTE*)pBlock2,(ULONG)blockBytes2);
129                         } else {
130                                 VC_WriteBytes((SBYTE*)pBlock1,(ULONG)blockBytes1);
131                                 if (pBlock2)
132                                         VC_WriteBytes((SBYTE*)pBlock2,(ULONG)blockBytes2);
133                         }
134                         MUTEX_UNLOCK(vars);
135
136                         IDirectSoundBuffer_Unlock
137                                                 (pSoundBuffer,pBlock1,blockBytes1,pBlock2,blockBytes2);
138                 }
139         }
140         return 0;
141 }
142
143 static void DS_CommandLine(const CHAR *cmdline)
144 {
145         CHAR *ptr=MD_GetAtom("buffer",cmdline,0);
146
147         if (ptr) {
148                 int buf=atoi(ptr);
149
150                 if ((buf<12)||(buf>19)) buf=FRAGSIZE;
151                 fragsize=1<<buf;
152
153                 MikMod_free(ptr);
154         }
155
156         ptr=MD_GetAtom("globalfocus",cmdline,1);
157         if (ptr) {
158                 controlflags |= DSBCAPS_GLOBALFOCUS;
159                 MikMod_free(ptr);
160         } else
161                 controlflags &= ~DSBCAPS_GLOBALFOCUS;
162 }
163
164 static BOOL DS_IsPresent(void)
165 {
166         if(DirectSoundCreate(NULL,&pSoundCard,NULL)!=DS_OK)
167                 return 0;
168         if (pSoundCard) {
169                 IDirectSound_Release(pSoundCard);
170                 pSoundCard = NULL;
171         }
172         return 1;
173 }
174
175 static int DS_Init(void)
176 {
177         DSBUFFERDESC soundBufferFormat;
178         WAVEFORMATEX pcmwf;
179         DSBPOSITIONNOTIFY positionNotifications[2];
180         DWORD updateBufferThreadID;
181         LPVOID p = NULL;
182
183         if (DirectSoundCreate(NULL,&pSoundCard,NULL)!=DS_OK) {
184                 _mm_errno=MMERR_OPENING_AUDIO;
185                 return 1;
186         }
187
188         if (IDirectSound_SetCooperativeLevel
189                                 (pSoundCard,GetForegroundWindow(),DSSCL_PRIORITY)!=DS_OK) {
190                 _mm_errno=MMERR_DS_PRIORITY;
191                 return 1;
192         }
193
194         memset(&soundBufferFormat,0,sizeof(DSBUFFERDESC));
195         soundBufferFormat.dwSize = sizeof(DSBUFFERDESC);
196         soundBufferFormat.dwFlags = DSBCAPS_PRIMARYBUFFER;
197         soundBufferFormat.dwBufferBytes = 0;
198         soundBufferFormat.lpwfxFormat = NULL;
199
200         if (IDirectSound_CreateSoundBuffer
201                                 (pSoundCard,&soundBufferFormat,&pPrimarySoundBuffer,NULL)!=DS_OK) {
202                 _mm_errno=MMERR_DS_BUFFER;
203                 return 1;
204         }
205
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;
213
214         if (IDirectSoundBuffer_SetFormat(pPrimarySoundBuffer,&pcmwf)!=DS_OK) {
215                 _mm_errno=MMERR_DS_FORMAT;
216                 return 1;
217         }
218         IDirectSoundBuffer_Play(pPrimarySoundBuffer,0,0,DSBPLAY_LOOPING);
219
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;
225
226         if (IDirectSound_CreateSoundBuffer
227                                 (pSoundCard,&soundBufferFormat,&pSoundBuffer,NULL)!=DS_OK) {
228                 _mm_errno=MMERR_DS_BUFFER;
229                 return 1;
230         }
231
232 #ifdef __cplusplus
233         IDirectSoundBuffer_QueryInterface(pSoundBuffer, IID_IDirectSoundNotify,&p);
234 #else
235         IDirectSoundBuffer_QueryInterface(pSoundBuffer,&IID_IDirectSoundNotify,&p);
236 #endif
237         if (!p) {
238                 _mm_errno=MMERR_DS_NOTIFY;
239                 return 1;
240         }
241         pSoundBufferNotify = (LPDIRECTSOUNDNOTIFY) p;
242
243         notifyUpdateHandle=CreateEvent
244                                 (NULL,FALSE,FALSE,TEXT("libmikmod DirectSound Driver positionNotify Event"));
245         if (!notifyUpdateHandle) {
246                 _mm_errno=MMERR_DS_EVENT;
247                 return 1;
248         }
249
250         updateBufferHandle=CreateThread
251                                 (NULL,0,updateBufferProc,NULL,CREATE_SUSPENDED,&updateBufferThreadID);
252         if (!updateBufferHandle) {
253                 _mm_errno=MMERR_DS_THREAD;
254                 return 1;
255         }
256
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;
265                 return 1;
266         }
267
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;
272         }
273 #endif
274         return VC_Init();
275 }
276
277 static void DS_Exit(void)
278 {
279         DWORD statusInfo;
280
281         if(updateBufferHandle) {
282                 /* Signal thread to exit and wait for the exit */
283                 if (threadInUse) {
284                         threadInUse = 0;
285                         MUTEX_UNLOCK(vars);
286                         SetEvent (notifyUpdateHandle);
287                         WaitForSingleObject (updateBufferHandle, INFINITE);
288                         MUTEX_LOCK(vars);
289                 }
290
291                 CloseHandle(updateBufferHandle),
292                 updateBufferHandle = NULL;
293         }
294         if (notifyUpdateHandle) {
295                 CloseHandle(notifyUpdateHandle),
296                 notifyUpdateHandle = NULL;
297         }
298
299         if (pSoundBufferNotify) {
300                 IDirectSoundNotify_Release(pSoundBufferNotify);
301                 pSoundBufferNotify = NULL;
302         }
303         if(pSoundBuffer) {
304                 if(IDirectSoundBuffer_GetStatus(pSoundBuffer,&statusInfo)==DS_OK)
305                         if(statusInfo&DSBSTATUS_PLAYING)
306                                 IDirectSoundBuffer_Stop(pSoundBuffer);
307                 IDirectSoundBuffer_Release(pSoundBuffer);
308                 pSoundBuffer = NULL;
309         }
310
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;
317         }
318
319         if (pSoundCard) {
320                 IDirectSound_Release(pSoundCard);
321                 pSoundCard = NULL;
322         }
323
324         VC_Exit();
325 }
326
327 static BOOL do_update = 0;
328
329 static void DS_Update(void)
330 {
331         LPVOID block;
332         DWORD bBytes;
333
334         /* Do first update in DS_Update() to be consistent with other
335            non threaded drivers. */
336         if (do_update && pSoundBuffer) {
337                 do_update = 0;
338
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);
343                 }
344
345                 if (Player_Paused_internal()) {
346                         VC_SilenceBytes ((SBYTE *)block, (ULONG)bBytes);
347                 } else {
348                         VC_WriteBytes ((SBYTE *)block, (ULONG)bBytes);
349                 }
350
351                 IDirectSoundBuffer_Unlock (pSoundBuffer, block, bBytes, NULL, 0);
352
353                 IDirectSoundBuffer_SetCurrentPosition(pSoundBuffer, 0);
354                 IDirectSoundBuffer_Play(pSoundBuffer, 0, 0, DSBPLAY_LOOPING);
355
356                 threadInUse=1;
357                 ResumeThread (updateBufferHandle);
358         }
359 }
360
361 static void DS_PlayStop(void)
362 {
363         do_update = 0;
364         if (pSoundBuffer)
365                 IDirectSoundBuffer_Stop(pSoundBuffer);
366         VC_PlayStop();
367 }
368
369 static int DS_PlayStart(void)
370 {
371         do_update = 1;
372         return VC_PlayStart();
373 }
374
375 MIKMODAPI MDRIVER drv_ds=
376 {
377         NULL,
378         "DirectSound",
379         "DirectSound Driver (DX6+) v0.6",
380         0,255,
381         "ds",
382         "buffer:r:12,19,16:Audio buffer log2 size\n"
383                 "globalfocus:b:0:Play if window does not have the focus\n",
384         DS_CommandLine,
385         DS_IsPresent,
386         VC_SampleLoad,
387         VC_SampleUnload,
388         VC_SampleSpace,
389         VC_SampleLength,
390         DS_Init,
391         DS_Exit,
392         NULL,
393         VC_SetNumVoices,
394         DS_PlayStart,
395         DS_PlayStop,
396         DS_Update,
397         NULL,
398         VC_VoiceSetVolume,
399         VC_VoiceGetVolume,
400         VC_VoiceSetFrequency,
401         VC_VoiceGetFrequency,
402         VC_VoiceSetPanning,
403         VC_VoiceGetPanning,
404         VC_VoicePlay,
405         VC_VoiceStop,
406         VC_VoiceStopped,
407         VC_VoiceGetPosition,
408         VC_VoiceRealVolume
409 };
410
411 #else
412
413 MISSING(drv_ds);
414
415 #endif
416
417 /* ex:set ts=4: */