Added sysres_Ru.s
[wine/multimedia.git] / multimedia / dsound.c
blob7fd6f929cbc9f24d1f635828c1a6d8ff265830e0
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 * Most thread locking is complete. There may be a few race
12 * conditions still lurking.
14 * Tested with a Soundblaster clone, a Gravis UltraSound Classic,
15 * and a Turtle Beach Tropez+.
17 * TODO:
18 * Implement DirectSoundCapture API
19 * Implement SetCooperativeLevel properly (need to address focus issues)
20 * Use wavetable synth for static buffers if available
21 * Implement DirectSound3DBuffers (stubs in place)
22 * Use hardware 3D support if available (OSS support may be needed first)
23 * Add support for APIs other than OSS: ALSA (http://alsa.jcu.cz/)
24 * and esound (http://www.gnome.org), for instance
26 * FIXME: Status needs updating.
28 * Status:
29 * - Wing Commander 4/W95:
30 * The intromovie plays without problems. Nearly lipsynchron.
31 * - DiscWorld 2
32 * The sound works, but noticeable chunks are left out (from the sound and
33 * the animation). Don't know why yet.
34 * - Diablo:
35 * Sound works, but slows down the movieplayer.
36 * - XvT:
37 * Doesn't sound yet.
38 * - Monkey Island 3:
39 * The background sound of the startscreen works ;)
40 * - WingCommander Prophecy Demo:
41 * Sound works for the intromovie.
42 * - Total Annihilation (1998/12/04):
43 * Sound plays perfectly in the game, but the Smacker movies
44 * (http://www.smacker.com/) play silently.
45 * - A-10 Cuba! Demo (1998/12/04):
46 * Sound works properly (for some people).
47 * - dsstream.exe, from DirectX 5.2 SDK (1998/12/04):
48 * Works properly, but requires "-dll -winmm".
49 * - dsshow.exe, from DirectX 5.2 SDK (1998/12/04):
50 * Initializes the DLL properly with CoCreateInstance(), but the
51 * FileOpen dialog box is broken - could not test properly
54 #include "config.h"
55 #include <assert.h>
56 #include <errno.h>
57 #include <sys/types.h>
58 #include <sys/time.h>
59 #include <sys/fcntl.h>
60 #include <unistd.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <math.h> /* Insomnia - pow() function */
64 #include "dsound.h"
65 #include "winuser.h"
66 #include "winerror.h"
67 #include "multimedia.h"
68 #include "wine/obj_base.h"
69 #include "thread.h"
70 #include "debug.h"
72 #ifdef HAVE_OSS
73 # include <sys/ioctl.h>
74 # ifdef HAVE_MACHINE_SOUNDCARD_H
75 # include <machine/soundcard.h>
76 # endif
77 # ifdef HAVE_SYS_SOUNDCARD_H
78 # include <sys/soundcard.h>
79 # endif
81 /* #define USE_DSOUND3D 1 */
83 #define DSOUND_FRAGLEN (primarybuf->wfx.nAvgBytesPerSec >> 4)
84 #define DSOUND_FREQSHIFT (14)
86 static int audiofd = -1;
87 static int audioOK = 0;
89 static LPDIRECTSOUND dsound = NULL;
91 static LPDIRECTSOUNDBUFFER primarybuf = NULL;
93 static int DSOUND_setformat(LPWAVEFORMATEX wfex);
94 static void DSOUND_CheckEvent(IDirectSoundBuffer *dsb, int len);
95 static void DSOUND_CloseAudio(void);
97 #endif
99 HRESULT WINAPI DirectSoundEnumerateA(
100 LPDSENUMCALLBACKA enumcb,
101 LPVOID context)
103 TRACE(dsound, "enumcb = %p, context = %p\n", enumcb, context);
105 #ifdef HAVE_OSS
106 if (enumcb != NULL)
107 enumcb(NULL,"WINE DirectSound using Open Sound System",
108 "sound",context);
109 #endif
111 return 0;
114 #ifdef HAVE_OSS
115 static void _dump_DSBCAPS(DWORD xmask) {
116 struct {
117 DWORD mask;
118 char *name;
119 } flags[] = {
120 #define FE(x) { x, #x },
121 FE(DSBCAPS_PRIMARYBUFFER)
122 FE(DSBCAPS_STATIC)
123 FE(DSBCAPS_LOCHARDWARE)
124 FE(DSBCAPS_LOCSOFTWARE)
125 FE(DSBCAPS_CTRLFREQUENCY)
126 FE(DSBCAPS_CTRLPAN)
127 FE(DSBCAPS_CTRLVOLUME)
128 FE(DSBCAPS_CTRLDEFAULT)
129 FE(DSBCAPS_CTRLALL)
130 FE(DSBCAPS_STICKYFOCUS)
131 FE(DSBCAPS_GETCURRENTPOSITION2)
133 int i;
135 for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
136 if (flags[i].mask & xmask)
137 fprintf(stderr,"%s ",flags[i].name);
140 /*******************************************************************************
141 * IDirectSound3DBuffer
144 /* IUnknown methods */
145 static HRESULT WINAPI IDirectSound3DBuffer_QueryInterface(
146 LPDIRECTSOUND3DBUFFER this, REFIID riid, LPVOID *ppobj)
148 char xbuf[50];
150 WINE_StringFromCLSID(riid,xbuf);
151 TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
152 return E_FAIL;
155 static ULONG WINAPI IDirectSound3DBuffer_AddRef(LPDIRECTSOUND3DBUFFER this)
157 this->ref++;
158 return this->ref;
161 static ULONG WINAPI IDirectSound3DBuffer_Release(LPDIRECTSOUND3DBUFFER this)
163 if(--this->ref)
164 return this->ref;
166 HeapFree(GetProcessHeap(),0,this->buffer);
167 HeapFree(GetProcessHeap(),0,this);
169 return 0;
172 /* IDirectSound3DBuffer methods */
173 static HRESULT WINAPI IDirectSound3DBuffer_GetAllParameters(
174 LPDIRECTSOUND3DBUFFER this,
175 LPDS3DBUFFER lpDs3dBuffer)
177 FIXME(dsound,"stub\n");
178 return DS_OK;
181 static HRESULT WINAPI IDirectSound3DBuffer_GetConeAngles(
182 LPDIRECTSOUND3DBUFFER this,
183 LPDWORD lpdwInsideConeAngle,
184 LPDWORD lpdwOutsideConeAngle)
186 FIXME(dsound,"stub\n");
187 return DS_OK;
190 static HRESULT WINAPI IDirectSound3DBuffer_GetConeOrientation(
191 LPDIRECTSOUND3DBUFFER this,
192 LPD3DVECTOR lpvConeOrientation)
194 FIXME(dsound,"stub\n");
195 return DS_OK;
198 static HRESULT WINAPI IDirectSound3DBuffer_GetConeOutsideVolume(
199 LPDIRECTSOUND3DBUFFER this,
200 LPLONG lplConeOutsideVolume)
202 FIXME(dsound,"stub\n");
203 return DS_OK;
206 static HRESULT WINAPI IDirectSound3DBuffer_GetMaxDistance(
207 LPDIRECTSOUND3DBUFFER this,
208 LPD3DVALUE lpfMaxDistance)
210 FIXME(dsound,"stub\n");
211 return DS_OK;
214 static HRESULT WINAPI IDirectSound3DBuffer_GetMinDistance(
215 LPDIRECTSOUND3DBUFFER this,
216 LPD3DVALUE lpfMinDistance)
218 FIXME(dsound,"stub\n");
219 return DS_OK;
222 static HRESULT WINAPI IDirectSound3DBuffer_GetMode(
223 LPDIRECTSOUND3DBUFFER this,
224 LPDWORD lpdwMode)
226 FIXME(dsound,"stub\n");
227 return DS_OK;
230 static HRESULT WINAPI IDirectSound3DBuffer_GetPosition(
231 LPDIRECTSOUND3DBUFFER this,
232 LPD3DVECTOR lpvPosition)
234 FIXME(dsound,"stub\n");
235 return DS_OK;
238 static HRESULT WINAPI IDirectSound3DBuffer_GetVelocity(
239 LPDIRECTSOUND3DBUFFER this,
240 LPD3DVECTOR lpvVelocity)
242 FIXME(dsound,"stub\n");
243 return DS_OK;
246 static HRESULT WINAPI IDirectSound3DBuffer_SetAllParameters(
247 LPDIRECTSOUND3DBUFFER this,
248 LPCDS3DBUFFER lpcDs3dBuffer,
249 DWORD dwApply)
251 FIXME(dsound,"stub\n");
252 return DS_OK;
255 static HRESULT WINAPI IDirectSound3DBuffer_SetConeAngles(
256 LPDIRECTSOUND3DBUFFER this,
257 DWORD dwInsideConeAngle,
258 DWORD dwOutsideConeAngle,
259 DWORD dwApply)
261 FIXME(dsound,"stub\n");
262 return DS_OK;
265 static HRESULT WINAPI IDirectSound3DBuffer_SetConeOrientation(
266 LPDIRECTSOUND3DBUFFER this,
267 D3DVALUE x, D3DVALUE y, D3DVALUE z,
268 DWORD dwApply)
270 FIXME(dsound,"stub\n");
271 return DS_OK;
274 static HRESULT WINAPI IDirectSound3DBuffer_SetConeOutsideVolume(
275 LPDIRECTSOUND3DBUFFER this,
276 LONG lConeOutsideVolume,
277 DWORD dwApply)
279 FIXME(dsound,"stub\n");
280 return DS_OK;
283 static HRESULT WINAPI IDirectSound3DBuffer_SetMaxDistance(
284 LPDIRECTSOUND3DBUFFER this,
285 D3DVALUE fMaxDistance,
286 DWORD dwApply)
288 FIXME(dsound,"stub\n");
289 return DS_OK;
292 static HRESULT WINAPI IDirectSound3DBuffer_SetMinDistance(
293 LPDIRECTSOUND3DBUFFER this,
294 D3DVALUE fMinDistance,
295 DWORD dwApply)
297 FIXME(dsound,"stub\n");
298 return DS_OK;
301 static HRESULT WINAPI IDirectSound3DBuffer_SetMode(
302 LPDIRECTSOUND3DBUFFER this,
303 DWORD dwMode,
304 DWORD dwApply)
306 TRACE(dsound, "mode = %lx\n", dwMode);
307 this->ds3db.dwMode = dwMode;
308 return DS_OK;
311 static HRESULT WINAPI IDirectSound3DBuffer_SetPosition(
312 LPDIRECTSOUND3DBUFFER this,
313 D3DVALUE x, D3DVALUE y, D3DVALUE z,
314 DWORD dwApply)
316 FIXME(dsound,"stub\n");
317 return DS_OK;
320 static HRESULT WINAPI IDirectSound3DBuffer_SetVelocity(
321 LPDIRECTSOUND3DBUFFER this,
322 D3DVALUE x, D3DVALUE y, D3DVALUE z,
323 DWORD dwApply)
325 FIXME(dsound,"stub\n");
326 return DS_OK;
329 IDirectSound3DBuffer_VTable ds3dbvt = {
330 /* IUnknown methods */
331 IDirectSound3DBuffer_QueryInterface,
332 IDirectSound3DBuffer_AddRef,
333 IDirectSound3DBuffer_Release,
334 /* IDirectSound3DBuffer methods */
335 IDirectSound3DBuffer_GetAllParameters,
336 IDirectSound3DBuffer_GetConeAngles,
337 IDirectSound3DBuffer_GetConeOrientation,
338 IDirectSound3DBuffer_GetConeOutsideVolume,
339 IDirectSound3DBuffer_GetMaxDistance,
340 IDirectSound3DBuffer_GetMinDistance,
341 IDirectSound3DBuffer_GetMode,
342 IDirectSound3DBuffer_GetPosition,
343 IDirectSound3DBuffer_GetVelocity,
344 IDirectSound3DBuffer_SetAllParameters,
345 IDirectSound3DBuffer_SetConeAngles,
346 IDirectSound3DBuffer_SetConeOrientation,
347 IDirectSound3DBuffer_SetConeOutsideVolume,
348 IDirectSound3DBuffer_SetMaxDistance,
349 IDirectSound3DBuffer_SetMinDistance,
350 IDirectSound3DBuffer_SetMode,
351 IDirectSound3DBuffer_SetPosition,
352 IDirectSound3DBuffer_SetVelocity,
355 #ifdef USE_DSOUND3D
356 static int DSOUND_Create3DBuffer(LPDIRECTSOUNDBUFFER dsb)
358 DWORD i, temp, iSize, oSize, offset;
359 LPBYTE bIbuf, bObuf, bTbuf = NULL;
360 LPWORD wIbuf, wObuf, wTbuf = NULL;
362 /* Inside DirectX says it's stupid but allowed */
363 if (dsb->wfx.nChannels == 2) {
364 /* Convert to mono */
365 if (dsb->wfx.wBitsPerSample == 16) {
366 iSize = dsb->buflen / 4;
367 wTbuf = malloc(dsb->buflen / 2);
368 if (wTbuf == NULL)
369 return DSERR_OUTOFMEMORY;
370 for (i = 0; i < iSize; i++)
371 wTbuf[i] = (dsb->buffer[i] + dsb->buffer[(i * 2) + 1]) / 2;
372 wIbuf = wTbuf;
373 } else {
374 iSize = dsb->buflen / 2;
375 bTbuf = malloc(dsb->buflen / 2);
376 if (bTbuf == NULL)
377 return DSERR_OUTOFMEMORY;
378 for (i = 0; i < iSize; i++)
379 bTbuf[i] = (dsb->buffer[i] + dsb->buffer[(i * 2) + 1]) / 2;
380 bIbuf = bTbuf;
382 } else {
383 if (dsb->wfx.wBitsPerSample == 16) {
384 iSize = dsb->buflen / 2;
385 wIbuf = (LPWORD) dsb->buffer;
386 } else {
387 bIbuf = (LPBYTE) dsb->buffer;
388 iSize = dsb->buflen;
392 if (primarybuf->wfx.wBitsPerSample == 16) {
393 wObuf = (LPWORD) dsb->ds3db->buffer;
394 oSize = dsb->ds3db->buflen / 2;
395 } else {
396 bObuf = (LPBYTE) dsb->ds3db->buffer;
397 oSize = dsb->ds3db->buflen;
400 offset = primarybuf->wfx.nSamplesPerSec / 100; /* 10ms */
401 if (primarybuf->wfx.wBitsPerSample == 16 && dsb->wfx.wBitsPerSample == 16)
402 for (i = 0; i < iSize; i++) {
403 temp = wIbuf[i];
404 if (i >= offset)
405 temp += wIbuf[i - offset] >> 9;
406 else
407 temp += wIbuf[i + iSize - offset] >> 9;
408 wObuf[i * 2] = temp;
409 wObuf[(i * 2) + 1] = temp;
411 else if (primarybuf->wfx.wBitsPerSample == 8 && dsb->wfx.wBitsPerSample == 8)
412 for (i = 0; i < iSize; i++) {
413 temp = bIbuf[i];
414 if (i >= offset)
415 temp += bIbuf[i - offset] >> 5;
416 else
417 temp += bIbuf[i + iSize - offset] >> 5;
418 bObuf[i * 2] = temp;
419 bObuf[(i * 2) + 1] = temp;
422 if (wTbuf)
423 free(wTbuf);
424 if (bTbuf)
425 free(bTbuf);
427 return DS_OK;
429 #endif
430 /*******************************************************************************
431 * IDirectSound3DListener
434 /* IUnknown methods */
435 static HRESULT WINAPI IDirectSound3DListener_QueryInterface(
436 LPDIRECTSOUND3DLISTENER this, REFIID riid, LPVOID *ppobj)
438 char xbuf[50];
440 WINE_StringFromCLSID(riid,xbuf);
441 TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
442 return E_FAIL;
445 static ULONG WINAPI IDirectSound3DListener_AddRef(LPDIRECTSOUND3DLISTENER this)
447 this->ref++;
448 return this->ref;
451 static ULONG WINAPI IDirectSound3DListener_Release(LPDIRECTSOUND3DLISTENER this)
453 this->ref--;
454 return this->ref;
457 /* IDirectSound3DListener methods */
458 static HRESULT WINAPI IDirectSound3DListener_GetAllParameter(
459 LPDIRECTSOUND3DLISTENER this,
460 LPDS3DLISTENER lpDS3DL)
462 FIXME(dsound,"stub\n");
463 return DS_OK;
466 static HRESULT WINAPI IDirectSound3DListener_GetDistanceFactor(
467 LPDIRECTSOUND3DLISTENER this,
468 LPD3DVALUE lpfDistanceFactor)
470 FIXME(dsound,"stub\n");
471 return DS_OK;
474 static HRESULT WINAPI IDirectSound3DListener_GetDopplerFactor(
475 LPDIRECTSOUND3DLISTENER this,
476 LPD3DVALUE lpfDopplerFactor)
478 FIXME(dsound,"stub\n");
479 return DS_OK;
482 static HRESULT WINAPI IDirectSound3DListener_GetOrientation(
483 LPDIRECTSOUND3DLISTENER this,
484 LPD3DVECTOR lpvOrientFront,
485 LPD3DVECTOR lpvOrientTop)
487 FIXME(dsound,"stub\n");
488 return DS_OK;
491 static HRESULT WINAPI IDirectSound3DListener_GetPosition(
492 LPDIRECTSOUND3DLISTENER this,
493 LPD3DVECTOR lpvPosition)
495 FIXME(dsound,"stub\n");
496 return DS_OK;
499 static HRESULT WINAPI IDirectSound3DListener_GetRolloffFactor(
500 LPDIRECTSOUND3DLISTENER this,
501 LPD3DVALUE lpfRolloffFactor)
503 FIXME(dsound,"stub\n");
504 return DS_OK;
507 static HRESULT WINAPI IDirectSound3DListener_GetVelocity(
508 LPDIRECTSOUND3DLISTENER this,
509 LPD3DVECTOR lpvVelocity)
511 FIXME(dsound,"stub\n");
512 return DS_OK;
515 static HRESULT WINAPI IDirectSound3DListener_SetAllParameters(
516 LPDIRECTSOUND3DLISTENER this,
517 LPCDS3DLISTENER lpcDS3DL,
518 DWORD dwApply)
520 FIXME(dsound,"stub\n");
521 return DS_OK;
524 static HRESULT WINAPI IDirectSound3DListener_SetDistanceFactor(
525 LPDIRECTSOUND3DLISTENER this,
526 D3DVALUE fDistanceFactor,
527 DWORD dwApply)
529 FIXME(dsound,"stub\n");
530 return DS_OK;
533 static HRESULT WINAPI IDirectSound3DListener_SetDopplerFactor(
534 LPDIRECTSOUND3DLISTENER this,
535 D3DVALUE fDopplerFactor,
536 DWORD dwApply)
538 FIXME(dsound,"stub\n");
539 return DS_OK;
542 static HRESULT WINAPI IDirectSound3DListener_SetOrientation(
543 LPDIRECTSOUND3DLISTENER this,
544 D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront,
545 D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop,
546 DWORD dwApply)
548 FIXME(dsound,"stub\n");
549 return DS_OK;
552 static HRESULT WINAPI IDirectSound3DListener_SetPosition(
553 LPDIRECTSOUND3DLISTENER this,
554 D3DVALUE x, D3DVALUE y, D3DVALUE z,
555 DWORD dwApply)
557 FIXME(dsound,"stub\n");
558 return DS_OK;
561 static HRESULT WINAPI IDirectSound3DListener_SetRolloffFactor(
562 LPDIRECTSOUND3DLISTENER this,
563 D3DVALUE fRolloffFactor,
564 DWORD dwApply)
566 FIXME(dsound,"stub\n");
567 return DS_OK;
570 static HRESULT WINAPI IDirectSound3DListener_SetVelocity(
571 LPDIRECTSOUND3DLISTENER this,
572 D3DVALUE x, D3DVALUE y, D3DVALUE z,
573 DWORD dwApply)
575 FIXME(dsound,"stub\n");
576 return DS_OK;
579 static HRESULT WINAPI IDirectSound3DListener_CommitDeferredSettings(
580 LPDIRECTSOUND3DLISTENER this)
583 FIXME(dsound,"stub\n");
584 return DS_OK;
587 IDirectSound3DListener_VTable ds3dlvt = {
588 /* IUnknown methods */
589 IDirectSound3DListener_QueryInterface,
590 IDirectSound3DListener_AddRef,
591 IDirectSound3DListener_Release,
592 /* IDirectSound3DListener methods */
593 IDirectSound3DListener_GetAllParameter,
594 IDirectSound3DListener_GetDistanceFactor,
595 IDirectSound3DListener_GetDopplerFactor,
596 IDirectSound3DListener_GetOrientation,
597 IDirectSound3DListener_GetPosition,
598 IDirectSound3DListener_GetRolloffFactor,
599 IDirectSound3DListener_GetVelocity,
600 IDirectSound3DListener_SetAllParameters,
601 IDirectSound3DListener_SetDistanceFactor,
602 IDirectSound3DListener_SetDopplerFactor,
603 IDirectSound3DListener_SetOrientation,
604 IDirectSound3DListener_SetPosition,
605 IDirectSound3DListener_SetRolloffFactor,
606 IDirectSound3DListener_SetVelocity,
607 IDirectSound3DListener_CommitDeferredSettings,
610 /*******************************************************************************
611 * IDirectSoundNotify
613 static HRESULT WINAPI IDirectSoundNotify_QueryInterface(
614 LPDIRECTSOUNDNOTIFY this,REFIID riid,LPVOID *ppobj
616 char xbuf[50];
618 WINE_StringFromCLSID(riid,xbuf);
619 TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
620 return E_FAIL;
623 static ULONG WINAPI IDirectSoundNotify_AddRef(LPDIRECTSOUNDNOTIFY this) {
624 return ++(this->ref);
627 static ULONG WINAPI IDirectSoundNotify_Release(LPDIRECTSOUNDNOTIFY this) {
628 this->ref--;
629 if (!this->ref) {
630 this->dsb->lpvtbl->fnRelease(this->dsb);
631 HeapFree(GetProcessHeap(),0,this);
632 return 0;
634 return this->ref;
637 static HRESULT WINAPI IDirectSoundNotify_SetNotificationPositions(
638 LPDIRECTSOUNDNOTIFY this,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
640 int i;
642 if (TRACE_ON(dsound)) {
643 TRACE(dsound,"(%p,0x%08lx,%p)\n",this,howmuch,notify);
644 for (i=0;i<howmuch;i++)
645 TRACE(dsound,"notify at %ld to 0x%08lx\n",
646 notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
648 this->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,this->dsb->notifies,(this->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
649 memcpy( this->dsb->notifies+this->dsb->nrofnotifies,
650 notify,
651 howmuch*sizeof(DSBPOSITIONNOTIFY)
653 this->dsb->nrofnotifies+=howmuch;
655 return 0;
658 IDirectSoundNotify_VTable dsnvt = {
659 IDirectSoundNotify_QueryInterface,
660 IDirectSoundNotify_AddRef,
661 IDirectSoundNotify_Release,
662 IDirectSoundNotify_SetNotificationPositions,
665 /*******************************************************************************
666 * IDirectSoundBuffer
669 /* This sets this format for the <em>Primary Buffer Only</em> */
670 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
671 static HRESULT WINAPI IDirectSoundBuffer_SetFormat(
672 LPDIRECTSOUNDBUFFER this,LPWAVEFORMATEX wfex
674 LPDIRECTSOUNDBUFFER *dsb;
675 int i;
677 /* Let's be pedantic! */
678 if ((wfex == NULL) ||
679 (wfex->wFormatTag != WAVE_FORMAT_PCM) ||
680 (wfex->nChannels < 1) || (wfex->nChannels > 2) ||
681 (wfex->nSamplesPerSec < 1) ||
682 (wfex->nBlockAlign < 1) || (wfex->nChannels > 4) ||
683 ((wfex->wBitsPerSample != 8) && (wfex->wBitsPerSample != 16))) {
684 TRACE(dsound, "failed pedantic check!\n");
685 return DSERR_INVALIDPARAM;
688 /* **** */
689 EnterCriticalSection(&(primarybuf->lock));
691 if (primarybuf->wfx.nSamplesPerSec != wfex->nSamplesPerSec) {
692 dsb = dsound->buffers;
693 for (i = 0; i < dsound->nrofbuffers; i++, dsb++) {
694 /* **** */
695 EnterCriticalSection(&((*dsb)->lock));
697 (*dsb)->freqAdjust = ((*dsb)->freq << DSOUND_FREQSHIFT) /
698 wfex->nSamplesPerSec;
700 LeaveCriticalSection(&((*dsb)->lock));
701 /* **** */
705 memcpy(&(primarybuf->wfx), wfex, sizeof(primarybuf->wfx));
707 TRACE(dsound,"(formattag=0x%04x,chans=%d,samplerate=%ld"
708 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
709 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
710 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
711 wfex->wBitsPerSample, wfex->cbSize);
713 primarybuf->wfx.nAvgBytesPerSec =
714 this->wfx.nSamplesPerSec * this->wfx.nBlockAlign;
716 DSOUND_CloseAudio();
718 LeaveCriticalSection(&(primarybuf->lock));
719 /* **** */
721 return DS_OK;
724 static HRESULT WINAPI IDirectSoundBuffer_SetVolume(
725 LPDIRECTSOUNDBUFFER this,LONG vol
727 double temp;
729 TRACE(dsound,"(%p,%ld)\n",this,vol);
731 /* I'm not sure if we need this for primary buffer */
732 if (!(this->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
733 return DSERR_CONTROLUNAVAIL;
735 if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN))
736 return DSERR_INVALIDPARAM;
738 /* This needs to adjust the soundcard volume when */
739 /* called for the primary buffer */
740 if (this->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
741 FIXME(dsound, "Volume control of primary unimplemented.\n");
742 this->volume = vol;
743 return DS_OK;
746 /* **** */
747 EnterCriticalSection(&(this->lock));
749 this->volume = vol;
751 temp = (double) (this->volume - (this->pan > 0 ? this->pan : 0));
752 this->lVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 32768.0);
753 temp = (double) (this->volume + (this->pan < 0 ? this->pan : 0));
754 this->rVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 32768.0);
756 LeaveCriticalSection(&(this->lock));
757 /* **** */
759 TRACE(dsound, "left = %lx, right = %lx\n", this->lVolAdjust, this->rVolAdjust);
761 return DS_OK;
764 static HRESULT WINAPI IDirectSoundBuffer_GetVolume(
765 LPDIRECTSOUNDBUFFER this,LPLONG vol
767 TRACE(dsound,"(%p,%p)\n",this,vol);
769 if (vol == NULL)
770 return DSERR_INVALIDPARAM;
772 *vol = this->volume;
773 return DS_OK;
776 static HRESULT WINAPI IDirectSoundBuffer_SetFrequency(
777 LPDIRECTSOUNDBUFFER this,DWORD freq
779 TRACE(dsound,"(%p,%ld)\n",this,freq);
781 /* You cannot set the frequency of the primary buffer */
782 if (!(this->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY) ||
783 (this->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
784 return DSERR_CONTROLUNAVAIL;
786 if ((freq < DSBFREQUENCY_MIN) || (freq > DSBFREQUENCY_MAX))
787 return DSERR_INVALIDPARAM;
789 /* **** */
790 EnterCriticalSection(&(this->lock));
792 this->freq = freq;
793 this->freqAdjust = (freq << DSOUND_FREQSHIFT) / primarybuf->wfx.nSamplesPerSec;
794 this->nAvgBytesPerSec = freq * this->wfx.nBlockAlign;
796 LeaveCriticalSection(&(this->lock));
797 /* **** */
799 return DS_OK;
802 static HRESULT WINAPI IDirectSoundBuffer_Play(
803 LPDIRECTSOUNDBUFFER this,DWORD reserved1,DWORD reserved2,DWORD flags
805 TRACE(dsound,"(%p,%08lx,%08lx,%08lx)\n",
806 this,reserved1,reserved2,flags
808 this->playflags = flags;
809 this->playing = 1;
810 return DS_OK;
813 static HRESULT WINAPI IDirectSoundBuffer_Stop(LPDIRECTSOUNDBUFFER this)
815 TRACE(dsound,"(%p)\n",this);
817 /* **** */
818 EnterCriticalSection(&(this->lock));
820 this->playing = 0;
821 DSOUND_CheckEvent(this, 0);
823 LeaveCriticalSection(&(this->lock));
824 /* **** */
826 return DS_OK;
829 static DWORD WINAPI IDirectSoundBuffer_AddRef(LPDIRECTSOUNDBUFFER this) {
830 /* TRACE(dsound,"(%p) ref was %ld\n",this, this->ref); */
832 return ++(this->ref);
834 static DWORD WINAPI IDirectSoundBuffer_Release(LPDIRECTSOUNDBUFFER this) {
835 int i;
837 /* TRACE(dsound,"(%p) ref was %ld\n",this, this->ref); */
839 if (--this->ref)
840 return this->ref;
842 for (i=0;i<this->dsound->nrofbuffers;i++)
843 if (this->dsound->buffers[i] == this)
844 break;
845 if (i < this->dsound->nrofbuffers) {
846 /* Put the last buffer of the list in the (now empty) position */
847 this->dsound->buffers[i] = this->dsound->buffers[this->dsound->nrofbuffers - 1];
848 this->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,this->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*this->dsound->nrofbuffers);
849 this->dsound->nrofbuffers--;
850 this->dsound->lpvtbl->fnRelease(this->dsound);
853 DeleteCriticalSection(&(this->lock));
855 if (this->ds3db && this->ds3db->lpvtbl)
856 this->ds3db->lpvtbl->fnRelease(this->ds3db);
858 if (this->parent)
859 /* this is a duplicate buffer */
860 this->parent->lpvtbl->fnRelease(this->parent);
861 else
862 /* this is a toplevel buffer */
863 HeapFree(GetProcessHeap(),0,this->buffer);
865 HeapFree(GetProcessHeap(),0,this);
867 if (this == primarybuf)
868 primarybuf = NULL;
870 return DS_OK;
873 static HRESULT WINAPI IDirectSoundBuffer_GetCurrentPosition(
874 LPDIRECTSOUNDBUFFER this,LPDWORD playpos,LPDWORD writepos
876 TRACE(dsound,"(%p,%p,%p)\n",this,playpos,writepos);
877 if (playpos) *playpos = this->playpos;
878 if (writepos) *writepos = this->writepos;
879 TRACE(dsound, "playpos = %ld, writepos = %ld\n", *playpos, *writepos);
880 return DS_OK;
883 static HRESULT WINAPI IDirectSoundBuffer_GetStatus(
884 LPDIRECTSOUNDBUFFER this,LPDWORD status
886 TRACE(dsound,"(%p,%p)\n",this,status);
888 if (status == NULL)
889 return DSERR_INVALIDPARAM;
891 *status = 0;
892 if (this->playing)
893 *status |= DSBSTATUS_PLAYING;
894 if (this->playflags & DSBPLAY_LOOPING)
895 *status |= DSBSTATUS_LOOPING;
897 return DS_OK;
901 static HRESULT WINAPI IDirectSoundBuffer_GetFormat(
902 LPDIRECTSOUNDBUFFER this,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
904 TRACE(dsound,"(%p,%p,%ld,%p)\n",this,lpwf,wfsize,wfwritten);
906 if (wfsize>sizeof(this->wfx))
907 wfsize = sizeof(this->wfx);
908 if (lpwf) { /* NULL is valid */
909 memcpy(lpwf,&(this->wfx),wfsize);
910 if (wfwritten)
911 *wfwritten = wfsize;
912 } else
913 if (wfwritten)
914 *wfwritten = sizeof(this->wfx);
915 else
916 return DSERR_INVALIDPARAM;
918 return DS_OK;
921 static HRESULT WINAPI IDirectSoundBuffer_Lock(
922 LPDIRECTSOUNDBUFFER this,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
925 TRACE(dsound,"(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx)\n",
926 this,
927 writecursor,
928 writebytes,
929 lplpaudioptr1,
930 audiobytes1,
931 lplpaudioptr2,
932 audiobytes2,
933 flags
935 if (flags & DSBLOCK_FROMWRITECURSOR)
936 writecursor += this->writepos;
937 if (flags & DSBLOCK_ENTIREBUFFER)
938 writebytes = this->buflen;
939 if (writebytes > this->buflen)
940 writebytes = this->buflen;
942 assert(audiobytes1!=audiobytes2);
943 assert(lplpaudioptr1!=lplpaudioptr2);
944 if (writecursor+writebytes <= this->buflen) {
945 *(LPBYTE*)lplpaudioptr1 = this->buffer+writecursor;
946 *audiobytes1 = writebytes;
947 if (lplpaudioptr2)
948 *(LPBYTE*)lplpaudioptr2 = NULL;
949 if (audiobytes2)
950 *audiobytes2 = 0;
951 TRACE(dsound,"->%ld.0\n",writebytes);
952 } else {
953 *(LPBYTE*)lplpaudioptr1 = this->buffer+writecursor;
954 *audiobytes1 = this->buflen-writecursor;
955 if (lplpaudioptr2)
956 *(LPBYTE*)lplpaudioptr2 = this->buffer;
957 if (audiobytes2)
958 *audiobytes2 = writebytes-(this->buflen-writecursor);
959 TRACE(dsound,"->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
961 /* No. See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 21 */
962 /* this->writepos=(writecursor+writebytes)%this->buflen; */
963 return DS_OK;
966 static HRESULT WINAPI IDirectSoundBuffer_SetCurrentPosition(
967 LPDIRECTSOUNDBUFFER this,DWORD newpos
969 TRACE(dsound,"(%p,%ld)\n",this,newpos);
971 /* **** */
972 EnterCriticalSection(&(this->lock));
974 this->playpos = newpos;
976 LeaveCriticalSection(&(this->lock));
977 /* **** */
979 return 0;
982 static HRESULT WINAPI IDirectSoundBuffer_SetPan(
983 LPDIRECTSOUNDBUFFER this,LONG pan
985 double temp;
987 TRACE(dsound,"(%p,%ld)\n",this,pan);
989 if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT))
990 return DSERR_INVALIDPARAM;
992 /* You cannot set the pan of the primary buffer */
993 /* and you cannot use both pan and 3D controls */
994 if (!(this->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
995 (this->dsbd.dwFlags & DSBCAPS_CTRL3D) ||
996 (this->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
997 return DSERR_CONTROLUNAVAIL;
999 /* **** */
1000 EnterCriticalSection(&(this->lock));
1002 this->pan = pan;
1004 temp = (double) (this->volume - (this->pan > 0 ? this->pan : 0));
1005 this->lVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 32768.0);
1006 temp = (double) (this->volume + (this->pan < 0 ? this->pan : 0));
1007 this->rVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 32768.0);
1009 LeaveCriticalSection(&(this->lock));
1010 /* **** */
1012 return DS_OK;
1015 static HRESULT WINAPI IDirectSoundBuffer_GetPan(
1016 LPDIRECTSOUNDBUFFER this,LPLONG pan
1018 TRACE(dsound,"(%p,%p)\n",this,pan);
1020 if (pan == NULL)
1021 return DSERR_INVALIDPARAM;
1023 *pan = this->pan;
1025 return DS_OK;
1028 static HRESULT WINAPI IDirectSoundBuffer_Unlock(
1029 LPDIRECTSOUNDBUFFER this,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
1031 TRACE(dsound,"(%p,%p,%ld,%p,%ld):stub\n", this,p1,x1,p2,x2);
1033 /* There is really nothing to do here. Should someone */
1034 /* choose to implement static buffers in hardware (by */
1035 /* using a wave table synth, for example) this is where */
1036 /* you'd want to do the loading. For software buffers, */
1037 /* which is what we currently use, we need do nothing. */
1039 #if 0
1040 /* It's also the place to pre-process 3D buffers... */
1042 /* This is highly experimental and liable to break things */
1043 if (this->dsbd.dwFlags & DSBCAPS_CTRL3D)
1044 DSOUND_Create3DBuffer(this);
1045 #endif
1047 return DS_OK;
1050 static HRESULT WINAPI IDirectSoundBuffer_GetFrequency(
1051 LPDIRECTSOUNDBUFFER this,LPDWORD freq
1053 TRACE(dsound,"(%p,%p)\n",this,freq);
1055 if (freq == NULL)
1056 return DSERR_INVALIDPARAM;
1058 *freq = this->freq;
1060 return DS_OK;
1063 static HRESULT WINAPI IDirectSoundBuffer_Initialize(
1064 LPDIRECTSOUNDBUFFER this,LPDIRECTSOUND dsound,LPDSBUFFERDESC dbsd
1066 FIXME(dsound,"(%p,%p,%p):stub\n",this,dsound,dbsd);
1067 printf("Re-Init!!!\n");
1068 return DSERR_ALREADYINITIALIZED;
1071 static HRESULT WINAPI IDirectSoundBuffer_GetCaps(
1072 LPDIRECTSOUNDBUFFER this,LPDSBCAPS caps
1074 TRACE(dsound,"(%p)->(%p)\n",this,caps);
1076 if (caps == NULL)
1077 return DSERR_INVALIDPARAM;
1079 /* I think we should check this value, not set it. See */
1080 /* Inside DirectX, p215. That should apply here, too. */
1081 caps->dwSize = sizeof(*caps);
1083 caps->dwFlags = this->dsbd.dwFlags | DSBCAPS_LOCSOFTWARE;
1084 caps->dwBufferBytes = this->dsbd.dwBufferBytes;
1085 /* This value represents the speed of the "unlock" command.
1086 As unlock is quite fast (it does not do anything), I put
1087 4096 ko/s = 4 Mo / s */
1088 caps->dwUnlockTransferRate = 4096;
1089 caps->dwPlayCpuOverhead = 0;
1091 return DS_OK;
1094 static HRESULT WINAPI IDirectSoundBuffer_QueryInterface(
1095 LPDIRECTSOUNDBUFFER this,REFIID riid,LPVOID *ppobj
1097 char xbuf[50];
1099 WINE_StringFromCLSID(riid,xbuf);
1100 TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
1102 if (!memcmp(&IID_IDirectSoundNotify,riid,sizeof(*riid))) {
1103 IDirectSoundNotify *dsn;
1105 dsn = (LPDIRECTSOUNDNOTIFY)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn));
1106 dsn->ref = 1;
1107 dsn->dsb = this;
1108 this->lpvtbl->fnAddRef(this);
1109 dsn->lpvtbl = &dsnvt;
1110 *ppobj = (LPVOID)dsn;
1111 return 0;
1114 if (!memcmp(&IID_IDirectSound3DBuffer,riid,sizeof(*riid))) {
1115 *ppobj = this->ds3db;
1116 if (*ppobj)
1117 return DS_OK;
1120 return E_FAIL;
1123 static struct tagLPDIRECTSOUNDBUFFER_VTABLE dsbvt = {
1124 IDirectSoundBuffer_QueryInterface,
1125 IDirectSoundBuffer_AddRef,
1126 IDirectSoundBuffer_Release,
1127 IDirectSoundBuffer_GetCaps,
1128 IDirectSoundBuffer_GetCurrentPosition,
1129 IDirectSoundBuffer_GetFormat,
1130 IDirectSoundBuffer_GetVolume,
1131 IDirectSoundBuffer_GetPan,
1132 IDirectSoundBuffer_GetFrequency,
1133 IDirectSoundBuffer_GetStatus,
1134 IDirectSoundBuffer_Initialize,
1135 IDirectSoundBuffer_Lock,
1136 IDirectSoundBuffer_Play,
1137 IDirectSoundBuffer_SetCurrentPosition,
1138 IDirectSoundBuffer_SetFormat,
1139 IDirectSoundBuffer_SetVolume,
1140 IDirectSoundBuffer_SetPan,
1141 IDirectSoundBuffer_SetFrequency,
1142 IDirectSoundBuffer_Stop,
1143 IDirectSoundBuffer_Unlock
1146 /*******************************************************************************
1147 * IDirectSound
1150 static HRESULT WINAPI IDirectSound_SetCooperativeLevel(
1151 LPDIRECTSOUND this,HWND hwnd,DWORD level
1153 FIXME(dsound,"(%p,%08lx,%ld):stub\n",this,(DWORD)hwnd,level);
1154 return 0;
1157 static HRESULT WINAPI IDirectSound_CreateSoundBuffer(
1158 LPDIRECTSOUND this,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER ppdsb,LPUNKNOWN lpunk
1160 LPWAVEFORMATEX wfex;
1162 TRACE(dsound,"(%p,%p,%p,%p)\n",this,dsbd,ppdsb,lpunk);
1164 if ((this == NULL) || (dsbd == NULL) || (ppdsb == NULL))
1165 return DSERR_INVALIDPARAM;
1167 if (TRACE_ON(dsound)) {
1168 TRACE(dsound,"(size=%ld)\n",dsbd->dwSize);
1169 TRACE(dsound,"(flags=0x%08lx\n",dsbd->dwFlags);
1170 _dump_DSBCAPS(dsbd->dwFlags);
1171 TRACE(dsound,"(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
1172 TRACE(dsound,"(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
1175 wfex = dsbd->lpwfxFormat;
1177 if (wfex)
1178 TRACE(dsound,"(formattag=0x%04x,chans=%d,samplerate=%ld"
1179 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1180 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
1181 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
1182 wfex->wBitsPerSample, wfex->cbSize);
1184 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
1185 if (primarybuf) {
1186 primarybuf->lpvtbl->fnAddRef(primarybuf);
1187 *ppdsb = primarybuf;
1188 return DS_OK;
1189 } /* Else create primarybuf */
1192 *ppdsb = (LPDIRECTSOUNDBUFFER)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBuffer));
1193 if (*ppdsb == NULL)
1194 return DSERR_OUTOFMEMORY;
1195 (*ppdsb)->ref = 1;
1197 TRACE(dsound, "Created buffer at %p\n", *ppdsb);
1199 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
1200 (*ppdsb)->buflen = dsound->wfx.nAvgBytesPerSec;
1201 (*ppdsb)->freq = dsound->wfx.nSamplesPerSec;
1202 } else {
1203 (*ppdsb)->buflen = dsbd->dwBufferBytes;
1204 (*ppdsb)->freq = dsbd->lpwfxFormat->nSamplesPerSec;
1206 (*ppdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,(*ppdsb)->buflen);
1207 if ((*ppdsb)->buffer == NULL) {
1208 HeapFree(GetProcessHeap(),0,(*ppdsb));
1209 *ppdsb = NULL;
1210 return DSERR_OUTOFMEMORY;
1212 /* It's not necessary to initialize values to zero since */
1213 /* we allocated this structure with HEAP_ZERO_MEMORY... */
1214 (*ppdsb)->playpos = 0;
1215 (*ppdsb)->writepos = 0;
1216 (*ppdsb)->parent = NULL;
1217 (*ppdsb)->lpvtbl = &dsbvt;
1218 (*ppdsb)->dsound = this;
1219 (*ppdsb)->playing = 0;
1220 (*ppdsb)->lVolAdjust = (1 << 15);
1221 (*ppdsb)->rVolAdjust = (1 << 15);
1223 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
1224 (*ppdsb)->freqAdjust = ((*ppdsb)->freq << DSOUND_FREQSHIFT) /
1225 primarybuf->wfx.nSamplesPerSec;
1226 (*ppdsb)->nAvgBytesPerSec = (*ppdsb)->freq *
1227 dsbd->lpwfxFormat->nBlockAlign;
1230 memcpy(&((*ppdsb)->dsbd),dsbd,sizeof(*dsbd));
1232 /* register buffer */
1233 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
1234 this->buffers = (LPDIRECTSOUNDBUFFER*)HeapReAlloc(GetProcessHeap(),0,this->buffers,sizeof(LPDIRECTSOUNDBUFFER)*(this->nrofbuffers+1));
1235 this->buffers[this->nrofbuffers] = *ppdsb;
1236 this->nrofbuffers++;
1238 this->lpvtbl->fnAddRef(this);
1240 if (dsbd->lpwfxFormat)
1241 memcpy(&((*ppdsb)->wfx), dsbd->lpwfxFormat, sizeof((*ppdsb)->wfx));
1243 InitializeCriticalSection(&((*ppdsb)->lock));
1245 #if USE_DSOUND3D
1246 if (dsbd->dwFlags & DSBCAPS_CTRL3D) {
1247 IDirectSound3DBuffer *ds3db;
1249 ds3db = (LPDIRECTSOUND3DBUFFER)HeapAlloc(GetProcessHeap(),
1250 0,sizeof(*ds3db));
1251 ds3db->ref = 1;
1252 ds3db->dsb = (*ppdsb);
1253 ds3db->lpvtbl = &ds3dbvt;
1254 (*ppdsb)->ds3db = ds3db;
1255 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
1256 ds3db->ds3db.vPosition.x.x = 0.0;
1257 ds3db->ds3db.vPosition.y.y = 0.0;
1258 ds3db->ds3db.vPosition.z.z = 0.0;
1259 ds3db->ds3db.vVelocity.x.x = 0.0;
1260 ds3db->ds3db.vVelocity.y.y = 0.0;
1261 ds3db->ds3db.vVelocity.z.z = 0.0;
1262 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
1263 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
1264 ds3db->ds3db.vConeOrientation.x.x = 0.0;
1265 ds3db->ds3db.vConeOrientation.y.y = 0.0;
1266 ds3db->ds3db.vConeOrientation.z.z = 0.0;
1267 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME;
1268 ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
1269 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
1270 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
1271 ds3db->buflen = ((*ppdsb)->buflen * primarybuf->wfx.nBlockAlign) /
1272 (*ppdsb)->wfx.nBlockAlign;
1273 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
1274 if (ds3db->buffer == NULL) {
1275 ds3db->buflen = 0;
1276 ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
1279 #endif
1280 return DS_OK;
1283 static HRESULT WINAPI IDirectSound_DuplicateSoundBuffer(
1284 LPDIRECTSOUND this,LPDIRECTSOUNDBUFFER pdsb,LPLPDIRECTSOUNDBUFFER ppdsb
1286 TRACE(dsound,"(%p,%p,%p)\n",this,pdsb,ppdsb);
1288 *ppdsb = (LPDIRECTSOUNDBUFFER)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBuffer));
1290 pdsb->lpvtbl->fnAddRef(pdsb);
1291 memcpy(*ppdsb, pdsb, sizeof(IDirectSoundBuffer));
1292 (*ppdsb)->ref = 1;
1293 (*ppdsb)->playpos = 0;
1294 (*ppdsb)->writepos = 0;
1295 (*ppdsb)->dsound = this;
1296 (*ppdsb)->parent = pdsb;
1297 memcpy(&((*ppdsb)->wfx), &(pdsb->wfx), sizeof((*ppdsb)->wfx));
1298 /* register buffer */
1299 this->buffers = (LPDIRECTSOUNDBUFFER*)HeapReAlloc(GetProcessHeap(),0,this->buffers,sizeof(LPDIRECTSOUNDBUFFER)*(this->nrofbuffers+1));
1300 this->buffers[this->nrofbuffers] = *ppdsb;
1301 this->nrofbuffers++;
1302 this->lpvtbl->fnAddRef(this);
1303 return 0;
1307 static HRESULT WINAPI IDirectSound_GetCaps(LPDIRECTSOUND this,LPDSCAPS caps) {
1308 TRACE(dsound,"(%p,%p)\n",this,caps);
1309 TRACE(dsound,"(flags=0x%08lx)\n",caps->dwFlags);
1311 if (caps == NULL)
1312 return DSERR_INVALIDPARAM;
1314 /* We should check this value, not set it. See Inside DirectX, p215. */
1315 caps->dwSize = sizeof(*caps);
1317 caps->dwFlags =
1318 DSCAPS_PRIMARYSTEREO |
1319 DSCAPS_PRIMARY16BIT |
1320 DSCAPS_SECONDARYSTEREO |
1321 DSCAPS_SECONDARY16BIT |
1322 DSCAPS_CONTINUOUSRATE;
1323 /* FIXME: query OSS */
1324 caps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
1325 caps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
1327 caps->dwPrimaryBuffers = 1;
1329 caps->dwMaxHwMixingAllBuffers = 0;
1330 caps->dwMaxHwMixingStaticBuffers = 0;
1331 caps->dwMaxHwMixingStreamingBuffers = 0;
1333 caps->dwFreeHwMixingAllBuffers = 0;
1334 caps->dwFreeHwMixingStaticBuffers = 0;
1335 caps->dwFreeHwMixingStreamingBuffers = 0;
1337 caps->dwMaxHw3DAllBuffers = 0;
1338 caps->dwMaxHw3DStaticBuffers = 0;
1339 caps->dwMaxHw3DStreamingBuffers = 0;
1341 caps->dwFreeHw3DAllBuffers = 0;
1342 caps->dwFreeHw3DStaticBuffers = 0;
1343 caps->dwFreeHw3DStreamingBuffers = 0;
1345 caps->dwTotalHwMemBytes = 0;
1347 caps->dwFreeHwMemBytes = 0;
1349 caps->dwMaxContigFreeHwMemBytes = 0;
1351 caps->dwUnlockTransferRateHwBuffers = 4096; /* But we have none... */
1353 caps->dwPlayCpuOverheadSwBuffers = 1; /* 1% */
1355 return 0;
1358 static ULONG WINAPI IDirectSound_AddRef(LPDIRECTSOUND this) {
1359 return ++(this->ref);
1362 static ULONG WINAPI IDirectSound_Release(LPDIRECTSOUND this) {
1363 TRACE(dsound,"(%p), ref was %ld\n",this,this->ref);
1364 if (!--(this->ref)) {
1365 DSOUND_CloseAudio();
1366 while(IDirectSoundBuffer_Release(primarybuf)); /* Deallocate */
1367 FIXME(dsound, "need to release all buffers!\n");
1368 HeapFree(GetProcessHeap(),0,this);
1369 dsound = NULL;
1370 return 0;
1372 return this->ref;
1375 static HRESULT WINAPI IDirectSound_SetSpeakerConfig(
1376 LPDIRECTSOUND this,DWORD config
1378 FIXME(dsound,"(%p,0x%08lx):stub\n",this,config);
1379 return 0;
1382 static HRESULT WINAPI IDirectSound_QueryInterface(
1383 LPDIRECTSOUND this,REFIID riid,LPVOID *ppobj
1385 char xbuf[50];
1387 if (!memcmp(&IID_IDirectSound3DListener,riid,sizeof(*riid))) {
1389 if (this->listener) {
1390 *ppobj = this->listener;
1391 return DS_OK;
1393 this->listener = (LPDIRECTSOUND3DLISTENER)HeapAlloc(
1394 GetProcessHeap(), 0, sizeof(*(this->listener)));
1395 this->listener->ref = 1;
1396 this->listener->lpvtbl = &ds3dlvt;
1397 this->lpvtbl->fnAddRef(this);
1398 this->listener->ds3dl.dwSize = sizeof(DS3DLISTENER);
1399 this->listener->ds3dl.vPosition.x.x = 0.0;
1400 this->listener->ds3dl.vPosition.y.y = 0.0;
1401 this->listener->ds3dl.vPosition.z.z = 0.0;
1402 this->listener->ds3dl.vVelocity.x.x = 0.0;
1403 this->listener->ds3dl.vVelocity.y.y = 0.0;
1404 this->listener->ds3dl.vVelocity.z.z = 0.0;
1405 this->listener->ds3dl.vOrientFront.x.x = 0.0;
1406 this->listener->ds3dl.vOrientFront.y.y = 0.0;
1407 this->listener->ds3dl.vOrientFront.z.z = 1.0;
1408 this->listener->ds3dl.vOrientTop.x.x = 0.0;
1409 this->listener->ds3dl.vOrientTop.y.y = 1.0;
1410 this->listener->ds3dl.vOrientTop.z.z = 0.0;
1411 this->listener->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
1412 this->listener->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
1413 this->listener->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
1414 *ppobj = (LPVOID)this->listener;
1415 return DS_OK;
1418 WINE_StringFromCLSID(riid,xbuf);
1419 TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
1420 return E_FAIL;
1423 static HRESULT WINAPI IDirectSound_Compact(
1424 LPDIRECTSOUND this)
1426 TRACE(dsound, "(%p)\n", this);
1427 return DS_OK;
1430 static HRESULT WINAPI IDirectSound_GetSpeakerConfig(
1431 LPDIRECTSOUND this,
1432 LPDWORD lpdwSpeakerConfig)
1434 TRACE(dsound, "(%p, %p)\n", this, lpdwSpeakerConfig);
1435 *lpdwSpeakerConfig = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16);
1436 return DS_OK;
1439 static HRESULT WINAPI IDirectSound_Initialize(
1440 LPDIRECTSOUND this,
1441 LPGUID lpGuid)
1443 TRACE(dsound, "(%p, %p)\n", this, lpGuid);
1444 return DS_OK;
1447 static struct tagLPDIRECTSOUND_VTABLE dsvt = {
1448 IDirectSound_QueryInterface,
1449 IDirectSound_AddRef,
1450 IDirectSound_Release,
1451 IDirectSound_CreateSoundBuffer,
1452 IDirectSound_GetCaps,
1453 IDirectSound_DuplicateSoundBuffer,
1454 IDirectSound_SetCooperativeLevel,
1455 IDirectSound_Compact,
1456 IDirectSound_GetSpeakerConfig,
1457 IDirectSound_SetSpeakerConfig,
1458 IDirectSound_Initialize
1462 /* See http://www.opensound.com/pguide/audio.html for more details */
1464 static int
1465 DSOUND_setformat(LPWAVEFORMATEX wfex) {
1466 int xx,channels,speed,format,nformat;
1468 if (!audioOK) {
1469 TRACE(dsound, "(%p) deferred\n", wfex);
1470 return 0;
1472 switch (wfex->wFormatTag) {
1473 default:
1474 WARN(dsound,"unknown WAVE_FORMAT tag %d\n",wfex->wFormatTag);
1475 return DSERR_BADFORMAT;
1476 case WAVE_FORMAT_PCM:
1477 break;
1479 if (wfex->wBitsPerSample==8)
1480 format = AFMT_U8;
1481 else
1482 format = AFMT_S16_LE;
1484 if (-1==ioctl(audiofd,SNDCTL_DSP_GETFMTS,&xx)) {
1485 perror("ioctl SNDCTL_DSP_GETFMTS");
1486 return -1;
1488 if ((xx&format)!=format) {/* format unsupported */
1489 FIXME(dsound,"SNDCTL_DSP_GETFMTS: format not supported\n");
1490 return -1;
1492 nformat = format;
1493 if (-1==ioctl(audiofd,SNDCTL_DSP_SETFMT,&nformat)) {
1494 perror("ioctl SNDCTL_DSP_SETFMT");
1495 return -1;
1497 if (nformat!=format) {/* didn't work */
1498 FIXME(dsound,"SNDCTL_DSP_GETFMTS: format not set\n");
1499 return -1;
1502 channels = wfex->nChannels-1;
1503 if (-1==ioctl(audiofd,SNDCTL_DSP_STEREO,&channels)) {
1504 perror("ioctl SNDCTL_DSP_STEREO");
1505 return -1;
1507 speed = wfex->nSamplesPerSec;
1508 if (-1==ioctl(audiofd,SNDCTL_DSP_SPEED,&speed)) {
1509 perror("ioctl SNDCTL_DSP_SPEED");
1510 return -1;
1512 TRACE(dsound,"(freq=%ld,channels=%d,bits=%d)\n",
1513 wfex->nSamplesPerSec,wfex->nChannels,wfex->wBitsPerSample
1515 return 0;
1518 static void DSOUND_CheckEvent(IDirectSoundBuffer *dsb, int len)
1520 int i;
1521 DWORD offset;
1522 LPDSBPOSITIONNOTIFY event;
1524 if (dsb->nrofnotifies == 0)
1525 return;
1527 TRACE(dsound,"(%p) buflen = %ld, playpos = %ld, len = %d\n",
1528 dsb, dsb->buflen, dsb->playpos, len);
1529 for (i = 0; i < dsb->nrofnotifies ; i++) {
1530 event = dsb->notifies + i;
1531 offset = event->dwOffset;
1532 TRACE(dsound, "checking %d, position %ld, event = %d\n",
1533 i, offset, event->hEventNotify);
1534 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
1535 /* OK. [Inside DirectX, p274] */
1536 /* */
1537 /* This also means we can't sort the entries by offset, */
1538 /* because DSBPN_OFFSETSTOP == -1 */
1539 if (offset == DSBPN_OFFSETSTOP) {
1540 if (dsb->playing == 0) {
1541 SetEvent(event->hEventNotify);
1542 TRACE(dsound,"signalled event %d (%d)\n", event->hEventNotify, i);
1543 return;
1544 } else
1545 return;
1547 if ((dsb->playpos + len) >= dsb->buflen) {
1548 if ((offset < ((dsb->playpos + len) % dsb->buflen)) ||
1549 (offset >= dsb->playpos)) {
1550 TRACE(dsound,"signalled event %d (%d)\n", event->hEventNotify, i);
1551 SetEvent(event->hEventNotify);
1553 } else {
1554 if ((offset >= dsb->playpos) && (offset < (dsb->playpos + len))) {
1555 TRACE(dsound,"signalled event %d (%d)\n", event->hEventNotify, i);
1556 SetEvent(event->hEventNotify);
1562 /* WAV format info can be found at: */
1563 /* */
1564 /* http://www.cwi.nl/ftp/audio/AudioFormats.part2 */
1565 /* ftp://ftp.cwi.nl/pub/audio/RIFF-format */
1566 /* */
1567 /* Import points to remember: */
1568 /* */
1569 /* 8-bit WAV is unsigned */
1570 /* 16-bit WAV is signed */
1572 static inline INT16 cvtU8toS16(BYTE byte)
1574 INT16 s = (byte - 128) << 8;
1576 return s;
1579 static inline BYTE cvtS16toU8(INT16 word)
1581 BYTE b = (word + 32768) >> 8;
1583 return b;
1587 /* We should be able to optimize these two inline functions */
1588 /* so that we aren't doing 8->16->8 conversions when it is */
1589 /* not necessary. But this is still a WIP. Optimize later. */
1590 static inline void get_fields(const IDirectSoundBuffer *dsb, BYTE *buf, INT *fl, INT *fr)
1592 INT16 *bufs = (INT16 *) buf;
1594 /* TRACE(dsound, "(%p)", buf); */
1595 if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 2) {
1596 *fl = cvtU8toS16(*buf);
1597 *fr = cvtU8toS16(*(buf + 1));
1598 return;
1601 if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 2) {
1602 *fl = *bufs;
1603 *fr = *(bufs + 1);
1604 return;
1607 if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 1) {
1608 *fl = cvtU8toS16(*buf);
1609 *fr = *fl;
1610 return;
1613 if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 1) {
1614 *fl = *bufs;
1615 *fr = *bufs;
1616 return;
1619 FIXME(dsound, "get_fields found an unsupported configuration\n");
1620 return;
1623 static inline void set_fields(BYTE *buf, INT fl, INT fr)
1625 INT16 *bufs = (INT16 *) buf;
1627 if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 2)) {
1628 *buf = cvtS16toU8(fl);
1629 *(buf + 1) = cvtS16toU8(fr);
1630 return;
1633 if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 2)) {
1634 *bufs = fl;
1635 *(bufs + 1) = fr;
1636 return;
1639 if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 1)) {
1640 *buf = cvtS16toU8((fl + fr) >> 1);
1641 return;
1644 if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 1)) {
1645 *bufs = (fl + fr) >> 1;
1646 return;
1648 FIXME(dsound, "set_fields found an unsupported configuration\n");
1649 return;
1652 /* Now with PerfectPitch (tm) technology */
1653 static INT DSOUND_MixerNorm(IDirectSoundBuffer *dsb, BYTE *buf, INT len)
1655 INT i, size, ipos, ilen, fieldL, fieldR;
1656 BYTE *ibp, *obp;
1657 INT iAdvance = dsb->wfx.nBlockAlign;
1658 INT oAdvance = primarybuf->wfx.nBlockAlign;
1660 ibp = dsb->buffer + dsb->playpos;
1661 obp = buf;
1663 TRACE(dsound, "(%p, %p, %p), playpos=%8.8lx\n", dsb, ibp, obp, dsb->playpos);
1664 /* Check for the best case */
1665 if ((dsb->freq == primarybuf->wfx.nSamplesPerSec) &&
1666 (dsb->wfx.wBitsPerSample == primarybuf->wfx.wBitsPerSample) &&
1667 (dsb->wfx.nChannels == primarybuf->wfx.nChannels)) {
1668 TRACE(dsound, "(%p) Best case\n", dsb);
1669 if ((ibp + len) < (BYTE *)(dsb->buffer + dsb->buflen))
1670 memcpy(obp, ibp, len);
1671 else { /* wrap */
1672 memcpy(obp, ibp, dsb->buflen - dsb->playpos);
1673 memcpy(obp + (dsb->buflen - dsb->playpos),
1674 dsb->buffer,
1675 len - (dsb->buflen - dsb->playpos));
1677 return len;
1680 /* Check for same sample rate */
1681 if (dsb->freq == primarybuf->wfx.nSamplesPerSec) {
1682 TRACE(dsound, "(%p) Same sample rate %ld = primary %ld\n", dsb,
1683 dsb->freq, primarybuf->wfx.nSamplesPerSec);
1684 ilen = 0;
1685 for (i = 0; i < len; i += oAdvance) {
1686 get_fields(dsb, ibp, &fieldL, &fieldR);
1687 ibp += iAdvance;
1688 ilen += iAdvance;
1689 set_fields(obp, fieldL, fieldR);
1690 obp += oAdvance;
1691 if (ibp >= (BYTE *)(dsb->buffer + dsb->buflen))
1692 ibp = dsb->buffer; /* wrap */
1694 return (ilen);
1697 /* Mix in different sample rates */
1698 /* */
1699 /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
1700 /* Patent Pending :-] */
1702 TRACE(dsound, "(%p) Adjusting frequency: %ld -> %ld\n",
1703 dsb, dsb->freq, primarybuf->wfx.nSamplesPerSec);
1705 size = len / oAdvance;
1706 ilen = ((size * dsb->freqAdjust) >> DSOUND_FREQSHIFT) * iAdvance;
1707 for (i = 0; i < size; i++) {
1709 ipos = (((i * dsb->freqAdjust) >> DSOUND_FREQSHIFT) * iAdvance) + dsb->playpos;
1711 if (ipos >= dsb->buflen)
1712 ipos %= dsb->buflen; /* wrap */
1714 get_fields(dsb, (dsb->buffer + ipos), &fieldL, &fieldR);
1715 set_fields(obp, fieldL, fieldR);
1716 obp += oAdvance;
1718 return ilen;
1721 static void DSOUND_MixerVol(IDirectSoundBuffer *dsb, BYTE *buf, INT len)
1723 INT i, inc = primarybuf->wfx.wBitsPerSample >> 3;
1724 BYTE *bpc = buf;
1725 INT16 *bps = (INT16 *) buf;
1727 TRACE(dsound, "(%p) left = %lx, right = %lx\n", dsb,
1728 dsb->lVolAdjust, dsb->rVolAdjust);
1729 if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->pan == 0)) &&
1730 (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->volume == 0)) &&
1731 !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
1732 return; /* Nothing to do */
1734 /* If we end up with some bozo coder using panning or 3D sound */
1735 /* with a mono primary buffer, it could sound very weird using */
1736 /* this method. Oh well, tough patooties. */
1738 for (i = 0; i < len; i += inc) {
1739 INT val;
1741 switch (inc) {
1743 case 1:
1744 /* 8-bit WAV is unsigned, but we need to operate */
1745 /* on signed data for this to work properly */
1746 val = *bpc - 128;
1747 val = ((val * (i & inc ? dsb->rVolAdjust : dsb->lVolAdjust)) >> 15);
1748 *bpc = val + 128;
1749 bpc++;
1750 break;
1751 case 2:
1752 /* 16-bit WAV is signed -- much better */
1753 val = *bps;
1754 val = ((val * ((i & inc) ? dsb->rVolAdjust : dsb->lVolAdjust)) >> 15);
1755 *bps = val;
1756 bps++;
1757 break;
1758 default:
1759 /* Very ugly! */
1760 FIXME(dsound, "MixerVol had a nasty error\n");
1765 #ifdef USE_DSOUND3D
1766 static void DSOUND_Mixer3D(IDirectSoundBuffer *dsb, BYTE *buf, INT len)
1768 BYTE *ibp, *obp;
1769 DWORD buflen, playpos;
1771 buflen = dsb->ds3db->buflen;
1772 playpos = (dsb->playpos * primarybuf->wfx.nBlockAlign) / dsb->wfx.nBlockAlign;
1773 ibp = dsb->ds3db->buffer + playpos;
1774 obp = buf;
1776 if (playpos > buflen) {
1777 FIXME(dsound, "Major breakage");
1778 return;
1781 if (len <= (playpos + buflen))
1782 memcpy(obp, ibp, len);
1783 else { /* wrap */
1784 memcpy(obp, ibp, buflen - playpos);
1785 memcpy(obp + (buflen - playpos),
1786 dsb->buffer,
1787 len - (buflen - playpos));
1789 return;
1791 #endif
1793 static DWORD DSOUND_MixInBuffer(IDirectSoundBuffer *dsb)
1795 INT i, len, ilen, temp, field;
1796 INT advance = primarybuf->wfx.wBitsPerSample >> 3;
1797 BYTE *buf, *ibuf, *obuf;
1798 INT16 *ibufs, *obufs;
1800 len = DSOUND_FRAGLEN; /* The most we will use */
1801 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
1802 temp = MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buflen,
1803 dsb->nAvgBytesPerSec) -
1804 MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->playpos,
1805 dsb->nAvgBytesPerSec);
1806 len = (len > temp) ? temp : len;
1808 len &= ~3; /* 4 byte alignment */
1810 if (len == 0) {
1811 /* This should only happen if we aren't looping and temp < 4 */
1813 /* We skip the remainder, so check for possible events */
1814 DSOUND_CheckEvent(dsb, dsb->buflen - dsb->playpos);
1815 /* Stop */
1816 dsb->playing = 0;
1817 dsb->writepos = 0;
1818 dsb->playpos = 0;
1819 /* Check for DSBPN_OFFSETSTOP */
1820 DSOUND_CheckEvent(dsb, 0);
1821 return 0;
1824 /* Been seeing segfaults in malloc() for some reason... */
1825 TRACE(dsound, "allocating buffer (size = %d)\n", len);
1826 if ((buf = ibuf = (BYTE *) malloc(len)) == NULL)
1827 return 0;
1829 TRACE(dsound, "MixInBuffer (%p) len = %d\n", dsb, len);
1831 ilen = DSOUND_MixerNorm(dsb, ibuf, len);
1832 if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
1833 (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
1834 DSOUND_MixerVol(dsb, ibuf, len);
1836 obuf = primarybuf->buffer + primarybuf->playpos;
1837 for (i = 0; i < len; i += advance) {
1838 obufs = (INT16 *) obuf;
1839 ibufs = (INT16 *) ibuf;
1840 if (primarybuf->wfx.wBitsPerSample == 8) {
1841 /* 8-bit WAV is unsigned */
1842 field = (*ibuf - 128);
1843 field += (*obuf - 128);
1844 field = field > 127 ? 127 : field;
1845 field = field < -128 ? -128 : field;
1846 *obuf = field + 128;
1847 } else {
1848 /* 16-bit WAV is signed */
1849 field = *ibufs;
1850 field += *obufs;
1851 field = field > 32767 ? 32767 : field;
1852 field = field < -32768 ? -32768 : field;
1853 *obufs = field;
1855 ibuf += advance;
1856 obuf += advance;
1857 if (obuf >= (BYTE *)(primarybuf->buffer + primarybuf->buflen))
1858 obuf = primarybuf->buffer;
1860 free(buf);
1862 if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY)
1863 DSOUND_CheckEvent(dsb, ilen);
1865 dsb->playpos += ilen;
1866 dsb->writepos = dsb->playpos + ilen;
1868 if (dsb->playpos >= dsb->buflen) {
1869 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
1870 dsb->playing = 0;
1871 dsb->writepos = 0;
1872 dsb->playpos = 0;
1873 DSOUND_CheckEvent(dsb, 0); /* For DSBPN_OFFSETSTOP */
1874 } else
1875 dsb->playpos %= dsb->buflen; /* wrap */
1878 if (dsb->writepos >= dsb->buflen)
1879 dsb->writepos %= dsb->buflen;
1881 return len;
1884 static DWORD WINAPI DSOUND_MixPrimary(void)
1886 INT i, len, maxlen = 0;
1887 IDirectSoundBuffer *dsb;
1889 for (i = dsound->nrofbuffers - 1; i >= 0; i--) {
1890 dsb = dsound->buffers[i];
1892 if (!dsb || !(dsb->lpvtbl))
1893 continue;
1894 dsb->lpvtbl->fnAddRef(dsb);
1895 if (dsb->buflen && dsb->playing) {
1896 EnterCriticalSection(&(dsb->lock));
1897 len = DSOUND_MixInBuffer(dsb);
1898 maxlen = len > maxlen ? len : maxlen;
1899 LeaveCriticalSection(&(dsb->lock));
1901 dsb->lpvtbl->fnRelease(dsb);
1904 return maxlen;
1907 static int DSOUND_OpenAudio(void)
1909 int audioFragment;
1911 if (primarybuf == NULL)
1912 return DSERR_OUTOFMEMORY;
1914 while (audiofd != -1)
1915 sleep(5);
1916 audiofd = open("/dev/audio",O_WRONLY);
1917 if (audiofd==-1) {
1918 /* Don't worry if sound is busy at the moment */
1919 if (errno != EBUSY)
1920 perror("open /dev/audio");
1921 return audiofd; /* -1 */
1924 /* We should probably do something here if SETFRAGMENT fails... */
1925 audioFragment=0x0002000c;
1926 if (-1==ioctl(audiofd,SNDCTL_DSP_SETFRAGMENT,&audioFragment))
1927 perror("ioctl SETFRAGMENT");
1929 audioOK = 1;
1930 DSOUND_setformat(&(primarybuf->wfx));
1932 return 0;
1935 static void DSOUND_CloseAudio(void)
1937 int neutral;
1939 neutral = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
1940 audioOK = 0; /* race condition */
1941 Sleep(5);
1942 /* It's possible we've been called with audio closed */
1943 /* from SetFormat()... this is just to force a call */
1944 /* to OpenAudio() to reset the hardware properly */
1945 if (audiofd != -1)
1946 close(audiofd);
1947 primarybuf->playpos = 0;
1948 primarybuf->writepos = DSOUND_FRAGLEN;
1949 memset(primarybuf->buffer, neutral, primarybuf->buflen);
1950 audiofd = -1;
1951 TRACE(dsound, "Audio stopped\n");
1954 static int DSOUND_WriteAudio(char *buf, int len)
1956 int result, left = 0;
1958 while (left < len) {
1959 result = write(audiofd, buf + left, len - left);
1960 if (result == -1) {
1961 if (errno == EINTR)
1962 continue;
1963 else
1964 return result;
1966 left += result;
1968 return 0;
1971 static void DSOUND_OutputPrimary(int len)
1973 int neutral, flen1, flen2;
1974 char *frag1, *frag2;
1976 /* This is a bad place for this. We need to clear the */
1977 /* buffer with a neutral value, for unsigned 8-bit WAVE */
1978 /* that's 128, for signed 16-bit it's 0 */
1979 neutral = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
1981 /* **** */
1982 EnterCriticalSection(&(primarybuf->lock));
1984 /* Write out the */
1985 if ((audioOK == 1) || (DSOUND_OpenAudio() == 0)) {
1986 if (primarybuf->playpos + len >= primarybuf->buflen) {
1987 frag1 = primarybuf->buffer + primarybuf->playpos;
1988 flen1 = primarybuf->buflen - primarybuf->playpos;
1989 frag2 = primarybuf->buffer;
1990 flen2 = len - (primarybuf->buflen - primarybuf->playpos);
1991 if (DSOUND_WriteAudio(frag1, flen1) != 0) {
1992 perror("DSOUND_WriteAudio");
1993 LeaveCriticalSection(&(primarybuf->lock));
1994 ExitThread(0);
1996 memset(frag1, neutral, flen1);
1997 if (DSOUND_WriteAudio(frag2, flen2) != 0) {
1998 perror("DSOUND_WriteAudio");
1999 LeaveCriticalSection(&(primarybuf->lock));
2000 ExitThread(0);
2002 memset(frag2, neutral, flen2);
2003 } else {
2004 frag1 = primarybuf->buffer + primarybuf->playpos;
2005 flen1 = len;
2006 if (DSOUND_WriteAudio(frag1, flen1) != 0) {
2007 perror("DSOUND_WriteAudio");
2008 LeaveCriticalSection(&(primarybuf->lock));
2009 ExitThread(0);
2011 memset(frag1, neutral, flen1);
2013 } else {
2014 /* Can't play audio at the moment -- we need to sleep */
2015 /* to make up for the time we'd be blocked in write() */
2016 /* to /dev/audio */
2017 Sleep(60);
2019 primarybuf->playpos += len;
2020 if (primarybuf->playpos >= primarybuf->buflen)
2021 primarybuf->playpos %= primarybuf->buflen;
2022 primarybuf->writepos = primarybuf->playpos + DSOUND_FRAGLEN;
2023 if (primarybuf->writepos >= primarybuf->buflen)
2024 primarybuf->writepos %= primarybuf->buflen;
2026 LeaveCriticalSection(&(primarybuf->lock));
2027 /* **** */
2030 static DWORD WINAPI DSOUND_thread(LPVOID arg)
2032 int len;
2034 TRACE(dsound,"dsound is at pid %d\n",getpid());
2035 while (1) {
2036 if (!dsound) {
2037 WARN(dsound,"DSOUND thread giving up.\n");
2038 ExitThread(0);
2040 if (getppid()==1) {
2041 WARN(dsound,"DSOUND father died? Giving up.\n");
2042 ExitThread(0);
2044 /* RACE: dsound could be deleted */
2045 dsound->lpvtbl->fnAddRef(dsound);
2046 if (primarybuf == NULL) {
2047 /* Should never happen */
2048 WARN(dsound, "Lost the primary buffer!\n");
2049 dsound->lpvtbl->fnRelease(dsound);
2050 ExitThread(0);
2053 /* **** */
2054 EnterCriticalSection(&(primarybuf->lock));
2055 len = DSOUND_MixPrimary();
2056 LeaveCriticalSection(&(primarybuf->lock));
2057 /* **** */
2059 if (primarybuf->playing)
2060 len = DSOUND_FRAGLEN > len ? DSOUND_FRAGLEN : len;
2061 if (len) {
2062 /* This does all the work */
2063 DSOUND_OutputPrimary(len);
2064 } else {
2065 /* no buffers playing -- close and wait */
2066 if (audioOK)
2067 DSOUND_CloseAudio();
2068 Sleep(100);
2070 dsound->lpvtbl->fnRelease(dsound);
2072 ExitThread(0);
2075 #endif /* HAVE_OSS */
2077 HRESULT WINAPI DirectSoundCreate(REFGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter )
2079 if (lpGUID)
2080 TRACE(dsound,"(%p,%p,%p)\n",lpGUID,ppDS,pUnkOuter);
2081 else
2082 TRACE(dsound,"DirectSoundCreate (%p)\n", ppDS);
2084 #ifdef HAVE_OSS
2086 if (ppDS == NULL)
2087 return DSERR_INVALIDPARAM;
2089 if (primarybuf) {
2090 dsound->lpvtbl->fnAddRef(dsound);
2091 *ppDS = dsound;
2092 return DS_OK;
2095 /* Check that we actually have audio capabilities */
2096 /* If we do, whether it's busy or not, we continue */
2097 /* otherwise we return with DSERR_NODRIVER */
2099 audiofd = open("/dev/audio",O_WRONLY);
2100 if (audiofd == -1) {
2101 if (errno == ENODEV) {
2102 MSG("No sound hardware found.\n");
2103 return DSERR_NODRIVER;
2104 } else if (errno == EBUSY) {
2105 MSG("Sound device busy, will keep trying.\n");
2106 } else {
2107 MSG("Unexpected error while checking for sound support.\n");
2108 return DSERR_GENERIC;
2110 } else {
2111 close(audiofd);
2112 audiofd = -1;
2115 *ppDS = (LPDIRECTSOUND)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSound));
2116 if (*ppDS == NULL)
2117 return DSERR_OUTOFMEMORY;
2119 (*ppDS)->ref = 1;
2120 (*ppDS)->lpvtbl = &dsvt;
2121 (*ppDS)->buffers = NULL;
2122 (*ppDS)->nrofbuffers = 0;
2124 (*ppDS)->wfx.wFormatTag = 1;
2125 (*ppDS)->wfx.nChannels = 2;
2126 (*ppDS)->wfx.nSamplesPerSec = 22050;
2127 (*ppDS)->wfx.nAvgBytesPerSec = 44100;
2128 (*ppDS)->wfx.nBlockAlign = 2;
2129 (*ppDS)->wfx.wBitsPerSample = 8;
2131 if (!dsound) {
2132 HANDLE hnd;
2133 DWORD xid;
2135 dsound = (*ppDS);
2136 if (primarybuf == NULL) {
2137 DSBUFFERDESC dsbd;
2138 HRESULT hr;
2140 dsbd.dwSize = sizeof(DSBUFFERDESC);
2141 dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
2142 dsbd.dwBufferBytes = 0;
2143 dsbd.lpwfxFormat = &(dsound->wfx);
2144 hr = IDirectSound_CreateSoundBuffer(*ppDS, &dsbd, &primarybuf, NULL);
2145 if (hr != DS_OK)
2146 return hr;
2147 dsound->primary = primarybuf;
2149 memset(primarybuf->buffer, 128, primarybuf->buflen);
2150 hnd = CreateThread(NULL,0,DSOUND_thread,0,0,&xid);
2152 return DS_OK;
2153 #else
2154 MessageBoxA(0,"DirectSound needs the Open Sound System Driver, which has not been found by ./configure.","WINE DirectSound",MB_OK|MB_ICONSTOP);
2155 return DSERR_NODRIVER;
2156 #endif
2159 /*******************************************************************************
2160 * DirectSound ClassFactory
2162 typedef struct
2164 /* IUnknown fields */
2165 ICOM_VTABLE(IClassFactory)* lpvtbl;
2166 DWORD ref;
2167 } IClassFactoryImpl;
2169 static HRESULT WINAPI
2170 DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
2171 ICOM_THIS(IClassFactoryImpl,iface);
2172 char buf[80];
2174 if (HIWORD(riid))
2175 WINE_StringFromCLSID(riid,buf);
2176 else
2177 sprintf(buf,"<guid-0x%04x>",LOWORD(riid));
2178 FIXME(dsound,"(%p)->(%s,%p),stub!\n",This,buf,ppobj);
2179 return E_NOINTERFACE;
2182 static ULONG WINAPI
2183 DSCF_AddRef(LPCLASSFACTORY iface) {
2184 ICOM_THIS(IClassFactoryImpl,iface);
2185 return ++(This->ref);
2188 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) {
2189 ICOM_THIS(IClassFactoryImpl,iface);
2190 /* static class, won't be freed */
2191 return --(This->ref);
2194 static HRESULT WINAPI DSCF_CreateInstance(
2195 LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
2197 ICOM_THIS(IClassFactoryImpl,iface);
2198 char buf[80];
2200 WINE_StringFromCLSID(riid,buf);
2201 TRACE(dsound,"(%p)->(%p,%s,%p)\n",This,pOuter,buf,ppobj);
2202 if (!memcmp(riid,&IID_IDirectSound,sizeof(IID_IDirectSound))) {
2203 /* FIXME: reuse already created dsound if present? */
2204 return DirectSoundCreate(riid,(LPDIRECTSOUND*)ppobj,pOuter);
2206 return E_NOINTERFACE;
2209 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
2210 ICOM_THIS(IClassFactoryImpl,iface);
2211 FIXME(dsound,"(%p)->(%d),stub!\n",This,dolock);
2212 return S_OK;
2215 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
2216 DSCF_QueryInterface,
2217 DSCF_AddRef,
2218 DSCF_Release,
2219 DSCF_CreateInstance,
2220 DSCF_LockServer
2222 static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 };
2224 /*******************************************************************************
2225 * DllGetClassObject [DSOUND.4]
2226 * Retrieves class object from a DLL object
2228 * NOTES
2229 * Docs say returns STDAPI
2231 * PARAMS
2232 * rclsid [I] CLSID for the class object
2233 * riid [I] Reference to identifier of interface for class object
2234 * ppv [O] Address of variable to receive interface pointer for riid
2236 * RETURNS
2237 * Success: S_OK
2238 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
2239 * E_UNEXPECTED
2241 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
2243 char buf[80],xbuf[80];
2245 if (HIWORD(rclsid))
2246 WINE_StringFromCLSID(rclsid,xbuf);
2247 else
2248 sprintf(xbuf,"<guid-0x%04x>",LOWORD(rclsid));
2249 if (HIWORD(riid))
2250 WINE_StringFromCLSID(riid,buf);
2251 else
2252 sprintf(buf,"<guid-0x%04x>",LOWORD(riid));
2253 WINE_StringFromCLSID(riid,xbuf);
2254 TRACE(dsound, "(%p,%p,%p)\n", xbuf, buf, ppv);
2255 if (!memcmp(riid,&IID_IClassFactory,sizeof(IID_IClassFactory))) {
2256 *ppv = (LPVOID)&DSOUND_CF;
2257 IClassFactory_AddRef((IClassFactory*)*ppv);
2258 return S_OK;
2260 FIXME(dsound, "(%p,%p,%p): no interface found.\n", xbuf, buf, ppv);
2261 return E_NOINTERFACE;
2265 /*******************************************************************************
2266 * DllCanUnloadNow [DSOUND.3] Determines whether the DLL is in use.
2268 * RETURNS
2269 * Success: S_OK
2270 * Failure: S_FALSE
2272 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
2274 FIXME(dsound, "(void): stub\n");
2275 return S_FALSE;