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