Fixed some warnings.
[wine.git] / dlls / dsound / dsound_main.c
blob4b24e91756485fbc3586695e985cf1aedc93f038
1 /* DirectSound
2 *
3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998 Rob Riggs
5 * Copyright 2000-2001 TransGaming Technologies, Inc.
6 */
7 /*
8 * Most thread locking is complete. There may be a few race
9 * conditions still lurking.
11 * Tested with a Soundblaster clone, a Gravis UltraSound Classic,
12 * and a Turtle Beach Tropez+.
14 * TODO:
15 * Implement DirectSoundCapture API
16 * Implement SetCooperativeLevel properly (need to address focus issues)
17 * Implement DirectSound3DBuffers (stubs in place)
18 * Use hardware 3D support if available
19 * Add critical section locking inside Release and AddRef methods
20 * Handle static buffers - put those in hardware, non-static not in hardware
21 * Hardware DuplicateSoundBuffer
22 * Proper volume calculation, and setting volume in HEL primary buffer
23 * Optimize WINMM and negotiate fragment size, decrease DS_HEL_MARGIN
26 #include "config.h"
27 #include <assert.h>
28 #include <stdio.h>
29 #include <sys/types.h>
30 #include <sys/fcntl.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <math.h> /* Insomnia - pow() function */
36 #include "windef.h"
37 #include "winbase.h"
38 #include "wingdi.h"
39 #include "winuser.h"
40 #include "winerror.h"
41 #include "mmsystem.h"
42 #include "mmddk.h"
43 #include "wine/windef16.h"
44 #include "debugtools.h"
45 #include "dsound.h"
46 #include "dsdriver.h"
48 DEFAULT_DEBUG_CHANNEL(dsound);
50 /* these are eligible for tuning... they must be high on slow machines... */
51 /* especially since the WINMM overhead is pretty high, and could be improved quite a bit;
52 * the high DS_HEL_MARGIN reflects the currently high wineoss/HEL latency
53 * some settings here should probably get ported to wine.conf */
54 #define DS_EMULDRIVER 1 /* some games (Quake 2, UT) refuse to accept
55 emulated dsound devices. set to 0 ! */
56 #define DS_HEL_FRAGS 48 /* HEL only: number of waveOut fragments in primary buffer */
57 #define DS_HEL_MARGIN 4 /* HEL only: number of waveOut fragments ahead to mix in new buffers */
59 #define DS_SND_QUEUE 28 /* max number of fragments to prebuffer */
61 /* Linux does not support better timing than 10ms */
62 #define DS_TIME_RES 10 /* Resolution of multimedia timer */
63 #define DS_TIME_DEL 10 /* Delay of multimedia timer callback, and duration of HEL fragment */
65 /*****************************************************************************
66 * Predeclare the interface implementation structures
68 typedef struct IDirectSoundImpl IDirectSoundImpl;
69 typedef struct IDirectSoundBufferImpl IDirectSoundBufferImpl;
70 typedef struct IDirectSoundNotifyImpl IDirectSoundNotifyImpl;
71 typedef struct IDirectSound3DListenerImpl IDirectSound3DListenerImpl;
72 typedef struct IDirectSound3DBufferImpl IDirectSound3DBufferImpl;
73 typedef struct IDirectSoundCaptureImpl IDirectSoundCaptureImpl;
74 typedef struct IDirectSoundCaptureBufferImpl IDirectSoundCaptureBufferImpl;
75 typedef struct IKsPropertySetImpl IKsPropertySetImpl;
77 /*****************************************************************************
78 * IDirectSound implementation structure
80 struct IDirectSoundImpl
82 /* IUnknown fields */
83 ICOM_VFIELD(IDirectSound);
84 DWORD ref;
85 /* IDirectSoundImpl fields */
86 PIDSDRIVER driver;
87 DSDRIVERDESC drvdesc;
88 DSDRIVERCAPS drvcaps;
89 HWAVEOUT hwo;
90 LPWAVEHDR pwave[DS_HEL_FRAGS];
91 UINT timerID, pwplay, pwwrite, pwqueue;
92 DWORD fraglen;
93 DWORD priolevel;
94 int nrofbuffers;
95 IDirectSoundBufferImpl** buffers;
96 IDirectSoundBufferImpl* primary;
97 IDirectSound3DListenerImpl* listener;
98 WAVEFORMATEX wfx; /* current main waveformat */
99 CRITICAL_SECTION lock;
102 /*****************************************************************************
103 * IDirectSoundBuffer implementation structure
105 struct IDirectSoundBufferImpl
107 /* FIXME: document */
108 /* IUnknown fields */
109 ICOM_VFIELD(IDirectSoundBuffer);
110 DWORD ref;
111 /* IDirectSoundBufferImpl fields */
112 PIDSDRIVERBUFFER hwbuf;
113 WAVEFORMATEX wfx;
114 LPBYTE buffer;
115 IDirectSound3DBufferImpl* ds3db;
116 DWORD playflags,state,leadin;
117 DWORD playpos,startpos,writelead,buflen;
118 DWORD nAvgBytesPerSec;
119 DWORD freq;
120 DSVOLUMEPAN volpan;
121 IDirectSoundBufferImpl* parent; /* for duplicates */
122 IDirectSoundImpl* dsound;
123 DSBUFFERDESC dsbd;
124 LPDSBPOSITIONNOTIFY notifies;
125 int nrofnotifies;
126 CRITICAL_SECTION lock;
127 /* used for frequency conversion (PerfectPitch) */
128 ULONG freqAdjust, freqAcc;
129 /* used for intelligent (well, sort of) prebuffering */
130 DWORD probably_valid_to;
131 DWORD primary_mixpos, buf_mixpos;
134 #define STATE_STOPPED 0
135 #define STATE_STARTING 1
136 #define STATE_PLAYING 2
137 #define STATE_STOPPING 3
139 /*****************************************************************************
140 * IDirectSoundNotify implementation structure
142 struct IDirectSoundNotifyImpl
144 /* IUnknown fields */
145 ICOM_VFIELD(IDirectSoundNotify);
146 DWORD ref;
147 /* IDirectSoundNotifyImpl fields */
148 IDirectSoundBufferImpl* dsb;
151 /*****************************************************************************
152 * IDirectSound3DListener implementation structure
154 struct IDirectSound3DListenerImpl
156 /* IUnknown fields */
157 ICOM_VFIELD(IDirectSound3DListener);
158 DWORD ref;
159 /* IDirectSound3DListenerImpl fields */
160 IDirectSoundBufferImpl* dsb;
161 DS3DLISTENER ds3dl;
162 CRITICAL_SECTION lock;
165 struct IKsPropertySetImpl
167 /* IUnknown fields */
168 ICOM_VFIELD(IKsPropertySet);
169 DWORD ref;
170 /* IKsPropertySetImpl fields */
171 IDirectSound3DBufferImpl *ds3db; /* backptr, no ref */
174 /*****************************************************************************
175 * IDirectSound3DBuffer implementation structure
177 struct IDirectSound3DBufferImpl
179 /* IUnknown fields */
180 ICOM_VFIELD(IDirectSound3DBuffer);
181 DWORD ref;
182 /* IDirectSound3DBufferImpl fields */
183 IDirectSoundBufferImpl* dsb;
184 DS3DBUFFER ds3db;
185 LPBYTE buffer;
186 DWORD buflen;
187 CRITICAL_SECTION lock;
188 IKsPropertySetImpl* iks;
192 /*****************************************************************************
193 * IDirectSoundCapture implementation structure
195 struct IDirectSoundCaptureImpl
197 /* IUnknown fields */
198 ICOM_VFIELD(IDirectSoundCapture);
199 DWORD ref;
201 /* IDirectSoundCaptureImpl fields */
202 CRITICAL_SECTION lock;
205 /*****************************************************************************
206 * IDirectSoundCapture implementation structure
208 struct IDirectSoundCaptureBufferImpl
210 /* IUnknown fields */
211 ICOM_VFIELD(IDirectSoundCaptureBuffer);
212 DWORD ref;
214 /* IDirectSoundCaptureBufferImpl fields */
215 CRITICAL_SECTION lock;
219 /* #define USE_DSOUND3D 1 */
221 #define DSOUND_FREQSHIFT (14)
223 static IDirectSoundImpl* dsound = NULL;
225 static IDirectSoundBufferImpl* primarybuf = NULL;
227 static void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len);
229 static HRESULT DSOUND_CreateDirectSoundCapture( LPVOID* ppobj );
230 static HRESULT DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc, LPVOID* ppobj );
232 static ICOM_VTABLE(IDirectSoundCapture) dscvt;
233 static ICOM_VTABLE(IDirectSoundCaptureBuffer) dscbvt;
235 static HRESULT mmErr(UINT err)
237 switch(err) {
238 case MMSYSERR_NOERROR:
239 return DS_OK;
240 case MMSYSERR_ALLOCATED:
241 return DSERR_ALLOCATED;
242 case MMSYSERR_INVALHANDLE:
243 return DSERR_GENERIC; /* FIXME */
244 case MMSYSERR_NODRIVER:
245 return DSERR_NODRIVER;
246 case MMSYSERR_NOMEM:
247 return DSERR_OUTOFMEMORY;
248 case MMSYSERR_INVALPARAM:
249 return DSERR_INVALIDPARAM;
250 default:
251 FIXME("Unknown MMSYS error %d\n",err);
252 return DSERR_GENERIC;
256 /***************************************************************************
257 * DirectSoundEnumerateA [DSOUND.2]
259 * Enumerate all DirectSound drivers installed in the system
261 * RETURNS
262 * Success: DS_OK
263 * Failure: DSERR_INVALIDPARAM
265 HRESULT WINAPI DirectSoundEnumerateA(
266 LPDSENUMCALLBACKA lpDSEnumCallback,
267 LPVOID lpContext)
269 TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
270 lpDSEnumCallback, lpContext);
272 #ifdef HAVE_OSS
273 if (lpDSEnumCallback != NULL)
274 lpDSEnumCallback(NULL,"WINE DirectSound using Open Sound System",
275 "sound",lpContext);
276 #endif
278 return DS_OK;
281 /***************************************************************************
282 * DirectSoundEnumerateW [DSOUND.3]
284 * Enumerate all DirectSound drivers installed in the system
286 * RETURNS
287 * Success: DS_OK
288 * Failure: DSERR_INVALIDPARAM
290 HRESULT WINAPI DirectSoundEnumerateW(
291 LPDSENUMCALLBACKW lpDSEnumCallback,
292 LPVOID lpContext )
294 FIXME("lpDSEnumCallback = %p, lpContext = %p: stub\n",
295 lpDSEnumCallback, lpContext);
297 return DS_OK;
301 static void _dump_DSBCAPS(DWORD xmask) {
302 struct {
303 DWORD mask;
304 char *name;
305 } flags[] = {
306 #define FE(x) { x, #x },
307 FE(DSBCAPS_PRIMARYBUFFER)
308 FE(DSBCAPS_STATIC)
309 FE(DSBCAPS_LOCHARDWARE)
310 FE(DSBCAPS_LOCSOFTWARE)
311 FE(DSBCAPS_CTRL3D)
312 FE(DSBCAPS_CTRLFREQUENCY)
313 FE(DSBCAPS_CTRLPAN)
314 FE(DSBCAPS_CTRLVOLUME)
315 FE(DSBCAPS_CTRLPOSITIONNOTIFY)
316 FE(DSBCAPS_CTRLDEFAULT)
317 FE(DSBCAPS_CTRLALL)
318 FE(DSBCAPS_STICKYFOCUS)
319 FE(DSBCAPS_GLOBALFOCUS)
320 FE(DSBCAPS_GETCURRENTPOSITION2)
321 FE(DSBCAPS_MUTE3DATMAXDISTANCE)
322 #undef FE
324 int i;
326 for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
327 if ((flags[i].mask & xmask) == flags[i].mask)
328 DPRINTF("%s ",flags[i].name);
331 /*******************************************************************************
332 * IKsPropertySet
335 /* IUnknown methods */
336 #ifdef USE_DSOUND3D
337 static HRESULT WINAPI IKsPropertySetImpl_QueryInterface(
338 LPKSPROPERTYSET iface, REFIID riid, LPVOID *ppobj
340 ICOM_THIS(IKsPropertySetImpl,iface);
342 FIXME("(%p,%s,%p), stub!\n",This,debugstr_guid(riid),ppobj);
343 return E_FAIL;
345 #endif
347 #ifdef USE_DSOUND3D
348 static ULONG WINAPI IKsPropertySetImpl_AddRef(LPKSPROPERTYSET iface) {
349 ICOM_THIS(IKsPropertySetImpl,iface);
351 This->ref++;
352 return This->ref;
354 #endif
356 #ifdef USE_DSOUND3D
357 static ULONG WINAPI IKsPropertySetImpl_Release(LPKSPROPERTYSET iface) {
358 ICOM_THIS(IKsPropertySetImpl,iface);
360 This->ref--;
361 return This->ref;
363 #endif
365 #ifdef USE_DSOUND3D
366 static HRESULT WINAPI IKsPropertySetImpl_Get(LPKSPROPERTYSET iface,
367 REFGUID guidPropSet, ULONG dwPropID,
368 LPVOID pInstanceData, ULONG cbInstanceData,
369 LPVOID pPropData, ULONG cbPropData,
370 PULONG pcbReturned
372 ICOM_THIS(IKsPropertySetImpl,iface);
374 FIXME("(%p,%s,%ld,%p,%ld,%p,%ld,%p), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pInstanceData,cbInstanceData,pPropData,cbPropData,pcbReturned);
375 return E_PROP_ID_UNSUPPORTED;
377 #endif
379 #ifdef USE_DSOUND3D
380 static HRESULT WINAPI IKsPropertySetImpl_Set(LPKSPROPERTYSET iface,
381 REFGUID guidPropSet, ULONG dwPropID,
382 LPVOID pInstanceData, ULONG cbInstanceData,
383 LPVOID pPropData, ULONG cbPropData
385 ICOM_THIS(IKsPropertySetImpl,iface);
387 FIXME("(%p,%s,%ld,%p,%ld,%p,%ld), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pInstanceData,cbInstanceData,pPropData,cbPropData);
388 return E_PROP_ID_UNSUPPORTED;
390 #endif
392 #ifdef USE_DSOUND3D
393 static HRESULT WINAPI IKsPropertySetImpl_QuerySupport(LPKSPROPERTYSET iface,
394 REFGUID guidPropSet, ULONG dwPropID, PULONG pTypeSupport
396 ICOM_THIS(IKsPropertySetImpl,iface);
398 FIXME("(%p,%s,%ld,%p), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pTypeSupport);
399 return E_PROP_ID_UNSUPPORTED;
401 #endif
403 #ifdef USE_DSOUND3D
404 static ICOM_VTABLE(IKsPropertySet) iksvt = {
405 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
406 IKsPropertySetImpl_QueryInterface,
407 IKsPropertySetImpl_AddRef,
408 IKsPropertySetImpl_Release,
409 IKsPropertySetImpl_Get,
410 IKsPropertySetImpl_Set,
411 IKsPropertySetImpl_QuerySupport
413 #endif
415 /*******************************************************************************
416 * IDirectSound3DBuffer
419 /* IUnknown methods */
420 #ifdef USE_DSOUND3D
421 static HRESULT WINAPI IDirectSound3DBufferImpl_QueryInterface(
422 LPDIRECTSOUND3DBUFFER iface, REFIID riid, LPVOID *ppobj)
424 ICOM_THIS(IDirectSound3DBufferImpl,iface);
426 if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
427 IDirectSound3DBuffer_AddRef(iface);
428 *ppobj = This->iks;
429 return S_OK;
432 FIXME("(%p,%s,%p), no such interface.\n",This,debugstr_guid(riid),ppobj);
433 return E_FAIL;
435 #endif
437 #ifdef USE_DSOUND3D
438 static ULONG WINAPI IDirectSound3DBufferImpl_AddRef(LPDIRECTSOUND3DBUFFER iface)
440 ICOM_THIS(IDirectSound3DBufferImpl,iface);
441 This->ref++;
442 return This->ref;
444 #endif
446 #ifdef USE_DSOUND3D
447 static ULONG WINAPI IDirectSound3DBufferImpl_Release(LPDIRECTSOUND3DBUFFER iface)
449 ICOM_THIS(IDirectSound3DBufferImpl,iface);
451 TRACE("(%p) ref was %ld\n", This, This->ref);
453 if(--This->ref)
454 return This->ref;
456 if (This->dsb)
457 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
459 DeleteCriticalSection(&This->lock);
461 HeapFree(GetProcessHeap(),0,This->buffer);
462 HeapFree(GetProcessHeap(),0,This);
464 return 0;
466 #endif
468 /* IDirectSound3DBuffer methods */
469 #ifdef USE_DSOUND3D
470 static HRESULT WINAPI IDirectSound3DBufferImpl_GetAllParameters(
471 LPDIRECTSOUND3DBUFFER iface,
472 LPDS3DBUFFER lpDs3dBuffer)
474 FIXME("stub\n");
475 return DS_OK;
477 #endif
479 #ifdef USE_DSOUND3D
480 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeAngles(
481 LPDIRECTSOUND3DBUFFER iface,
482 LPDWORD lpdwInsideConeAngle,
483 LPDWORD lpdwOutsideConeAngle)
485 FIXME("stub\n");
486 return DS_OK;
488 #endif
490 #ifdef USE_DSOUND3D
491 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOrientation(
492 LPDIRECTSOUND3DBUFFER iface,
493 LPD3DVECTOR lpvConeOrientation)
495 FIXME("stub\n");
496 return DS_OK;
498 #endif
500 #ifdef USE_DSOUND3D
501 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOutsideVolume(
502 LPDIRECTSOUND3DBUFFER iface,
503 LPLONG lplConeOutsideVolume)
505 FIXME("stub\n");
506 return DS_OK;
508 #endif
510 #ifdef USE_DSOUND3D
511 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMaxDistance(
512 LPDIRECTSOUND3DBUFFER iface,
513 LPD3DVALUE lpfMaxDistance)
515 FIXME("stub\n");
516 return DS_OK;
518 #endif
520 #ifdef USE_DSOUND3D
521 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMinDistance(
522 LPDIRECTSOUND3DBUFFER iface,
523 LPD3DVALUE lpfMinDistance)
525 FIXME("stub\n");
526 return DS_OK;
528 #endif
530 #ifdef USE_DSOUND3D
531 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMode(
532 LPDIRECTSOUND3DBUFFER iface,
533 LPDWORD lpdwMode)
535 FIXME("stub\n");
536 return DS_OK;
538 #endif
540 #ifdef USE_DSOUND3D
541 static HRESULT WINAPI IDirectSound3DBufferImpl_GetPosition(
542 LPDIRECTSOUND3DBUFFER iface,
543 LPD3DVECTOR lpvPosition)
545 FIXME("stub\n");
546 return DS_OK;
548 #endif
550 #ifdef USE_DSOUND3D
551 static HRESULT WINAPI IDirectSound3DBufferImpl_GetVelocity(
552 LPDIRECTSOUND3DBUFFER iface,
553 LPD3DVECTOR lpvVelocity)
555 FIXME("stub\n");
556 return DS_OK;
558 #endif
560 #ifdef USE_DSOUND3D
561 static HRESULT WINAPI IDirectSound3DBufferImpl_SetAllParameters(
562 LPDIRECTSOUND3DBUFFER iface,
563 LPCDS3DBUFFER lpcDs3dBuffer,
564 DWORD dwApply)
566 FIXME("stub\n");
567 return DS_OK;
569 #endif
571 #ifdef USE_DSOUND3D
572 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeAngles(
573 LPDIRECTSOUND3DBUFFER iface,
574 DWORD dwInsideConeAngle,
575 DWORD dwOutsideConeAngle,
576 DWORD dwApply)
578 FIXME("stub\n");
579 return DS_OK;
581 #endif
583 #ifdef USE_DSOUND3D
584 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOrientation(
585 LPDIRECTSOUND3DBUFFER iface,
586 D3DVALUE x, D3DVALUE y, D3DVALUE z,
587 DWORD dwApply)
589 FIXME("stub\n");
590 return DS_OK;
592 #endif
594 #ifdef USE_DSOUND3D
595 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOutsideVolume(
596 LPDIRECTSOUND3DBUFFER iface,
597 LONG lConeOutsideVolume,
598 DWORD dwApply)
600 FIXME("stub\n");
601 return DS_OK;
603 #endif
605 #ifdef USE_DSOUND3D
606 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMaxDistance(
607 LPDIRECTSOUND3DBUFFER iface,
608 D3DVALUE fMaxDistance,
609 DWORD dwApply)
611 FIXME("stub\n");
612 return DS_OK;
614 #endif
616 #ifdef USE_DSOUND3D
617 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMinDistance(
618 LPDIRECTSOUND3DBUFFER iface,
619 D3DVALUE fMinDistance,
620 DWORD dwApply)
622 FIXME("stub\n");
623 return DS_OK;
625 #endif
627 #ifdef USE_DSOUND3D
628 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMode(
629 LPDIRECTSOUND3DBUFFER iface,
630 DWORD dwMode,
631 DWORD dwApply)
633 ICOM_THIS(IDirectSound3DBufferImpl,iface);
634 TRACE("mode = %lx\n", dwMode);
635 This->ds3db.dwMode = dwMode;
636 return DS_OK;
638 #endif
640 #ifdef USE_DSOUND3D
641 static HRESULT WINAPI IDirectSound3DBufferImpl_SetPosition(
642 LPDIRECTSOUND3DBUFFER iface,
643 D3DVALUE x, D3DVALUE y, D3DVALUE z,
644 DWORD dwApply)
646 FIXME("stub\n");
647 return DS_OK;
649 #endif
651 #ifdef USE_DSOUND3D
652 static HRESULT WINAPI IDirectSound3DBufferImpl_SetVelocity(
653 LPDIRECTSOUND3DBUFFER iface,
654 D3DVALUE x, D3DVALUE y, D3DVALUE z,
655 DWORD dwApply)
657 FIXME("stub\n");
658 return DS_OK;
660 #endif
662 #ifdef USE_DSOUND3D
663 static ICOM_VTABLE(IDirectSound3DBuffer) ds3dbvt =
665 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
666 /* IUnknown methods */
667 IDirectSound3DBufferImpl_QueryInterface,
668 IDirectSound3DBufferImpl_AddRef,
669 IDirectSound3DBufferImpl_Release,
670 /* IDirectSound3DBuffer methods */
671 IDirectSound3DBufferImpl_GetAllParameters,
672 IDirectSound3DBufferImpl_GetConeAngles,
673 IDirectSound3DBufferImpl_GetConeOrientation,
674 IDirectSound3DBufferImpl_GetConeOutsideVolume,
675 IDirectSound3DBufferImpl_GetMaxDistance,
676 IDirectSound3DBufferImpl_GetMinDistance,
677 IDirectSound3DBufferImpl_GetMode,
678 IDirectSound3DBufferImpl_GetPosition,
679 IDirectSound3DBufferImpl_GetVelocity,
680 IDirectSound3DBufferImpl_SetAllParameters,
681 IDirectSound3DBufferImpl_SetConeAngles,
682 IDirectSound3DBufferImpl_SetConeOrientation,
683 IDirectSound3DBufferImpl_SetConeOutsideVolume,
684 IDirectSound3DBufferImpl_SetMaxDistance,
685 IDirectSound3DBufferImpl_SetMinDistance,
686 IDirectSound3DBufferImpl_SetMode,
687 IDirectSound3DBufferImpl_SetPosition,
688 IDirectSound3DBufferImpl_SetVelocity,
690 #endif
692 #ifdef USE_DSOUND3D
693 static int DSOUND_Create3DBuffer(IDirectSoundBufferImpl* dsb)
695 DWORD i, temp, iSize, oSize, offset;
696 LPBYTE bIbuf, bObuf, bTbuf = NULL;
697 LPWORD wIbuf, wObuf, wTbuf = NULL;
699 /* Inside DirectX says it's stupid but allowed */
700 if (dsb->wfx.nChannels == 2) {
701 /* Convert to mono */
702 if (dsb->wfx.wBitsPerSample == 16) {
703 iSize = dsb->buflen / 4;
704 wTbuf = malloc(dsb->buflen / 2);
705 if (wTbuf == NULL)
706 return DSERR_OUTOFMEMORY;
707 for (i = 0; i < iSize; i++)
708 wTbuf[i] = (dsb->buffer[i * 2] + dsb->buffer[(i * 2) + 1]) / 2;
709 wIbuf = wTbuf;
710 } else {
711 iSize = dsb->buflen / 2;
712 bTbuf = malloc(dsb->buflen / 2);
713 if (bTbuf == NULL)
714 return DSERR_OUTOFMEMORY;
715 for (i = 0; i < iSize; i++)
716 bTbuf[i] = (dsb->buffer[i * 2] + dsb->buffer[(i * 2) + 1]) / 2;
717 bIbuf = bTbuf;
719 } else {
720 if (dsb->wfx.wBitsPerSample == 16) {
721 iSize = dsb->buflen / 2;
722 wIbuf = (LPWORD) dsb->buffer;
723 } else {
724 bIbuf = (LPBYTE) dsb->buffer;
725 iSize = dsb->buflen;
729 if (primarybuf->wfx.wBitsPerSample == 16) {
730 wObuf = (LPWORD) dsb->ds3db->buffer;
731 oSize = dsb->ds3db->buflen / 2;
732 } else {
733 bObuf = (LPBYTE) dsb->ds3db->buffer;
734 oSize = dsb->ds3db->buflen;
737 offset = primarybuf->wfx.nSamplesPerSec / 100; /* 10ms */
738 if (primarybuf->wfx.wBitsPerSample == 16 && dsb->wfx.wBitsPerSample == 16)
739 for (i = 0; i < iSize; i++) {
740 temp = wIbuf[i];
741 if (i >= offset)
742 temp += wIbuf[i - offset] >> 9;
743 else
744 temp += wIbuf[i + iSize - offset] >> 9;
745 wObuf[i * 2] = temp;
746 wObuf[(i * 2) + 1] = temp;
748 else if (primarybuf->wfx.wBitsPerSample == 8 && dsb->wfx.wBitsPerSample == 8)
749 for (i = 0; i < iSize; i++) {
750 temp = bIbuf[i];
751 if (i >= offset)
752 temp += bIbuf[i - offset] >> 5;
753 else
754 temp += bIbuf[i + iSize - offset] >> 5;
755 bObuf[i * 2] = temp;
756 bObuf[(i * 2) + 1] = temp;
759 if (wTbuf)
760 free(wTbuf);
761 if (bTbuf)
762 free(bTbuf);
764 return DS_OK;
766 #endif
767 /*******************************************************************************
768 * IDirectSound3DListener
771 /* IUnknown methods */
772 static HRESULT WINAPI IDirectSound3DListenerImpl_QueryInterface(
773 LPDIRECTSOUND3DLISTENER iface, REFIID riid, LPVOID *ppobj)
775 ICOM_THIS(IDirectSound3DListenerImpl,iface);
777 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
778 return E_FAIL;
781 static ULONG WINAPI IDirectSound3DListenerImpl_AddRef(LPDIRECTSOUND3DLISTENER iface)
783 ICOM_THIS(IDirectSound3DListenerImpl,iface);
784 This->ref++;
785 return This->ref;
788 static ULONG WINAPI IDirectSound3DListenerImpl_Release(LPDIRECTSOUND3DLISTENER iface)
790 ULONG ulReturn;
791 ICOM_THIS(IDirectSound3DListenerImpl,iface);
793 TRACE("(%p) ref was %ld\n", This, This->ref);
795 ulReturn = --This->ref;
797 /* Free all resources */
798 if( ulReturn == 0 ) {
799 if(This->dsb)
800 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
801 DeleteCriticalSection(&This->lock);
802 HeapFree(GetProcessHeap(),0,This);
805 return ulReturn;
808 /* IDirectSound3DListener methods */
809 static HRESULT WINAPI IDirectSound3DListenerImpl_GetAllParameter(
810 LPDIRECTSOUND3DLISTENER iface,
811 LPDS3DLISTENER lpDS3DL)
813 FIXME("stub\n");
814 return DS_OK;
817 static HRESULT WINAPI IDirectSound3DListenerImpl_GetDistanceFactor(
818 LPDIRECTSOUND3DLISTENER iface,
819 LPD3DVALUE lpfDistanceFactor)
821 FIXME("stub\n");
822 return DS_OK;
825 static HRESULT WINAPI IDirectSound3DListenerImpl_GetDopplerFactor(
826 LPDIRECTSOUND3DLISTENER iface,
827 LPD3DVALUE lpfDopplerFactor)
829 FIXME("stub\n");
830 return DS_OK;
833 static HRESULT WINAPI IDirectSound3DListenerImpl_GetOrientation(
834 LPDIRECTSOUND3DLISTENER iface,
835 LPD3DVECTOR lpvOrientFront,
836 LPD3DVECTOR lpvOrientTop)
838 FIXME("stub\n");
839 return DS_OK;
842 static HRESULT WINAPI IDirectSound3DListenerImpl_GetPosition(
843 LPDIRECTSOUND3DLISTENER iface,
844 LPD3DVECTOR lpvPosition)
846 FIXME("stub\n");
847 return DS_OK;
850 static HRESULT WINAPI IDirectSound3DListenerImpl_GetRolloffFactor(
851 LPDIRECTSOUND3DLISTENER iface,
852 LPD3DVALUE lpfRolloffFactor)
854 FIXME("stub\n");
855 return DS_OK;
858 static HRESULT WINAPI IDirectSound3DListenerImpl_GetVelocity(
859 LPDIRECTSOUND3DLISTENER iface,
860 LPD3DVECTOR lpvVelocity)
862 FIXME("stub\n");
863 return DS_OK;
866 static HRESULT WINAPI IDirectSound3DListenerImpl_SetAllParameters(
867 LPDIRECTSOUND3DLISTENER iface,
868 LPCDS3DLISTENER lpcDS3DL,
869 DWORD dwApply)
871 FIXME("stub\n");
872 return DS_OK;
875 static HRESULT WINAPI IDirectSound3DListenerImpl_SetDistanceFactor(
876 LPDIRECTSOUND3DLISTENER iface,
877 D3DVALUE fDistanceFactor,
878 DWORD dwApply)
880 FIXME("stub\n");
881 return DS_OK;
884 static HRESULT WINAPI IDirectSound3DListenerImpl_SetDopplerFactor(
885 LPDIRECTSOUND3DLISTENER iface,
886 D3DVALUE fDopplerFactor,
887 DWORD dwApply)
889 FIXME("stub\n");
890 return DS_OK;
893 static HRESULT WINAPI IDirectSound3DListenerImpl_SetOrientation(
894 LPDIRECTSOUND3DLISTENER iface,
895 D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront,
896 D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop,
897 DWORD dwApply)
899 FIXME("stub\n");
900 return DS_OK;
903 static HRESULT WINAPI IDirectSound3DListenerImpl_SetPosition(
904 LPDIRECTSOUND3DLISTENER iface,
905 D3DVALUE x, D3DVALUE y, D3DVALUE z,
906 DWORD dwApply)
908 FIXME("stub\n");
909 return DS_OK;
912 static HRESULT WINAPI IDirectSound3DListenerImpl_SetRolloffFactor(
913 LPDIRECTSOUND3DLISTENER iface,
914 D3DVALUE fRolloffFactor,
915 DWORD dwApply)
917 FIXME("stub\n");
918 return DS_OK;
921 static HRESULT WINAPI IDirectSound3DListenerImpl_SetVelocity(
922 LPDIRECTSOUND3DLISTENER iface,
923 D3DVALUE x, D3DVALUE y, D3DVALUE z,
924 DWORD dwApply)
926 FIXME("stub\n");
927 return DS_OK;
930 static HRESULT WINAPI IDirectSound3DListenerImpl_CommitDeferredSettings(
931 LPDIRECTSOUND3DLISTENER iface)
934 FIXME("stub\n");
935 return DS_OK;
938 static ICOM_VTABLE(IDirectSound3DListener) ds3dlvt =
940 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
941 /* IUnknown methods */
942 IDirectSound3DListenerImpl_QueryInterface,
943 IDirectSound3DListenerImpl_AddRef,
944 IDirectSound3DListenerImpl_Release,
945 /* IDirectSound3DListener methods */
946 IDirectSound3DListenerImpl_GetAllParameter,
947 IDirectSound3DListenerImpl_GetDistanceFactor,
948 IDirectSound3DListenerImpl_GetDopplerFactor,
949 IDirectSound3DListenerImpl_GetOrientation,
950 IDirectSound3DListenerImpl_GetPosition,
951 IDirectSound3DListenerImpl_GetRolloffFactor,
952 IDirectSound3DListenerImpl_GetVelocity,
953 IDirectSound3DListenerImpl_SetAllParameters,
954 IDirectSound3DListenerImpl_SetDistanceFactor,
955 IDirectSound3DListenerImpl_SetDopplerFactor,
956 IDirectSound3DListenerImpl_SetOrientation,
957 IDirectSound3DListenerImpl_SetPosition,
958 IDirectSound3DListenerImpl_SetRolloffFactor,
959 IDirectSound3DListenerImpl_SetVelocity,
960 IDirectSound3DListenerImpl_CommitDeferredSettings,
963 /*******************************************************************************
964 * IDirectSoundNotify
966 static HRESULT WINAPI IDirectSoundNotifyImpl_QueryInterface(
967 LPDIRECTSOUNDNOTIFY iface,REFIID riid,LPVOID *ppobj
969 ICOM_THIS(IDirectSoundNotifyImpl,iface);
971 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
972 return E_FAIL;
975 static ULONG WINAPI IDirectSoundNotifyImpl_AddRef(LPDIRECTSOUNDNOTIFY iface) {
976 ICOM_THIS(IDirectSoundNotifyImpl,iface);
977 return ++(This->ref);
980 static ULONG WINAPI IDirectSoundNotifyImpl_Release(LPDIRECTSOUNDNOTIFY iface) {
981 ICOM_THIS(IDirectSoundNotifyImpl,iface);
983 TRACE("(%p) ref was %ld\n", This, This->ref);
985 This->ref--;
986 if (!This->ref) {
987 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
988 HeapFree(GetProcessHeap(),0,This);
989 return 0;
991 return This->ref;
994 static HRESULT WINAPI IDirectSoundNotifyImpl_SetNotificationPositions(
995 LPDIRECTSOUNDNOTIFY iface,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
997 ICOM_THIS(IDirectSoundNotifyImpl,iface);
998 int i;
1000 if (TRACE_ON(dsound)) {
1001 TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);
1002 for (i=0;i<howmuch;i++)
1003 TRACE("notify at %ld to 0x%08lx\n",
1004 notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
1006 This->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,This->dsb->notifies,(This->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
1007 memcpy( This->dsb->notifies+This->dsb->nrofnotifies,
1008 notify,
1009 howmuch*sizeof(DSBPOSITIONNOTIFY)
1011 This->dsb->nrofnotifies+=howmuch;
1013 return S_OK;
1016 static ICOM_VTABLE(IDirectSoundNotify) dsnvt =
1018 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1019 IDirectSoundNotifyImpl_QueryInterface,
1020 IDirectSoundNotifyImpl_AddRef,
1021 IDirectSoundNotifyImpl_Release,
1022 IDirectSoundNotifyImpl_SetNotificationPositions,
1025 /*******************************************************************************
1026 * IDirectSoundBuffer
1029 static void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan)
1031 double temp;
1033 /* the AmpFactors are expressed in 16.16 fixed point */
1034 volpan->dwVolAmpFactor = (ULONG) (pow(2.0, volpan->lVolume / 600.0) * 65536);
1035 /* FIXME: dwPan{Left|Right}AmpFactor */
1037 /* FIXME: use calculated vol and pan ampfactors */
1038 temp = (double) (volpan->lVolume - (volpan->lPan > 0 ? volpan->lPan : 0));
1039 volpan->dwTotalLeftAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 65536);
1040 temp = (double) (volpan->lVolume + (volpan->lPan < 0 ? volpan->lPan : 0));
1041 volpan->dwTotalRightAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 65536);
1043 TRACE("left = %lx, right = %lx\n", volpan->dwTotalLeftAmpFactor, volpan->dwTotalRightAmpFactor);
1046 static void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
1048 DWORD sw;
1050 sw = dsb->wfx.nChannels * (dsb->wfx.wBitsPerSample / 8);
1051 if ((dsb->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && dsb->hwbuf) {
1052 DWORD fraglen;
1053 /* let fragment size approximate the timer delay */
1054 fraglen = (dsb->freq * DS_TIME_DEL / 1000) * sw;
1055 /* reduce fragment size until an integer number of them fits in the buffer */
1056 /* (FIXME: this may or may not be a good idea) */
1057 while (dsb->buflen % fraglen) fraglen -= sw;
1058 dsb->dsound->fraglen = fraglen;
1059 TRACE("fraglen=%ld\n", dsb->dsound->fraglen);
1061 /* calculate the 10ms write lead */
1062 dsb->writelead = (dsb->freq / 100) * sw;
1065 static HRESULT DSOUND_PrimaryOpen(IDirectSoundBufferImpl *dsb)
1067 HRESULT err = DS_OK;
1069 /* are we using waveOut stuff? */
1070 if (!dsb->hwbuf) {
1071 LPBYTE newbuf;
1072 DWORD buflen;
1073 HRESULT merr = DS_OK;
1074 /* Start in pause mode, to allow buffers to get filled */
1075 waveOutPause(dsb->dsound->hwo);
1076 if (dsb->state == STATE_PLAYING) dsb->state = STATE_STARTING;
1077 else if (dsb->state == STATE_STOPPING) dsb->state = STATE_STOPPED;
1078 /* use fragments of 10ms (1/100s) each (which should get us within
1079 * the documented write cursor lead of 10-15ms) */
1080 buflen = ((dsb->wfx.nAvgBytesPerSec / 100) & ~3) * DS_HEL_FRAGS;
1081 TRACE("desired buflen=%ld, old buffer=%p\n", buflen, dsb->buffer);
1082 /* reallocate emulated primary buffer */
1083 newbuf = (LPBYTE)HeapReAlloc(GetProcessHeap(),0,dsb->buffer,buflen);
1084 if (newbuf == NULL) {
1085 ERR("failed to allocate primary buffer\n");
1086 merr = DSERR_OUTOFMEMORY;
1087 /* but the old buffer might still exists and must be re-prepared */
1088 } else {
1089 dsb->buffer = newbuf;
1090 dsb->buflen = buflen;
1092 if (dsb->buffer) {
1093 unsigned c;
1094 IDirectSoundImpl *ds = dsb->dsound;
1096 ds->fraglen = dsb->buflen / DS_HEL_FRAGS;
1098 /* prepare fragment headers */
1099 for (c=0; c<DS_HEL_FRAGS; c++) {
1100 ds->pwave[c]->lpData = dsb->buffer + c*ds->fraglen;
1101 ds->pwave[c]->dwBufferLength = ds->fraglen;
1102 ds->pwave[c]->dwUser = (DWORD)dsb;
1103 ds->pwave[c]->dwFlags = 0;
1104 ds->pwave[c]->dwLoops = 0;
1105 err = mmErr(waveOutPrepareHeader(ds->hwo,ds->pwave[c],sizeof(WAVEHDR)));
1106 if (err != DS_OK) {
1107 while (c--)
1108 waveOutUnprepareHeader(ds->hwo,ds->pwave[c],sizeof(WAVEHDR));
1109 break;
1113 ds->pwplay = 0;
1114 ds->pwwrite = 0;
1115 ds->pwqueue = 0;
1116 memset(dsb->buffer, (dsb->wfx.wBitsPerSample == 16) ? 0 : 128, dsb->buflen);
1117 TRACE("fraglen=%ld\n", ds->fraglen);
1119 if ((err == DS_OK) && (merr != DS_OK))
1120 err = merr;
1122 return err;
1126 static void DSOUND_PrimaryClose(IDirectSoundBufferImpl *dsb)
1128 /* are we using waveOut stuff? */
1129 if (!dsb->hwbuf) {
1130 unsigned c;
1131 IDirectSoundImpl *ds = dsb->dsound;
1133 waveOutReset(ds->hwo);
1134 for (c=0; c<DS_HEL_FRAGS; c++)
1135 waveOutUnprepareHeader(ds->hwo, ds->pwave[c], sizeof(WAVEHDR));
1139 static HRESULT DSOUND_PrimaryPlay(IDirectSoundBufferImpl *dsb)
1141 HRESULT err = DS_OK;
1142 if (dsb->hwbuf)
1143 err = IDsDriverBuffer_Play(dsb->hwbuf, 0, 0, DSBPLAY_LOOPING);
1144 return err;
1147 static HRESULT DSOUND_PrimaryStop(IDirectSoundBufferImpl *dsb)
1149 HRESULT err = DS_OK;
1150 if (dsb->hwbuf) {
1151 err = IDsDriverBuffer_Stop(dsb->hwbuf);
1152 if (err == DSERR_BUFFERLOST) {
1153 /* Wine-only: the driver wants us to reopen the device */
1154 /* FIXME: check for errors */
1155 IDsDriverBuffer_Release(primarybuf->hwbuf);
1156 waveOutClose(dsb->dsound->hwo);
1157 dsb->dsound->hwo = 0;
1158 waveOutOpen(&(dsb->dsound->hwo), dsb->dsound->drvdesc.dnDevNode,
1159 &(primarybuf->wfx), 0, 0, CALLBACK_NULL | WAVE_DIRECTSOUND);
1160 err = IDsDriver_CreateSoundBuffer(dsb->dsound->driver,&(dsb->wfx),dsb->dsbd.dwFlags,0,
1161 &(dsb->buflen),&(dsb->buffer),
1162 (LPVOID)&(dsb->hwbuf));
1165 return err;
1168 /* This sets this format for the <em>Primary Buffer Only</em> */
1169 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
1170 static HRESULT WINAPI IDirectSoundBufferImpl_SetFormat(
1171 LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX wfex
1173 ICOM_THIS(IDirectSoundBufferImpl,iface);
1174 IDirectSoundBufferImpl** dsb;
1175 HRESULT err = DS_OK;
1176 int i;
1178 /* Let's be pedantic! */
1179 if ((wfex == NULL) ||
1180 (wfex->wFormatTag != WAVE_FORMAT_PCM) ||
1181 (wfex->nChannels < 1) || (wfex->nChannels > 2) ||
1182 (wfex->nSamplesPerSec < 1) ||
1183 (wfex->nBlockAlign < 1) || (wfex->nChannels > 4) ||
1184 ((wfex->wBitsPerSample != 8) && (wfex->wBitsPerSample != 16))) {
1185 TRACE("failed pedantic check!\n");
1186 return DSERR_INVALIDPARAM;
1189 /* **** */
1190 EnterCriticalSection(&(This->dsound->lock));
1192 if (primarybuf->wfx.nSamplesPerSec != wfex->nSamplesPerSec) {
1193 dsb = dsound->buffers;
1194 for (i = 0; i < dsound->nrofbuffers; i++, dsb++) {
1195 /* **** */
1196 EnterCriticalSection(&((*dsb)->lock));
1198 (*dsb)->freqAdjust = ((*dsb)->freq << DSOUND_FREQSHIFT) /
1199 wfex->nSamplesPerSec;
1201 LeaveCriticalSection(&((*dsb)->lock));
1202 /* **** */
1206 memcpy(&(primarybuf->wfx), wfex, sizeof(primarybuf->wfx));
1208 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
1209 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1210 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
1211 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
1212 wfex->wBitsPerSample, wfex->cbSize);
1214 primarybuf->wfx.nAvgBytesPerSec =
1215 This->wfx.nSamplesPerSec * This->wfx.nBlockAlign;
1216 if (primarybuf->dsound->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) {
1217 /* FIXME: check for errors */
1218 DSOUND_PrimaryClose(primarybuf);
1219 waveOutClose(This->dsound->hwo);
1220 This->dsound->hwo = 0;
1221 waveOutOpen(&(This->dsound->hwo), This->dsound->drvdesc.dnDevNode,
1222 &(primarybuf->wfx), 0, 0, CALLBACK_NULL | WAVE_DIRECTSOUND);
1223 DSOUND_PrimaryOpen(primarybuf);
1225 if (primarybuf->hwbuf) {
1226 err = IDsDriverBuffer_SetFormat(primarybuf->hwbuf, &(primarybuf->wfx));
1227 if (err == DSERR_BUFFERLOST) {
1228 /* Wine-only: the driver wants us to recreate the HW buffer */
1229 IDsDriverBuffer_Release(primarybuf->hwbuf);
1230 err = IDsDriver_CreateSoundBuffer(primarybuf->dsound->driver,&(primarybuf->wfx),primarybuf->dsbd.dwFlags,0,
1231 &(primarybuf->buflen),&(primarybuf->buffer),
1232 (LPVOID)&(primarybuf->hwbuf));
1233 if (primarybuf->state == STATE_PLAYING) primarybuf->state = STATE_STARTING;
1234 else if (primarybuf->state == STATE_STOPPING) primarybuf->state = STATE_STOPPED;
1237 DSOUND_RecalcFormat(primarybuf);
1239 LeaveCriticalSection(&(This->dsound->lock));
1240 /* **** */
1242 return DS_OK;
1245 static HRESULT WINAPI IDirectSoundBufferImpl_SetVolume(
1246 LPDIRECTSOUNDBUFFER iface,LONG vol
1248 ICOM_THIS(IDirectSoundBufferImpl,iface);
1250 TRACE("(%p,%ld)\n",This,vol);
1252 /* I'm not sure if we need this for primary buffer */
1253 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
1254 return DSERR_CONTROLUNAVAIL;
1256 if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN))
1257 return DSERR_INVALIDPARAM;
1259 /* **** */
1260 EnterCriticalSection(&(This->lock));
1262 This->volpan.lVolume = vol;
1264 DSOUND_RecalcVolPan(&(This->volpan));
1266 if (This->hwbuf) {
1267 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
1269 else if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
1270 #if 0 /* should we really do this? */
1271 /* the DS volume ranges from 0 (max, 0dB attenuation) to -10000 (min, 100dB attenuation) */
1272 /* the MM volume ranges from 0 to 0xffff in an unspecified logarithmic scale */
1273 WORD cvol = 0xffff + vol*6 + vol/2;
1274 DWORD vol = cvol | ((DWORD)cvol << 16)
1275 waveOutSetVolume(This->dsound->hwo, vol);
1276 #endif
1279 LeaveCriticalSection(&(This->lock));
1280 /* **** */
1282 return DS_OK;
1285 static HRESULT WINAPI IDirectSoundBufferImpl_GetVolume(
1286 LPDIRECTSOUNDBUFFER iface,LPLONG vol
1288 ICOM_THIS(IDirectSoundBufferImpl,iface);
1289 TRACE("(%p,%p)\n",This,vol);
1291 if (vol == NULL)
1292 return DSERR_INVALIDPARAM;
1294 *vol = This->volpan.lVolume;
1295 return DS_OK;
1298 static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency(
1299 LPDIRECTSOUNDBUFFER iface,DWORD freq
1301 ICOM_THIS(IDirectSoundBufferImpl,iface);
1302 TRACE("(%p,%ld)\n",This,freq);
1304 /* You cannot set the frequency of the primary buffer */
1305 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY) ||
1306 (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
1307 return DSERR_CONTROLUNAVAIL;
1309 if (!freq) freq = This->wfx.nSamplesPerSec;
1311 if ((freq < DSBFREQUENCY_MIN) || (freq > DSBFREQUENCY_MAX))
1312 return DSERR_INVALIDPARAM;
1314 /* **** */
1315 EnterCriticalSection(&(This->lock));
1317 This->freq = freq;
1318 This->freqAdjust = (freq << DSOUND_FREQSHIFT) / primarybuf->wfx.nSamplesPerSec;
1319 This->nAvgBytesPerSec = freq * This->wfx.nBlockAlign;
1320 DSOUND_RecalcFormat(This);
1322 LeaveCriticalSection(&(This->lock));
1323 /* **** */
1325 return DS_OK;
1328 static HRESULT WINAPI IDirectSoundBufferImpl_Play(
1329 LPDIRECTSOUNDBUFFER iface,DWORD reserved1,DWORD reserved2,DWORD flags
1331 ICOM_THIS(IDirectSoundBufferImpl,iface);
1332 TRACE("(%p,%08lx,%08lx,%08lx)\n",
1333 This,reserved1,reserved2,flags
1336 /* **** */
1337 EnterCriticalSection(&(This->lock));
1339 This->playflags = flags;
1340 if (This->state == STATE_STOPPED) {
1341 This->leadin = TRUE;
1342 This->startpos = This->buf_mixpos;
1343 This->state = STATE_STARTING;
1344 } else if (This->state == STATE_STOPPING)
1345 This->state = STATE_PLAYING;
1346 if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
1347 IDsDriverBuffer_Play(This->hwbuf, 0, 0, This->playflags);
1348 This->state = STATE_PLAYING;
1351 LeaveCriticalSection(&(This->lock));
1352 /* **** */
1354 return DS_OK;
1357 static HRESULT WINAPI IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface)
1359 ICOM_THIS(IDirectSoundBufferImpl,iface);
1360 TRACE("(%p)\n",This);
1362 /* **** */
1363 EnterCriticalSection(&(This->lock));
1365 if (This->state == STATE_PLAYING)
1366 This->state = STATE_STOPPING;
1367 else if (This->state == STATE_STARTING)
1368 This->state = STATE_STOPPED;
1369 if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
1370 IDsDriverBuffer_Stop(This->hwbuf);
1371 This->state = STATE_STOPPED;
1373 DSOUND_CheckEvent(This, 0);
1375 LeaveCriticalSection(&(This->lock));
1376 /* **** */
1378 return DS_OK;
1381 static DWORD WINAPI IDirectSoundBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface) {
1382 ICOM_THIS(IDirectSoundBufferImpl,iface);
1383 DWORD ref;
1385 TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
1387 ref = InterlockedIncrement(&(This->ref));
1388 if (!ref) {
1389 FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n");
1391 return ref;
1393 static DWORD WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER iface) {
1394 ICOM_THIS(IDirectSoundBufferImpl,iface);
1395 int i;
1396 DWORD ref;
1398 TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
1400 ref = InterlockedDecrement(&(This->ref));
1401 if (ref) return ref;
1403 EnterCriticalSection(&(This->dsound->lock));
1404 for (i=0;i<This->dsound->nrofbuffers;i++)
1405 if (This->dsound->buffers[i] == This)
1406 break;
1408 if (i < This->dsound->nrofbuffers) {
1409 /* Put the last buffer of the list in the (now empty) position */
1410 This->dsound->buffers[i] = This->dsound->buffers[This->dsound->nrofbuffers - 1];
1411 This->dsound->nrofbuffers--;
1412 This->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,This->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*This->dsound->nrofbuffers);
1413 TRACE("buffer count is now %d\n", This->dsound->nrofbuffers);
1414 IDirectSound_Release((LPDIRECTSOUND)This->dsound);
1416 LeaveCriticalSection(&(This->dsound->lock));
1418 DeleteCriticalSection(&(This->lock));
1419 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1420 DSOUND_PrimaryClose(This);
1421 if (This->hwbuf) {
1422 IDsDriverBuffer_Release(This->hwbuf);
1424 if (This->ds3db)
1425 IDirectSound3DBuffer_Release((LPDIRECTSOUND3DBUFFER)This->ds3db);
1426 if (This->parent)
1427 /* this is a duplicate buffer */
1428 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->parent);
1429 else
1430 /* this is a toplevel buffer */
1431 HeapFree(GetProcessHeap(),0,This->buffer);
1433 HeapFree(GetProcessHeap(),0,This);
1435 if (This == primarybuf)
1436 primarybuf = NULL;
1438 return 0;
1441 static DWORD DSOUND_CalcPlayPosition(IDirectSoundBufferImpl *This,
1442 DWORD state, DWORD pplay, DWORD pwrite, DWORD pmix, DWORD bmix)
1444 DWORD bplay;
1446 TRACE("primary playpos=%ld, mixpos=%ld\n", pplay, pmix);
1447 TRACE("this mixpos=%ld\n", bmix);
1449 /* the actual primary play position (pplay) is always behind last mixed (pmix),
1450 * unless the computer is too slow or something */
1451 /* we need to know how far away we are from there */
1452 if (pmix == pplay) {
1453 if ((state == STATE_PLAYING) || (state == STATE_STOPPING)) {
1454 /* wow, the software mixer is really doing well,
1455 * seems the entire primary buffer is filled! */
1456 pmix += primarybuf->buflen;
1458 /* else: the primary buffer is not playing, so probably empty */
1460 if (pmix < pplay) pmix += primarybuf->buflen; /* wraparound */
1461 pmix -= pplay;
1462 /* detect buffer underrun */
1463 if (pwrite < pplay) pwrite += primarybuf->buflen; /* wraparound */
1464 pwrite -= pplay;
1465 if (pmix > (DS_SND_QUEUE * primarybuf->dsound->fraglen + pwrite + primarybuf->writelead)) {
1466 WARN("detected an underrun: primary queue was %ld\n",pmix);
1467 pmix = 0;
1469 /* divide the offset by its sample size */
1470 pmix /= primarybuf->wfx.nBlockAlign;
1471 TRACE("primary back-samples=%ld\n",pmix);
1472 /* adjust for our frequency */
1473 pmix = (pmix * This->freqAdjust) >> DSOUND_FREQSHIFT;
1474 /* multiply by our own sample size */
1475 pmix *= This->wfx.nBlockAlign;
1476 TRACE("this back-offset=%ld\n", pmix);
1477 /* subtract from our last mixed position */
1478 bplay = bmix;
1479 while (bplay < pmix) bplay += This->buflen; /* wraparound */
1480 bplay -= pmix;
1481 if (This->leadin && ((bplay < This->startpos) || (bplay > bmix))) {
1482 /* seems we haven't started playing yet */
1483 TRACE("this still in lead-in phase\n");
1484 bplay = This->startpos;
1486 /* return the result */
1487 return bplay;
1490 static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
1491 LPDIRECTSOUNDBUFFER iface,LPDWORD playpos,LPDWORD writepos
1493 HRESULT hres;
1494 ICOM_THIS(IDirectSoundBufferImpl,iface);
1495 TRACE("(%p,%p,%p)\n",This,playpos,writepos);
1496 if (This->hwbuf) {
1497 hres=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);
1498 if (hres)
1499 return hres;
1502 else if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
1503 if (playpos) {
1504 MMTIME mtime;
1505 mtime.wType = TIME_BYTES;
1506 waveOutGetPosition(This->dsound->hwo, &mtime, sizeof(mtime));
1507 mtime.u.cb = mtime.u.cb % This->buflen;
1508 *playpos = mtime.u.cb;
1510 if (writepos) {
1511 /* the writepos should only be used by apps with WRITEPRIMARY priority,
1512 * in which case our software mixer is disabled anyway */
1513 *writepos = This->playpos + DS_HEL_MARGIN * This->dsound->fraglen;
1514 while (*writepos >= This->buflen)
1515 *writepos -= This->buflen;
1517 } else {
1518 if (playpos && (This->state != STATE_PLAYING)) {
1519 /* we haven't been merged into the primary buffer (yet) */
1520 *playpos = This->buf_mixpos;
1522 else if (playpos) {
1523 DWORD pplay, pwrite, lplay, splay, pstate;
1524 /* let's get this exact; first, recursively call GetPosition on the primary */
1525 EnterCriticalSection(&(primarybuf->lock));
1526 if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || primarybuf->hwbuf || !DS_EMULDRIVER) {
1527 IDirectSoundBufferImpl_GetCurrentPosition((LPDIRECTSOUNDBUFFER)primarybuf, &pplay, &pwrite);
1528 /* detect HEL mode underrun */
1529 pstate = primarybuf->state;
1530 if (!(primarybuf->hwbuf || primarybuf->dsound->pwqueue)) {
1531 TRACE("detected an underrun\n");
1532 /* pplay = ? */
1533 if (pstate == STATE_PLAYING)
1534 pstate = STATE_STARTING;
1535 else if (pstate == STATE_STOPPING)
1536 pstate = STATE_STOPPED;
1538 /* get data for ourselves while we still have the lock */
1539 pstate &= This->state;
1540 lplay = This->primary_mixpos;
1541 splay = This->buf_mixpos;
1542 /* calculate play position using this */
1543 *playpos = DSOUND_CalcPlayPosition(This, pstate, pplay, pwrite, lplay, splay);
1544 } else {
1545 /* (unless the app isn't using GETCURRENTPOSITION2) */
1546 /* don't know exactly how this should be handled...
1547 * the docs says that play cursor is reported as directly
1548 * behind write cursor, hmm... */
1549 *playpos = This->playpos;
1551 LeaveCriticalSection(&(primarybuf->lock));
1553 if (writepos) *writepos = This->buf_mixpos;
1555 if (writepos) {
1556 if (This->state != STATE_STOPPED)
1557 /* apply the documented 10ms lead to writepos */
1558 *writepos += This->writelead;
1559 while (*writepos >= This->buflen) *writepos -= This->buflen;
1561 TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
1562 return DS_OK;
1565 static HRESULT WINAPI IDirectSoundBufferImpl_GetStatus(
1566 LPDIRECTSOUNDBUFFER iface,LPDWORD status
1568 ICOM_THIS(IDirectSoundBufferImpl,iface);
1569 TRACE("(%p,%p), thread is %lx\n",This,status,GetCurrentThreadId());
1571 if (status == NULL)
1572 return DSERR_INVALIDPARAM;
1574 *status = 0;
1575 if ((This->state == STATE_STARTING) || (This->state == STATE_PLAYING))
1576 *status |= DSBSTATUS_PLAYING;
1577 if (This->playflags & DSBPLAY_LOOPING)
1578 *status |= DSBSTATUS_LOOPING;
1580 TRACE("status=%lx\n", *status);
1581 return DS_OK;
1585 static HRESULT WINAPI IDirectSoundBufferImpl_GetFormat(
1586 LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
1588 ICOM_THIS(IDirectSoundBufferImpl,iface);
1589 TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
1591 if (wfsize>sizeof(This->wfx))
1592 wfsize = sizeof(This->wfx);
1593 if (lpwf) { /* NULL is valid */
1594 memcpy(lpwf,&(This->wfx),wfsize);
1595 if (wfwritten)
1596 *wfwritten = wfsize;
1597 } else
1598 if (wfwritten)
1599 *wfwritten = sizeof(This->wfx);
1600 else
1601 return DSERR_INVALIDPARAM;
1603 return DS_OK;
1606 static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
1607 LPDIRECTSOUNDBUFFER iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
1609 ICOM_THIS(IDirectSoundBufferImpl,iface);
1610 DWORD capf;
1612 TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx)\n",
1613 This,
1614 writecursor,
1615 writebytes,
1616 lplpaudioptr1,
1617 audiobytes1,
1618 lplpaudioptr2,
1619 audiobytes2,
1620 flags
1623 if (flags & DSBLOCK_FROMWRITECURSOR) {
1624 DWORD writepos;
1625 /* GetCurrentPosition does too much magic to duplicate here */
1626 IDirectSoundBufferImpl_GetCurrentPosition(iface, NULL, &writepos);
1627 writecursor += writepos;
1629 if (flags & DSBLOCK_ENTIREBUFFER)
1630 writebytes = This->buflen;
1631 if (writebytes > This->buflen)
1632 writebytes = This->buflen;
1634 assert(audiobytes1!=audiobytes2);
1635 assert(lplpaudioptr1!=lplpaudioptr2);
1637 if ((writebytes == This->buflen) &&
1638 ((This->state == STATE_STARTING) ||
1639 (This->state == STATE_PLAYING)))
1640 /* some games, like Half-Life, try to be clever (not) and
1641 * keep one secondary buffer, and mix sounds into it itself,
1642 * locking the entire buffer every time... so we can just forget
1643 * about tracking the last-written-to-position... */
1644 This->probably_valid_to = (DWORD)-1;
1645 else
1646 This->probably_valid_to = writecursor;
1648 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1649 capf = DSDDESC_DONTNEEDPRIMARYLOCK;
1650 else
1651 capf = DSDDESC_DONTNEEDSECONDARYLOCK;
1652 if (!(This->dsound->drvdesc.dwFlags & capf) && This->hwbuf) {
1653 IDsDriverBuffer_Lock(This->hwbuf,
1654 lplpaudioptr1, audiobytes1,
1655 lplpaudioptr2, audiobytes2,
1656 writecursor, writebytes,
1658 } else
1659 if (writecursor+writebytes <= This->buflen) {
1660 *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
1661 *audiobytes1 = writebytes;
1662 if (lplpaudioptr2)
1663 *(LPBYTE*)lplpaudioptr2 = NULL;
1664 if (audiobytes2)
1665 *audiobytes2 = 0;
1666 TRACE("->%ld.0\n",writebytes);
1667 } else {
1668 *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
1669 *audiobytes1 = This->buflen-writecursor;
1670 if (lplpaudioptr2)
1671 *(LPBYTE*)lplpaudioptr2 = This->buffer;
1672 if (audiobytes2)
1673 *audiobytes2 = writebytes-(This->buflen-writecursor);
1674 TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
1676 return DS_OK;
1679 static HRESULT WINAPI IDirectSoundBufferImpl_SetCurrentPosition(
1680 LPDIRECTSOUNDBUFFER iface,DWORD newpos
1682 ICOM_THIS(IDirectSoundBufferImpl,iface);
1683 TRACE("(%p,%ld)\n",This,newpos);
1685 /* **** */
1686 EnterCriticalSection(&(This->lock));
1688 This->buf_mixpos = newpos;
1689 if (This->hwbuf)
1690 IDsDriverBuffer_SetPosition(This->hwbuf, This->buf_mixpos);
1692 LeaveCriticalSection(&(This->lock));
1693 /* **** */
1695 return DS_OK;
1698 static HRESULT WINAPI IDirectSoundBufferImpl_SetPan(
1699 LPDIRECTSOUNDBUFFER iface,LONG pan
1701 ICOM_THIS(IDirectSoundBufferImpl,iface);
1703 TRACE("(%p,%ld)\n",This,pan);
1705 if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT))
1706 return DSERR_INVALIDPARAM;
1708 /* You cannot set the pan of the primary buffer */
1709 /* and you cannot use both pan and 3D controls */
1710 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
1711 (This->dsbd.dwFlags & DSBCAPS_CTRL3D) ||
1712 (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
1713 return DSERR_CONTROLUNAVAIL;
1715 /* **** */
1716 EnterCriticalSection(&(This->lock));
1718 This->volpan.lPan = pan;
1720 DSOUND_RecalcVolPan(&(This->volpan));
1722 if (This->hwbuf) {
1723 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
1726 LeaveCriticalSection(&(This->lock));
1727 /* **** */
1729 return DS_OK;
1732 static HRESULT WINAPI IDirectSoundBufferImpl_GetPan(
1733 LPDIRECTSOUNDBUFFER iface,LPLONG pan
1735 ICOM_THIS(IDirectSoundBufferImpl,iface);
1736 TRACE("(%p,%p)\n",This,pan);
1738 if (pan == NULL)
1739 return DSERR_INVALIDPARAM;
1741 *pan = This->volpan.lPan;
1743 return DS_OK;
1746 static HRESULT WINAPI IDirectSoundBufferImpl_Unlock(
1747 LPDIRECTSOUNDBUFFER iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
1749 ICOM_THIS(IDirectSoundBufferImpl,iface);
1750 DWORD capf, probably_valid_to;
1752 TRACE("(%p,%p,%ld,%p,%ld):stub\n", This,p1,x1,p2,x2);
1754 #if 0
1755 /* Preprocess 3D buffers... */
1757 /* This is highly experimental and liable to break things */
1758 if (This->dsbd.dwFlags & DSBCAPS_CTRL3D)
1759 DSOUND_Create3DBuffer(This);
1760 #endif
1762 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1763 capf = DSDDESC_DONTNEEDPRIMARYLOCK;
1764 else
1765 capf = DSDDESC_DONTNEEDSECONDARYLOCK;
1766 if (!(This->dsound->drvdesc.dwFlags & capf) && This->hwbuf) {
1767 IDsDriverBuffer_Unlock(This->hwbuf, p1, x1, p2, x2);
1770 if (p2) probably_valid_to = (((LPBYTE)p2)-This->buffer) + x2;
1771 else probably_valid_to = (((LPBYTE)p1)-This->buffer) + x1;
1772 while (probably_valid_to >= This->buflen)
1773 probably_valid_to -= This->buflen;
1774 if ((probably_valid_to == 0) && ((x1+x2) == This->buflen) &&
1775 ((This->state == STATE_STARTING) ||
1776 (This->state == STATE_PLAYING)))
1777 /* see IDirectSoundBufferImpl_Lock */
1778 probably_valid_to = (DWORD)-1;
1779 This->probably_valid_to = probably_valid_to;
1781 return DS_OK;
1784 static HRESULT WINAPI IDirectSoundBufferImpl_Restore(
1785 LPDIRECTSOUNDBUFFER iface
1787 ICOM_THIS(IDirectSoundBufferImpl,iface);
1788 FIXME("(%p):stub\n",This);
1789 return DS_OK;
1792 static HRESULT WINAPI IDirectSoundBufferImpl_GetFrequency(
1793 LPDIRECTSOUNDBUFFER iface,LPDWORD freq
1795 ICOM_THIS(IDirectSoundBufferImpl,iface);
1796 TRACE("(%p,%p)\n",This,freq);
1798 if (freq == NULL)
1799 return DSERR_INVALIDPARAM;
1801 *freq = This->freq;
1802 TRACE("-> %ld\n", *freq);
1804 return DS_OK;
1807 static HRESULT WINAPI IDirectSoundBufferImpl_Initialize(
1808 LPDIRECTSOUNDBUFFER iface,LPDIRECTSOUND dsound,LPDSBUFFERDESC dbsd
1810 ICOM_THIS(IDirectSoundBufferImpl,iface);
1811 FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
1812 DPRINTF("Re-Init!!!\n");
1813 return DSERR_ALREADYINITIALIZED;
1816 static HRESULT WINAPI IDirectSoundBufferImpl_GetCaps(
1817 LPDIRECTSOUNDBUFFER iface,LPDSBCAPS caps
1819 ICOM_THIS(IDirectSoundBufferImpl,iface);
1820 TRACE("(%p)->(%p)\n",This,caps);
1822 if (caps == NULL)
1823 return DSERR_INVALIDPARAM;
1825 /* I think we should check this value, not set it. See */
1826 /* Inside DirectX, p215. That should apply here, too. */
1827 caps->dwSize = sizeof(*caps);
1829 caps->dwFlags = This->dsbd.dwFlags;
1830 if (This->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
1831 else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
1833 caps->dwBufferBytes = This->dsbd.dwBufferBytes;
1835 /* This value represents the speed of the "unlock" command.
1836 As unlock is quite fast (it does not do anything), I put
1837 4096 ko/s = 4 Mo / s */
1838 /* FIXME: hwbuf speed */
1839 caps->dwUnlockTransferRate = 4096;
1840 caps->dwPlayCpuOverhead = 0;
1842 return DS_OK;
1845 static HRESULT WINAPI IDirectSoundBufferImpl_QueryInterface(
1846 LPDIRECTSOUNDBUFFER iface,REFIID riid,LPVOID *ppobj
1848 ICOM_THIS(IDirectSoundBufferImpl,iface);
1850 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
1852 if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
1853 IDirectSoundNotifyImpl *dsn;
1855 dsn = (IDirectSoundNotifyImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn));
1856 dsn->ref = 1;
1857 dsn->dsb = This;
1858 IDirectSoundBuffer_AddRef(iface);
1859 ICOM_VTBL(dsn) = &dsnvt;
1860 *ppobj = (LPVOID)dsn;
1861 return S_OK;
1864 #ifdef USE_DSOUND3D
1865 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1866 IDirectSound3DBufferImpl *ds3db;
1868 *ppobj = This->ds3db;
1869 if (*ppobj) {
1870 IDirectSound3DBuffer_AddRef((LPDIRECTSOUND3DBUFFER)This->ds3db);
1871 return S_OK;
1874 ds3db = (IDirectSound3DBufferImpl*)HeapAlloc(GetProcessHeap(),
1875 0,sizeof(*ds3db));
1876 ds3db->ref = 1;
1877 ds3db->dsb = This;
1878 ICOM_VTBL(ds3db) = &ds3dbvt;
1879 InitializeCriticalSection(&ds3db->lock);
1881 IDirectSoundBuffer_AddRef(iface);
1883 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
1884 ds3db->ds3db.vPosition.u1.x = 0.0;
1885 ds3db->ds3db.vPosition.u2.y = 0.0;
1886 ds3db->ds3db.vPosition.u3.z = 0.0;
1887 ds3db->ds3db.vVelocity.u1.x = 0.0;
1888 ds3db->ds3db.vVelocity.u2.y = 0.0;
1889 ds3db->ds3db.vVelocity.u3.z = 0.0;
1890 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
1891 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
1892 ds3db->ds3db.vConeOrientation.u1.x = 0.0;
1893 ds3db->ds3db.vConeOrientation.u2.y = 0.0;
1894 ds3db->ds3db.vConeOrientation.u3.z = 0.0;
1895 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME; ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
1896 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
1897 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
1898 ds3db->buflen = (This->buflen * primarybuf->wfx.nBlockAlign) /
1899 This->wfx.nBlockAlign;
1900 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
1901 if (ds3db->buffer == NULL) {
1902 ds3db->buflen = 0;
1903 ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
1906 ds3db->iks = (IKsPropertySetImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db->iks)));
1907 ds3db->iks->ref = 1;
1908 ds3db->iks->ds3db = ds3db;
1909 ICOM_VTBL(ds3db->iks) = &iksvt;
1911 return S_OK;
1913 #else
1914 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1915 FIXME("%s: I know about this GUID, but don't support it yet\n",
1916 debugstr_guid( riid ));
1917 *ppobj = NULL;
1918 return E_FAIL;
1920 #endif
1922 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1923 IDirectSound3DListenerImpl* dsl;
1925 if (This->dsound->listener) {
1926 *ppobj = This->dsound->listener;
1927 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)This->dsound->listener);
1928 return DS_OK;
1931 dsl = (IDirectSound3DListenerImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsl));
1932 dsl->ref = 1;
1933 ICOM_VTBL(dsl) = &ds3dlvt;
1934 *ppobj = (LPVOID)dsl;
1936 dsl->ds3dl.dwSize = sizeof(DS3DLISTENER);
1937 dsl->ds3dl.vPosition.u1.x = 0.0;
1938 dsl->ds3dl.vPosition.u2.y = 0.0;
1939 dsl->ds3dl.vPosition.u3.z = 0.0;
1940 dsl->ds3dl.vVelocity.u1.x = 0.0;
1941 dsl->ds3dl.vVelocity.u2.y = 0.0;
1942 dsl->ds3dl.vVelocity.u3.z = 0.0;
1943 dsl->ds3dl.vOrientFront.u1.x = 0.0;
1944 dsl->ds3dl.vOrientFront.u2.y = 0.0;
1945 dsl->ds3dl.vOrientFront.u3.z = 1.0;
1946 dsl->ds3dl.vOrientTop.u1.x = 0.0;
1947 dsl->ds3dl.vOrientTop.u2.y = 1.0;
1948 dsl->ds3dl.vOrientTop.u3.z = 0.0;
1949 dsl->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
1950 dsl->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
1952 InitializeCriticalSection(&dsl->lock);
1954 dsl->dsb = This;
1955 IDirectSoundBuffer_AddRef(iface);
1957 This->dsound->listener = dsl;
1958 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)dsl);
1960 return S_OK;
1963 FIXME( "Unknown GUID %s\n", debugstr_guid( riid ) );
1965 *ppobj = NULL;
1967 return E_FAIL;
1970 static ICOM_VTABLE(IDirectSoundBuffer) dsbvt =
1972 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1973 IDirectSoundBufferImpl_QueryInterface,
1974 IDirectSoundBufferImpl_AddRef,
1975 IDirectSoundBufferImpl_Release,
1976 IDirectSoundBufferImpl_GetCaps,
1977 IDirectSoundBufferImpl_GetCurrentPosition,
1978 IDirectSoundBufferImpl_GetFormat,
1979 IDirectSoundBufferImpl_GetVolume,
1980 IDirectSoundBufferImpl_GetPan,
1981 IDirectSoundBufferImpl_GetFrequency,
1982 IDirectSoundBufferImpl_GetStatus,
1983 IDirectSoundBufferImpl_Initialize,
1984 IDirectSoundBufferImpl_Lock,
1985 IDirectSoundBufferImpl_Play,
1986 IDirectSoundBufferImpl_SetCurrentPosition,
1987 IDirectSoundBufferImpl_SetFormat,
1988 IDirectSoundBufferImpl_SetVolume,
1989 IDirectSoundBufferImpl_SetPan,
1990 IDirectSoundBufferImpl_SetFrequency,
1991 IDirectSoundBufferImpl_Stop,
1992 IDirectSoundBufferImpl_Unlock,
1993 IDirectSoundBufferImpl_Restore
1996 /*******************************************************************************
1997 * IDirectSound
2000 static HRESULT WINAPI IDirectSoundImpl_SetCooperativeLevel(
2001 LPDIRECTSOUND iface,HWND hwnd,DWORD level
2003 ICOM_THIS(IDirectSoundImpl,iface);
2005 FIXME("(%p,%08lx,%ld):stub\n",This,(DWORD)hwnd,level);
2007 This->priolevel = level;
2009 return DS_OK;
2012 static HRESULT WINAPI IDirectSoundImpl_CreateSoundBuffer(
2013 LPDIRECTSOUND iface,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER ppdsb,LPUNKNOWN lpunk
2015 ICOM_THIS(IDirectSoundImpl,iface);
2016 IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
2017 LPWAVEFORMATEX wfex;
2018 HRESULT err = DS_OK;
2020 TRACE("(%p,%p,%p,%p)\n",This,dsbd,ippdsb,lpunk);
2022 if ((This == NULL) || (dsbd == NULL) || (ippdsb == NULL))
2023 return DSERR_INVALIDPARAM;
2025 if (TRACE_ON(dsound)) {
2026 TRACE("(structsize=%ld)\n",dsbd->dwSize);
2027 TRACE("(flags=0x%08lx:\n",dsbd->dwFlags);
2028 _dump_DSBCAPS(dsbd->dwFlags);
2029 DPRINTF(")\n");
2030 TRACE("(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
2031 TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
2034 wfex = dsbd->lpwfxFormat;
2036 if (wfex)
2037 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
2038 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
2039 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
2040 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
2041 wfex->wBitsPerSample, wfex->cbSize);
2043 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
2044 if (primarybuf) {
2045 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)primarybuf);
2046 *ippdsb = primarybuf;
2047 primarybuf->dsbd.dwFlags = dsbd->dwFlags;
2048 return DS_OK;
2049 } /* Else create primary buffer */
2052 *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
2053 if (*ippdsb == NULL)
2054 return DSERR_OUTOFMEMORY;
2055 ICOM_VTBL(*ippdsb) = &dsbvt;
2056 (*ippdsb)->ref = 1;
2057 (*ippdsb)->dsound = This;
2058 (*ippdsb)->parent = NULL;
2059 (*ippdsb)->buffer = NULL;
2061 memcpy(&((*ippdsb)->dsbd),dsbd,sizeof(*dsbd));
2062 if (dsbd->lpwfxFormat)
2063 memcpy(&((*ippdsb)->wfx), dsbd->lpwfxFormat, sizeof((*ippdsb)->wfx));
2065 TRACE("Created buffer at %p\n", *ippdsb);
2067 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
2068 (*ippdsb)->buflen = dsound->wfx.nAvgBytesPerSec;
2069 (*ippdsb)->freq = dsound->wfx.nSamplesPerSec;
2071 /* FIXME: verify that hardware capabilities (DSCAPS_PRIMARY flags) match */
2073 if (This->driver) {
2074 err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
2075 &((*ippdsb)->buflen),&((*ippdsb)->buffer),
2076 (LPVOID*)&((*ippdsb)->hwbuf));
2078 if (err == DS_OK)
2079 err = DSOUND_PrimaryOpen(*ippdsb);
2080 } else {
2081 DWORD capf = 0;
2082 int use_hw;
2084 (*ippdsb)->buflen = dsbd->dwBufferBytes;
2085 (*ippdsb)->freq = dsbd->lpwfxFormat->nSamplesPerSec;
2087 /* Check necessary hardware mixing capabilities */
2088 if (wfex->nChannels==2) capf |= DSCAPS_SECONDARYSTEREO;
2089 else capf |= DSCAPS_SECONDARYMONO;
2090 if (wfex->wBitsPerSample==16) capf |= DSCAPS_SECONDARY16BIT;
2091 else capf |= DSCAPS_SECONDARY8BIT;
2092 use_hw = (This->drvcaps.dwFlags & capf) == capf;
2094 /* FIXME: check hardware sample rate mixing capabilities */
2095 /* FIXME: check app hints for software/hardware buffer (STATIC, LOCHARDWARE, etc) */
2096 /* FIXME: check whether any hardware buffers are left */
2097 /* FIXME: handle DSDHEAP_CREATEHEAP for hardware buffers */
2099 /* Allocate system memory if applicable */
2100 if ((This->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) || !use_hw) {
2101 (*ippdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,(*ippdsb)->buflen);
2102 if ((*ippdsb)->buffer == NULL)
2103 err = DSERR_OUTOFMEMORY;
2106 /* Allocate the hardware buffer */
2107 if (use_hw && (err == DS_OK)) {
2108 err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
2109 &((*ippdsb)->buflen),&((*ippdsb)->buffer),
2110 (LPVOID*)&((*ippdsb)->hwbuf));
2114 if (err != DS_OK) {
2115 if ((*ippdsb)->buffer)
2116 HeapFree(GetProcessHeap(),0,(*ippdsb)->buffer);
2117 HeapFree(GetProcessHeap(),0,(*ippdsb));
2118 *ippdsb = NULL;
2119 return err;
2121 /* calculate fragment size and write lead */
2122 DSOUND_RecalcFormat(*ippdsb);
2124 /* It's not necessary to initialize values to zero since */
2125 /* we allocated this structure with HEAP_ZERO_MEMORY... */
2126 (*ippdsb)->playpos = 0;
2127 (*ippdsb)->buf_mixpos = 0;
2128 (*ippdsb)->state = STATE_STOPPED;
2129 DSOUND_RecalcVolPan(&((*ippdsb)->volpan));
2131 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
2132 (*ippdsb)->freqAdjust = ((*ippdsb)->freq << DSOUND_FREQSHIFT) /
2133 primarybuf->wfx.nSamplesPerSec;
2134 (*ippdsb)->nAvgBytesPerSec = (*ippdsb)->freq *
2135 dsbd->lpwfxFormat->nBlockAlign;
2138 EnterCriticalSection(&(This->lock));
2139 /* register buffer */
2140 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
2141 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl*)*(This->nrofbuffers+1));
2142 if (newbuffers) {
2143 This->buffers = newbuffers;
2144 This->buffers[This->nrofbuffers] = *ippdsb;
2145 This->nrofbuffers++;
2146 TRACE("buffer count is now %d\n", This->nrofbuffers);
2147 } else {
2148 ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
2149 err = DSERR_OUTOFMEMORY;
2152 LeaveCriticalSection(&(This->lock));
2154 IDirectSound_AddRef(iface);
2156 InitializeCriticalSection(&((*ippdsb)->lock));
2158 if (err != DS_OK) {
2159 /* oops... */
2160 IDirectSoundBuffer_Release(*ppdsb);
2161 *ippdsb = NULL;
2162 return err;
2165 #ifdef USE_DSOUND3D
2166 if (dsbd->dwFlags & DSBCAPS_CTRL3D) {
2167 IDirectSound3DBufferImpl *ds3db;
2169 ds3db = (IDirectSound3DBufferImpl*)HeapAlloc(GetProcessHeap(),
2170 0,sizeof(*ds3db));
2171 ICOM_VTBL(ds3db) = &ds3dbvt;
2172 ds3db->ref = 1;
2173 (*ippdsb)->ds3db = ds3db;
2175 ds3db->dsb = (*ippdsb);
2176 IDirectSoundBufferImpl_AddRef((LPDIRECTSOUNDBUFFER)(*ippdsb));
2178 InitializeCriticalSection(&ds3db->lock);
2180 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
2181 ds3db->ds3db.vPosition.u1.x = 0.0;
2182 ds3db->ds3db.vPosition.u2.y = 0.0;
2183 ds3db->ds3db.vPosition.u3.z = 0.0;
2184 ds3db->ds3db.vVelocity.u1.x = 0.0;
2185 ds3db->ds3db.vVelocity.u2.y = 0.0;
2186 ds3db->ds3db.vVelocity.u3.z = 0.0;
2187 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
2188 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
2189 ds3db->ds3db.vConeOrientation.u1.x = 0.0;
2190 ds3db->ds3db.vConeOrientation.u2.y = 0.0;
2191 ds3db->ds3db.vConeOrientation.u3.z = 0.0;
2192 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME;
2193 ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
2194 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
2195 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
2196 ds3db->buflen = ((*ippdsb)->buflen * primarybuf->wfx.nBlockAlign) /
2197 (*ippdsb)->wfx.nBlockAlign;
2198 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
2199 if (ds3db->buffer == NULL) {
2200 ds3db->buflen = 0;
2201 ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
2203 ds3db->iks = (IKsPropertySetImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db->iks)));
2204 ds3db->iks->ref = 1;
2205 ds3db->iks->ds3db = ds3db;
2206 ICOM_VTBL(ds3db->iks) = &iksvt;
2209 #endif
2210 return DS_OK;
2213 static HRESULT WINAPI IDirectSoundImpl_DuplicateSoundBuffer(
2214 LPDIRECTSOUND iface,LPDIRECTSOUNDBUFFER pdsb,LPLPDIRECTSOUNDBUFFER ppdsb
2216 ICOM_THIS(IDirectSoundImpl,iface);
2217 IDirectSoundBufferImpl* ipdsb=(IDirectSoundBufferImpl*)pdsb;
2218 IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
2219 TRACE("(%p,%p,%p)\n",This,ipdsb,ippdsb);
2221 if (ipdsb->hwbuf) {
2222 FIXME("need to duplicate hardware buffer\n");
2225 *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
2227 IDirectSoundBuffer_AddRef(pdsb);
2228 memcpy(*ippdsb, ipdsb, sizeof(IDirectSoundBufferImpl));
2229 (*ippdsb)->ref = 1;
2230 (*ippdsb)->state = STATE_STOPPED;
2231 (*ippdsb)->playpos = 0;
2232 (*ippdsb)->buf_mixpos = 0;
2233 (*ippdsb)->dsound = This;
2234 (*ippdsb)->parent = ipdsb;
2235 memcpy(&((*ippdsb)->wfx), &(ipdsb->wfx), sizeof((*ippdsb)->wfx));
2236 InitializeCriticalSection(&(*ippdsb)->lock);
2237 /* register buffer */
2238 EnterCriticalSection(&(This->lock));
2240 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl**)*(This->nrofbuffers+1));
2241 if (newbuffers) {
2242 This->buffers = newbuffers;
2243 This->buffers[This->nrofbuffers] = *ippdsb;
2244 This->nrofbuffers++;
2245 TRACE("buffer count is now %d\n", This->nrofbuffers);
2246 } else {
2247 ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
2248 /* FIXME: release buffer */
2251 LeaveCriticalSection(&(This->lock));
2252 IDirectSound_AddRef(iface);
2253 return DS_OK;
2257 static HRESULT WINAPI IDirectSoundImpl_GetCaps(LPDIRECTSOUND iface,LPDSCAPS caps) {
2258 ICOM_THIS(IDirectSoundImpl,iface);
2259 TRACE("(%p,%p)\n",This,caps);
2260 TRACE("(flags=0x%08lx)\n",caps->dwFlags);
2262 if (caps == NULL)
2263 return DSERR_INVALIDPARAM;
2265 /* We should check this value, not set it. See Inside DirectX, p215. */
2266 caps->dwSize = sizeof(*caps);
2268 caps->dwFlags = This->drvcaps.dwFlags;
2270 /* FIXME: copy caps from This->drvcaps */
2271 caps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
2272 caps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
2274 caps->dwPrimaryBuffers = 1;
2276 caps->dwMaxHwMixingAllBuffers = 0;
2277 caps->dwMaxHwMixingStaticBuffers = 0;
2278 caps->dwMaxHwMixingStreamingBuffers = 0;
2280 caps->dwFreeHwMixingAllBuffers = 0;
2281 caps->dwFreeHwMixingStaticBuffers = 0;
2282 caps->dwFreeHwMixingStreamingBuffers = 0;
2284 caps->dwMaxHw3DAllBuffers = 0;
2285 caps->dwMaxHw3DStaticBuffers = 0;
2286 caps->dwMaxHw3DStreamingBuffers = 0;
2288 caps->dwFreeHw3DAllBuffers = 0;
2289 caps->dwFreeHw3DStaticBuffers = 0;
2290 caps->dwFreeHw3DStreamingBuffers = 0;
2292 caps->dwTotalHwMemBytes = 0;
2294 caps->dwFreeHwMemBytes = 0;
2296 caps->dwMaxContigFreeHwMemBytes = 0;
2298 caps->dwUnlockTransferRateHwBuffers = 4096; /* But we have none... */
2300 caps->dwPlayCpuOverheadSwBuffers = 1; /* 1% */
2302 return DS_OK;
2305 static ULONG WINAPI IDirectSoundImpl_AddRef(LPDIRECTSOUND iface) {
2306 ICOM_THIS(IDirectSoundImpl,iface);
2307 return ++(This->ref);
2310 static ULONG WINAPI IDirectSoundImpl_Release(LPDIRECTSOUND iface) {
2311 ICOM_THIS(IDirectSoundImpl,iface);
2312 TRACE("(%p), ref was %ld\n",This,This->ref);
2313 if (!--(This->ref)) {
2314 UINT i;
2316 timeKillEvent(This->timerID);
2317 timeEndPeriod(DS_TIME_RES);
2319 if (primarybuf)
2320 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)primarybuf);
2322 if (This->buffers) {
2323 for( i=0;i<This->nrofbuffers;i++)
2324 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->buffers[i]);
2327 if (This->primary)
2328 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->primary);
2330 DeleteCriticalSection(&This->lock);
2331 if (This->driver) {
2332 IDsDriver_Close(This->driver);
2333 } else {
2334 unsigned c;
2335 for (c=0; c<DS_HEL_FRAGS; c++)
2336 HeapFree(GetProcessHeap(),0,This->pwave[c]);
2338 if (This->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
2339 waveOutClose(This->hwo);
2341 if (This->driver)
2342 IDsDriver_Release(This->driver);
2344 HeapFree(GetProcessHeap(),0,This);
2345 dsound = NULL;
2346 return 0;
2348 return This->ref;
2351 static HRESULT WINAPI IDirectSoundImpl_SetSpeakerConfig(
2352 LPDIRECTSOUND iface,DWORD config
2354 ICOM_THIS(IDirectSoundImpl,iface);
2355 FIXME("(%p,0x%08lx):stub\n",This,config);
2356 return DS_OK;
2359 static HRESULT WINAPI IDirectSoundImpl_QueryInterface(
2360 LPDIRECTSOUND iface,REFIID riid,LPVOID *ppobj
2362 ICOM_THIS(IDirectSoundImpl,iface);
2364 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
2366 if (This->listener) {
2367 *ppobj = This->listener;
2368 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)This->listener);
2369 return DS_OK;
2372 This->listener = (IDirectSound3DListenerImpl*)HeapAlloc(
2373 GetProcessHeap(), 0, sizeof(*(This->listener)));
2374 This->listener->ref = 1;
2375 ICOM_VTBL(This->listener) = &ds3dlvt;
2376 *ppobj = (LPVOID)This->listener;
2377 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
2379 This->listener->dsb = NULL;
2381 This->listener->ds3dl.dwSize = sizeof(DS3DLISTENER);
2382 This->listener->ds3dl.vPosition.u1.x = 0.0;
2383 This->listener->ds3dl.vPosition.u2.y = 0.0;
2384 This->listener->ds3dl.vPosition.u3.z = 0.0;
2385 This->listener->ds3dl.vVelocity.u1.x = 0.0;
2386 This->listener->ds3dl.vVelocity.u2.y = 0.0;
2387 This->listener->ds3dl.vVelocity.u3.z = 0.0;
2388 This->listener->ds3dl.vOrientFront.u1.x = 0.0;
2389 This->listener->ds3dl.vOrientFront.u2.y = 0.0;
2390 This->listener->ds3dl.vOrientFront.u3.z = 1.0;
2391 This->listener->ds3dl.vOrientTop.u1.x = 0.0;
2392 This->listener->ds3dl.vOrientTop.u2.y = 1.0;
2393 This->listener->ds3dl.vOrientTop.u3.z = 0.0;
2394 This->listener->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
2395 This->listener->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
2396 This->listener->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
2398 InitializeCriticalSection(&This->listener->lock);
2400 return DS_OK;
2403 FIXME("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
2404 return E_FAIL;
2407 static HRESULT WINAPI IDirectSoundImpl_Compact(
2408 LPDIRECTSOUND iface)
2410 ICOM_THIS(IDirectSoundImpl,iface);
2411 TRACE("(%p)\n", This);
2412 return DS_OK;
2415 static HRESULT WINAPI IDirectSoundImpl_GetSpeakerConfig(
2416 LPDIRECTSOUND iface,
2417 LPDWORD lpdwSpeakerConfig)
2419 ICOM_THIS(IDirectSoundImpl,iface);
2420 TRACE("(%p, %p)\n", This, lpdwSpeakerConfig);
2421 *lpdwSpeakerConfig = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16);
2422 return DS_OK;
2425 static HRESULT WINAPI IDirectSoundImpl_Initialize(
2426 LPDIRECTSOUND iface,
2427 LPCGUID lpcGuid)
2429 ICOM_THIS(IDirectSoundImpl,iface);
2430 TRACE("(%p, %p)\n", This, lpcGuid);
2431 return DS_OK;
2434 static ICOM_VTABLE(IDirectSound) dsvt =
2436 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2437 IDirectSoundImpl_QueryInterface,
2438 IDirectSoundImpl_AddRef,
2439 IDirectSoundImpl_Release,
2440 IDirectSoundImpl_CreateSoundBuffer,
2441 IDirectSoundImpl_GetCaps,
2442 IDirectSoundImpl_DuplicateSoundBuffer,
2443 IDirectSoundImpl_SetCooperativeLevel,
2444 IDirectSoundImpl_Compact,
2445 IDirectSoundImpl_GetSpeakerConfig,
2446 IDirectSoundImpl_SetSpeakerConfig,
2447 IDirectSoundImpl_Initialize
2451 static void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len)
2453 int i;
2454 DWORD offset;
2455 LPDSBPOSITIONNOTIFY event;
2457 if (dsb->nrofnotifies == 0)
2458 return;
2460 TRACE("(%p) buflen = %ld, playpos = %ld, len = %d\n",
2461 dsb, dsb->buflen, dsb->playpos, len);
2462 for (i = 0; i < dsb->nrofnotifies ; i++) {
2463 event = dsb->notifies + i;
2464 offset = event->dwOffset;
2465 TRACE("checking %d, position %ld, event = %d\n",
2466 i, offset, event->hEventNotify);
2467 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
2468 /* OK. [Inside DirectX, p274] */
2469 /* */
2470 /* This also means we can't sort the entries by offset, */
2471 /* because DSBPN_OFFSETSTOP == -1 */
2472 if (offset == DSBPN_OFFSETSTOP) {
2473 if (dsb->state == STATE_STOPPED) {
2474 SetEvent(event->hEventNotify);
2475 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2476 return;
2477 } else
2478 return;
2480 if ((dsb->playpos + len) >= dsb->buflen) {
2481 if ((offset < ((dsb->playpos + len) % dsb->buflen)) ||
2482 (offset >= dsb->playpos)) {
2483 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2484 SetEvent(event->hEventNotify);
2486 } else {
2487 if ((offset >= dsb->playpos) && (offset < (dsb->playpos + len))) {
2488 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2489 SetEvent(event->hEventNotify);
2495 /* WAV format info can be found at: */
2496 /* */
2497 /* http://www.cwi.nl/ftp/audio/AudioFormats.part2 */
2498 /* ftp://ftp.cwi.nl/pub/audio/RIFF-format */
2499 /* */
2500 /* Import points to remember: */
2501 /* */
2502 /* 8-bit WAV is unsigned */
2503 /* 16-bit WAV is signed */
2505 static inline INT16 cvtU8toS16(BYTE byte)
2507 INT16 s = (byte - 128) << 8;
2509 return s;
2512 static inline BYTE cvtS16toU8(INT16 word)
2514 BYTE b = (word + 32768) >> 8;
2516 return b;
2520 /* We should be able to optimize these two inline functions */
2521 /* so that we aren't doing 8->16->8 conversions when it is */
2522 /* not necessary. But this is still a WIP. Optimize later. */
2523 static inline void get_fields(const IDirectSoundBufferImpl *dsb, BYTE *buf, INT *fl, INT *fr)
2525 INT16 *bufs = (INT16 *) buf;
2527 /* TRACE("(%p)\n", buf); */
2528 if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 2) {
2529 *fl = cvtU8toS16(*buf);
2530 *fr = cvtU8toS16(*(buf + 1));
2531 return;
2534 if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 2) {
2535 *fl = *bufs;
2536 *fr = *(bufs + 1);
2537 return;
2540 if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 1) {
2541 *fl = cvtU8toS16(*buf);
2542 *fr = *fl;
2543 return;
2546 if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 1) {
2547 *fl = *bufs;
2548 *fr = *bufs;
2549 return;
2552 FIXME("get_fields found an unsupported configuration\n");
2553 return;
2556 static inline void set_fields(BYTE *buf, INT fl, INT fr)
2558 INT16 *bufs = (INT16 *) buf;
2560 if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 2)) {
2561 *buf = cvtS16toU8(fl);
2562 *(buf + 1) = cvtS16toU8(fr);
2563 return;
2566 if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 2)) {
2567 *bufs = fl;
2568 *(bufs + 1) = fr;
2569 return;
2572 if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 1)) {
2573 *buf = cvtS16toU8((fl + fr) >> 1);
2574 return;
2577 if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 1)) {
2578 *bufs = (fl + fr) >> 1;
2579 return;
2581 FIXME("set_fields found an unsupported configuration\n");
2582 return;
2585 /* Now with PerfectPitch (tm) technology */
2586 static INT DSOUND_MixerNorm(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2588 INT i, size, ipos, ilen, fieldL, fieldR;
2589 BYTE *ibp, *obp;
2590 INT iAdvance = dsb->wfx.nBlockAlign;
2591 INT oAdvance = primarybuf->wfx.nBlockAlign;
2593 ibp = dsb->buffer + dsb->buf_mixpos;
2594 obp = buf;
2596 TRACE("(%p, %p, %p), buf_mixpos=%ld\n", dsb, ibp, obp, dsb->buf_mixpos);
2597 /* Check for the best case */
2598 if ((dsb->freq == primarybuf->wfx.nSamplesPerSec) &&
2599 (dsb->wfx.wBitsPerSample == primarybuf->wfx.wBitsPerSample) &&
2600 (dsb->wfx.nChannels == primarybuf->wfx.nChannels)) {
2601 DWORD bytesleft = dsb->buflen - dsb->buf_mixpos;
2602 TRACE("(%p) Best case\n", dsb);
2603 if (len <= bytesleft )
2604 memcpy(obp, ibp, len);
2605 else { /* wrap */
2606 memcpy(obp, ibp, bytesleft );
2607 memcpy(obp + bytesleft, dsb->buffer, len - bytesleft);
2609 return len;
2612 /* Check for same sample rate */
2613 if (dsb->freq == primarybuf->wfx.nSamplesPerSec) {
2614 TRACE("(%p) Same sample rate %ld = primary %ld\n", dsb,
2615 dsb->freq, primarybuf->wfx.nSamplesPerSec);
2616 ilen = 0;
2617 for (i = 0; i < len; i += oAdvance) {
2618 get_fields(dsb, ibp, &fieldL, &fieldR);
2619 ibp += iAdvance;
2620 ilen += iAdvance;
2621 set_fields(obp, fieldL, fieldR);
2622 obp += oAdvance;
2623 if (ibp >= (BYTE *)(dsb->buffer + dsb->buflen))
2624 ibp = dsb->buffer; /* wrap */
2626 return (ilen);
2629 /* Mix in different sample rates */
2630 /* */
2631 /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
2632 /* Patent Pending :-] */
2634 /* Patent enhancements (c) 2000 Ove KÃ¥ven,
2635 * TransGaming Technologies Inc. */
2637 TRACE("(%p) Adjusting frequency: %ld -> %ld\n",
2638 dsb, dsb->freq, primarybuf->wfx.nSamplesPerSec);
2640 size = len / oAdvance;
2641 ilen = 0;
2642 ipos = dsb->buf_mixpos;
2643 for (i = 0; i < size; i++) {
2644 get_fields(dsb, (dsb->buffer + ipos), &fieldL, &fieldR);
2645 set_fields(obp, fieldL, fieldR);
2646 obp += oAdvance;
2648 dsb->freqAcc += dsb->freqAdjust;
2649 if (dsb->freqAcc >= (1<<DSOUND_FREQSHIFT)) {
2650 ULONG adv = (dsb->freqAcc>>DSOUND_FREQSHIFT) * iAdvance;
2651 dsb->freqAcc &= (1<<DSOUND_FREQSHIFT)-1;
2652 ipos += adv; ilen += adv;
2653 while (ipos >= dsb->buflen)
2654 ipos -= dsb->buflen;
2657 return ilen;
2660 static void DSOUND_MixerVol(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2662 INT i, inc = primarybuf->wfx.wBitsPerSample >> 3;
2663 BYTE *bpc = buf;
2664 INT16 *bps = (INT16 *) buf;
2666 TRACE("(%p) left = %lx, right = %lx\n", dsb,
2667 dsb->volpan.dwTotalLeftAmpFactor, dsb->volpan.dwTotalRightAmpFactor);
2668 if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->volpan.lPan == 0)) &&
2669 (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->volpan.lVolume == 0)) &&
2670 !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
2671 return; /* Nothing to do */
2673 /* If we end up with some bozo coder using panning or 3D sound */
2674 /* with a mono primary buffer, it could sound very weird using */
2675 /* this method. Oh well, tough patooties. */
2677 for (i = 0; i < len; i += inc) {
2678 INT val;
2680 switch (inc) {
2682 case 1:
2683 /* 8-bit WAV is unsigned, but we need to operate */
2684 /* on signed data for this to work properly */
2685 val = *bpc - 128;
2686 val = ((val * (i & inc ? dsb->volpan.dwTotalRightAmpFactor : dsb->volpan.dwTotalLeftAmpFactor)) >> 16);
2687 *bpc = val + 128;
2688 bpc++;
2689 break;
2690 case 2:
2691 /* 16-bit WAV is signed -- much better */
2692 val = *bps;
2693 val = ((val * ((i & inc) ? dsb->volpan.dwTotalRightAmpFactor : dsb->volpan.dwTotalLeftAmpFactor)) >> 16);
2694 *bps = val;
2695 bps++;
2696 break;
2697 default:
2698 /* Very ugly! */
2699 FIXME("MixerVol had a nasty error\n");
2704 #ifdef USE_DSOUND3D
2705 static void DSOUND_Mixer3D(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2707 BYTE *ibp, *obp;
2708 DWORD buflen, buf_mixpos;
2710 buflen = dsb->ds3db->buflen;
2711 buf_mixpos = (dsb->buf_mixpos * primarybuf->wfx.nBlockAlign) / dsb->wfx.nBlockAlign;
2712 ibp = dsb->ds3db->buffer + buf_mixpos;
2713 obp = buf;
2715 if (buf_mixpos > buflen) {
2716 FIXME("Major breakage\n");
2717 return;
2720 if (len <= (buf_mixpos + buflen))
2721 memcpy(obp, ibp, len);
2722 else { /* wrap */
2723 memcpy(obp, ibp, buflen - buf_mixpos);
2724 memcpy(obp + (buflen - buf_mixpos),
2725 dsb->buffer,
2726 len - (buflen - buf_mixpos));
2728 return;
2730 #endif
2732 static void *tmp_buffer;
2733 static size_t tmp_buffer_len = 0;
2735 static void *DSOUND_tmpbuffer(size_t len)
2737 if (len>tmp_buffer_len) {
2738 void *new_buffer = realloc(tmp_buffer, len);
2739 if (new_buffer) {
2740 tmp_buffer = new_buffer;
2741 tmp_buffer_len = len;
2743 return new_buffer;
2745 return tmp_buffer;
2748 static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen)
2750 INT i, len, ilen, temp, field;
2751 INT advance = primarybuf->wfx.wBitsPerSample >> 3;
2752 BYTE *buf, *ibuf, *obuf;
2753 INT16 *ibufs, *obufs;
2755 len = fraglen;
2756 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
2757 temp = MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buflen,
2758 dsb->nAvgBytesPerSec) -
2759 MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buf_mixpos,
2760 dsb->nAvgBytesPerSec);
2761 len = (len > temp) ? temp : len;
2763 len &= ~3; /* 4 byte alignment */
2765 if (len == 0) {
2766 /* This should only happen if we aren't looping and temp < 4 */
2768 /* We skip the remainder, so check for possible events */
2769 DSOUND_CheckEvent(dsb, dsb->buflen - dsb->buf_mixpos);
2770 /* Stop */
2771 dsb->state = STATE_STOPPED;
2772 dsb->playpos = 0;
2773 dsb->buf_mixpos = 0;
2774 dsb->leadin = FALSE;
2775 /* Check for DSBPN_OFFSETSTOP */
2776 DSOUND_CheckEvent(dsb, 0);
2777 return 0;
2780 /* Been seeing segfaults in malloc() for some reason... */
2781 TRACE("allocating buffer (size = %d)\n", len);
2782 if ((buf = ibuf = (BYTE *) DSOUND_tmpbuffer(len)) == NULL)
2783 return 0;
2785 TRACE("MixInBuffer (%p) len = %d, dest = %ld\n", dsb, len, writepos);
2787 ilen = DSOUND_MixerNorm(dsb, ibuf, len);
2788 if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
2789 (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
2790 DSOUND_MixerVol(dsb, ibuf, len);
2792 obuf = primarybuf->buffer + writepos;
2793 for (i = 0; i < len; i += advance) {
2794 obufs = (INT16 *) obuf;
2795 ibufs = (INT16 *) ibuf;
2796 if (primarybuf->wfx.wBitsPerSample == 8) {
2797 /* 8-bit WAV is unsigned */
2798 field = (*ibuf - 128);
2799 field += (*obuf - 128);
2800 field = field > 127 ? 127 : field;
2801 field = field < -128 ? -128 : field;
2802 *obuf = field + 128;
2803 } else {
2804 /* 16-bit WAV is signed */
2805 field = *ibufs;
2806 field += *obufs;
2807 field = field > 32767 ? 32767 : field;
2808 field = field < -32768 ? -32768 : field;
2809 *obufs = field;
2811 ibuf += advance;
2812 obuf += advance;
2813 if (obuf >= (BYTE *)(primarybuf->buffer + primarybuf->buflen))
2814 obuf = primarybuf->buffer;
2816 /* free(buf); */
2818 if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY)
2819 DSOUND_CheckEvent(dsb, ilen);
2821 if (dsb->leadin && (dsb->startpos > dsb->buf_mixpos) && (dsb->startpos <= dsb->buf_mixpos + ilen)) {
2822 /* HACK... leadin should be reset when the PLAY position reaches the startpos,
2823 * not the MIX position... but if the sound buffer is bigger than our prebuffering
2824 * (which must be the case for the streaming buffers that need this hack anyway)
2825 * plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
2826 dsb->leadin = FALSE;
2829 dsb->buf_mixpos += ilen;
2831 if (dsb->buf_mixpos >= dsb->buflen) {
2832 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
2833 dsb->state = STATE_STOPPED;
2834 dsb->playpos = 0;
2835 dsb->buf_mixpos = 0;
2836 dsb->leadin = FALSE;
2837 DSOUND_CheckEvent(dsb, 0); /* For DSBPN_OFFSETSTOP */
2838 } else {
2839 /* wrap */
2840 while (dsb->buf_mixpos >= dsb->buflen)
2841 dsb->buf_mixpos -= dsb->buflen;
2842 if (dsb->leadin && (dsb->startpos <= dsb->buf_mixpos))
2843 dsb->leadin = FALSE; /* HACK: see above */
2847 return len;
2850 static void DSOUND_MixCancel(IDirectSoundBufferImpl *dsb, DWORD writepos)
2852 FIXME("prebuffer cancel not implemented yet\n");
2855 static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD playpos, DWORD writepos, DWORD mixlen)
2857 DWORD len, slen;
2858 /* determine this buffer's write position */
2859 DWORD buf_writepos = DSOUND_CalcPlayPosition(dsb, dsb->state & primarybuf->state, writepos,
2860 writepos, dsb->primary_mixpos, dsb->buf_mixpos);
2861 /* determine how much already-mixed data exists */
2862 DWORD buf_done =
2863 ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
2864 dsb->buf_mixpos - buf_writepos;
2865 DWORD primary_done =
2866 ((dsb->primary_mixpos < writepos) ? primarybuf->buflen : 0) +
2867 dsb->primary_mixpos - writepos;
2868 DWORD adv_done =
2869 ((primarybuf->buf_mixpos < writepos) ? primarybuf->buflen : 0) +
2870 primarybuf->buf_mixpos - writepos;
2871 int still_behind;
2873 TRACE("buf_writepos=%ld, primary_writepos=%ld\n", buf_writepos, writepos);
2874 TRACE("buf_done=%ld, primary_done=%ld\n", buf_done, primary_done);
2875 TRACE("buf_mixpos=%ld, primary_mixpos=%ld, mixlen=%ld\n", dsb->buf_mixpos, dsb->primary_mixpos,
2876 mixlen);
2877 TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb->playflags, dsb->startpos, dsb->leadin);
2879 /* save write position for non-GETCURRENTPOSITION2... */
2880 dsb->playpos = buf_writepos;
2882 /* check whether CalcPlayPosition detected a mixing underrun */
2883 if ((buf_done == 0) && (dsb->primary_mixpos != writepos)) {
2884 /* it did, but did we have more to play? */
2885 if ((dsb->playflags & DSBPLAY_LOOPING) ||
2886 (dsb->buf_mixpos < dsb->buflen)) {
2887 /* yes, have to recover */
2888 ERR("underrun on sound buffer %p\n", dsb);
2889 TRACE("recovering from underrun: primary_mixpos=%ld\n", writepos);
2891 dsb->primary_mixpos = writepos;
2892 primary_done = 0;
2894 /* determine how far ahead we should mix */
2895 if (((dsb->playflags & DSBPLAY_LOOPING) ||
2896 (dsb->leadin && (dsb->probably_valid_to != 0))) &&
2897 !(dsb->dsbd.dwFlags & DSBCAPS_STATIC)) {
2898 /* if this is a streaming buffer, it typically means that
2899 * we should defer mixing past probably_valid_to as long
2900 * as we can, to avoid unnecessary remixing */
2901 /* the heavy-looking calculations shouldn't be that bad,
2902 * as any game isn't likely to be have more than 1 or 2
2903 * streaming buffers in use at any time anyway... */
2904 DWORD probably_valid_left =
2905 (dsb->probably_valid_to == (DWORD)-1) ? dsb->buflen :
2906 ((dsb->probably_valid_to < buf_writepos) ? dsb->buflen : 0) +
2907 dsb->probably_valid_to - buf_writepos;
2908 /* check for leadin condition */
2909 if ((probably_valid_left == 0) &&
2910 (dsb->probably_valid_to == dsb->startpos) &&
2911 dsb->leadin)
2912 probably_valid_left = dsb->buflen;
2913 TRACE("streaming buffer probably_valid_to=%ld, probably_valid_left=%ld\n",
2914 dsb->probably_valid_to, probably_valid_left);
2915 /* check whether the app's time is already up */
2916 if (probably_valid_left < dsb->writelead) {
2917 WARN("probably_valid_to now within writelead, possible streaming underrun\n");
2918 /* once we pass the point of no return,
2919 * no reason to hold back anymore */
2920 dsb->probably_valid_to = (DWORD)-1;
2921 /* we just have to go ahead and mix what we have,
2922 * there's no telling what the app is thinking anyway */
2923 } else {
2924 /* divide valid length by our sample size */
2925 probably_valid_left /= dsb->wfx.nBlockAlign;
2926 /* adjust for our frequency */
2927 probably_valid_left = (probably_valid_left << DSOUND_FREQSHIFT) / dsb->freqAdjust;
2928 /* multiply by primary sample size */
2929 probably_valid_left *= primarybuf->wfx.nBlockAlign;
2930 /* check whether to clip mix_len */
2931 if (probably_valid_left < mixlen) {
2932 TRACE("clipping to probably_valid_left=%ld\n", probably_valid_left);
2933 mixlen = probably_valid_left;
2937 /* cut mixlen with what's already been mixed */
2938 if (mixlen < primary_done) {
2939 /* huh? and still CalcPlayPosition didn't
2940 * detect an underrun? */
2941 FIXME("problem with underrun detection (mixlen=%ld < primary_done=%ld)\n", mixlen, primary_done);
2942 return 0;
2944 len = mixlen - primary_done;
2945 TRACE("remaining mixlen=%ld\n", len);
2947 if (len < primarybuf->dsound->fraglen) {
2948 /* smaller than a fragment, wait until it gets larger
2949 * before we take the mixing overhead */
2950 TRACE("mixlen not worth it, deferring mixing\n");
2951 return 0;
2954 /* ok, we know how much to mix, let's go */
2955 still_behind = (adv_done > primary_done);
2956 while (len) {
2957 slen = primarybuf->buflen - dsb->primary_mixpos;
2958 if (slen > len) slen = len;
2959 slen = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, slen);
2961 if ((dsb->primary_mixpos < primarybuf->buf_mixpos) &&
2962 (dsb->primary_mixpos + slen >= primarybuf->buf_mixpos))
2963 still_behind = FALSE;
2965 dsb->primary_mixpos += slen; len -= slen;
2966 while (dsb->primary_mixpos >= primarybuf->buflen)
2967 dsb->primary_mixpos -= primarybuf->buflen;
2969 if ((dsb->state == STATE_STOPPED) || !slen) break;
2971 TRACE("new primary_mixpos=%ld, primary_advbase=%ld\n", dsb->primary_mixpos, primarybuf->buf_mixpos);
2972 TRACE("mixed data len=%ld, still_behind=%d\n", mixlen-len, still_behind);
2973 /* return how far we think the primary buffer can
2974 * advance its underrun detector...*/
2975 if (still_behind) return 0;
2976 if ((mixlen - len) < primary_done) return 0;
2977 slen = ((dsb->primary_mixpos < primarybuf->buf_mixpos) ?
2978 primarybuf->buflen : 0) + dsb->primary_mixpos -
2979 primarybuf->buf_mixpos;
2980 if (slen > mixlen) {
2981 /* the primary_done and still_behind checks above should have worked */
2982 FIXME("problem with advancement calculation (advlen=%ld > mixlen=%ld)\n", slen, mixlen);
2983 slen = 0;
2985 return slen;
2988 static DWORD DSOUND_MixToPrimary(DWORD playpos, DWORD writepos, DWORD mixlen, BOOL recover)
2990 INT i, len, maxlen = 0;
2991 IDirectSoundBufferImpl *dsb;
2993 TRACE("(%ld,%ld,%ld)\n", playpos, writepos, mixlen);
2994 for (i = dsound->nrofbuffers - 1; i >= 0; i--) {
2995 dsb = dsound->buffers[i];
2997 if (!dsb || !(ICOM_VTBL(dsb)))
2998 continue;
2999 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
3000 TRACE("Checking %p, mixlen=%ld\n", dsb, mixlen);
3001 EnterCriticalSection(&(dsb->lock));
3002 if (dsb->state == STATE_STOPPING) {
3003 DSOUND_MixCancel(dsb, writepos);
3004 dsb->state = STATE_STOPPED;
3005 } else {
3006 if ((dsb->state == STATE_STARTING) || recover)
3007 dsb->primary_mixpos = writepos;
3008 len = DSOUND_MixOne(dsb, playpos, writepos, mixlen);
3009 if (dsb->state == STATE_STARTING)
3010 dsb->state = STATE_PLAYING;
3011 maxlen = (len > maxlen) ? len : maxlen;
3013 LeaveCriticalSection(&(dsb->lock));
3017 return maxlen;
3020 static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
3022 int nfiller;
3023 BOOL forced;
3024 HRESULT hres;
3026 if (!dsound || !primarybuf) {
3027 ERR("dsound died without killing us?\n");
3028 timeKillEvent(timerID);
3029 timeEndPeriod(DS_TIME_RES);
3030 return;
3033 EnterCriticalSection(&(dsound->lock));
3035 if (!primarybuf || !primarybuf->ref) {
3036 /* seems the primary buffer is currently being released */
3037 LeaveCriticalSection(&(dsound->lock));
3038 return;
3041 /* the sound of silence */
3042 nfiller = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
3044 /* whether the primary is forced to play even without secondary buffers */
3045 forced = ((primarybuf->state == STATE_PLAYING) || (primarybuf->state == STATE_STARTING));
3047 if (primarybuf->hwbuf) {
3048 if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
3049 BOOL paused = ((primarybuf->state == STATE_STOPPED) || (primarybuf->state == STATE_STARTING));
3050 /* FIXME: document variables */
3051 DWORD playpos, writepos, inq, maxq, frag;
3052 hres = IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, &writepos);
3053 if (hres) {
3054 LeaveCriticalSection(&(dsound->lock));
3055 return;
3057 /* Well, we *could* do Just-In-Time mixing using the writepos,
3058 * but that's a little bit ambitious and unnecessary... */
3059 /* rather add our safety margin to the writepos, if we're playing */
3060 if (!paused) {
3061 writepos += primarybuf->writelead;
3062 while (writepos >= primarybuf->buflen)
3063 writepos -= primarybuf->buflen;
3064 } else writepos = playpos;
3065 TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld\n",
3066 playpos,writepos,primarybuf->playpos,primarybuf->buf_mixpos);
3067 /* wipe out just-played sound data */
3068 if (playpos < primarybuf->playpos) {
3069 memset(primarybuf->buffer + primarybuf->playpos, nfiller, primarybuf->buflen - primarybuf->playpos);
3070 memset(primarybuf->buffer, nfiller, playpos);
3071 } else {
3072 memset(primarybuf->buffer + primarybuf->playpos, nfiller, playpos - primarybuf->playpos);
3074 primarybuf->playpos = playpos;
3076 /* check how much prebuffering is left */
3077 inq = primarybuf->buf_mixpos;
3078 if (inq < writepos)
3079 inq += primarybuf->buflen;
3080 inq -= writepos;
3082 /* find the maximum we can prebuffer */
3083 if (!paused) {
3084 maxq = playpos;
3085 if (maxq < writepos)
3086 maxq += primarybuf->buflen;
3087 maxq -= writepos;
3088 } else maxq = primarybuf->buflen;
3090 /* clip maxq to DS_SND_QUEUE */
3091 frag = DS_SND_QUEUE * dsound->fraglen;
3092 if (maxq > frag) maxq = frag;
3094 EnterCriticalSection(&(primarybuf->lock));
3096 /* check for consistency */
3097 if (inq > maxq) {
3098 /* the playback position must have passed our last
3099 * mixed position, i.e. it's an underrun, or we have
3100 * nothing more to play */
3101 TRACE("reached end of mixed data (inq=%ld, maxq=%ld)\n", inq, maxq);
3102 inq = 0;
3103 /* stop the playback now, to allow buffers to refill */
3104 DSOUND_PrimaryStop(primarybuf);
3105 if (primarybuf->state == STATE_PLAYING) {
3106 primarybuf->state = STATE_STARTING;
3108 else if (primarybuf->state == STATE_STOPPING) {
3109 primarybuf->state = STATE_STOPPED;
3111 else {
3112 /* how can we have an underrun if we aren't playing? */
3113 WARN("unexpected primary state (%ld)\n", primarybuf->state);
3115 /* the Stop is supposed to reset play position to beginning of buffer */
3116 /* unfortunately, OSS is not able to do so, so get current pointer */
3117 hres = IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, NULL);
3118 if (hres) {
3119 LeaveCriticalSection(&(dsound->lock));
3120 LeaveCriticalSection(&(primarybuf->lock));
3121 return;
3123 writepos = playpos;
3124 primarybuf->playpos = playpos;
3125 primarybuf->buf_mixpos = writepos;
3126 inq = 0;
3127 maxq = primarybuf->buflen;
3128 if (maxq > frag) maxq = frag;
3129 memset(primarybuf->buffer, nfiller, primarybuf->buflen);
3130 paused = TRUE;
3133 /* do the mixing */
3134 frag = DSOUND_MixToPrimary(playpos, writepos, maxq, paused);
3135 if (forced) frag = maxq - inq;
3136 primarybuf->buf_mixpos += frag;
3137 while (primarybuf->buf_mixpos >= primarybuf->buflen)
3138 primarybuf->buf_mixpos -= primarybuf->buflen;
3140 if (frag) {
3141 /* buffers have been filled, restart playback */
3142 if (primarybuf->state == STATE_STARTING) {
3143 DSOUND_PrimaryPlay(primarybuf);
3144 primarybuf->state = STATE_PLAYING;
3145 TRACE("starting playback\n");
3147 else if (primarybuf->state == STATE_STOPPED) {
3148 /* the primarybuf is supposed to play if there's something to play
3149 * even if it is reported as stopped, so don't let this confuse you */
3150 DSOUND_PrimaryPlay(primarybuf);
3151 primarybuf->state = STATE_STOPPING;
3152 TRACE("starting playback\n");
3155 LeaveCriticalSection(&(primarybuf->lock));
3156 } else {
3157 /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
3158 if (primarybuf->state == STATE_STARTING) {
3159 DSOUND_PrimaryPlay(primarybuf);
3160 primarybuf->state = STATE_PLAYING;
3162 else if (primarybuf->state == STATE_STOPPING) {
3163 DSOUND_PrimaryStop(primarybuf);
3164 primarybuf->state = STATE_STOPPED;
3167 } else {
3168 /* using waveOut stuff */
3169 /* if no buffers are playing, we should be in pause mode now */
3170 DWORD writepos, mixq;
3171 /* clean out completed fragments */
3172 while (dsound->pwqueue && (dsound->pwave[dsound->pwplay]->dwFlags & WHDR_DONE)) {
3173 if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
3174 DWORD pos = dsound->pwplay * dsound->fraglen;
3175 TRACE("done playing primary pos=%ld\n",pos);
3176 memset(primarybuf->buffer + pos, nfiller, dsound->fraglen);
3178 dsound->pwplay++;
3179 if (dsound->pwplay >= DS_HEL_FRAGS) dsound->pwplay = 0;
3180 dsound->pwqueue--;
3182 primarybuf->playpos = dsound->pwplay * dsound->fraglen;
3183 TRACE("primary playpos=%ld, mixpos=%ld\n",primarybuf->playpos,primarybuf->buf_mixpos);
3184 EnterCriticalSection(&(primarybuf->lock));
3186 if (!dsound->pwqueue) {
3187 /* this is either an underrun or we have nothing more to play...
3188 * since playback has already stopped now, we can enter pause mode,
3189 * in order to allow buffers to refill */
3190 if (primarybuf->state == STATE_PLAYING) {
3191 TRACE("no more fragments ready to play\n");
3192 waveOutPause(dsound->hwo);
3193 primarybuf->state = STATE_STARTING;
3195 else if (primarybuf->state == STATE_STOPPING) {
3196 TRACE("no more fragments ready to play\n");
3197 waveOutPause(dsound->hwo);
3198 primarybuf->state = STATE_STOPPED;
3202 /* find next write position, plus some extra margin, if necessary */
3203 mixq = DS_HEL_MARGIN;
3204 if (mixq > dsound->pwqueue) mixq = dsound->pwqueue;
3205 writepos = primarybuf->playpos + mixq * dsound->fraglen;
3206 while (writepos >= primarybuf->buflen) writepos -= primarybuf->buflen;
3208 /* do the mixing */
3209 if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
3210 DWORD frag, maxq = DS_SND_QUEUE * dsound->fraglen;
3211 frag = DSOUND_MixToPrimary(primarybuf->playpos, writepos, maxq, FALSE);
3212 mixq = frag / dsound->fraglen;
3213 if (frag - (mixq * dsound->fraglen))
3214 mixq++;
3215 } else mixq = 0;
3216 if (forced) mixq = DS_SND_QUEUE;
3218 /* output it */
3219 for (; mixq; mixq--) {
3220 waveOutWrite(dsound->hwo, dsound->pwave[dsound->pwwrite], sizeof(WAVEHDR));
3221 dsound->pwwrite++;
3222 if (dsound->pwwrite >= DS_HEL_FRAGS) dsound->pwwrite = 0;
3223 dsound->pwqueue++;
3225 primarybuf->buf_mixpos = dsound->pwwrite * dsound->fraglen;
3227 if (dsound->pwqueue) {
3228 /* buffers have been filled, restart playback */
3229 if (primarybuf->state == STATE_STARTING) {
3230 waveOutRestart(dsound->hwo);
3231 primarybuf->state = STATE_PLAYING;
3232 TRACE("starting playback\n");
3234 else if (primarybuf->state == STATE_STOPPED) {
3235 /* the primarybuf is supposed to play if there's something to play
3236 * even if it is reported as stopped, so don't let this confuse you */
3237 waveOutRestart(dsound->hwo);
3238 primarybuf->state = STATE_STOPPING;
3239 TRACE("starting playback\n");
3242 LeaveCriticalSection(&(primarybuf->lock));
3244 TRACE("completed processing\n");
3245 LeaveCriticalSection(&(dsound->lock));
3248 /*******************************************************************************
3249 * DirectSoundCreate (DSOUND.1)
3251 HRESULT WINAPI DirectSoundCreate(REFGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter )
3253 IDirectSoundImpl** ippDS=(IDirectSoundImpl**)ppDS;
3254 PIDSDRIVER drv = NULL;
3255 WAVEOUTCAPSA wcaps;
3256 unsigned wod, wodn;
3257 HRESULT err = DS_OK;
3259 if (lpGUID)
3260 TRACE("(%p,%p,%p)\n",lpGUID,ippDS,pUnkOuter);
3261 else
3262 TRACE("DirectSoundCreate (%p)\n", ippDS);
3264 if (ippDS == NULL)
3265 return DSERR_INVALIDPARAM;
3267 if (primarybuf) {
3268 IDirectSound_AddRef((LPDIRECTSOUND)dsound);
3269 *ippDS = dsound;
3270 return DS_OK;
3273 /* Enumerate WINMM audio devices and find the one we want */
3274 wodn = waveOutGetNumDevs();
3275 if (!wodn) return DSERR_NODRIVER;
3277 /* FIXME: How do we find the GUID of an audio device? */
3278 /* Well, just use the first available device for now... */
3279 wod = 0;
3280 /* Get output device caps */
3281 waveOutGetDevCapsA(wod, &wcaps, sizeof(wcaps));
3282 /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
3283 waveOutMessage(wod, DRV_QUERYDSOUNDIFACE, (DWORD)&drv, 0);
3285 /* Allocate memory */
3286 *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundImpl));
3287 if (*ippDS == NULL)
3288 return DSERR_OUTOFMEMORY;
3290 ICOM_VTBL(*ippDS) = &dsvt;
3291 (*ippDS)->ref = 1;
3293 (*ippDS)->driver = drv;
3294 (*ippDS)->fraglen = 0;
3295 (*ippDS)->priolevel = DSSCL_NORMAL;
3296 (*ippDS)->nrofbuffers = 0;
3297 (*ippDS)->buffers = NULL;
3298 (*ippDS)->primary = NULL;
3299 (*ippDS)->listener = NULL;
3301 /* Get driver description */
3302 if (drv) {
3303 IDsDriver_GetDriverDesc(drv,&((*ippDS)->drvdesc));
3304 } else {
3305 /* if no DirectSound interface available, use WINMM API instead */
3306 (*ippDS)->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
3307 (*ippDS)->drvdesc.dnDevNode = wod; /* FIXME? */
3310 /* Set default wave format (may need it for waveOutOpen) */
3311 (*ippDS)->wfx.wFormatTag = WAVE_FORMAT_PCM;
3312 (*ippDS)->wfx.nChannels = 2;
3313 (*ippDS)->wfx.nSamplesPerSec = 22050;
3314 (*ippDS)->wfx.nAvgBytesPerSec = 44100;
3315 (*ippDS)->wfx.nBlockAlign = 2;
3316 (*ippDS)->wfx.wBitsPerSample = 8;
3318 /* If the driver requests being opened through MMSYSTEM
3319 * (which is recommended by the DDK), it is supposed to happen
3320 * before the DirectSound interface is opened */
3321 if ((*ippDS)->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
3322 /* FIXME: is this right? */
3323 err = mmErr(waveOutOpen(&((*ippDS)->hwo), (*ippDS)->drvdesc.dnDevNode, &((*ippDS)->wfx),
3324 0, 0, CALLBACK_NULL | WAVE_DIRECTSOUND));
3327 if (drv && (err == DS_OK))
3328 err = IDsDriver_Open(drv);
3330 /* FIXME: do we want to handle a temporarily busy device? */
3331 if (err != DS_OK) {
3332 HeapFree(GetProcessHeap(),0,*ippDS);
3333 *ippDS = NULL;
3334 return err;
3337 /* the driver is now open, so it's now allowed to call GetCaps */
3338 if (drv) {
3339 IDsDriver_GetCaps(drv,&((*ippDS)->drvcaps));
3340 } else {
3341 unsigned c;
3343 /* FIXME: look at wcaps */
3344 (*ippDS)->drvcaps.dwFlags =
3345 DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
3346 if (DS_EMULDRIVER)
3347 (*ippDS)->drvcaps.dwFlags |= DSCAPS_EMULDRIVER;
3349 /* Allocate memory for HEL buffer headers */
3350 for (c=0; c<DS_HEL_FRAGS; c++) {
3351 (*ippDS)->pwave[c] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEHDR));
3352 if (!(*ippDS)->pwave[c]) {
3353 /* Argh, out of memory */
3354 while (c--) {
3355 HeapFree(GetProcessHeap(),0,(*ippDS)->pwave[c]);
3356 waveOutClose((*ippDS)->hwo);
3357 HeapFree(GetProcessHeap(),0,*ippDS);
3358 *ippDS = NULL;
3359 return DSERR_OUTOFMEMORY;
3365 InitializeCriticalSection(&((*ippDS)->lock));
3367 if (!dsound) {
3368 dsound = (*ippDS);
3369 if (primarybuf == NULL) {
3370 DSBUFFERDESC dsbd;
3371 HRESULT hr;
3373 dsbd.dwSize = sizeof(DSBUFFERDESC);
3374 dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
3375 dsbd.dwBufferBytes = 0;
3376 dsbd.lpwfxFormat = &(dsound->wfx);
3377 hr = IDirectSound_CreateSoundBuffer(*ppDS, &dsbd, (LPDIRECTSOUNDBUFFER*)&primarybuf, NULL);
3378 if (hr != DS_OK)
3379 return hr;
3381 /* dsound->primary is NULL - don't need to Release */
3382 dsound->primary = primarybuf;
3383 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)primarybuf);
3385 timeBeginPeriod(DS_TIME_RES);
3386 dsound->timerID = timeSetEvent(DS_TIME_DEL, DS_TIME_RES, DSOUND_timer,
3387 (DWORD)dsound, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
3389 return DS_OK;
3392 /***************************************************************************
3393 * DirectSoundCaptureCreate [DSOUND.6]
3395 * Create and initialize a DirectSoundCapture interface
3397 * RETURNS
3398 * Success: DS_OK
3399 * Failure: DSERR_NOAGGREGATION, DSERR_ALLOCATED, DSERR_INVALIDPARAM,
3400 * DSERR_OUTOFMEMORY
3402 HRESULT WINAPI DirectSoundCaptureCreate(
3403 LPCGUID lpcGUID,
3404 LPDIRECTSOUNDCAPTURE* lplpDSC,
3405 LPUNKNOWN pUnkOuter )
3407 TRACE("(%s,%p,%p)\n", debugstr_guid(lpcGUID), lplpDSC, pUnkOuter);
3409 if( pUnkOuter ) {
3410 return DSERR_NOAGGREGATION;
3413 /* Default device? */
3414 if ( !lpcGUID ) {
3415 return DSOUND_CreateDirectSoundCapture( (LPVOID*)lplpDSC );
3418 FIXME( "Unknown GUID %s\n", debugstr_guid(lpcGUID) );
3419 *lplpDSC = NULL;
3421 return DSERR_OUTOFMEMORY;
3424 /***************************************************************************
3425 * DirectSoundCaptureEnumerateA [DSOUND.7]
3427 * Enumerate all DirectSound drivers installed in the system
3429 * RETURNS
3430 * Success: DS_OK
3431 * Failure: DSERR_INVALIDPARAM
3433 HRESULT WINAPI DirectSoundCaptureEnumerateA(
3434 LPDSENUMCALLBACKA lpDSEnumCallback,
3435 LPVOID lpContext)
3437 TRACE("(%p,%p)\n", lpDSEnumCallback, lpContext );
3439 if ( lpDSEnumCallback )
3440 lpDSEnumCallback(NULL,"WINE Primary Sound Capture Driver",
3441 "SoundCap",lpContext);
3444 return DS_OK;
3447 /***************************************************************************
3448 * DirectSoundCaptureEnumerateW [DSOUND.8]
3450 * Enumerate all DirectSound drivers installed in the system
3452 * RETURNS
3453 * Success: DS_OK
3454 * Failure: DSERR_INVALIDPARAM
3456 HRESULT WINAPI DirectSoundCaptureEnumerateW(
3457 LPDSENUMCALLBACKW lpDSEnumCallback,
3458 LPVOID lpContext)
3460 FIXME("(%p,%p):stub\n", lpDSEnumCallback, lpContext );
3461 return DS_OK;
3464 static HRESULT
3465 DSOUND_CreateDirectSoundCapture( LPVOID* ppobj )
3467 *ppobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( IDirectSoundCaptureImpl ) );
3469 if ( *ppobj == NULL ) {
3470 return DSERR_OUTOFMEMORY;
3474 ICOM_THIS(IDirectSoundCaptureImpl,*ppobj);
3476 This->ref = 1;
3477 ICOM_VTBL(This) = &dscvt;
3479 InitializeCriticalSection( &This->lock );
3482 return S_OK;
3485 static HRESULT WINAPI
3486 IDirectSoundCaptureImpl_QueryInterface(
3487 LPDIRECTSOUNDCAPTURE iface,
3488 REFIID riid,
3489 LPVOID* ppobj )
3491 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3493 FIXME( "(%p)->(%s,%p): stub\n", This, debugstr_guid(riid), ppobj );
3495 return E_FAIL;
3498 static ULONG WINAPI
3499 IDirectSoundCaptureImpl_AddRef( LPDIRECTSOUNDCAPTURE iface )
3501 ULONG uRef;
3502 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3504 EnterCriticalSection( &This->lock );
3506 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3507 uRef = ++(This->ref);
3509 LeaveCriticalSection( &This->lock );
3511 return uRef;
3514 static ULONG WINAPI
3515 IDirectSoundCaptureImpl_Release( LPDIRECTSOUNDCAPTURE iface )
3517 ULONG uRef;
3518 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3520 EnterCriticalSection( &This->lock );
3522 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3523 uRef = --(This->ref);
3525 LeaveCriticalSection( &This->lock );
3527 if ( uRef == 0 ) {
3528 DeleteCriticalSection( &This->lock );
3529 HeapFree( GetProcessHeap(), 0, This );
3532 return uRef;
3535 static HRESULT WINAPI
3536 IDirectSoundCaptureImpl_CreateCaptureBuffer(
3537 LPDIRECTSOUNDCAPTURE iface,
3538 LPCDSCBUFFERDESC lpcDSCBufferDesc,
3539 LPDIRECTSOUNDCAPTUREBUFFER* lplpDSCaptureBuffer,
3540 LPUNKNOWN pUnk )
3542 HRESULT hr;
3543 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3545 TRACE( "(%p)->(%p,%p,%p)\n", This, lpcDSCBufferDesc, lplpDSCaptureBuffer, pUnk );
3547 if ( pUnk ) {
3548 return DSERR_INVALIDPARAM;
3551 hr = DSOUND_CreateDirectSoundCaptureBuffer( lpcDSCBufferDesc, (LPVOID*)lplpDSCaptureBuffer );
3553 return hr;
3556 static HRESULT WINAPI
3557 IDirectSoundCaptureImpl_GetCaps(
3558 LPDIRECTSOUNDCAPTURE iface,
3559 LPDSCCAPS lpDSCCaps )
3561 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3563 FIXME( "(%p)->(%p): stub\n", This, lpDSCCaps );
3565 return DS_OK;
3568 static HRESULT WINAPI
3569 IDirectSoundCaptureImpl_Initialize(
3570 LPDIRECTSOUNDCAPTURE iface,
3571 LPCGUID lpcGUID )
3573 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3575 FIXME( "(%p)->(%p): stub\n", This, lpcGUID );
3577 return DS_OK;
3581 static ICOM_VTABLE(IDirectSoundCapture) dscvt =
3583 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3584 /* IUnknown methods */
3585 IDirectSoundCaptureImpl_QueryInterface,
3586 IDirectSoundCaptureImpl_AddRef,
3587 IDirectSoundCaptureImpl_Release,
3589 /* IDirectSoundCapture methods */
3590 IDirectSoundCaptureImpl_CreateCaptureBuffer,
3591 IDirectSoundCaptureImpl_GetCaps,
3592 IDirectSoundCaptureImpl_Initialize
3595 static HRESULT
3596 DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc, LPVOID* ppobj )
3599 FIXME( "(%p,%p): ignoring lpcDSCBufferDesc\n", lpcDSCBufferDesc, ppobj );
3601 *ppobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( IDirectSoundCaptureBufferImpl ) );
3603 if ( *ppobj == NULL ) {
3604 return DSERR_OUTOFMEMORY;
3608 ICOM_THIS(IDirectSoundCaptureBufferImpl,*ppobj);
3610 This->ref = 1;
3611 ICOM_VTBL(This) = &dscbvt;
3613 InitializeCriticalSection( &This->lock );
3616 return S_OK;
3620 static HRESULT WINAPI
3621 IDirectSoundCaptureBufferImpl_QueryInterface(
3622 LPDIRECTSOUNDCAPTUREBUFFER iface,
3623 REFIID riid,
3624 LPVOID* ppobj )
3626 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3628 FIXME( "(%p)->(%s,%p): stub\n", This, debugstr_guid(riid), ppobj );
3630 return E_FAIL;
3633 static ULONG WINAPI
3634 IDirectSoundCaptureBufferImpl_AddRef( LPDIRECTSOUNDCAPTUREBUFFER iface )
3636 ULONG uRef;
3637 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3639 EnterCriticalSection( &This->lock );
3641 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3642 uRef = ++(This->ref);
3644 LeaveCriticalSection( &This->lock );
3646 return uRef;
3649 static ULONG WINAPI
3650 IDirectSoundCaptureBufferImpl_Release( LPDIRECTSOUNDCAPTUREBUFFER iface )
3652 ULONG uRef;
3653 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3655 EnterCriticalSection( &This->lock );
3657 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3658 uRef = --(This->ref);
3660 LeaveCriticalSection( &This->lock );
3662 if ( uRef == 0 ) {
3663 DeleteCriticalSection( &This->lock );
3664 HeapFree( GetProcessHeap(), 0, This );
3667 return uRef;
3670 static HRESULT WINAPI
3671 IDirectSoundCaptureBufferImpl_GetCaps(
3672 LPDIRECTSOUNDCAPTUREBUFFER iface,
3673 LPDSCBCAPS lpDSCBCaps )
3675 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3677 FIXME( "(%p)->(%p): stub\n", This, lpDSCBCaps );
3679 return DS_OK;
3682 static HRESULT WINAPI
3683 IDirectSoundCaptureBufferImpl_GetCurrentPosition(
3684 LPDIRECTSOUNDCAPTUREBUFFER iface,
3685 LPDWORD lpdwCapturePosition,
3686 LPDWORD lpdwReadPosition )
3688 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3690 FIXME( "(%p)->(%p,%p): stub\n", This, lpdwCapturePosition, lpdwReadPosition );
3692 return DS_OK;
3695 static HRESULT WINAPI
3696 IDirectSoundCaptureBufferImpl_GetFormat(
3697 LPDIRECTSOUNDCAPTUREBUFFER iface,
3698 LPWAVEFORMATEX lpwfxFormat,
3699 DWORD dwSizeAllocated,
3700 LPDWORD lpdwSizeWritten )
3702 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3704 FIXME( "(%p)->(%p,0x%08lx,%p): stub\n", This, lpwfxFormat, dwSizeAllocated, lpdwSizeWritten );
3706 return DS_OK;
3709 static HRESULT WINAPI
3710 IDirectSoundCaptureBufferImpl_GetStatus(
3711 LPDIRECTSOUNDCAPTUREBUFFER iface,
3712 LPDWORD lpdwStatus )
3714 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3716 FIXME( "(%p)->(%p): stub\n", This, lpdwStatus );
3718 return DS_OK;
3721 static HRESULT WINAPI
3722 IDirectSoundCaptureBufferImpl_Initialize(
3723 LPDIRECTSOUNDCAPTUREBUFFER iface,
3724 LPDIRECTSOUNDCAPTURE lpDSC,
3725 LPCDSCBUFFERDESC lpcDSCBDesc )
3727 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3729 FIXME( "(%p)->(%p,%p): stub\n", This, lpDSC, lpcDSCBDesc );
3731 return DS_OK;
3734 static HRESULT WINAPI
3735 IDirectSoundCaptureBufferImpl_Lock(
3736 LPDIRECTSOUNDCAPTUREBUFFER iface,
3737 DWORD dwReadCusor,
3738 DWORD dwReadBytes,
3739 LPVOID* lplpvAudioPtr1,
3740 LPDWORD lpdwAudioBytes1,
3741 LPVOID* lplpvAudioPtr2,
3742 LPDWORD lpdwAudioBytes2,
3743 DWORD dwFlags )
3745 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3747 FIXME( "(%p)->(%08lu,%08lu,%p,%p,%p,%p,0x%08lx): stub\n", This, dwReadCusor, dwReadBytes, lplpvAudioPtr1, lpdwAudioBytes1, lplpvAudioPtr2, lpdwAudioBytes2, dwFlags );
3749 return DS_OK;
3752 static HRESULT WINAPI
3753 IDirectSoundCaptureBufferImpl_Start(
3754 LPDIRECTSOUNDCAPTUREBUFFER iface,
3755 DWORD dwFlags )
3757 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3759 FIXME( "(%p)->(0x%08lx): stub\n", This, dwFlags );
3761 return DS_OK;
3764 static HRESULT WINAPI
3765 IDirectSoundCaptureBufferImpl_Stop( LPDIRECTSOUNDCAPTUREBUFFER iface )
3767 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3769 FIXME( "(%p): stub\n", This );
3771 return DS_OK;
3774 static HRESULT WINAPI
3775 IDirectSoundCaptureBufferImpl_Unlock(
3776 LPDIRECTSOUNDCAPTUREBUFFER iface,
3777 LPVOID lpvAudioPtr1,
3778 DWORD dwAudioBytes1,
3779 LPVOID lpvAudioPtr2,
3780 DWORD dwAudioBytes2 )
3782 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3784 FIXME( "(%p)->(%p,%08lu,%p,%08lu): stub\n", This, lpvAudioPtr1, dwAudioBytes1, lpvAudioPtr2, dwAudioBytes2 );
3786 return DS_OK;
3790 static ICOM_VTABLE(IDirectSoundCaptureBuffer) dscbvt =
3792 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3793 /* IUnknown methods */
3794 IDirectSoundCaptureBufferImpl_QueryInterface,
3795 IDirectSoundCaptureBufferImpl_AddRef,
3796 IDirectSoundCaptureBufferImpl_Release,
3798 /* IDirectSoundCaptureBuffer methods */
3799 IDirectSoundCaptureBufferImpl_GetCaps,
3800 IDirectSoundCaptureBufferImpl_GetCurrentPosition,
3801 IDirectSoundCaptureBufferImpl_GetFormat,
3802 IDirectSoundCaptureBufferImpl_GetStatus,
3803 IDirectSoundCaptureBufferImpl_Initialize,
3804 IDirectSoundCaptureBufferImpl_Lock,
3805 IDirectSoundCaptureBufferImpl_Start,
3806 IDirectSoundCaptureBufferImpl_Stop,
3807 IDirectSoundCaptureBufferImpl_Unlock
3810 /*******************************************************************************
3811 * DirectSound ClassFactory
3813 typedef struct
3815 /* IUnknown fields */
3816 ICOM_VFIELD(IClassFactory);
3817 DWORD ref;
3818 } IClassFactoryImpl;
3820 static HRESULT WINAPI
3821 DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
3822 ICOM_THIS(IClassFactoryImpl,iface);
3824 FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
3825 return E_NOINTERFACE;
3828 static ULONG WINAPI
3829 DSCF_AddRef(LPCLASSFACTORY iface) {
3830 ICOM_THIS(IClassFactoryImpl,iface);
3831 return ++(This->ref);
3834 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) {
3835 ICOM_THIS(IClassFactoryImpl,iface);
3836 /* static class, won't be freed */
3837 return --(This->ref);
3840 static HRESULT WINAPI DSCF_CreateInstance(
3841 LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
3843 ICOM_THIS(IClassFactoryImpl,iface);
3845 TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
3846 if ( IsEqualGUID( &IID_IDirectSound, riid ) ) {
3847 /* FIXME: reuse already created dsound if present? */
3848 return DirectSoundCreate(riid,(LPDIRECTSOUND*)ppobj,pOuter);
3850 return E_NOINTERFACE;
3853 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
3854 ICOM_THIS(IClassFactoryImpl,iface);
3855 FIXME("(%p)->(%d),stub!\n",This,dolock);
3856 return S_OK;
3859 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
3860 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3861 DSCF_QueryInterface,
3862 DSCF_AddRef,
3863 DSCF_Release,
3864 DSCF_CreateInstance,
3865 DSCF_LockServer
3867 static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 };
3869 /*******************************************************************************
3870 * DllGetClassObject [DSOUND.5]
3871 * Retrieves class object from a DLL object
3873 * NOTES
3874 * Docs say returns STDAPI
3876 * PARAMS
3877 * rclsid [I] CLSID for the class object
3878 * riid [I] Reference to identifier of interface for class object
3879 * ppv [O] Address of variable to receive interface pointer for riid
3881 * RETURNS
3882 * Success: S_OK
3883 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
3884 * E_UNEXPECTED
3886 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
3888 TRACE("(%p,%p,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
3889 if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
3890 *ppv = (LPVOID)&DSOUND_CF;
3891 IClassFactory_AddRef((IClassFactory*)*ppv);
3892 return S_OK;
3895 FIXME("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
3896 return CLASS_E_CLASSNOTAVAILABLE;
3900 /*******************************************************************************
3901 * DllCanUnloadNow [DSOUND.4] Determines whether the DLL is in use.
3903 * RETURNS
3904 * Success: S_OK
3905 * Failure: S_FALSE
3907 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
3909 FIXME("(void): stub\n");
3910 return S_FALSE;