Release 981025.
[wine/multimedia.git] / multimedia / dsound.c
blobbfb24a7b50eb7dde50c6ddc42dda9ca88cb09ab8
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 = 65536;
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_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 TRACE(dsound,"(%p), ref was %d\n",this,this->ref);
507 return ++(this->ref);
510 static ULONG WINAPI IDirectSound_Release(LPDIRECTSOUND this) {
511 TRACE(dsound,"(%p), ref was %d\n",this,this->ref);
512 if (!--(this->ref)) {
513 HeapFree(GetProcessHeap(),0,this);
514 dsound = NULL;
515 close(audiofd);audiofd = -1;
516 return 0;
518 return this->ref;
521 static HRESULT WINAPI IDirectSound_SetSpeakerConfig(
522 LPDIRECTSOUND this,DWORD config
524 FIXME(dsound,"(%p,0x%08lx):stub\n",this,config);
525 return 0;
528 static HRESULT WINAPI IDirectSound_QueryInterface(
529 LPDIRECTSOUND this,REFIID riid,LPVOID *ppobj
531 char xbuf[50];
533 WINE_StringFromCLSID(riid,xbuf);
534 TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
535 return E_FAIL;
538 static struct tagLPDIRECTSOUND_VTABLE dsvt = {
539 IDirectSound_QueryInterface,
540 IDirectSound_AddRef,
541 IDirectSound_Release,
542 IDirectSound_CreateSoundBuffer,
543 IDirectSound_GetCaps,
544 IDirectSound_DuplicateSoundBuffer,
545 IDirectSound_SetCooperativeLevel,
546 (void *)8,
547 (void *)9,
548 IDirectSound_SetSpeakerConfig,
549 (void *)11
552 static int
553 DSOUND_setformat(LPWAVEFORMATEX wfex) {
554 int xx,channels,speed,format,nformat;
557 switch (wfex->wFormatTag) {
558 default:
559 WARN(dsound,"unknown WAVE_FORMAT tag %d\n",wfex->wFormatTag);
560 return DSERR_BADFORMAT;
561 case WAVE_FORMAT_PCM:
562 break;
564 if (wfex->wBitsPerSample==8)
565 format = AFMT_U8;
566 else
567 format = AFMT_S16_LE;
569 if (-1==ioctl(audiofd,SNDCTL_DSP_GETFMTS,&xx)) {
570 perror("ioctl SNDCTL_DSP_GETFMTS");
571 return -1;
573 if ((xx&format)!=format) {/* format unsupported */
574 WARN(dsound,"SNDCTL_DSP_GETFMTS: format not supported\n");
575 return -1;
577 nformat = format;
578 if (-1==ioctl(audiofd,SNDCTL_DSP_SETFMT,&nformat)) {
579 perror("ioctl SNDCTL_DSP_SETFMT");
580 return -1;
582 if (nformat!=format) {/* didn't work */
583 WARN(dsound,"SNDCTL_DSP_GETFMTS: format not set\n");
584 return -1;
587 channels = wfex->nChannels-1;
588 if (-1==ioctl(audiofd,SNDCTL_DSP_STEREO,&channels)) {
589 perror("ioctl SNDCTL_DSP_STEREO");
590 return -1;
592 speed = wfex->nSamplesPerSec;
593 if (-1==ioctl(audiofd,SNDCTL_DSP_SPEED,&speed)) {
594 perror("ioctl SNDCTL_DSP_SPEED");
595 return -1;
597 TRACE(dsound,"(freq=%ld,channels=%d,bits=%d)\n",
598 wfex->nSamplesPerSec,wfex->nChannels,wfex->wBitsPerSample
600 return 0;
603 static LPDSBPOSITIONNOTIFY
604 DSOUND_nextevent(IDirectSoundBuffer *dsb) {
605 int i;
607 if (dsb->nrofnotifies) {
608 for (i=0;i<dsb->nrofnotifies;i++) {
609 if (dsb->playpos<dsb->notifies[i].dwOffset)
610 break;
612 if (i==dsb->nrofnotifies)
613 i=0;
614 return dsb->notifies+i;
616 return NULL;
619 #define CHECK_EVENT \
620 if (nextevent && (dsb->playpos == nextevent->dwOffset)) { \
621 SetEvent(nextevent->hEventNotify); \
622 TRACE(dsound,"signalled event %d\n",nextevent->hEventNotify);\
623 nextevent = DSOUND_nextevent(dsb); \
627 static void
628 DSOUND_MixInBuffer(IDirectSoundBuffer *dsb) {
629 int j,buflen = dsb->buflen;
630 LPDSBPOSITIONNOTIFY nextevent;
631 int xdiff = dsb->wfx.nSamplesPerSec-dsound->wfx.nSamplesPerSec;
633 /* Insomnia - Going along with REAL author's style */
634 long Rvoldec, Lvoldec;
635 long pan = dsb->pan;
636 long samp; /* temporary sample workspace */
639 double tmpr=dsb->volume-500;
640 double tmpl=tmpr;
641 if(pan>0) tmpl -= (double)pan;
642 else tmpr += (double)pan;
643 tmpl /= 1000.0;
644 tmpr /= 1000.0;
645 tmpl = pow(2.0, tmpl);
646 tmpr = pow(2.0, tmpr);
647 tmpl *= 65536; /* Set to the correct multiple times */
648 tmpr *= 65536; /* 65536 to be convenient for bit shifting */
649 tmpl += 0.5; /* Add .5 for rounding accuracy */
650 tmpr += 0.5;
651 Lvoldec = (long)tmpl;
652 Rvoldec = (long)tmpr;
654 /* End Insomnia's mod */
656 if (xdiff<0) xdiff=-xdiff;
657 if (xdiff>1500) {
658 WARN(dsound,"mixing in buffer of different frequency (%ld vs %ld), argh!\n",
659 dsb->wfx.nSamplesPerSec,dsound->wfx.nSamplesPerSec);
661 nextevent = DSOUND_nextevent(dsb);
662 /* TRACE(dsound,"(%d.%d.%d.%d)\n",dsound->wfx.wBitsPerSample,dsb->wfx.wBitsPerSample,dsound->wfx.nChannels,dsb->wfx.nChannels);*/
664 if (dsound->wfx.wBitsPerSample == 8) {
665 char *playbuf8 = (char*)playbuf;
667 if (dsb->wfx.wBitsPerSample == 8) {
668 unsigned char *xbuf = (unsigned char*)(dsb->buffer);
669 if (dsb->wfx.nChannels == 1) {
670 for (j=0;j<sizeof(playbuf)/2;j++) {
672 dsb->playpos=(dsb->playpos+1)%buflen;
673 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
674 dsb->playing = 0;
675 dsb->playpos = buflen;
676 return;
678 /* Insomnia- volume, panning, and correcting against wrap */
679 /* Left Channel */
680 samp = xbuf[dsb->playpos>>1];
681 samp *= Lvoldec;
682 samp >>= 16;
683 samp += playbuf8[(j<<1)];
684 if(samp > 127L) samp = 127L;
685 else if(samp < -128L) samp = -128L;
686 playbuf8[(j<<1)] = (short)samp;
688 /* Right Channel */
689 samp = xbuf[dsb->playpos>>1];
690 samp *= Rvoldec;
691 samp >>= 16;
692 samp += playbuf8[(j<<1)+1];
693 if(samp > 127L) samp = 127L;
694 else if(samp < -128L) samp = -128L;
695 playbuf8[(j<<1)+1] = (short)samp;
696 /* End Insomnia's mod */
698 CHECK_EVENT
700 } else {
701 for (j=0;j<sizeof(playbuf);j++) {
702 dsb->playpos=(dsb->playpos+1)%buflen;
703 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
704 dsb->playing = 0;
705 dsb->playpos = buflen;
706 return;
708 /* Insomnia- volume, panning, and correcting against wrap */
709 samp = xbuf[dsb->playpos>>1];
711 /* Right Channel */
712 if(j&1) samp *= Rvoldec;
713 /* Left Channel */
714 else samp *= Lvoldec;
716 samp >>= 16;
717 samp += playbuf8[j];
718 if(samp > 127L) samp = 127L;
719 else if(samp < -128L) samp = -128L;
720 playbuf8[j] = (short)samp;
721 /* End Insomnia's mod */
723 CHECK_EVENT
726 } else { /* 16 */
727 short *xbuf = (short*)(dsb->buffer);
728 if (dsb->wfx.nChannels == 1) {
729 for (j=0;j<sizeof(playbuf)/2;j++) {
730 dsb->playpos=(dsb->playpos+2)%buflen;
731 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
732 dsb->playing = 0;
733 dsb->playpos = buflen;
734 return;
736 /* Insomnia- volume, panning, and correcting against wrap */
737 /* Left Channel */
738 samp = xbuf[dsb->playpos>>1];
739 samp *= Lvoldec;
740 samp >>= 24;
741 samp += playbuf8[(j<<1)];
742 if(samp > 127L) samp = 127L;
743 else if(samp < -128L) samp = -128L;
744 playbuf8[(j<<1)] = (short)samp;
746 /* Right Channel */
747 samp = xbuf[dsb->playpos>>1];
748 samp *= Rvoldec;
749 samp >>= 24;
750 samp += playbuf8[(j<<1)+1];
751 if(samp > 127L) samp = 127L;
752 else if(samp < -128L) samp = -128L;
753 playbuf8[(j<<1)+1] = (short)samp;
754 /* End Insomnia's mod */
756 CHECK_EVENT
758 } else {
759 for (j=0;j<sizeof(playbuf);j++) {
760 dsb->playpos=(dsb->playpos+2)%buflen;
761 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
762 dsb->playing = 0;
763 dsb->playpos = buflen;
764 return;
766 /* Insomnia- volume, panning, and correcting against wrap */
767 samp = xbuf[dsb->playpos>>1];
769 /* Right Channel */
770 if(j&1) samp *= Rvoldec;
771 /* Left Channel */
772 else samp *= Lvoldec;
774 samp >>= 24;
775 samp += playbuf8[j];
776 if(samp > 127L) samp = 127L;
777 else if(samp < -128L) samp = -128L;
778 playbuf8[j] = (short)samp;
779 /* End Insomnia's mod */
781 CHECK_EVENT
785 } else { /* 16 bit */
786 if (dsb->wfx.wBitsPerSample == 8) {
787 /* unsigned char *xbuf = (unsigned char*)(dsb->buffer); */
788 char *xbuf = dsb->buffer;
789 if (dsb->wfx.nChannels == 1) {
790 WARN(dsound,"Mixing 8-bit stereo into 16!!\n");
791 for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0])/2;j++) {
792 dsb->playpos=(dsb->playpos+1)%buflen;
793 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
794 dsb->playing = 0;
795 dsb->playpos = buflen;
796 return;
798 /* Insomnia- volume, panning, and correcting against wrap */
799 /* Left Channel */
800 samp = xbuf[dsb->playpos>>1];
801 samp *= Lvoldec;
802 samp >>= 8;
803 samp += playbuf[(j<<1)];
804 if(samp > 32767L) samp = 32767L;
805 else if(samp < -32768L) samp = -32768L;
806 playbuf[(j<<1)] = (short)samp;
808 /* Right Channel */
809 samp = xbuf[dsb->playpos>>1];
810 samp *= Rvoldec;
811 samp >>= 8;
812 samp += playbuf[(j<<1)+1];
813 if(samp > 32767L) samp = 32767L;
814 else if(samp < -32768L) samp = -32768L;
815 playbuf[(j<<1)+1] = (short)samp;
816 /* End Insomnia's mod */
818 CHECK_EVENT
820 } else {
821 for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0]);j++) {
822 dsb->playpos=(dsb->playpos+1)%buflen;
823 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
824 dsb->playing = 0;
825 dsb->playpos = buflen;
826 return;
828 /* Insomnia- volume, panning, and correcting against wrap */
829 samp = xbuf[dsb->playpos>>1];
831 /* Right Channel */
832 if(j&1) samp *= Rvoldec;
833 /* Left Channel */
834 else samp *= Lvoldec;
836 samp >>= 8;
837 samp += playbuf[j];
838 if(samp > 32767L) samp = 32767L;
839 else if(samp < -32768L) samp = -32768L;
840 playbuf[j] = (short)samp;
841 /* End Insomnia's mod */
843 CHECK_EVENT
846 } else { /* 16 */
847 short *xbuf = (short*)(dsb->buffer);
848 if (dsb->wfx.nChannels == 1) {
849 for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0])/2;j++) {
850 dsb->playpos=(dsb->playpos+2)%buflen;
851 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
852 dsb->playing = 0;
853 dsb->playpos = buflen;
854 return;
856 /* Insomnia- volume, panning, and correcting against wrap */
857 /* Left Channel */
858 samp = xbuf[dsb->playpos>>1];
859 samp *= Lvoldec;
860 samp >>= 16;
861 samp += playbuf[(j<<1)];
862 if(samp > 32767L) samp = 32767L;
863 else if(samp < -32768L) samp = -32768L;
864 playbuf[(j<<1)] = (short)samp;
866 /* Right Channel */
867 samp = xbuf[dsb->playpos>>1];
868 samp *= Rvoldec;
869 samp >>= 16;
870 samp += playbuf[(j<<1)+1];
871 if(samp > 32767L) samp = 32767L;
872 else if(samp < -32768L) samp = -32768L;
873 playbuf[(j<<1)+1] = (short)samp;
874 /* End Insomnia's mod */
876 CHECK_EVENT
878 } else {
879 for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0]);j++) {
880 dsb->playpos=(dsb->playpos+2)%buflen;
881 if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) {
882 dsb->playing = 0;
883 dsb->playpos = buflen;
884 return;
886 /* Insomnia- volume, panning, and correcting against wrap */
887 samp = xbuf[dsb->playpos>>1];
889 /* Right Channel */
890 if(j&1) samp *= Rvoldec;
891 /* Left Channel */
892 else samp *= Lvoldec;
894 samp >>= 16;
895 samp += playbuf[j];
896 if(samp > 32767L) samp = 32767L;
897 else if(samp < -32768L) samp = -32768L;
898 playbuf[j] = (short)samp;
899 /* End Insomnia's mod */
901 CHECK_EVENT
908 static DWORD WINAPI
909 DSOUND_thread(LPVOID arg) {
910 int res,i,curleft,playing,haveprimary = 0;
912 TRACE(dsound,"dsound is at pid %d\n",getpid());
913 while (1) {
914 if (!dsound) {
915 WARN(dsound,"DSOUND thread giving up.\n");
916 ExitThread(0);
918 if (getppid()==1) {
919 WARN(dsound,"DSOUND father died? Giving up.\n");
920 ExitThread(0);
922 /* RACE: dsound could be deleted */
923 dsound->lpvtbl->fnAddRef(dsound);
924 if (!dsound->nrofbuffers) {
925 /* no soundbuffer yet... wait. */
926 Sleep(1000);
927 dsound->lpvtbl->fnRelease(dsound);
928 continue;
930 memset(playbuf,0,sizeof(playbuf));
931 playing = 0;
932 haveprimary = 0;
933 for (i=dsound->nrofbuffers;i--;) {
934 IDirectSoundBuffer *dsb = dsound->buffers[i];
936 if (!dsb || !dsb->lpvtbl)
937 continue;
938 dsb->lpvtbl->fnAddRef(dsb);
939 if (dsb->playing && dsb->buflen)
940 playing++;
941 if (dsb->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
942 haveprimary = 1;
943 if (memcmp(&dsound->wfx,&(dsb->wfx),sizeof(dsound->wfx))) {
944 DSOUND_setformat(&(dsb->wfx));
945 memcpy(&dsound->wfx,&(dsb->wfx),sizeof(dsb->wfx));
948 dsb->lpvtbl->fnRelease(dsb);
950 /* We have just one playbuffer, so use its format */
951 if ((playing==1) && !haveprimary) {
952 for (i=dsound->nrofbuffers;i--;) {
953 IDirectSoundBuffer *dsb = dsound->buffers[i];
955 dsb->lpvtbl->fnAddRef(dsb);
956 if (dsb->playing && dsb->buflen) {
957 if (memcmp(&dsound->wfx,&(dsb->wfx),sizeof(dsound->wfx))) {
958 DSOUND_setformat(&(dsb->wfx));
959 memcpy(&dsound->wfx,&(dsb->wfx),sizeof(dsb->wfx));
962 dsb->lpvtbl->fnRelease(dsb);
965 for (i=dsound->nrofbuffers;i--;) {
966 IDirectSoundBuffer *dsb = dsound->buffers[i];
968 if (!dsb || !dsb->lpvtbl)
969 continue;
970 dsb->lpvtbl->fnAddRef(dsb);
971 if (dsb->buflen && dsb->playing) {
972 playing++;
973 DSOUND_MixInBuffer(dsb);
975 dsb->lpvtbl->fnRelease(dsb);
977 dsound->lpvtbl->fnRelease(dsound);
979 /*fputc('0'+playing,stderr);*/
980 curleft = 0;
981 while (curleft < sizeof(playbuf)) {
982 res = write(audiofd,(LPBYTE)playbuf+curleft,sizeof(playbuf)-curleft);
983 if (res==-1) {
984 if (errno==EINTR)
985 continue;
986 perror("write audiofd");
987 ExitThread(0);
988 break;
990 curleft+=res;
993 ExitThread(0);
996 #endif /* HAVE_OSS */
998 HRESULT WINAPI DirectSoundCreate(LPGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter ) {
999 int xx;
1000 if (lpGUID)
1001 TRACE(dsound,"(%p,%p,%p)\n",lpGUID,ppDS,pUnkOuter);
1002 #ifdef HAVE_OSS
1003 if (audiofd>=0)
1004 return DSERR_ALLOCATED;
1005 audiofd = open("/dev/audio",O_WRONLY);
1006 if (audiofd==-1) {
1007 perror("open /dev/audio");
1008 audiofd=0;
1009 return DSERR_NODRIVER;
1011 xx=0x0002000c;
1012 if (-1==ioctl(audiofd,SNDCTL_DSP_SETFRAGMENT,&xx))
1013 perror("ioctl SETFRAGMENT");
1015 TRACE(dsound,"SETFRAGMENT. count is now %d, fragsize is %d\n",
1016 (xx>>16)+1,xx&0xffff
1020 *ppDS = (LPDIRECTSOUND)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSound));
1021 (*ppDS)->ref = 1;
1022 (*ppDS)->lpvtbl = &dsvt;
1023 (*ppDS)->buffers = NULL;
1024 (*ppDS)->nrofbuffers = 0;
1026 (*ppDS)->wfx.wFormatTag = 1;
1027 (*ppDS)->wfx.nChannels = 2;
1028 (*ppDS)->wfx.nSamplesPerSec = 22050;
1029 (*ppDS)->wfx.nAvgBytesPerSec = 44100;
1030 (*ppDS)->wfx.nBlockAlign = 2;
1031 (*ppDS)->wfx.wBitsPerSample = 8;
1033 DSOUND_setformat(&((*ppDS)->wfx));
1035 if (!dsound) {
1036 HANDLE32 hnd;
1037 DWORD xid;
1039 dsound = (*ppDS);
1040 hnd = CreateThread(NULL,0,DSOUND_thread,0,0,&xid);
1042 return 0;
1043 #else
1044 MessageBox32A(0,"DirectSound needs the Open Sound System Driver, which has not been found by ./configure.","WINE DirectSound",MB_OK|MB_ICONSTOP);
1045 return DSERR_NODRIVER;
1046 #endif
1050 /*******************************************************************************
1051 * DllGetClassObject [DSOUND.4]
1052 * Retrieves class object from a DLL object
1054 * NOTES
1055 * Docs say returns STDAPI
1057 * PARAMS
1058 * rclsid [I] CLSID for the class object
1059 * riid [I] Reference to identifier of interface for class object
1060 * ppv [O] Address of variable to receive interface pointer for riid
1062 * RETURNS
1063 * Success: S_OK
1064 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
1065 * E_UNEXPECTED
1067 DWORD WINAPI DllGetClassObject( REFCLSID rclsid, REFIID riid, LPVOID *ppv )
1069 FIXME(dsound, "(%p,%p,%p): stub\n", rclsid, riid, ppv);
1070 return S_OK;
1074 /*******************************************************************************
1075 * DllCanUnloadNow [DSOUND.3] Determines whether the DLL is in use.
1077 * RETURNS
1078 * Success: S_OK
1079 * Failure: S_FALSE
1081 DWORD WINAPI DllCanUnloadNow(void)
1083 FIXME(dsound, "(void): stub\n");
1084 return S_FALSE;