Desktop window queue handling adapted.
[wine/multimedia.git] / multimedia / dsound.c
blob36245fd0803a7a024e1f0256f4eb69d5f0af63c4
1 /* DirectSound
2 *
3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998 Rob Riggs
5 */
6 /*
7 * Note: This file requires multithread ability. It is not possible to
8 * implement the stuff in a single thread anyway. And most DirectX apps
9 * require threading themselves.
11 * FIXME: This file is full of race conditions and unlocked variable access
12 * from two threads. But we usually don't need to bother.
14 * Tested with a Soundblaster clone and a Gravis UltraSound Classic.
16 * Status:
17 * - Wing Commander 4/W95:
18 * The intromovie plays without problems. Nearly lipsynchron.
19 * - DiscWorld 2
20 * The sound works, but noticeable chunks are left out (from the sound and
21 * the animation). Don't know why yet.
22 * - Diablo:
23 * Sound works, but slows down the movieplayer.
24 * - XvT:
25 * Doesn't sound yet.
26 * - Monkey Island 3:
27 * The background sound of the startscreen works ;)
28 * - WingCommander Prophecy Demo:
29 * Sound works for the intromovie.
32 #include "config.h"
33 #include <assert.h>
34 #include <errno.h>
35 #include <sys/types.h>
36 #include <sys/time.h>
37 #include <sys/fcntl.h>
38 #include <unistd.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <math.h> /* Insomnia - pow() function */
42 #include "windows.h"
43 #include "winerror.h"
44 #include "interfaces.h"
45 #include "multimedia.h"
46 #include "dsound.h"
47 #include "thread.h"
48 #include "debug.h"
49 #include "xmalloc.h"
51 #ifdef HAVE_OSS
53 #include <sys/ioctl.h>
55 /* #define USE_DSOUND3D 1 */
57 #define DSOUND_FRAGLEN (primarybuf->wfx.nAvgBytesPerSec >> 4)
58 #define DSOUND_FREQSHIFT (14)
60 static int audiofd = -1;
61 static int audioOK = 0;
63 static LPDIRECTSOUND dsound = NULL;
65 static LPDIRECTSOUNDBUFFER primarybuf = NULL;
67 static int DSOUND_setformat(LPWAVEFORMATEX wfex);
68 static void DSOUND_CheckEvent(IDirectSoundBuffer *dsb, int len);
69 static void DSOUND_CloseAudio(void);
71 #endif
73 HRESULT WINAPI DirectSoundEnumerate32A(LPDSENUMCALLBACK32A enumcb,LPVOID context) {
74 #ifdef HAVE_OSS
75 enumcb(NULL,"WINE DirectSound using Open Sound System","sound",context);
76 #endif
77 return 0;
80 #ifdef HAVE_OSS
81 static void _dump_DSBCAPS(DWORD xmask) {
82 struct {
83 DWORD mask;
84 char *name;
85 } flags[] = {
86 #define FE(x) { x, #x },
87 FE(DSBCAPS_PRIMARYBUFFER)
88 FE(DSBCAPS_STATIC)
89 FE(DSBCAPS_LOCHARDWARE)
90 FE(DSBCAPS_LOCSOFTWARE)
91 FE(DSBCAPS_CTRLFREQUENCY)
92 FE(DSBCAPS_CTRLPAN)
93 FE(DSBCAPS_CTRLVOLUME)
94 FE(DSBCAPS_CTRLDEFAULT)
95 FE(DSBCAPS_CTRLALL)
96 FE(DSBCAPS_STICKYFOCUS)
97 FE(DSBCAPS_GETCURRENTPOSITION2)
99 int i;
101 for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
102 if (flags[i].mask & xmask)
103 fprintf(stderr,"%s ",flags[i].name);
106 /*******************************************************************************
107 * IDirectSound3DBuffer
110 // IUnknown methods
111 static HRESULT WINAPI IDirectSound3DBuffer_QueryInterface(
112 LPDIRECTSOUND3DBUFFER this, REFIID riid, LPVOID *ppobj)
114 char xbuf[50];
116 WINE_StringFromCLSID(riid,xbuf);
117 TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
118 return E_FAIL;
121 static ULONG WINAPI IDirectSound3DBuffer_AddRef(LPDIRECTSOUND3DBUFFER this)
123 this->ref++;
124 return this->ref;
127 static ULONG WINAPI IDirectSound3DBuffer_Release(LPDIRECTSOUND3DBUFFER this)
129 if(--this->ref)
130 return this->ref;
132 HeapFree(GetProcessHeap(),0,this->buffer);
133 HeapFree(GetProcessHeap(),0,this);
135 return 0;
138 // IDirectSound3DBuffer methods
139 static HRESULT WINAPI IDirectSound3DBuffer_GetAllParameters(
140 LPDIRECTSOUND3DBUFFER this,
141 LPDS3DBUFFER lpDs3dBuffer)
143 FIXME(dsound,"stub\n");
144 return DS_OK;
147 static HRESULT WINAPI IDirectSound3DBuffer_GetConeAngles(
148 LPDIRECTSOUND3DBUFFER this,
149 LPDWORD lpdwInsideConeAngle,
150 LPDWORD lpdwOutsideConeAngle)
152 FIXME(dsound,"stub\n");
153 return DS_OK;
156 static HRESULT WINAPI IDirectSound3DBuffer_GetConeOrientation(
157 LPDIRECTSOUND3DBUFFER this,
158 LPD3DVECTOR lpvConeOrientation)
160 FIXME(dsound,"stub\n");
161 return DS_OK;
164 static HRESULT WINAPI IDirectSound3DBuffer_GetConeOutsideVolume(
165 LPDIRECTSOUND3DBUFFER this,
166 LPLONG lplConeOutsideVolume)
168 FIXME(dsound,"stub\n");
169 return DS_OK;
172 static HRESULT WINAPI IDirectSound3DBuffer_GetMaxDistance(
173 LPDIRECTSOUND3DBUFFER this,
174 LPD3DVALUE lpfMaxDistance)
176 FIXME(dsound,"stub\n");
177 return DS_OK;
180 static HRESULT WINAPI IDirectSound3DBuffer_GetMinDistance(
181 LPDIRECTSOUND3DBUFFER this,
182 LPD3DVALUE lpfMinDistance)
184 FIXME(dsound,"stub\n");
185 return DS_OK;
188 static HRESULT WINAPI IDirectSound3DBuffer_GetMode(
189 LPDIRECTSOUND3DBUFFER this,
190 LPDWORD lpdwMode)
192 FIXME(dsound,"stub\n");
193 return DS_OK;
196 static HRESULT WINAPI IDirectSound3DBuffer_GetPosition(
197 LPDIRECTSOUND3DBUFFER this,
198 LPD3DVECTOR lpvPosition)
200 FIXME(dsound,"stub\n");
201 return DS_OK;
204 static HRESULT WINAPI IDirectSound3DBuffer_GetVelocity(
205 LPDIRECTSOUND3DBUFFER this,
206 LPD3DVECTOR lpvVelocity)
208 FIXME(dsound,"stub\n");
209 return DS_OK;
212 static HRESULT WINAPI IDirectSound3DBuffer_SetAllParameters(
213 LPDIRECTSOUND3DBUFFER this,
214 LPCDS3DBUFFER lpcDs3dBuffer,
215 DWORD dwApply)
217 FIXME(dsound,"stub\n");
218 return DS_OK;
221 static HRESULT WINAPI IDirectSound3DBuffer_SetConeAngles(
222 LPDIRECTSOUND3DBUFFER this,
223 DWORD dwInsideConeAngle,
224 DWORD dwOutsideConeAngle,
225 DWORD dwApply)
227 FIXME(dsound,"stub\n");
228 return DS_OK;
231 static HRESULT WINAPI IDirectSound3DBuffer_SetConeOrientation(
232 LPDIRECTSOUND3DBUFFER this,
233 D3DVALUE x, D3DVALUE y, D3DVALUE z,
234 DWORD dwApply)
236 FIXME(dsound,"stub\n");
237 return DS_OK;
240 static HRESULT WINAPI IDirectSound3DBuffer_SetConeOutsideVolume(
241 LPDIRECTSOUND3DBUFFER this,
242 LONG lConeOutsideVolume,
243 DWORD dwApply)
245 FIXME(dsound,"stub\n");
246 return DS_OK;
249 static HRESULT WINAPI IDirectSound3DBuffer_SetMaxDistance(
250 LPDIRECTSOUND3DBUFFER this,
251 D3DVALUE fMaxDistance,
252 DWORD dwApply)
254 FIXME(dsound,"stub\n");
255 return DS_OK;
258 static HRESULT WINAPI IDirectSound3DBuffer_SetMinDistance(
259 LPDIRECTSOUND3DBUFFER this,
260 D3DVALUE fMinDistance,
261 DWORD dwApply)
263 FIXME(dsound,"stub\n");
264 return DS_OK;
267 static HRESULT WINAPI IDirectSound3DBuffer_SetMode(
268 LPDIRECTSOUND3DBUFFER this,
269 DWORD dwMode,
270 DWORD dwApply)
272 TRACE(dsound, "mode = %lx\n", dwMode);
273 this->ds3db.dwMode = dwMode;
274 return DS_OK;
277 static HRESULT WINAPI IDirectSound3DBuffer_SetPosition(
278 LPDIRECTSOUND3DBUFFER this,
279 D3DVALUE x, D3DVALUE y, D3DVALUE z,
280 DWORD dwApply)
282 FIXME(dsound,"stub\n");
283 return DS_OK;
286 static HRESULT WINAPI IDirectSound3DBuffer_SetVelocity(
287 LPDIRECTSOUND3DBUFFER this,
288 D3DVALUE x, D3DVALUE y, D3DVALUE z,
289 DWORD dwApply)
291 FIXME(dsound,"stub\n");
292 return DS_OK;
295 IDirectSound3DBuffer_VTable ds3dbvt = {
296 // IUnknown methods
297 IDirectSound3DBuffer_QueryInterface,
298 IDirectSound3DBuffer_AddRef,
299 IDirectSound3DBuffer_Release,
300 // IDirectSound3DBuffer methods
301 IDirectSound3DBuffer_GetAllParameters,
302 IDirectSound3DBuffer_GetConeAngles,
303 IDirectSound3DBuffer_GetConeOrientation,
304 IDirectSound3DBuffer_GetConeOutsideVolume,
305 IDirectSound3DBuffer_GetMaxDistance,
306 IDirectSound3DBuffer_GetMinDistance,
307 IDirectSound3DBuffer_GetMode,
308 IDirectSound3DBuffer_GetPosition,
309 IDirectSound3DBuffer_GetVelocity,
310 IDirectSound3DBuffer_SetAllParameters,
311 IDirectSound3DBuffer_SetConeAngles,
312 IDirectSound3DBuffer_SetConeOrientation,
313 IDirectSound3DBuffer_SetConeOutsideVolume,
314 IDirectSound3DBuffer_SetMaxDistance,
315 IDirectSound3DBuffer_SetMinDistance,
316 IDirectSound3DBuffer_SetMode,
317 IDirectSound3DBuffer_SetPosition,
318 IDirectSound3DBuffer_SetVelocity,
321 #ifdef USE_DSOUND3D
322 static int DSOUND_Create3DBuffer(LPDIRECTSOUNDBUFFER dsb)
324 DWORD i, temp, iSize, oSize, offset;
325 LPBYTE bIbuf, bObuf, bTbuf = NULL;
326 LPWORD wIbuf, wObuf, wTbuf = NULL;
328 // Inside DirectX says it's stupid but allowed
329 if (dsb->wfx.nChannels == 2) {
330 // Convert to mono
331 if (dsb->wfx.wBitsPerSample == 16) {
332 iSize = dsb->buflen / 4;
333 wTbuf = malloc(dsb->buflen / 2);
334 if (wTbuf == NULL)
335 return DSERR_OUTOFMEMORY;
336 for (i = 0; i < iSize; i++)
337 wTbuf[i] = (dsb->buffer[i] + dsb->buffer[(i * 2) + 1]) / 2;
338 wIbuf = wTbuf;
339 } else {
340 iSize = dsb->buflen / 2;
341 bTbuf = malloc(dsb->buflen / 2);
342 if (bTbuf == NULL)
343 return DSERR_OUTOFMEMORY;
344 for (i = 0; i < iSize; i++)
345 bTbuf[i] = (dsb->buffer[i] + dsb->buffer[(i * 2) + 1]) / 2;
346 bIbuf = bTbuf;
348 } else {
349 if (dsb->wfx.wBitsPerSample == 16) {
350 iSize = dsb->buflen / 2;
351 wIbuf = (LPWORD) dsb->buffer;
352 } else {
353 bIbuf = (LPBYTE) dsb->buffer;
354 iSize = dsb->buflen;
358 if (primarybuf->wfx.wBitsPerSample == 16) {
359 wObuf = (LPWORD) dsb->ds3db->buffer;
360 oSize = dsb->ds3db->buflen / 2;
361 } else {
362 bObuf = (LPBYTE) dsb->ds3db->buffer;
363 oSize = dsb->ds3db->buflen;
366 offset = primarybuf->wfx.nSamplesPerSec / 100; // 10ms
367 if (primarybuf->wfx.wBitsPerSample == 16 && dsb->wfx.wBitsPerSample == 16)
368 for (i = 0; i < iSize; i++) {
369 temp = wIbuf[i];
370 if (i >= offset)
371 temp += wIbuf[i - offset] >> 9;
372 else
373 temp += wIbuf[i + iSize - offset] >> 9;
374 wObuf[i * 2] = temp;
375 wObuf[(i * 2) + 1] = temp;
377 else if (primarybuf->wfx.wBitsPerSample == 8 && dsb->wfx.wBitsPerSample == 8)
378 for (i = 0; i < iSize; i++) {
379 temp = bIbuf[i];
380 if (i >= offset)
381 temp += bIbuf[i - offset] >> 5;
382 else
383 temp += bIbuf[i + iSize - offset] >> 5;
384 bObuf[i * 2] = temp;
385 bObuf[(i * 2) + 1] = temp;
388 if (wTbuf)
389 free(wTbuf);
390 if (bTbuf)
391 free(bTbuf);
393 return DS_OK;
395 #endif
396 /*******************************************************************************
397 * IDirectSound3DListener
400 // IUnknown methods
401 static HRESULT WINAPI IDirectSound3DListener_QueryInterface(
402 LPDIRECTSOUND3DLISTENER this, REFIID riid, LPVOID *ppobj)
404 char xbuf[50];
406 WINE_StringFromCLSID(riid,xbuf);
407 TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
408 return E_FAIL;
411 static ULONG WINAPI IDirectSound3DListener_AddRef(LPDIRECTSOUND3DLISTENER this)
413 this->ref++;
414 return this->ref;
417 static ULONG WINAPI IDirectSound3DListener_Release(LPDIRECTSOUND3DLISTENER this)
419 this->ref--;
420 return this->ref;
423 // IDirectSound3DListener methods
424 static HRESULT WINAPI IDirectSound3DListener_GetAllParameter(
425 LPDIRECTSOUND3DLISTENER this,
426 LPDS3DLISTENER lpDS3DL)
428 FIXME(dsound,"stub\n");
429 return DS_OK;
432 static HRESULT WINAPI IDirectSound3DListener_GetDistanceFactor(
433 LPDIRECTSOUND3DLISTENER this,
434 LPD3DVALUE lpfDistanceFactor)
436 FIXME(dsound,"stub\n");
437 return DS_OK;
440 static HRESULT WINAPI IDirectSound3DListener_GetDopplerFactor(
441 LPDIRECTSOUND3DLISTENER this,
442 LPD3DVALUE lpfDopplerFactor)
444 FIXME(dsound,"stub\n");
445 return DS_OK;
448 static HRESULT WINAPI IDirectSound3DListener_GetOrientation(
449 LPDIRECTSOUND3DLISTENER this,
450 LPD3DVECTOR lpvOrientFront,
451 LPD3DVECTOR lpvOrientTop)
453 FIXME(dsound,"stub\n");
454 return DS_OK;
457 static HRESULT WINAPI IDirectSound3DListener_GetPosition(
458 LPDIRECTSOUND3DLISTENER this,
459 LPD3DVECTOR lpvPosition)
461 FIXME(dsound,"stub\n");
462 return DS_OK;
465 static HRESULT WINAPI IDirectSound3DListener_GetRolloffFactor(
466 LPDIRECTSOUND3DLISTENER this,
467 LPD3DVALUE lpfRolloffFactor)
469 FIXME(dsound,"stub\n");
470 return DS_OK;
473 static HRESULT WINAPI IDirectSound3DListener_GetVelocity(
474 LPDIRECTSOUND3DLISTENER this,
475 LPD3DVECTOR lpvVelocity)
477 FIXME(dsound,"stub\n");
478 return DS_OK;
481 static HRESULT WINAPI IDirectSound3DListener_SetAllParameters(
482 LPDIRECTSOUND3DLISTENER this,
483 LPCDS3DLISTENER lpcDS3DL,
484 DWORD dwApply)
486 FIXME(dsound,"stub\n");
487 return DS_OK;
490 static HRESULT WINAPI IDirectSound3DListener_SetDistanceFactor(
491 LPDIRECTSOUND3DLISTENER this,
492 D3DVALUE fDistanceFactor,
493 DWORD dwApply)
495 FIXME(dsound,"stub\n");
496 return DS_OK;
499 static HRESULT WINAPI IDirectSound3DListener_SetDopplerFactor(
500 LPDIRECTSOUND3DLISTENER this,
501 D3DVALUE fDopplerFactor,
502 DWORD dwApply)
504 FIXME(dsound,"stub\n");
505 return DS_OK;
508 static HRESULT WINAPI IDirectSound3DListener_SetOrientation(
509 LPDIRECTSOUND3DLISTENER this,
510 D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront,
511 D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop,
512 DWORD dwApply)
514 FIXME(dsound,"stub\n");
515 return DS_OK;
518 static HRESULT WINAPI IDirectSound3DListener_SetPosition(
519 LPDIRECTSOUND3DLISTENER this,
520 D3DVALUE x, D3DVALUE y, D3DVALUE z,
521 DWORD dwApply)
523 FIXME(dsound,"stub\n");
524 return DS_OK;
527 static HRESULT WINAPI IDirectSound3DListener_SetRolloffFactor(
528 LPDIRECTSOUND3DLISTENER this,
529 D3DVALUE fRolloffFactor,
530 DWORD dwApply)
532 FIXME(dsound,"stub\n");
533 return DS_OK;
536 static HRESULT WINAPI IDirectSound3DListener_SetVelocity(
537 LPDIRECTSOUND3DLISTENER this,
538 D3DVALUE x, D3DVALUE y, D3DVALUE z,
539 DWORD dwApply)
541 FIXME(dsound,"stub\n");
542 return DS_OK;
545 static HRESULT WINAPI IDirectSound3DListener_CommitDeferredSettings(
546 LPDIRECTSOUND3DLISTENER this)
549 FIXME(dsound,"stub\n");
550 return DS_OK;
553 IDirectSound3DListener_VTable ds3dlvt = {
554 // IUnknown methods
555 IDirectSound3DListener_QueryInterface,
556 IDirectSound3DListener_AddRef,
557 IDirectSound3DListener_Release,
558 // IDirectSound3DListener methods
559 IDirectSound3DListener_GetAllParameter,
560 IDirectSound3DListener_GetDistanceFactor,
561 IDirectSound3DListener_GetDopplerFactor,
562 IDirectSound3DListener_GetOrientation,
563 IDirectSound3DListener_GetPosition,
564 IDirectSound3DListener_GetRolloffFactor,
565 IDirectSound3DListener_GetVelocity,
566 IDirectSound3DListener_SetAllParameters,
567 IDirectSound3DListener_SetDistanceFactor,
568 IDirectSound3DListener_SetDopplerFactor,
569 IDirectSound3DListener_SetOrientation,
570 IDirectSound3DListener_SetPosition,
571 IDirectSound3DListener_SetRolloffFactor,
572 IDirectSound3DListener_SetVelocity,
573 IDirectSound3DListener_CommitDeferredSettings,
576 /*******************************************************************************
577 * IDirectSoundNotify
579 static HRESULT WINAPI IDirectSoundNotify_QueryInterface(
580 LPDIRECTSOUNDNOTIFY this,REFIID riid,LPVOID *ppobj
582 char xbuf[50];
584 WINE_StringFromCLSID(riid,xbuf);
585 TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
586 return E_FAIL;
589 static ULONG WINAPI IDirectSoundNotify_AddRef(LPDIRECTSOUNDNOTIFY this) {
590 return ++(this->ref);
593 static ULONG WINAPI IDirectSoundNotify_Release(LPDIRECTSOUNDNOTIFY this) {
594 this->ref--;
595 if (!this->ref) {
596 this->dsb->lpvtbl->fnRelease(this->dsb);
597 HeapFree(GetProcessHeap(),0,this);
598 return 0;
600 return this->ref;
603 static HRESULT WINAPI IDirectSoundNotify_SetNotificationPositions(
604 LPDIRECTSOUNDNOTIFY this,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
606 int i;
608 if (TRACE_ON(dsound)) {
609 TRACE(dsound,"(%p,0x%08lx,%p)\n",this,howmuch,notify);
610 for (i=0;i<howmuch;i++)
611 TRACE(dsound,"notify at %ld to 0x%08lx\n",
612 notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
614 this->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,this->dsb->notifies,(this->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
615 memcpy( this->dsb->notifies+this->dsb->nrofnotifies,
616 notify,
617 howmuch*sizeof(DSBPOSITIONNOTIFY)
619 this->dsb->nrofnotifies+=howmuch;
621 return 0;
624 IDirectSoundNotify_VTable dsnvt = {
625 IDirectSoundNotify_QueryInterface,
626 IDirectSoundNotify_AddRef,
627 IDirectSoundNotify_Release,
628 IDirectSoundNotify_SetNotificationPositions,
631 /*******************************************************************************
632 * IDirectSoundBuffer
635 // This sets this format for the <em>Primary Buffer Only</em>
636 // See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120
637 static HRESULT WINAPI IDirectSoundBuffer_SetFormat(
638 LPDIRECTSOUNDBUFFER this,LPWAVEFORMATEX wfex
640 LPDIRECTSOUNDBUFFER *dsb;
641 int i;
643 // Let's be pedantic!
644 if ((wfex == NULL) ||
645 (wfex->wFormatTag != WAVE_FORMAT_PCM) ||
646 (wfex->nChannels < 1) || (wfex->nChannels > 2) ||
647 (wfex->nSamplesPerSec < 1) ||
648 (wfex->nBlockAlign < 1) || (wfex->nChannels > 4) ||
649 ((wfex->wBitsPerSample != 8) && (wfex->wBitsPerSample != 16))) {
650 TRACE(dsound, "failed pedantic check!\n");
651 return DSERR_INVALIDPARAM;
654 // ****
655 EnterCriticalSection(&(primarybuf->lock));
657 if (primarybuf->wfx.nSamplesPerSec != wfex->nSamplesPerSec) {
658 dsb = dsound->buffers;
659 for (i = 0; i < dsound->nrofbuffers; i++, dsb++) {
660 // ****
661 EnterCriticalSection(&((*dsb)->lock));
663 (*dsb)->freqAdjust = ((*dsb)->freq << DSOUND_FREQSHIFT) /
664 wfex->nSamplesPerSec;
666 LeaveCriticalSection(&((*dsb)->lock));
667 // ****
671 memcpy(&(primarybuf->wfx), wfex, sizeof(primarybuf->wfx));
673 TRACE(dsound,"(formattag=0x%04x,chans=%d,samplerate=%ld"
674 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
675 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
676 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
677 wfex->wBitsPerSample, wfex->cbSize);
679 primarybuf->wfx.nAvgBytesPerSec =
680 this->wfx.nSamplesPerSec * this->wfx.nBlockAlign;
682 DSOUND_CloseAudio();
684 LeaveCriticalSection(&(primarybuf->lock));
685 // ****
687 return DS_OK;
690 static HRESULT WINAPI IDirectSoundBuffer_SetVolume(
691 LPDIRECTSOUNDBUFFER this,LONG vol
693 double temp;
695 TRACE(dsound,"(%p,%ld)\n",this,vol);
697 // I'm not sure if we need this for primary buffer
698 if (!(this->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
699 return DSERR_CONTROLUNAVAIL;
701 if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN))
702 return DSERR_INVALIDPARAM;
704 // This needs to adjust the soundcard volume when
705 // called for the primary buffer
706 if (this->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
707 FIXME(dsound, "Volume control of primary unimplemented.\n");
708 this->volume = vol;
709 return DS_OK;
712 // ****
713 EnterCriticalSection(&(this->lock));
715 this->volume = vol;
717 temp = (double) (this->volume - (this->pan > 0 ? this->pan : 0));
718 this->lVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 32768.0);
719 temp = (double) (this->volume + (this->pan < 0 ? this->pan : 0));
720 this->rVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 32768.0);
722 LeaveCriticalSection(&(this->lock));
723 // ****
725 TRACE(dsound, "left = %lx, right = %lx\n", this->lVolAdjust, this->rVolAdjust);
727 return DS_OK;
730 static HRESULT WINAPI IDirectSoundBuffer_GetVolume(
731 LPDIRECTSOUNDBUFFER this,LPLONG vol
733 TRACE(dsound,"(%p,%p)\n",this,vol);
735 if (vol == NULL)
736 return DSERR_INVALIDPARAM;
738 *vol = this->volume;
739 return DS_OK;
742 static HRESULT WINAPI IDirectSoundBuffer_SetFrequency(
743 LPDIRECTSOUNDBUFFER this,DWORD freq
745 TRACE(dsound,"(%p,%ld)\n",this,freq);
747 // You cannot set the frequency of the primary buffer
748 if (!(this->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY) ||
749 (this->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
750 return DSERR_CONTROLUNAVAIL;
752 if ((freq < DSBFREQUENCY_MIN) || (freq > DSBFREQUENCY_MAX))
753 return DSERR_INVALIDPARAM;
755 // ****
756 EnterCriticalSection(&(this->lock));
758 this->freq = freq;
759 this->freqAdjust = (freq << DSOUND_FREQSHIFT) / primarybuf->wfx.nSamplesPerSec;
760 this->nAvgBytesPerSec = freq * this->wfx.nBlockAlign;
762 LeaveCriticalSection(&(this->lock));
763 // ****
765 return DS_OK;
768 static HRESULT WINAPI IDirectSoundBuffer_Play(
769 LPDIRECTSOUNDBUFFER this,DWORD reserved1,DWORD reserved2,DWORD flags
771 TRACE(dsound,"(%p,%08lx,%08lx,%08lx)\n",
772 this,reserved1,reserved2,flags
774 this->playflags = flags;
775 this->playing = 1;
776 return DS_OK;
779 static HRESULT WINAPI IDirectSoundBuffer_Stop(LPDIRECTSOUNDBUFFER this)
781 TRACE(dsound,"(%p)\n",this);
783 // ****
784 EnterCriticalSection(&(this->lock));
786 this->playing = 0;
787 DSOUND_CheckEvent(this, 0);
789 LeaveCriticalSection(&(this->lock));
790 // ****
792 return DS_OK;
795 static DWORD WINAPI IDirectSoundBuffer_AddRef(LPDIRECTSOUNDBUFFER this) {
796 // TRACE(dsound,"(%p) ref was %ld\n",this, this->ref);
798 return ++(this->ref);
800 static DWORD WINAPI IDirectSoundBuffer_Release(LPDIRECTSOUNDBUFFER this) {
801 int i;
803 // TRACE(dsound,"(%p) ref was %ld\n",this, this->ref);
805 if (--this->ref)
806 return this->ref;
808 for (i=0;i<this->dsound->nrofbuffers;i++)
809 if (this->dsound->buffers[i] == this)
810 break;
811 if (i < this->dsound->nrofbuffers) {
812 /* Put the last buffer of the list in the (now empty) position */
813 this->dsound->buffers[i] = this->dsound->buffers[this->dsound->nrofbuffers - 1];
814 this->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,this->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*this->dsound->nrofbuffers);
815 this->dsound->nrofbuffers--;
816 this->dsound->lpvtbl->fnRelease(this->dsound);
819 DeleteCriticalSection(&(this->lock));
821 if (this->ds3db && this->ds3db->lpvtbl)
822 this->ds3db->lpvtbl->fnRelease(this->ds3db);
824 HeapFree(GetProcessHeap(),0,this->buffer);
825 HeapFree(GetProcessHeap(),0,this);
827 if (this == primarybuf)
828 primarybuf = NULL;
830 return DS_OK;
833 static HRESULT WINAPI IDirectSoundBuffer_GetCurrentPosition(
834 LPDIRECTSOUNDBUFFER this,LPDWORD playpos,LPDWORD writepos
836 TRACE(dsound,"(%p,%p,%p)\n",this,playpos,writepos);
837 if (playpos) *playpos = this->playpos;
838 if (writepos) *writepos = this->writepos;
839 return DS_OK;
842 static HRESULT WINAPI IDirectSoundBuffer_GetStatus(
843 LPDIRECTSOUNDBUFFER this,LPDWORD status
845 TRACE(dsound,"(%p,%p)\n",this,status);
847 if (status == NULL)
848 return DSERR_INVALIDPARAM;
850 *status = 0;
851 if (this->playing)
852 *status |= DSBSTATUS_PLAYING;
853 if (this->playflags & DSBPLAY_LOOPING)
854 *status |= DSBSTATUS_LOOPING;
856 return DS_OK;
860 static HRESULT WINAPI IDirectSoundBuffer_GetFormat(
861 LPDIRECTSOUNDBUFFER this,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
863 TRACE(dsound,"(%p,%p,%ld,%p)\n",this,lpwf,wfsize,wfwritten);
865 if (wfsize>sizeof(this->wfx))
866 wfsize = sizeof(this->wfx);
867 if (lpwf) { // NULL is valid
868 memcpy(lpwf,&(this->wfx),wfsize);
869 if (wfwritten)
870 *wfwritten = wfsize;
871 } else
872 if (wfwritten)
873 *wfwritten = sizeof(this->wfx);
874 else
875 return DSERR_INVALIDPARAM;
877 return DS_OK;
880 static HRESULT WINAPI IDirectSoundBuffer_Lock(
881 LPDIRECTSOUNDBUFFER this,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
884 TRACE(dsound,"(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx)\n",
885 this,
886 writecursor,
887 writebytes,
888 lplpaudioptr1,
889 audiobytes1,
890 lplpaudioptr2,
891 audiobytes2,
892 flags
894 if (flags & DSBLOCK_FROMWRITECURSOR)
895 writecursor += this->writepos;
896 if (flags & DSBLOCK_ENTIREBUFFER)
897 writebytes = this->buflen;
898 if (writebytes > this->buflen)
899 writebytes = this->buflen;
901 assert(audiobytes1!=audiobytes2);
902 assert(lplpaudioptr1!=lplpaudioptr2);
903 if (writecursor+writebytes <= this->buflen) {
904 *(LPBYTE*)lplpaudioptr1 = this->buffer+writecursor;
905 *audiobytes1 = writebytes;
906 if (lplpaudioptr2)
907 *(LPBYTE*)lplpaudioptr2 = NULL;
908 if (audiobytes2)
909 *audiobytes2 = 0;
910 TRACE(dsound,"->%ld.0\n",writebytes);
911 } else {
912 *(LPBYTE*)lplpaudioptr1 = this->buffer+writecursor;
913 *audiobytes1 = this->buflen-writecursor;
914 if (lplpaudioptr2)
915 *(LPBYTE*)lplpaudioptr2 = this->buffer;
916 if (audiobytes2)
917 *audiobytes2 = writebytes-(this->buflen-writecursor);
918 TRACE(dsound,"->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
920 // No. See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 21
921 // this->writepos=(writecursor+writebytes)%this->buflen;
922 return DS_OK;
925 static HRESULT WINAPI IDirectSoundBuffer_SetCurrentPosition(
926 LPDIRECTSOUNDBUFFER this,DWORD newpos
928 TRACE(dsound,"(%p,%ld)\n",this,newpos);
930 // ****
931 EnterCriticalSection(&(this->lock));
933 this->playpos = newpos;
935 LeaveCriticalSection(&(this->lock));
936 // ****
938 return 0;
941 static HRESULT WINAPI IDirectSoundBuffer_SetPan(
942 LPDIRECTSOUNDBUFFER this,LONG pan
944 double temp;
946 TRACE(dsound,"(%p,%ld)\n",this,pan);
948 if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT))
949 return DSERR_INVALIDPARAM;
951 // You cannot set the pan of the primary buffer
952 // and you cannot use both pan and 3D controls
953 if (!(this->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
954 (this->dsbd.dwFlags & DSBCAPS_CTRL3D) ||
955 (this->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
956 return DSERR_CONTROLUNAVAIL;
958 // ****
959 EnterCriticalSection(&(this->lock));
961 this->pan = pan;
963 temp = (double) (this->volume - (this->pan > 0 ? this->pan : 0));
964 this->lVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 32768.0);
965 temp = (double) (this->volume + (this->pan < 0 ? this->pan : 0));
966 this->rVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 32768.0);
968 LeaveCriticalSection(&(this->lock));
969 // ****
971 return DS_OK;
974 static HRESULT WINAPI IDirectSoundBuffer_GetPan(
975 LPDIRECTSOUNDBUFFER this,LPLONG pan
977 TRACE(dsound,"(%p,%p)\n",this,pan);
979 if (pan == NULL)
980 return DSERR_INVALIDPARAM;
982 *pan = this->pan;
984 return DS_OK;
987 static HRESULT WINAPI IDirectSoundBuffer_Unlock(
988 LPDIRECTSOUNDBUFFER this,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
990 TRACE(dsound,"(%p,%p,%ld,%p,%ld):stub\n", this,p1,x1,p2,x2);
992 // There is really nothing to do here. Should someone
993 // choose to implement static buffers in hardware (by
994 // using a wave table synth, for example) this is where
995 // you'd want to do the loading. For software buffers,
996 // which is what we currently use, we need do nothing.
998 #if 0
999 // It's also the place to pre-process 3D buffers...
1001 // This is highly experimental and liable to break things
1002 if (this->dsbd.dwFlags & DSBCAPS_CTRL3D)
1003 DSOUND_Create3DBuffer(this);
1004 #endif
1006 return DS_OK;
1009 static HRESULT WINAPI IDirectSoundBuffer_GetFrequency(
1010 LPDIRECTSOUNDBUFFER this,LPDWORD freq
1012 TRACE(dsound,"(%p,%p)\n",this,freq);
1014 if (freq == NULL)
1015 return DSERR_INVALIDPARAM;
1017 *freq = this->freq;
1019 return DS_OK;
1022 static HRESULT WINAPI IDirectSoundBuffer_Initialize(
1023 LPDIRECTSOUNDBUFFER this,LPDIRECTSOUND dsound,LPDSBUFFERDESC dbsd
1025 FIXME(dsound,"(%p,%p,%p):stub\n",this,dsound,dbsd);
1026 printf("Re-Init!!!\n");
1027 return DSERR_ALREADYINITIALIZED;
1030 static HRESULT WINAPI IDirectSoundBuffer_GetCaps(
1031 LPDIRECTSOUNDBUFFER this,LPDSBCAPS caps
1033 TRACE(dsound,"(%p)->(%p)\n",this,caps);
1035 if (caps == NULL)
1036 return DSERR_INVALIDPARAM;
1038 // I think we should check this value, not set it. See
1039 // Inside DirectX, p215. That should apply here, too.
1040 caps->dwSize = sizeof(*caps);
1042 caps->dwFlags = this->dsbd.dwFlags | DSBCAPS_LOCSOFTWARE;
1043 caps->dwBufferBytes = this->dsbd.dwBufferBytes;
1044 /* This value represents the speed of the "unlock" command.
1045 As unlock is quite fast (it does not do anything), I put
1046 4096 ko/s = 4 Mo / s */
1047 caps->dwUnlockTransferRate = 4096;
1048 caps->dwPlayCpuOverhead = 0;
1050 return DS_OK;
1053 static HRESULT WINAPI IDirectSoundBuffer_QueryInterface(
1054 LPDIRECTSOUNDBUFFER this,REFIID riid,LPVOID *ppobj
1056 char xbuf[50];
1058 WINE_StringFromCLSID(riid,xbuf);
1059 TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
1061 if (!memcmp(&IID_IDirectSoundNotify,riid,sizeof(*riid))) {
1062 IDirectSoundNotify *dsn;
1064 dsn = (LPDIRECTSOUNDNOTIFY)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn));
1065 dsn->ref = 1;
1066 dsn->dsb = this;
1067 this->lpvtbl->fnAddRef(this);
1068 dsn->lpvtbl = &dsnvt;
1069 *ppobj = (LPVOID)dsn;
1070 return 0;
1073 if (!memcmp(&IID_IDirectSound3DBuffer,riid,sizeof(*riid))) {
1074 *ppobj = this->ds3db;
1075 if (*ppobj)
1076 return DS_OK;
1079 return E_FAIL;
1082 static struct tagLPDIRECTSOUNDBUFFER_VTABLE dsbvt = {
1083 IDirectSoundBuffer_QueryInterface,
1084 IDirectSoundBuffer_AddRef,
1085 IDirectSoundBuffer_Release,
1086 IDirectSoundBuffer_GetCaps,
1087 IDirectSoundBuffer_GetCurrentPosition,
1088 IDirectSoundBuffer_GetFormat,
1089 IDirectSoundBuffer_GetVolume,
1090 IDirectSoundBuffer_GetPan,
1091 IDirectSoundBuffer_GetFrequency,
1092 IDirectSoundBuffer_GetStatus,
1093 IDirectSoundBuffer_Initialize,
1094 IDirectSoundBuffer_Lock,
1095 IDirectSoundBuffer_Play,
1096 IDirectSoundBuffer_SetCurrentPosition,
1097 IDirectSoundBuffer_SetFormat,
1098 IDirectSoundBuffer_SetVolume,
1099 IDirectSoundBuffer_SetPan,
1100 IDirectSoundBuffer_SetFrequency,
1101 IDirectSoundBuffer_Stop,
1102 IDirectSoundBuffer_Unlock
1105 /*******************************************************************************
1106 * IDirectSound
1109 static HRESULT WINAPI IDirectSound_SetCooperativeLevel(
1110 LPDIRECTSOUND this,HWND32 hwnd,DWORD level
1112 FIXME(dsound,"(%p,%08lx,%ld):stub\n",this,(DWORD)hwnd,level);
1113 return 0;
1116 static HRESULT WINAPI IDirectSound_CreateSoundBuffer(
1117 LPDIRECTSOUND this,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER ppdsb,LPUNKNOWN lpunk
1119 LPWAVEFORMATEX wfex;
1121 TRACE(dsound,"(%p,%p,%p,%p)\n",this,dsbd,ppdsb,lpunk);
1123 if ((this == NULL) || (dsbd == NULL) || (ppdsb == NULL))
1124 return DSERR_INVALIDPARAM;
1126 if (TRACE_ON(dsound)) {
1127 TRACE(dsound,"(size=%ld)\n",dsbd->dwSize);
1128 TRACE(dsound,"(flags=0x%08lx\n",dsbd->dwFlags);
1129 _dump_DSBCAPS(dsbd->dwFlags);
1130 TRACE(dsound,"(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
1131 TRACE(dsound,"(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
1134 wfex = dsbd->lpwfxFormat;
1136 if (wfex)
1137 TRACE(dsound,"(formattag=0x%04x,chans=%d,samplerate=%ld"
1138 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1139 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
1140 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
1141 wfex->wBitsPerSample, wfex->cbSize);
1143 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
1144 if (primarybuf) {
1145 primarybuf->lpvtbl->fnAddRef(primarybuf);
1146 *ppdsb = primarybuf;
1147 return DS_OK;
1148 } // Else create primarybuf
1151 *ppdsb = (LPDIRECTSOUNDBUFFER)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBuffer));
1152 if (*ppdsb == NULL)
1153 return DSERR_OUTOFMEMORY;
1154 (*ppdsb)->ref = 1;
1156 TRACE(dsound, "Created buffer at %p\n", *ppdsb);
1158 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
1159 (*ppdsb)->buflen = dsound->wfx.nAvgBytesPerSec;
1160 (*ppdsb)->freq = dsound->wfx.nSamplesPerSec;
1161 } else {
1162 (*ppdsb)->buflen = dsbd->dwBufferBytes;
1163 (*ppdsb)->freq = dsbd->lpwfxFormat->nSamplesPerSec;
1165 (*ppdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,(*ppdsb)->buflen);
1166 if ((*ppdsb)->buffer == NULL) {
1167 HeapFree(GetProcessHeap(),0,(*ppdsb));
1168 *ppdsb = NULL;
1169 return DSERR_OUTOFMEMORY;
1171 // It's not necessary to initialize values to zero since
1172 // we allocated this structure with HEAP_ZERO_MEMORY...
1173 (*ppdsb)->playpos = 0;
1174 (*ppdsb)->writepos = 0;
1175 (*ppdsb)->lpvtbl = &dsbvt;
1176 (*ppdsb)->dsound = this;
1177 (*ppdsb)->playing = 0;
1178 (*ppdsb)->lVolAdjust = (1 << 15);
1179 (*ppdsb)->rVolAdjust = (1 << 15);
1181 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
1182 (*ppdsb)->freqAdjust = ((*ppdsb)->freq << DSOUND_FREQSHIFT) /
1183 primarybuf->wfx.nSamplesPerSec;
1184 (*ppdsb)->nAvgBytesPerSec = (*ppdsb)->freq *
1185 dsbd->lpwfxFormat->nBlockAlign;
1188 memcpy(&((*ppdsb)->dsbd),dsbd,sizeof(*dsbd));
1190 /* register buffer */
1191 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
1192 this->buffers = (LPDIRECTSOUNDBUFFER*)HeapReAlloc(GetProcessHeap(),0,this->buffers,sizeof(LPDIRECTSOUNDBUFFER)*(this->nrofbuffers+1));
1193 this->buffers[this->nrofbuffers] = *ppdsb;
1194 this->nrofbuffers++;
1196 this->lpvtbl->fnAddRef(this);
1198 if (dsbd->lpwfxFormat)
1199 memcpy(&((*ppdsb)->wfx), dsbd->lpwfxFormat, sizeof((*ppdsb)->wfx));
1201 InitializeCriticalSection(&((*ppdsb)->lock));
1203 #if 0
1204 if (dsbd->dwFlags & DSBCAPS_CTRL3D) {
1205 IDirectSound3DBuffer *ds3db;
1207 ds3db = (LPDIRECTSOUND3DBUFFER)HeapAlloc(GetProcessHeap(),
1208 0,sizeof(*ds3db));
1209 ds3db->ref = 1;
1210 ds3db->dsb = (*ppdsb);
1211 ds3db->lpvtbl = &ds3dbvt;
1212 (*ppdsb)->ds3db = ds3db;
1213 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
1214 ds3db->ds3db.vPosition.x = 0.0;
1215 ds3db->ds3db.vPosition.y = 0.0;
1216 ds3db->ds3db.vPosition.z = 0.0;
1217 ds3db->ds3db.vVelocity.x = 0.0;
1218 ds3db->ds3db.vVelocity.y = 0.0;
1219 ds3db->ds3db.vVelocity.z = 0.0;
1220 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
1221 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
1222 ds3db->ds3db.vConeOrientation.x = 0.0;
1223 ds3db->ds3db.vConeOrientation.y = 0.0;
1224 ds3db->ds3db.vConeOrientation.z = 0.0;
1225 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME;
1226 ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
1227 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
1228 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
1229 ds3db->buflen = ((*ppdsb)->buflen * primarybuf->wfx.nBlockAlign) /
1230 (*ppdsb)->wfx.nBlockAlign;
1231 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
1232 if (ds3db->buffer == NULL) {
1233 ds3db->buflen = 0;
1234 ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
1237 #endif
1238 return DS_OK;
1241 static HRESULT WINAPI IDirectSound_DuplicateSoundBuffer(
1242 LPDIRECTSOUND this,LPDIRECTSOUNDBUFFER pdsb,LPLPDIRECTSOUNDBUFFER ppdsb
1244 TRACE(dsound,"(%p,%p,%p)\n",this,pdsb,ppdsb);
1246 *ppdsb = (LPDIRECTSOUNDBUFFER)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBuffer));
1248 (*ppdsb)->ref =1;
1249 (*ppdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,pdsb->buflen);
1250 memcpy((*ppdsb)->buffer,pdsb->buffer,pdsb->buflen);
1251 (*ppdsb)->buflen = pdsb->buflen;
1252 (*ppdsb)->playpos = 0;
1253 (*ppdsb)->writepos = 0;
1254 (*ppdsb)->lpvtbl = &dsbvt;
1255 (*ppdsb)->dsound = this;
1256 memcpy(&((*ppdsb)->wfx), &(pdsb->wfx), sizeof((*ppdsb)->wfx));
1257 /* register buffer */
1258 this->buffers = (LPDIRECTSOUNDBUFFER*)HeapReAlloc(GetProcessHeap(),0,this->buffers,sizeof(LPDIRECTSOUNDBUFFER)*(this->nrofbuffers+1));
1259 this->buffers[this->nrofbuffers] = *ppdsb;
1260 this->nrofbuffers++;
1261 this->lpvtbl->fnAddRef(this);
1262 return 0;
1266 static HRESULT WINAPI IDirectSound_GetCaps(LPDIRECTSOUND this,LPDSCAPS caps) {
1267 TRACE(dsound,"(%p,%p)\n",this,caps);
1268 TRACE(dsound,"(flags=0x%08lx)\n",caps->dwFlags);
1270 if (caps == NULL)
1271 return DSERR_INVALIDPARAM;
1273 // We should check this value, not set it. See Inside DirectX, p215.
1274 caps->dwSize = sizeof(*caps);
1276 caps->dwFlags =
1277 DSCAPS_PRIMARYSTEREO |
1278 DSCAPS_PRIMARY16BIT |
1279 DSCAPS_SECONDARYSTEREO |
1280 DSCAPS_SECONDARY16BIT |
1281 DSCAPS_CONTINUOUSRATE;
1282 /* FIXME: query OSS */
1283 caps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
1284 caps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
1286 caps->dwPrimaryBuffers = 1;
1288 caps->dwMaxHwMixingAllBuffers = 0;
1289 caps->dwMaxHwMixingStaticBuffers = 0;
1290 caps->dwMaxHwMixingStreamingBuffers = 0;
1292 caps->dwFreeHwMixingAllBuffers = 0;
1293 caps->dwFreeHwMixingStaticBuffers = 0;
1294 caps->dwFreeHwMixingStreamingBuffers = 0;
1296 caps->dwMaxHw3DAllBuffers = 0;
1297 caps->dwMaxHw3DStaticBuffers = 0;
1298 caps->dwMaxHw3DStreamingBuffers = 0;
1300 caps->dwFreeHw3DAllBuffers = 0;
1301 caps->dwFreeHw3DStaticBuffers = 0;
1302 caps->dwFreeHw3DStreamingBuffers = 0;
1304 caps->dwTotalHwMemBytes = 0;
1306 caps->dwFreeHwMemBytes = 0;
1308 caps->dwMaxContigFreeHwMemBytes = 0;
1310 caps->dwUnlockTransferRateHwBuffers = 4096; // But we have none...
1312 caps->dwPlayCpuOverheadSwBuffers = 1; // 1%
1314 return 0;
1317 static ULONG WINAPI IDirectSound_AddRef(LPDIRECTSOUND this) {
1318 return ++(this->ref);
1321 static ULONG WINAPI IDirectSound_Release(LPDIRECTSOUND this) {
1322 TRACE(dsound,"(%p), ref was %ld\n",this,this->ref);
1323 if (!--(this->ref)) {
1324 DSOUND_CloseAudio();
1325 while(IDirectSoundBuffer_Release(primarybuf)); // Deallocate
1326 FIXME(dsound, "need to release all buffers!\n");
1327 HeapFree(GetProcessHeap(),0,this);
1328 dsound = NULL;
1329 return 0;
1331 return this->ref;
1334 static HRESULT WINAPI IDirectSound_SetSpeakerConfig(
1335 LPDIRECTSOUND this,DWORD config
1337 FIXME(dsound,"(%p,0x%08lx):stub\n",this,config);
1338 return 0;
1341 static HRESULT WINAPI IDirectSound_QueryInterface(
1342 LPDIRECTSOUND this,REFIID riid,LPVOID *ppobj
1344 char xbuf[50];
1346 if (!memcmp(&IID_IDirectSound3DListener,riid,sizeof(*riid))) {
1348 if (this->listener) {
1349 *ppobj = this->listener;
1350 return DS_OK;
1352 this->listener = (LPDIRECTSOUND3DLISTENER)HeapAlloc(
1353 GetProcessHeap(), 0, sizeof(*(this->listener)));
1354 this->listener->ref = 1;
1355 this->listener->lpvtbl = &ds3dlvt;
1356 this->lpvtbl->fnAddRef(this);
1357 this->listener->ds3dl.dwSize = sizeof(DS3DLISTENER);
1358 this->listener->ds3dl.vPosition.x = 0.0;
1359 this->listener->ds3dl.vPosition.y = 0.0;
1360 this->listener->ds3dl.vPosition.z = 0.0;
1361 this->listener->ds3dl.vVelocity.x = 0.0;
1362 this->listener->ds3dl.vVelocity.y = 0.0;
1363 this->listener->ds3dl.vVelocity.z = 0.0;
1364 this->listener->ds3dl.vOrientFront.x = 0.0;
1365 this->listener->ds3dl.vOrientFront.y = 0.0;
1366 this->listener->ds3dl.vOrientFront.z = 1.0;
1367 this->listener->ds3dl.vOrientTop.x = 0.0;
1368 this->listener->ds3dl.vOrientTop.y = 1.0;
1369 this->listener->ds3dl.vOrientTop.z = 0.0;
1370 this->listener->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
1371 this->listener->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
1372 this->listener->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
1373 *ppobj = (LPVOID)this->listener;
1374 return DS_OK;
1377 WINE_StringFromCLSID(riid,xbuf);
1378 TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
1379 return E_FAIL;
1382 static HRESULT WINAPI IDirectSound_Compact(
1383 LPDIRECTSOUND this)
1385 TRACE(dsound, "(%p)\n", this);
1386 return DS_OK;
1389 static HRESULT WINAPI IDirectSound_GetSpeakerConfig(
1390 LPDIRECTSOUND this,
1391 LPDWORD lpdwSpeakerConfig)
1393 TRACE(dsound, "(%p, %p)\n", this, lpdwSpeakerConfig);
1394 *lpdwSpeakerConfig = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16);
1395 return DS_OK;
1398 static HRESULT WINAPI IDirectSound_Initialize(
1399 LPDIRECTSOUND this,
1400 LPGUID lpGuid)
1402 TRACE(dsound, "(%p, %p)\n", this, lpGuid);
1403 return DS_OK;
1406 static struct tagLPDIRECTSOUND_VTABLE dsvt = {
1407 IDirectSound_QueryInterface,
1408 IDirectSound_AddRef,
1409 IDirectSound_Release,
1410 IDirectSound_CreateSoundBuffer,
1411 IDirectSound_GetCaps,
1412 IDirectSound_DuplicateSoundBuffer,
1413 IDirectSound_SetCooperativeLevel,
1414 IDirectSound_Compact,
1415 IDirectSound_GetSpeakerConfig,
1416 IDirectSound_SetSpeakerConfig,
1417 IDirectSound_Initialize
1420 static int
1421 DSOUND_setformat(LPWAVEFORMATEX wfex) {
1422 int xx,channels,speed,format,nformat;
1424 if (!audioOK) {
1425 TRACE(dsound, "(%p) deferred\n", wfex);
1426 return 0;
1428 switch (wfex->wFormatTag) {
1429 default:
1430 WARN(dsound,"unknown WAVE_FORMAT tag %d\n",wfex->wFormatTag);
1431 return DSERR_BADFORMAT;
1432 case WAVE_FORMAT_PCM:
1433 break;
1435 if (wfex->wBitsPerSample==8)
1436 format = AFMT_U8;
1437 else
1438 format = AFMT_S16_LE;
1440 if (-1==ioctl(audiofd,SNDCTL_DSP_GETFMTS,&xx)) {
1441 perror("ioctl SNDCTL_DSP_GETFMTS");
1442 return -1;
1444 if ((xx&format)!=format) {/* format unsupported */
1445 FIXME(dsound,"SNDCTL_DSP_GETFMTS: format not supported\n");
1446 return -1;
1448 nformat = format;
1449 if (-1==ioctl(audiofd,SNDCTL_DSP_SETFMT,&nformat)) {
1450 perror("ioctl SNDCTL_DSP_SETFMT");
1451 return -1;
1453 if (nformat!=format) {/* didn't work */
1454 FIXME(dsound,"SNDCTL_DSP_GETFMTS: format not set\n");
1455 return -1;
1458 channels = wfex->nChannels-1;
1459 if (-1==ioctl(audiofd,SNDCTL_DSP_STEREO,&channels)) {
1460 perror("ioctl SNDCTL_DSP_STEREO");
1461 return -1;
1463 speed = wfex->nSamplesPerSec;
1464 if (-1==ioctl(audiofd,SNDCTL_DSP_SPEED,&speed)) {
1465 perror("ioctl SNDCTL_DSP_SPEED");
1466 return -1;
1468 TRACE(dsound,"(freq=%ld,channels=%d,bits=%d)\n",
1469 wfex->nSamplesPerSec,wfex->nChannels,wfex->wBitsPerSample
1471 return 0;
1474 static void DSOUND_CheckEvent(IDirectSoundBuffer *dsb, int len)
1476 int i;
1477 DWORD offset;
1478 LPDSBPOSITIONNOTIFY event;
1480 if (dsb->nrofnotifies == 0)
1481 return;
1483 TRACE(dsound,"(%p) buflen = %ld, playpos = %ld, len = %d\n",
1484 dsb, dsb->buflen, dsb->playpos, len);
1485 for (i = 0; i < dsb->nrofnotifies ; i++) {
1486 event = dsb->notifies + i;
1487 offset = event->dwOffset;
1488 TRACE(dsound, "checking %d, position %ld, event = %d\n",
1489 i, offset, event->hEventNotify);
1490 // DSBPN_OFFSETSTOP has to be the last element. So this is
1491 // OK. [Inside DirectX, p274]
1493 // This also means we can't sort the entries by offset,
1494 // because DSBPN_OFFSETSTOP == -1
1495 if (offset == DSBPN_OFFSETSTOP) {
1496 if (dsb->playing == 0) {
1497 SetEvent(event->hEventNotify);
1498 TRACE(dsound,"signalled event %d (%d)\n", event->hEventNotify, i);
1499 return;
1500 } else
1501 return;
1503 if ((dsb->playpos + len) >= dsb->buflen) {
1504 if ((offset < ((dsb->playpos + len) % dsb->buflen)) ||
1505 (offset >= dsb->playpos)) {
1506 TRACE(dsound,"signalled event %d (%d)\n", event->hEventNotify, i);
1507 SetEvent(event->hEventNotify);
1509 } else {
1510 if ((offset >= dsb->playpos) && (offset < (dsb->playpos + len))) {
1511 TRACE(dsound,"signalled event %d (%d)\n", event->hEventNotify, i);
1512 SetEvent(event->hEventNotify);
1518 // WAV format info can be found at:
1520 // http://www.cwi.nl/ftp/audio/AudioFormats.part2
1521 // ftp://ftp.cwi.nl/pub/audio/RIFF-format
1523 // Import points to remember:
1525 // 8-bit WAV is unsigned
1526 // 16-bit WAV is signed
1528 static inline INT16 cvtU8toS16(BYTE byte)
1530 INT16 s = (byte - 128) << 8;
1532 return s;
1535 static inline BYTE cvtS16toU8(INT16 word)
1537 BYTE b = (word + 32768) >> 8;
1539 return b;
1543 // We should be able to optimize these two inline functions
1544 // so that we aren't doing 8->16->8 conversions when it is
1545 // not necessary. But this is still a WIP. Optimize later.
1546 static inline void get_fields(const IDirectSoundBuffer *dsb, BYTE *buf, INT32 *fl, INT32 *fr)
1548 INT16 *bufs = (INT16 *) buf;
1550 // TRACE(dsound, "(%p)", buf);
1551 if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 2) {
1552 *fl = cvtU8toS16(*buf);
1553 *fr = cvtU8toS16(*(buf + 1));
1554 return;
1557 if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 2) {
1558 *fl = *bufs;
1559 *fr = *(bufs + 1);
1560 return;
1563 if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 1) {
1564 *fl = cvtU8toS16(*buf);
1565 *fr = *fl;
1566 return;
1569 if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 1) {
1570 *fl = *bufs;
1571 *fr = *bufs;
1572 return;
1575 FIXME(dsound, "get_fields found an unsupported configuration\n");
1576 return;
1579 static inline void set_fields(BYTE *buf, INT32 fl, INT32 fr)
1581 INT16 *bufs = (INT16 *) buf;
1583 if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 2)) {
1584 *buf = cvtS16toU8(fl);
1585 *(buf + 1) = cvtS16toU8(fr);
1586 return;
1589 if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 2)) {
1590 *bufs = fl;
1591 *(bufs + 1) = fr;
1592 return;
1595 if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 1)) {
1596 *buf = cvtS16toU8((fl + fr) >> 1);
1597 return;
1600 if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 1)) {
1601 *bufs = (fl + fr) >> 1;
1602 return;
1604 FIXME(dsound, "set_fields found an unsupported configuration\n");
1605 return;
1608 // Now with PerfectPitch (tm) technology
1609 static INT32 DSOUND_MixerNorm(IDirectSoundBuffer *dsb, BYTE *buf, INT32 len)
1611 INT32 i, size, ipos, ilen, fieldL, fieldR;
1612 BYTE *ibp, *obp;
1613 INT32 iAdvance = dsb->wfx.nBlockAlign;
1614 INT32 oAdvance = primarybuf->wfx.nBlockAlign;
1616 ibp = dsb->buffer + dsb->playpos;
1617 obp = buf;
1619 TRACE(dsound, "(%p, %p, %p), playpos=%8.8lx\n", dsb, ibp, obp, dsb->playpos);
1620 // Check for the best case
1621 if ((dsb->freq == primarybuf->wfx.nSamplesPerSec) &&
1622 (dsb->wfx.wBitsPerSample == primarybuf->wfx.wBitsPerSample) &&
1623 (dsb->wfx.nChannels == primarybuf->wfx.nChannels)) {
1624 TRACE(dsound, "(%p) Best case\n", dsb);
1625 if ((ibp + len) < (BYTE *)(dsb->buffer + dsb->buflen))
1626 memcpy(obp, ibp, len);
1627 else { // wrap
1628 memcpy(obp, ibp, dsb->buflen - dsb->playpos);
1629 memcpy(obp + (dsb->buflen - dsb->playpos),
1630 dsb->buffer,
1631 len - (dsb->buflen - dsb->playpos));
1633 return len;
1636 // Check for same sample rate
1637 if (dsb->freq == primarybuf->wfx.nSamplesPerSec) {
1638 TRACE(dsound, "(%p) Same sample rate %ld = primary %ld\n", dsb,
1639 dsb->freq, primarybuf->wfx.nSamplesPerSec);
1640 ilen = 0;
1641 for (i = 0; i < len; i += oAdvance) {
1642 get_fields(dsb, ibp, &fieldL, &fieldR);
1643 ibp += iAdvance;
1644 ilen += iAdvance;
1645 set_fields(obp, fieldL, fieldR);
1646 obp += oAdvance;
1647 if (ibp >= (BYTE *)(dsb->buffer + dsb->buflen))
1648 ibp = dsb->buffer; // wrap
1650 return (ilen);
1653 // Mix in different sample rates
1655 // New PerfectPitch(tm) Technology (c) 1998 Rob Riggs
1656 // Patent Pending :-]
1658 TRACE(dsound, "(%p) Adjusting frequency: %ld -> %ld\n",
1659 dsb, dsb->freq, primarybuf->wfx.nSamplesPerSec);
1661 size = len / oAdvance;
1662 ilen = ((size * dsb->freqAdjust) >> DSOUND_FREQSHIFT) * iAdvance;
1663 for (i = 0; i < size; i++) {
1665 ipos = (((i * dsb->freqAdjust) >> DSOUND_FREQSHIFT) * iAdvance) + dsb->playpos;
1667 if (ipos >= dsb->buflen)
1668 ipos %= dsb->buflen; // wrap
1670 get_fields(dsb, (dsb->buffer + ipos), &fieldL, &fieldR);
1671 set_fields(obp, fieldL, fieldR);
1672 obp += oAdvance;
1674 return ilen;
1677 static void DSOUND_MixerVol(IDirectSoundBuffer *dsb, BYTE *buf, INT32 len)
1679 INT32 i, inc = primarybuf->wfx.wBitsPerSample >> 3;
1680 BYTE *bpc = buf;
1681 INT16 *bps = (INT16 *) buf;
1683 TRACE(dsound, "(%p) left = %lx, right = %lx\n", dsb,
1684 dsb->lVolAdjust, dsb->rVolAdjust);
1685 if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->pan == 0)) &&
1686 (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->volume == 0)) &&
1687 !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
1688 return; // Nothing to do
1690 // If we end up with some bozo coder using panning or 3D sound
1691 // with a mono primary buffer, it could sound very weird using
1692 // this method. Oh well, tough patooties.
1694 for (i = 0; i < len; i += inc) {
1695 INT32 val;
1697 switch (inc) {
1699 case 1:
1700 // 8-bit WAV is unsigned, but we need to operate
1701 // on signed data for this to work properly
1702 val = *bpc - 128;
1703 val = ((val * (i & inc ? dsb->rVolAdjust : dsb->lVolAdjust)) >> 15);
1704 *bpc = val + 128;
1705 bpc++;
1706 break;
1707 case 2:
1708 // 16-bit WAV is signed -- much better
1709 val = *bps;
1710 val = ((val * ((i & inc) ? dsb->rVolAdjust : dsb->lVolAdjust)) >> 15);
1711 *bps = val;
1712 bps++;
1713 break;
1714 default:
1715 // Very ugly!
1716 FIXME(dsound, "MixerVol had a nasty error\n");
1721 #ifdef USE_DSOUND3D
1722 static void DSOUND_Mixer3D(IDirectSoundBuffer *dsb, BYTE *buf, INT32 len)
1724 BYTE *ibp, *obp;
1725 DWORD buflen, playpos;
1727 buflen = dsb->ds3db->buflen;
1728 playpos = (dsb->playpos * primarybuf->wfx.nBlockAlign) / dsb->wfx.nBlockAlign;
1729 ibp = dsb->ds3db->buffer + playpos;
1730 obp = buf;
1732 if (playpos > buflen) {
1733 FIXME(dsound, "Major breakage");
1734 return;
1737 if (len <= (playpos + buflen))
1738 memcpy(obp, ibp, len);
1739 else { // wrap
1740 memcpy(obp, ibp, buflen - playpos);
1741 memcpy(obp + (buflen - playpos),
1742 dsb->buffer,
1743 len - (buflen - playpos));
1745 return;
1747 #endif
1749 static DWORD DSOUND_MixInBuffer(IDirectSoundBuffer *dsb)
1751 INT32 i, len, ilen, temp, field;
1752 INT32 advance = primarybuf->wfx.wBitsPerSample >> 3;
1753 BYTE *buf, *ibuf, *obuf;
1754 INT16 *ibufs, *obufs;
1756 len = DSOUND_FRAGLEN; // The most we will use
1757 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
1758 temp = ((primarybuf->wfx.nAvgBytesPerSec * dsb->buflen) /
1759 dsb->nAvgBytesPerSec) -
1760 ((primarybuf->wfx.nAvgBytesPerSec * dsb->playpos) /
1761 dsb->nAvgBytesPerSec);
1762 len = (len > temp) ? temp : len;
1764 len &= ~3; // 4 byte alignment
1766 if (len == 0) {
1767 // This should only happen if we aren't looping and temp < 4
1769 // We skip the remainder, so check for possible events
1770 DSOUND_CheckEvent(dsb, dsb->buflen - dsb->playpos);
1771 // Stop
1772 dsb->playing = 0;
1773 dsb->writepos = 0;
1774 dsb->playpos = 0;
1775 // Check for DSBPN_OFFSETSTOP
1776 DSOUND_CheckEvent(dsb, 0);
1777 return 0;
1780 // Been seeing segfaults in malloc() for some reason...
1781 TRACE(dsound, "allocating buffer (size = %d)\n", len);
1782 if ((buf = ibuf = (BYTE *) malloc(len)) == NULL)
1783 return 0;
1785 TRACE(dsound, "MixInBuffer (%p) len = %d\n", dsb, len);
1787 ilen = DSOUND_MixerNorm(dsb, ibuf, len);
1788 if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
1789 (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
1790 DSOUND_MixerVol(dsb, ibuf, len);
1792 obuf = primarybuf->buffer + primarybuf->playpos;
1793 for (i = 0; i < len; i += advance) {
1794 obufs = (INT16 *) obuf;
1795 ibufs = (INT16 *) ibuf;
1796 if (primarybuf->wfx.wBitsPerSample == 8) {
1797 // 8-bit WAV is unsigned
1798 field = (*ibuf - 128);
1799 field += (*obuf - 128);
1800 field = field > 127 ? 127 : field;
1801 field = field < -128 ? -128 : field;
1802 *obuf = field + 128;
1803 } else {
1804 // 16-bit WAV is signed
1805 field = *ibufs;
1806 field += *obufs;
1807 field = field > 32767 ? 32767 : field;
1808 field = field < -32768 ? -32768 : field;
1809 *obufs = field;
1811 ibuf += advance;
1812 obuf += advance;
1813 if (obuf >= (BYTE *)(primarybuf->buffer + primarybuf->buflen))
1814 obuf = primarybuf->buffer;
1816 free(buf);
1818 if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY)
1819 DSOUND_CheckEvent(dsb, ilen);
1821 dsb->playpos += ilen;
1822 dsb->writepos = dsb->playpos + ilen;
1824 if (dsb->playpos >= dsb->buflen) {
1825 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
1826 dsb->playing = 0;
1827 dsb->writepos = 0;
1828 dsb->playpos = 0;
1829 DSOUND_CheckEvent(dsb, 0); // For DSBPN_OFFSETSTOP
1830 } else
1831 dsb->playpos %= dsb->buflen; // wrap
1834 if (dsb->writepos >= dsb->buflen)
1835 dsb->writepos %= dsb->buflen;
1837 return len;
1840 static DWORD WINAPI DSOUND_MixPrimary(void)
1842 INT32 i, len, maxlen = 0;
1843 IDirectSoundBuffer *dsb;
1845 for (i = dsound->nrofbuffers - 1; i >= 0; i--) {
1846 dsb = dsound->buffers[i];
1848 if (!dsb || !(dsb->lpvtbl))
1849 continue;
1850 dsb->lpvtbl->fnAddRef(dsb);
1851 if (dsb->buflen && dsb->playing) {
1852 EnterCriticalSection(&(dsb->lock));
1853 len = DSOUND_MixInBuffer(dsb);
1854 maxlen = len > maxlen ? len : maxlen;
1855 LeaveCriticalSection(&(dsb->lock));
1857 dsb->lpvtbl->fnRelease(dsb);
1860 return maxlen;
1863 static int DSOUND_OpenAudio(void)
1865 int audioFragment;
1867 if (primarybuf == NULL)
1868 return DSERR_OUTOFMEMORY;
1870 while (audiofd != -1)
1871 sleep(5);
1872 audiofd = open("/dev/audio",O_WRONLY);
1873 if (audiofd==-1) {
1874 // Don't worry if sound is busy at the moment
1875 if (errno != EBUSY)
1876 perror("open /dev/audio");
1877 return audiofd; // -1
1880 // We should probably do something here if SETFRAGMENT fails...
1881 audioFragment=0x0002000c;
1882 if (-1==ioctl(audiofd,SNDCTL_DSP_SETFRAGMENT,&audioFragment))
1883 perror("ioctl SETFRAGMENT");
1885 audioOK = 1;
1886 DSOUND_setformat(&(primarybuf->wfx));
1888 return 0;
1891 static void DSOUND_CloseAudio(void)
1893 int neutral;
1895 neutral = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
1896 audioOK = 0; // race condition
1897 Sleep(5);
1898 // It's possible we've been called with audio closed
1899 // from SetFormat()... this is just to force a call
1900 // to OpenAudio() to reset the hardware properly
1901 if (audiofd != -1)
1902 close(audiofd);
1903 primarybuf->playpos = 0;
1904 primarybuf->writepos = DSOUND_FRAGLEN;
1905 memset(primarybuf->buffer, neutral, primarybuf->buflen);
1906 audiofd = -1;
1907 TRACE(dsound, "Audio stopped\n");
1910 static int DSOUND_WriteAudio(char *buf, int len)
1912 int result, left = 0;
1914 while (left < len) {
1915 result = write(audiofd, buf + left, len - left);
1916 if (result == -1)
1917 if (errno == EINTR)
1918 continue;
1919 else
1920 return result;
1921 left += result;
1923 return 0;
1926 static void DSOUND_OutputPrimary(int len)
1928 int neutral, flen1, flen2;
1929 char *frag1, *frag2;
1931 // This is a bad place for this. We need to clear the
1932 // buffer with a neutral value, for unsigned 8-bit WAVE
1933 // that's 128, for signed 16-bit it's 0
1934 neutral = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
1936 // ****
1937 EnterCriticalSection(&(primarybuf->lock));
1939 // Write out the
1940 if ((audioOK == 1) || (DSOUND_OpenAudio() == 0)) {
1941 if (primarybuf->playpos + len >= primarybuf->buflen) {
1942 frag1 = primarybuf->buffer + primarybuf->playpos;
1943 flen1 = primarybuf->buflen - primarybuf->playpos;
1944 frag2 = primarybuf->buffer;
1945 flen2 = len - (primarybuf->buflen - primarybuf->playpos);
1946 if (DSOUND_WriteAudio(frag1, flen1) != 0) {
1947 perror("DSOUND_WriteAudio");
1948 LeaveCriticalSection(&(primarybuf->lock));
1949 ExitThread(0);
1951 memset(frag1, neutral, flen1);
1952 if (DSOUND_WriteAudio(frag2, flen2) != 0) {
1953 perror("DSOUND_WriteAudio");
1954 LeaveCriticalSection(&(primarybuf->lock));
1955 ExitThread(0);
1957 memset(frag2, neutral, flen2);
1958 } else {
1959 frag1 = primarybuf->buffer + primarybuf->playpos;
1960 flen1 = len;
1961 if (DSOUND_WriteAudio(frag1, flen1) != 0) {
1962 perror("DSOUND_WriteAudio");
1963 LeaveCriticalSection(&(primarybuf->lock));
1964 ExitThread(0);
1966 memset(frag1, neutral, flen1);
1968 } else {
1969 // Can't play audio at the moment -- we need to sleep
1970 // to make up for the time we'd be blocked in write()
1971 // to /dev/audio
1972 Sleep(60);
1974 primarybuf->playpos += len;
1975 if (primarybuf->playpos >= primarybuf->buflen)
1976 primarybuf->playpos %= primarybuf->buflen;
1977 primarybuf->writepos = primarybuf->playpos + DSOUND_FRAGLEN;
1978 if (primarybuf->writepos >= primarybuf->buflen)
1979 primarybuf->writepos %= primarybuf->buflen;
1981 LeaveCriticalSection(&(primarybuf->lock));
1982 // ****
1985 static DWORD WINAPI DSOUND_thread(LPVOID arg)
1987 int len;
1989 TRACE(dsound,"dsound is at pid %d\n",getpid());
1990 while (1) {
1991 if (!dsound) {
1992 WARN(dsound,"DSOUND thread giving up.\n");
1993 ExitThread(0);
1995 if (getppid()==1) {
1996 WARN(dsound,"DSOUND father died? Giving up.\n");
1997 ExitThread(0);
1999 /* RACE: dsound could be deleted */
2000 dsound->lpvtbl->fnAddRef(dsound);
2001 if (primarybuf == NULL) {
2002 // Should never happen
2003 WARN(dsound, "Lost the primary buffer!\n");
2004 dsound->lpvtbl->fnRelease(dsound);
2005 ExitThread(0);
2008 // ****
2009 EnterCriticalSection(&(primarybuf->lock));
2010 len = DSOUND_MixPrimary();
2011 LeaveCriticalSection(&(primarybuf->lock));
2012 // ****
2014 if (primarybuf->playing)
2015 len = DSOUND_FRAGLEN > len ? DSOUND_FRAGLEN : len;
2016 if (len) {
2017 // This does all the work
2018 DSOUND_OutputPrimary(len);
2019 } else {
2020 // no buffers playing -- close and wait
2021 if (audioOK)
2022 DSOUND_CloseAudio();
2023 Sleep(100);
2025 dsound->lpvtbl->fnRelease(dsound);
2027 ExitThread(0);
2030 #endif /* HAVE_OSS */
2032 HRESULT WINAPI DirectSoundCreate(LPGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter )
2034 if (lpGUID)
2035 TRACE(dsound,"(%p,%p,%p)\n",lpGUID,ppDS,pUnkOuter);
2036 else
2037 TRACE(dsound,"DirectSoundCreate (%p)\n", ppDS);
2039 #ifdef HAVE_OSS
2041 if (ppDS == NULL)
2042 return DSERR_INVALIDPARAM;
2044 if (primarybuf) {
2045 dsound->lpvtbl->fnAddRef(dsound);
2046 *ppDS = dsound;
2047 return DS_OK;
2050 // Check that we actually have audio capabilities
2051 // If we do, whether it's busy or not, we continue
2052 // otherwise we return with DSERR_NODRIVER
2054 audiofd = open("/dev/audio",O_WRONLY);
2055 if (audiofd == -1) {
2056 if (errno == ENODEV) {
2057 TRACE(dsound, "No sound hardware\n");
2058 return DSERR_NODRIVER;
2059 } else if (errno == EBUSY) {
2060 TRACE(dsound, "Sound device busy, will keep trying\n");
2061 } else {
2062 TRACE(dsound, "Unexpected error while checking for sound support\n");
2063 return DSERR_GENERIC;
2065 } else {
2066 close(audiofd);
2067 audiofd = -1;
2070 *ppDS = (LPDIRECTSOUND)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSound));
2071 if (*ppDS == NULL)
2072 return DSERR_OUTOFMEMORY;
2074 (*ppDS)->ref = 1;
2075 (*ppDS)->lpvtbl = &dsvt;
2076 (*ppDS)->buffers = NULL;
2077 (*ppDS)->nrofbuffers = 0;
2079 (*ppDS)->wfx.wFormatTag = 1;
2080 (*ppDS)->wfx.nChannels = 2;
2081 (*ppDS)->wfx.nSamplesPerSec = 22050;
2082 (*ppDS)->wfx.nAvgBytesPerSec = 44100;
2083 (*ppDS)->wfx.nBlockAlign = 2;
2084 (*ppDS)->wfx.wBitsPerSample = 8;
2086 if (!dsound) {
2087 HANDLE32 hnd;
2088 DWORD xid;
2090 dsound = (*ppDS);
2091 if (primarybuf == NULL) {
2092 DSBUFFERDESC dsbd;
2093 HRESULT hr;
2095 dsbd.dwSize = sizeof(DSBUFFERDESC);
2096 dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
2097 dsbd.dwBufferBytes = 0;
2098 dsbd.lpwfxFormat = &(dsound->wfx);
2099 hr = IDirectSound_CreateSoundBuffer(*ppDS, &dsbd, &primarybuf, NULL);
2100 if (hr != DS_OK)
2101 return hr;
2102 dsound->primary = primarybuf;
2104 memset(primarybuf->buffer, 128, primarybuf->buflen);
2105 hnd = CreateThread(NULL,0,DSOUND_thread,0,0,&xid);
2107 return DS_OK;
2108 #else
2109 MessageBox32A(0,"DirectSound needs the Open Sound System Driver, which has not been found by ./configure.","WINE DirectSound",MB_OK|MB_ICONSTOP);
2110 return DSERR_NODRIVER;
2111 #endif
2114 /*******************************************************************************
2115 * DirectSound ClassFactory
2117 static HRESULT WINAPI
2118 DSCF_QueryInterface(LPCLASSFACTORY this,REFIID riid,LPVOID *ppobj) {
2119 char buf[80];
2121 if (HIWORD(riid))
2122 WINE_StringFromCLSID(riid,buf);
2123 else
2124 sprintf(buf,"<guid-0x%04x>",LOWORD(riid));
2125 FIXME(dsound,"(%p)->(%s,%p),stub!\n",this,buf,ppobj);
2126 return E_NOINTERFACE;
2129 static ULONG WINAPI
2130 DSCF_AddRef(LPCLASSFACTORY this) {
2131 return ++(this->ref);
2134 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY this) {
2135 /* static class, won't be freed */
2136 return --(this->ref);
2139 static HRESULT WINAPI DSCF_CreateInstance(
2140 LPCLASSFACTORY this,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
2142 char buf[80];
2144 WINE_StringFromCLSID(riid,buf);
2145 TRACE(dsound,"(%p)->(%p,%s,%p)\n",this,pOuter,buf,ppobj);
2146 if (!memcmp(riid,&IID_IDirectSound,sizeof(IID_IDirectSound))) {
2147 /* FIXME: reuse already created dsound if present? */
2148 return DirectSoundCreate(riid,(LPDIRECTSOUND*)ppobj,pOuter);
2150 return E_NOINTERFACE;
2153 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY this,BOOL32 dolock) {
2154 FIXME(dsound,"(%p)->(%d),stub!\n",this,dolock);
2155 return S_OK;
2158 static IClassFactory_VTable DSCF_VTable = {
2159 DSCF_QueryInterface,
2160 DSCF_AddRef,
2161 DSCF_Release,
2162 DSCF_CreateInstance,
2163 DSCF_LockServer
2165 static IClassFactory DSOUND_CF = {&DSCF_VTable, 1 };
2167 /*******************************************************************************
2168 * DllGetClassObject [DSOUND.4]
2169 * Retrieves class object from a DLL object
2171 * NOTES
2172 * Docs say returns STDAPI
2174 * PARAMS
2175 * rclsid [I] CLSID for the class object
2176 * riid [I] Reference to identifier of interface for class object
2177 * ppv [O] Address of variable to receive interface pointer for riid
2179 * RETURNS
2180 * Success: S_OK
2181 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
2182 * E_UNEXPECTED
2184 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
2186 char buf[80],xbuf[80];
2188 if (HIWORD(rclsid))
2189 WINE_StringFromCLSID(rclsid,xbuf);
2190 else
2191 sprintf(xbuf,"<guid-0x%04x>",LOWORD(rclsid));
2192 if (HIWORD(riid))
2193 WINE_StringFromCLSID(riid,buf);
2194 else
2195 sprintf(buf,"<guid-0x%04x>",LOWORD(riid));
2196 WINE_StringFromCLSID(riid,xbuf);
2197 TRACE(dsound, "(%p,%p,%p)\n", xbuf, buf, ppv);
2198 if (!memcmp(riid,&IID_IClassFactory,sizeof(IID_IClassFactory))) {
2199 *ppv = (LPVOID)&DSOUND_CF;
2200 DSOUND_CF.lpvtbl->fnAddRef(&DSOUND_CF);
2201 return S_OK;
2203 FIXME(dsound, "(%p,%p,%p): no interface found.\n", xbuf, buf, ppv);
2204 return E_NOINTERFACE;
2208 /*******************************************************************************
2209 * DllCanUnloadNow [DSOUND.3] Determines whether the DLL is in use.
2211 * RETURNS
2212 * Success: S_OK
2213 * Failure: S_FALSE
2215 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
2217 FIXME(dsound, "(void): stub\n");
2218 return S_FALSE;