Release 980201
[wine] / multimedia / dsound.c
1 /*                      DirectSound
2  * 
3  * Copyright 1998 Marcus Meissner
4  */
5 /*
6  * Note: This file requires multithread ability. It is not possible to
7  * implement the stuff in a single thread anyway. And most DirectX apps
8  * require threading themselves.
9  *
10  * FIXME: This file is full of race conditions and unlocked variable access
11  * from two threads. But we usually don't need to bother.
12  *
13  * Tested with a Soundblaster clone and a Gravis UltraSound Classic.
14  *
15  * Status:
16  * - Wing Commander 4/W95:
17  *   The intromovie plays without problems. Nearly lipsynchron.
18  * - DiscWorld 2
19  *   The sound works, but noticeable chunks are left out (from the sound and
20  *   the animation). Don't know why yet.
21  * - Diablo:
22  *   Sound works, but slows down the movieplayer.
23  * - XvT: 
24  *   Doesn't sound yet.
25  * - Monkey Island 3:
26  *   The background sound of the startscreen works ;)
27  * - WingCommander Prophecy Demo:
28  *   Sound works for the intromovie.
29  */
30
31 #include <stdio.h>
32 #include <assert.h>
33 #include <sys/types.h>
34 #include <sys/time.h>
35 #include <sys/fcntl.h>
36 #include <unistd.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include "windows.h"
40 #include "winerror.h"
41 #include "interfaces.h"
42 #include "mmsystem.h"
43 #include "dsound.h"
44 #include "thread.h"
45 #include "stddebug.h"
46 #include "debug.h"
47
48 #ifdef HAVE_OSS
49 #include <sys/ioctl.h>
50 #include <sys/soundcard.h>
51
52 static int audiofd = -1;
53 static int current_buffered_frags = 4;
54 static LPDIRECTSOUND    dsound = NULL;
55
56 static short playbuf[2048];
57
58 #endif
59
60 HRESULT WINAPI DirectSoundEnumerate32A(LPDSENUMCALLBACK32A enumcb,LPVOID context) {
61 #ifdef HAVE_OSS
62         enumcb(NULL,"WINE DirectSound using Open Sound System","sound",context);
63 #endif
64         return 0;
65 }
66
67 #ifdef HAVE_OSS
68 static void _dump_DSBCAPS(DWORD xmask) {
69         struct {
70                 DWORD   mask;
71                 char    *name;
72         } flags[] = {
73 #define FE(x) { x, #x },
74                 FE(DSBCAPS_PRIMARYBUFFER)
75                 FE(DSBCAPS_STATIC)
76                 FE(DSBCAPS_LOCHARDWARE)
77                 FE(DSBCAPS_LOCSOFTWARE)
78                 FE(DSBCAPS_CTRLFREQUENCY)
79                 FE(DSBCAPS_CTRLPAN)
80                 FE(DSBCAPS_CTRLVOLUME)
81                 FE(DSBCAPS_CTRLDEFAULT)
82                 FE(DSBCAPS_CTRLALL)
83                 FE(DSBCAPS_STICKYFOCUS)
84                 FE(DSBCAPS_GETCURRENTPOSITION2)
85         };
86         int     i;
87
88         for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
89                 if (flags[i].mask & xmask)
90                         fprintf(stderr,"%s ",flags[i].name);
91 }
92
93 /*******************************************************************************
94  *              IDirectSoundNotify
95  */
96 static HRESULT WINAPI IDirectSoundNotify_QueryInterface(
97         LPDIRECTSOUNDNOTIFY this,REFIID riid,LPVOID *ppobj
98 ) {
99         char xbuf[50];
100
101         StringFromCLSID(riid,xbuf);
102         fprintf(stderr,"IDirectSound(%p)->QueryInterface(%s,%p)\n",this,xbuf,ppobj);
103         return E_FAIL;
104 }
105
106 static ULONG WINAPI IDirectSoundNotify_AddRef(LPDIRECTSOUNDNOTIFY this) {
107         return ++(this->ref);
108 }
109
110 static ULONG WINAPI IDirectSoundNotify_Release(LPDIRECTSOUNDNOTIFY this) {
111         this->ref--;
112         if (!this->ref) {
113                 this->dsb->lpvtbl->fnRelease(this->dsb);
114                 HeapFree(GetProcessHeap(),0,this);
115                 return 0;
116         }
117         return this->ref;
118 }
119
120 static int _sort_notifies(const void *a,const void *b) {
121         LPDSBPOSITIONNOTIFY     na = (LPDSBPOSITIONNOTIFY)a;
122         LPDSBPOSITIONNOTIFY     nb = (LPDSBPOSITIONNOTIFY)b;
123
124         return na->dwOffset-nb->dwOffset;
125 }
126
127 static HRESULT WINAPI IDirectSoundNotify_SetNotificationPositions(
128         LPDIRECTSOUNDNOTIFY this,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
129 ) {
130         int     i;
131
132         fprintf(stderr,"IDirectSoundNotify(%p)->SetNotificationPositions(0x%08lx,%p),stub!\n",this,howmuch,notify);
133         for (i=0;i<howmuch;i++)
134                 fprintf(stderr,"        notify at %ld to 0x%08lx\n",notify[i].dwOffset,notify[i].hEventNotify);
135         this->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,this->dsb->notifies,(this->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
136         memcpy( this->dsb->notifies+this->dsb->nrofnotifies,
137                 notify,
138                 howmuch*sizeof(DSBPOSITIONNOTIFY)
139         );
140         this->dsb->nrofnotifies+=howmuch;
141         qsort(this->dsb->notifies,this->dsb->nrofnotifies,sizeof(DSBPOSITIONNOTIFY),_sort_notifies);
142         for (i=0;i<this->dsb->nrofnotifies;i++)
143                 fprintf(stderr,"        notify at %ld to 0x%08lx\n",this->dsb->notifies[i].dwOffset,this->dsb->notifies[i].hEventNotify);
144         return 0;
145 }
146
147 IDirectSoundNotify_VTable dsnvt = {
148         IDirectSoundNotify_QueryInterface,
149         IDirectSoundNotify_AddRef,
150         IDirectSoundNotify_Release,
151         IDirectSoundNotify_SetNotificationPositions,
152 };
153
154 /*******************************************************************************
155  *              IDirectSoundBuffer
156  */
157 static HRESULT WINAPI IDirectSoundBuffer_SetFormat(
158         LPDIRECTSOUNDBUFFER this,LPWAVEFORMATEX wfex
159 ) {
160
161         memcpy(&(this->wfx),wfex,sizeof(this->wfx));
162         dprintf_dsound(stderr,"IDirectSoundBuffer(%p)->SetFormat(%p),stub!\n",this,wfex);
163         dprintf_dsound(stderr," [formattag=0x%04x,",wfex->wFormatTag);
164         dprintf_dsound(stderr,"chans=%d,",wfex->nChannels);
165         dprintf_dsound(stderr,"samplerate=%ld,",wfex->nSamplesPerSec);
166         dprintf_dsound(stderr,"bytespersec=%ld,",wfex->nAvgBytesPerSec);
167         dprintf_dsound(stderr,"blockalign=%d,",wfex->nBlockAlign);
168         dprintf_dsound(stderr,"bitspersamp=%d,",wfex->wBitsPerSample);
169         dprintf_dsound(stderr,"cbSize=%d]\n",wfex->cbSize);
170
171         return 0;
172 }
173
174 static HRESULT WINAPI IDirectSoundBuffer_SetVolume(
175         LPDIRECTSOUNDBUFFER this,LONG vol
176 ) {
177         fprintf(stderr,"IDirectSoundBuffer(%p)->SetVolume(%ld),stub!\n",this,vol);
178         this->volume = vol;
179         return 0;
180 }
181
182 static HRESULT WINAPI IDirectSoundBuffer_GetVolume(
183         LPDIRECTSOUNDBUFFER this,LPLONG vol
184 ) {
185         dprintf_dsound(stderr,"IDirectSoundBuffer(%p)->GetVolume(%p),stub!\n",this,vol);
186         *vol = this->volume;
187         return 0;
188 }
189
190 static HRESULT WINAPI IDirectSoundBuffer_SetFrequency(
191         LPDIRECTSOUNDBUFFER this,DWORD freq
192 ) {
193         fprintf(stderr,"IDirectSoundBuffer(%p)->SetFrequency(%08lx),stub!\n",this,freq);
194         return 0;
195 }
196
197 static HRESULT WINAPI IDirectSoundBuffer_Play(
198         LPDIRECTSOUNDBUFFER this,DWORD reserved1,DWORD reserved2,DWORD flags
199 ) {
200         dprintf_dsound(stderr,"IDirectSoundBuffer(%p)->Play(%08lx,%08lx,%08lx),stub!\n",
201                 this,reserved1,reserved2,flags
202         );
203         this->playpos = 0;
204         this->playflags = flags;
205         this->playing = 1;
206         return 0;
207 }
208
209 static HRESULT WINAPI IDirectSoundBuffer_Stop(LPDIRECTSOUNDBUFFER this) {
210         dprintf_dsound(stderr,"IDirectSoundBuffer(%p)->Stop()\n",this);
211         this->playing = 0;
212         this->writepos = 0; /* hmm */
213         return 0;
214 }
215
216 static DWORD WINAPI IDirectSoundBuffer_AddRef(LPDIRECTSOUNDBUFFER this) {
217         return ++(this->ref);
218 }
219 static DWORD WINAPI IDirectSoundBuffer_Release(LPDIRECTSOUNDBUFFER this) {
220         int     i;
221
222         if (--this->ref)
223                 return this->ref;
224         for (i=0;i<this->dsound->nrofbuffers;i++)
225                 if (this->dsound->buffers[i] == this)
226                         break;
227         if (i < this->dsound->nrofbuffers) {
228                 memcpy(
229                         this->dsound->buffers+i,
230                         this->dsound->buffers+i+1,
231                         sizeof(LPDIRECTSOUNDBUFFER)*(this->dsound->nrofbuffers-i-1)
232                 );
233                 this->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,this->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*this->dsound->nrofbuffers);
234                 this->dsound->nrofbuffers--;
235                 this->dsound->lpvtbl->fnRelease(this->dsound);
236         }
237         HeapFree(GetProcessHeap(),0,this);
238         return 0;
239 }
240
241 static HRESULT WINAPI IDirectSoundBuffer_GetCurrentPosition(
242         LPDIRECTSOUNDBUFFER this,LPDWORD playpos,LPDWORD writepos
243 ) {
244         dprintf_dsound(stderr,"IDirectSoundBuffer(%p)->GetCurrentPosition(%p,%p),stub!\n",this,playpos,writepos);
245         if (playpos) *playpos = this->playpos;
246         if (writepos) *writepos = this->writepos;
247         return 0;
248 }
249
250 static HRESULT WINAPI IDirectSoundBuffer_GetStatus(
251         LPDIRECTSOUNDBUFFER this,LPDWORD status
252 ) {
253         dprintf_dsound(stderr,"IDirectSoundBuffer(%p)->GetStatus(%p)\n",this,status);
254         *status = 0;
255         if (this->playing)
256                 *status |= DSBSTATUS_PLAYING;
257         if (this->playflags & DSBPLAY_LOOPING)
258                 *status |= DSBSTATUS_LOOPING;
259         return 0;
260 }
261
262 static HRESULT WINAPI IDirectSoundBuffer_GetFormat(
263         LPDIRECTSOUNDBUFFER this,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
264 ) {
265         dprintf_dsound(stderr,"IDirectSoundBuffer(%p)->GetFormat(%p,%ld,%p)\n",this,lpwf,wfsize,wfwritten);
266         if (wfsize>sizeof(this->wfx)) wfsize = sizeof(this->wfx);
267         memcpy(lpwf,&(this->wfx),wfsize);
268         if (wfwritten) *wfwritten = wfsize;
269         return 0;
270 }
271
272 static HRESULT WINAPI IDirectSoundBuffer_Lock(
273         LPDIRECTSOUNDBUFFER this,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
274 ) {
275
276         dprintf_dsound(stderr,"IDirectSoundBuffer(%p)->Lock(%ld,%ld,%p,%p,%p,%p,0x%08lx)\n",
277                 this,
278                 writecursor,
279                 writebytes,
280                 lplpaudioptr1,
281                 audiobytes1,
282                 lplpaudioptr2,
283                 audiobytes2,
284                 flags
285         );
286         if (flags & DSBLOCK_FROMWRITECURSOR)
287                 writecursor = this->writepos;
288         assert(audiobytes1!=audiobytes2);
289         assert(lplpaudioptr1!=lplpaudioptr2);
290         if (writecursor+writebytes <= this->buflen) {
291                 *(LPBYTE*)lplpaudioptr1 = this->buffer+writecursor;
292                 *audiobytes1 = writebytes;
293                 if (lplpaudioptr2)
294                         *(LPBYTE*)lplpaudioptr2 = NULL;
295                 if (audiobytes2)
296                         *audiobytes2 = 0;
297                 dprintf_dsound(stderr,"->%ld.0\n",writebytes);
298         } else {
299                 *(LPBYTE*)lplpaudioptr1 = this->buffer+writecursor;
300                 *audiobytes1 = this->buflen-writecursor;
301                 if (lplpaudioptr2)
302                         *(LPBYTE*)lplpaudioptr2 = this->buffer;
303                 if (audiobytes2)
304                         *audiobytes2 = writebytes-(this->buflen-writecursor);
305                 dprintf_dsound(stderr,"->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
306         }
307         this->writepos=(writecursor+writebytes)%this->buflen;
308         return 0;
309 }
310
311 static HRESULT WINAPI IDirectSoundBuffer_SetCurrentPosition(
312         LPDIRECTSOUNDBUFFER this,DWORD newpos
313 ) {
314         dprintf_dsound(stderr,"IDirectSoundBuffer(%p)->SetCurrentPosition(%ld)\n",this,newpos);
315         this->playpos = newpos;
316         return 0;
317 }
318
319 static HRESULT WINAPI IDirectSoundBuffer_SetPan(
320         LPDIRECTSOUNDBUFFER this,LONG newpan
321 ) {
322         dprintf_dsound(stderr,"IDirectSoundBuffer(%p)->SetPan(%ld),stub!\n",this,newpan);
323         this->pan = newpan;
324         return 0;
325 }
326
327 static HRESULT WINAPI IDirectSoundBuffer_GetPan(
328         LPDIRECTSOUNDBUFFER this,LPLONG pan
329 ) {
330         dprintf_dsound(stderr,"IDirectSoundBuffer(%p)->GetPan(%p),stub!\n",this,pan);
331         *pan = this->pan;
332         return 0;
333 }
334
335 static HRESULT WINAPI IDirectSoundBuffer_Unlock(
336         LPDIRECTSOUNDBUFFER this,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
337 ) {
338         dprintf_dsound(stderr,"IDirectSoundBuffer(%p)->Unlock(%p,%ld,%p,%ld)\n",
339                 this,p1,x1,p2,x2
340         );
341         return 0;
342 }
343
344 static HRESULT WINAPI IDirectSoundBuffer_GetFrequency(
345         LPDIRECTSOUNDBUFFER this,LPDWORD freq
346 ) {
347         dprintf_dsound(stderr,"IDirectSoundBuffer(%p)->GetFrequency(%p)\n",
348                 this,freq
349         );
350         *freq = this->wfx.nSamplesPerSec;
351         return 0;
352 }
353
354 static HRESULT WINAPI IDirectSoundBuffer_Initialize(
355         LPDIRECTSOUNDBUFFER this,LPDIRECTSOUND dsound,LPDSBUFFERDESC dbsd
356 ) {
357         fprintf(stderr,"IDirectSoundBuffer(%p)->Initialize(%p,%p),stub!\n",this,dsound,dbsd);
358         return DSERR_ALREADYINITIALIZED;
359 }
360
361 static HRESULT WINAPI IDirectSoundBuffer_GetCaps(
362         LPDIRECTSOUNDBUFFER this,LPDSBCAPS caps
363 ) {
364         caps->dwSize = sizeof(*caps);
365         caps->dwFlags = DSBCAPS_PRIMARYBUFFER|DSBCAPS_STATIC|DSBCAPS_CTRLALL|DSBCAPS_LOCSOFTWARE;
366         caps->dwBufferBytes = 0;
367         caps->dwUnlockTransferRate = 0;
368         caps->dwPlayCpuOverhead = 0;
369         return DS_OK;
370 }
371
372 static HRESULT WINAPI IDirectSoundBuffer_QueryInterface(
373         LPDIRECTSOUNDBUFFER this,REFIID riid,LPVOID *ppobj
374 ) {
375         char    xbuf[50];
376
377         if (!memcmp(&IID_IDirectSoundNotify,riid,sizeof(*riid))) {
378                 IDirectSoundNotify      *dsn;
379
380                 dsn = (LPDIRECTSOUNDNOTIFY)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn));
381                 dsn->ref = 1;
382                 dsn->dsb = this;
383                 this->lpvtbl->fnAddRef(this);
384                 dsn->lpvtbl = &dsnvt;
385                 *ppobj = (LPVOID)dsn;
386                 return 0;
387         }
388         StringFromCLSID(riid,xbuf);
389         fprintf(stderr,"IDirectSoundBuffer(%p)->QueryInterface(%s,%p)\n",this,xbuf,ppobj);
390         return E_FAIL;
391 }
392
393 static struct tagLPDIRECTSOUNDBUFFER_VTABLE dsbvt = {
394         IDirectSoundBuffer_QueryInterface,
395         IDirectSoundBuffer_AddRef,
396         IDirectSoundBuffer_Release,
397         IDirectSoundBuffer_GetCaps,
398         IDirectSoundBuffer_GetCurrentPosition,
399         IDirectSoundBuffer_GetFormat,
400         IDirectSoundBuffer_GetVolume,
401         IDirectSoundBuffer_GetPan,
402         IDirectSoundBuffer_GetFrequency,
403         IDirectSoundBuffer_GetStatus,
404         IDirectSoundBuffer_Initialize,
405         IDirectSoundBuffer_Lock,
406         IDirectSoundBuffer_Play,
407         IDirectSoundBuffer_SetCurrentPosition,
408         IDirectSoundBuffer_SetFormat,
409         IDirectSoundBuffer_SetVolume,
410         IDirectSoundBuffer_SetPan,
411         IDirectSoundBuffer_SetFrequency,
412         IDirectSoundBuffer_Stop,
413         IDirectSoundBuffer_Unlock
414 };
415
416 /*******************************************************************************
417  *              IDirectSound
418  */
419
420 static HRESULT WINAPI IDirectSound_SetCooperativeLevel(
421         LPDIRECTSOUND this,HWND32 hwnd,DWORD level
422 ) {
423         dprintf_dsound(stderr,"IDirectSound(%p)->SetCooperativeLevel(%08lx,%ld)\n",
424                 this,(DWORD)hwnd,level
425         );
426         return 0;
427 }
428
429
430 static HRESULT WINAPI IDirectSound_CreateSoundBuffer(
431         LPDIRECTSOUND this,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER ppdsb,LPUNKNOWN lpunk
432 ) {
433         if (debugging_dsound) {
434                 fprintf(stderr,"IDirectSound(%p)->CreateSoundBuffer(%p,%p,%p),stub!\n",this,dsbd,ppdsb,lpunk);
435                 fprintf(stderr,"[size=%ld,",dsbd->dwSize);
436                 fprintf(stderr,"flags = 0x%08lx,",dsbd->dwFlags);
437                 _dump_DSBCAPS(dsbd->dwFlags);
438                 fprintf(stderr,"bufferbytes = %ld,",dsbd->dwBufferBytes);
439                 fprintf(stderr,"lpwfxFormat = %p]\n",dsbd->lpwfxFormat);
440         }
441         *ppdsb = (LPDIRECTSOUNDBUFFER)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBuffer));
442         (*ppdsb)->ref =1;
443         (*ppdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,dsbd->dwBufferBytes);
444         (*ppdsb)->buflen = dsbd->dwBufferBytes;
445         (*ppdsb)->playpos = 0;
446         (*ppdsb)->writepos = 0;
447         (*ppdsb)->lpvtbl = &dsbvt;
448         (*ppdsb)->dsound = this;
449         (*ppdsb)->playing = 0;
450         memcpy(&((*ppdsb)->dsbd),dsbd,sizeof(*dsbd));
451
452         /* register buffer */
453         this->buffers = (LPDIRECTSOUNDBUFFER*)HeapReAlloc(GetProcessHeap(),0,this->buffers,sizeof(LPDIRECTSOUNDBUFFER)*(this->nrofbuffers+1));
454         this->buffers[this->nrofbuffers] = *ppdsb;
455         this->nrofbuffers++;
456         this->lpvtbl->fnAddRef(this);
457
458         if (dsbd->lpwfxFormat) dsbvt.fnSetFormat(*ppdsb,dsbd->lpwfxFormat);
459         return 0;
460 }
461
462 static HRESULT WINAPI IDirectSound_DuplicateSoundBuffer(
463         LPDIRECTSOUND this,LPDIRECTSOUNDBUFFER pdsb,LPLPDIRECTSOUNDBUFFER ppdsb
464 ) {
465         fprintf(stderr,"IDirectSound(%p)->DuplicateSoundBuffer(%p,%p),stub!\n",this,pdsb,ppdsb);
466
467         *ppdsb = (LPDIRECTSOUNDBUFFER)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBuffer));
468         (*ppdsb)->ref =1;
469         (*ppdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,pdsb->buflen);
470         memcpy((*ppdsb)->buffer,pdsb->buffer,pdsb->buflen);
471         (*ppdsb)->buflen = pdsb->buflen;
472         (*ppdsb)->playpos = 0;
473         (*ppdsb)->writepos = 0;
474         (*ppdsb)->lpvtbl = &dsbvt;
475         (*ppdsb)->dsound = this;
476         dsbvt.fnSetFormat(*ppdsb,&(pdsb->wfx));
477         /* register buffer */
478         this->buffers = (LPDIRECTSOUNDBUFFER*)HeapReAlloc(GetProcessHeap(),0,this->buffers,sizeof(LPDIRECTSOUNDBUFFER)*(this->nrofbuffers+1));
479         this->buffers[this->nrofbuffers] = *ppdsb;
480         this->nrofbuffers++;
481         this->lpvtbl->fnAddRef(this);
482         return 0;
483 }
484
485
486 static HRESULT WINAPI IDirectSound_GetCaps(LPDIRECTSOUND this,LPDSCAPS caps) {
487         fprintf(stderr,"IDirectSound(%p)->GetCaps(%p),stub!\n",this,caps);
488         fprintf(stderr,"        flags = 0x%08lx\n",caps->dwFlags);
489
490         caps->dwSize = sizeof(*caps);
491         caps->dwFlags = DSCAPS_PRIMARYSTEREO|DSCAPS_PRIMARY16BIT|DSCAPS_EMULDRIVER|DSCAPS_SECONDARYSTEREO|DSCAPS_SECONDARY16BIT;
492         /* FIXME: query OSS */
493         caps->dwMinSecondarySampleRate = 22050;
494         caps->dwMaxSecondarySampleRate = 48000;
495         caps->dwPrimaryBuffers = 1;
496         /* FIXME: set the rest... hmm */
497         return 0;
498 }
499
500 static ULONG WINAPI IDirectSound_AddRef(LPDIRECTSOUND this) {
501         return ++(this->ref);
502 }
503
504 static ULONG WINAPI IDirectSound_Release(LPDIRECTSOUND this) {
505         if (!--(this->ref)) {
506                 HeapFree(GetProcessHeap(),0,this);
507                 dsound = NULL;
508                 close(audiofd);audiofd = -1;
509                 return 0;
510         }
511         return this->ref;
512 }
513
514 static HRESULT WINAPI IDirectSound_SetSpeakerConfig(
515         LPDIRECTSOUND this,DWORD config
516 ) {
517         fprintf(stderr,"IDirectSound(%p)->SetSpeakerConfig(0x%08lx)\n",this,config);
518         return 0;
519 }
520
521 static HRESULT WINAPI IDirectSound_QueryInterface(
522         LPDIRECTSOUND this,REFIID riid,LPVOID *ppobj
523 ) {
524         char xbuf[50];
525
526         StringFromCLSID(riid,xbuf);
527         fprintf(stderr,"IDirectSound(%p)->QueryInterface(%s,%p)\n",this,xbuf,ppobj);
528         return E_FAIL;
529 }
530
531 static struct tagLPDIRECTSOUND_VTABLE dsvt = {
532         IDirectSound_QueryInterface,
533         IDirectSound_AddRef,
534         IDirectSound_Release,
535         IDirectSound_CreateSoundBuffer,
536         IDirectSound_GetCaps,
537         IDirectSound_DuplicateSoundBuffer,
538         IDirectSound_SetCooperativeLevel,
539         (void *)8,
540         (void *)9,
541         IDirectSound_SetSpeakerConfig,
542         (void *)11
543 };
544
545 static int
546 DSOUND_setformat(LPWAVEFORMATEX wfex) {
547         int     xx,channels,speed,format,nformat;
548
549         switch (wfex->wFormatTag) {
550         default:
551                 fprintf(stderr,"unknown WAVE_FORMAT tag %d\n",wfex->wFormatTag);
552                 return DSERR_BADFORMAT;
553         case WAVE_FORMAT_PCM:
554                 format = AFMT_S16_LE;
555                 break;
556         }
557         if (-1==ioctl(audiofd,SNDCTL_DSP_GETFMTS,&xx)) {
558                 perror("ioctl SNDCTL_DSP_GETFMTS");
559                 return -1;
560         }
561         if ((xx&format)!=format) {/* format unsupported */
562                 fprintf(stderr,"SNDCTL_DSP_GETFMTS: format not supported\n"); 
563                 return -1;
564         }
565         nformat = format;
566         if (-1==ioctl(audiofd,SNDCTL_DSP_SETFMT,&nformat)) {
567                 perror("ioctl SNDCTL_DSP_SETFMT");
568                 return -1;
569         }
570         if (nformat!=format) {/* didn't work */
571                 fprintf(stderr,"SNDCTL_DSP_GETFMTS: format not set\n"); 
572                 return -1;
573         }
574
575         channels = wfex->nChannels-1;
576         if (-1==ioctl(audiofd,SNDCTL_DSP_STEREO,&channels)) {
577                 perror("ioctl SNDCTL_DSP_STEREO");
578                 return -1;
579         }
580         speed = wfex->nSamplesPerSec;
581         if (-1==ioctl(audiofd,SNDCTL_DSP_SPEED,&speed)) {
582                 perror("ioctl SNDCTL_DSP_SPEED");
583                 return -1;
584         }
585         return 0;
586 }
587
588 static LPDSBPOSITIONNOTIFY
589 DSOUND_nextevent(IDirectSoundBuffer *dsb) {
590         int     i;
591
592         if (dsb->nrofnotifies) {
593                 for (i=0;i<dsb->nrofnotifies;i++) {
594                         if (dsb->playpos<dsb->notifies[i].dwOffset)
595                                 break;
596                 }
597                 if (i==dsb->nrofnotifies)
598                         i=0;
599                 return dsb->notifies+i;
600         }
601         return NULL;
602 }
603
604 #define CHECK_EVENT                                                     \
605         if (nextevent && (dsb->playpos == nextevent->dwOffset)) {       \
606                 SetEvent(nextevent->hEventNotify);                      \
607                 fprintf(stderr,"signalled event %d\n",nextevent->hEventNotify);\
608                 nextevent = DSOUND_nextevent(dsb);                      \
609         }
610                 
611
612 static void 
613 DSOUND_MixInBuffer(IDirectSoundBuffer *dsb) {
614         int     i,j,buflen = dsb->buflen;
615         LPDSBPOSITIONNOTIFY     nextevent;
616
617         if (dsb->wfx.nSamplesPerSec != dsound->wfx.nSamplesPerSec) {
618                 fprintf(stderr,"mixing in buffer of different frequency, argh!\n");
619         }
620         nextevent = DSOUND_nextevent(dsb);
621
622         if (dsb->wfx.wBitsPerSample == 8) {
623                 unsigned char   *xbuf = (unsigned char*)(dsb->buffer);
624                 if (dsb->wfx.nChannels == 1) {
625                         for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0])/2;j++) {
626                                 dsb->playpos=(dsb->playpos+1)%buflen;
627                                 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
628                                         dsb->playing = 0;
629                                         dsb->playpos = buflen;
630                                         return;
631                                 }
632                                 /* FIXME: pan,volume */
633                                 playbuf[(j<<1)  ]+=xbuf[dsb->playpos]<<8;
634                                 playbuf[(j<<1)+1]+=xbuf[dsb->playpos]<<8;
635                                 CHECK_EVENT
636                         }
637                 } else {
638                         for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0]);j++) {
639                                 dsb->playpos=(dsb->playpos+1)%buflen;
640                                 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
641                                         dsb->playing = 0;
642                                         dsb->playpos = buflen;
643                                         return;
644                                 }
645                                 /* FIXME: pan,volume */
646                                 playbuf[j]+=xbuf[dsb->playpos]<<8;
647                                 CHECK_EVENT
648                         }
649                 }
650         } else { /* 16 */
651                 short   *xbuf = (short*)(dsb->buffer);
652                 if (dsb->wfx.nChannels == 1) {
653                         for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0])/2;j++) {
654                                 dsb->playpos=(dsb->playpos+2)%buflen;
655                                 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
656                                         dsb->playing = 0;
657                                         dsb->playpos = buflen;
658                                         return;
659                                 }
660                                 /* FIXME: pan,volume */
661                                 playbuf[(j<<1)  ]+=xbuf[dsb->playpos>>1];
662                                 playbuf[(j<<1)+1]+=xbuf[dsb->playpos>>1];
663                                 CHECK_EVENT
664                         }
665                 } else {
666                         for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0]);j++) {
667                                 dsb->playpos=(dsb->playpos+2)%buflen;
668                                 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
669                                         dsb->playing = 0;
670                                         dsb->playpos = buflen;
671                                         return;
672                                 }
673                                 /* FIXME: pan,volume */
674                                 playbuf[j]+=xbuf[dsb->playpos>>1];
675                                 CHECK_EVENT
676                         }
677                 }
678         }
679 }
680
681 static DWORD
682 DSOUND_thread(LPVOID arg) {
683         int     fragsdiff,res,i,curleft,playing;
684         struct audio_buf_info   abi;
685
686         fprintf(stderr,"dsound is at pid %d\n",getpid());
687         ioctl(audiofd,SNDCTL_DSP_GETOSPACE,&abi);
688         fragsdiff = abi.fragstotal-abi.fragments;
689         while (1) {
690                 if (!dsound) {
691                         fprintf(stderr,"DSOUND thread giving up.\n");
692                         ExitThread(0);
693                 }
694                 /* RACE: dsound could be deleted */
695                 dsound->lpvtbl->fnAddRef(dsound);
696                 if (!dsound->nrofbuffers) {
697                         /* no soundbuffer yet... wait. */
698                         Sleep(1000);
699                         continue;
700                 }
701                 memset(playbuf,0,sizeof(playbuf));
702                 /* empty in memory soundbuffers */
703                 while (1) {
704                         ioctl(audiofd,SNDCTL_DSP_GETOSPACE,&abi);
705                         if (abi.fragstotal-abi.fragments<=fragsdiff+current_buffered_frags)
706                                 break;
707                         Sleep(1);
708                 }
709                 playing = 0;
710                 dsound->lpvtbl->fnAddRef(dsound); 
711                 for (i=dsound->nrofbuffers;i--;) {
712                         IDirectSoundBuffer      *dsb = dsound->buffers[i];
713
714                         dsb->lpvtbl->fnAddRef(dsb);
715                         if (dsb->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
716                                 if (memcmp(&dsound->wfx,&(dsb->wfx),sizeof(dsound->wfx))) {
717                                         DSOUND_setformat(&(dsb->wfx));
718                                         memcpy(&dsound->wfx,&(dsb->wfx),sizeof(dsb->wfx));
719                                 }
720                         }
721                         dsb->lpvtbl->fnRelease(dsb);
722                 }
723                 for (i=dsound->nrofbuffers;i--;) {
724                         IDirectSoundBuffer      *dsb = dsound->buffers[i];
725
726                         dsb->lpvtbl->fnAddRef(dsb);
727                         if (dsb->buflen && dsb->playing) {
728                                 playing++;
729                                 DSOUND_MixInBuffer(dsb);
730                         }
731                         dsb->lpvtbl->fnRelease(dsb);
732                 }
733                 dsound->lpvtbl->fnRelease(dsound);
734
735                 /*fputc('0'+playing,stderr);*/
736                 curleft = 0;
737                 ioctl(audiofd,SNDCTL_DSP_GETOSPACE,&abi);
738                 if ((abi.fragstotal-abi.fragments)<=1+fragsdiff) {
739                         current_buffered_frags++;
740                 } else if ((abi.fragstotal-abi.fragments)>2+fragsdiff) {
741                         current_buffered_frags--;
742                 }
743                 while (curleft < sizeof(playbuf)) {
744                         res = write(audiofd,(LPBYTE)playbuf+curleft,sizeof(playbuf)-curleft);
745                         if (res==-1) {
746                                 perror("write audiofd");
747                                 ExitThread(0);
748                                 break;
749                         }
750                         curleft+=res;
751                 }
752         }
753         ExitThread(0);
754 }
755
756 #endif /* HAVE_OSS */
757
758 HRESULT WINAPI DirectSoundCreate(LPGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter ) {
759         if (lpGUID)
760                 fprintf(stderr,"DirectSoundCreate(%p,%p,%p)\n",lpGUID,ppDS,pUnkOuter);
761 #ifdef HAVE_OSS
762         if (audiofd>=0)
763                 return DSERR_ALLOCATED;
764         audiofd = open("/dev/audio",O_WRONLY);
765         if (audiofd==-1) {
766                 perror("open /dev/audio");
767                 audiofd=0;
768                 return DSERR_NODRIVER;
769         }
770         /*
771         xx=0x0004000c;
772         if (-1==ioctl(audiofd,SNDCTL_DSP_SETFRAGMENT,&xx))
773                 perror("ioctl SETFRAGMENT");
774         */
775
776         *ppDS = (LPDIRECTSOUND)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSound));
777         (*ppDS)->ref            = 1;
778         (*ppDS)->lpvtbl         = &dsvt;
779         (*ppDS)->buffers        = NULL;
780         (*ppDS)->nrofbuffers    = 0;
781
782         (*ppDS)->wfx.wFormatTag         = 1;
783         (*ppDS)->wfx.nChannels          = 2;
784         (*ppDS)->wfx.nSamplesPerSec     = 22050;
785         (*ppDS)->wfx.nAvgBytesPerSec    = 44100;
786         (*ppDS)->wfx.nBlockAlign        = 2;
787         (*ppDS)->wfx.wBitsPerSample     = 8;
788
789         DSOUND_setformat(&((*ppDS)->wfx));
790
791         if (!dsound) {
792                 HANDLE32        hnd;
793                 DWORD           xid;
794
795                 dsound = (*ppDS);
796                 hnd = CreateThread(NULL,NULL,DSOUND_thread,0,0,&xid);
797         }
798         return 0;
799 #else
800         MessageBox32A(0,"DirectSound needs the Open Sound System Driver, which has not been found by ./configure.","WINE DirectSound",MB_OK|MB_ICONSTOP);
801         return DSERR_NODRIVER;
802 #endif
803 }