Release 980628
[wine/multimedia.git] / multimedia / dsound.c
blob8926af3e20a43c8723f0c8a7e44339544b411cd4
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.
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.
13 * Tested with a Soundblaster clone and a Gravis UltraSound Classic.
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.
31 #include "config.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 <math.h> /* Insomnia - pow() function */
40 #include "windows.h"
41 #include "winerror.h"
42 #include "interfaces.h"
43 #include "mmsystem.h"
44 #include "dsound.h"
45 #include "thread.h"
46 #include "debug.h"
48 #ifdef HAVE_OSS
49 # include <sys/ioctl.h>
50 # ifdef HAVE_MACHINE_SOUNDCARD_H
51 # include <machine/soundcard.h>
52 # endif
53 # ifdef HAVE_SYS_SOUNDCARD_H
54 # include <sys/soundcard.h>
55 # endif
57 static int audiofd = -1;
58 static LPDIRECTSOUND dsound = NULL;
60 static short playbuf[2048];
62 #endif
64 HRESULT WINAPI DirectSoundEnumerate32A(LPDSENUMCALLBACK32A enumcb,LPVOID context) {
65 #ifdef HAVE_OSS
66 enumcb(NULL,"WINE DirectSound using Open Sound System","sound",context);
67 #endif
68 return 0;
71 #ifdef HAVE_OSS
72 static void _dump_DSBCAPS(DWORD xmask) {
73 struct {
74 DWORD mask;
75 char *name;
76 } flags[] = {
77 #define FE(x) { x, #x },
78 FE(DSBCAPS_PRIMARYBUFFER)
79 FE(DSBCAPS_STATIC)
80 FE(DSBCAPS_LOCHARDWARE)
81 FE(DSBCAPS_LOCSOFTWARE)
82 FE(DSBCAPS_CTRLFREQUENCY)
83 FE(DSBCAPS_CTRLPAN)
84 FE(DSBCAPS_CTRLVOLUME)
85 FE(DSBCAPS_CTRLDEFAULT)
86 FE(DSBCAPS_CTRLALL)
87 FE(DSBCAPS_STICKYFOCUS)
88 FE(DSBCAPS_GETCURRENTPOSITION2)
90 int i;
92 for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
93 if (flags[i].mask & xmask)
94 fprintf(stderr,"%s ",flags[i].name);
97 /*******************************************************************************
98 * IDirectSoundNotify
100 static HRESULT WINAPI IDirectSoundNotify_QueryInterface(
101 LPDIRECTSOUNDNOTIFY this,REFIID riid,LPVOID *ppobj
103 char xbuf[50];
105 WINE_StringFromCLSID(riid,xbuf);
106 TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
107 return E_FAIL;
110 static ULONG WINAPI IDirectSoundNotify_AddRef(LPDIRECTSOUNDNOTIFY this) {
111 return ++(this->ref);
114 static ULONG WINAPI IDirectSoundNotify_Release(LPDIRECTSOUNDNOTIFY this) {
115 this->ref--;
116 if (!this->ref) {
117 this->dsb->lpvtbl->fnRelease(this->dsb);
118 HeapFree(GetProcessHeap(),0,this);
119 return 0;
121 return this->ref;
124 static int _sort_notifies(const void *a,const void *b) {
125 LPDSBPOSITIONNOTIFY na = (LPDSBPOSITIONNOTIFY)a;
126 LPDSBPOSITIONNOTIFY nb = (LPDSBPOSITIONNOTIFY)b;
128 return na->dwOffset-nb->dwOffset;
131 static HRESULT WINAPI IDirectSoundNotify_SetNotificationPositions(
132 LPDIRECTSOUNDNOTIFY this,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
134 int i;
136 if (TRACE_ON(dsound)) {
137 TRACE(dsound,"(%p,0x%08lx,%p)\n",this,howmuch,notify);
138 for (i=0;i<howmuch;i++)
139 TRACE(dsound,"notify at %ld to 0x%08lx\n",
140 notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
142 this->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,this->dsb->notifies,(this->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
143 memcpy( this->dsb->notifies+this->dsb->nrofnotifies,
144 notify,
145 howmuch*sizeof(DSBPOSITIONNOTIFY)
147 this->dsb->nrofnotifies+=howmuch;
148 qsort(this->dsb->notifies,this->dsb->nrofnotifies,sizeof(DSBPOSITIONNOTIFY),_sort_notifies);
149 return 0;
152 IDirectSoundNotify_VTable dsnvt = {
153 IDirectSoundNotify_QueryInterface,
154 IDirectSoundNotify_AddRef,
155 IDirectSoundNotify_Release,
156 IDirectSoundNotify_SetNotificationPositions,
159 /*******************************************************************************
160 * IDirectSoundBuffer
162 static HRESULT WINAPI IDirectSoundBuffer_SetFormat(
163 LPDIRECTSOUNDBUFFER this,LPWAVEFORMATEX wfex
166 memcpy(&(this->wfx),wfex,sizeof(this->wfx));
167 TRACE(dsound,"(%p,%p)\n", this,wfex);
168 TRACE(dsound,"(formattag=0x%04x,chans=%d,samplerate=%ld"
169 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
170 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
171 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
172 wfex->wBitsPerSample, wfex->cbSize);
174 return 0;
177 static HRESULT WINAPI IDirectSoundBuffer_SetVolume(
178 LPDIRECTSOUNDBUFFER this,LONG vol
180 TRACE(dsound,"(%p,%ld)\n",this,vol);
181 this->volume = vol;
182 this->volfac = ((double)vol+10000.0)/10000.0;
184 return 0;
187 static HRESULT WINAPI IDirectSoundBuffer_GetVolume(
188 LPDIRECTSOUNDBUFFER this,LPLONG vol
190 TRACE(dsound,"(%p,%p)\n",this,vol);
191 *vol = this->volume;
192 return 0;
195 static HRESULT WINAPI IDirectSoundBuffer_SetFrequency(
196 LPDIRECTSOUNDBUFFER this,DWORD freq
198 TRACE(dsound,"(%p,%ld)\n",this,freq);
199 this->wfx.nSamplesPerSec = freq;
200 this->wfx.nAvgBytesPerSec = freq*this->wfx.nChannels*(this->wfx.wBitsPerSample/8);
201 return 0;
204 static HRESULT WINAPI IDirectSoundBuffer_Play(
205 LPDIRECTSOUNDBUFFER this,DWORD reserved1,DWORD reserved2,DWORD flags
207 TRACE(dsound,"(%p,%08lx,%08lx,%08lx)\n",
208 this,reserved1,reserved2,flags
210 this->playpos = 0;
211 this->playflags = flags;
212 this->playing = 1;
213 return 0;
216 static HRESULT WINAPI IDirectSoundBuffer_Stop(LPDIRECTSOUNDBUFFER this) {
217 TRACE(dsound,"(%p)\n",this);
218 this->playing = 0;
219 this->writepos = 0; /* hmm */
220 return 0;
223 static DWORD WINAPI IDirectSoundBuffer_AddRef(LPDIRECTSOUNDBUFFER this) {
224 return ++(this->ref);
226 static DWORD WINAPI IDirectSoundBuffer_Release(LPDIRECTSOUNDBUFFER this) {
227 int i;
229 if (--this->ref)
230 return this->ref;
231 for (i=0;i<this->dsound->nrofbuffers;i++)
232 if (this->dsound->buffers[i] == this)
233 break;
234 if (i < this->dsound->nrofbuffers) {
235 memcpy(
236 this->dsound->buffers+i,
237 this->dsound->buffers+i+1,
238 sizeof(LPDIRECTSOUNDBUFFER)*(this->dsound->nrofbuffers-i-1)
240 this->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,this->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*this->dsound->nrofbuffers);
241 this->dsound->nrofbuffers--;
242 this->dsound->lpvtbl->fnRelease(this->dsound);
244 HeapFree(GetProcessHeap(),0,this);
245 return 0;
248 static HRESULT WINAPI IDirectSoundBuffer_GetCurrentPosition(
249 LPDIRECTSOUNDBUFFER this,LPDWORD playpos,LPDWORD writepos
251 TRACE(dsound,"(%p,%p,%p)\n",this,playpos,writepos);
252 if (playpos) *playpos = this->playpos;
253 if (writepos) *writepos = this->writepos;
254 return 0;
257 static HRESULT WINAPI IDirectSoundBuffer_GetStatus(
258 LPDIRECTSOUNDBUFFER this,LPDWORD status
260 TRACE(dsound,"(%p,%p)\n",this,status);
261 *status = 0;
262 if (this->playing)
263 *status |= DSBSTATUS_PLAYING;
264 if (this->playflags & DSBPLAY_LOOPING)
265 *status |= DSBSTATUS_LOOPING;
266 return 0;
269 static HRESULT WINAPI IDirectSoundBuffer_GetFormat(
270 LPDIRECTSOUNDBUFFER this,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
272 TRACE(dsound,"(%p,%p,%ld,%p)\n",this,lpwf,wfsize,wfwritten);
273 if (wfsize>sizeof(this->wfx)) wfsize = sizeof(this->wfx);
274 memcpy(lpwf,&(this->wfx),wfsize);
275 if (wfwritten) *wfwritten = wfsize;
276 return 0;
279 static HRESULT WINAPI IDirectSoundBuffer_Lock(
280 LPDIRECTSOUNDBUFFER this,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
283 TRACE(dsound,"(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx)\n",
284 this,
285 writecursor,
286 writebytes,
287 lplpaudioptr1,
288 audiobytes1,
289 lplpaudioptr2,
290 audiobytes2,
291 flags
293 if (flags & DSBLOCK_FROMWRITECURSOR)
294 writecursor = this->writepos;
295 assert(audiobytes1!=audiobytes2);
296 assert(lplpaudioptr1!=lplpaudioptr2);
297 if (writecursor+writebytes <= this->buflen) {
298 *(LPBYTE*)lplpaudioptr1 = this->buffer+writecursor;
299 *audiobytes1 = writebytes;
300 if (lplpaudioptr2)
301 *(LPBYTE*)lplpaudioptr2 = NULL;
302 if (audiobytes2)
303 *audiobytes2 = 0;
304 TRACE(dsound,"->%ld.0\n",writebytes);
305 } else {
306 *(LPBYTE*)lplpaudioptr1 = this->buffer+writecursor;
307 *audiobytes1 = this->buflen-writecursor;
308 if (lplpaudioptr2)
309 *(LPBYTE*)lplpaudioptr2 = this->buffer;
310 if (audiobytes2)
311 *audiobytes2 = writebytes-(this->buflen-writecursor);
312 TRACE(dsound,"->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
314 this->writepos=(writecursor+writebytes)%this->buflen;
315 return 0;
318 static HRESULT WINAPI IDirectSoundBuffer_SetCurrentPosition(
319 LPDIRECTSOUNDBUFFER this,DWORD newpos
321 TRACE(dsound,"(%p,%ld)\n",this,newpos);
322 this->playpos = newpos;
323 return 0;
326 static HRESULT WINAPI IDirectSoundBuffer_SetPan(
327 LPDIRECTSOUNDBUFFER this,LONG newpan
329 TRACE(dsound,"(%p,%ld)\n",this,newpan);
330 this->pan = newpan;
331 return 0;
334 static HRESULT WINAPI IDirectSoundBuffer_GetPan(
335 LPDIRECTSOUNDBUFFER this,LPLONG pan
337 TRACE(dsound,"(%p,%p)\n",this,pan);
338 *pan = this->pan;
339 return 0;
342 static HRESULT WINAPI IDirectSoundBuffer_Unlock(
343 LPDIRECTSOUNDBUFFER this,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
345 /* FIXME(dsound,"(%p,%p,%ld,%p,%ld):stub\n", this,p1,x1,p2,x2); */
346 return 0;
349 static HRESULT WINAPI IDirectSoundBuffer_GetFrequency(
350 LPDIRECTSOUNDBUFFER this,LPDWORD freq
352 TRACE(dsound,"(%p,%p)\n",this,freq);
353 *freq = this->wfx.nSamplesPerSec;
354 return 0;
357 static HRESULT WINAPI IDirectSoundBuffer_Initialize(
358 LPDIRECTSOUNDBUFFER this,LPDIRECTSOUND dsound,LPDSBUFFERDESC dbsd
360 FIXME(dsound,"(%p,%p,%p):stub\n",this,dsound,dbsd);
361 printf("Re-Init!!!\n");
362 return DSERR_ALREADYINITIALIZED;
365 static HRESULT WINAPI IDirectSoundBuffer_GetCaps(
366 LPDIRECTSOUNDBUFFER this,LPDSBCAPS caps
368 caps->dwSize = sizeof(*caps);
369 caps->dwFlags = DSBCAPS_PRIMARYBUFFER|DSBCAPS_STATIC|DSBCAPS_CTRLALL|DSBCAPS_LOCSOFTWARE;
370 caps->dwBufferBytes = 0;
371 caps->dwUnlockTransferRate = 0;
372 caps->dwPlayCpuOverhead = 0;
373 return DS_OK;
376 static HRESULT WINAPI IDirectSoundBuffer_QueryInterface(
377 LPDIRECTSOUNDBUFFER this,REFIID riid,LPVOID *ppobj
379 char xbuf[50];
381 if (!memcmp(&IID_IDirectSoundNotify,riid,sizeof(*riid))) {
382 IDirectSoundNotify *dsn;
384 dsn = (LPDIRECTSOUNDNOTIFY)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn));
385 dsn->ref = 1;
386 dsn->dsb = this;
387 this->lpvtbl->fnAddRef(this);
388 dsn->lpvtbl = &dsnvt;
389 *ppobj = (LPVOID)dsn;
390 return 0;
392 WINE_StringFromCLSID(riid,xbuf);
393 TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
394 return E_FAIL;
397 static struct tagLPDIRECTSOUNDBUFFER_VTABLE dsbvt = {
398 IDirectSoundBuffer_QueryInterface,
399 IDirectSoundBuffer_AddRef,
400 IDirectSoundBuffer_Release,
401 IDirectSoundBuffer_GetCaps,
402 IDirectSoundBuffer_GetCurrentPosition,
403 IDirectSoundBuffer_GetFormat,
404 IDirectSoundBuffer_GetVolume,
405 IDirectSoundBuffer_GetPan,
406 IDirectSoundBuffer_GetFrequency,
407 IDirectSoundBuffer_GetStatus,
408 IDirectSoundBuffer_Initialize,
409 IDirectSoundBuffer_Lock,
410 IDirectSoundBuffer_Play,
411 IDirectSoundBuffer_SetCurrentPosition,
412 IDirectSoundBuffer_SetFormat,
413 IDirectSoundBuffer_SetVolume,
414 IDirectSoundBuffer_SetPan,
415 IDirectSoundBuffer_SetFrequency,
416 IDirectSoundBuffer_Stop,
417 IDirectSoundBuffer_Unlock
420 /*******************************************************************************
421 * IDirectSound
424 static HRESULT WINAPI IDirectSound_SetCooperativeLevel(
425 LPDIRECTSOUND this,HWND32 hwnd,DWORD level
427 FIXME(dsound,"(%p,%08lx,%ld):stub\n",this,(DWORD)hwnd,level);
428 return 0;
432 static HRESULT WINAPI IDirectSound_CreateSoundBuffer(
433 LPDIRECTSOUND this,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER ppdsb,LPUNKNOWN lpunk
435 if (TRACE_ON(dsound)) {
436 TRACE(dsound,"(%p,%p,%p,%p)\n",this,dsbd,ppdsb,lpunk);
437 TRACE(dsound,"(size=%ld)\n",dsbd->dwSize);
438 TRACE(dsound,"(flags=0x%08lx\n",dsbd->dwFlags);
439 _dump_DSBCAPS(dsbd->dwFlags);
440 TRACE(dsound,"(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
441 TRACE(dsound,"(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
443 *ppdsb = (LPDIRECTSOUNDBUFFER)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBuffer));
444 (*ppdsb)->ref =1;
445 (*ppdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,dsbd->dwBufferBytes);
446 (*ppdsb)->buflen = dsbd->dwBufferBytes;
447 (*ppdsb)->playpos = 0;
448 (*ppdsb)->writepos = 0;
449 (*ppdsb)->lpvtbl = &dsbvt;
450 (*ppdsb)->dsound = this;
451 (*ppdsb)->playing = 0;
452 (*ppdsb)->volfac = 1.0;
453 memcpy(&((*ppdsb)->dsbd),dsbd,sizeof(*dsbd));
455 /* register buffer */
456 this->buffers = (LPDIRECTSOUNDBUFFER*)HeapReAlloc(GetProcessHeap(),0,this->buffers,sizeof(LPDIRECTSOUNDBUFFER)*(this->nrofbuffers+1));
457 this->buffers[this->nrofbuffers] = *ppdsb;
458 this->nrofbuffers++;
459 this->lpvtbl->fnAddRef(this);
461 if (dsbd->lpwfxFormat) dsbvt.fnSetFormat(*ppdsb,dsbd->lpwfxFormat);
462 return 0;
465 static HRESULT WINAPI IDirectSound_DuplicateSoundBuffer(
466 LPDIRECTSOUND this,LPDIRECTSOUNDBUFFER pdsb,LPLPDIRECTSOUNDBUFFER ppdsb
468 TRACE(dsound,"(%p,%p,%p)\n",this,pdsb,ppdsb);
470 *ppdsb = (LPDIRECTSOUNDBUFFER)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBuffer));
471 (*ppdsb)->ref =1;
472 (*ppdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,pdsb->buflen);
473 memcpy((*ppdsb)->buffer,pdsb->buffer,pdsb->buflen);
474 (*ppdsb)->buflen = pdsb->buflen;
475 (*ppdsb)->playpos = 0;
476 (*ppdsb)->writepos = 0;
477 (*ppdsb)->lpvtbl = &dsbvt;
478 (*ppdsb)->dsound = this;
479 dsbvt.fnSetFormat(*ppdsb,&(pdsb->wfx));
480 /* register buffer */
481 this->buffers = (LPDIRECTSOUNDBUFFER*)HeapReAlloc(GetProcessHeap(),0,this->buffers,sizeof(LPDIRECTSOUNDBUFFER)*(this->nrofbuffers+1));
482 this->buffers[this->nrofbuffers] = *ppdsb;
483 this->nrofbuffers++;
484 this->lpvtbl->fnAddRef(this);
485 return 0;
489 static HRESULT WINAPI IDirectSound_GetCaps(LPDIRECTSOUND this,LPDSCAPS caps) {
490 TRACE(dsound,"(%p,%p)\n",this,caps);
491 TRACE(dsound,"(flags=0x%08lx)\n",caps->dwFlags);
493 caps->dwSize = sizeof(*caps);
494 caps->dwFlags = DSCAPS_PRIMARYSTEREO|DSCAPS_PRIMARY16BIT|DSCAPS_EMULDRIVER|DSCAPS_SECONDARYSTEREO|DSCAPS_SECONDARY16BIT;
495 /* FIXME: query OSS */
496 caps->dwMinSecondarySampleRate = 22050;
497 caps->dwMaxSecondarySampleRate = 48000;
498 caps->dwPrimaryBuffers = 1;
499 /* FIXME: set the rest... hmm */
500 return 0;
503 static ULONG WINAPI IDirectSound_AddRef(LPDIRECTSOUND this) {
504 return ++(this->ref);
507 static ULONG WINAPI IDirectSound_Release(LPDIRECTSOUND this) {
508 if (!--(this->ref)) {
509 HeapFree(GetProcessHeap(),0,this);
510 dsound = NULL;
511 close(audiofd);audiofd = -1;
512 return 0;
514 return this->ref;
517 static HRESULT WINAPI IDirectSound_SetSpeakerConfig(
518 LPDIRECTSOUND this,DWORD config
520 FIXME(dsound,"(%p,0x%08lx):stub\n",this,config);
521 return 0;
524 static HRESULT WINAPI IDirectSound_QueryInterface(
525 LPDIRECTSOUND this,REFIID riid,LPVOID *ppobj
527 char xbuf[50];
529 WINE_StringFromCLSID(riid,xbuf);
530 TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
531 return E_FAIL;
534 static struct tagLPDIRECTSOUND_VTABLE dsvt = {
535 IDirectSound_QueryInterface,
536 IDirectSound_AddRef,
537 IDirectSound_Release,
538 IDirectSound_CreateSoundBuffer,
539 IDirectSound_GetCaps,
540 IDirectSound_DuplicateSoundBuffer,
541 IDirectSound_SetCooperativeLevel,
542 (void *)8,
543 (void *)9,
544 IDirectSound_SetSpeakerConfig,
545 (void *)11
548 static int
549 DSOUND_setformat(LPWAVEFORMATEX wfex) {
550 int xx,channels,speed,format,nformat;
553 switch (wfex->wFormatTag) {
554 default:
555 WARN(dsound,"unknown WAVE_FORMAT tag %d\n",wfex->wFormatTag);
556 return DSERR_BADFORMAT;
557 case WAVE_FORMAT_PCM:
558 break;
560 if (wfex->wBitsPerSample==8)
561 format = AFMT_U8;
562 else
563 format = AFMT_S16_LE;
565 if (-1==ioctl(audiofd,SNDCTL_DSP_GETFMTS,&xx)) {
566 perror("ioctl SNDCTL_DSP_GETFMTS");
567 return -1;
569 if ((xx&format)!=format) {/* format unsupported */
570 WARN(dsound,"SNDCTL_DSP_GETFMTS: format not supported\n");
571 return -1;
573 nformat = format;
574 if (-1==ioctl(audiofd,SNDCTL_DSP_SETFMT,&nformat)) {
575 perror("ioctl SNDCTL_DSP_SETFMT");
576 return -1;
578 if (nformat!=format) {/* didn't work */
579 WARN(dsound,"SNDCTL_DSP_GETFMTS: format not set\n");
580 return -1;
583 channels = wfex->nChannels-1;
584 if (-1==ioctl(audiofd,SNDCTL_DSP_STEREO,&channels)) {
585 perror("ioctl SNDCTL_DSP_STEREO");
586 return -1;
588 speed = wfex->nSamplesPerSec;
589 if (-1==ioctl(audiofd,SNDCTL_DSP_SPEED,&speed)) {
590 perror("ioctl SNDCTL_DSP_SPEED");
591 return -1;
593 TRACE(dsound,"(freq=%ld,channels=%d,bits=%d)\n",
594 wfex->nSamplesPerSec,wfex->nChannels,wfex->wBitsPerSample
596 return 0;
599 static LPDSBPOSITIONNOTIFY
600 DSOUND_nextevent(IDirectSoundBuffer *dsb) {
601 int i;
603 if (dsb->nrofnotifies) {
604 for (i=0;i<dsb->nrofnotifies;i++) {
605 if (dsb->playpos<dsb->notifies[i].dwOffset)
606 break;
608 if (i==dsb->nrofnotifies)
609 i=0;
610 return dsb->notifies+i;
612 return NULL;
615 #define CHECK_EVENT \
616 if (nextevent && (dsb->playpos == nextevent->dwOffset)) { \
617 SetEvent(nextevent->hEventNotify); \
618 TRACE(dsound,"signalled event %d\n",nextevent->hEventNotify);\
619 nextevent = DSOUND_nextevent(dsb); \
623 static void
624 DSOUND_MixInBuffer(IDirectSoundBuffer *dsb) {
625 int j,buflen = dsb->buflen;
626 LPDSBPOSITIONNOTIFY nextevent;
627 int xdiff = dsb->wfx.nSamplesPerSec-dsound->wfx.nSamplesPerSec;
629 /* Insomnia - Going along with REAL author's style */
630 long Rvoldec, Lvoldec;
631 long pan = dsb->pan;
632 long samp; /* temporary sample workspace */
635 double tmpr=dsb->volume-500;
636 double tmpl=tmpr;
637 if(pan>0) tmpl -= (double)pan;
638 else tmpr += (double)pan;
639 tmpl /= 1000.0;
640 tmpr /= 1000.0;
641 tmpl = pow(2.0, tmpl);
642 tmpr = pow(2.0, tmpr);
643 tmpl *= 65536; /* Set to the correct multiple times */
644 tmpr *= 65536; /* 65536 to be convenient for bit shifting */
645 tmpl += 0.5; /* Add .5 for rounding accuracy */
646 tmpr += 0.5;
647 Lvoldec = (long)tmpl;
648 Rvoldec = (long)tmpr;
650 /* End Insomnia's mod */
652 if (xdiff<0) xdiff=-xdiff;
653 if (xdiff>1500) {
654 WARN(dsound,"mixing in buffer of different frequency (%ld vs %ld), argh!\n",
655 dsb->wfx.nSamplesPerSec,dsound->wfx.nSamplesPerSec);
657 nextevent = DSOUND_nextevent(dsb);
658 /* TRACE(dsound,"(%d.%d.%d.%d)\n",dsound->wfx.wBitsPerSample,dsb->wfx.wBitsPerSample,dsound->wfx.nChannels,dsb->wfx.nChannels);*/
660 if (dsound->wfx.wBitsPerSample == 8) {
661 char *playbuf8 = (char*)playbuf;
663 if (dsb->wfx.wBitsPerSample == 8) {
664 unsigned char *xbuf = (unsigned char*)(dsb->buffer);
665 if (dsb->wfx.nChannels == 1) {
666 for (j=0;j<sizeof(playbuf)/2;j++) {
668 dsb->playpos=(dsb->playpos+1)%buflen;
669 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
670 dsb->playing = 0;
671 dsb->playpos = buflen;
672 return;
674 /* Insomnia- volume, panning, and correcting against wrap */
675 /* Left Channel */
676 samp = xbuf[dsb->playpos>>1];
677 samp *= Lvoldec;
678 samp >>= 16;
679 samp += playbuf8[(j<<1)];
680 if(samp > 127L) samp = 127L;
681 else if(samp < -128L) samp = -128L;
682 playbuf8[(j<<1)] = (short)samp;
684 /* Right Channel */
685 samp = xbuf[dsb->playpos>>1];
686 samp *= Rvoldec;
687 samp >>= 16;
688 samp += playbuf8[(j<<1)+1];
689 if(samp > 127L) samp = 127L;
690 else if(samp < -128L) samp = -128L;
691 playbuf8[(j<<1)+1] = (short)samp;
692 /* End Insomnia's mod */
694 CHECK_EVENT
696 } else {
697 for (j=0;j<sizeof(playbuf);j++) {
698 dsb->playpos=(dsb->playpos+1)%buflen;
699 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
700 dsb->playing = 0;
701 dsb->playpos = buflen;
702 return;
704 /* Insomnia- volume, panning, and correcting against wrap */
705 samp = xbuf[dsb->playpos>>1];
707 /* Right Channel */
708 if(j&1) samp *= Rvoldec;
709 /* Left Channel */
710 else samp *= Lvoldec;
712 samp >>= 16;
713 samp += playbuf8[j];
714 if(samp > 127L) samp = 127L;
715 else if(samp < -128L) samp = -128L;
716 playbuf8[j] = (short)samp;
717 /* End Insomnia's mod */
719 CHECK_EVENT
722 } else { /* 16 */
723 short *xbuf = (short*)(dsb->buffer);
724 if (dsb->wfx.nChannels == 1) {
725 for (j=0;j<sizeof(playbuf)/2;j++) {
726 dsb->playpos=(dsb->playpos+2)%buflen;
727 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
728 dsb->playing = 0;
729 dsb->playpos = buflen;
730 return;
732 /* Insomnia- volume, panning, and correcting against wrap */
733 /* Left Channel */
734 samp = xbuf[dsb->playpos>>1];
735 samp *= Lvoldec;
736 samp >>= 24;
737 samp += playbuf8[(j<<1)];
738 if(samp > 127L) samp = 127L;
739 else if(samp < -128L) samp = -128L;
740 playbuf8[(j<<1)] = (short)samp;
742 /* Right Channel */
743 samp = xbuf[dsb->playpos>>1];
744 samp *= Rvoldec;
745 samp >>= 24;
746 samp += playbuf8[(j<<1)+1];
747 if(samp > 127L) samp = 127L;
748 else if(samp < -128L) samp = -128L;
749 playbuf8[(j<<1)+1] = (short)samp;
750 /* End Insomnia's mod */
752 CHECK_EVENT
754 } else {
755 for (j=0;j<sizeof(playbuf);j++) {
756 dsb->playpos=(dsb->playpos+2)%buflen;
757 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
758 dsb->playing = 0;
759 dsb->playpos = buflen;
760 return;
762 /* Insomnia- volume, panning, and correcting against wrap */
763 samp = xbuf[dsb->playpos>>1];
765 /* Right Channel */
766 if(j&1) samp *= Rvoldec;
767 /* Left Channel */
768 else samp *= Lvoldec;
770 samp >>= 24;
771 samp += playbuf8[j];
772 if(samp > 127L) samp = 127L;
773 else if(samp < -128L) samp = -128L;
774 playbuf8[j] = (short)samp;
775 /* End Insomnia's mod */
777 CHECK_EVENT
781 } else { /* 16 bit */
782 if (dsb->wfx.wBitsPerSample == 8) {
783 /* unsigned char *xbuf = (unsigned char*)(dsb->buffer); */
784 char *xbuf = dsb->buffer;
785 if (dsb->wfx.nChannels == 1) {
786 printf("Mixing 8-bit stereo into 16!!\n");
787 for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0])/2;j++) {
788 dsb->playpos=(dsb->playpos+1)%buflen;
789 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
790 dsb->playing = 0;
791 dsb->playpos = buflen;
792 return;
794 /* Insomnia- volume, panning, and correcting against wrap */
795 /* Left Channel */
796 samp = xbuf[dsb->playpos>>1];
797 samp *= Lvoldec;
798 samp >>= 8;
799 samp += playbuf[(j<<1)];
800 if(samp > 32767L) samp = 32767L;
801 else if(samp < -32768L) samp = -32768L;
802 playbuf[(j<<1)] = (short)samp;
804 /* Right Channel */
805 samp = xbuf[dsb->playpos>>1];
806 samp *= Rvoldec;
807 samp >>= 8;
808 samp += playbuf[(j<<1)+1];
809 if(samp > 32767L) samp = 32767L;
810 else if(samp < -32768L) samp = -32768L;
811 playbuf[(j<<1)+1] = (short)samp;
812 /* End Insomnia's mod */
814 CHECK_EVENT
816 } else {
817 for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0]);j++) {
818 dsb->playpos=(dsb->playpos+1)%buflen;
819 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
820 dsb->playing = 0;
821 dsb->playpos = buflen;
822 return;
824 /* Insomnia- volume, panning, and correcting against wrap */
825 samp = xbuf[dsb->playpos>>1];
827 /* Right Channel */
828 if(j&1) samp *= Rvoldec;
829 /* Left Channel */
830 else samp *= Lvoldec;
832 samp >>= 8;
833 samp += playbuf[j];
834 if(samp > 32767L) samp = 32767L;
835 else if(samp < -32768L) samp = -32768L;
836 playbuf[j] = (short)samp;
837 /* End Insomnia's mod */
839 CHECK_EVENT
842 } else { /* 16 */
843 short *xbuf = (short*)(dsb->buffer);
844 if (dsb->wfx.nChannels == 1) {
845 for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0])/2;j++) {
846 dsb->playpos=(dsb->playpos+2)%buflen;
847 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
848 dsb->playing = 0;
849 dsb->playpos = buflen;
850 return;
852 /* Insomnia- volume, panning, and correcting against wrap */
853 /* Left Channel */
854 samp = xbuf[dsb->playpos>>1];
855 samp *= Lvoldec;
856 samp >>= 16;
857 samp += playbuf[(j<<1)];
858 if(samp > 32767L) samp = 32767L;
859 else if(samp < -32768L) samp = -32768L;
860 playbuf[(j<<1)] = (short)samp;
862 /* Right Channel */
863 samp = xbuf[dsb->playpos>>1];
864 samp *= Rvoldec;
865 samp >>= 16;
866 samp += playbuf[(j<<1)+1];
867 if(samp > 32767L) samp = 32767L;
868 else if(samp < -32768L) samp = -32768L;
869 playbuf[(j<<1)+1] = (short)samp;
870 /* End Insomnia's mod */
872 CHECK_EVENT
874 } else {
875 for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0]);j++) {
876 dsb->playpos=(dsb->playpos+2)%buflen;
877 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
878 dsb->playing = 0;
879 dsb->playpos = buflen;
880 return;
882 /* Insomnia- volume, panning, and correcting against wrap */
883 samp = xbuf[dsb->playpos>>1];
885 /* Right Channel */
886 if(j&1) samp *= Rvoldec;
887 /* Left Channel */
888 else samp *= Lvoldec;
890 samp >>= 16;
891 samp += playbuf[j];
892 if(samp > 32767L) samp = 32767L;
893 else if(samp < -32768L) samp = -32768L;
894 playbuf[j] = (short)samp;
895 /* End Insomnia's mod */
897 CHECK_EVENT
904 static DWORD
905 DSOUND_thread(LPVOID arg) {
906 int res,i,curleft,playing,haveprimary = 0;
908 TRACE(dsound,"dsound is at pid %d\n",getpid());
909 while (1) {
910 if (!dsound) {
911 WARN(dsound,"DSOUND thread giving up.\n");
912 ExitThread(0);
914 if (getppid()==1) {
915 WARN(dsound,"DSOUND father died? Giving up.\n");
916 ExitThread(0);
918 /* RACE: dsound could be deleted */
919 dsound->lpvtbl->fnAddRef(dsound);
920 if (!dsound->nrofbuffers) {
921 /* no soundbuffer yet... wait. */
922 Sleep(1000);
923 continue;
925 memset(playbuf,0,sizeof(playbuf));
926 playing = 0;
927 dsound->lpvtbl->fnAddRef(dsound);
928 haveprimary = 0;
929 for (i=dsound->nrofbuffers;i--;) {
930 IDirectSoundBuffer *dsb = dsound->buffers[i];
932 if (!dsb || !dsb->lpvtbl)
933 continue;
934 dsb->lpvtbl->fnAddRef(dsb);
935 if (dsb->playing && dsb->buflen)
936 playing++;
937 if (dsb->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
938 haveprimary = 1;
939 if (memcmp(&dsound->wfx,&(dsb->wfx),sizeof(dsound->wfx))) {
940 DSOUND_setformat(&(dsb->wfx));
941 memcpy(&dsound->wfx,&(dsb->wfx),sizeof(dsb->wfx));
944 dsb->lpvtbl->fnRelease(dsb);
946 /* We have just one playbuffer, so use its format */
947 if ((playing==1) && !haveprimary) {
948 for (i=dsound->nrofbuffers;i--;) {
949 IDirectSoundBuffer *dsb = dsound->buffers[i];
951 dsb->lpvtbl->fnAddRef(dsb);
952 if (dsb->playing && dsb->buflen) {
953 if (memcmp(&dsound->wfx,&(dsb->wfx),sizeof(dsound->wfx))) {
954 DSOUND_setformat(&(dsb->wfx));
955 memcpy(&dsound->wfx,&(dsb->wfx),sizeof(dsb->wfx));
958 dsb->lpvtbl->fnRelease(dsb);
961 for (i=dsound->nrofbuffers;i--;) {
962 IDirectSoundBuffer *dsb = dsound->buffers[i];
964 if (!dsb || !dsb->lpvtbl)
965 continue;
966 dsb->lpvtbl->fnAddRef(dsb);
967 if (dsb->buflen && dsb->playing) {
968 playing++;
969 DSOUND_MixInBuffer(dsb);
971 dsb->lpvtbl->fnRelease(dsb);
973 dsound->lpvtbl->fnRelease(dsound);
975 /*fputc('0'+playing,stderr);*/
976 curleft = 0;
977 while (curleft < sizeof(playbuf)) {
978 res = write(audiofd,(LPBYTE)playbuf+curleft,sizeof(playbuf)-curleft);
979 if (res==-1) {
980 perror("write audiofd");
981 ExitThread(0);
982 break;
984 curleft+=res;
987 ExitThread(0);
990 #endif /* HAVE_OSS */
992 HRESULT WINAPI DirectSoundCreate(LPGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter ) {
993 int xx;
994 if (lpGUID)
995 TRACE(dsound,"(%p,%p,%p)\n",lpGUID,ppDS,pUnkOuter);
996 #ifdef HAVE_OSS
997 if (audiofd>=0)
998 return DSERR_ALLOCATED;
999 audiofd = open("/dev/audio",O_WRONLY);
1000 if (audiofd==-1) {
1001 perror("open /dev/audio");
1002 audiofd=0;
1003 return DSERR_NODRIVER;
1005 xx=0x0002000c;
1006 if (-1==ioctl(audiofd,SNDCTL_DSP_SETFRAGMENT,&xx))
1007 perror("ioctl SETFRAGMENT");
1009 TRACE(dsound,"SETFRAGMENT. count is now %d, fragsize is %d\n",
1010 (xx>>16)+1,xx&0xffff
1014 *ppDS = (LPDIRECTSOUND)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSound));
1015 (*ppDS)->ref = 1;
1016 (*ppDS)->lpvtbl = &dsvt;
1017 (*ppDS)->buffers = NULL;
1018 (*ppDS)->nrofbuffers = 0;
1020 (*ppDS)->wfx.wFormatTag = 1;
1021 (*ppDS)->wfx.nChannels = 2;
1022 (*ppDS)->wfx.nSamplesPerSec = 22050;
1023 (*ppDS)->wfx.nAvgBytesPerSec = 44100;
1024 (*ppDS)->wfx.nBlockAlign = 2;
1025 (*ppDS)->wfx.wBitsPerSample = 8;
1027 DSOUND_setformat(&((*ppDS)->wfx));
1029 if (!dsound) {
1030 HANDLE32 hnd;
1031 DWORD xid;
1033 dsound = (*ppDS);
1034 hnd = CreateThread(NULL,0,DSOUND_thread,0,0,&xid);
1036 return 0;
1037 #else
1038 MessageBox32A(0,"DirectSound needs the Open Sound System Driver, which has not been found by ./configure.","WINE DirectSound",MB_OK|MB_ICONSTOP);
1039 return DSERR_NODRIVER;
1040 #endif
1044 /*******************************************************************************
1045 * DllGetClassObject [DSOUND.4]
1046 * Retrieves class object from a DLL object
1048 * NOTES
1049 * Docs say returns STDAPI
1051 * PARAMS
1052 * rclsid [I] CLSID for the class object
1053 * riid [I] Reference to identifier of interface for class object
1054 * ppv [O] Address of variable to receive interface pointer for riid
1056 * RETURNS
1057 * Success: S_OK
1058 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
1059 * E_UNEXPECTED
1061 DWORD WINAPI DllGetClassObject( REFCLSID rclsid, REFIID riid, LPVOID *ppv )
1063 FIXME(dsound, "(%p,%p,%p): stub\n", rclsid, riid, ppv);
1064 return S_OK;
1068 /*******************************************************************************
1069 * DllCanUnloadNow [DSOUND.3] Determines whether the DLL is in use.
1071 * RETURNS
1072 * Success: S_OK
1073 * Failure: S_FALSE
1075 DWORD WINAPI DllCanUnloadNow(void)
1077 FIXME(dsound, "(void): stub\n");
1078 return S_FALSE;