Changed ICOM_THIS definition.
[wine] / multimedia / dsound.c
1 /*                      DirectSound
2  * 
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998 Rob Riggs
5  */
6 /*
7  * Note: This file requires multithread ability. It is not possible to
8  * implement the stuff in a single thread anyway. And most DirectX apps
9  * require threading themselves.
10  *
11  * Most thread locking is complete. There may be a few race
12  * conditions still lurking.
13  *
14  * Tested with a Soundblaster clone, a Gravis UltraSound Classic,
15  * and a Turtle Beach Tropez+.
16  *
17  * TODO:
18  *      Implement DirectSoundCapture API
19  *      Implement SetCooperativeLevel properly (need to address focus issues)
20  *      Use wavetable synth for static buffers if available
21  *      Implement DirectSound3DBuffers (stubs in place)
22  *      Use hardware 3D support if available (OSS support may be needed first)
23  *      Add support for APIs other than OSS: ALSA (http://alsa.jcu.cz/)
24  *      and esound (http://www.gnome.org), for instance
25  *
26  * FIXME: Status needs updating.
27  *
28  * Status:
29  * - Wing Commander 4/W95:
30  *   The intromovie plays without problems. Nearly lipsynchron.
31  * - DiscWorld 2
32  *   The sound works, but noticeable chunks are left out (from the sound and
33  *   the animation). Don't know why yet.
34  * - Diablo:
35  *   Sound works, but slows down the movieplayer.
36  * - XvT: 
37  *   Doesn't sound yet.
38  * - Monkey Island 3:
39  *   The background sound of the startscreen works ;)
40  * - WingCommander Prophecy Demo:
41  *   Sound works for the intromovie.
42  * - Total Annihilation (1998/12/04):
43  *   Sound plays perfectly in the game, but the Smacker movies
44  *   (http://www.smacker.com/) play silently.
45  * - A-10 Cuba! Demo (1998/12/04):
46  *   Sound works properly (for some people).
47  * - dsstream.exe, from DirectX 5.2 SDK (1998/12/04):
48  *   Works properly, but requires "-dll -winmm".
49  * - dsshow.exe, from DirectX 5.2 SDK (1998/12/04):
50  *   Initializes the DLL properly with CoCreateInstance(), but the
51  *   FileOpen dialog box is broken - could not test properly
52  */
53
54 #include "config.h"
55 #include <assert.h>
56 #include <errno.h>
57 #include <sys/types.h>
58 #include <sys/time.h>
59 #include <sys/fcntl.h>
60 #include <unistd.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <math.h>       /* Insomnia - pow() function */
64 #include "dsound.h"
65 #include "winerror.h"
66 #include "multimedia.h"
67 #include "objbase.h"
68 #include "thread.h"
69 #include "debug.h"
70
71 #ifdef HAVE_OSS
72 # include <sys/ioctl.h>
73 # ifdef HAVE_MACHINE_SOUNDCARD_H
74 #  include <machine/soundcard.h>
75 # endif
76 # ifdef HAVE_SYS_SOUNDCARD_H
77 #  include <sys/soundcard.h>
78 # endif
79
80 /* #define USE_DSOUND3D 1 */
81
82 #define DSOUND_FRAGLEN (primarybuf->wfx.nAvgBytesPerSec >> 4)
83 #define DSOUND_FREQSHIFT (14)
84
85 static int audiofd = -1;
86 static int audioOK = 0;
87
88 static LPDIRECTSOUND    dsound = NULL;
89
90 static LPDIRECTSOUNDBUFFER      primarybuf = NULL;
91
92 static int DSOUND_setformat(LPWAVEFORMATEX wfex);
93 static void DSOUND_CheckEvent(IDirectSoundBuffer *dsb, int len);
94 static void DSOUND_CloseAudio(void);
95
96 #endif
97
98 HRESULT WINAPI DirectSoundEnumerate32A(
99         LPDSENUMCALLBACK32A enumcb,
100         LPVOID context)
101 {
102         TRACE(dsound, "enumcb = %p, context = %p\n", enumcb, context);
103
104 #ifdef HAVE_OSS
105         if (enumcb != NULL)
106                 enumcb(NULL,"WINE DirectSound using Open Sound System",
107                     "sound",context);
108 #endif
109
110         return 0;
111 }
112
113 #ifdef HAVE_OSS
114 static void _dump_DSBCAPS(DWORD xmask) {
115         struct {
116                 DWORD   mask;
117                 char    *name;
118         } flags[] = {
119 #define FE(x) { x, #x },
120                 FE(DSBCAPS_PRIMARYBUFFER)
121                 FE(DSBCAPS_STATIC)
122                 FE(DSBCAPS_LOCHARDWARE)
123                 FE(DSBCAPS_LOCSOFTWARE)
124                 FE(DSBCAPS_CTRLFREQUENCY)
125                 FE(DSBCAPS_CTRLPAN)
126                 FE(DSBCAPS_CTRLVOLUME)
127                 FE(DSBCAPS_CTRLDEFAULT)
128                 FE(DSBCAPS_CTRLALL)
129                 FE(DSBCAPS_STICKYFOCUS)
130                 FE(DSBCAPS_GETCURRENTPOSITION2)
131         };
132         int     i;
133
134         for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
135                 if (flags[i].mask & xmask)
136                         fprintf(stderr,"%s ",flags[i].name);
137 }
138
139 /*******************************************************************************
140  *              IDirectSound3DBuffer
141  */
142
143 /* IUnknown methods */
144 static HRESULT WINAPI IDirectSound3DBuffer_QueryInterface(
145         LPDIRECTSOUND3DBUFFER this, REFIID riid, LPVOID *ppobj)
146 {
147         char xbuf[50];
148
149         WINE_StringFromCLSID(riid,xbuf);
150         TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
151         return E_FAIL;
152 }
153         
154 static ULONG WINAPI IDirectSound3DBuffer_AddRef(LPDIRECTSOUND3DBUFFER this)
155 {
156         this->ref++;
157         return this->ref;
158 }
159
160 static ULONG WINAPI IDirectSound3DBuffer_Release(LPDIRECTSOUND3DBUFFER this)
161 {
162         if(--this->ref)
163                 return this->ref;
164
165         HeapFree(GetProcessHeap(),0,this->buffer);
166         HeapFree(GetProcessHeap(),0,this);
167
168         return 0;
169 }
170
171 /* IDirectSound3DBuffer methods */
172 static HRESULT WINAPI IDirectSound3DBuffer_GetAllParameters(
173         LPDIRECTSOUND3DBUFFER this,
174         LPDS3DBUFFER lpDs3dBuffer)
175 {
176         FIXME(dsound,"stub\n");
177         return DS_OK;
178 }
179
180 static HRESULT WINAPI IDirectSound3DBuffer_GetConeAngles(
181         LPDIRECTSOUND3DBUFFER this,
182         LPDWORD lpdwInsideConeAngle,
183         LPDWORD lpdwOutsideConeAngle)
184 {
185         FIXME(dsound,"stub\n");
186         return DS_OK;
187 }
188
189 static HRESULT WINAPI IDirectSound3DBuffer_GetConeOrientation(
190         LPDIRECTSOUND3DBUFFER this,
191         LPD3DVECTOR lpvConeOrientation)
192 {
193         FIXME(dsound,"stub\n");
194         return DS_OK;
195 }
196
197 static HRESULT WINAPI IDirectSound3DBuffer_GetConeOutsideVolume(
198         LPDIRECTSOUND3DBUFFER this,
199         LPLONG lplConeOutsideVolume)
200 {
201         FIXME(dsound,"stub\n");
202         return DS_OK;
203 }
204
205 static HRESULT WINAPI IDirectSound3DBuffer_GetMaxDistance(
206         LPDIRECTSOUND3DBUFFER this,
207         LPD3DVALUE lpfMaxDistance)
208 {
209         FIXME(dsound,"stub\n");
210         return DS_OK;
211 }
212
213 static HRESULT WINAPI IDirectSound3DBuffer_GetMinDistance(
214         LPDIRECTSOUND3DBUFFER this,
215         LPD3DVALUE lpfMinDistance)
216 {
217         FIXME(dsound,"stub\n");
218         return DS_OK;
219 }
220
221 static HRESULT WINAPI IDirectSound3DBuffer_GetMode(
222         LPDIRECTSOUND3DBUFFER this,
223         LPDWORD lpdwMode)
224 {
225         FIXME(dsound,"stub\n");
226         return DS_OK;
227 }
228
229 static HRESULT WINAPI IDirectSound3DBuffer_GetPosition(
230         LPDIRECTSOUND3DBUFFER this,
231         LPD3DVECTOR lpvPosition)
232 {
233         FIXME(dsound,"stub\n");
234         return DS_OK;
235 }
236
237 static HRESULT WINAPI IDirectSound3DBuffer_GetVelocity(
238         LPDIRECTSOUND3DBUFFER this,
239         LPD3DVECTOR lpvVelocity)
240 {
241         FIXME(dsound,"stub\n");
242         return DS_OK;
243 }
244
245 static HRESULT WINAPI IDirectSound3DBuffer_SetAllParameters(
246         LPDIRECTSOUND3DBUFFER this,
247         LPCDS3DBUFFER lpcDs3dBuffer,
248         DWORD dwApply)
249 {
250         FIXME(dsound,"stub\n");
251         return DS_OK;
252 }
253
254 static HRESULT WINAPI IDirectSound3DBuffer_SetConeAngles(
255         LPDIRECTSOUND3DBUFFER this,
256         DWORD dwInsideConeAngle,
257         DWORD dwOutsideConeAngle,
258         DWORD dwApply)
259 {
260         FIXME(dsound,"stub\n");
261         return DS_OK;
262 }
263
264 static HRESULT WINAPI IDirectSound3DBuffer_SetConeOrientation(
265         LPDIRECTSOUND3DBUFFER this,
266         D3DVALUE x, D3DVALUE y, D3DVALUE z,
267         DWORD dwApply)
268 {
269         FIXME(dsound,"stub\n");
270         return DS_OK;
271 }
272
273 static HRESULT WINAPI IDirectSound3DBuffer_SetConeOutsideVolume(
274         LPDIRECTSOUND3DBUFFER this,
275         LONG lConeOutsideVolume,
276         DWORD dwApply)
277 {
278         FIXME(dsound,"stub\n");
279         return DS_OK;
280 }
281
282 static HRESULT WINAPI IDirectSound3DBuffer_SetMaxDistance(
283         LPDIRECTSOUND3DBUFFER this,
284         D3DVALUE fMaxDistance,
285         DWORD dwApply)
286 {
287         FIXME(dsound,"stub\n");
288         return DS_OK;
289 }
290
291 static HRESULT WINAPI IDirectSound3DBuffer_SetMinDistance(
292         LPDIRECTSOUND3DBUFFER this,
293         D3DVALUE fMinDistance,
294         DWORD dwApply)
295 {
296         FIXME(dsound,"stub\n");
297         return DS_OK;
298 }
299
300 static HRESULT WINAPI IDirectSound3DBuffer_SetMode(
301         LPDIRECTSOUND3DBUFFER this,
302         DWORD dwMode,
303         DWORD dwApply)
304 {
305         TRACE(dsound, "mode = %lx\n", dwMode);
306         this->ds3db.dwMode = dwMode;
307         return DS_OK;
308 }
309
310 static HRESULT WINAPI IDirectSound3DBuffer_SetPosition(
311         LPDIRECTSOUND3DBUFFER this,
312         D3DVALUE x, D3DVALUE y, D3DVALUE z,
313         DWORD dwApply)
314 {
315         FIXME(dsound,"stub\n");
316         return DS_OK;
317 }
318
319 static HRESULT WINAPI IDirectSound3DBuffer_SetVelocity(
320         LPDIRECTSOUND3DBUFFER this,
321         D3DVALUE x, D3DVALUE y, D3DVALUE z,
322         DWORD dwApply)
323 {
324         FIXME(dsound,"stub\n");
325         return DS_OK;
326 }
327
328 IDirectSound3DBuffer_VTable ds3dbvt = {
329         /* IUnknown methods */
330         IDirectSound3DBuffer_QueryInterface,
331         IDirectSound3DBuffer_AddRef,
332         IDirectSound3DBuffer_Release,
333         /* IDirectSound3DBuffer methods */
334         IDirectSound3DBuffer_GetAllParameters,
335         IDirectSound3DBuffer_GetConeAngles,
336         IDirectSound3DBuffer_GetConeOrientation,
337         IDirectSound3DBuffer_GetConeOutsideVolume,
338         IDirectSound3DBuffer_GetMaxDistance,
339         IDirectSound3DBuffer_GetMinDistance,
340         IDirectSound3DBuffer_GetMode,
341         IDirectSound3DBuffer_GetPosition,
342         IDirectSound3DBuffer_GetVelocity,
343         IDirectSound3DBuffer_SetAllParameters,
344         IDirectSound3DBuffer_SetConeAngles,
345         IDirectSound3DBuffer_SetConeOrientation,
346         IDirectSound3DBuffer_SetConeOutsideVolume,
347         IDirectSound3DBuffer_SetMaxDistance,
348         IDirectSound3DBuffer_SetMinDistance,
349         IDirectSound3DBuffer_SetMode,
350         IDirectSound3DBuffer_SetPosition,
351         IDirectSound3DBuffer_SetVelocity,
352 };
353
354 #ifdef USE_DSOUND3D
355 static int DSOUND_Create3DBuffer(LPDIRECTSOUNDBUFFER dsb)
356 {
357         DWORD   i, temp, iSize, oSize, offset;
358         LPBYTE  bIbuf, bObuf, bTbuf = NULL;
359         LPWORD  wIbuf, wObuf, wTbuf = NULL;
360
361         /* Inside DirectX says it's stupid but allowed */
362         if (dsb->wfx.nChannels == 2) {
363                 /* Convert to mono */
364                 if (dsb->wfx.wBitsPerSample == 16) {
365                         iSize = dsb->buflen / 4;
366                         wTbuf = malloc(dsb->buflen / 2);
367                         if (wTbuf == NULL)
368                                 return DSERR_OUTOFMEMORY;
369                         for (i = 0; i < iSize; i++)
370                                 wTbuf[i] = (dsb->buffer[i] + dsb->buffer[(i * 2) + 1]) / 2;
371                         wIbuf = wTbuf;
372                 } else {
373                         iSize = dsb->buflen / 2;
374                         bTbuf = malloc(dsb->buflen / 2);
375                         if (bTbuf == NULL)
376                                 return DSERR_OUTOFMEMORY;
377                         for (i = 0; i < iSize; i++)
378                                 bTbuf[i] = (dsb->buffer[i] + dsb->buffer[(i * 2) + 1]) / 2;
379                         bIbuf = bTbuf;
380                 }
381         } else {
382                 if (dsb->wfx.wBitsPerSample == 16) {
383                         iSize = dsb->buflen / 2;
384                         wIbuf = (LPWORD) dsb->buffer;
385                 } else {
386                         bIbuf = (LPBYTE) dsb->buffer;
387                         iSize = dsb->buflen;
388                 }
389         }
390
391         if (primarybuf->wfx.wBitsPerSample == 16) {
392                 wObuf = (LPWORD) dsb->ds3db->buffer;
393                 oSize = dsb->ds3db->buflen / 2;
394         } else {
395                 bObuf = (LPBYTE) dsb->ds3db->buffer;
396                 oSize = dsb->ds3db->buflen;
397         }
398
399         offset = primarybuf->wfx.nSamplesPerSec / 100;          /* 10ms */
400         if (primarybuf->wfx.wBitsPerSample == 16 && dsb->wfx.wBitsPerSample == 16)
401                 for (i = 0; i < iSize; i++) {
402                         temp = wIbuf[i];
403                         if (i >= offset)
404                                 temp += wIbuf[i - offset] >> 9;
405                         else
406                                 temp += wIbuf[i + iSize - offset] >> 9;
407                         wObuf[i * 2] = temp;
408                         wObuf[(i * 2) + 1] = temp;
409                 }
410         else if (primarybuf->wfx.wBitsPerSample == 8 && dsb->wfx.wBitsPerSample == 8)
411                 for (i = 0; i < iSize; i++) {
412                         temp = bIbuf[i];
413                         if (i >= offset)
414                                 temp += bIbuf[i - offset] >> 5;
415                         else
416                                 temp += bIbuf[i + iSize - offset] >> 5;
417                         bObuf[i * 2] = temp;
418                         bObuf[(i * 2) + 1] = temp;
419                 }
420         
421         if (wTbuf)
422                 free(wTbuf);
423         if (bTbuf)
424                 free(bTbuf);
425
426         return DS_OK;
427 }
428 #endif
429 /*******************************************************************************
430  *              IDirectSound3DListener
431  */
432
433 /* IUnknown methods */
434 static HRESULT WINAPI IDirectSound3DListener_QueryInterface(
435         LPDIRECTSOUND3DLISTENER this, REFIID riid, LPVOID *ppobj)
436 {
437         char xbuf[50];
438
439         WINE_StringFromCLSID(riid,xbuf);
440         TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
441         return E_FAIL;
442 }
443         
444 static ULONG WINAPI IDirectSound3DListener_AddRef(LPDIRECTSOUND3DLISTENER this)
445 {
446         this->ref++;
447         return this->ref;
448 }
449
450 static ULONG WINAPI IDirectSound3DListener_Release(LPDIRECTSOUND3DLISTENER this)
451 {
452         this->ref--;
453         return this->ref;
454 }
455
456 /* IDirectSound3DListener methods */
457 static HRESULT WINAPI IDirectSound3DListener_GetAllParameter(
458         LPDIRECTSOUND3DLISTENER this,
459         LPDS3DLISTENER lpDS3DL)
460 {
461         FIXME(dsound,"stub\n");
462         return DS_OK;
463 }
464
465 static HRESULT WINAPI IDirectSound3DListener_GetDistanceFactor(
466         LPDIRECTSOUND3DLISTENER this,
467         LPD3DVALUE lpfDistanceFactor)
468 {
469         FIXME(dsound,"stub\n");
470         return DS_OK;
471 }
472
473 static HRESULT WINAPI IDirectSound3DListener_GetDopplerFactor(
474         LPDIRECTSOUND3DLISTENER this,
475         LPD3DVALUE lpfDopplerFactor)
476 {
477         FIXME(dsound,"stub\n");
478         return DS_OK;
479 }
480
481 static HRESULT WINAPI IDirectSound3DListener_GetOrientation(
482         LPDIRECTSOUND3DLISTENER this,
483         LPD3DVECTOR lpvOrientFront,
484         LPD3DVECTOR lpvOrientTop)
485 {
486         FIXME(dsound,"stub\n");
487         return DS_OK;
488 }
489
490 static HRESULT WINAPI IDirectSound3DListener_GetPosition(
491         LPDIRECTSOUND3DLISTENER this,
492         LPD3DVECTOR lpvPosition)
493 {
494         FIXME(dsound,"stub\n");
495         return DS_OK;
496 }
497
498 static HRESULT WINAPI IDirectSound3DListener_GetRolloffFactor(
499         LPDIRECTSOUND3DLISTENER this,
500         LPD3DVALUE lpfRolloffFactor)
501 {
502         FIXME(dsound,"stub\n");
503         return DS_OK;
504 }
505
506 static HRESULT WINAPI IDirectSound3DListener_GetVelocity(
507         LPDIRECTSOUND3DLISTENER this,
508         LPD3DVECTOR lpvVelocity)
509 {
510         FIXME(dsound,"stub\n");
511         return DS_OK;
512 }
513
514 static HRESULT WINAPI IDirectSound3DListener_SetAllParameters(
515         LPDIRECTSOUND3DLISTENER this,
516         LPCDS3DLISTENER lpcDS3DL,
517         DWORD dwApply)
518 {
519         FIXME(dsound,"stub\n");
520         return DS_OK;
521 }
522
523 static HRESULT WINAPI IDirectSound3DListener_SetDistanceFactor(
524         LPDIRECTSOUND3DLISTENER this,
525         D3DVALUE fDistanceFactor,
526         DWORD dwApply)
527 {
528         FIXME(dsound,"stub\n");
529         return DS_OK;
530 }
531
532 static HRESULT WINAPI IDirectSound3DListener_SetDopplerFactor(
533         LPDIRECTSOUND3DLISTENER this,
534         D3DVALUE fDopplerFactor,
535         DWORD dwApply)
536 {
537         FIXME(dsound,"stub\n");
538         return DS_OK;
539 }
540
541 static HRESULT WINAPI IDirectSound3DListener_SetOrientation(
542         LPDIRECTSOUND3DLISTENER this,
543         D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront,
544         D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop,
545         DWORD dwApply)
546 {
547         FIXME(dsound,"stub\n");
548         return DS_OK;
549 }
550
551 static HRESULT WINAPI IDirectSound3DListener_SetPosition(
552         LPDIRECTSOUND3DLISTENER this,
553         D3DVALUE x, D3DVALUE y, D3DVALUE z,
554         DWORD dwApply)
555 {
556         FIXME(dsound,"stub\n");
557         return DS_OK;
558 }
559
560 static HRESULT WINAPI IDirectSound3DListener_SetRolloffFactor(
561         LPDIRECTSOUND3DLISTENER this,
562         D3DVALUE fRolloffFactor,
563         DWORD dwApply)
564 {
565         FIXME(dsound,"stub\n");
566         return DS_OK;
567 }
568
569 static HRESULT WINAPI IDirectSound3DListener_SetVelocity(
570         LPDIRECTSOUND3DLISTENER this,
571         D3DVALUE x, D3DVALUE y, D3DVALUE z,
572         DWORD dwApply)
573 {
574         FIXME(dsound,"stub\n");
575         return DS_OK;
576 }
577
578 static HRESULT WINAPI IDirectSound3DListener_CommitDeferredSettings(
579         LPDIRECTSOUND3DLISTENER this)
580
581 {
582         FIXME(dsound,"stub\n");
583         return DS_OK;
584 }
585
586 IDirectSound3DListener_VTable ds3dlvt = {
587         /* IUnknown methods */
588         IDirectSound3DListener_QueryInterface,
589         IDirectSound3DListener_AddRef,
590         IDirectSound3DListener_Release,
591         /* IDirectSound3DListener methods */
592         IDirectSound3DListener_GetAllParameter,
593         IDirectSound3DListener_GetDistanceFactor,
594         IDirectSound3DListener_GetDopplerFactor,
595         IDirectSound3DListener_GetOrientation,
596         IDirectSound3DListener_GetPosition,
597         IDirectSound3DListener_GetRolloffFactor,
598         IDirectSound3DListener_GetVelocity,
599         IDirectSound3DListener_SetAllParameters,
600         IDirectSound3DListener_SetDistanceFactor,
601         IDirectSound3DListener_SetDopplerFactor,
602         IDirectSound3DListener_SetOrientation,
603         IDirectSound3DListener_SetPosition,
604         IDirectSound3DListener_SetRolloffFactor,
605         IDirectSound3DListener_SetVelocity,
606         IDirectSound3DListener_CommitDeferredSettings,
607 };
608
609 /*******************************************************************************
610  *              IDirectSoundNotify
611  */
612 static HRESULT WINAPI IDirectSoundNotify_QueryInterface(
613         LPDIRECTSOUNDNOTIFY this,REFIID riid,LPVOID *ppobj
614 ) {
615         char xbuf[50];
616
617         WINE_StringFromCLSID(riid,xbuf);
618         TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
619         return E_FAIL;
620 }
621
622 static ULONG WINAPI IDirectSoundNotify_AddRef(LPDIRECTSOUNDNOTIFY this) {
623         return ++(this->ref);
624 }
625
626 static ULONG WINAPI IDirectSoundNotify_Release(LPDIRECTSOUNDNOTIFY this) {
627         this->ref--;
628         if (!this->ref) {
629                 this->dsb->lpvtbl->fnRelease(this->dsb);
630                 HeapFree(GetProcessHeap(),0,this);
631                 return 0;
632         }
633         return this->ref;
634 }
635
636 static HRESULT WINAPI IDirectSoundNotify_SetNotificationPositions(
637         LPDIRECTSOUNDNOTIFY this,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
638 ) {
639         int     i;
640
641         if (TRACE_ON(dsound)) {
642             TRACE(dsound,"(%p,0x%08lx,%p)\n",this,howmuch,notify);
643             for (i=0;i<howmuch;i++)
644                     TRACE(dsound,"notify at %ld to 0x%08lx\n",
645                             notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
646         }
647         this->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,this->dsb->notifies,(this->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
648         memcpy( this->dsb->notifies+this->dsb->nrofnotifies,
649                 notify,
650                 howmuch*sizeof(DSBPOSITIONNOTIFY)
651         );
652         this->dsb->nrofnotifies+=howmuch;
653
654         return 0;
655 }
656
657 IDirectSoundNotify_VTable dsnvt = {
658         IDirectSoundNotify_QueryInterface,
659         IDirectSoundNotify_AddRef,
660         IDirectSoundNotify_Release,
661         IDirectSoundNotify_SetNotificationPositions,
662 };
663
664 /*******************************************************************************
665  *              IDirectSoundBuffer
666  */
667
668 /* This sets this format for the <em>Primary Buffer Only</em> */
669 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
670 static HRESULT WINAPI IDirectSoundBuffer_SetFormat(
671         LPDIRECTSOUNDBUFFER this,LPWAVEFORMATEX wfex
672 ) {
673         LPDIRECTSOUNDBUFFER     *dsb;
674         int                     i;
675
676         /* Let's be pedantic! */
677         if ((wfex == NULL) ||
678             (wfex->wFormatTag != WAVE_FORMAT_PCM) ||
679             (wfex->nChannels < 1) || (wfex->nChannels > 2) ||
680             (wfex->nSamplesPerSec < 1) ||
681             (wfex->nBlockAlign < 1) || (wfex->nChannels > 4) ||
682             ((wfex->wBitsPerSample != 8) && (wfex->wBitsPerSample != 16))) {
683                 TRACE(dsound, "failed pedantic check!\n");
684                 return DSERR_INVALIDPARAM;
685         }
686
687         /* **** */
688         EnterCriticalSection(&(primarybuf->lock));
689
690         if (primarybuf->wfx.nSamplesPerSec != wfex->nSamplesPerSec) {
691                 dsb = dsound->buffers;
692                 for (i = 0; i < dsound->nrofbuffers; i++, dsb++) {
693                         /* **** */
694                         EnterCriticalSection(&((*dsb)->lock));
695
696                         (*dsb)->freqAdjust = ((*dsb)->freq << DSOUND_FREQSHIFT) /
697                                 wfex->nSamplesPerSec;
698
699                         LeaveCriticalSection(&((*dsb)->lock));
700                         /* **** */
701                 }
702         }
703
704         memcpy(&(primarybuf->wfx), wfex, sizeof(primarybuf->wfx));
705
706         TRACE(dsound,"(formattag=0x%04x,chans=%d,samplerate=%ld"
707                    "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
708                    wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
709                    wfex->nAvgBytesPerSec, wfex->nBlockAlign, 
710                    wfex->wBitsPerSample, wfex->cbSize);
711
712         primarybuf->wfx.nAvgBytesPerSec =
713                 this->wfx.nSamplesPerSec * this->wfx.nBlockAlign;
714
715         DSOUND_CloseAudio();
716
717         LeaveCriticalSection(&(primarybuf->lock));
718         /* **** */
719
720         return DS_OK;
721 }
722
723 static HRESULT WINAPI IDirectSoundBuffer_SetVolume(
724         LPDIRECTSOUNDBUFFER this,LONG vol
725 ) {
726         double  temp;
727
728         TRACE(dsound,"(%p,%ld)\n",this,vol);
729
730         /* I'm not sure if we need this for primary buffer */
731         if (!(this->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
732                 return DSERR_CONTROLUNAVAIL;
733
734         if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN))
735                 return DSERR_INVALIDPARAM;
736
737         /* This needs to adjust the soundcard volume when */
738         /* called for the primary buffer */
739         if (this->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
740                 FIXME(dsound, "Volume control of primary unimplemented.\n");
741                 this->volume = vol;
742                 return DS_OK;
743         }
744
745         /* **** */
746         EnterCriticalSection(&(this->lock));
747
748         this->volume = vol;
749
750         temp = (double) (this->volume - (this->pan > 0 ? this->pan : 0));
751         this->lVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 32768.0);
752         temp = (double) (this->volume + (this->pan < 0 ? this->pan : 0));
753         this->rVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 32768.0);
754
755         LeaveCriticalSection(&(this->lock));
756         /* **** */
757
758         TRACE(dsound, "left = %lx, right = %lx\n", this->lVolAdjust, this->rVolAdjust);
759
760         return DS_OK;
761 }
762
763 static HRESULT WINAPI IDirectSoundBuffer_GetVolume(
764         LPDIRECTSOUNDBUFFER this,LPLONG vol
765 ) {
766         TRACE(dsound,"(%p,%p)\n",this,vol);
767
768         if (vol == NULL)
769                 return DSERR_INVALIDPARAM;
770
771         *vol = this->volume;
772         return DS_OK;
773 }
774
775 static HRESULT WINAPI IDirectSoundBuffer_SetFrequency(
776         LPDIRECTSOUNDBUFFER this,DWORD freq
777 ) {
778         TRACE(dsound,"(%p,%ld)\n",this,freq);
779
780         /* You cannot set the frequency of the primary buffer */
781         if (!(this->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY) ||
782             (this->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
783                 return DSERR_CONTROLUNAVAIL;
784
785         if ((freq < DSBFREQUENCY_MIN) || (freq > DSBFREQUENCY_MAX))
786                 return DSERR_INVALIDPARAM;
787
788         /* **** */
789         EnterCriticalSection(&(this->lock));
790
791         this->freq = freq;
792         this->freqAdjust = (freq << DSOUND_FREQSHIFT) / primarybuf->wfx.nSamplesPerSec;
793         this->nAvgBytesPerSec = freq * this->wfx.nBlockAlign;
794
795         LeaveCriticalSection(&(this->lock));
796         /* **** */
797
798         return DS_OK;
799 }
800
801 static HRESULT WINAPI IDirectSoundBuffer_Play(
802         LPDIRECTSOUNDBUFFER this,DWORD reserved1,DWORD reserved2,DWORD flags
803 ) {
804         TRACE(dsound,"(%p,%08lx,%08lx,%08lx)\n",
805                 this,reserved1,reserved2,flags
806         );
807         this->playflags = flags;
808         this->playing = 1;
809         return DS_OK;
810 }
811
812 static HRESULT WINAPI IDirectSoundBuffer_Stop(LPDIRECTSOUNDBUFFER this)
813 {
814         TRACE(dsound,"(%p)\n",this);
815
816         /* **** */
817         EnterCriticalSection(&(this->lock));
818
819         this->playing = 0;
820         DSOUND_CheckEvent(this, 0);
821
822         LeaveCriticalSection(&(this->lock));
823         /* **** */
824
825         return DS_OK;
826 }
827
828 static DWORD WINAPI IDirectSoundBuffer_AddRef(LPDIRECTSOUNDBUFFER this) {
829 /*      TRACE(dsound,"(%p) ref was %ld\n",this, this->ref); */
830
831         return ++(this->ref);
832 }
833 static DWORD WINAPI IDirectSoundBuffer_Release(LPDIRECTSOUNDBUFFER this) {
834         int     i;
835
836 /*      TRACE(dsound,"(%p) ref was %ld\n",this, this->ref); */
837
838         if (--this->ref)
839                 return this->ref;
840
841         for (i=0;i<this->dsound->nrofbuffers;i++)
842                 if (this->dsound->buffers[i] == this)
843                         break;
844         if (i < this->dsound->nrofbuffers) {
845                 /* Put the last buffer of the list in the (now empty) position */
846                 this->dsound->buffers[i] = this->dsound->buffers[this->dsound->nrofbuffers - 1];
847                 this->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,this->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*this->dsound->nrofbuffers);
848                 this->dsound->nrofbuffers--;
849                 this->dsound->lpvtbl->fnRelease(this->dsound);
850         }
851
852         DeleteCriticalSection(&(this->lock));
853
854         if (this->ds3db && this->ds3db->lpvtbl)
855                 this->ds3db->lpvtbl->fnRelease(this->ds3db);
856
857         if (this->parent)
858                 /* this is a duplicate buffer */
859                 this->parent->lpvtbl->fnRelease(this->parent);
860         else
861                 /* this is a toplevel buffer */
862                 HeapFree(GetProcessHeap(),0,this->buffer);
863
864         HeapFree(GetProcessHeap(),0,this);
865         
866         if (this == primarybuf)
867                 primarybuf = NULL;
868
869         return DS_OK;
870 }
871
872 static HRESULT WINAPI IDirectSoundBuffer_GetCurrentPosition(
873         LPDIRECTSOUNDBUFFER this,LPDWORD playpos,LPDWORD writepos
874 ) {
875         TRACE(dsound,"(%p,%p,%p)\n",this,playpos,writepos);
876         if (playpos) *playpos = this->playpos;
877         if (writepos) *writepos = this->writepos;
878         TRACE(dsound, "playpos = %ld, writepos = %ld\n", *playpos, *writepos);
879         return DS_OK;
880 }
881
882 static HRESULT WINAPI IDirectSoundBuffer_GetStatus(
883         LPDIRECTSOUNDBUFFER this,LPDWORD status
884 ) {
885         TRACE(dsound,"(%p,%p)\n",this,status);
886
887         if (status == NULL)
888                 return DSERR_INVALIDPARAM;
889
890         *status = 0;
891         if (this->playing)
892                 *status |= DSBSTATUS_PLAYING;
893         if (this->playflags & DSBPLAY_LOOPING)
894                 *status |= DSBSTATUS_LOOPING;
895
896         return DS_OK;
897 }
898
899
900 static HRESULT WINAPI IDirectSoundBuffer_GetFormat(
901         LPDIRECTSOUNDBUFFER this,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
902 ) {
903         TRACE(dsound,"(%p,%p,%ld,%p)\n",this,lpwf,wfsize,wfwritten);
904
905         if (wfsize>sizeof(this->wfx))
906                 wfsize = sizeof(this->wfx);
907         if (lpwf) {     /* NULL is valid */
908                 memcpy(lpwf,&(this->wfx),wfsize);
909                 if (wfwritten)
910                         *wfwritten = wfsize;
911         } else
912                 if (wfwritten)
913                         *wfwritten = sizeof(this->wfx);
914                 else
915                         return DSERR_INVALIDPARAM;
916
917         return DS_OK;
918 }
919
920 static HRESULT WINAPI IDirectSoundBuffer_Lock(
921         LPDIRECTSOUNDBUFFER this,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
922 ) {
923
924         TRACE(dsound,"(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx)\n",
925                 this,
926                 writecursor,
927                 writebytes,
928                 lplpaudioptr1,
929                 audiobytes1,
930                 lplpaudioptr2,
931                 audiobytes2,
932                 flags
933         );
934         if (flags & DSBLOCK_FROMWRITECURSOR)
935                 writecursor += this->writepos;
936         if (flags & DSBLOCK_ENTIREBUFFER)
937                 writebytes = this->buflen;
938         if (writebytes > this->buflen)
939                 writebytes = this->buflen;
940
941         assert(audiobytes1!=audiobytes2);
942         assert(lplpaudioptr1!=lplpaudioptr2);
943         if (writecursor+writebytes <= this->buflen) {
944                 *(LPBYTE*)lplpaudioptr1 = this->buffer+writecursor;
945                 *audiobytes1 = writebytes;
946                 if (lplpaudioptr2)
947                         *(LPBYTE*)lplpaudioptr2 = NULL;
948                 if (audiobytes2)
949                         *audiobytes2 = 0;
950                 TRACE(dsound,"->%ld.0\n",writebytes);
951         } else {
952                 *(LPBYTE*)lplpaudioptr1 = this->buffer+writecursor;
953                 *audiobytes1 = this->buflen-writecursor;
954                 if (lplpaudioptr2)
955                         *(LPBYTE*)lplpaudioptr2 = this->buffer;
956                 if (audiobytes2)
957                         *audiobytes2 = writebytes-(this->buflen-writecursor);
958                 TRACE(dsound,"->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
959         }
960         /* No. See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 21 */
961         /* this->writepos=(writecursor+writebytes)%this->buflen; */
962         return DS_OK;
963 }
964
965 static HRESULT WINAPI IDirectSoundBuffer_SetCurrentPosition(
966         LPDIRECTSOUNDBUFFER this,DWORD newpos
967 ) {
968         TRACE(dsound,"(%p,%ld)\n",this,newpos);
969
970         /* **** */
971         EnterCriticalSection(&(this->lock));
972
973         this->playpos = newpos;
974
975         LeaveCriticalSection(&(this->lock));
976         /* **** */
977
978         return 0;
979 }
980
981 static HRESULT WINAPI IDirectSoundBuffer_SetPan(
982         LPDIRECTSOUNDBUFFER this,LONG pan
983 ) {
984         double  temp;
985
986         TRACE(dsound,"(%p,%ld)\n",this,pan);
987
988         if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT))
989                 return DSERR_INVALIDPARAM;
990
991         /* You cannot set the pan of the primary buffer */
992         /* and you cannot use both pan and 3D controls */
993         if (!(this->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
994             (this->dsbd.dwFlags & DSBCAPS_CTRL3D) ||
995             (this->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
996                 return DSERR_CONTROLUNAVAIL;
997
998         /* **** */
999         EnterCriticalSection(&(this->lock));
1000
1001         this->pan = pan;
1002         
1003         temp = (double) (this->volume - (this->pan > 0 ? this->pan : 0));
1004         this->lVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 32768.0);
1005         temp = (double) (this->volume + (this->pan < 0 ? this->pan : 0));
1006         this->rVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 32768.0);
1007
1008         LeaveCriticalSection(&(this->lock));
1009         /* **** */
1010
1011         return DS_OK;
1012 }
1013
1014 static HRESULT WINAPI IDirectSoundBuffer_GetPan(
1015         LPDIRECTSOUNDBUFFER this,LPLONG pan
1016 ) {
1017         TRACE(dsound,"(%p,%p)\n",this,pan);
1018
1019         if (pan == NULL)
1020                 return DSERR_INVALIDPARAM;
1021
1022         *pan = this->pan;
1023
1024         return DS_OK;
1025 }
1026
1027 static HRESULT WINAPI IDirectSoundBuffer_Unlock(
1028         LPDIRECTSOUNDBUFFER this,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
1029 ) {
1030         TRACE(dsound,"(%p,%p,%ld,%p,%ld):stub\n", this,p1,x1,p2,x2);
1031
1032         /* There is really nothing to do here. Should someone */
1033         /* choose to implement static buffers in hardware (by */
1034         /* using a wave table synth, for example) this is where */
1035         /* you'd want to do the loading. For software buffers, */
1036         /* which is what we currently use, we need do nothing. */
1037
1038 #if 0
1039         /* It's also the place to pre-process 3D buffers... */
1040         
1041         /* This is highly experimental and liable to break things */
1042         if (this->dsbd.dwFlags & DSBCAPS_CTRL3D)
1043                 DSOUND_Create3DBuffer(this);
1044 #endif
1045
1046         return DS_OK;
1047 }
1048
1049 static HRESULT WINAPI IDirectSoundBuffer_GetFrequency(
1050         LPDIRECTSOUNDBUFFER this,LPDWORD freq
1051 ) {
1052         TRACE(dsound,"(%p,%p)\n",this,freq);
1053
1054         if (freq == NULL)
1055                 return DSERR_INVALIDPARAM;
1056
1057         *freq = this->freq;
1058
1059         return DS_OK;
1060 }
1061
1062 static HRESULT WINAPI IDirectSoundBuffer_Initialize(
1063         LPDIRECTSOUNDBUFFER this,LPDIRECTSOUND dsound,LPDSBUFFERDESC dbsd
1064 ) {
1065         FIXME(dsound,"(%p,%p,%p):stub\n",this,dsound,dbsd);
1066         printf("Re-Init!!!\n");
1067         return DSERR_ALREADYINITIALIZED;
1068 }
1069
1070 static HRESULT WINAPI IDirectSoundBuffer_GetCaps(
1071         LPDIRECTSOUNDBUFFER this,LPDSBCAPS caps
1072 ) {
1073         TRACE(dsound,"(%p)->(%p)\n",this,caps);
1074   
1075         if (caps == NULL)
1076                 return DSERR_INVALIDPARAM;
1077
1078         /* I think we should check this value, not set it. See */
1079         /* Inside DirectX, p215. That should apply here, too. */
1080         caps->dwSize = sizeof(*caps);
1081
1082         caps->dwFlags = this->dsbd.dwFlags | DSBCAPS_LOCSOFTWARE;
1083         caps->dwBufferBytes = this->dsbd.dwBufferBytes;
1084         /* This value represents the speed of the "unlock" command.
1085            As unlock is quite fast (it does not do anything), I put
1086            4096 ko/s = 4 Mo / s */
1087         caps->dwUnlockTransferRate = 4096;
1088         caps->dwPlayCpuOverhead = 0;
1089         
1090         return DS_OK;
1091 }
1092
1093 static HRESULT WINAPI IDirectSoundBuffer_QueryInterface(
1094         LPDIRECTSOUNDBUFFER this,REFIID riid,LPVOID *ppobj
1095 ) {
1096         char    xbuf[50];
1097
1098         WINE_StringFromCLSID(riid,xbuf);
1099         TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
1100
1101         if (!memcmp(&IID_IDirectSoundNotify,riid,sizeof(*riid))) {
1102                 IDirectSoundNotify      *dsn;
1103
1104                 dsn = (LPDIRECTSOUNDNOTIFY)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn));
1105                 dsn->ref = 1;
1106                 dsn->dsb = this;
1107                 this->lpvtbl->fnAddRef(this);
1108                 dsn->lpvtbl = &dsnvt;
1109                 *ppobj = (LPVOID)dsn;
1110                 return 0;
1111         }
1112
1113         if (!memcmp(&IID_IDirectSound3DBuffer,riid,sizeof(*riid))) {
1114                 *ppobj = this->ds3db;
1115                 if (*ppobj)
1116                         return DS_OK;
1117         }
1118
1119         return E_FAIL;
1120 }
1121
1122 static struct tagLPDIRECTSOUNDBUFFER_VTABLE dsbvt = {
1123         IDirectSoundBuffer_QueryInterface,
1124         IDirectSoundBuffer_AddRef,
1125         IDirectSoundBuffer_Release,
1126         IDirectSoundBuffer_GetCaps,
1127         IDirectSoundBuffer_GetCurrentPosition,
1128         IDirectSoundBuffer_GetFormat,
1129         IDirectSoundBuffer_GetVolume,
1130         IDirectSoundBuffer_GetPan,
1131         IDirectSoundBuffer_GetFrequency,
1132         IDirectSoundBuffer_GetStatus,
1133         IDirectSoundBuffer_Initialize,
1134         IDirectSoundBuffer_Lock,
1135         IDirectSoundBuffer_Play,
1136         IDirectSoundBuffer_SetCurrentPosition,
1137         IDirectSoundBuffer_SetFormat,
1138         IDirectSoundBuffer_SetVolume,
1139         IDirectSoundBuffer_SetPan,
1140         IDirectSoundBuffer_SetFrequency,
1141         IDirectSoundBuffer_Stop,
1142         IDirectSoundBuffer_Unlock
1143 };
1144
1145 /*******************************************************************************
1146  *              IDirectSound
1147  */
1148
1149 static HRESULT WINAPI IDirectSound_SetCooperativeLevel(
1150         LPDIRECTSOUND this,HWND32 hwnd,DWORD level
1151 ) {
1152         FIXME(dsound,"(%p,%08lx,%ld):stub\n",this,(DWORD)hwnd,level);
1153         return 0;
1154 }
1155
1156 static HRESULT WINAPI IDirectSound_CreateSoundBuffer(
1157         LPDIRECTSOUND this,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER ppdsb,LPUNKNOWN lpunk
1158 ) {
1159         LPWAVEFORMATEX  wfex;
1160
1161         TRACE(dsound,"(%p,%p,%p,%p)\n",this,dsbd,ppdsb,lpunk);
1162         
1163         if ((this == NULL) || (dsbd == NULL) || (ppdsb == NULL))
1164                 return DSERR_INVALIDPARAM;
1165         
1166         if (TRACE_ON(dsound)) {
1167                 TRACE(dsound,"(size=%ld)\n",dsbd->dwSize);
1168                 TRACE(dsound,"(flags=0x%08lx\n",dsbd->dwFlags);
1169                 _dump_DSBCAPS(dsbd->dwFlags);
1170                 TRACE(dsound,"(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
1171                 TRACE(dsound,"(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
1172         }
1173
1174         wfex = dsbd->lpwfxFormat;
1175
1176         if (wfex)
1177                 TRACE(dsound,"(formattag=0x%04x,chans=%d,samplerate=%ld"
1178                    "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1179                    wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
1180                    wfex->nAvgBytesPerSec, wfex->nBlockAlign, 
1181                    wfex->wBitsPerSample, wfex->cbSize);
1182
1183         if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
1184                 if (primarybuf) {
1185                         primarybuf->lpvtbl->fnAddRef(primarybuf);
1186                         *ppdsb = primarybuf;
1187                         return DS_OK;
1188                 } /* Else create primarybuf */
1189         }
1190
1191         *ppdsb = (LPDIRECTSOUNDBUFFER)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBuffer));
1192         if (*ppdsb == NULL)
1193                 return DSERR_OUTOFMEMORY;
1194         (*ppdsb)->ref = 1;
1195
1196         TRACE(dsound, "Created buffer at %p\n", *ppdsb);
1197
1198         if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
1199                 (*ppdsb)->buflen = dsound->wfx.nAvgBytesPerSec;
1200                 (*ppdsb)->freq = dsound->wfx.nSamplesPerSec;
1201         } else {
1202                 (*ppdsb)->buflen = dsbd->dwBufferBytes;
1203                 (*ppdsb)->freq = dsbd->lpwfxFormat->nSamplesPerSec;
1204         }
1205         (*ppdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,(*ppdsb)->buflen);
1206         if ((*ppdsb)->buffer == NULL) {
1207                 HeapFree(GetProcessHeap(),0,(*ppdsb));
1208                 *ppdsb = NULL;
1209                 return DSERR_OUTOFMEMORY;
1210         }
1211         /* It's not necessary to initialize values to zero since */
1212         /* we allocated this structure with HEAP_ZERO_MEMORY... */
1213         (*ppdsb)->playpos = 0;
1214         (*ppdsb)->writepos = 0;
1215         (*ppdsb)->parent = NULL;
1216         (*ppdsb)->lpvtbl = &dsbvt;
1217         (*ppdsb)->dsound = this;
1218         (*ppdsb)->playing = 0;
1219         (*ppdsb)->lVolAdjust = (1 << 15);
1220         (*ppdsb)->rVolAdjust = (1 << 15);
1221
1222         if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
1223                 (*ppdsb)->freqAdjust = ((*ppdsb)->freq << DSOUND_FREQSHIFT) /
1224                         primarybuf->wfx.nSamplesPerSec;
1225                 (*ppdsb)->nAvgBytesPerSec = (*ppdsb)->freq *
1226                         dsbd->lpwfxFormat->nBlockAlign;
1227         }
1228
1229         memcpy(&((*ppdsb)->dsbd),dsbd,sizeof(*dsbd));
1230
1231         /* register buffer */
1232         if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
1233                 this->buffers = (LPDIRECTSOUNDBUFFER*)HeapReAlloc(GetProcessHeap(),0,this->buffers,sizeof(LPDIRECTSOUNDBUFFER)*(this->nrofbuffers+1));
1234                 this->buffers[this->nrofbuffers] = *ppdsb;
1235                 this->nrofbuffers++;
1236         }
1237         this->lpvtbl->fnAddRef(this);
1238
1239         if (dsbd->lpwfxFormat)
1240                 memcpy(&((*ppdsb)->wfx), dsbd->lpwfxFormat, sizeof((*ppdsb)->wfx));
1241
1242         InitializeCriticalSection(&((*ppdsb)->lock));
1243         
1244 #if USE_DSOUND3D
1245         if (dsbd->dwFlags & DSBCAPS_CTRL3D) {
1246                 IDirectSound3DBuffer    *ds3db;
1247
1248                 ds3db = (LPDIRECTSOUND3DBUFFER)HeapAlloc(GetProcessHeap(),
1249                         0,sizeof(*ds3db));
1250                 ds3db->ref = 1;
1251                 ds3db->dsb = (*ppdsb);
1252                 ds3db->lpvtbl = &ds3dbvt;
1253                 (*ppdsb)->ds3db = ds3db;
1254                 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
1255                 ds3db->ds3db.vPosition.x.x = 0.0;
1256                 ds3db->ds3db.vPosition.y.y = 0.0;
1257                 ds3db->ds3db.vPosition.z.z = 0.0;
1258                 ds3db->ds3db.vVelocity.x.x = 0.0;
1259                 ds3db->ds3db.vVelocity.y.y = 0.0;
1260                 ds3db->ds3db.vVelocity.z.z = 0.0;
1261                 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
1262                 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
1263                 ds3db->ds3db.vConeOrientation.x.x = 0.0;
1264                 ds3db->ds3db.vConeOrientation.y.y = 0.0;
1265                 ds3db->ds3db.vConeOrientation.z.z = 0.0;
1266                 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME;
1267                 ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
1268                 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
1269                 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
1270                 ds3db->buflen = ((*ppdsb)->buflen * primarybuf->wfx.nBlockAlign) /
1271                         (*ppdsb)->wfx.nBlockAlign;
1272                 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
1273                 if (ds3db->buffer == NULL) {
1274                         ds3db->buflen = 0;
1275                         ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
1276                 }
1277         }
1278 #endif
1279         return DS_OK;
1280 }
1281
1282 static HRESULT WINAPI IDirectSound_DuplicateSoundBuffer(
1283         LPDIRECTSOUND this,LPDIRECTSOUNDBUFFER pdsb,LPLPDIRECTSOUNDBUFFER ppdsb
1284 ) {
1285         TRACE(dsound,"(%p,%p,%p)\n",this,pdsb,ppdsb);
1286
1287         *ppdsb = (LPDIRECTSOUNDBUFFER)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBuffer));
1288
1289         pdsb->lpvtbl->fnAddRef(pdsb);
1290         memcpy(*ppdsb, pdsb, sizeof(IDirectSoundBuffer));
1291         (*ppdsb)->ref = 1;
1292         (*ppdsb)->playpos = 0;
1293         (*ppdsb)->writepos = 0;
1294         (*ppdsb)->dsound = this;
1295         (*ppdsb)->parent = pdsb;
1296         memcpy(&((*ppdsb)->wfx), &(pdsb->wfx), sizeof((*ppdsb)->wfx));
1297         /* register buffer */
1298         this->buffers = (LPDIRECTSOUNDBUFFER*)HeapReAlloc(GetProcessHeap(),0,this->buffers,sizeof(LPDIRECTSOUNDBUFFER)*(this->nrofbuffers+1));
1299         this->buffers[this->nrofbuffers] = *ppdsb;
1300         this->nrofbuffers++;
1301         this->lpvtbl->fnAddRef(this);
1302         return 0;
1303 }
1304
1305
1306 static HRESULT WINAPI IDirectSound_GetCaps(LPDIRECTSOUND this,LPDSCAPS caps) {
1307         TRACE(dsound,"(%p,%p)\n",this,caps);
1308         TRACE(dsound,"(flags=0x%08lx)\n",caps->dwFlags);
1309
1310         if (caps == NULL)
1311                 return DSERR_INVALIDPARAM;
1312
1313         /* We should check this value, not set it. See Inside DirectX, p215. */
1314         caps->dwSize = sizeof(*caps);
1315
1316         caps->dwFlags =
1317                 DSCAPS_PRIMARYSTEREO |
1318                 DSCAPS_PRIMARY16BIT |
1319                 DSCAPS_SECONDARYSTEREO |
1320                 DSCAPS_SECONDARY16BIT |
1321                 DSCAPS_CONTINUOUSRATE;
1322         /* FIXME: query OSS */
1323         caps->dwMinSecondarySampleRate          = DSBFREQUENCY_MIN;
1324         caps->dwMaxSecondarySampleRate          = DSBFREQUENCY_MAX;
1325
1326         caps->dwPrimaryBuffers                  = 1;
1327
1328         caps->dwMaxHwMixingAllBuffers           = 0;
1329         caps->dwMaxHwMixingStaticBuffers        = 0;
1330         caps->dwMaxHwMixingStreamingBuffers     = 0;
1331
1332         caps->dwFreeHwMixingAllBuffers          = 0;
1333         caps->dwFreeHwMixingStaticBuffers       = 0;
1334         caps->dwFreeHwMixingStreamingBuffers    = 0;
1335
1336         caps->dwMaxHw3DAllBuffers               = 0;
1337         caps->dwMaxHw3DStaticBuffers            = 0;
1338         caps->dwMaxHw3DStreamingBuffers         = 0;
1339
1340         caps->dwFreeHw3DAllBuffers              = 0;
1341         caps->dwFreeHw3DStaticBuffers           = 0;
1342         caps->dwFreeHw3DStreamingBuffers        = 0;
1343
1344         caps->dwTotalHwMemBytes                 = 0;
1345
1346         caps->dwFreeHwMemBytes                  = 0;
1347
1348         caps->dwMaxContigFreeHwMemBytes         = 0;
1349
1350         caps->dwUnlockTransferRateHwBuffers     = 4096; /* But we have none... */
1351
1352         caps->dwPlayCpuOverheadSwBuffers        = 1;    /* 1% */
1353
1354         return 0;
1355 }
1356
1357 static ULONG WINAPI IDirectSound_AddRef(LPDIRECTSOUND this) {
1358         return ++(this->ref);
1359 }
1360
1361 static ULONG WINAPI IDirectSound_Release(LPDIRECTSOUND this) {
1362         TRACE(dsound,"(%p), ref was %ld\n",this,this->ref);
1363         if (!--(this->ref)) {
1364                 DSOUND_CloseAudio();
1365                 while(IDirectSoundBuffer_Release(primarybuf)); /* Deallocate */
1366                 FIXME(dsound, "need to release all buffers!\n");
1367                 HeapFree(GetProcessHeap(),0,this);
1368                 dsound = NULL;
1369                 return 0;
1370         }
1371         return this->ref;
1372 }
1373
1374 static HRESULT WINAPI IDirectSound_SetSpeakerConfig(
1375         LPDIRECTSOUND this,DWORD config
1376 ) {
1377         FIXME(dsound,"(%p,0x%08lx):stub\n",this,config);
1378         return 0;
1379 }
1380
1381 static HRESULT WINAPI IDirectSound_QueryInterface(
1382         LPDIRECTSOUND this,REFIID riid,LPVOID *ppobj
1383 ) {
1384         char xbuf[50];
1385
1386         if (!memcmp(&IID_IDirectSound3DListener,riid,sizeof(*riid))) {
1387
1388                 if (this->listener) {
1389                         *ppobj = this->listener;
1390                         return DS_OK;
1391                 }
1392                 this->listener = (LPDIRECTSOUND3DLISTENER)HeapAlloc(
1393                         GetProcessHeap(), 0, sizeof(*(this->listener)));
1394                 this->listener->ref = 1;
1395                 this->listener->lpvtbl = &ds3dlvt;
1396                 this->lpvtbl->fnAddRef(this);
1397                 this->listener->ds3dl.dwSize = sizeof(DS3DLISTENER);
1398                 this->listener->ds3dl.vPosition.x.x = 0.0;
1399                 this->listener->ds3dl.vPosition.y.y = 0.0;
1400                 this->listener->ds3dl.vPosition.z.z = 0.0;
1401                 this->listener->ds3dl.vVelocity.x.x = 0.0;
1402                 this->listener->ds3dl.vVelocity.y.y = 0.0;
1403                 this->listener->ds3dl.vVelocity.z.z = 0.0;
1404                 this->listener->ds3dl.vOrientFront.x.x = 0.0;
1405                 this->listener->ds3dl.vOrientFront.y.y = 0.0;
1406                 this->listener->ds3dl.vOrientFront.z.z = 1.0;
1407                 this->listener->ds3dl.vOrientTop.x.x = 0.0;
1408                 this->listener->ds3dl.vOrientTop.y.y = 1.0;
1409                 this->listener->ds3dl.vOrientTop.z.z = 0.0;
1410                 this->listener->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
1411                 this->listener->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
1412                 this->listener->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
1413                 *ppobj = (LPVOID)this->listener;
1414                 return DS_OK;
1415         }
1416
1417         WINE_StringFromCLSID(riid,xbuf);
1418         TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
1419         return E_FAIL;
1420 }
1421
1422 static HRESULT WINAPI IDirectSound_Compact(
1423         LPDIRECTSOUND this)
1424 {
1425         TRACE(dsound, "(%p)\n", this);
1426         return DS_OK;
1427 }
1428
1429 static HRESULT WINAPI IDirectSound_GetSpeakerConfig(
1430         LPDIRECTSOUND this,
1431         LPDWORD lpdwSpeakerConfig)
1432 {
1433         TRACE(dsound, "(%p, %p)\n", this, lpdwSpeakerConfig);
1434         *lpdwSpeakerConfig = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16);
1435         return DS_OK;
1436 }
1437
1438 static HRESULT WINAPI IDirectSound_Initialize(
1439         LPDIRECTSOUND this,
1440         LPGUID lpGuid)
1441 {
1442         TRACE(dsound, "(%p, %p)\n", this, lpGuid);
1443         return DS_OK;
1444 }
1445
1446 static struct tagLPDIRECTSOUND_VTABLE dsvt = {
1447         IDirectSound_QueryInterface,
1448         IDirectSound_AddRef,
1449         IDirectSound_Release,
1450         IDirectSound_CreateSoundBuffer,
1451         IDirectSound_GetCaps,
1452         IDirectSound_DuplicateSoundBuffer,
1453         IDirectSound_SetCooperativeLevel,
1454         IDirectSound_Compact,
1455         IDirectSound_GetSpeakerConfig,
1456         IDirectSound_SetSpeakerConfig,
1457         IDirectSound_Initialize
1458 };
1459
1460
1461 /* See http://www.opensound.com/pguide/audio.html for more details */
1462
1463 static int
1464 DSOUND_setformat(LPWAVEFORMATEX wfex) {
1465         int     xx,channels,speed,format,nformat;
1466
1467         if (!audioOK) {
1468                 TRACE(dsound, "(%p) deferred\n", wfex);
1469                 return 0;
1470         }
1471         switch (wfex->wFormatTag) {
1472         default:
1473                 WARN(dsound,"unknown WAVE_FORMAT tag %d\n",wfex->wFormatTag);
1474                 return DSERR_BADFORMAT;
1475         case WAVE_FORMAT_PCM:
1476                 break;
1477         }
1478         if (wfex->wBitsPerSample==8)
1479                 format = AFMT_U8;
1480         else
1481                 format = AFMT_S16_LE;
1482
1483         if (-1==ioctl(audiofd,SNDCTL_DSP_GETFMTS,&xx)) {
1484                 perror("ioctl SNDCTL_DSP_GETFMTS");
1485                 return -1;
1486         }
1487         if ((xx&format)!=format) {/* format unsupported */
1488                 FIXME(dsound,"SNDCTL_DSP_GETFMTS: format not supported\n"); 
1489                 return -1;
1490         }
1491         nformat = format;
1492         if (-1==ioctl(audiofd,SNDCTL_DSP_SETFMT,&nformat)) {
1493                 perror("ioctl SNDCTL_DSP_SETFMT");
1494                 return -1;
1495         }
1496         if (nformat!=format) {/* didn't work */
1497                 FIXME(dsound,"SNDCTL_DSP_GETFMTS: format not set\n"); 
1498                 return -1;
1499         }
1500
1501         channels = wfex->nChannels-1;
1502         if (-1==ioctl(audiofd,SNDCTL_DSP_STEREO,&channels)) {
1503                 perror("ioctl SNDCTL_DSP_STEREO");
1504                 return -1;
1505         }
1506         speed = wfex->nSamplesPerSec;
1507         if (-1==ioctl(audiofd,SNDCTL_DSP_SPEED,&speed)) {
1508                 perror("ioctl SNDCTL_DSP_SPEED");
1509                 return -1;
1510         }
1511         TRACE(dsound,"(freq=%ld,channels=%d,bits=%d)\n",
1512                 wfex->nSamplesPerSec,wfex->nChannels,wfex->wBitsPerSample
1513         );
1514         return 0;
1515 }
1516
1517 static void DSOUND_CheckEvent(IDirectSoundBuffer *dsb, int len)
1518 {
1519         int                     i;
1520         DWORD                   offset;
1521         LPDSBPOSITIONNOTIFY     event;
1522
1523         if (dsb->nrofnotifies == 0)
1524                 return;
1525
1526         TRACE(dsound,"(%p) buflen = %ld, playpos = %ld, len = %d\n",
1527                 dsb, dsb->buflen, dsb->playpos, len);
1528         for (i = 0; i < dsb->nrofnotifies ; i++) {
1529                 event = dsb->notifies + i;
1530                 offset = event->dwOffset;
1531                 TRACE(dsound, "checking %d, position %ld, event = %d\n",
1532                         i, offset, event->hEventNotify);
1533                 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
1534                 /* OK. [Inside DirectX, p274] */
1535                 /*  */
1536                 /* This also means we can't sort the entries by offset, */
1537                 /* because DSBPN_OFFSETSTOP == -1 */
1538                 if (offset == DSBPN_OFFSETSTOP) {
1539                         if (dsb->playing == 0) {
1540                                 SetEvent(event->hEventNotify);
1541                                 TRACE(dsound,"signalled event %d (%d)\n", event->hEventNotify, i);
1542                                 return;
1543                         } else
1544                                 return;
1545                 }
1546                 if ((dsb->playpos + len) >= dsb->buflen) {
1547                         if ((offset < ((dsb->playpos + len) % dsb->buflen)) ||
1548                             (offset >= dsb->playpos)) {
1549                                 TRACE(dsound,"signalled event %d (%d)\n", event->hEventNotify, i);
1550                                 SetEvent(event->hEventNotify);
1551                         }
1552                 } else {
1553                         if ((offset >= dsb->playpos) && (offset < (dsb->playpos + len))) {
1554                                 TRACE(dsound,"signalled event %d (%d)\n", event->hEventNotify, i);
1555                                 SetEvent(event->hEventNotify);
1556                         }
1557                 }
1558         }
1559 }
1560
1561 /* WAV format info can be found at: */
1562 /* */
1563 /*      http://www.cwi.nl/ftp/audio/AudioFormats.part2 */
1564 /*      ftp://ftp.cwi.nl/pub/audio/RIFF-format */
1565 /* */
1566 /* Import points to remember: */
1567 /* */
1568 /*      8-bit WAV is unsigned */
1569 /*      16-bit WAV is signed */
1570
1571 static inline INT16 cvtU8toS16(BYTE byte)
1572 {
1573         INT16   s = (byte - 128) << 8;
1574
1575         return s;
1576 }
1577
1578 static inline BYTE cvtS16toU8(INT16 word)
1579 {
1580         BYTE    b = (word + 32768) >> 8;
1581         
1582         return b;
1583 }
1584
1585
1586 /* We should be able to optimize these two inline functions */
1587 /* so that we aren't doing 8->16->8 conversions when it is */
1588 /* not necessary. But this is still a WIP. Optimize later. */
1589 static inline void get_fields(const IDirectSoundBuffer *dsb, BYTE *buf, INT32 *fl, INT32 *fr)
1590 {
1591         INT16   *bufs = (INT16 *) buf;
1592
1593         /* TRACE(dsound, "(%p)", buf); */
1594         if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 2) {
1595                 *fl = cvtU8toS16(*buf);
1596                 *fr = cvtU8toS16(*(buf + 1));
1597                 return;
1598         }
1599
1600         if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 2) {
1601                 *fl = *bufs;
1602                 *fr = *(bufs + 1);
1603                 return;
1604         }
1605
1606         if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 1) {
1607                 *fl = cvtU8toS16(*buf);
1608                 *fr = *fl;
1609                 return;
1610         }
1611
1612         if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 1) {
1613                 *fl = *bufs;
1614                 *fr = *bufs;
1615                 return;
1616         }
1617
1618         FIXME(dsound, "get_fields found an unsupported configuration\n");
1619         return;
1620 }
1621
1622 static inline void set_fields(BYTE *buf, INT32 fl, INT32 fr)
1623 {
1624         INT16 *bufs = (INT16 *) buf;
1625
1626         if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 2)) {
1627                 *buf = cvtS16toU8(fl);
1628                 *(buf + 1) = cvtS16toU8(fr);
1629                 return;
1630         }
1631
1632         if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 2)) {
1633                 *bufs = fl;
1634                 *(bufs + 1) = fr;
1635                 return;
1636         }
1637
1638         if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 1)) {
1639                 *buf = cvtS16toU8((fl + fr) >> 1);
1640                 return;
1641         }
1642
1643         if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 1)) {
1644                 *bufs = (fl + fr) >> 1;
1645                 return;
1646         }
1647         FIXME(dsound, "set_fields found an unsupported configuration\n");
1648         return;
1649 }
1650
1651 /* Now with PerfectPitch (tm) technology */
1652 static INT32 DSOUND_MixerNorm(IDirectSoundBuffer *dsb, BYTE *buf, INT32 len)
1653 {
1654         INT32   i, size, ipos, ilen, fieldL, fieldR;
1655         BYTE    *ibp, *obp;
1656         INT32   iAdvance = dsb->wfx.nBlockAlign;
1657         INT32   oAdvance = primarybuf->wfx.nBlockAlign;
1658
1659         ibp = dsb->buffer + dsb->playpos;
1660         obp = buf;
1661
1662         TRACE(dsound, "(%p, %p, %p), playpos=%8.8lx\n", dsb, ibp, obp, dsb->playpos);
1663         /* Check for the best case */
1664         if ((dsb->freq == primarybuf->wfx.nSamplesPerSec) &&
1665             (dsb->wfx.wBitsPerSample == primarybuf->wfx.wBitsPerSample) &&
1666             (dsb->wfx.nChannels == primarybuf->wfx.nChannels)) {
1667                 TRACE(dsound, "(%p) Best case\n", dsb);
1668                 if ((ibp + len) < (BYTE *)(dsb->buffer + dsb->buflen))
1669                         memcpy(obp, ibp, len);
1670                 else { /* wrap */
1671                         memcpy(obp, ibp, dsb->buflen - dsb->playpos);
1672                         memcpy(obp + (dsb->buflen - dsb->playpos),
1673                             dsb->buffer,
1674                             len - (dsb->buflen - dsb->playpos));
1675                 }
1676                 return len;
1677         }
1678         
1679         /* Check for same sample rate */
1680         if (dsb->freq == primarybuf->wfx.nSamplesPerSec) {
1681                 TRACE(dsound, "(%p) Same sample rate %ld = primary %ld\n", dsb,
1682                         dsb->freq, primarybuf->wfx.nSamplesPerSec);
1683                 ilen = 0;
1684                 for (i = 0; i < len; i += oAdvance) {
1685                         get_fields(dsb, ibp, &fieldL, &fieldR);
1686                         ibp += iAdvance;
1687                         ilen += iAdvance;
1688                         set_fields(obp, fieldL, fieldR);
1689                         obp += oAdvance;
1690                         if (ibp >= (BYTE *)(dsb->buffer + dsb->buflen))
1691                                 ibp = dsb->buffer;      /* wrap */
1692                 }
1693                 return (ilen);  
1694         }
1695
1696         /* Mix in different sample rates */
1697         /* */
1698         /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
1699         /* Patent Pending :-] */
1700
1701         TRACE(dsound, "(%p) Adjusting frequency: %ld -> %ld\n",
1702                 dsb, dsb->freq, primarybuf->wfx.nSamplesPerSec);
1703
1704         size = len / oAdvance;
1705         ilen = ((size * dsb->freqAdjust) >> DSOUND_FREQSHIFT) * iAdvance;
1706         for (i = 0; i < size; i++) {
1707
1708                 ipos = (((i * dsb->freqAdjust) >> DSOUND_FREQSHIFT) * iAdvance) + dsb->playpos;
1709
1710                 if (ipos >= dsb->buflen)
1711                         ipos %= dsb->buflen;    /* wrap */
1712
1713                 get_fields(dsb, (dsb->buffer + ipos), &fieldL, &fieldR);
1714                 set_fields(obp, fieldL, fieldR);
1715                 obp += oAdvance;
1716         }
1717         return ilen;
1718 }
1719
1720 static void DSOUND_MixerVol(IDirectSoundBuffer *dsb, BYTE *buf, INT32 len)
1721 {
1722         INT32   i, inc = primarybuf->wfx.wBitsPerSample >> 3;
1723         BYTE    *bpc = buf;
1724         INT16   *bps = (INT16 *) buf;
1725         
1726         TRACE(dsound, "(%p) left = %lx, right = %lx\n", dsb,
1727                 dsb->lVolAdjust, dsb->rVolAdjust);
1728         if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->pan == 0)) &&
1729             (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->volume == 0)) &&
1730             !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
1731                 return;         /* Nothing to do */
1732
1733         /* If we end up with some bozo coder using panning or 3D sound */
1734         /* with a mono primary buffer, it could sound very weird using */
1735         /* this method. Oh well, tough patooties. */
1736
1737         for (i = 0; i < len; i += inc) {
1738                 INT32   val;
1739
1740                 switch (inc) {
1741
1742                 case 1:
1743                         /* 8-bit WAV is unsigned, but we need to operate */
1744                         /* on signed data for this to work properly */
1745                         val = *bpc - 128;
1746                         val = ((val * (i & inc ? dsb->rVolAdjust : dsb->lVolAdjust)) >> 15);
1747                         *bpc = val + 128;
1748                         bpc++;
1749                         break;
1750                 case 2:
1751                         /* 16-bit WAV is signed -- much better */
1752                         val = *bps;
1753                         val = ((val * ((i & inc) ? dsb->rVolAdjust : dsb->lVolAdjust)) >> 15);
1754                         *bps = val;
1755                         bps++;
1756                         break;
1757                 default:
1758                         /* Very ugly! */
1759                         FIXME(dsound, "MixerVol had a nasty error\n");
1760                 }
1761         }               
1762 }
1763
1764 #ifdef USE_DSOUND3D
1765 static void DSOUND_Mixer3D(IDirectSoundBuffer *dsb, BYTE *buf, INT32 len)
1766 {
1767         BYTE    *ibp, *obp;
1768         DWORD   buflen, playpos;
1769
1770         buflen = dsb->ds3db->buflen;
1771         playpos = (dsb->playpos * primarybuf->wfx.nBlockAlign) / dsb->wfx.nBlockAlign;
1772         ibp = dsb->ds3db->buffer + playpos;
1773         obp = buf;
1774
1775         if (playpos > buflen) {
1776                 FIXME(dsound, "Major breakage");
1777                 return;
1778         }
1779
1780         if (len <= (playpos + buflen))
1781                 memcpy(obp, ibp, len);
1782         else { /* wrap */
1783                 memcpy(obp, ibp, buflen - playpos);
1784                 memcpy(obp + (buflen - playpos),
1785                     dsb->buffer,
1786                     len - (buflen - playpos));
1787         }
1788         return;
1789 }
1790 #endif
1791
1792 static DWORD DSOUND_MixInBuffer(IDirectSoundBuffer *dsb)
1793 {
1794         INT32   i, len, ilen, temp, field;
1795         INT32   advance = primarybuf->wfx.wBitsPerSample >> 3;
1796         BYTE    *buf, *ibuf, *obuf;
1797         INT16   *ibufs, *obufs;
1798
1799         len = DSOUND_FRAGLEN;                   /* The most we will use */
1800         if (!(dsb->playflags & DSBPLAY_LOOPING)) {
1801                 temp = MulDiv32(primarybuf->wfx.nAvgBytesPerSec, dsb->buflen,
1802                         dsb->nAvgBytesPerSec) -
1803                        MulDiv32(primarybuf->wfx.nAvgBytesPerSec, dsb->playpos,
1804                         dsb->nAvgBytesPerSec);
1805                 len = (len > temp) ? temp : len;
1806         }
1807         len &= ~3;                              /* 4 byte alignment */
1808
1809         if (len == 0) {
1810                 /* This should only happen if we aren't looping and temp < 4 */
1811
1812                 /* We skip the remainder, so check for possible events */
1813                 DSOUND_CheckEvent(dsb, dsb->buflen - dsb->playpos);
1814                 /* Stop */
1815                 dsb->playing = 0;
1816                 dsb->writepos = 0;
1817                 dsb->playpos = 0;
1818                 /* Check for DSBPN_OFFSETSTOP */
1819                 DSOUND_CheckEvent(dsb, 0);
1820                 return 0;
1821         }
1822
1823         /* Been seeing segfaults in malloc() for some reason... */
1824         TRACE(dsound, "allocating buffer (size = %d)\n", len);
1825         if ((buf = ibuf = (BYTE *) malloc(len)) == NULL)
1826                 return 0;
1827
1828         TRACE(dsound, "MixInBuffer (%p) len = %d\n", dsb, len);
1829
1830         ilen = DSOUND_MixerNorm(dsb, ibuf, len);
1831         if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
1832             (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
1833                 DSOUND_MixerVol(dsb, ibuf, len);
1834
1835         obuf = primarybuf->buffer + primarybuf->playpos;
1836         for (i = 0; i < len; i += advance) {
1837                 obufs = (INT16 *) obuf;
1838                 ibufs = (INT16 *) ibuf;
1839                 if (primarybuf->wfx.wBitsPerSample == 8) {
1840                         /* 8-bit WAV is unsigned */
1841                         field = (*ibuf - 128);
1842                         field += (*obuf - 128);
1843                         field = field > 127 ? 127 : field;
1844                         field = field < -128 ? -128 : field;
1845                         *obuf = field + 128;
1846                 } else {
1847                         /* 16-bit WAV is signed */
1848                         field = *ibufs;
1849                         field += *obufs;
1850                         field = field > 32767 ? 32767 : field;
1851                         field = field < -32768 ? -32768 : field;
1852                         *obufs = field;
1853                 }
1854                 ibuf += advance;
1855                 obuf += advance;
1856                 if (obuf >= (BYTE *)(primarybuf->buffer + primarybuf->buflen))
1857                         obuf = primarybuf->buffer;
1858         }
1859         free(buf);
1860
1861         if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY)
1862                 DSOUND_CheckEvent(dsb, ilen);
1863
1864         dsb->playpos += ilen;
1865         dsb->writepos = dsb->playpos + ilen;
1866         
1867         if (dsb->playpos >= dsb->buflen) {
1868                 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
1869                         dsb->playing = 0;
1870                         dsb->writepos = 0;
1871                         dsb->playpos = 0;
1872                         DSOUND_CheckEvent(dsb, 0);              /* For DSBPN_OFFSETSTOP */
1873                 } else
1874                         dsb->playpos %= dsb->buflen;            /* wrap */
1875         }
1876         
1877         if (dsb->writepos >= dsb->buflen)
1878                 dsb->writepos %= dsb->buflen;
1879
1880         return len;
1881 }
1882
1883 static DWORD WINAPI DSOUND_MixPrimary(void)
1884 {
1885         INT32                   i, len, maxlen = 0;
1886         IDirectSoundBuffer      *dsb;
1887
1888         for (i = dsound->nrofbuffers - 1; i >= 0; i--) {
1889                 dsb = dsound->buffers[i];
1890
1891                 if (!dsb || !(dsb->lpvtbl))
1892                         continue;
1893                 dsb->lpvtbl->fnAddRef(dsb);
1894                 if (dsb->buflen && dsb->playing) {
1895                         EnterCriticalSection(&(dsb->lock));
1896                         len = DSOUND_MixInBuffer(dsb);
1897                         maxlen = len > maxlen ? len : maxlen;
1898                         LeaveCriticalSection(&(dsb->lock));
1899                 }
1900                 dsb->lpvtbl->fnRelease(dsb);
1901         }
1902         
1903         return maxlen;
1904 }
1905
1906 static int DSOUND_OpenAudio(void)
1907 {
1908         int     audioFragment;
1909
1910         if (primarybuf == NULL)
1911                 return DSERR_OUTOFMEMORY;
1912
1913         while (audiofd != -1)
1914                 sleep(5);
1915         audiofd = open("/dev/audio",O_WRONLY);
1916         if (audiofd==-1) {
1917                 /* Don't worry if sound is busy at the moment */
1918                 if (errno != EBUSY)
1919                         perror("open /dev/audio");
1920                 return audiofd; /* -1 */
1921         }
1922
1923         /* We should probably do something here if SETFRAGMENT fails... */
1924         audioFragment=0x0002000c;
1925         if (-1==ioctl(audiofd,SNDCTL_DSP_SETFRAGMENT,&audioFragment))
1926                 perror("ioctl SETFRAGMENT");
1927
1928         audioOK = 1;
1929         DSOUND_setformat(&(primarybuf->wfx));
1930
1931         return 0;
1932 }
1933
1934 static void DSOUND_CloseAudio(void)
1935 {
1936         int     neutral;
1937         
1938         neutral = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
1939         audioOK = 0;    /* race condition */
1940         Sleep(5);
1941         /* It's possible we've been called with audio closed */
1942         /* from SetFormat()... this is just to force a call */
1943         /* to OpenAudio() to reset the hardware properly */
1944         if (audiofd != -1)
1945                 close(audiofd);
1946         primarybuf->playpos = 0;
1947         primarybuf->writepos = DSOUND_FRAGLEN;
1948         memset(primarybuf->buffer, neutral, primarybuf->buflen);
1949         audiofd = -1;
1950         TRACE(dsound, "Audio stopped\n");
1951 }
1952         
1953 static int DSOUND_WriteAudio(char *buf, int len)
1954 {
1955         int     result, left = 0;
1956
1957         while (left < len) {
1958                 result = write(audiofd, buf + left, len - left);
1959                 if (result == -1) {
1960                         if (errno == EINTR)
1961                                 continue;
1962                         else
1963                                 return result;
1964                 }
1965                 left += result;
1966         }
1967         return 0;
1968 }
1969
1970 static void DSOUND_OutputPrimary(int len)
1971 {
1972         int     neutral, flen1, flen2;
1973         char    *frag1, *frag2;
1974         
1975         /* This is a bad place for this. We need to clear the */
1976         /* buffer with a neutral value, for unsigned 8-bit WAVE */
1977         /* that's 128, for signed 16-bit it's 0 */
1978         neutral = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
1979         
1980         /* **** */
1981         EnterCriticalSection(&(primarybuf->lock));
1982
1983         /* Write out the  */
1984         if ((audioOK == 1) || (DSOUND_OpenAudio() == 0)) {
1985                 if (primarybuf->playpos + len >= primarybuf->buflen) {
1986                         frag1 = primarybuf->buffer + primarybuf->playpos;
1987                         flen1 = primarybuf->buflen - primarybuf->playpos;
1988                         frag2 = primarybuf->buffer;
1989                         flen2 = len - (primarybuf->buflen - primarybuf->playpos);
1990                         if (DSOUND_WriteAudio(frag1, flen1) != 0) {
1991                                 perror("DSOUND_WriteAudio");
1992                                 LeaveCriticalSection(&(primarybuf->lock));
1993                                 ExitThread(0);
1994                         }
1995                         memset(frag1, neutral, flen1);
1996                         if (DSOUND_WriteAudio(frag2, flen2) != 0) {
1997                                 perror("DSOUND_WriteAudio");
1998                                 LeaveCriticalSection(&(primarybuf->lock));
1999                                 ExitThread(0);
2000                         }
2001                         memset(frag2, neutral, flen2);
2002                 } else {
2003                         frag1 = primarybuf->buffer + primarybuf->playpos;
2004                         flen1 = len;
2005                         if (DSOUND_WriteAudio(frag1, flen1) != 0) {
2006                                 perror("DSOUND_WriteAudio");
2007                                 LeaveCriticalSection(&(primarybuf->lock));
2008                                 ExitThread(0);
2009                         }
2010                         memset(frag1, neutral, flen1);
2011                 }
2012         } else {
2013                 /* Can't play audio at the moment -- we need to sleep */
2014                 /* to make up for the time we'd be blocked in write() */
2015                 /* to /dev/audio */
2016                 Sleep(60);
2017         }
2018         primarybuf->playpos += len;
2019         if (primarybuf->playpos >= primarybuf->buflen)
2020                 primarybuf->playpos %= primarybuf->buflen;
2021         primarybuf->writepos = primarybuf->playpos + DSOUND_FRAGLEN;
2022         if (primarybuf->writepos >= primarybuf->buflen)
2023                 primarybuf->writepos %= primarybuf->buflen;
2024
2025         LeaveCriticalSection(&(primarybuf->lock));
2026         /* **** */
2027 }
2028
2029 static DWORD WINAPI DSOUND_thread(LPVOID arg)
2030 {
2031         int     len;
2032
2033         TRACE(dsound,"dsound is at pid %d\n",getpid());
2034         while (1) {
2035                 if (!dsound) {
2036                         WARN(dsound,"DSOUND thread giving up.\n");
2037                         ExitThread(0);
2038                 }
2039                 if (getppid()==1) {
2040                         WARN(dsound,"DSOUND father died? Giving up.\n");
2041                         ExitThread(0);
2042                 }
2043                 /* RACE: dsound could be deleted */
2044                 dsound->lpvtbl->fnAddRef(dsound);
2045                 if (primarybuf == NULL) {
2046                         /* Should never happen */
2047                         WARN(dsound, "Lost the primary buffer!\n");
2048                         dsound->lpvtbl->fnRelease(dsound);
2049                         ExitThread(0);
2050                 }
2051
2052                 /* **** */
2053                 EnterCriticalSection(&(primarybuf->lock));
2054                 len = DSOUND_MixPrimary();
2055                 LeaveCriticalSection(&(primarybuf->lock));
2056                 /* **** */
2057
2058                 if (primarybuf->playing)
2059                         len = DSOUND_FRAGLEN > len ? DSOUND_FRAGLEN : len;
2060                 if (len) {
2061                         /* This does all the work */
2062                         DSOUND_OutputPrimary(len);
2063                 } else {
2064                         /* no buffers playing -- close and wait */
2065                         if (audioOK)
2066                                 DSOUND_CloseAudio();
2067                         Sleep(100);
2068                 }
2069                 dsound->lpvtbl->fnRelease(dsound);
2070         }
2071         ExitThread(0);
2072 }
2073
2074 #endif /* HAVE_OSS */
2075
2076 HRESULT WINAPI DirectSoundCreate(LPGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter )
2077 {
2078         if (lpGUID)
2079                 TRACE(dsound,"(%p,%p,%p)\n",lpGUID,ppDS,pUnkOuter);
2080         else
2081                 TRACE(dsound,"DirectSoundCreate (%p)\n", ppDS);
2082
2083 #ifdef HAVE_OSS
2084
2085         if (ppDS == NULL)
2086                 return DSERR_INVALIDPARAM;
2087
2088         if (primarybuf) {
2089                 dsound->lpvtbl->fnAddRef(dsound);
2090                 *ppDS = dsound;
2091                 return DS_OK;
2092         }
2093
2094         /* Check that we actually have audio capabilities */
2095         /* If we do, whether it's busy or not, we continue */
2096         /* otherwise we return with DSERR_NODRIVER */
2097
2098         audiofd = open("/dev/audio",O_WRONLY);
2099         if (audiofd == -1) {
2100                 if (errno == ENODEV) {
2101                         MSG("No sound hardware found.\n");
2102                         return DSERR_NODRIVER;
2103                 } else if (errno == EBUSY) {
2104                         MSG("Sound device busy, will keep trying.\n");
2105                 } else {
2106                         MSG("Unexpected error while checking for sound support.\n");
2107                         return DSERR_GENERIC;
2108                 }
2109         } else {
2110                 close(audiofd);
2111                 audiofd = -1;
2112         }
2113
2114         *ppDS = (LPDIRECTSOUND)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSound));
2115         if (*ppDS == NULL)
2116                 return DSERR_OUTOFMEMORY;
2117
2118         (*ppDS)->ref            = 1;
2119         (*ppDS)->lpvtbl         = &dsvt;
2120         (*ppDS)->buffers        = NULL;
2121         (*ppDS)->nrofbuffers    = 0;
2122
2123         (*ppDS)->wfx.wFormatTag         = 1;
2124         (*ppDS)->wfx.nChannels          = 2;
2125         (*ppDS)->wfx.nSamplesPerSec     = 22050;
2126         (*ppDS)->wfx.nAvgBytesPerSec    = 44100;
2127         (*ppDS)->wfx.nBlockAlign        = 2;
2128         (*ppDS)->wfx.wBitsPerSample     = 8;
2129
2130         if (!dsound) {
2131                 HANDLE32        hnd;
2132                 DWORD           xid;
2133
2134                 dsound = (*ppDS);
2135                 if (primarybuf == NULL) {
2136                         DSBUFFERDESC    dsbd;
2137                         HRESULT         hr;
2138
2139                         dsbd.dwSize = sizeof(DSBUFFERDESC);
2140                         dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
2141                         dsbd.dwBufferBytes = 0;
2142                         dsbd.lpwfxFormat = &(dsound->wfx);
2143                         hr = IDirectSound_CreateSoundBuffer(*ppDS, &dsbd, &primarybuf, NULL);
2144                         if (hr != DS_OK)
2145                                 return hr;
2146                         dsound->primary = primarybuf;
2147                 }
2148                 memset(primarybuf->buffer, 128, primarybuf->buflen);
2149                 hnd = CreateThread(NULL,0,DSOUND_thread,0,0,&xid);
2150         }
2151         return DS_OK;
2152 #else
2153         MessageBox32A(0,"DirectSound needs the Open Sound System Driver, which has not been found by ./configure.","WINE DirectSound",MB_OK|MB_ICONSTOP);
2154         return DSERR_NODRIVER;
2155 #endif
2156 }
2157
2158 /*******************************************************************************
2159  * DirectSound ClassFactory
2160  */
2161 typedef struct
2162 {
2163     /* IUnknown fields */
2164     ICOM_VTABLE(IClassFactory)* lpvtbl;
2165     DWORD                       ref;
2166 } IClassFactoryImpl;
2167
2168 static HRESULT WINAPI 
2169 DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
2170         ICOM_THIS(IClassFactoryImpl,iface);
2171         char buf[80];
2172
2173         if (HIWORD(riid))
2174             WINE_StringFromCLSID(riid,buf);
2175         else
2176             sprintf(buf,"<guid-0x%04x>",LOWORD(riid));
2177         FIXME(dsound,"(%p)->(%s,%p),stub!\n",This,buf,ppobj);
2178         return E_NOINTERFACE;
2179 }
2180
2181 static ULONG WINAPI
2182 DSCF_AddRef(LPCLASSFACTORY iface) {
2183         ICOM_THIS(IClassFactoryImpl,iface);
2184         return ++(This->ref);
2185 }
2186
2187 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) {
2188         ICOM_THIS(IClassFactoryImpl,iface);
2189         /* static class, won't be  freed */
2190         return --(This->ref);
2191 }
2192
2193 static HRESULT WINAPI DSCF_CreateInstance(
2194         LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
2195 ) {
2196         ICOM_THIS(IClassFactoryImpl,iface);
2197         char buf[80];
2198
2199         WINE_StringFromCLSID(riid,buf);
2200         TRACE(dsound,"(%p)->(%p,%s,%p)\n",This,pOuter,buf,ppobj);
2201         if (!memcmp(riid,&IID_IDirectSound,sizeof(IID_IDirectSound))) {
2202                 /* FIXME: reuse already created dsound if present? */
2203                 return DirectSoundCreate(riid,(LPDIRECTSOUND*)ppobj,pOuter);
2204         }
2205         return E_NOINTERFACE;
2206 }
2207
2208 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL32 dolock) {
2209         ICOM_THIS(IClassFactoryImpl,iface);
2210         FIXME(dsound,"(%p)->(%d),stub!\n",This,dolock);
2211         return S_OK;
2212 }
2213
2214 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
2215         DSCF_QueryInterface,
2216         DSCF_AddRef,
2217         DSCF_Release,
2218         DSCF_CreateInstance,
2219         DSCF_LockServer
2220 };
2221 static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 };
2222
2223 /*******************************************************************************
2224  * DllGetClassObject [DSOUND.4]
2225  * Retrieves class object from a DLL object
2226  *
2227  * NOTES
2228  *    Docs say returns STDAPI
2229  *
2230  * PARAMS
2231  *    rclsid [I] CLSID for the class object
2232  *    riid   [I] Reference to identifier of interface for class object
2233  *    ppv    [O] Address of variable to receive interface pointer for riid
2234  *
2235  * RETURNS
2236  *    Success: S_OK
2237  *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
2238  *             E_UNEXPECTED
2239  */
2240 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
2241 {
2242     char buf[80],xbuf[80];
2243
2244     if (HIWORD(rclsid))
2245         WINE_StringFromCLSID(rclsid,xbuf);
2246     else
2247         sprintf(xbuf,"<guid-0x%04x>",LOWORD(rclsid));
2248     if (HIWORD(riid))
2249         WINE_StringFromCLSID(riid,buf);
2250     else
2251         sprintf(buf,"<guid-0x%04x>",LOWORD(riid));
2252     WINE_StringFromCLSID(riid,xbuf);
2253     TRACE(dsound, "(%p,%p,%p)\n", xbuf, buf, ppv);
2254     if (!memcmp(riid,&IID_IClassFactory,sizeof(IID_IClassFactory))) {
2255         *ppv = (LPVOID)&DSOUND_CF;
2256         IClassFactory_AddRef((IClassFactory*)*ppv);
2257     return S_OK;
2258     }
2259     FIXME(dsound, "(%p,%p,%p): no interface found.\n", xbuf, buf, ppv);
2260     return E_NOINTERFACE;
2261 }
2262
2263
2264 /*******************************************************************************
2265  * DllCanUnloadNow [DSOUND.3]  Determines whether the DLL is in use.
2266  *
2267  * RETURNS
2268  *    Success: S_OK
2269  *    Failure: S_FALSE
2270  */
2271 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
2272 {
2273     FIXME(dsound, "(void): stub\n");
2274     return S_FALSE;
2275 }