Documentation ordinal fixes.
[wine/dcerpc.git] / dlls / dsound / dsound_main.c
blobcb937b8cd8dce0eaa5334fc1a082c5cb2036ae43
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 static HRESULT WINAPI IKsPropertySetImpl_QueryInterface(
337 LPKSPROPERTYSET iface, REFIID riid, LPVOID *ppobj
339 ICOM_THIS(IKsPropertySetImpl,iface);
341 FIXME("(%p,%s,%p), stub!\n",This,debugstr_guid(riid),ppobj);
342 return E_FAIL;
345 static ULONG WINAPI IKsPropertySetImpl_AddRef(LPKSPROPERTYSET iface) {
346 ICOM_THIS(IKsPropertySetImpl,iface);
348 This->ref++;
349 return This->ref;
352 static ULONG WINAPI IKsPropertySetImpl_Release(LPKSPROPERTYSET iface) {
353 ICOM_THIS(IKsPropertySetImpl,iface);
355 This->ref--;
356 return This->ref;
359 static HRESULT WINAPI IKsPropertySetImpl_Get(LPKSPROPERTYSET iface,
360 REFGUID guidPropSet, ULONG dwPropID,
361 LPVOID pInstanceData, ULONG cbInstanceData,
362 LPVOID pPropData, ULONG cbPropData,
363 PULONG pcbReturned
365 ICOM_THIS(IKsPropertySetImpl,iface);
367 FIXME("(%p,%s,%ld,%p,%ld,%p,%ld,%p), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pInstanceData,cbInstanceData,pPropData,cbPropData,pcbReturned);
368 return E_PROP_ID_UNSUPPORTED;
371 static HRESULT WINAPI IKsPropertySetImpl_Set(LPKSPROPERTYSET iface,
372 REFGUID guidPropSet, ULONG dwPropID,
373 LPVOID pInstanceData, ULONG cbInstanceData,
374 LPVOID pPropData, ULONG cbPropData
376 ICOM_THIS(IKsPropertySetImpl,iface);
378 FIXME("(%p,%s,%ld,%p,%ld,%p,%ld), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pInstanceData,cbInstanceData,pPropData,cbPropData);
379 return E_PROP_ID_UNSUPPORTED;
382 static HRESULT WINAPI IKsPropertySetImpl_QuerySupport(LPKSPROPERTYSET iface,
383 REFGUID guidPropSet, ULONG dwPropID, PULONG pTypeSupport
385 ICOM_THIS(IKsPropertySetImpl,iface);
387 FIXME("(%p,%s,%ld,%p), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pTypeSupport);
388 return E_PROP_ID_UNSUPPORTED;
391 static ICOM_VTABLE(IKsPropertySet) iksvt = {
392 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
393 IKsPropertySetImpl_QueryInterface,
394 IKsPropertySetImpl_AddRef,
395 IKsPropertySetImpl_Release,
396 IKsPropertySetImpl_Get,
397 IKsPropertySetImpl_Set,
398 IKsPropertySetImpl_QuerySupport
401 /*******************************************************************************
402 * IDirectSound3DBuffer
405 /* IUnknown methods */
406 #ifdef USE_DSOUND3D
407 static HRESULT WINAPI IDirectSound3DBufferImpl_QueryInterface(
408 LPDIRECTSOUND3DBUFFER iface, REFIID riid, LPVOID *ppobj)
410 ICOM_THIS(IDirectSound3DBufferImpl,iface);
412 if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
413 IDirectSound3DBuffer_AddRef(iface);
414 *ppobj = This->iks;
415 return S_OK;
418 FIXME("(%p,%s,%p), no such interface.\n",This,debugstr_guid(riid),ppobj);
419 return E_FAIL;
421 #endif
423 #ifdef USE_DSOUND3D
424 static ULONG WINAPI IDirectSound3DBufferImpl_AddRef(LPDIRECTSOUND3DBUFFER iface)
426 ICOM_THIS(IDirectSound3DBufferImpl,iface);
427 This->ref++;
428 return This->ref;
430 #endif
432 #ifdef USE_DSOUND3D
433 static ULONG WINAPI IDirectSound3DBufferImpl_Release(LPDIRECTSOUND3DBUFFER iface)
435 ICOM_THIS(IDirectSound3DBufferImpl,iface);
437 TRACE("(%p) ref was %ld\n", This, This->ref);
439 if(--This->ref)
440 return This->ref;
442 if (This->dsb)
443 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
445 DeleteCriticalSection(&This->lock);
447 HeapFree(GetProcessHeap(),0,This->buffer);
448 HeapFree(GetProcessHeap(),0,This);
450 return 0;
452 #endif
454 /* IDirectSound3DBuffer methods */
455 #ifdef USE_DSOUND3D
456 static HRESULT WINAPI IDirectSound3DBufferImpl_GetAllParameters(
457 LPDIRECTSOUND3DBUFFER iface,
458 LPDS3DBUFFER lpDs3dBuffer)
460 FIXME("stub\n");
461 return DS_OK;
463 #endif
465 #ifdef USE_DSOUND3D
466 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeAngles(
467 LPDIRECTSOUND3DBUFFER iface,
468 LPDWORD lpdwInsideConeAngle,
469 LPDWORD lpdwOutsideConeAngle)
471 FIXME("stub\n");
472 return DS_OK;
474 #endif
476 #ifdef USE_DSOUND3D
477 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOrientation(
478 LPDIRECTSOUND3DBUFFER iface,
479 LPD3DVECTOR lpvConeOrientation)
481 FIXME("stub\n");
482 return DS_OK;
484 #endif
486 #ifdef USE_DSOUND3D
487 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOutsideVolume(
488 LPDIRECTSOUND3DBUFFER iface,
489 LPLONG lplConeOutsideVolume)
491 FIXME("stub\n");
492 return DS_OK;
494 #endif
496 #ifdef USE_DSOUND3D
497 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMaxDistance(
498 LPDIRECTSOUND3DBUFFER iface,
499 LPD3DVALUE lpfMaxDistance)
501 FIXME("stub\n");
502 return DS_OK;
504 #endif
506 #ifdef USE_DSOUND3D
507 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMinDistance(
508 LPDIRECTSOUND3DBUFFER iface,
509 LPD3DVALUE lpfMinDistance)
511 FIXME("stub\n");
512 return DS_OK;
514 #endif
516 #ifdef USE_DSOUND3D
517 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMode(
518 LPDIRECTSOUND3DBUFFER iface,
519 LPDWORD lpdwMode)
521 FIXME("stub\n");
522 return DS_OK;
524 #endif
526 #ifdef USE_DSOUND3D
527 static HRESULT WINAPI IDirectSound3DBufferImpl_GetPosition(
528 LPDIRECTSOUND3DBUFFER iface,
529 LPD3DVECTOR lpvPosition)
531 FIXME("stub\n");
532 return DS_OK;
534 #endif
536 #ifdef USE_DSOUND3D
537 static HRESULT WINAPI IDirectSound3DBufferImpl_GetVelocity(
538 LPDIRECTSOUND3DBUFFER iface,
539 LPD3DVECTOR lpvVelocity)
541 FIXME("stub\n");
542 return DS_OK;
544 #endif
546 #ifdef USE_DSOUND3D
547 static HRESULT WINAPI IDirectSound3DBufferImpl_SetAllParameters(
548 LPDIRECTSOUND3DBUFFER iface,
549 LPCDS3DBUFFER lpcDs3dBuffer,
550 DWORD dwApply)
552 FIXME("stub\n");
553 return DS_OK;
555 #endif
557 #ifdef USE_DSOUND3D
558 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeAngles(
559 LPDIRECTSOUND3DBUFFER iface,
560 DWORD dwInsideConeAngle,
561 DWORD dwOutsideConeAngle,
562 DWORD dwApply)
564 FIXME("stub\n");
565 return DS_OK;
567 #endif
569 #ifdef USE_DSOUND3D
570 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOrientation(
571 LPDIRECTSOUND3DBUFFER iface,
572 D3DVALUE x, D3DVALUE y, D3DVALUE z,
573 DWORD dwApply)
575 FIXME("stub\n");
576 return DS_OK;
578 #endif
580 #ifdef USE_DSOUND3D
581 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOutsideVolume(
582 LPDIRECTSOUND3DBUFFER iface,
583 LONG lConeOutsideVolume,
584 DWORD dwApply)
586 FIXME("stub\n");
587 return DS_OK;
589 #endif
591 #ifdef USE_DSOUND3D
592 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMaxDistance(
593 LPDIRECTSOUND3DBUFFER iface,
594 D3DVALUE fMaxDistance,
595 DWORD dwApply)
597 FIXME("stub\n");
598 return DS_OK;
600 #endif
602 #ifdef USE_DSOUND3D
603 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMinDistance(
604 LPDIRECTSOUND3DBUFFER iface,
605 D3DVALUE fMinDistance,
606 DWORD dwApply)
608 FIXME("stub\n");
609 return DS_OK;
611 #endif
613 #ifdef USE_DSOUND3D
614 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMode(
615 LPDIRECTSOUND3DBUFFER iface,
616 DWORD dwMode,
617 DWORD dwApply)
619 ICOM_THIS(IDirectSound3DBufferImpl,iface);
620 TRACE("mode = %lx\n", dwMode);
621 This->ds3db.dwMode = dwMode;
622 return DS_OK;
624 #endif
626 #ifdef USE_DSOUND3D
627 static HRESULT WINAPI IDirectSound3DBufferImpl_SetPosition(
628 LPDIRECTSOUND3DBUFFER iface,
629 D3DVALUE x, D3DVALUE y, D3DVALUE z,
630 DWORD dwApply)
632 FIXME("stub\n");
633 return DS_OK;
635 #endif
637 #ifdef USE_DSOUND3D
638 static HRESULT WINAPI IDirectSound3DBufferImpl_SetVelocity(
639 LPDIRECTSOUND3DBUFFER iface,
640 D3DVALUE x, D3DVALUE y, D3DVALUE z,
641 DWORD dwApply)
643 FIXME("stub\n");
644 return DS_OK;
646 #endif
648 #ifdef USE_DSOUND3D
649 static ICOM_VTABLE(IDirectSound3DBuffer) ds3dbvt =
651 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
652 /* IUnknown methods */
653 IDirectSound3DBufferImpl_QueryInterface,
654 IDirectSound3DBufferImpl_AddRef,
655 IDirectSound3DBufferImpl_Release,
656 /* IDirectSound3DBuffer methods */
657 IDirectSound3DBufferImpl_GetAllParameters,
658 IDirectSound3DBufferImpl_GetConeAngles,
659 IDirectSound3DBufferImpl_GetConeOrientation,
660 IDirectSound3DBufferImpl_GetConeOutsideVolume,
661 IDirectSound3DBufferImpl_GetMaxDistance,
662 IDirectSound3DBufferImpl_GetMinDistance,
663 IDirectSound3DBufferImpl_GetMode,
664 IDirectSound3DBufferImpl_GetPosition,
665 IDirectSound3DBufferImpl_GetVelocity,
666 IDirectSound3DBufferImpl_SetAllParameters,
667 IDirectSound3DBufferImpl_SetConeAngles,
668 IDirectSound3DBufferImpl_SetConeOrientation,
669 IDirectSound3DBufferImpl_SetConeOutsideVolume,
670 IDirectSound3DBufferImpl_SetMaxDistance,
671 IDirectSound3DBufferImpl_SetMinDistance,
672 IDirectSound3DBufferImpl_SetMode,
673 IDirectSound3DBufferImpl_SetPosition,
674 IDirectSound3DBufferImpl_SetVelocity,
676 #endif
678 #ifdef USE_DSOUND3D
679 static int DSOUND_Create3DBuffer(IDirectSoundBufferImpl* dsb)
681 DWORD i, temp, iSize, oSize, offset;
682 LPBYTE bIbuf, bObuf, bTbuf = NULL;
683 LPWORD wIbuf, wObuf, wTbuf = NULL;
685 /* Inside DirectX says it's stupid but allowed */
686 if (dsb->wfx.nChannels == 2) {
687 /* Convert to mono */
688 if (dsb->wfx.wBitsPerSample == 16) {
689 iSize = dsb->buflen / 4;
690 wTbuf = malloc(dsb->buflen / 2);
691 if (wTbuf == NULL)
692 return DSERR_OUTOFMEMORY;
693 for (i = 0; i < iSize; i++)
694 wTbuf[i] = (dsb->buffer[i * 2] + dsb->buffer[(i * 2) + 1]) / 2;
695 wIbuf = wTbuf;
696 } else {
697 iSize = dsb->buflen / 2;
698 bTbuf = malloc(dsb->buflen / 2);
699 if (bTbuf == NULL)
700 return DSERR_OUTOFMEMORY;
701 for (i = 0; i < iSize; i++)
702 bTbuf[i] = (dsb->buffer[i * 2] + dsb->buffer[(i * 2) + 1]) / 2;
703 bIbuf = bTbuf;
705 } else {
706 if (dsb->wfx.wBitsPerSample == 16) {
707 iSize = dsb->buflen / 2;
708 wIbuf = (LPWORD) dsb->buffer;
709 } else {
710 bIbuf = (LPBYTE) dsb->buffer;
711 iSize = dsb->buflen;
715 if (primarybuf->wfx.wBitsPerSample == 16) {
716 wObuf = (LPWORD) dsb->ds3db->buffer;
717 oSize = dsb->ds3db->buflen / 2;
718 } else {
719 bObuf = (LPBYTE) dsb->ds3db->buffer;
720 oSize = dsb->ds3db->buflen;
723 offset = primarybuf->wfx.nSamplesPerSec / 100; /* 10ms */
724 if (primarybuf->wfx.wBitsPerSample == 16 && dsb->wfx.wBitsPerSample == 16)
725 for (i = 0; i < iSize; i++) {
726 temp = wIbuf[i];
727 if (i >= offset)
728 temp += wIbuf[i - offset] >> 9;
729 else
730 temp += wIbuf[i + iSize - offset] >> 9;
731 wObuf[i * 2] = temp;
732 wObuf[(i * 2) + 1] = temp;
734 else if (primarybuf->wfx.wBitsPerSample == 8 && dsb->wfx.wBitsPerSample == 8)
735 for (i = 0; i < iSize; i++) {
736 temp = bIbuf[i];
737 if (i >= offset)
738 temp += bIbuf[i - offset] >> 5;
739 else
740 temp += bIbuf[i + iSize - offset] >> 5;
741 bObuf[i * 2] = temp;
742 bObuf[(i * 2) + 1] = temp;
745 if (wTbuf)
746 free(wTbuf);
747 if (bTbuf)
748 free(bTbuf);
750 return DS_OK;
752 #endif
753 /*******************************************************************************
754 * IDirectSound3DListener
757 /* IUnknown methods */
758 static HRESULT WINAPI IDirectSound3DListenerImpl_QueryInterface(
759 LPDIRECTSOUND3DLISTENER iface, REFIID riid, LPVOID *ppobj)
761 ICOM_THIS(IDirectSound3DListenerImpl,iface);
763 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
764 return E_FAIL;
767 static ULONG WINAPI IDirectSound3DListenerImpl_AddRef(LPDIRECTSOUND3DLISTENER iface)
769 ICOM_THIS(IDirectSound3DListenerImpl,iface);
770 This->ref++;
771 return This->ref;
774 static ULONG WINAPI IDirectSound3DListenerImpl_Release(LPDIRECTSOUND3DLISTENER iface)
776 ULONG ulReturn;
777 ICOM_THIS(IDirectSound3DListenerImpl,iface);
779 TRACE("(%p) ref was %ld\n", This, This->ref);
781 ulReturn = --This->ref;
783 /* Free all resources */
784 if( ulReturn == 0 ) {
785 if(This->dsb)
786 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
787 DeleteCriticalSection(&This->lock);
788 HeapFree(GetProcessHeap(),0,This);
791 return ulReturn;
794 /* IDirectSound3DListener methods */
795 static HRESULT WINAPI IDirectSound3DListenerImpl_GetAllParameter(
796 LPDIRECTSOUND3DLISTENER iface,
797 LPDS3DLISTENER lpDS3DL)
799 FIXME("stub\n");
800 return DS_OK;
803 static HRESULT WINAPI IDirectSound3DListenerImpl_GetDistanceFactor(
804 LPDIRECTSOUND3DLISTENER iface,
805 LPD3DVALUE lpfDistanceFactor)
807 FIXME("stub\n");
808 return DS_OK;
811 static HRESULT WINAPI IDirectSound3DListenerImpl_GetDopplerFactor(
812 LPDIRECTSOUND3DLISTENER iface,
813 LPD3DVALUE lpfDopplerFactor)
815 FIXME("stub\n");
816 return DS_OK;
819 static HRESULT WINAPI IDirectSound3DListenerImpl_GetOrientation(
820 LPDIRECTSOUND3DLISTENER iface,
821 LPD3DVECTOR lpvOrientFront,
822 LPD3DVECTOR lpvOrientTop)
824 FIXME("stub\n");
825 return DS_OK;
828 static HRESULT WINAPI IDirectSound3DListenerImpl_GetPosition(
829 LPDIRECTSOUND3DLISTENER iface,
830 LPD3DVECTOR lpvPosition)
832 FIXME("stub\n");
833 return DS_OK;
836 static HRESULT WINAPI IDirectSound3DListenerImpl_GetRolloffFactor(
837 LPDIRECTSOUND3DLISTENER iface,
838 LPD3DVALUE lpfRolloffFactor)
840 FIXME("stub\n");
841 return DS_OK;
844 static HRESULT WINAPI IDirectSound3DListenerImpl_GetVelocity(
845 LPDIRECTSOUND3DLISTENER iface,
846 LPD3DVECTOR lpvVelocity)
848 FIXME("stub\n");
849 return DS_OK;
852 static HRESULT WINAPI IDirectSound3DListenerImpl_SetAllParameters(
853 LPDIRECTSOUND3DLISTENER iface,
854 LPCDS3DLISTENER lpcDS3DL,
855 DWORD dwApply)
857 FIXME("stub\n");
858 return DS_OK;
861 static HRESULT WINAPI IDirectSound3DListenerImpl_SetDistanceFactor(
862 LPDIRECTSOUND3DLISTENER iface,
863 D3DVALUE fDistanceFactor,
864 DWORD dwApply)
866 FIXME("stub\n");
867 return DS_OK;
870 static HRESULT WINAPI IDirectSound3DListenerImpl_SetDopplerFactor(
871 LPDIRECTSOUND3DLISTENER iface,
872 D3DVALUE fDopplerFactor,
873 DWORD dwApply)
875 FIXME("stub\n");
876 return DS_OK;
879 static HRESULT WINAPI IDirectSound3DListenerImpl_SetOrientation(
880 LPDIRECTSOUND3DLISTENER iface,
881 D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront,
882 D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop,
883 DWORD dwApply)
885 FIXME("stub\n");
886 return DS_OK;
889 static HRESULT WINAPI IDirectSound3DListenerImpl_SetPosition(
890 LPDIRECTSOUND3DLISTENER iface,
891 D3DVALUE x, D3DVALUE y, D3DVALUE z,
892 DWORD dwApply)
894 FIXME("stub\n");
895 return DS_OK;
898 static HRESULT WINAPI IDirectSound3DListenerImpl_SetRolloffFactor(
899 LPDIRECTSOUND3DLISTENER iface,
900 D3DVALUE fRolloffFactor,
901 DWORD dwApply)
903 FIXME("stub\n");
904 return DS_OK;
907 static HRESULT WINAPI IDirectSound3DListenerImpl_SetVelocity(
908 LPDIRECTSOUND3DLISTENER iface,
909 D3DVALUE x, D3DVALUE y, D3DVALUE z,
910 DWORD dwApply)
912 FIXME("stub\n");
913 return DS_OK;
916 static HRESULT WINAPI IDirectSound3DListenerImpl_CommitDeferredSettings(
917 LPDIRECTSOUND3DLISTENER iface)
920 FIXME("stub\n");
921 return DS_OK;
924 static ICOM_VTABLE(IDirectSound3DListener) ds3dlvt =
926 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
927 /* IUnknown methods */
928 IDirectSound3DListenerImpl_QueryInterface,
929 IDirectSound3DListenerImpl_AddRef,
930 IDirectSound3DListenerImpl_Release,
931 /* IDirectSound3DListener methods */
932 IDirectSound3DListenerImpl_GetAllParameter,
933 IDirectSound3DListenerImpl_GetDistanceFactor,
934 IDirectSound3DListenerImpl_GetDopplerFactor,
935 IDirectSound3DListenerImpl_GetOrientation,
936 IDirectSound3DListenerImpl_GetPosition,
937 IDirectSound3DListenerImpl_GetRolloffFactor,
938 IDirectSound3DListenerImpl_GetVelocity,
939 IDirectSound3DListenerImpl_SetAllParameters,
940 IDirectSound3DListenerImpl_SetDistanceFactor,
941 IDirectSound3DListenerImpl_SetDopplerFactor,
942 IDirectSound3DListenerImpl_SetOrientation,
943 IDirectSound3DListenerImpl_SetPosition,
944 IDirectSound3DListenerImpl_SetRolloffFactor,
945 IDirectSound3DListenerImpl_SetVelocity,
946 IDirectSound3DListenerImpl_CommitDeferredSettings,
949 /*******************************************************************************
950 * IDirectSoundNotify
952 static HRESULT WINAPI IDirectSoundNotifyImpl_QueryInterface(
953 LPDIRECTSOUNDNOTIFY iface,REFIID riid,LPVOID *ppobj
955 ICOM_THIS(IDirectSoundNotifyImpl,iface);
957 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
958 return E_FAIL;
961 static ULONG WINAPI IDirectSoundNotifyImpl_AddRef(LPDIRECTSOUNDNOTIFY iface) {
962 ICOM_THIS(IDirectSoundNotifyImpl,iface);
963 return ++(This->ref);
966 static ULONG WINAPI IDirectSoundNotifyImpl_Release(LPDIRECTSOUNDNOTIFY iface) {
967 ICOM_THIS(IDirectSoundNotifyImpl,iface);
969 TRACE("(%p) ref was %ld\n", This, This->ref);
971 This->ref--;
972 if (!This->ref) {
973 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
974 HeapFree(GetProcessHeap(),0,This);
975 return 0;
977 return This->ref;
980 static HRESULT WINAPI IDirectSoundNotifyImpl_SetNotificationPositions(
981 LPDIRECTSOUNDNOTIFY iface,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
983 ICOM_THIS(IDirectSoundNotifyImpl,iface);
984 int i;
986 if (TRACE_ON(dsound)) {
987 TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);
988 for (i=0;i<howmuch;i++)
989 TRACE("notify at %ld to 0x%08lx\n",
990 notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
992 This->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,This->dsb->notifies,(This->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
993 memcpy( This->dsb->notifies+This->dsb->nrofnotifies,
994 notify,
995 howmuch*sizeof(DSBPOSITIONNOTIFY)
997 This->dsb->nrofnotifies+=howmuch;
999 return S_OK;
1002 static ICOM_VTABLE(IDirectSoundNotify) dsnvt =
1004 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1005 IDirectSoundNotifyImpl_QueryInterface,
1006 IDirectSoundNotifyImpl_AddRef,
1007 IDirectSoundNotifyImpl_Release,
1008 IDirectSoundNotifyImpl_SetNotificationPositions,
1011 /*******************************************************************************
1012 * IDirectSoundBuffer
1015 static void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan)
1017 double temp;
1019 /* the AmpFactors are expressed in 16.16 fixed point */
1020 volpan->dwVolAmpFactor = (ULONG) (pow(2.0, volpan->lVolume / 600.0) * 65536);
1021 /* FIXME: dwPan{Left|Right}AmpFactor */
1023 /* FIXME: use calculated vol and pan ampfactors */
1024 temp = (double) (volpan->lVolume - (volpan->lPan > 0 ? volpan->lPan : 0));
1025 volpan->dwTotalLeftAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 65536);
1026 temp = (double) (volpan->lVolume + (volpan->lPan < 0 ? volpan->lPan : 0));
1027 volpan->dwTotalRightAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 65536);
1029 TRACE("left = %lx, right = %lx\n", volpan->dwTotalLeftAmpFactor, volpan->dwTotalRightAmpFactor);
1032 static void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
1034 DWORD sw;
1036 sw = dsb->wfx.nChannels * (dsb->wfx.wBitsPerSample / 8);
1037 if ((dsb->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && dsb->hwbuf) {
1038 DWORD fraglen;
1039 /* let fragment size approximate the timer delay */
1040 fraglen = (dsb->freq * DS_TIME_DEL / 1000) * sw;
1041 /* reduce fragment size until an integer number of them fits in the buffer */
1042 /* (FIXME: this may or may not be a good idea) */
1043 while (dsb->buflen % fraglen) fraglen -= sw;
1044 dsb->dsound->fraglen = fraglen;
1045 TRACE("fraglen=%ld\n", dsb->dsound->fraglen);
1047 /* calculate the 10ms write lead */
1048 dsb->writelead = (dsb->freq / 100) * sw;
1051 static HRESULT DSOUND_PrimaryOpen(IDirectSoundBufferImpl *dsb)
1053 HRESULT err = DS_OK;
1055 /* are we using waveOut stuff? */
1056 if (!dsb->hwbuf) {
1057 LPBYTE newbuf;
1058 DWORD buflen;
1059 HRESULT merr = DS_OK;
1060 /* Start in pause mode, to allow buffers to get filled */
1061 waveOutPause(dsb->dsound->hwo);
1062 if (dsb->state == STATE_PLAYING) dsb->state = STATE_STARTING;
1063 else if (dsb->state == STATE_STOPPING) dsb->state = STATE_STOPPED;
1064 /* use fragments of 10ms (1/100s) each (which should get us within
1065 * the documented write cursor lead of 10-15ms) */
1066 buflen = ((dsb->wfx.nAvgBytesPerSec / 100) & ~3) * DS_HEL_FRAGS;
1067 TRACE("desired buflen=%ld, old buffer=%p\n", buflen, dsb->buffer);
1068 /* reallocate emulated primary buffer */
1069 newbuf = (LPBYTE)HeapReAlloc(GetProcessHeap(),0,dsb->buffer,buflen);
1070 if (newbuf == NULL) {
1071 ERR("failed to allocate primary buffer\n");
1072 merr = DSERR_OUTOFMEMORY;
1073 /* but the old buffer might still exists and must be re-prepared */
1074 } else {
1075 dsb->buffer = newbuf;
1076 dsb->buflen = buflen;
1078 if (dsb->buffer) {
1079 unsigned c;
1080 IDirectSoundImpl *ds = dsb->dsound;
1082 ds->fraglen = dsb->buflen / DS_HEL_FRAGS;
1084 /* prepare fragment headers */
1085 for (c=0; c<DS_HEL_FRAGS; c++) {
1086 ds->pwave[c]->lpData = dsb->buffer + c*ds->fraglen;
1087 ds->pwave[c]->dwBufferLength = ds->fraglen;
1088 ds->pwave[c]->dwUser = (DWORD)dsb;
1089 ds->pwave[c]->dwFlags = 0;
1090 ds->pwave[c]->dwLoops = 0;
1091 err = mmErr(waveOutPrepareHeader(ds->hwo,ds->pwave[c],sizeof(WAVEHDR)));
1092 if (err != DS_OK) {
1093 while (c--)
1094 waveOutUnprepareHeader(ds->hwo,ds->pwave[c],sizeof(WAVEHDR));
1095 break;
1099 ds->pwplay = 0;
1100 ds->pwwrite = 0;
1101 ds->pwqueue = 0;
1102 memset(dsb->buffer, (dsb->wfx.wBitsPerSample == 16) ? 0 : 128, dsb->buflen);
1103 TRACE("fraglen=%ld\n", ds->fraglen);
1105 if ((err == DS_OK) && (merr != DS_OK))
1106 err = merr;
1108 return err;
1112 static void DSOUND_PrimaryClose(IDirectSoundBufferImpl *dsb)
1114 /* are we using waveOut stuff? */
1115 if (!dsb->hwbuf) {
1116 unsigned c;
1117 IDirectSoundImpl *ds = dsb->dsound;
1119 waveOutReset(ds->hwo);
1120 for (c=0; c<DS_HEL_FRAGS; c++)
1121 waveOutUnprepareHeader(ds->hwo, ds->pwave[c], sizeof(WAVEHDR));
1125 static HRESULT DSOUND_PrimaryPlay(IDirectSoundBufferImpl *dsb)
1127 HRESULT err = DS_OK;
1128 if (dsb->hwbuf)
1129 err = IDsDriverBuffer_Play(dsb->hwbuf, 0, 0, DSBPLAY_LOOPING);
1130 return err;
1133 static HRESULT DSOUND_PrimaryStop(IDirectSoundBufferImpl *dsb)
1135 HRESULT err = DS_OK;
1136 if (dsb->hwbuf) {
1137 err = IDsDriverBuffer_Stop(dsb->hwbuf);
1138 if (err == DSERR_BUFFERLOST) {
1139 /* Wine-only: the driver wants us to reopen the device */
1140 /* FIXME: check for errors */
1141 IDsDriverBuffer_Release(primarybuf->hwbuf);
1142 waveOutClose(dsb->dsound->hwo);
1143 dsb->dsound->hwo = 0;
1144 waveOutOpen(&(dsb->dsound->hwo), dsb->dsound->drvdesc.dnDevNode,
1145 &(primarybuf->wfx), 0, 0, CALLBACK_NULL | WAVE_DIRECTSOUND);
1146 err = IDsDriver_CreateSoundBuffer(dsb->dsound->driver,&(dsb->wfx),dsb->dsbd.dwFlags,0,
1147 &(dsb->buflen),&(dsb->buffer),
1148 (LPVOID)&(dsb->hwbuf));
1151 return err;
1154 /* This sets this format for the <em>Primary Buffer Only</em> */
1155 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
1156 static HRESULT WINAPI IDirectSoundBufferImpl_SetFormat(
1157 LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX wfex
1159 ICOM_THIS(IDirectSoundBufferImpl,iface);
1160 IDirectSoundBufferImpl** dsb;
1161 HRESULT err = DS_OK;
1162 int i;
1164 /* Let's be pedantic! */
1165 if ((wfex == NULL) ||
1166 (wfex->wFormatTag != WAVE_FORMAT_PCM) ||
1167 (wfex->nChannels < 1) || (wfex->nChannels > 2) ||
1168 (wfex->nSamplesPerSec < 1) ||
1169 (wfex->nBlockAlign < 1) || (wfex->nChannels > 4) ||
1170 ((wfex->wBitsPerSample != 8) && (wfex->wBitsPerSample != 16))) {
1171 TRACE("failed pedantic check!\n");
1172 return DSERR_INVALIDPARAM;
1175 /* **** */
1176 EnterCriticalSection(&(This->dsound->lock));
1178 if (primarybuf->wfx.nSamplesPerSec != wfex->nSamplesPerSec) {
1179 dsb = dsound->buffers;
1180 for (i = 0; i < dsound->nrofbuffers; i++, dsb++) {
1181 /* **** */
1182 EnterCriticalSection(&((*dsb)->lock));
1184 (*dsb)->freqAdjust = ((*dsb)->freq << DSOUND_FREQSHIFT) /
1185 wfex->nSamplesPerSec;
1187 LeaveCriticalSection(&((*dsb)->lock));
1188 /* **** */
1192 memcpy(&(primarybuf->wfx), wfex, sizeof(primarybuf->wfx));
1194 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
1195 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1196 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
1197 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
1198 wfex->wBitsPerSample, wfex->cbSize);
1200 primarybuf->wfx.nAvgBytesPerSec =
1201 This->wfx.nSamplesPerSec * This->wfx.nBlockAlign;
1202 if (primarybuf->dsound->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) {
1203 /* FIXME: check for errors */
1204 DSOUND_PrimaryClose(primarybuf);
1205 waveOutClose(This->dsound->hwo);
1206 This->dsound->hwo = 0;
1207 waveOutOpen(&(This->dsound->hwo), This->dsound->drvdesc.dnDevNode,
1208 &(primarybuf->wfx), 0, 0, CALLBACK_NULL | WAVE_DIRECTSOUND);
1209 DSOUND_PrimaryOpen(primarybuf);
1211 if (primarybuf->hwbuf) {
1212 err = IDsDriverBuffer_SetFormat(primarybuf->hwbuf, &(primarybuf->wfx));
1213 if (err == DSERR_BUFFERLOST) {
1214 /* Wine-only: the driver wants us to recreate the HW buffer */
1215 IDsDriverBuffer_Release(primarybuf->hwbuf);
1216 err = IDsDriver_CreateSoundBuffer(primarybuf->dsound->driver,&(primarybuf->wfx),primarybuf->dsbd.dwFlags,0,
1217 &(primarybuf->buflen),&(primarybuf->buffer),
1218 (LPVOID)&(primarybuf->hwbuf));
1219 if (primarybuf->state == STATE_PLAYING) primarybuf->state = STATE_STARTING;
1220 else if (primarybuf->state == STATE_STOPPING) primarybuf->state = STATE_STOPPED;
1223 DSOUND_RecalcFormat(primarybuf);
1225 LeaveCriticalSection(&(This->dsound->lock));
1226 /* **** */
1228 return DS_OK;
1231 static HRESULT WINAPI IDirectSoundBufferImpl_SetVolume(
1232 LPDIRECTSOUNDBUFFER iface,LONG vol
1234 ICOM_THIS(IDirectSoundBufferImpl,iface);
1236 TRACE("(%p,%ld)\n",This,vol);
1238 /* I'm not sure if we need this for primary buffer */
1239 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
1240 return DSERR_CONTROLUNAVAIL;
1242 if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN))
1243 return DSERR_INVALIDPARAM;
1245 /* **** */
1246 EnterCriticalSection(&(This->lock));
1248 This->volpan.lVolume = vol;
1250 DSOUND_RecalcVolPan(&(This->volpan));
1252 if (This->hwbuf) {
1253 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
1255 else if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
1256 #if 0 /* should we really do this? */
1257 /* the DS volume ranges from 0 (max, 0dB attenuation) to -10000 (min, 100dB attenuation) */
1258 /* the MM volume ranges from 0 to 0xffff in an unspecified logarithmic scale */
1259 WORD cvol = 0xffff + vol*6 + vol/2;
1260 DWORD vol = cvol | ((DWORD)cvol << 16)
1261 waveOutSetVolume(This->dsound->hwo, vol);
1262 #endif
1265 LeaveCriticalSection(&(This->lock));
1266 /* **** */
1268 return DS_OK;
1271 static HRESULT WINAPI IDirectSoundBufferImpl_GetVolume(
1272 LPDIRECTSOUNDBUFFER iface,LPLONG vol
1274 ICOM_THIS(IDirectSoundBufferImpl,iface);
1275 TRACE("(%p,%p)\n",This,vol);
1277 if (vol == NULL)
1278 return DSERR_INVALIDPARAM;
1280 *vol = This->volpan.lVolume;
1281 return DS_OK;
1284 static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency(
1285 LPDIRECTSOUNDBUFFER iface,DWORD freq
1287 ICOM_THIS(IDirectSoundBufferImpl,iface);
1288 TRACE("(%p,%ld)\n",This,freq);
1290 /* You cannot set the frequency of the primary buffer */
1291 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY) ||
1292 (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
1293 return DSERR_CONTROLUNAVAIL;
1295 if (!freq) freq = This->wfx.nSamplesPerSec;
1297 if ((freq < DSBFREQUENCY_MIN) || (freq > DSBFREQUENCY_MAX))
1298 return DSERR_INVALIDPARAM;
1300 /* **** */
1301 EnterCriticalSection(&(This->lock));
1303 This->freq = freq;
1304 This->freqAdjust = (freq << DSOUND_FREQSHIFT) / primarybuf->wfx.nSamplesPerSec;
1305 This->nAvgBytesPerSec = freq * This->wfx.nBlockAlign;
1306 DSOUND_RecalcFormat(This);
1308 LeaveCriticalSection(&(This->lock));
1309 /* **** */
1311 return DS_OK;
1314 static HRESULT WINAPI IDirectSoundBufferImpl_Play(
1315 LPDIRECTSOUNDBUFFER iface,DWORD reserved1,DWORD reserved2,DWORD flags
1317 ICOM_THIS(IDirectSoundBufferImpl,iface);
1318 TRACE("(%p,%08lx,%08lx,%08lx)\n",
1319 This,reserved1,reserved2,flags
1322 /* **** */
1323 EnterCriticalSection(&(This->lock));
1325 This->playflags = flags;
1326 if (This->state == STATE_STOPPED) {
1327 This->leadin = TRUE;
1328 This->startpos = This->buf_mixpos;
1329 This->state = STATE_STARTING;
1330 } else if (This->state == STATE_STOPPING)
1331 This->state = STATE_PLAYING;
1332 if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
1333 IDsDriverBuffer_Play(This->hwbuf, 0, 0, This->playflags);
1334 This->state = STATE_PLAYING;
1337 LeaveCriticalSection(&(This->lock));
1338 /* **** */
1340 return DS_OK;
1343 static HRESULT WINAPI IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface)
1345 ICOM_THIS(IDirectSoundBufferImpl,iface);
1346 TRACE("(%p)\n",This);
1348 /* **** */
1349 EnterCriticalSection(&(This->lock));
1351 if (This->state == STATE_PLAYING)
1352 This->state = STATE_STOPPING;
1353 else if (This->state == STATE_STARTING)
1354 This->state = STATE_STOPPED;
1355 if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
1356 IDsDriverBuffer_Stop(This->hwbuf);
1357 This->state = STATE_STOPPED;
1359 DSOUND_CheckEvent(This, 0);
1361 LeaveCriticalSection(&(This->lock));
1362 /* **** */
1364 return DS_OK;
1367 static DWORD WINAPI IDirectSoundBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface) {
1368 ICOM_THIS(IDirectSoundBufferImpl,iface);
1369 DWORD ref;
1371 TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
1373 ref = InterlockedIncrement(&(This->ref));
1374 if (!ref) {
1375 FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n");
1377 return ref;
1379 static DWORD WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER iface) {
1380 ICOM_THIS(IDirectSoundBufferImpl,iface);
1381 int i;
1382 DWORD ref;
1384 TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
1386 ref = InterlockedDecrement(&(This->ref));
1387 if (ref) return ref;
1389 EnterCriticalSection(&(This->dsound->lock));
1390 for (i=0;i<This->dsound->nrofbuffers;i++)
1391 if (This->dsound->buffers[i] == This)
1392 break;
1394 if (i < This->dsound->nrofbuffers) {
1395 /* Put the last buffer of the list in the (now empty) position */
1396 This->dsound->buffers[i] = This->dsound->buffers[This->dsound->nrofbuffers - 1];
1397 This->dsound->nrofbuffers--;
1398 This->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,This->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*This->dsound->nrofbuffers);
1399 TRACE("buffer count is now %d\n", This->dsound->nrofbuffers);
1400 IDirectSound_Release((LPDIRECTSOUND)This->dsound);
1402 LeaveCriticalSection(&(This->dsound->lock));
1404 DeleteCriticalSection(&(This->lock));
1405 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1406 DSOUND_PrimaryClose(This);
1407 if (This->hwbuf) {
1408 IDsDriverBuffer_Release(This->hwbuf);
1410 if (This->ds3db)
1411 IDirectSound3DBuffer_Release((LPDIRECTSOUND3DBUFFER)This->ds3db);
1412 if (This->parent)
1413 /* this is a duplicate buffer */
1414 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->parent);
1415 else
1416 /* this is a toplevel buffer */
1417 HeapFree(GetProcessHeap(),0,This->buffer);
1419 HeapFree(GetProcessHeap(),0,This);
1421 if (This == primarybuf)
1422 primarybuf = NULL;
1424 return 0;
1427 static DWORD DSOUND_CalcPlayPosition(IDirectSoundBufferImpl *This,
1428 DWORD state, DWORD pplay, DWORD pwrite, DWORD pmix, DWORD bmix)
1430 DWORD bplay;
1432 TRACE("primary playpos=%ld, mixpos=%ld\n", pplay, pmix);
1433 TRACE("this mixpos=%ld\n", bmix);
1435 /* the actual primary play position (pplay) is always behind last mixed (pmix),
1436 * unless the computer is too slow or something */
1437 /* we need to know how far away we are from there */
1438 if (pmix == pplay) {
1439 if ((state == STATE_PLAYING) || (state == STATE_STOPPING)) {
1440 /* wow, the software mixer is really doing well,
1441 * seems the entire primary buffer is filled! */
1442 pmix += primarybuf->buflen;
1444 /* else: the primary buffer is not playing, so probably empty */
1446 if (pmix < pplay) pmix += primarybuf->buflen; /* wraparound */
1447 pmix -= pplay;
1448 /* detect buffer underrun */
1449 if (pwrite < pplay) pwrite += primarybuf->buflen; /* wraparound */
1450 pwrite -= pplay;
1451 if (pmix > (DS_SND_QUEUE * primarybuf->dsound->fraglen + pwrite + primarybuf->writelead)) {
1452 WARN("detected an underrun: primary queue was %ld\n",pmix);
1453 pmix = 0;
1455 /* divide the offset by its sample size */
1456 pmix /= primarybuf->wfx.nBlockAlign;
1457 TRACE("primary back-samples=%ld\n",pmix);
1458 /* adjust for our frequency */
1459 pmix = (pmix * This->freqAdjust) >> DSOUND_FREQSHIFT;
1460 /* multiply by our own sample size */
1461 pmix *= This->wfx.nBlockAlign;
1462 TRACE("this back-offset=%ld\n", pmix);
1463 /* subtract from our last mixed position */
1464 bplay = bmix;
1465 while (bplay < pmix) bplay += This->buflen; /* wraparound */
1466 bplay -= pmix;
1467 if (This->leadin && ((bplay < This->startpos) || (bplay > bmix))) {
1468 /* seems we haven't started playing yet */
1469 TRACE("this still in lead-in phase\n");
1470 bplay = This->startpos;
1472 /* return the result */
1473 return bplay;
1476 static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
1477 LPDIRECTSOUNDBUFFER iface,LPDWORD playpos,LPDWORD writepos
1479 HRESULT hres;
1480 ICOM_THIS(IDirectSoundBufferImpl,iface);
1481 TRACE("(%p,%p,%p)\n",This,playpos,writepos);
1482 if (This->hwbuf) {
1483 hres=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);
1484 if (hres)
1485 return hres;
1488 else if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
1489 if (playpos) {
1490 MMTIME mtime;
1491 mtime.wType = TIME_BYTES;
1492 waveOutGetPosition(This->dsound->hwo, &mtime, sizeof(mtime));
1493 mtime.u.cb = mtime.u.cb % This->buflen;
1494 *playpos = mtime.u.cb;
1496 if (writepos) {
1497 /* the writepos should only be used by apps with WRITEPRIMARY priority,
1498 * in which case our software mixer is disabled anyway */
1499 *writepos = This->playpos + DS_HEL_MARGIN * This->dsound->fraglen;
1500 while (*writepos >= This->buflen)
1501 *writepos -= This->buflen;
1503 } else {
1504 if (playpos && (This->state != STATE_PLAYING)) {
1505 /* we haven't been merged into the primary buffer (yet) */
1506 *playpos = This->buf_mixpos;
1508 else if (playpos) {
1509 DWORD pplay, pwrite, lplay, splay, pstate;
1510 /* let's get this exact; first, recursively call GetPosition on the primary */
1511 EnterCriticalSection(&(primarybuf->lock));
1512 if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || primarybuf->hwbuf || !DS_EMULDRIVER) {
1513 IDirectSoundBufferImpl_GetCurrentPosition((LPDIRECTSOUNDBUFFER)primarybuf, &pplay, &pwrite);
1514 /* detect HEL mode underrun */
1515 pstate = primarybuf->state;
1516 if (!(primarybuf->hwbuf || primarybuf->dsound->pwqueue)) {
1517 TRACE("detected an underrun\n");
1518 /* pplay = ? */
1519 if (pstate == STATE_PLAYING)
1520 pstate = STATE_STARTING;
1521 else if (pstate == STATE_STOPPING)
1522 pstate = STATE_STOPPED;
1524 /* get data for ourselves while we still have the lock */
1525 pstate &= This->state;
1526 lplay = This->primary_mixpos;
1527 splay = This->buf_mixpos;
1528 /* calculate play position using this */
1529 *playpos = DSOUND_CalcPlayPosition(This, pstate, pplay, pwrite, lplay, splay);
1530 } else {
1531 /* (unless the app isn't using GETCURRENTPOSITION2) */
1532 /* don't know exactly how this should be handled...
1533 * the docs says that play cursor is reported as directly
1534 * behind write cursor, hmm... */
1535 *playpos = This->playpos;
1537 LeaveCriticalSection(&(primarybuf->lock));
1539 if (writepos) *writepos = This->buf_mixpos;
1541 if (writepos) {
1542 if (This->state != STATE_STOPPED)
1543 /* apply the documented 10ms lead to writepos */
1544 *writepos += This->writelead;
1545 while (*writepos >= This->buflen) *writepos -= This->buflen;
1547 TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
1548 return DS_OK;
1551 static HRESULT WINAPI IDirectSoundBufferImpl_GetStatus(
1552 LPDIRECTSOUNDBUFFER iface,LPDWORD status
1554 ICOM_THIS(IDirectSoundBufferImpl,iface);
1555 TRACE("(%p,%p), thread is %lx\n",This,status,GetCurrentThreadId());
1557 if (status == NULL)
1558 return DSERR_INVALIDPARAM;
1560 *status = 0;
1561 if ((This->state == STATE_STARTING) || (This->state == STATE_PLAYING))
1562 *status |= DSBSTATUS_PLAYING;
1563 if (This->playflags & DSBPLAY_LOOPING)
1564 *status |= DSBSTATUS_LOOPING;
1566 TRACE("status=%lx\n", *status);
1567 return DS_OK;
1571 static HRESULT WINAPI IDirectSoundBufferImpl_GetFormat(
1572 LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
1574 ICOM_THIS(IDirectSoundBufferImpl,iface);
1575 TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
1577 if (wfsize>sizeof(This->wfx))
1578 wfsize = sizeof(This->wfx);
1579 if (lpwf) { /* NULL is valid */
1580 memcpy(lpwf,&(This->wfx),wfsize);
1581 if (wfwritten)
1582 *wfwritten = wfsize;
1583 } else
1584 if (wfwritten)
1585 *wfwritten = sizeof(This->wfx);
1586 else
1587 return DSERR_INVALIDPARAM;
1589 return DS_OK;
1592 static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
1593 LPDIRECTSOUNDBUFFER iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
1595 ICOM_THIS(IDirectSoundBufferImpl,iface);
1596 DWORD capf;
1598 TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx)\n",
1599 This,
1600 writecursor,
1601 writebytes,
1602 lplpaudioptr1,
1603 audiobytes1,
1604 lplpaudioptr2,
1605 audiobytes2,
1606 flags
1609 if (flags & DSBLOCK_FROMWRITECURSOR) {
1610 DWORD writepos;
1611 /* GetCurrentPosition does too much magic to duplicate here */
1612 IDirectSoundBufferImpl_GetCurrentPosition(iface, NULL, &writepos);
1613 writecursor += writepos;
1615 if (flags & DSBLOCK_ENTIREBUFFER)
1616 writebytes = This->buflen;
1617 if (writebytes > This->buflen)
1618 writebytes = This->buflen;
1620 assert(audiobytes1!=audiobytes2);
1621 assert(lplpaudioptr1!=lplpaudioptr2);
1623 if ((writebytes == This->buflen) &&
1624 ((This->state == STATE_STARTING) ||
1625 (This->state == STATE_PLAYING)))
1626 /* some games, like Half-Life, try to be clever (not) and
1627 * keep one secondary buffer, and mix sounds into it itself,
1628 * locking the entire buffer every time... so we can just forget
1629 * about tracking the last-written-to-position... */
1630 This->probably_valid_to = (DWORD)-1;
1631 else
1632 This->probably_valid_to = writecursor;
1634 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1635 capf = DSDDESC_DONTNEEDPRIMARYLOCK;
1636 else
1637 capf = DSDDESC_DONTNEEDSECONDARYLOCK;
1638 if (!(This->dsound->drvdesc.dwFlags & capf) && This->hwbuf) {
1639 IDsDriverBuffer_Lock(This->hwbuf,
1640 lplpaudioptr1, audiobytes1,
1641 lplpaudioptr2, audiobytes2,
1642 writecursor, writebytes,
1644 } else
1645 if (writecursor+writebytes <= This->buflen) {
1646 *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
1647 *audiobytes1 = writebytes;
1648 if (lplpaudioptr2)
1649 *(LPBYTE*)lplpaudioptr2 = NULL;
1650 if (audiobytes2)
1651 *audiobytes2 = 0;
1652 TRACE("->%ld.0\n",writebytes);
1653 } else {
1654 *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
1655 *audiobytes1 = This->buflen-writecursor;
1656 if (lplpaudioptr2)
1657 *(LPBYTE*)lplpaudioptr2 = This->buffer;
1658 if (audiobytes2)
1659 *audiobytes2 = writebytes-(This->buflen-writecursor);
1660 TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
1662 return DS_OK;
1665 static HRESULT WINAPI IDirectSoundBufferImpl_SetCurrentPosition(
1666 LPDIRECTSOUNDBUFFER iface,DWORD newpos
1668 ICOM_THIS(IDirectSoundBufferImpl,iface);
1669 TRACE("(%p,%ld)\n",This,newpos);
1671 /* **** */
1672 EnterCriticalSection(&(This->lock));
1674 This->buf_mixpos = newpos;
1675 if (This->hwbuf)
1676 IDsDriverBuffer_SetPosition(This->hwbuf, This->buf_mixpos);
1678 LeaveCriticalSection(&(This->lock));
1679 /* **** */
1681 return DS_OK;
1684 static HRESULT WINAPI IDirectSoundBufferImpl_SetPan(
1685 LPDIRECTSOUNDBUFFER iface,LONG pan
1687 ICOM_THIS(IDirectSoundBufferImpl,iface);
1689 TRACE("(%p,%ld)\n",This,pan);
1691 if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT))
1692 return DSERR_INVALIDPARAM;
1694 /* You cannot set the pan of the primary buffer */
1695 /* and you cannot use both pan and 3D controls */
1696 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
1697 (This->dsbd.dwFlags & DSBCAPS_CTRL3D) ||
1698 (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
1699 return DSERR_CONTROLUNAVAIL;
1701 /* **** */
1702 EnterCriticalSection(&(This->lock));
1704 This->volpan.lPan = pan;
1706 DSOUND_RecalcVolPan(&(This->volpan));
1708 if (This->hwbuf) {
1709 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
1712 LeaveCriticalSection(&(This->lock));
1713 /* **** */
1715 return DS_OK;
1718 static HRESULT WINAPI IDirectSoundBufferImpl_GetPan(
1719 LPDIRECTSOUNDBUFFER iface,LPLONG pan
1721 ICOM_THIS(IDirectSoundBufferImpl,iface);
1722 TRACE("(%p,%p)\n",This,pan);
1724 if (pan == NULL)
1725 return DSERR_INVALIDPARAM;
1727 *pan = This->volpan.lPan;
1729 return DS_OK;
1732 static HRESULT WINAPI IDirectSoundBufferImpl_Unlock(
1733 LPDIRECTSOUNDBUFFER iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
1735 ICOM_THIS(IDirectSoundBufferImpl,iface);
1736 DWORD capf, probably_valid_to;
1738 TRACE("(%p,%p,%ld,%p,%ld):stub\n", This,p1,x1,p2,x2);
1740 #if 0
1741 /* Preprocess 3D buffers... */
1743 /* This is highly experimental and liable to break things */
1744 if (This->dsbd.dwFlags & DSBCAPS_CTRL3D)
1745 DSOUND_Create3DBuffer(This);
1746 #endif
1748 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1749 capf = DSDDESC_DONTNEEDPRIMARYLOCK;
1750 else
1751 capf = DSDDESC_DONTNEEDSECONDARYLOCK;
1752 if (!(This->dsound->drvdesc.dwFlags & capf) && This->hwbuf) {
1753 IDsDriverBuffer_Unlock(This->hwbuf, p1, x1, p2, x2);
1756 if (p2) probably_valid_to = (((LPBYTE)p2)-This->buffer) + x2;
1757 else probably_valid_to = (((LPBYTE)p1)-This->buffer) + x1;
1758 while (probably_valid_to >= This->buflen)
1759 probably_valid_to -= This->buflen;
1760 if ((probably_valid_to == 0) && ((x1+x2) == This->buflen) &&
1761 ((This->state == STATE_STARTING) ||
1762 (This->state == STATE_PLAYING)))
1763 /* see IDirectSoundBufferImpl_Lock */
1764 probably_valid_to = (DWORD)-1;
1765 This->probably_valid_to = probably_valid_to;
1767 return DS_OK;
1770 static HRESULT WINAPI IDirectSoundBufferImpl_Restore(
1771 LPDIRECTSOUNDBUFFER iface
1773 ICOM_THIS(IDirectSoundBufferImpl,iface);
1774 FIXME("(%p):stub\n",This);
1775 return DS_OK;
1778 static HRESULT WINAPI IDirectSoundBufferImpl_GetFrequency(
1779 LPDIRECTSOUNDBUFFER iface,LPDWORD freq
1781 ICOM_THIS(IDirectSoundBufferImpl,iface);
1782 TRACE("(%p,%p)\n",This,freq);
1784 if (freq == NULL)
1785 return DSERR_INVALIDPARAM;
1787 *freq = This->freq;
1788 TRACE("-> %ld\n", *freq);
1790 return DS_OK;
1793 static HRESULT WINAPI IDirectSoundBufferImpl_Initialize(
1794 LPDIRECTSOUNDBUFFER iface,LPDIRECTSOUND dsound,LPDSBUFFERDESC dbsd
1796 ICOM_THIS(IDirectSoundBufferImpl,iface);
1797 FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
1798 DPRINTF("Re-Init!!!\n");
1799 return DSERR_ALREADYINITIALIZED;
1802 static HRESULT WINAPI IDirectSoundBufferImpl_GetCaps(
1803 LPDIRECTSOUNDBUFFER iface,LPDSBCAPS caps
1805 ICOM_THIS(IDirectSoundBufferImpl,iface);
1806 TRACE("(%p)->(%p)\n",This,caps);
1808 if (caps == NULL)
1809 return DSERR_INVALIDPARAM;
1811 /* I think we should check this value, not set it. See */
1812 /* Inside DirectX, p215. That should apply here, too. */
1813 caps->dwSize = sizeof(*caps);
1815 caps->dwFlags = This->dsbd.dwFlags;
1816 if (This->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
1817 else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
1819 caps->dwBufferBytes = This->dsbd.dwBufferBytes;
1821 /* This value represents the speed of the "unlock" command.
1822 As unlock is quite fast (it does not do anything), I put
1823 4096 ko/s = 4 Mo / s */
1824 /* FIXME: hwbuf speed */
1825 caps->dwUnlockTransferRate = 4096;
1826 caps->dwPlayCpuOverhead = 0;
1828 return DS_OK;
1831 static HRESULT WINAPI IDirectSoundBufferImpl_QueryInterface(
1832 LPDIRECTSOUNDBUFFER iface,REFIID riid,LPVOID *ppobj
1834 ICOM_THIS(IDirectSoundBufferImpl,iface);
1836 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
1838 if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
1839 IDirectSoundNotifyImpl *dsn;
1841 dsn = (IDirectSoundNotifyImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn));
1842 dsn->ref = 1;
1843 dsn->dsb = This;
1844 IDirectSoundBuffer_AddRef(iface);
1845 ICOM_VTBL(dsn) = &dsnvt;
1846 *ppobj = (LPVOID)dsn;
1847 return S_OK;
1850 #if USE_DSOUND3D
1851 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1852 IDirectSound3DBufferImpl *ds3db;
1854 *ppobj = This->ds3db;
1855 if (*ppobj) {
1856 IDirectSound3DBuffer_AddRef((LPDIRECTSOUND3DBUFFER)This->ds3db);
1857 return S_OK;
1860 ds3db = (IDirectSound3DBufferImpl*)HeapAlloc(GetProcessHeap(),
1861 0,sizeof(*ds3db));
1862 ds3db->ref = 1;
1863 ds3db->dsb = This;
1864 ICOM_VTBL(ds3db) = &ds3dbvt;
1865 InitializeCriticalSection(&ds3db->lock);
1867 IDirectSoundBuffer_AddRef(iface);
1869 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
1870 ds3db->ds3db.vPosition.u1.x = 0.0;
1871 ds3db->ds3db.vPosition.u2.y = 0.0;
1872 ds3db->ds3db.vPosition.u3.z = 0.0;
1873 ds3db->ds3db.vVelocity.u1.x = 0.0;
1874 ds3db->ds3db.vVelocity.u2.y = 0.0;
1875 ds3db->ds3db.vVelocity.u3.z = 0.0;
1876 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
1877 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
1878 ds3db->ds3db.vConeOrientation.u1.x = 0.0;
1879 ds3db->ds3db.vConeOrientation.u2.y = 0.0;
1880 ds3db->ds3db.vConeOrientation.u3.z = 0.0;
1881 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME; ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
1882 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
1883 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
1884 ds3db->buflen = (This->buflen * primarybuf->wfx.nBlockAlign) /
1885 This->wfx.nBlockAlign;
1886 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
1887 if (ds3db->buffer == NULL) {
1888 ds3db->buflen = 0;
1889 ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
1892 ds3db->iks = (IKsPropertySetImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db->iks)));
1893 ds3db->iks->ref = 1;
1894 ds3db->iks->ds3db = ds3db;
1895 ICOM_VTBL(ds3db->iks) = &iksvt;
1897 return S_OK;
1899 #else
1900 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1901 FIXME("%s: I know about this GUID, but don't support it yet\n",
1902 debugstr_guid( riid ));
1903 *ppobj = NULL;
1904 return E_FAIL;
1906 #endif
1908 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1909 IDirectSound3DListenerImpl* dsl;
1911 if (This->dsound->listener) {
1912 *ppobj = This->dsound->listener;
1913 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)This->dsound->listener);
1914 return DS_OK;
1917 dsl = (IDirectSound3DListenerImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsl));
1918 dsl->ref = 1;
1919 ICOM_VTBL(dsl) = &ds3dlvt;
1920 *ppobj = (LPVOID)dsl;
1922 dsl->ds3dl.dwSize = sizeof(DS3DLISTENER);
1923 dsl->ds3dl.vPosition.u1.x = 0.0;
1924 dsl->ds3dl.vPosition.u2.y = 0.0;
1925 dsl->ds3dl.vPosition.u3.z = 0.0;
1926 dsl->ds3dl.vVelocity.u1.x = 0.0;
1927 dsl->ds3dl.vVelocity.u2.y = 0.0;
1928 dsl->ds3dl.vVelocity.u3.z = 0.0;
1929 dsl->ds3dl.vOrientFront.u1.x = 0.0;
1930 dsl->ds3dl.vOrientFront.u2.y = 0.0;
1931 dsl->ds3dl.vOrientFront.u3.z = 1.0;
1932 dsl->ds3dl.vOrientTop.u1.x = 0.0;
1933 dsl->ds3dl.vOrientTop.u2.y = 1.0;
1934 dsl->ds3dl.vOrientTop.u3.z = 0.0;
1935 dsl->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
1936 dsl->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
1938 InitializeCriticalSection(&dsl->lock);
1940 dsl->dsb = This;
1941 IDirectSoundBuffer_AddRef(iface);
1943 This->dsound->listener = dsl;
1944 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)dsl);
1946 return S_OK;
1949 FIXME( "Unknown GUID %s\n", debugstr_guid( riid ) );
1951 *ppobj = NULL;
1953 return E_FAIL;
1956 static ICOM_VTABLE(IDirectSoundBuffer) dsbvt =
1958 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1959 IDirectSoundBufferImpl_QueryInterface,
1960 IDirectSoundBufferImpl_AddRef,
1961 IDirectSoundBufferImpl_Release,
1962 IDirectSoundBufferImpl_GetCaps,
1963 IDirectSoundBufferImpl_GetCurrentPosition,
1964 IDirectSoundBufferImpl_GetFormat,
1965 IDirectSoundBufferImpl_GetVolume,
1966 IDirectSoundBufferImpl_GetPan,
1967 IDirectSoundBufferImpl_GetFrequency,
1968 IDirectSoundBufferImpl_GetStatus,
1969 IDirectSoundBufferImpl_Initialize,
1970 IDirectSoundBufferImpl_Lock,
1971 IDirectSoundBufferImpl_Play,
1972 IDirectSoundBufferImpl_SetCurrentPosition,
1973 IDirectSoundBufferImpl_SetFormat,
1974 IDirectSoundBufferImpl_SetVolume,
1975 IDirectSoundBufferImpl_SetPan,
1976 IDirectSoundBufferImpl_SetFrequency,
1977 IDirectSoundBufferImpl_Stop,
1978 IDirectSoundBufferImpl_Unlock,
1979 IDirectSoundBufferImpl_Restore
1982 /*******************************************************************************
1983 * IDirectSound
1986 static HRESULT WINAPI IDirectSoundImpl_SetCooperativeLevel(
1987 LPDIRECTSOUND iface,HWND hwnd,DWORD level
1989 ICOM_THIS(IDirectSoundImpl,iface);
1991 FIXME("(%p,%08lx,%ld):stub\n",This,(DWORD)hwnd,level);
1993 This->priolevel = level;
1995 return DS_OK;
1998 static HRESULT WINAPI IDirectSoundImpl_CreateSoundBuffer(
1999 LPDIRECTSOUND iface,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER ppdsb,LPUNKNOWN lpunk
2001 ICOM_THIS(IDirectSoundImpl,iface);
2002 IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
2003 LPWAVEFORMATEX wfex;
2004 HRESULT err = DS_OK;
2006 TRACE("(%p,%p,%p,%p)\n",This,dsbd,ippdsb,lpunk);
2008 if ((This == NULL) || (dsbd == NULL) || (ippdsb == NULL))
2009 return DSERR_INVALIDPARAM;
2011 if (TRACE_ON(dsound)) {
2012 TRACE("(structsize=%ld)\n",dsbd->dwSize);
2013 TRACE("(flags=0x%08lx:\n",dsbd->dwFlags);
2014 _dump_DSBCAPS(dsbd->dwFlags);
2015 DPRINTF(")\n");
2016 TRACE("(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
2017 TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
2020 wfex = dsbd->lpwfxFormat;
2022 if (wfex)
2023 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
2024 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
2025 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
2026 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
2027 wfex->wBitsPerSample, wfex->cbSize);
2029 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
2030 if (primarybuf) {
2031 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)primarybuf);
2032 *ippdsb = primarybuf;
2033 primarybuf->dsbd.dwFlags = dsbd->dwFlags;
2034 return DS_OK;
2035 } /* Else create primary buffer */
2038 *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
2039 if (*ippdsb == NULL)
2040 return DSERR_OUTOFMEMORY;
2041 ICOM_VTBL(*ippdsb) = &dsbvt;
2042 (*ippdsb)->ref = 1;
2043 (*ippdsb)->dsound = This;
2044 (*ippdsb)->parent = NULL;
2045 (*ippdsb)->buffer = NULL;
2047 memcpy(&((*ippdsb)->dsbd),dsbd,sizeof(*dsbd));
2048 if (dsbd->lpwfxFormat)
2049 memcpy(&((*ippdsb)->wfx), dsbd->lpwfxFormat, sizeof((*ippdsb)->wfx));
2051 TRACE("Created buffer at %p\n", *ippdsb);
2053 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
2054 (*ippdsb)->buflen = dsound->wfx.nAvgBytesPerSec;
2055 (*ippdsb)->freq = dsound->wfx.nSamplesPerSec;
2057 /* FIXME: verify that hardware capabilities (DSCAPS_PRIMARY flags) match */
2059 if (This->driver) {
2060 err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
2061 &((*ippdsb)->buflen),&((*ippdsb)->buffer),
2062 (LPVOID*)&((*ippdsb)->hwbuf));
2064 if (err == DS_OK)
2065 err = DSOUND_PrimaryOpen(*ippdsb);
2066 } else {
2067 DWORD capf = 0;
2068 int use_hw;
2070 (*ippdsb)->buflen = dsbd->dwBufferBytes;
2071 (*ippdsb)->freq = dsbd->lpwfxFormat->nSamplesPerSec;
2073 /* Check necessary hardware mixing capabilities */
2074 if (wfex->nChannels==2) capf |= DSCAPS_SECONDARYSTEREO;
2075 else capf |= DSCAPS_SECONDARYMONO;
2076 if (wfex->wBitsPerSample==16) capf |= DSCAPS_SECONDARY16BIT;
2077 else capf |= DSCAPS_SECONDARY8BIT;
2078 use_hw = (This->drvcaps.dwFlags & capf) == capf;
2080 /* FIXME: check hardware sample rate mixing capabilities */
2081 /* FIXME: check app hints for software/hardware buffer (STATIC, LOCHARDWARE, etc) */
2082 /* FIXME: check whether any hardware buffers are left */
2083 /* FIXME: handle DSDHEAP_CREATEHEAP for hardware buffers */
2085 /* Allocate system memory if applicable */
2086 if ((This->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) || !use_hw) {
2087 (*ippdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,(*ippdsb)->buflen);
2088 if ((*ippdsb)->buffer == NULL)
2089 err = DSERR_OUTOFMEMORY;
2092 /* Allocate the hardware buffer */
2093 if (use_hw && (err == DS_OK)) {
2094 err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
2095 &((*ippdsb)->buflen),&((*ippdsb)->buffer),
2096 (LPVOID*)&((*ippdsb)->hwbuf));
2100 if (err != DS_OK) {
2101 if ((*ippdsb)->buffer)
2102 HeapFree(GetProcessHeap(),0,(*ippdsb)->buffer);
2103 HeapFree(GetProcessHeap(),0,(*ippdsb));
2104 *ippdsb = NULL;
2105 return err;
2107 /* calculate fragment size and write lead */
2108 DSOUND_RecalcFormat(*ippdsb);
2110 /* It's not necessary to initialize values to zero since */
2111 /* we allocated this structure with HEAP_ZERO_MEMORY... */
2112 (*ippdsb)->playpos = 0;
2113 (*ippdsb)->buf_mixpos = 0;
2114 (*ippdsb)->state = STATE_STOPPED;
2115 DSOUND_RecalcVolPan(&((*ippdsb)->volpan));
2117 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
2118 (*ippdsb)->freqAdjust = ((*ippdsb)->freq << DSOUND_FREQSHIFT) /
2119 primarybuf->wfx.nSamplesPerSec;
2120 (*ippdsb)->nAvgBytesPerSec = (*ippdsb)->freq *
2121 dsbd->lpwfxFormat->nBlockAlign;
2124 EnterCriticalSection(&(This->lock));
2125 /* register buffer */
2126 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
2127 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl*)*(This->nrofbuffers+1));
2128 if (newbuffers) {
2129 This->buffers = newbuffers;
2130 This->buffers[This->nrofbuffers] = *ippdsb;
2131 This->nrofbuffers++;
2132 TRACE("buffer count is now %d\n", This->nrofbuffers);
2133 } else {
2134 ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
2135 err = DSERR_OUTOFMEMORY;
2138 LeaveCriticalSection(&(This->lock));
2140 IDirectSound_AddRef(iface);
2142 InitializeCriticalSection(&((*ippdsb)->lock));
2144 if (err != DS_OK) {
2145 /* oops... */
2146 IDirectSoundBuffer_Release(*ppdsb);
2147 *ippdsb = NULL;
2148 return err;
2151 #if USE_DSOUND3D
2152 if (dsbd->dwFlags & DSBCAPS_CTRL3D) {
2153 IDirectSound3DBufferImpl *ds3db;
2155 ds3db = (IDirectSound3DBufferImpl*)HeapAlloc(GetProcessHeap(),
2156 0,sizeof(*ds3db));
2157 ICOM_VTBL(ds3db) = &ds3dbvt;
2158 ds3db->ref = 1;
2159 (*ippdsb)->ds3db = ds3db;
2161 ds3db->dsb = (*ippdsb);
2162 IDirectSoundBufferImpl_AddRef((LPDIRECTSOUNDBUFFER)(*ippdsb));
2164 InitializeCriticalSection(&ds3db->lock);
2166 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
2167 ds3db->ds3db.vPosition.u1.x = 0.0;
2168 ds3db->ds3db.vPosition.u2.y = 0.0;
2169 ds3db->ds3db.vPosition.u3.z = 0.0;
2170 ds3db->ds3db.vVelocity.u1.x = 0.0;
2171 ds3db->ds3db.vVelocity.u2.y = 0.0;
2172 ds3db->ds3db.vVelocity.u3.z = 0.0;
2173 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
2174 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
2175 ds3db->ds3db.vConeOrientation.u1.x = 0.0;
2176 ds3db->ds3db.vConeOrientation.u2.y = 0.0;
2177 ds3db->ds3db.vConeOrientation.u3.z = 0.0;
2178 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME;
2179 ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
2180 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
2181 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
2182 ds3db->buflen = ((*ippdsb)->buflen * primarybuf->wfx.nBlockAlign) /
2183 (*ippdsb)->wfx.nBlockAlign;
2184 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
2185 if (ds3db->buffer == NULL) {
2186 ds3db->buflen = 0;
2187 ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
2189 ds3db->iks = (IKsPropertySetImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db->iks)));
2190 ds3db->iks->ref = 1;
2191 ds3db->iks->ds3db = ds3db;
2192 ICOM_VTBL(ds3db->iks) = &iksvt;
2195 #endif
2196 return DS_OK;
2199 static HRESULT WINAPI IDirectSoundImpl_DuplicateSoundBuffer(
2200 LPDIRECTSOUND iface,LPDIRECTSOUNDBUFFER pdsb,LPLPDIRECTSOUNDBUFFER ppdsb
2202 ICOM_THIS(IDirectSoundImpl,iface);
2203 IDirectSoundBufferImpl* ipdsb=(IDirectSoundBufferImpl*)pdsb;
2204 IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
2205 TRACE("(%p,%p,%p)\n",This,ipdsb,ippdsb);
2207 if (ipdsb->hwbuf) {
2208 FIXME("need to duplicate hardware buffer\n");
2211 *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
2213 IDirectSoundBuffer_AddRef(pdsb);
2214 memcpy(*ippdsb, ipdsb, sizeof(IDirectSoundBufferImpl));
2215 (*ippdsb)->ref = 1;
2216 (*ippdsb)->state = STATE_STOPPED;
2217 (*ippdsb)->playpos = 0;
2218 (*ippdsb)->buf_mixpos = 0;
2219 (*ippdsb)->dsound = This;
2220 (*ippdsb)->parent = ipdsb;
2221 memcpy(&((*ippdsb)->wfx), &(ipdsb->wfx), sizeof((*ippdsb)->wfx));
2222 InitializeCriticalSection(&(*ippdsb)->lock);
2223 /* register buffer */
2224 EnterCriticalSection(&(This->lock));
2226 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl**)*(This->nrofbuffers+1));
2227 if (newbuffers) {
2228 This->buffers = newbuffers;
2229 This->buffers[This->nrofbuffers] = *ippdsb;
2230 This->nrofbuffers++;
2231 TRACE("buffer count is now %d\n", This->nrofbuffers);
2232 } else {
2233 ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
2234 /* FIXME: release buffer */
2237 LeaveCriticalSection(&(This->lock));
2238 IDirectSound_AddRef(iface);
2239 return DS_OK;
2243 static HRESULT WINAPI IDirectSoundImpl_GetCaps(LPDIRECTSOUND iface,LPDSCAPS caps) {
2244 ICOM_THIS(IDirectSoundImpl,iface);
2245 TRACE("(%p,%p)\n",This,caps);
2246 TRACE("(flags=0x%08lx)\n",caps->dwFlags);
2248 if (caps == NULL)
2249 return DSERR_INVALIDPARAM;
2251 /* We should check this value, not set it. See Inside DirectX, p215. */
2252 caps->dwSize = sizeof(*caps);
2254 caps->dwFlags = This->drvcaps.dwFlags;
2256 /* FIXME: copy caps from This->drvcaps */
2257 caps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
2258 caps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
2260 caps->dwPrimaryBuffers = 1;
2262 caps->dwMaxHwMixingAllBuffers = 0;
2263 caps->dwMaxHwMixingStaticBuffers = 0;
2264 caps->dwMaxHwMixingStreamingBuffers = 0;
2266 caps->dwFreeHwMixingAllBuffers = 0;
2267 caps->dwFreeHwMixingStaticBuffers = 0;
2268 caps->dwFreeHwMixingStreamingBuffers = 0;
2270 caps->dwMaxHw3DAllBuffers = 0;
2271 caps->dwMaxHw3DStaticBuffers = 0;
2272 caps->dwMaxHw3DStreamingBuffers = 0;
2274 caps->dwFreeHw3DAllBuffers = 0;
2275 caps->dwFreeHw3DStaticBuffers = 0;
2276 caps->dwFreeHw3DStreamingBuffers = 0;
2278 caps->dwTotalHwMemBytes = 0;
2280 caps->dwFreeHwMemBytes = 0;
2282 caps->dwMaxContigFreeHwMemBytes = 0;
2284 caps->dwUnlockTransferRateHwBuffers = 4096; /* But we have none... */
2286 caps->dwPlayCpuOverheadSwBuffers = 1; /* 1% */
2288 return DS_OK;
2291 static ULONG WINAPI IDirectSoundImpl_AddRef(LPDIRECTSOUND iface) {
2292 ICOM_THIS(IDirectSoundImpl,iface);
2293 return ++(This->ref);
2296 static ULONG WINAPI IDirectSoundImpl_Release(LPDIRECTSOUND iface) {
2297 ICOM_THIS(IDirectSoundImpl,iface);
2298 TRACE("(%p), ref was %ld\n",This,This->ref);
2299 if (!--(This->ref)) {
2300 UINT i;
2302 timeKillEvent(This->timerID);
2303 timeEndPeriod(DS_TIME_RES);
2305 if (primarybuf)
2306 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)primarybuf);
2308 if (This->buffers) {
2309 for( i=0;i<This->nrofbuffers;i++)
2310 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->buffers[i]);
2313 if (This->primary)
2314 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->primary);
2316 DeleteCriticalSection(&This->lock);
2317 if (This->driver) {
2318 IDsDriver_Close(This->driver);
2319 } else {
2320 unsigned c;
2321 for (c=0; c<DS_HEL_FRAGS; c++)
2322 HeapFree(GetProcessHeap(),0,This->pwave[c]);
2324 if (This->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
2325 waveOutClose(This->hwo);
2327 if (This->driver)
2328 IDsDriver_Release(This->driver);
2330 HeapFree(GetProcessHeap(),0,This);
2331 dsound = NULL;
2332 return 0;
2334 return This->ref;
2337 static HRESULT WINAPI IDirectSoundImpl_SetSpeakerConfig(
2338 LPDIRECTSOUND iface,DWORD config
2340 ICOM_THIS(IDirectSoundImpl,iface);
2341 FIXME("(%p,0x%08lx):stub\n",This,config);
2342 return DS_OK;
2345 static HRESULT WINAPI IDirectSoundImpl_QueryInterface(
2346 LPDIRECTSOUND iface,REFIID riid,LPVOID *ppobj
2348 ICOM_THIS(IDirectSoundImpl,iface);
2350 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
2352 if (This->listener) {
2353 *ppobj = This->listener;
2354 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)This->listener);
2355 return DS_OK;
2358 This->listener = (IDirectSound3DListenerImpl*)HeapAlloc(
2359 GetProcessHeap(), 0, sizeof(*(This->listener)));
2360 This->listener->ref = 1;
2361 ICOM_VTBL(This->listener) = &ds3dlvt;
2362 *ppobj = (LPVOID)This->listener;
2363 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
2365 This->listener->dsb = NULL;
2367 This->listener->ds3dl.dwSize = sizeof(DS3DLISTENER);
2368 This->listener->ds3dl.vPosition.u1.x = 0.0;
2369 This->listener->ds3dl.vPosition.u2.y = 0.0;
2370 This->listener->ds3dl.vPosition.u3.z = 0.0;
2371 This->listener->ds3dl.vVelocity.u1.x = 0.0;
2372 This->listener->ds3dl.vVelocity.u2.y = 0.0;
2373 This->listener->ds3dl.vVelocity.u3.z = 0.0;
2374 This->listener->ds3dl.vOrientFront.u1.x = 0.0;
2375 This->listener->ds3dl.vOrientFront.u2.y = 0.0;
2376 This->listener->ds3dl.vOrientFront.u3.z = 1.0;
2377 This->listener->ds3dl.vOrientTop.u1.x = 0.0;
2378 This->listener->ds3dl.vOrientTop.u2.y = 1.0;
2379 This->listener->ds3dl.vOrientTop.u3.z = 0.0;
2380 This->listener->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
2381 This->listener->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
2382 This->listener->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
2384 InitializeCriticalSection(&This->listener->lock);
2386 return DS_OK;
2389 FIXME("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
2390 return E_FAIL;
2393 static HRESULT WINAPI IDirectSoundImpl_Compact(
2394 LPDIRECTSOUND iface)
2396 ICOM_THIS(IDirectSoundImpl,iface);
2397 TRACE("(%p)\n", This);
2398 return DS_OK;
2401 static HRESULT WINAPI IDirectSoundImpl_GetSpeakerConfig(
2402 LPDIRECTSOUND iface,
2403 LPDWORD lpdwSpeakerConfig)
2405 ICOM_THIS(IDirectSoundImpl,iface);
2406 TRACE("(%p, %p)\n", This, lpdwSpeakerConfig);
2407 *lpdwSpeakerConfig = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16);
2408 return DS_OK;
2411 static HRESULT WINAPI IDirectSoundImpl_Initialize(
2412 LPDIRECTSOUND iface,
2413 LPCGUID lpcGuid)
2415 ICOM_THIS(IDirectSoundImpl,iface);
2416 TRACE("(%p, %p)\n", This, lpcGuid);
2417 return DS_OK;
2420 static ICOM_VTABLE(IDirectSound) dsvt =
2422 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2423 IDirectSoundImpl_QueryInterface,
2424 IDirectSoundImpl_AddRef,
2425 IDirectSoundImpl_Release,
2426 IDirectSoundImpl_CreateSoundBuffer,
2427 IDirectSoundImpl_GetCaps,
2428 IDirectSoundImpl_DuplicateSoundBuffer,
2429 IDirectSoundImpl_SetCooperativeLevel,
2430 IDirectSoundImpl_Compact,
2431 IDirectSoundImpl_GetSpeakerConfig,
2432 IDirectSoundImpl_SetSpeakerConfig,
2433 IDirectSoundImpl_Initialize
2437 static void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len)
2439 int i;
2440 DWORD offset;
2441 LPDSBPOSITIONNOTIFY event;
2443 if (dsb->nrofnotifies == 0)
2444 return;
2446 TRACE("(%p) buflen = %ld, playpos = %ld, len = %d\n",
2447 dsb, dsb->buflen, dsb->playpos, len);
2448 for (i = 0; i < dsb->nrofnotifies ; i++) {
2449 event = dsb->notifies + i;
2450 offset = event->dwOffset;
2451 TRACE("checking %d, position %ld, event = %d\n",
2452 i, offset, event->hEventNotify);
2453 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
2454 /* OK. [Inside DirectX, p274] */
2455 /* */
2456 /* This also means we can't sort the entries by offset, */
2457 /* because DSBPN_OFFSETSTOP == -1 */
2458 if (offset == DSBPN_OFFSETSTOP) {
2459 if (dsb->state == STATE_STOPPED) {
2460 SetEvent(event->hEventNotify);
2461 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2462 return;
2463 } else
2464 return;
2466 if ((dsb->playpos + len) >= dsb->buflen) {
2467 if ((offset < ((dsb->playpos + len) % dsb->buflen)) ||
2468 (offset >= dsb->playpos)) {
2469 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2470 SetEvent(event->hEventNotify);
2472 } else {
2473 if ((offset >= dsb->playpos) && (offset < (dsb->playpos + len))) {
2474 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2475 SetEvent(event->hEventNotify);
2481 /* WAV format info can be found at: */
2482 /* */
2483 /* http://www.cwi.nl/ftp/audio/AudioFormats.part2 */
2484 /* ftp://ftp.cwi.nl/pub/audio/RIFF-format */
2485 /* */
2486 /* Import points to remember: */
2487 /* */
2488 /* 8-bit WAV is unsigned */
2489 /* 16-bit WAV is signed */
2491 static inline INT16 cvtU8toS16(BYTE byte)
2493 INT16 s = (byte - 128) << 8;
2495 return s;
2498 static inline BYTE cvtS16toU8(INT16 word)
2500 BYTE b = (word + 32768) >> 8;
2502 return b;
2506 /* We should be able to optimize these two inline functions */
2507 /* so that we aren't doing 8->16->8 conversions when it is */
2508 /* not necessary. But this is still a WIP. Optimize later. */
2509 static inline void get_fields(const IDirectSoundBufferImpl *dsb, BYTE *buf, INT *fl, INT *fr)
2511 INT16 *bufs = (INT16 *) buf;
2513 /* TRACE("(%p)\n", buf); */
2514 if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 2) {
2515 *fl = cvtU8toS16(*buf);
2516 *fr = cvtU8toS16(*(buf + 1));
2517 return;
2520 if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 2) {
2521 *fl = *bufs;
2522 *fr = *(bufs + 1);
2523 return;
2526 if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 1) {
2527 *fl = cvtU8toS16(*buf);
2528 *fr = *fl;
2529 return;
2532 if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 1) {
2533 *fl = *bufs;
2534 *fr = *bufs;
2535 return;
2538 FIXME("get_fields found an unsupported configuration\n");
2539 return;
2542 static inline void set_fields(BYTE *buf, INT fl, INT fr)
2544 INT16 *bufs = (INT16 *) buf;
2546 if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 2)) {
2547 *buf = cvtS16toU8(fl);
2548 *(buf + 1) = cvtS16toU8(fr);
2549 return;
2552 if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 2)) {
2553 *bufs = fl;
2554 *(bufs + 1) = fr;
2555 return;
2558 if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 1)) {
2559 *buf = cvtS16toU8((fl + fr) >> 1);
2560 return;
2563 if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 1)) {
2564 *bufs = (fl + fr) >> 1;
2565 return;
2567 FIXME("set_fields found an unsupported configuration\n");
2568 return;
2571 /* Now with PerfectPitch (tm) technology */
2572 static INT DSOUND_MixerNorm(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2574 INT i, size, ipos, ilen, fieldL, fieldR;
2575 BYTE *ibp, *obp;
2576 INT iAdvance = dsb->wfx.nBlockAlign;
2577 INT oAdvance = primarybuf->wfx.nBlockAlign;
2579 ibp = dsb->buffer + dsb->buf_mixpos;
2580 obp = buf;
2582 TRACE("(%p, %p, %p), buf_mixpos=%ld\n", dsb, ibp, obp, dsb->buf_mixpos);
2583 /* Check for the best case */
2584 if ((dsb->freq == primarybuf->wfx.nSamplesPerSec) &&
2585 (dsb->wfx.wBitsPerSample == primarybuf->wfx.wBitsPerSample) &&
2586 (dsb->wfx.nChannels == primarybuf->wfx.nChannels)) {
2587 DWORD bytesleft = dsb->buflen - dsb->buf_mixpos;
2588 TRACE("(%p) Best case\n", dsb);
2589 if (len <= bytesleft )
2590 memcpy(obp, ibp, len);
2591 else { /* wrap */
2592 memcpy(obp, ibp, bytesleft );
2593 memcpy(obp + bytesleft, dsb->buffer, len - bytesleft);
2595 return len;
2598 /* Check for same sample rate */
2599 if (dsb->freq == primarybuf->wfx.nSamplesPerSec) {
2600 TRACE("(%p) Same sample rate %ld = primary %ld\n", dsb,
2601 dsb->freq, primarybuf->wfx.nSamplesPerSec);
2602 ilen = 0;
2603 for (i = 0; i < len; i += oAdvance) {
2604 get_fields(dsb, ibp, &fieldL, &fieldR);
2605 ibp += iAdvance;
2606 ilen += iAdvance;
2607 set_fields(obp, fieldL, fieldR);
2608 obp += oAdvance;
2609 if (ibp >= (BYTE *)(dsb->buffer + dsb->buflen))
2610 ibp = dsb->buffer; /* wrap */
2612 return (ilen);
2615 /* Mix in different sample rates */
2616 /* */
2617 /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
2618 /* Patent Pending :-] */
2620 /* Patent enhancements (c) 2000 Ove KÃ¥ven,
2621 * TransGaming Technologies Inc. */
2623 TRACE("(%p) Adjusting frequency: %ld -> %ld\n",
2624 dsb, dsb->freq, primarybuf->wfx.nSamplesPerSec);
2626 size = len / oAdvance;
2627 ilen = 0;
2628 ipos = dsb->buf_mixpos;
2629 for (i = 0; i < size; i++) {
2630 get_fields(dsb, (dsb->buffer + ipos), &fieldL, &fieldR);
2631 set_fields(obp, fieldL, fieldR);
2632 obp += oAdvance;
2634 dsb->freqAcc += dsb->freqAdjust;
2635 if (dsb->freqAcc >= (1<<DSOUND_FREQSHIFT)) {
2636 ULONG adv = (dsb->freqAcc>>DSOUND_FREQSHIFT) * iAdvance;
2637 dsb->freqAcc &= (1<<DSOUND_FREQSHIFT)-1;
2638 ipos += adv; ilen += adv;
2639 while (ipos >= dsb->buflen)
2640 ipos -= dsb->buflen;
2643 return ilen;
2646 static void DSOUND_MixerVol(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2648 INT i, inc = primarybuf->wfx.wBitsPerSample >> 3;
2649 BYTE *bpc = buf;
2650 INT16 *bps = (INT16 *) buf;
2652 TRACE("(%p) left = %lx, right = %lx\n", dsb,
2653 dsb->volpan.dwTotalLeftAmpFactor, dsb->volpan.dwTotalRightAmpFactor);
2654 if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->volpan.lPan == 0)) &&
2655 (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->volpan.lVolume == 0)) &&
2656 !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
2657 return; /* Nothing to do */
2659 /* If we end up with some bozo coder using panning or 3D sound */
2660 /* with a mono primary buffer, it could sound very weird using */
2661 /* this method. Oh well, tough patooties. */
2663 for (i = 0; i < len; i += inc) {
2664 INT val;
2666 switch (inc) {
2668 case 1:
2669 /* 8-bit WAV is unsigned, but we need to operate */
2670 /* on signed data for this to work properly */
2671 val = *bpc - 128;
2672 val = ((val * (i & inc ? dsb->volpan.dwTotalRightAmpFactor : dsb->volpan.dwTotalLeftAmpFactor)) >> 16);
2673 *bpc = val + 128;
2674 bpc++;
2675 break;
2676 case 2:
2677 /* 16-bit WAV is signed -- much better */
2678 val = *bps;
2679 val = ((val * ((i & inc) ? dsb->volpan.dwTotalRightAmpFactor : dsb->volpan.dwTotalLeftAmpFactor)) >> 16);
2680 *bps = val;
2681 bps++;
2682 break;
2683 default:
2684 /* Very ugly! */
2685 FIXME("MixerVol had a nasty error\n");
2690 #ifdef USE_DSOUND3D
2691 static void DSOUND_Mixer3D(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2693 BYTE *ibp, *obp;
2694 DWORD buflen, buf_mixpos;
2696 buflen = dsb->ds3db->buflen;
2697 buf_mixpos = (dsb->buf_mixpos * primarybuf->wfx.nBlockAlign) / dsb->wfx.nBlockAlign;
2698 ibp = dsb->ds3db->buffer + buf_mixpos;
2699 obp = buf;
2701 if (buf_mixpos > buflen) {
2702 FIXME("Major breakage\n");
2703 return;
2706 if (len <= (buf_mixpos + buflen))
2707 memcpy(obp, ibp, len);
2708 else { /* wrap */
2709 memcpy(obp, ibp, buflen - buf_mixpos);
2710 memcpy(obp + (buflen - buf_mixpos),
2711 dsb->buffer,
2712 len - (buflen - buf_mixpos));
2714 return;
2716 #endif
2718 static void *tmp_buffer;
2719 static size_t tmp_buffer_len = 0;
2721 static void *DSOUND_tmpbuffer(size_t len)
2723 if (len>tmp_buffer_len) {
2724 void *new_buffer = realloc(tmp_buffer, len);
2725 if (new_buffer) {
2726 tmp_buffer = new_buffer;
2727 tmp_buffer_len = len;
2729 return new_buffer;
2731 return tmp_buffer;
2734 static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen)
2736 INT i, len, ilen, temp, field;
2737 INT advance = primarybuf->wfx.wBitsPerSample >> 3;
2738 BYTE *buf, *ibuf, *obuf;
2739 INT16 *ibufs, *obufs;
2741 len = fraglen;
2742 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
2743 temp = MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buflen,
2744 dsb->nAvgBytesPerSec) -
2745 MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buf_mixpos,
2746 dsb->nAvgBytesPerSec);
2747 len = (len > temp) ? temp : len;
2749 len &= ~3; /* 4 byte alignment */
2751 if (len == 0) {
2752 /* This should only happen if we aren't looping and temp < 4 */
2754 /* We skip the remainder, so check for possible events */
2755 DSOUND_CheckEvent(dsb, dsb->buflen - dsb->buf_mixpos);
2756 /* Stop */
2757 dsb->state = STATE_STOPPED;
2758 dsb->playpos = 0;
2759 dsb->buf_mixpos = 0;
2760 dsb->leadin = FALSE;
2761 /* Check for DSBPN_OFFSETSTOP */
2762 DSOUND_CheckEvent(dsb, 0);
2763 return 0;
2766 /* Been seeing segfaults in malloc() for some reason... */
2767 TRACE("allocating buffer (size = %d)\n", len);
2768 if ((buf = ibuf = (BYTE *) DSOUND_tmpbuffer(len)) == NULL)
2769 return 0;
2771 TRACE("MixInBuffer (%p) len = %d, dest = %ld\n", dsb, len, writepos);
2773 ilen = DSOUND_MixerNorm(dsb, ibuf, len);
2774 if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
2775 (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
2776 DSOUND_MixerVol(dsb, ibuf, len);
2778 obuf = primarybuf->buffer + writepos;
2779 for (i = 0; i < len; i += advance) {
2780 obufs = (INT16 *) obuf;
2781 ibufs = (INT16 *) ibuf;
2782 if (primarybuf->wfx.wBitsPerSample == 8) {
2783 /* 8-bit WAV is unsigned */
2784 field = (*ibuf - 128);
2785 field += (*obuf - 128);
2786 field = field > 127 ? 127 : field;
2787 field = field < -128 ? -128 : field;
2788 *obuf = field + 128;
2789 } else {
2790 /* 16-bit WAV is signed */
2791 field = *ibufs;
2792 field += *obufs;
2793 field = field > 32767 ? 32767 : field;
2794 field = field < -32768 ? -32768 : field;
2795 *obufs = field;
2797 ibuf += advance;
2798 obuf += advance;
2799 if (obuf >= (BYTE *)(primarybuf->buffer + primarybuf->buflen))
2800 obuf = primarybuf->buffer;
2802 /* free(buf); */
2804 if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY)
2805 DSOUND_CheckEvent(dsb, ilen);
2807 if (dsb->leadin && (dsb->startpos > dsb->buf_mixpos) && (dsb->startpos <= dsb->buf_mixpos + ilen)) {
2808 /* HACK... leadin should be reset when the PLAY position reaches the startpos,
2809 * not the MIX position... but if the sound buffer is bigger than our prebuffering
2810 * (which must be the case for the streaming buffers that need this hack anyway)
2811 * plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
2812 dsb->leadin = FALSE;
2815 dsb->buf_mixpos += ilen;
2817 if (dsb->buf_mixpos >= dsb->buflen) {
2818 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
2819 dsb->state = STATE_STOPPED;
2820 dsb->playpos = 0;
2821 dsb->buf_mixpos = 0;
2822 dsb->leadin = FALSE;
2823 DSOUND_CheckEvent(dsb, 0); /* For DSBPN_OFFSETSTOP */
2824 } else {
2825 /* wrap */
2826 while (dsb->buf_mixpos >= dsb->buflen)
2827 dsb->buf_mixpos -= dsb->buflen;
2828 if (dsb->leadin && (dsb->startpos <= dsb->buf_mixpos))
2829 dsb->leadin = FALSE; /* HACK: see above */
2833 return len;
2836 static void DSOUND_MixCancel(IDirectSoundBufferImpl *dsb, DWORD writepos)
2838 FIXME("prebuffer cancel not implemented yet\n");
2841 static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD playpos, DWORD writepos, DWORD mixlen)
2843 DWORD len, slen;
2844 /* determine this buffer's write position */
2845 DWORD buf_writepos = DSOUND_CalcPlayPosition(dsb, dsb->state & primarybuf->state, writepos,
2846 writepos, dsb->primary_mixpos, dsb->buf_mixpos);
2847 /* determine how much already-mixed data exists */
2848 DWORD buf_done =
2849 ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
2850 dsb->buf_mixpos - buf_writepos;
2851 DWORD primary_done =
2852 ((dsb->primary_mixpos < writepos) ? primarybuf->buflen : 0) +
2853 dsb->primary_mixpos - writepos;
2854 DWORD adv_done =
2855 ((primarybuf->buf_mixpos < writepos) ? primarybuf->buflen : 0) +
2856 primarybuf->buf_mixpos - writepos;
2857 int still_behind;
2859 TRACE("buf_writepos=%ld, primary_writepos=%ld\n", buf_writepos, writepos);
2860 TRACE("buf_done=%ld, primary_done=%ld\n", buf_done, primary_done);
2861 TRACE("buf_mixpos=%ld, primary_mixpos=%ld, mixlen=%ld\n", dsb->buf_mixpos, dsb->primary_mixpos,
2862 mixlen);
2863 TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb->playflags, dsb->startpos, dsb->leadin);
2865 /* save write position for non-GETCURRENTPOSITION2... */
2866 dsb->playpos = buf_writepos;
2868 /* check whether CalcPlayPosition detected a mixing underrun */
2869 if ((buf_done == 0) && (dsb->primary_mixpos != writepos)) {
2870 /* it did, but did we have more to play? */
2871 if ((dsb->playflags & DSBPLAY_LOOPING) ||
2872 (dsb->buf_mixpos < dsb->buflen)) {
2873 /* yes, have to recover */
2874 ERR("underrun on sound buffer %p\n", dsb);
2875 TRACE("recovering from underrun: primary_mixpos=%ld\n", writepos);
2877 dsb->primary_mixpos = writepos;
2878 primary_done = 0;
2880 /* determine how far ahead we should mix */
2881 if (((dsb->playflags & DSBPLAY_LOOPING) ||
2882 (dsb->leadin && (dsb->probably_valid_to != 0))) &&
2883 !(dsb->dsbd.dwFlags & DSBCAPS_STATIC)) {
2884 /* if this is a streaming buffer, it typically means that
2885 * we should defer mixing past probably_valid_to as long
2886 * as we can, to avoid unnecessary remixing */
2887 /* the heavy-looking calculations shouldn't be that bad,
2888 * as any game isn't likely to be have more than 1 or 2
2889 * streaming buffers in use at any time anyway... */
2890 DWORD probably_valid_left =
2891 (dsb->probably_valid_to == (DWORD)-1) ? dsb->buflen :
2892 ((dsb->probably_valid_to < buf_writepos) ? dsb->buflen : 0) +
2893 dsb->probably_valid_to - buf_writepos;
2894 /* check for leadin condition */
2895 if ((probably_valid_left == 0) &&
2896 (dsb->probably_valid_to == dsb->startpos) &&
2897 dsb->leadin)
2898 probably_valid_left = dsb->buflen;
2899 TRACE("streaming buffer probably_valid_to=%ld, probably_valid_left=%ld\n",
2900 dsb->probably_valid_to, probably_valid_left);
2901 /* check whether the app's time is already up */
2902 if (probably_valid_left < dsb->writelead) {
2903 WARN("probably_valid_to now within writelead, possible streaming underrun\n");
2904 /* once we pass the point of no return,
2905 * no reason to hold back anymore */
2906 dsb->probably_valid_to = (DWORD)-1;
2907 /* we just have to go ahead and mix what we have,
2908 * there's no telling what the app is thinking anyway */
2909 } else {
2910 /* divide valid length by our sample size */
2911 probably_valid_left /= dsb->wfx.nBlockAlign;
2912 /* adjust for our frequency */
2913 probably_valid_left = (probably_valid_left << DSOUND_FREQSHIFT) / dsb->freqAdjust;
2914 /* multiply by primary sample size */
2915 probably_valid_left *= primarybuf->wfx.nBlockAlign;
2916 /* check whether to clip mix_len */
2917 if (probably_valid_left < mixlen) {
2918 TRACE("clipping to probably_valid_left=%ld\n", probably_valid_left);
2919 mixlen = probably_valid_left;
2923 /* cut mixlen with what's already been mixed */
2924 if (mixlen < primary_done) {
2925 /* huh? and still CalcPlayPosition didn't
2926 * detect an underrun? */
2927 FIXME("problem with underrun detection (mixlen=%ld < primary_done=%ld)\n", mixlen, primary_done);
2928 return 0;
2930 len = mixlen - primary_done;
2931 TRACE("remaining mixlen=%ld\n", len);
2933 if (len < primarybuf->dsound->fraglen) {
2934 /* smaller than a fragment, wait until it gets larger
2935 * before we take the mixing overhead */
2936 TRACE("mixlen not worth it, deferring mixing\n");
2937 return 0;
2940 /* ok, we know how much to mix, let's go */
2941 still_behind = (adv_done > primary_done);
2942 while (len) {
2943 slen = primarybuf->buflen - dsb->primary_mixpos;
2944 if (slen > len) slen = len;
2945 slen = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, slen);
2947 if ((dsb->primary_mixpos < primarybuf->buf_mixpos) &&
2948 (dsb->primary_mixpos + slen >= primarybuf->buf_mixpos))
2949 still_behind = FALSE;
2951 dsb->primary_mixpos += slen; len -= slen;
2952 while (dsb->primary_mixpos >= primarybuf->buflen)
2953 dsb->primary_mixpos -= primarybuf->buflen;
2955 if ((dsb->state == STATE_STOPPED) || !slen) break;
2957 TRACE("new primary_mixpos=%ld, primary_advbase=%ld\n", dsb->primary_mixpos, primarybuf->buf_mixpos);
2958 TRACE("mixed data len=%ld, still_behind=%d\n", mixlen-len, still_behind);
2959 /* return how far we think the primary buffer can
2960 * advance its underrun detector...*/
2961 if (still_behind) return 0;
2962 if ((mixlen - len) < primary_done) return 0;
2963 slen = ((dsb->primary_mixpos < primarybuf->buf_mixpos) ?
2964 primarybuf->buflen : 0) + dsb->primary_mixpos -
2965 primarybuf->buf_mixpos;
2966 if (slen > mixlen) {
2967 /* the primary_done and still_behind checks above should have worked */
2968 FIXME("problem with advancement calculation (advlen=%ld > mixlen=%ld)\n", slen, mixlen);
2969 slen = 0;
2971 return slen;
2974 static DWORD DSOUND_MixToPrimary(DWORD playpos, DWORD writepos, DWORD mixlen, BOOL recover)
2976 INT i, len, maxlen = 0;
2977 IDirectSoundBufferImpl *dsb;
2979 TRACE("(%ld,%ld,%ld)\n", playpos, writepos, mixlen);
2980 for (i = dsound->nrofbuffers - 1; i >= 0; i--) {
2981 dsb = dsound->buffers[i];
2983 if (!dsb || !(ICOM_VTBL(dsb)))
2984 continue;
2985 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
2986 TRACE("Checking %p, mixlen=%ld\n", dsb, mixlen);
2987 EnterCriticalSection(&(dsb->lock));
2988 if (dsb->state == STATE_STOPPING) {
2989 DSOUND_MixCancel(dsb, writepos);
2990 dsb->state = STATE_STOPPED;
2991 } else {
2992 if ((dsb->state == STATE_STARTING) || recover)
2993 dsb->primary_mixpos = writepos;
2994 len = DSOUND_MixOne(dsb, playpos, writepos, mixlen);
2995 if (dsb->state == STATE_STARTING)
2996 dsb->state = STATE_PLAYING;
2997 maxlen = (len > maxlen) ? len : maxlen;
2999 LeaveCriticalSection(&(dsb->lock));
3003 return maxlen;
3006 static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
3008 int nfiller;
3009 BOOL forced;
3010 HRESULT hres;
3012 if (!dsound || !primarybuf) {
3013 ERR("dsound died without killing us?\n");
3014 timeKillEvent(timerID);
3015 timeEndPeriod(DS_TIME_RES);
3016 return;
3019 EnterCriticalSection(&(dsound->lock));
3021 if (!primarybuf || !primarybuf->ref) {
3022 /* seems the primary buffer is currently being released */
3023 LeaveCriticalSection(&(dsound->lock));
3024 return;
3027 /* the sound of silence */
3028 nfiller = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
3030 /* whether the primary is forced to play even without secondary buffers */
3031 forced = ((primarybuf->state == STATE_PLAYING) || (primarybuf->state == STATE_STARTING));
3033 if (primarybuf->hwbuf) {
3034 if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
3035 BOOL paused = ((primarybuf->state == STATE_STOPPED) || (primarybuf->state == STATE_STARTING));
3036 /* FIXME: document variables */
3037 DWORD playpos, writepos, inq, maxq, frag;
3038 hres = IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, &writepos);
3039 if (hres) {
3040 LeaveCriticalSection(&(dsound->lock));
3041 return;
3043 /* Well, we *could* do Just-In-Time mixing using the writepos,
3044 * but that's a little bit ambitious and unnecessary... */
3045 /* rather add our safety margin to the writepos, if we're playing */
3046 if (!paused) {
3047 writepos += primarybuf->writelead;
3048 while (writepos >= primarybuf->buflen)
3049 writepos -= primarybuf->buflen;
3050 } else writepos = playpos;
3051 TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld\n",
3052 playpos,writepos,primarybuf->playpos,primarybuf->buf_mixpos);
3053 /* wipe out just-played sound data */
3054 if (playpos < primarybuf->playpos) {
3055 memset(primarybuf->buffer + primarybuf->playpos, nfiller, primarybuf->buflen - primarybuf->playpos);
3056 memset(primarybuf->buffer, nfiller, playpos);
3057 } else {
3058 memset(primarybuf->buffer + primarybuf->playpos, nfiller, playpos - primarybuf->playpos);
3060 primarybuf->playpos = playpos;
3062 /* check how much prebuffering is left */
3063 inq = primarybuf->buf_mixpos;
3064 if (inq < writepos)
3065 inq += primarybuf->buflen;
3066 inq -= writepos;
3068 /* find the maximum we can prebuffer */
3069 if (!paused) {
3070 maxq = playpos;
3071 if (maxq < writepos)
3072 maxq += primarybuf->buflen;
3073 maxq -= writepos;
3074 } else maxq = primarybuf->buflen;
3076 /* clip maxq to DS_SND_QUEUE */
3077 frag = DS_SND_QUEUE * dsound->fraglen;
3078 if (maxq > frag) maxq = frag;
3080 EnterCriticalSection(&(primarybuf->lock));
3082 /* check for consistency */
3083 if (inq > maxq) {
3084 /* the playback position must have passed our last
3085 * mixed position, i.e. it's an underrun, or we have
3086 * nothing more to play */
3087 TRACE("reached end of mixed data (inq=%ld, maxq=%ld)\n", inq, maxq);
3088 inq = 0;
3089 /* stop the playback now, to allow buffers to refill */
3090 DSOUND_PrimaryStop(primarybuf);
3091 if (primarybuf->state == STATE_PLAYING) {
3092 primarybuf->state = STATE_STARTING;
3094 else if (primarybuf->state == STATE_STOPPING) {
3095 primarybuf->state = STATE_STOPPED;
3097 else {
3098 /* how can we have an underrun if we aren't playing? */
3099 WARN("unexpected primary state (%ld)\n", primarybuf->state);
3101 /* the Stop is supposed to reset play position to beginning of buffer */
3102 /* unfortunately, OSS is not able to do so, so get current pointer */
3103 hres = IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, NULL);
3104 if (hres) {
3105 LeaveCriticalSection(&(dsound->lock));
3106 LeaveCriticalSection(&(primarybuf->lock));
3107 return;
3109 writepos = playpos;
3110 primarybuf->playpos = playpos;
3111 primarybuf->buf_mixpos = writepos;
3112 inq = 0;
3113 maxq = primarybuf->buflen;
3114 if (maxq > frag) maxq = frag;
3115 memset(primarybuf->buffer, nfiller, primarybuf->buflen);
3116 paused = TRUE;
3119 /* do the mixing */
3120 frag = DSOUND_MixToPrimary(playpos, writepos, maxq, paused);
3121 if (forced) frag = maxq - inq;
3122 primarybuf->buf_mixpos += frag;
3123 while (primarybuf->buf_mixpos >= primarybuf->buflen)
3124 primarybuf->buf_mixpos -= primarybuf->buflen;
3126 if (frag) {
3127 /* buffers have been filled, restart playback */
3128 if (primarybuf->state == STATE_STARTING) {
3129 DSOUND_PrimaryPlay(primarybuf);
3130 primarybuf->state = STATE_PLAYING;
3131 TRACE("starting playback\n");
3133 else if (primarybuf->state == STATE_STOPPED) {
3134 /* the primarybuf is supposed to play if there's something to play
3135 * even if it is reported as stopped, so don't let this confuse you */
3136 DSOUND_PrimaryPlay(primarybuf);
3137 primarybuf->state = STATE_STOPPING;
3138 TRACE("starting playback\n");
3141 LeaveCriticalSection(&(primarybuf->lock));
3142 } else {
3143 /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
3144 if (primarybuf->state == STATE_STARTING) {
3145 DSOUND_PrimaryPlay(primarybuf);
3146 primarybuf->state = STATE_PLAYING;
3148 else if (primarybuf->state == STATE_STOPPING) {
3149 DSOUND_PrimaryStop(primarybuf);
3150 primarybuf->state = STATE_STOPPED;
3153 } else {
3154 /* using waveOut stuff */
3155 /* if no buffers are playing, we should be in pause mode now */
3156 DWORD writepos, mixq;
3157 /* clean out completed fragments */
3158 while (dsound->pwqueue && (dsound->pwave[dsound->pwplay]->dwFlags & WHDR_DONE)) {
3159 if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
3160 DWORD pos = dsound->pwplay * dsound->fraglen;
3161 TRACE("done playing primary pos=%ld\n",pos);
3162 memset(primarybuf->buffer + pos, nfiller, dsound->fraglen);
3164 dsound->pwplay++;
3165 if (dsound->pwplay >= DS_HEL_FRAGS) dsound->pwplay = 0;
3166 dsound->pwqueue--;
3168 primarybuf->playpos = dsound->pwplay * dsound->fraglen;
3169 TRACE("primary playpos=%ld, mixpos=%ld\n",primarybuf->playpos,primarybuf->buf_mixpos);
3170 EnterCriticalSection(&(primarybuf->lock));
3172 if (!dsound->pwqueue) {
3173 /* this is either an underrun or we have nothing more to play...
3174 * since playback has already stopped now, we can enter pause mode,
3175 * in order to allow buffers to refill */
3176 if (primarybuf->state == STATE_PLAYING) {
3177 TRACE("no more fragments ready to play\n");
3178 waveOutPause(dsound->hwo);
3179 primarybuf->state = STATE_STARTING;
3181 else if (primarybuf->state == STATE_STOPPING) {
3182 TRACE("no more fragments ready to play\n");
3183 waveOutPause(dsound->hwo);
3184 primarybuf->state = STATE_STOPPED;
3188 /* find next write position, plus some extra margin, if necessary */
3189 mixq = DS_HEL_MARGIN;
3190 if (mixq > dsound->pwqueue) mixq = dsound->pwqueue;
3191 writepos = primarybuf->playpos + mixq * dsound->fraglen;
3192 while (writepos >= primarybuf->buflen) writepos -= primarybuf->buflen;
3194 /* do the mixing */
3195 if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
3196 DWORD frag, maxq = DS_SND_QUEUE * dsound->fraglen;
3197 frag = DSOUND_MixToPrimary(primarybuf->playpos, writepos, maxq, FALSE);
3198 mixq = frag / dsound->fraglen;
3199 if (frag - (mixq * dsound->fraglen))
3200 mixq++;
3201 } else mixq = 0;
3202 if (forced) mixq = DS_SND_QUEUE;
3204 /* output it */
3205 for (; mixq; mixq--) {
3206 waveOutWrite(dsound->hwo, dsound->pwave[dsound->pwwrite], sizeof(WAVEHDR));
3207 dsound->pwwrite++;
3208 if (dsound->pwwrite >= DS_HEL_FRAGS) dsound->pwwrite = 0;
3209 dsound->pwqueue++;
3211 primarybuf->buf_mixpos = dsound->pwwrite * dsound->fraglen;
3213 if (dsound->pwqueue) {
3214 /* buffers have been filled, restart playback */
3215 if (primarybuf->state == STATE_STARTING) {
3216 waveOutRestart(dsound->hwo);
3217 primarybuf->state = STATE_PLAYING;
3218 TRACE("starting playback\n");
3220 else if (primarybuf->state == STATE_STOPPED) {
3221 /* the primarybuf is supposed to play if there's something to play
3222 * even if it is reported as stopped, so don't let this confuse you */
3223 waveOutRestart(dsound->hwo);
3224 primarybuf->state = STATE_STOPPING;
3225 TRACE("starting playback\n");
3228 LeaveCriticalSection(&(primarybuf->lock));
3230 TRACE("completed processing\n");
3231 LeaveCriticalSection(&(dsound->lock));
3234 /*******************************************************************************
3235 * DirectSoundCreate (DSOUND.1)
3237 HRESULT WINAPI DirectSoundCreate(REFGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter )
3239 IDirectSoundImpl** ippDS=(IDirectSoundImpl**)ppDS;
3240 PIDSDRIVER drv = NULL;
3241 WAVEOUTCAPSA wcaps;
3242 unsigned wod, wodn;
3243 HRESULT err = DS_OK;
3245 if (lpGUID)
3246 TRACE("(%p,%p,%p)\n",lpGUID,ippDS,pUnkOuter);
3247 else
3248 TRACE("DirectSoundCreate (%p)\n", ippDS);
3250 if (ippDS == NULL)
3251 return DSERR_INVALIDPARAM;
3253 if (primarybuf) {
3254 IDirectSound_AddRef((LPDIRECTSOUND)dsound);
3255 *ippDS = dsound;
3256 return DS_OK;
3259 /* Enumerate WINMM audio devices and find the one we want */
3260 wodn = waveOutGetNumDevs();
3261 if (!wodn) return DSERR_NODRIVER;
3263 /* FIXME: How do we find the GUID of an audio device? */
3264 /* Well, just use the first available device for now... */
3265 wod = 0;
3266 /* Get output device caps */
3267 waveOutGetDevCapsA(wod, &wcaps, sizeof(wcaps));
3268 /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
3269 waveOutMessage(wod, DRV_QUERYDSOUNDIFACE, (DWORD)&drv, 0);
3271 /* Allocate memory */
3272 *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundImpl));
3273 if (*ippDS == NULL)
3274 return DSERR_OUTOFMEMORY;
3276 ICOM_VTBL(*ippDS) = &dsvt;
3277 (*ippDS)->ref = 1;
3279 (*ippDS)->driver = drv;
3280 (*ippDS)->fraglen = 0;
3281 (*ippDS)->priolevel = DSSCL_NORMAL;
3282 (*ippDS)->nrofbuffers = 0;
3283 (*ippDS)->buffers = NULL;
3284 (*ippDS)->primary = NULL;
3285 (*ippDS)->listener = NULL;
3287 /* Get driver description */
3288 if (drv) {
3289 IDsDriver_GetDriverDesc(drv,&((*ippDS)->drvdesc));
3290 } else {
3291 /* if no DirectSound interface available, use WINMM API instead */
3292 (*ippDS)->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
3293 (*ippDS)->drvdesc.dnDevNode = wod; /* FIXME? */
3296 /* Set default wave format (may need it for waveOutOpen) */
3297 (*ippDS)->wfx.wFormatTag = WAVE_FORMAT_PCM;
3298 (*ippDS)->wfx.nChannels = 2;
3299 (*ippDS)->wfx.nSamplesPerSec = 22050;
3300 (*ippDS)->wfx.nAvgBytesPerSec = 44100;
3301 (*ippDS)->wfx.nBlockAlign = 2;
3302 (*ippDS)->wfx.wBitsPerSample = 8;
3304 /* If the driver requests being opened through MMSYSTEM
3305 * (which is recommended by the DDK), it is supposed to happen
3306 * before the DirectSound interface is opened */
3307 if ((*ippDS)->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
3308 /* FIXME: is this right? */
3309 err = mmErr(waveOutOpen(&((*ippDS)->hwo), (*ippDS)->drvdesc.dnDevNode, &((*ippDS)->wfx),
3310 0, 0, CALLBACK_NULL | WAVE_DIRECTSOUND));
3313 if (drv && (err == DS_OK))
3314 err = IDsDriver_Open(drv);
3316 /* FIXME: do we want to handle a temporarily busy device? */
3317 if (err != DS_OK) {
3318 HeapFree(GetProcessHeap(),0,*ippDS);
3319 *ippDS = NULL;
3320 return err;
3323 /* the driver is now open, so it's now allowed to call GetCaps */
3324 if (drv) {
3325 IDsDriver_GetCaps(drv,&((*ippDS)->drvcaps));
3326 } else {
3327 unsigned c;
3329 /* FIXME: look at wcaps */
3330 (*ippDS)->drvcaps.dwFlags =
3331 DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
3332 if (DS_EMULDRIVER)
3333 (*ippDS)->drvcaps.dwFlags |= DSCAPS_EMULDRIVER;
3335 /* Allocate memory for HEL buffer headers */
3336 for (c=0; c<DS_HEL_FRAGS; c++) {
3337 (*ippDS)->pwave[c] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEHDR));
3338 if (!(*ippDS)->pwave[c]) {
3339 /* Argh, out of memory */
3340 while (c--) {
3341 HeapFree(GetProcessHeap(),0,(*ippDS)->pwave[c]);
3342 waveOutClose((*ippDS)->hwo);
3343 HeapFree(GetProcessHeap(),0,*ippDS);
3344 *ippDS = NULL;
3345 return DSERR_OUTOFMEMORY;
3351 InitializeCriticalSection(&((*ippDS)->lock));
3353 if (!dsound) {
3354 dsound = (*ippDS);
3355 if (primarybuf == NULL) {
3356 DSBUFFERDESC dsbd;
3357 HRESULT hr;
3359 dsbd.dwSize = sizeof(DSBUFFERDESC);
3360 dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
3361 dsbd.dwBufferBytes = 0;
3362 dsbd.lpwfxFormat = &(dsound->wfx);
3363 hr = IDirectSound_CreateSoundBuffer(*ppDS, &dsbd, (LPDIRECTSOUNDBUFFER*)&primarybuf, NULL);
3364 if (hr != DS_OK)
3365 return hr;
3367 /* dsound->primary is NULL - don't need to Release */
3368 dsound->primary = primarybuf;
3369 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)primarybuf);
3371 timeBeginPeriod(DS_TIME_RES);
3372 dsound->timerID = timeSetEvent(DS_TIME_DEL, DS_TIME_RES, DSOUND_timer,
3373 (DWORD)dsound, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
3375 return DS_OK;
3378 /***************************************************************************
3379 * DirectSoundCaptureCreate [DSOUND.6]
3381 * Create and initialize a DirectSoundCapture interface
3383 * RETURNS
3384 * Success: DS_OK
3385 * Failure: DSERR_NOAGGREGATION, DSERR_ALLOCATED, DSERR_INVALIDPARAM,
3386 * DSERR_OUTOFMEMORY
3388 HRESULT WINAPI DirectSoundCaptureCreate(
3389 LPCGUID lpcGUID,
3390 LPDIRECTSOUNDCAPTURE* lplpDSC,
3391 LPUNKNOWN pUnkOuter )
3393 TRACE("(%s,%p,%p)\n", debugstr_guid(lpcGUID), lplpDSC, pUnkOuter);
3395 if( pUnkOuter ) {
3396 return DSERR_NOAGGREGATION;
3399 /* Default device? */
3400 if ( !lpcGUID ) {
3401 return DSOUND_CreateDirectSoundCapture( (LPVOID*)lplpDSC );
3404 FIXME( "Unknown GUID %s\n", debugstr_guid(lpcGUID) );
3405 *lplpDSC = NULL;
3407 return DSERR_OUTOFMEMORY;
3410 /***************************************************************************
3411 * DirectSoundCaptureEnumerateA [DSOUND.7]
3413 * Enumerate all DirectSound drivers installed in the system
3415 * RETURNS
3416 * Success: DS_OK
3417 * Failure: DSERR_INVALIDPARAM
3419 HRESULT WINAPI DirectSoundCaptureEnumerateA(
3420 LPDSENUMCALLBACKA lpDSEnumCallback,
3421 LPVOID lpContext)
3423 TRACE("(%p,%p)\n", lpDSEnumCallback, lpContext );
3425 if ( lpDSEnumCallback )
3426 lpDSEnumCallback(NULL,"WINE Primary Sound Capture Driver",
3427 "SoundCap",lpContext);
3430 return DS_OK;
3433 /***************************************************************************
3434 * DirectSoundCaptureEnumerateW [DSOUND.8]
3436 * Enumerate all DirectSound drivers installed in the system
3438 * RETURNS
3439 * Success: DS_OK
3440 * Failure: DSERR_INVALIDPARAM
3442 HRESULT WINAPI DirectSoundCaptureEnumerateW(
3443 LPDSENUMCALLBACKW lpDSEnumCallback,
3444 LPVOID lpContext)
3446 FIXME("(%p,%p):stub\n", lpDSEnumCallback, lpContext );
3447 return DS_OK;
3450 static HRESULT
3451 DSOUND_CreateDirectSoundCapture( LPVOID* ppobj )
3453 *ppobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( IDirectSoundCaptureImpl ) );
3455 if ( *ppobj == NULL ) {
3456 return DSERR_OUTOFMEMORY;
3460 ICOM_THIS(IDirectSoundCaptureImpl,*ppobj);
3462 This->ref = 1;
3463 ICOM_VTBL(This) = &dscvt;
3465 InitializeCriticalSection( &This->lock );
3468 return S_OK;
3471 static HRESULT WINAPI
3472 IDirectSoundCaptureImpl_QueryInterface(
3473 LPDIRECTSOUNDCAPTURE iface,
3474 REFIID riid,
3475 LPVOID* ppobj )
3477 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3479 FIXME( "(%p)->(%s,%p): stub\n", This, debugstr_guid(riid), ppobj );
3481 return E_FAIL;
3484 static ULONG WINAPI
3485 IDirectSoundCaptureImpl_AddRef( LPDIRECTSOUNDCAPTURE iface )
3487 ULONG uRef;
3488 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3490 EnterCriticalSection( &This->lock );
3492 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3493 uRef = ++(This->ref);
3495 LeaveCriticalSection( &This->lock );
3497 return uRef;
3500 static ULONG WINAPI
3501 IDirectSoundCaptureImpl_Release( LPDIRECTSOUNDCAPTURE iface )
3503 ULONG uRef;
3504 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3506 EnterCriticalSection( &This->lock );
3508 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3509 uRef = --(This->ref);
3511 LeaveCriticalSection( &This->lock );
3513 if ( uRef == 0 ) {
3514 DeleteCriticalSection( &This->lock );
3515 HeapFree( GetProcessHeap(), 0, This );
3518 return uRef;
3521 static HRESULT WINAPI
3522 IDirectSoundCaptureImpl_CreateCaptureBuffer(
3523 LPDIRECTSOUNDCAPTURE iface,
3524 LPCDSCBUFFERDESC lpcDSCBufferDesc,
3525 LPDIRECTSOUNDCAPTUREBUFFER* lplpDSCaptureBuffer,
3526 LPUNKNOWN pUnk )
3528 HRESULT hr;
3529 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3531 TRACE( "(%p)->(%p,%p,%p)\n", This, lpcDSCBufferDesc, lplpDSCaptureBuffer, pUnk );
3533 if ( pUnk ) {
3534 return DSERR_INVALIDPARAM;
3537 hr = DSOUND_CreateDirectSoundCaptureBuffer( lpcDSCBufferDesc, (LPVOID*)lplpDSCaptureBuffer );
3539 return hr;
3542 static HRESULT WINAPI
3543 IDirectSoundCaptureImpl_GetCaps(
3544 LPDIRECTSOUNDCAPTURE iface,
3545 LPDSCCAPS lpDSCCaps )
3547 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3549 FIXME( "(%p)->(%p): stub\n", This, lpDSCCaps );
3551 return DS_OK;
3554 static HRESULT WINAPI
3555 IDirectSoundCaptureImpl_Initialize(
3556 LPDIRECTSOUNDCAPTURE iface,
3557 LPCGUID lpcGUID )
3559 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3561 FIXME( "(%p)->(%p): stub\n", This, lpcGUID );
3563 return DS_OK;
3567 static ICOM_VTABLE(IDirectSoundCapture) dscvt =
3569 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3570 /* IUnknown methods */
3571 IDirectSoundCaptureImpl_QueryInterface,
3572 IDirectSoundCaptureImpl_AddRef,
3573 IDirectSoundCaptureImpl_Release,
3575 /* IDirectSoundCapture methods */
3576 IDirectSoundCaptureImpl_CreateCaptureBuffer,
3577 IDirectSoundCaptureImpl_GetCaps,
3578 IDirectSoundCaptureImpl_Initialize
3581 static HRESULT
3582 DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc, LPVOID* ppobj )
3585 FIXME( "(%p,%p): ignoring lpcDSCBufferDesc\n", lpcDSCBufferDesc, ppobj );
3587 *ppobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( IDirectSoundCaptureBufferImpl ) );
3589 if ( *ppobj == NULL ) {
3590 return DSERR_OUTOFMEMORY;
3594 ICOM_THIS(IDirectSoundCaptureBufferImpl,*ppobj);
3596 This->ref = 1;
3597 ICOM_VTBL(This) = &dscbvt;
3599 InitializeCriticalSection( &This->lock );
3602 return S_OK;
3606 static HRESULT WINAPI
3607 IDirectSoundCaptureBufferImpl_QueryInterface(
3608 LPDIRECTSOUNDCAPTUREBUFFER iface,
3609 REFIID riid,
3610 LPVOID* ppobj )
3612 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3614 FIXME( "(%p)->(%s,%p): stub\n", This, debugstr_guid(riid), ppobj );
3616 return E_FAIL;
3619 static ULONG WINAPI
3620 IDirectSoundCaptureBufferImpl_AddRef( LPDIRECTSOUNDCAPTUREBUFFER iface )
3622 ULONG uRef;
3623 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3625 EnterCriticalSection( &This->lock );
3627 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3628 uRef = ++(This->ref);
3630 LeaveCriticalSection( &This->lock );
3632 return uRef;
3635 static ULONG WINAPI
3636 IDirectSoundCaptureBufferImpl_Release( LPDIRECTSOUNDCAPTUREBUFFER iface )
3638 ULONG uRef;
3639 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3641 EnterCriticalSection( &This->lock );
3643 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3644 uRef = --(This->ref);
3646 LeaveCriticalSection( &This->lock );
3648 if ( uRef == 0 ) {
3649 DeleteCriticalSection( &This->lock );
3650 HeapFree( GetProcessHeap(), 0, This );
3653 return uRef;
3656 static HRESULT WINAPI
3657 IDirectSoundCaptureBufferImpl_GetCaps(
3658 LPDIRECTSOUNDCAPTUREBUFFER iface,
3659 LPDSCBCAPS lpDSCBCaps )
3661 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3663 FIXME( "(%p)->(%p): stub\n", This, lpDSCBCaps );
3665 return DS_OK;
3668 static HRESULT WINAPI
3669 IDirectSoundCaptureBufferImpl_GetCurrentPosition(
3670 LPDIRECTSOUNDCAPTUREBUFFER iface,
3671 LPDWORD lpdwCapturePosition,
3672 LPDWORD lpdwReadPosition )
3674 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3676 FIXME( "(%p)->(%p,%p): stub\n", This, lpdwCapturePosition, lpdwReadPosition );
3678 return DS_OK;
3681 static HRESULT WINAPI
3682 IDirectSoundCaptureBufferImpl_GetFormat(
3683 LPDIRECTSOUNDCAPTUREBUFFER iface,
3684 LPWAVEFORMATEX lpwfxFormat,
3685 DWORD dwSizeAllocated,
3686 LPDWORD lpdwSizeWritten )
3688 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3690 FIXME( "(%p)->(%p,0x%08lx,%p): stub\n", This, lpwfxFormat, dwSizeAllocated, lpdwSizeWritten );
3692 return DS_OK;
3695 static HRESULT WINAPI
3696 IDirectSoundCaptureBufferImpl_GetStatus(
3697 LPDIRECTSOUNDCAPTUREBUFFER iface,
3698 LPDWORD lpdwStatus )
3700 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3702 FIXME( "(%p)->(%p): stub\n", This, lpdwStatus );
3704 return DS_OK;
3707 static HRESULT WINAPI
3708 IDirectSoundCaptureBufferImpl_Initialize(
3709 LPDIRECTSOUNDCAPTUREBUFFER iface,
3710 LPDIRECTSOUNDCAPTURE lpDSC,
3711 LPCDSCBUFFERDESC lpcDSCBDesc )
3713 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3715 FIXME( "(%p)->(%p,%p): stub\n", This, lpDSC, lpcDSCBDesc );
3717 return DS_OK;
3720 static HRESULT WINAPI
3721 IDirectSoundCaptureBufferImpl_Lock(
3722 LPDIRECTSOUNDCAPTUREBUFFER iface,
3723 DWORD dwReadCusor,
3724 DWORD dwReadBytes,
3725 LPVOID* lplpvAudioPtr1,
3726 LPDWORD lpdwAudioBytes1,
3727 LPVOID* lplpvAudioPtr2,
3728 LPDWORD lpdwAudioBytes2,
3729 DWORD dwFlags )
3731 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3733 FIXME( "(%p)->(%08lu,%08lu,%p,%p,%p,%p,0x%08lx): stub\n", This, dwReadCusor, dwReadBytes, lplpvAudioPtr1, lpdwAudioBytes1, lplpvAudioPtr2, lpdwAudioBytes2, dwFlags );
3735 return DS_OK;
3738 static HRESULT WINAPI
3739 IDirectSoundCaptureBufferImpl_Start(
3740 LPDIRECTSOUNDCAPTUREBUFFER iface,
3741 DWORD dwFlags )
3743 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3745 FIXME( "(%p)->(0x%08lx): stub\n", This, dwFlags );
3747 return DS_OK;
3750 static HRESULT WINAPI
3751 IDirectSoundCaptureBufferImpl_Stop( LPDIRECTSOUNDCAPTUREBUFFER iface )
3753 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3755 FIXME( "(%p): stub\n", This );
3757 return DS_OK;
3760 static HRESULT WINAPI
3761 IDirectSoundCaptureBufferImpl_Unlock(
3762 LPDIRECTSOUNDCAPTUREBUFFER iface,
3763 LPVOID lpvAudioPtr1,
3764 DWORD dwAudioBytes1,
3765 LPVOID lpvAudioPtr2,
3766 DWORD dwAudioBytes2 )
3768 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3770 FIXME( "(%p)->(%p,%08lu,%p,%08lu): stub\n", This, lpvAudioPtr1, dwAudioBytes1, lpvAudioPtr2, dwAudioBytes2 );
3772 return DS_OK;
3776 static ICOM_VTABLE(IDirectSoundCaptureBuffer) dscbvt =
3778 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3779 /* IUnknown methods */
3780 IDirectSoundCaptureBufferImpl_QueryInterface,
3781 IDirectSoundCaptureBufferImpl_AddRef,
3782 IDirectSoundCaptureBufferImpl_Release,
3784 /* IDirectSoundCaptureBuffer methods */
3785 IDirectSoundCaptureBufferImpl_GetCaps,
3786 IDirectSoundCaptureBufferImpl_GetCurrentPosition,
3787 IDirectSoundCaptureBufferImpl_GetFormat,
3788 IDirectSoundCaptureBufferImpl_GetStatus,
3789 IDirectSoundCaptureBufferImpl_Initialize,
3790 IDirectSoundCaptureBufferImpl_Lock,
3791 IDirectSoundCaptureBufferImpl_Start,
3792 IDirectSoundCaptureBufferImpl_Stop,
3793 IDirectSoundCaptureBufferImpl_Unlock
3796 /*******************************************************************************
3797 * DirectSound ClassFactory
3799 typedef struct
3801 /* IUnknown fields */
3802 ICOM_VFIELD(IClassFactory);
3803 DWORD ref;
3804 } IClassFactoryImpl;
3806 static HRESULT WINAPI
3807 DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
3808 ICOM_THIS(IClassFactoryImpl,iface);
3810 FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
3811 return E_NOINTERFACE;
3814 static ULONG WINAPI
3815 DSCF_AddRef(LPCLASSFACTORY iface) {
3816 ICOM_THIS(IClassFactoryImpl,iface);
3817 return ++(This->ref);
3820 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) {
3821 ICOM_THIS(IClassFactoryImpl,iface);
3822 /* static class, won't be freed */
3823 return --(This->ref);
3826 static HRESULT WINAPI DSCF_CreateInstance(
3827 LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
3829 ICOM_THIS(IClassFactoryImpl,iface);
3831 TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
3832 if ( IsEqualGUID( &IID_IDirectSound, riid ) ) {
3833 /* FIXME: reuse already created dsound if present? */
3834 return DirectSoundCreate(riid,(LPDIRECTSOUND*)ppobj,pOuter);
3836 return E_NOINTERFACE;
3839 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
3840 ICOM_THIS(IClassFactoryImpl,iface);
3841 FIXME("(%p)->(%d),stub!\n",This,dolock);
3842 return S_OK;
3845 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
3846 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3847 DSCF_QueryInterface,
3848 DSCF_AddRef,
3849 DSCF_Release,
3850 DSCF_CreateInstance,
3851 DSCF_LockServer
3853 static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 };
3855 /*******************************************************************************
3856 * DllGetClassObject [DSOUND.@]
3857 * Retrieves class object from a DLL object
3859 * NOTES
3860 * Docs say returns STDAPI
3862 * PARAMS
3863 * rclsid [I] CLSID for the class object
3864 * riid [I] Reference to identifier of interface for class object
3865 * ppv [O] Address of variable to receive interface pointer for riid
3867 * RETURNS
3868 * Success: S_OK
3869 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
3870 * E_UNEXPECTED
3872 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
3874 TRACE("(%p,%p,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
3875 if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
3876 *ppv = (LPVOID)&DSOUND_CF;
3877 IClassFactory_AddRef((IClassFactory*)*ppv);
3878 return S_OK;
3881 FIXME("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
3882 return CLASS_E_CLASSNOTAVAILABLE;
3886 /*******************************************************************************
3887 * DllCanUnloadNow [DSOUND.@] Determines whether the DLL is in use.
3889 * RETURNS
3890 * Success: S_OK
3891 * Failure: S_FALSE
3893 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
3895 FIXME("(void): stub\n");
3896 return S_FALSE;