Do not clip source rectangle even by visible region for bitblts.
[wine/multimedia.git] / multimedia / dsound.c
blob654206fe361ee035ae33a20cc79298ac8ef6eae9
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 <errno.h>
34 #include <sys/types.h>
35 #include <sys/time.h>
36 #include <sys/fcntl.h>
37 #include <unistd.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <math.h> /* Insomnia - pow() function */
41 #include "windows.h"
42 #include "winerror.h"
43 #include "interfaces.h"
44 #include "mmsystem.h"
45 #include "dsound.h"
46 #include "thread.h"
47 #include "debug.h"
49 #ifdef HAVE_OSS
50 # include <sys/ioctl.h>
51 # ifdef HAVE_MACHINE_SOUNDCARD_H
52 # include <machine/soundcard.h>
53 # endif
54 # ifdef HAVE_SYS_SOUNDCARD_H
55 # include <sys/soundcard.h>
56 # endif
58 static int audiofd = -1;
59 static LPDIRECTSOUND dsound = NULL;
61 static short playbuf[2048];
63 #endif
65 HRESULT WINAPI DirectSoundEnumerate32A(LPDSENUMCALLBACK32A enumcb,LPVOID context) {
66 #ifdef HAVE_OSS
67 enumcb(NULL,"WINE DirectSound using Open Sound System","sound",context);
68 #endif
69 return 0;
72 #ifdef HAVE_OSS
73 static void _dump_DSBCAPS(DWORD xmask) {
74 struct {
75 DWORD mask;
76 char *name;
77 } flags[] = {
78 #define FE(x) { x, #x },
79 FE(DSBCAPS_PRIMARYBUFFER)
80 FE(DSBCAPS_STATIC)
81 FE(DSBCAPS_LOCHARDWARE)
82 FE(DSBCAPS_LOCSOFTWARE)
83 FE(DSBCAPS_CTRLFREQUENCY)
84 FE(DSBCAPS_CTRLPAN)
85 FE(DSBCAPS_CTRLVOLUME)
86 FE(DSBCAPS_CTRLDEFAULT)
87 FE(DSBCAPS_CTRLALL)
88 FE(DSBCAPS_STICKYFOCUS)
89 FE(DSBCAPS_GETCURRENTPOSITION2)
91 int i;
93 for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
94 if (flags[i].mask & xmask)
95 fprintf(stderr,"%s ",flags[i].name);
98 /*******************************************************************************
99 * IDirectSoundNotify
101 static HRESULT WINAPI IDirectSoundNotify_QueryInterface(
102 LPDIRECTSOUNDNOTIFY this,REFIID riid,LPVOID *ppobj
104 char xbuf[50];
106 WINE_StringFromCLSID(riid,xbuf);
107 TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
108 return E_FAIL;
111 static ULONG WINAPI IDirectSoundNotify_AddRef(LPDIRECTSOUNDNOTIFY this) {
112 return ++(this->ref);
115 static ULONG WINAPI IDirectSoundNotify_Release(LPDIRECTSOUNDNOTIFY this) {
116 this->ref--;
117 if (!this->ref) {
118 this->dsb->lpvtbl->fnRelease(this->dsb);
119 HeapFree(GetProcessHeap(),0,this);
120 return 0;
122 return this->ref;
125 static int _sort_notifies(const void *a,const void *b) {
126 LPDSBPOSITIONNOTIFY na = (LPDSBPOSITIONNOTIFY)a;
127 LPDSBPOSITIONNOTIFY nb = (LPDSBPOSITIONNOTIFY)b;
129 return na->dwOffset-nb->dwOffset;
132 static HRESULT WINAPI IDirectSoundNotify_SetNotificationPositions(
133 LPDIRECTSOUNDNOTIFY this,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
135 int i;
137 if (TRACE_ON(dsound)) {
138 TRACE(dsound,"(%p,0x%08lx,%p)\n",this,howmuch,notify);
139 for (i=0;i<howmuch;i++)
140 TRACE(dsound,"notify at %ld to 0x%08lx\n",
141 notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
143 this->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,this->dsb->notifies,(this->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
144 memcpy( this->dsb->notifies+this->dsb->nrofnotifies,
145 notify,
146 howmuch*sizeof(DSBPOSITIONNOTIFY)
148 this->dsb->nrofnotifies+=howmuch;
149 qsort(this->dsb->notifies,this->dsb->nrofnotifies,sizeof(DSBPOSITIONNOTIFY),_sort_notifies);
150 return 0;
153 IDirectSoundNotify_VTable dsnvt = {
154 IDirectSoundNotify_QueryInterface,
155 IDirectSoundNotify_AddRef,
156 IDirectSoundNotify_Release,
157 IDirectSoundNotify_SetNotificationPositions,
160 /*******************************************************************************
161 * IDirectSoundBuffer
163 static HRESULT WINAPI IDirectSoundBuffer_SetFormat(
164 LPDIRECTSOUNDBUFFER this,LPWAVEFORMATEX wfex
167 memcpy(&(this->wfx),wfex,sizeof(this->wfx));
168 TRACE(dsound,"(%p,%p)\n", this,wfex);
169 TRACE(dsound,"(formattag=0x%04x,chans=%d,samplerate=%ld"
170 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
171 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
172 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
173 wfex->wBitsPerSample, wfex->cbSize);
175 return 0;
178 static HRESULT WINAPI IDirectSoundBuffer_SetVolume(
179 LPDIRECTSOUNDBUFFER this,LONG vol
181 TRACE(dsound,"(%p,%ld)\n",this,vol);
182 this->volume = vol;
183 this->volfac = ((double)vol+10000.0)/10000.0;
185 return 0;
188 static HRESULT WINAPI IDirectSoundBuffer_GetVolume(
189 LPDIRECTSOUNDBUFFER this,LPLONG vol
191 TRACE(dsound,"(%p,%p)\n",this,vol);
192 *vol = this->volume;
193 return 0;
196 static HRESULT WINAPI IDirectSoundBuffer_SetFrequency(
197 LPDIRECTSOUNDBUFFER this,DWORD freq
199 TRACE(dsound,"(%p,%ld)\n",this,freq);
200 this->wfx.nSamplesPerSec = freq;
201 this->wfx.nAvgBytesPerSec = freq*this->wfx.nChannels*(this->wfx.wBitsPerSample/8);
202 return 0;
205 static HRESULT WINAPI IDirectSoundBuffer_Play(
206 LPDIRECTSOUNDBUFFER this,DWORD reserved1,DWORD reserved2,DWORD flags
208 TRACE(dsound,"(%p,%08lx,%08lx,%08lx)\n",
209 this,reserved1,reserved2,flags
211 this->playpos = 0;
212 this->playflags = flags;
213 this->playing = 1;
214 return 0;
217 static HRESULT WINAPI IDirectSoundBuffer_Stop(LPDIRECTSOUNDBUFFER this) {
218 TRACE(dsound,"(%p)\n",this);
219 this->playing = 0;
220 this->writepos = 0; /* hmm */
221 return 0;
224 static DWORD WINAPI IDirectSoundBuffer_AddRef(LPDIRECTSOUNDBUFFER this) {
225 return ++(this->ref);
227 static DWORD WINAPI IDirectSoundBuffer_Release(LPDIRECTSOUNDBUFFER this) {
228 int i;
230 if (--this->ref)
231 return this->ref;
232 for (i=0;i<this->dsound->nrofbuffers;i++)
233 if (this->dsound->buffers[i] == this)
234 break;
235 if (i < this->dsound->nrofbuffers) {
236 memcpy(
237 this->dsound->buffers+i,
238 this->dsound->buffers+i+1,
239 sizeof(LPDIRECTSOUNDBUFFER)*(this->dsound->nrofbuffers-i-1)
241 this->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,this->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*this->dsound->nrofbuffers);
242 this->dsound->nrofbuffers--;
243 this->dsound->lpvtbl->fnRelease(this->dsound);
245 HeapFree(GetProcessHeap(),0,this->buffer);
246 HeapFree(GetProcessHeap(),0,this);
247 return 0;
250 static HRESULT WINAPI IDirectSoundBuffer_GetCurrentPosition(
251 LPDIRECTSOUNDBUFFER this,LPDWORD playpos,LPDWORD writepos
253 TRACE(dsound,"(%p,%p,%p)\n",this,playpos,writepos);
254 if (playpos) *playpos = this->playpos;
255 if (writepos) *writepos = this->writepos;
256 return 0;
259 static HRESULT WINAPI IDirectSoundBuffer_GetStatus(
260 LPDIRECTSOUNDBUFFER this,LPDWORD status
262 TRACE(dsound,"(%p,%p)\n",this,status);
263 *status = 0;
264 if (this->playing)
265 *status |= DSBSTATUS_PLAYING;
266 if (this->playflags & DSBPLAY_LOOPING)
267 *status |= DSBSTATUS_LOOPING;
268 return 0;
271 static HRESULT WINAPI IDirectSoundBuffer_GetFormat(
272 LPDIRECTSOUNDBUFFER this,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
274 TRACE(dsound,"(%p,%p,%ld,%p)\n",this,lpwf,wfsize,wfwritten);
275 if (wfsize>sizeof(this->wfx)) wfsize = sizeof(this->wfx);
276 memcpy(lpwf,&(this->wfx),wfsize);
277 if (wfwritten) *wfwritten = wfsize;
278 return 0;
281 static HRESULT WINAPI IDirectSoundBuffer_Lock(
282 LPDIRECTSOUNDBUFFER this,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
285 TRACE(dsound,"(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx)\n",
286 this,
287 writecursor,
288 writebytes,
289 lplpaudioptr1,
290 audiobytes1,
291 lplpaudioptr2,
292 audiobytes2,
293 flags
295 if (flags & DSBLOCK_FROMWRITECURSOR)
296 writecursor = this->writepos;
297 assert(audiobytes1!=audiobytes2);
298 assert(lplpaudioptr1!=lplpaudioptr2);
299 if (writecursor+writebytes <= this->buflen) {
300 *(LPBYTE*)lplpaudioptr1 = this->buffer+writecursor;
301 *audiobytes1 = writebytes;
302 if (lplpaudioptr2)
303 *(LPBYTE*)lplpaudioptr2 = NULL;
304 if (audiobytes2)
305 *audiobytes2 = 0;
306 TRACE(dsound,"->%ld.0\n",writebytes);
307 } else {
308 *(LPBYTE*)lplpaudioptr1 = this->buffer+writecursor;
309 *audiobytes1 = this->buflen-writecursor;
310 if (lplpaudioptr2)
311 *(LPBYTE*)lplpaudioptr2 = this->buffer;
312 if (audiobytes2)
313 *audiobytes2 = writebytes-(this->buflen-writecursor);
314 TRACE(dsound,"->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
316 this->writepos=(writecursor+writebytes)%this->buflen;
317 return 0;
320 static HRESULT WINAPI IDirectSoundBuffer_SetCurrentPosition(
321 LPDIRECTSOUNDBUFFER this,DWORD newpos
323 TRACE(dsound,"(%p,%ld)\n",this,newpos);
324 this->playpos = newpos;
325 return 0;
328 static HRESULT WINAPI IDirectSoundBuffer_SetPan(
329 LPDIRECTSOUNDBUFFER this,LONG newpan
331 TRACE(dsound,"(%p,%ld)\n",this,newpan);
332 this->pan = newpan;
333 return 0;
336 static HRESULT WINAPI IDirectSoundBuffer_GetPan(
337 LPDIRECTSOUNDBUFFER this,LPLONG pan
339 TRACE(dsound,"(%p,%p)\n",this,pan);
340 *pan = this->pan;
341 return 0;
344 static HRESULT WINAPI IDirectSoundBuffer_Unlock(
345 LPDIRECTSOUNDBUFFER this,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
347 /* FIXME(dsound,"(%p,%p,%ld,%p,%ld):stub\n", this,p1,x1,p2,x2); */
348 return 0;
351 static HRESULT WINAPI IDirectSoundBuffer_GetFrequency(
352 LPDIRECTSOUNDBUFFER this,LPDWORD freq
354 TRACE(dsound,"(%p,%p)\n",this,freq);
355 *freq = this->wfx.nSamplesPerSec;
356 return 0;
359 static HRESULT WINAPI IDirectSoundBuffer_Initialize(
360 LPDIRECTSOUNDBUFFER this,LPDIRECTSOUND dsound,LPDSBUFFERDESC dbsd
362 FIXME(dsound,"(%p,%p,%p):stub\n",this,dsound,dbsd);
363 printf("Re-Init!!!\n");
364 return DSERR_ALREADYINITIALIZED;
367 static HRESULT WINAPI IDirectSoundBuffer_GetCaps(
368 LPDIRECTSOUNDBUFFER this,LPDSBCAPS caps
370 caps->dwSize = sizeof(*caps);
371 caps->dwFlags = DSBCAPS_PRIMARYBUFFER|DSBCAPS_STATIC|DSBCAPS_CTRLALL|DSBCAPS_LOCSOFTWARE;
372 caps->dwBufferBytes = 0;
373 caps->dwUnlockTransferRate = 0;
374 caps->dwPlayCpuOverhead = 0;
375 return DS_OK;
378 static HRESULT WINAPI IDirectSoundBuffer_QueryInterface(
379 LPDIRECTSOUNDBUFFER this,REFIID riid,LPVOID *ppobj
381 char xbuf[50];
383 if (!memcmp(&IID_IDirectSoundNotify,riid,sizeof(*riid))) {
384 IDirectSoundNotify *dsn;
386 dsn = (LPDIRECTSOUNDNOTIFY)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn));
387 dsn->ref = 1;
388 dsn->dsb = this;
389 this->lpvtbl->fnAddRef(this);
390 dsn->lpvtbl = &dsnvt;
391 *ppobj = (LPVOID)dsn;
392 return 0;
394 WINE_StringFromCLSID(riid,xbuf);
395 TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
396 return E_FAIL;
399 static struct tagLPDIRECTSOUNDBUFFER_VTABLE dsbvt = {
400 IDirectSoundBuffer_QueryInterface,
401 IDirectSoundBuffer_AddRef,
402 IDirectSoundBuffer_Release,
403 IDirectSoundBuffer_GetCaps,
404 IDirectSoundBuffer_GetCurrentPosition,
405 IDirectSoundBuffer_GetFormat,
406 IDirectSoundBuffer_GetVolume,
407 IDirectSoundBuffer_GetPan,
408 IDirectSoundBuffer_GetFrequency,
409 IDirectSoundBuffer_GetStatus,
410 IDirectSoundBuffer_Initialize,
411 IDirectSoundBuffer_Lock,
412 IDirectSoundBuffer_Play,
413 IDirectSoundBuffer_SetCurrentPosition,
414 IDirectSoundBuffer_SetFormat,
415 IDirectSoundBuffer_SetVolume,
416 IDirectSoundBuffer_SetPan,
417 IDirectSoundBuffer_SetFrequency,
418 IDirectSoundBuffer_Stop,
419 IDirectSoundBuffer_Unlock
422 /*******************************************************************************
423 * IDirectSound
426 static HRESULT WINAPI IDirectSound_SetCooperativeLevel(
427 LPDIRECTSOUND this,HWND32 hwnd,DWORD level
429 FIXME(dsound,"(%p,%08lx,%ld):stub\n",this,(DWORD)hwnd,level);
430 return 0;
434 static HRESULT WINAPI IDirectSound_CreateSoundBuffer(
435 LPDIRECTSOUND this,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER ppdsb,LPUNKNOWN lpunk
437 if (TRACE_ON(dsound)) {
438 TRACE(dsound,"(%p,%p,%p,%p)\n",this,dsbd,ppdsb,lpunk);
439 TRACE(dsound,"(size=%ld)\n",dsbd->dwSize);
440 TRACE(dsound,"(flags=0x%08lx\n",dsbd->dwFlags);
441 _dump_DSBCAPS(dsbd->dwFlags);
442 TRACE(dsound,"(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
443 TRACE(dsound,"(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
445 *ppdsb = (LPDIRECTSOUNDBUFFER)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBuffer));
446 (*ppdsb)->ref =1;
447 (*ppdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,dsbd->dwBufferBytes);
448 (*ppdsb)->buflen = dsbd->dwBufferBytes;
449 (*ppdsb)->playpos = 0;
450 (*ppdsb)->writepos = 0;
451 (*ppdsb)->lpvtbl = &dsbvt;
452 (*ppdsb)->dsound = this;
453 (*ppdsb)->playing = 0;
454 (*ppdsb)->volfac = 1.0;
455 memcpy(&((*ppdsb)->dsbd),dsbd,sizeof(*dsbd));
457 /* register buffer */
458 this->buffers = (LPDIRECTSOUNDBUFFER*)HeapReAlloc(GetProcessHeap(),0,this->buffers,sizeof(LPDIRECTSOUNDBUFFER)*(this->nrofbuffers+1));
459 this->buffers[this->nrofbuffers] = *ppdsb;
460 this->nrofbuffers++;
461 this->lpvtbl->fnAddRef(this);
463 if (dsbd->lpwfxFormat) dsbvt.fnSetFormat(*ppdsb,dsbd->lpwfxFormat);
464 return 0;
467 static HRESULT WINAPI IDirectSound_DuplicateSoundBuffer(
468 LPDIRECTSOUND this,LPDIRECTSOUNDBUFFER pdsb,LPLPDIRECTSOUNDBUFFER ppdsb
470 TRACE(dsound,"(%p,%p,%p)\n",this,pdsb,ppdsb);
472 *ppdsb = (LPDIRECTSOUNDBUFFER)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBuffer));
473 (*ppdsb)->ref =1;
474 (*ppdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,pdsb->buflen);
475 memcpy((*ppdsb)->buffer,pdsb->buffer,pdsb->buflen);
476 (*ppdsb)->buflen = pdsb->buflen;
477 (*ppdsb)->playpos = 0;
478 (*ppdsb)->writepos = 0;
479 (*ppdsb)->lpvtbl = &dsbvt;
480 (*ppdsb)->dsound = this;
481 dsbvt.fnSetFormat(*ppdsb,&(pdsb->wfx));
482 /* register buffer */
483 this->buffers = (LPDIRECTSOUNDBUFFER*)HeapReAlloc(GetProcessHeap(),0,this->buffers,sizeof(LPDIRECTSOUNDBUFFER)*(this->nrofbuffers+1));
484 this->buffers[this->nrofbuffers] = *ppdsb;
485 this->nrofbuffers++;
486 this->lpvtbl->fnAddRef(this);
487 return 0;
491 static HRESULT WINAPI IDirectSound_GetCaps(LPDIRECTSOUND this,LPDSCAPS caps) {
492 TRACE(dsound,"(%p,%p)\n",this,caps);
493 TRACE(dsound,"(flags=0x%08lx)\n",caps->dwFlags);
495 caps->dwSize = sizeof(*caps);
496 caps->dwFlags = DSCAPS_PRIMARYSTEREO|DSCAPS_PRIMARY16BIT|DSCAPS_EMULDRIVER|DSCAPS_SECONDARYSTEREO|DSCAPS_SECONDARY16BIT;
497 /* FIXME: query OSS */
498 caps->dwMinSecondarySampleRate = 22050;
499 caps->dwMaxSecondarySampleRate = 48000;
500 caps->dwPrimaryBuffers = 1;
501 /* FIXME: set the rest... hmm */
502 return 0;
505 static ULONG WINAPI IDirectSound_AddRef(LPDIRECTSOUND this) {
506 return ++(this->ref);
509 static ULONG WINAPI IDirectSound_Release(LPDIRECTSOUND this) {
510 if (!--(this->ref)) {
511 HeapFree(GetProcessHeap(),0,this);
512 dsound = NULL;
513 close(audiofd);audiofd = -1;
514 return 0;
516 return this->ref;
519 static HRESULT WINAPI IDirectSound_SetSpeakerConfig(
520 LPDIRECTSOUND this,DWORD config
522 FIXME(dsound,"(%p,0x%08lx):stub\n",this,config);
523 return 0;
526 static HRESULT WINAPI IDirectSound_QueryInterface(
527 LPDIRECTSOUND this,REFIID riid,LPVOID *ppobj
529 char xbuf[50];
531 WINE_StringFromCLSID(riid,xbuf);
532 TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
533 return E_FAIL;
536 static struct tagLPDIRECTSOUND_VTABLE dsvt = {
537 IDirectSound_QueryInterface,
538 IDirectSound_AddRef,
539 IDirectSound_Release,
540 IDirectSound_CreateSoundBuffer,
541 IDirectSound_GetCaps,
542 IDirectSound_DuplicateSoundBuffer,
543 IDirectSound_SetCooperativeLevel,
544 (void *)8,
545 (void *)9,
546 IDirectSound_SetSpeakerConfig,
547 (void *)11
550 static int
551 DSOUND_setformat(LPWAVEFORMATEX wfex) {
552 int xx,channels,speed,format,nformat;
555 switch (wfex->wFormatTag) {
556 default:
557 WARN(dsound,"unknown WAVE_FORMAT tag %d\n",wfex->wFormatTag);
558 return DSERR_BADFORMAT;
559 case WAVE_FORMAT_PCM:
560 break;
562 if (wfex->wBitsPerSample==8)
563 format = AFMT_U8;
564 else
565 format = AFMT_S16_LE;
567 if (-1==ioctl(audiofd,SNDCTL_DSP_GETFMTS,&xx)) {
568 perror("ioctl SNDCTL_DSP_GETFMTS");
569 return -1;
571 if ((xx&format)!=format) {/* format unsupported */
572 WARN(dsound,"SNDCTL_DSP_GETFMTS: format not supported\n");
573 return -1;
575 nformat = format;
576 if (-1==ioctl(audiofd,SNDCTL_DSP_SETFMT,&nformat)) {
577 perror("ioctl SNDCTL_DSP_SETFMT");
578 return -1;
580 if (nformat!=format) {/* didn't work */
581 WARN(dsound,"SNDCTL_DSP_GETFMTS: format not set\n");
582 return -1;
585 channels = wfex->nChannels-1;
586 if (-1==ioctl(audiofd,SNDCTL_DSP_STEREO,&channels)) {
587 perror("ioctl SNDCTL_DSP_STEREO");
588 return -1;
590 speed = wfex->nSamplesPerSec;
591 if (-1==ioctl(audiofd,SNDCTL_DSP_SPEED,&speed)) {
592 perror("ioctl SNDCTL_DSP_SPEED");
593 return -1;
595 TRACE(dsound,"(freq=%ld,channels=%d,bits=%d)\n",
596 wfex->nSamplesPerSec,wfex->nChannels,wfex->wBitsPerSample
598 return 0;
601 static LPDSBPOSITIONNOTIFY
602 DSOUND_nextevent(IDirectSoundBuffer *dsb) {
603 int i;
605 if (dsb->nrofnotifies) {
606 for (i=0;i<dsb->nrofnotifies;i++) {
607 if (dsb->playpos<dsb->notifies[i].dwOffset)
608 break;
610 if (i==dsb->nrofnotifies)
611 i=0;
612 return dsb->notifies+i;
614 return NULL;
617 #define CHECK_EVENT \
618 if (nextevent && (dsb->playpos == nextevent->dwOffset)) { \
619 SetEvent(nextevent->hEventNotify); \
620 TRACE(dsound,"signalled event %d\n",nextevent->hEventNotify);\
621 nextevent = DSOUND_nextevent(dsb); \
625 static void
626 DSOUND_MixInBuffer(IDirectSoundBuffer *dsb) {
627 int j,buflen = dsb->buflen;
628 LPDSBPOSITIONNOTIFY nextevent;
629 int xdiff = dsb->wfx.nSamplesPerSec-dsound->wfx.nSamplesPerSec;
631 /* Insomnia - Going along with REAL author's style */
632 long Rvoldec, Lvoldec;
633 long pan = dsb->pan;
634 long samp; /* temporary sample workspace */
637 double tmpr=dsb->volume-500;
638 double tmpl=tmpr;
639 if(pan>0) tmpl -= (double)pan;
640 else tmpr += (double)pan;
641 tmpl /= 1000.0;
642 tmpr /= 1000.0;
643 tmpl = pow(2.0, tmpl);
644 tmpr = pow(2.0, tmpr);
645 tmpl *= 65536; /* Set to the correct multiple times */
646 tmpr *= 65536; /* 65536 to be convenient for bit shifting */
647 tmpl += 0.5; /* Add .5 for rounding accuracy */
648 tmpr += 0.5;
649 Lvoldec = (long)tmpl;
650 Rvoldec = (long)tmpr;
652 /* End Insomnia's mod */
654 if (xdiff<0) xdiff=-xdiff;
655 if (xdiff>1500) {
656 WARN(dsound,"mixing in buffer of different frequency (%ld vs %ld), argh!\n",
657 dsb->wfx.nSamplesPerSec,dsound->wfx.nSamplesPerSec);
659 nextevent = DSOUND_nextevent(dsb);
660 /* TRACE(dsound,"(%d.%d.%d.%d)\n",dsound->wfx.wBitsPerSample,dsb->wfx.wBitsPerSample,dsound->wfx.nChannels,dsb->wfx.nChannels);*/
662 if (dsound->wfx.wBitsPerSample == 8) {
663 char *playbuf8 = (char*)playbuf;
665 if (dsb->wfx.wBitsPerSample == 8) {
666 unsigned char *xbuf = (unsigned char*)(dsb->buffer);
667 if (dsb->wfx.nChannels == 1) {
668 for (j=0;j<sizeof(playbuf)/2;j++) {
670 dsb->playpos=(dsb->playpos+1)%buflen;
671 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
672 dsb->playing = 0;
673 dsb->playpos = buflen;
674 return;
676 /* Insomnia- volume, panning, and correcting against wrap */
677 /* Left Channel */
678 samp = xbuf[dsb->playpos>>1];
679 samp *= Lvoldec;
680 samp >>= 16;
681 samp += playbuf8[(j<<1)];
682 if(samp > 127L) samp = 127L;
683 else if(samp < -128L) samp = -128L;
684 playbuf8[(j<<1)] = (short)samp;
686 /* Right Channel */
687 samp = xbuf[dsb->playpos>>1];
688 samp *= Rvoldec;
689 samp >>= 16;
690 samp += playbuf8[(j<<1)+1];
691 if(samp > 127L) samp = 127L;
692 else if(samp < -128L) samp = -128L;
693 playbuf8[(j<<1)+1] = (short)samp;
694 /* End Insomnia's mod */
696 CHECK_EVENT
698 } else {
699 for (j=0;j<sizeof(playbuf);j++) {
700 dsb->playpos=(dsb->playpos+1)%buflen;
701 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
702 dsb->playing = 0;
703 dsb->playpos = buflen;
704 return;
706 /* Insomnia- volume, panning, and correcting against wrap */
707 samp = xbuf[dsb->playpos>>1];
709 /* Right Channel */
710 if(j&1) samp *= Rvoldec;
711 /* Left Channel */
712 else samp *= Lvoldec;
714 samp >>= 16;
715 samp += playbuf8[j];
716 if(samp > 127L) samp = 127L;
717 else if(samp < -128L) samp = -128L;
718 playbuf8[j] = (short)samp;
719 /* End Insomnia's mod */
721 CHECK_EVENT
724 } else { /* 16 */
725 short *xbuf = (short*)(dsb->buffer);
726 if (dsb->wfx.nChannels == 1) {
727 for (j=0;j<sizeof(playbuf)/2;j++) {
728 dsb->playpos=(dsb->playpos+2)%buflen;
729 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
730 dsb->playing = 0;
731 dsb->playpos = buflen;
732 return;
734 /* Insomnia- volume, panning, and correcting against wrap */
735 /* Left Channel */
736 samp = xbuf[dsb->playpos>>1];
737 samp *= Lvoldec;
738 samp >>= 24;
739 samp += playbuf8[(j<<1)];
740 if(samp > 127L) samp = 127L;
741 else if(samp < -128L) samp = -128L;
742 playbuf8[(j<<1)] = (short)samp;
744 /* Right Channel */
745 samp = xbuf[dsb->playpos>>1];
746 samp *= Rvoldec;
747 samp >>= 24;
748 samp += playbuf8[(j<<1)+1];
749 if(samp > 127L) samp = 127L;
750 else if(samp < -128L) samp = -128L;
751 playbuf8[(j<<1)+1] = (short)samp;
752 /* End Insomnia's mod */
754 CHECK_EVENT
756 } else {
757 for (j=0;j<sizeof(playbuf);j++) {
758 dsb->playpos=(dsb->playpos+2)%buflen;
759 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
760 dsb->playing = 0;
761 dsb->playpos = buflen;
762 return;
764 /* Insomnia- volume, panning, and correcting against wrap */
765 samp = xbuf[dsb->playpos>>1];
767 /* Right Channel */
768 if(j&1) samp *= Rvoldec;
769 /* Left Channel */
770 else samp *= Lvoldec;
772 samp >>= 24;
773 samp += playbuf8[j];
774 if(samp > 127L) samp = 127L;
775 else if(samp < -128L) samp = -128L;
776 playbuf8[j] = (short)samp;
777 /* End Insomnia's mod */
779 CHECK_EVENT
783 } else { /* 16 bit */
784 if (dsb->wfx.wBitsPerSample == 8) {
785 /* unsigned char *xbuf = (unsigned char*)(dsb->buffer); */
786 char *xbuf = dsb->buffer;
787 if (dsb->wfx.nChannels == 1) {
788 WARN(dsound,"Mixing 8-bit stereo into 16!!\n");
789 for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0])/2;j++) {
790 dsb->playpos=(dsb->playpos+1)%buflen;
791 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
792 dsb->playing = 0;
793 dsb->playpos = buflen;
794 return;
796 /* Insomnia- volume, panning, and correcting against wrap */
797 /* Left Channel */
798 samp = xbuf[dsb->playpos>>1];
799 samp *= Lvoldec;
800 samp >>= 8;
801 samp += playbuf[(j<<1)];
802 if(samp > 32767L) samp = 32767L;
803 else if(samp < -32768L) samp = -32768L;
804 playbuf[(j<<1)] = (short)samp;
806 /* Right Channel */
807 samp = xbuf[dsb->playpos>>1];
808 samp *= Rvoldec;
809 samp >>= 8;
810 samp += playbuf[(j<<1)+1];
811 if(samp > 32767L) samp = 32767L;
812 else if(samp < -32768L) samp = -32768L;
813 playbuf[(j<<1)+1] = (short)samp;
814 /* End Insomnia's mod */
816 CHECK_EVENT
818 } else {
819 for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0]);j++) {
820 dsb->playpos=(dsb->playpos+1)%buflen;
821 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
822 dsb->playing = 0;
823 dsb->playpos = buflen;
824 return;
826 /* Insomnia- volume, panning, and correcting against wrap */
827 samp = xbuf[dsb->playpos>>1];
829 /* Right Channel */
830 if(j&1) samp *= Rvoldec;
831 /* Left Channel */
832 else samp *= Lvoldec;
834 samp >>= 8;
835 samp += playbuf[j];
836 if(samp > 32767L) samp = 32767L;
837 else if(samp < -32768L) samp = -32768L;
838 playbuf[j] = (short)samp;
839 /* End Insomnia's mod */
841 CHECK_EVENT
844 } else { /* 16 */
845 short *xbuf = (short*)(dsb->buffer);
846 if (dsb->wfx.nChannels == 1) {
847 for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0])/2;j++) {
848 dsb->playpos=(dsb->playpos+2)%buflen;
849 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
850 dsb->playing = 0;
851 dsb->playpos = buflen;
852 return;
854 /* Insomnia- volume, panning, and correcting against wrap */
855 /* Left Channel */
856 samp = xbuf[dsb->playpos>>1];
857 samp *= Lvoldec;
858 samp >>= 16;
859 samp += playbuf[(j<<1)];
860 if(samp > 32767L) samp = 32767L;
861 else if(samp < -32768L) samp = -32768L;
862 playbuf[(j<<1)] = (short)samp;
864 /* Right Channel */
865 samp = xbuf[dsb->playpos>>1];
866 samp *= Rvoldec;
867 samp >>= 16;
868 samp += playbuf[(j<<1)+1];
869 if(samp > 32767L) samp = 32767L;
870 else if(samp < -32768L) samp = -32768L;
871 playbuf[(j<<1)+1] = (short)samp;
872 /* End Insomnia's mod */
874 CHECK_EVENT
876 } else {
877 for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0]);j++) {
878 dsb->playpos=(dsb->playpos+2)%buflen;
879 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
880 dsb->playing = 0;
881 dsb->playpos = buflen;
882 return;
884 /* Insomnia- volume, panning, and correcting against wrap */
885 samp = xbuf[dsb->playpos>>1];
887 /* Right Channel */
888 if(j&1) samp *= Rvoldec;
889 /* Left Channel */
890 else samp *= Lvoldec;
892 samp >>= 16;
893 samp += playbuf[j];
894 if(samp > 32767L) samp = 32767L;
895 else if(samp < -32768L) samp = -32768L;
896 playbuf[j] = (short)samp;
897 /* End Insomnia's mod */
899 CHECK_EVENT
906 static DWORD
907 DSOUND_thread(LPVOID arg) {
908 int res,i,curleft,playing,haveprimary = 0;
910 TRACE(dsound,"dsound is at pid %d\n",getpid());
911 while (1) {
912 if (!dsound) {
913 WARN(dsound,"DSOUND thread giving up.\n");
914 ExitThread(0);
916 if (getppid()==1) {
917 WARN(dsound,"DSOUND father died? Giving up.\n");
918 ExitThread(0);
920 /* RACE: dsound could be deleted */
921 dsound->lpvtbl->fnAddRef(dsound);
922 if (!dsound->nrofbuffers) {
923 /* no soundbuffer yet... wait. */
924 Sleep(1000);
925 continue;
927 memset(playbuf,0,sizeof(playbuf));
928 playing = 0;
929 dsound->lpvtbl->fnAddRef(dsound);
930 haveprimary = 0;
931 for (i=dsound->nrofbuffers;i--;) {
932 IDirectSoundBuffer *dsb = dsound->buffers[i];
934 if (!dsb || !dsb->lpvtbl)
935 continue;
936 dsb->lpvtbl->fnAddRef(dsb);
937 if (dsb->playing && dsb->buflen)
938 playing++;
939 if (dsb->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
940 haveprimary = 1;
941 if (memcmp(&dsound->wfx,&(dsb->wfx),sizeof(dsound->wfx))) {
942 DSOUND_setformat(&(dsb->wfx));
943 memcpy(&dsound->wfx,&(dsb->wfx),sizeof(dsb->wfx));
946 dsb->lpvtbl->fnRelease(dsb);
948 /* We have just one playbuffer, so use its format */
949 if ((playing==1) && !haveprimary) {
950 for (i=dsound->nrofbuffers;i--;) {
951 IDirectSoundBuffer *dsb = dsound->buffers[i];
953 dsb->lpvtbl->fnAddRef(dsb);
954 if (dsb->playing && dsb->buflen) {
955 if (memcmp(&dsound->wfx,&(dsb->wfx),sizeof(dsound->wfx))) {
956 DSOUND_setformat(&(dsb->wfx));
957 memcpy(&dsound->wfx,&(dsb->wfx),sizeof(dsb->wfx));
960 dsb->lpvtbl->fnRelease(dsb);
963 for (i=dsound->nrofbuffers;i--;) {
964 IDirectSoundBuffer *dsb = dsound->buffers[i];
966 if (!dsb || !dsb->lpvtbl)
967 continue;
968 dsb->lpvtbl->fnAddRef(dsb);
969 if (dsb->buflen && dsb->playing) {
970 playing++;
971 DSOUND_MixInBuffer(dsb);
973 dsb->lpvtbl->fnRelease(dsb);
975 dsound->lpvtbl->fnRelease(dsound);
977 /*fputc('0'+playing,stderr);*/
978 curleft = 0;
979 while (curleft < sizeof(playbuf)) {
980 res = write(audiofd,(LPBYTE)playbuf+curleft,sizeof(playbuf)-curleft);
981 if (res==-1) {
982 if (errno==EINTR)
983 continue;
984 perror("write audiofd");
985 ExitThread(0);
986 break;
988 curleft+=res;
991 ExitThread(0);
994 #endif /* HAVE_OSS */
996 HRESULT WINAPI DirectSoundCreate(LPGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter ) {
997 int xx;
998 if (lpGUID)
999 TRACE(dsound,"(%p,%p,%p)\n",lpGUID,ppDS,pUnkOuter);
1000 #ifdef HAVE_OSS
1001 if (audiofd>=0)
1002 return DSERR_ALLOCATED;
1003 audiofd = open("/dev/audio",O_WRONLY);
1004 if (audiofd==-1) {
1005 perror("open /dev/audio");
1006 audiofd=0;
1007 return DSERR_NODRIVER;
1009 xx=0x0002000c;
1010 if (-1==ioctl(audiofd,SNDCTL_DSP_SETFRAGMENT,&xx))
1011 perror("ioctl SETFRAGMENT");
1013 TRACE(dsound,"SETFRAGMENT. count is now %d, fragsize is %d\n",
1014 (xx>>16)+1,xx&0xffff
1018 *ppDS = (LPDIRECTSOUND)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSound));
1019 (*ppDS)->ref = 1;
1020 (*ppDS)->lpvtbl = &dsvt;
1021 (*ppDS)->buffers = NULL;
1022 (*ppDS)->nrofbuffers = 0;
1024 (*ppDS)->wfx.wFormatTag = 1;
1025 (*ppDS)->wfx.nChannels = 2;
1026 (*ppDS)->wfx.nSamplesPerSec = 22050;
1027 (*ppDS)->wfx.nAvgBytesPerSec = 44100;
1028 (*ppDS)->wfx.nBlockAlign = 2;
1029 (*ppDS)->wfx.wBitsPerSample = 8;
1031 DSOUND_setformat(&((*ppDS)->wfx));
1033 if (!dsound) {
1034 HANDLE32 hnd;
1035 DWORD xid;
1037 dsound = (*ppDS);
1038 hnd = CreateThread(NULL,0,DSOUND_thread,0,0,&xid);
1040 return 0;
1041 #else
1042 MessageBox32A(0,"DirectSound needs the Open Sound System Driver, which has not been found by ./configure.","WINE DirectSound",MB_OK|MB_ICONSTOP);
1043 return DSERR_NODRIVER;
1044 #endif
1048 /*******************************************************************************
1049 * DllGetClassObject [DSOUND.4]
1050 * Retrieves class object from a DLL object
1052 * NOTES
1053 * Docs say returns STDAPI
1055 * PARAMS
1056 * rclsid [I] CLSID for the class object
1057 * riid [I] Reference to identifier of interface for class object
1058 * ppv [O] Address of variable to receive interface pointer for riid
1060 * RETURNS
1061 * Success: S_OK
1062 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
1063 * E_UNEXPECTED
1065 DWORD WINAPI DllGetClassObject( REFCLSID rclsid, REFIID riid, LPVOID *ppv )
1067 FIXME(dsound, "(%p,%p,%p): stub\n", rclsid, riid, ppv);
1068 return S_OK;
1072 /*******************************************************************************
1073 * DllCanUnloadNow [DSOUND.3] Determines whether the DLL is in use.
1075 * RETURNS
1076 * Success: S_OK
1077 * Failure: S_FALSE
1079 DWORD WINAPI DllCanUnloadNow(void)
1081 FIXME(dsound, "(void): stub\n");
1082 return S_FALSE;