Further unified HAL and HEL mixing, and added some more intelligence
[wine/multimedia.git] / dlls / dsound / dsound_main.c
blob3e0c9698f75779aa68c0f2ea8ab7ea590dd201ba
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 /* some stuff may get more responsive with lower values though... */
52 #define DS_EMULDRIVER 1 /* some games (Quake 2, UT) refuse to accept
53 emulated dsound devices. set to 0 ! */
54 #define DS_HEL_FRAGS 48 /* HEL only: number of waveOut fragments in primary buffer
55 * (changing this won't help you) */
56 #define DS_HEL_MARGIN 5 /* HEL only: number of waveOut fragments ahead to mix in new buffers
57 * (keep this close or equal to DS_HEL_QUEUE for best results) */
58 #define DS_HEL_QUEUE 5 /* HEL only: number of waveOut fragments ahead to queue to driver
59 * (this will affect HEL sound reliability and latency) */
61 #define DS_SND_QUEUE_MAX 28 /* max number of fragments to prebuffer */
62 #define DS_SND_QUEUE_MIN 12 /* min number of fragments to prebuffer */
64 /* Linux does not support better timing than 10ms */
65 #define DS_TIME_RES 10 /* Resolution of multimedia timer */
66 #define DS_TIME_DEL 10 /* Delay of multimedia timer callback, and duration of HEL fragment */
68 /*****************************************************************************
69 * Predeclare the interface implementation structures
71 typedef struct IDirectSoundImpl IDirectSoundImpl;
72 typedef struct IDirectSoundBufferImpl IDirectSoundBufferImpl;
73 typedef struct IDirectSoundNotifyImpl IDirectSoundNotifyImpl;
74 typedef struct IDirectSound3DListenerImpl IDirectSound3DListenerImpl;
75 typedef struct IDirectSound3DBufferImpl IDirectSound3DBufferImpl;
76 typedef struct IDirectSoundCaptureImpl IDirectSoundCaptureImpl;
77 typedef struct IDirectSoundCaptureBufferImpl IDirectSoundCaptureBufferImpl;
78 typedef struct IKsPropertySetImpl IKsPropertySetImpl;
80 /*****************************************************************************
81 * IDirectSound implementation structure
83 struct IDirectSoundImpl
85 /* IUnknown fields */
86 ICOM_VFIELD(IDirectSound);
87 DWORD ref;
88 /* IDirectSoundImpl fields */
89 PIDSDRIVER driver;
90 DSDRIVERDESC drvdesc;
91 DSDRIVERCAPS drvcaps;
92 HWAVEOUT hwo;
93 LPWAVEHDR pwave[DS_HEL_FRAGS];
94 UINT timerID, pwplay, pwwrite, pwqueue, prebuf;
95 DWORD fraglen;
96 DWORD priolevel;
97 int nrofbuffers;
98 IDirectSoundBufferImpl** buffers;
99 IDirectSoundBufferImpl* primary;
100 IDirectSound3DListenerImpl* listener;
101 WAVEFORMATEX wfx; /* current main waveformat */
102 CRITICAL_SECTION lock;
105 /*****************************************************************************
106 * IDirectSoundBuffer implementation structure
108 struct IDirectSoundBufferImpl
110 /* FIXME: document */
111 /* IUnknown fields */
112 ICOM_VFIELD(IDirectSoundBuffer);
113 DWORD ref;
114 /* IDirectSoundBufferImpl fields */
115 PIDSDRIVERBUFFER hwbuf;
116 WAVEFORMATEX wfx;
117 LPBYTE buffer;
118 IDirectSound3DBufferImpl* ds3db;
119 DWORD playflags,state,leadin;
120 DWORD playpos,startpos,writelead,buflen;
121 DWORD nAvgBytesPerSec;
122 DWORD freq;
123 DSVOLUMEPAN volpan;
124 IDirectSoundBufferImpl* parent; /* for duplicates */
125 IDirectSoundImpl* dsound;
126 DSBUFFERDESC dsbd;
127 LPDSBPOSITIONNOTIFY notifies;
128 int nrofnotifies;
129 CRITICAL_SECTION lock;
130 /* used for frequency conversion (PerfectPitch) */
131 ULONG freqAdjust, freqAcc;
132 /* used for intelligent (well, sort of) prebuffering */
133 DWORD probably_valid_to;
134 DWORD primary_mixpos, buf_mixpos;
135 BOOL need_remix;
138 #define STATE_STOPPED 0
139 #define STATE_STARTING 1
140 #define STATE_PLAYING 2
141 #define STATE_STOPPING 3
143 /*****************************************************************************
144 * IDirectSoundNotify implementation structure
146 struct IDirectSoundNotifyImpl
148 /* IUnknown fields */
149 ICOM_VFIELD(IDirectSoundNotify);
150 DWORD ref;
151 /* IDirectSoundNotifyImpl fields */
152 IDirectSoundBufferImpl* dsb;
155 /*****************************************************************************
156 * IDirectSound3DListener implementation structure
158 struct IDirectSound3DListenerImpl
160 /* IUnknown fields */
161 ICOM_VFIELD(IDirectSound3DListener);
162 DWORD ref;
163 /* IDirectSound3DListenerImpl fields */
164 IDirectSoundBufferImpl* dsb;
165 DS3DLISTENER ds3dl;
166 CRITICAL_SECTION lock;
169 struct IKsPropertySetImpl
171 /* IUnknown fields */
172 ICOM_VFIELD(IKsPropertySet);
173 DWORD ref;
174 /* IKsPropertySetImpl fields */
175 IDirectSound3DBufferImpl *ds3db; /* backptr, no ref */
178 /*****************************************************************************
179 * IDirectSound3DBuffer implementation structure
181 struct IDirectSound3DBufferImpl
183 /* IUnknown fields */
184 ICOM_VFIELD(IDirectSound3DBuffer);
185 DWORD ref;
186 /* IDirectSound3DBufferImpl fields */
187 IDirectSoundBufferImpl* dsb;
188 DS3DBUFFER ds3db;
189 LPBYTE buffer;
190 DWORD buflen;
191 CRITICAL_SECTION lock;
192 IKsPropertySetImpl* iks;
196 /*****************************************************************************
197 * IDirectSoundCapture implementation structure
199 struct IDirectSoundCaptureImpl
201 /* IUnknown fields */
202 ICOM_VFIELD(IDirectSoundCapture);
203 DWORD ref;
205 /* IDirectSoundCaptureImpl fields */
206 CRITICAL_SECTION lock;
209 /*****************************************************************************
210 * IDirectSoundCapture implementation structure
212 struct IDirectSoundCaptureBufferImpl
214 /* IUnknown fields */
215 ICOM_VFIELD(IDirectSoundCaptureBuffer);
216 DWORD ref;
218 /* IDirectSoundCaptureBufferImpl fields */
219 CRITICAL_SECTION lock;
223 /* #define USE_DSOUND3D 1 */
225 #define DSOUND_FREQSHIFT (14)
227 static IDirectSoundImpl* dsound = NULL;
229 static IDirectSoundBufferImpl* primarybuf = NULL;
231 static void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len);
232 static void DSOUND_MixCancelAt(IDirectSoundBufferImpl *dsb, DWORD buf_writepos);
234 static void DSOUND_WaveQueue(IDirectSoundImpl *dsound, DWORD mixq);
235 static void DSOUND_PerformMix(void);
236 static void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2);
238 static HRESULT DSOUND_CreateDirectSoundCapture( LPVOID* ppobj );
239 static HRESULT DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc, LPVOID* ppobj );
241 static ICOM_VTABLE(IDirectSoundCapture) dscvt;
242 static ICOM_VTABLE(IDirectSoundCaptureBuffer) dscbvt;
244 static HRESULT mmErr(UINT err)
246 switch(err) {
247 case MMSYSERR_NOERROR:
248 return DS_OK;
249 case MMSYSERR_ALLOCATED:
250 return DSERR_ALLOCATED;
251 case MMSYSERR_INVALHANDLE:
252 return DSERR_GENERIC; /* FIXME */
253 case MMSYSERR_NODRIVER:
254 return DSERR_NODRIVER;
255 case MMSYSERR_NOMEM:
256 return DSERR_OUTOFMEMORY;
257 case MMSYSERR_INVALPARAM:
258 return DSERR_INVALIDPARAM;
259 default:
260 FIXME("Unknown MMSYS error %d\n",err);
261 return DSERR_GENERIC;
265 /***************************************************************************
266 * DirectSoundEnumerateA [DSOUND.2]
268 * Enumerate all DirectSound drivers installed in the system
270 * RETURNS
271 * Success: DS_OK
272 * Failure: DSERR_INVALIDPARAM
274 HRESULT WINAPI DirectSoundEnumerateA(
275 LPDSENUMCALLBACKA lpDSEnumCallback,
276 LPVOID lpContext)
278 TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
279 lpDSEnumCallback, lpContext);
281 #ifdef HAVE_OSS
282 if (lpDSEnumCallback != NULL)
283 lpDSEnumCallback(NULL,"WINE DirectSound using Open Sound System",
284 "sound",lpContext);
285 #endif
287 return DS_OK;
290 /***************************************************************************
291 * DirectSoundEnumerateW [DSOUND.3]
293 * Enumerate all DirectSound drivers installed in the system
295 * RETURNS
296 * Success: DS_OK
297 * Failure: DSERR_INVALIDPARAM
299 HRESULT WINAPI DirectSoundEnumerateW(
300 LPDSENUMCALLBACKW lpDSEnumCallback,
301 LPVOID lpContext )
303 FIXME("lpDSEnumCallback = %p, lpContext = %p: stub\n",
304 lpDSEnumCallback, lpContext);
306 return DS_OK;
310 static void _dump_DSBCAPS(DWORD xmask) {
311 struct {
312 DWORD mask;
313 char *name;
314 } flags[] = {
315 #define FE(x) { x, #x },
316 FE(DSBCAPS_PRIMARYBUFFER)
317 FE(DSBCAPS_STATIC)
318 FE(DSBCAPS_LOCHARDWARE)
319 FE(DSBCAPS_LOCSOFTWARE)
320 FE(DSBCAPS_CTRL3D)
321 FE(DSBCAPS_CTRLFREQUENCY)
322 FE(DSBCAPS_CTRLPAN)
323 FE(DSBCAPS_CTRLVOLUME)
324 FE(DSBCAPS_CTRLPOSITIONNOTIFY)
325 FE(DSBCAPS_CTRLDEFAULT)
326 FE(DSBCAPS_CTRLALL)
327 FE(DSBCAPS_STICKYFOCUS)
328 FE(DSBCAPS_GLOBALFOCUS)
329 FE(DSBCAPS_GETCURRENTPOSITION2)
330 FE(DSBCAPS_MUTE3DATMAXDISTANCE)
331 #undef FE
333 int i;
335 for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
336 if ((flags[i].mask & xmask) == flags[i].mask)
337 DPRINTF("%s ",flags[i].name);
340 /*******************************************************************************
341 * IKsPropertySet
344 /* IUnknown methods */
345 #ifdef USE_DSOUND3D
346 static HRESULT WINAPI IKsPropertySetImpl_QueryInterface(
347 LPKSPROPERTYSET iface, REFIID riid, LPVOID *ppobj
349 ICOM_THIS(IKsPropertySetImpl,iface);
351 FIXME("(%p,%s,%p), stub!\n",This,debugstr_guid(riid),ppobj);
352 return E_FAIL;
354 #endif
356 #ifdef USE_DSOUND3D
357 static ULONG WINAPI IKsPropertySetImpl_AddRef(LPKSPROPERTYSET iface) {
358 ICOM_THIS(IKsPropertySetImpl,iface);
360 This->ref++;
361 return This->ref;
363 #endif
365 #ifdef USE_DSOUND3D
366 static ULONG WINAPI IKsPropertySetImpl_Release(LPKSPROPERTYSET iface) {
367 ICOM_THIS(IKsPropertySetImpl,iface);
369 This->ref--;
370 return This->ref;
372 #endif
374 #ifdef USE_DSOUND3D
375 static HRESULT WINAPI IKsPropertySetImpl_Get(LPKSPROPERTYSET iface,
376 REFGUID guidPropSet, ULONG dwPropID,
377 LPVOID pInstanceData, ULONG cbInstanceData,
378 LPVOID pPropData, ULONG cbPropData,
379 PULONG pcbReturned
381 ICOM_THIS(IKsPropertySetImpl,iface);
383 FIXME("(%p,%s,%ld,%p,%ld,%p,%ld,%p), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pInstanceData,cbInstanceData,pPropData,cbPropData,pcbReturned);
384 return E_PROP_ID_UNSUPPORTED;
386 #endif
388 #ifdef USE_DSOUND3D
389 static HRESULT WINAPI IKsPropertySetImpl_Set(LPKSPROPERTYSET iface,
390 REFGUID guidPropSet, ULONG dwPropID,
391 LPVOID pInstanceData, ULONG cbInstanceData,
392 LPVOID pPropData, ULONG cbPropData
394 ICOM_THIS(IKsPropertySetImpl,iface);
396 FIXME("(%p,%s,%ld,%p,%ld,%p,%ld), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pInstanceData,cbInstanceData,pPropData,cbPropData);
397 return E_PROP_ID_UNSUPPORTED;
399 #endif
401 #ifdef USE_DSOUND3D
402 static HRESULT WINAPI IKsPropertySetImpl_QuerySupport(LPKSPROPERTYSET iface,
403 REFGUID guidPropSet, ULONG dwPropID, PULONG pTypeSupport
405 ICOM_THIS(IKsPropertySetImpl,iface);
407 FIXME("(%p,%s,%ld,%p), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pTypeSupport);
408 return E_PROP_ID_UNSUPPORTED;
410 #endif
412 #ifdef USE_DSOUND3D
413 static ICOM_VTABLE(IKsPropertySet) iksvt = {
414 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
415 IKsPropertySetImpl_QueryInterface,
416 IKsPropertySetImpl_AddRef,
417 IKsPropertySetImpl_Release,
418 IKsPropertySetImpl_Get,
419 IKsPropertySetImpl_Set,
420 IKsPropertySetImpl_QuerySupport
422 #endif
424 /*******************************************************************************
425 * IDirectSound3DBuffer
428 /* IUnknown methods */
429 #ifdef USE_DSOUND3D
430 static HRESULT WINAPI IDirectSound3DBufferImpl_QueryInterface(
431 LPDIRECTSOUND3DBUFFER iface, REFIID riid, LPVOID *ppobj)
433 ICOM_THIS(IDirectSound3DBufferImpl,iface);
435 if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
436 IDirectSound3DBuffer_AddRef(iface);
437 *ppobj = This->iks;
438 return S_OK;
441 FIXME("(%p,%s,%p), no such interface.\n",This,debugstr_guid(riid),ppobj);
442 return E_FAIL;
444 #endif
446 #ifdef USE_DSOUND3D
447 static ULONG WINAPI IDirectSound3DBufferImpl_AddRef(LPDIRECTSOUND3DBUFFER iface)
449 ICOM_THIS(IDirectSound3DBufferImpl,iface);
450 This->ref++;
451 return This->ref;
453 #endif
455 #ifdef USE_DSOUND3D
456 static ULONG WINAPI IDirectSound3DBufferImpl_Release(LPDIRECTSOUND3DBUFFER iface)
458 ICOM_THIS(IDirectSound3DBufferImpl,iface);
460 TRACE("(%p) ref was %ld\n", This, This->ref);
462 if(--This->ref)
463 return This->ref;
465 if (This->dsb)
466 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
468 DeleteCriticalSection(&This->lock);
470 HeapFree(GetProcessHeap(),0,This->buffer);
471 HeapFree(GetProcessHeap(),0,This);
473 return 0;
475 #endif
477 /* IDirectSound3DBuffer methods */
478 #ifdef USE_DSOUND3D
479 static HRESULT WINAPI IDirectSound3DBufferImpl_GetAllParameters(
480 LPDIRECTSOUND3DBUFFER iface,
481 LPDS3DBUFFER lpDs3dBuffer)
483 FIXME("stub\n");
484 return DS_OK;
486 #endif
488 #ifdef USE_DSOUND3D
489 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeAngles(
490 LPDIRECTSOUND3DBUFFER iface,
491 LPDWORD lpdwInsideConeAngle,
492 LPDWORD lpdwOutsideConeAngle)
494 FIXME("stub\n");
495 return DS_OK;
497 #endif
499 #ifdef USE_DSOUND3D
500 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOrientation(
501 LPDIRECTSOUND3DBUFFER iface,
502 LPD3DVECTOR lpvConeOrientation)
504 FIXME("stub\n");
505 return DS_OK;
507 #endif
509 #ifdef USE_DSOUND3D
510 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOutsideVolume(
511 LPDIRECTSOUND3DBUFFER iface,
512 LPLONG lplConeOutsideVolume)
514 FIXME("stub\n");
515 return DS_OK;
517 #endif
519 #ifdef USE_DSOUND3D
520 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMaxDistance(
521 LPDIRECTSOUND3DBUFFER iface,
522 LPD3DVALUE lpfMaxDistance)
524 FIXME("stub\n");
525 return DS_OK;
527 #endif
529 #ifdef USE_DSOUND3D
530 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMinDistance(
531 LPDIRECTSOUND3DBUFFER iface,
532 LPD3DVALUE lpfMinDistance)
534 FIXME("stub\n");
535 return DS_OK;
537 #endif
539 #ifdef USE_DSOUND3D
540 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMode(
541 LPDIRECTSOUND3DBUFFER iface,
542 LPDWORD lpdwMode)
544 FIXME("stub\n");
545 return DS_OK;
547 #endif
549 #ifdef USE_DSOUND3D
550 static HRESULT WINAPI IDirectSound3DBufferImpl_GetPosition(
551 LPDIRECTSOUND3DBUFFER iface,
552 LPD3DVECTOR lpvPosition)
554 FIXME("stub\n");
555 return DS_OK;
557 #endif
559 #ifdef USE_DSOUND3D
560 static HRESULT WINAPI IDirectSound3DBufferImpl_GetVelocity(
561 LPDIRECTSOUND3DBUFFER iface,
562 LPD3DVECTOR lpvVelocity)
564 FIXME("stub\n");
565 return DS_OK;
567 #endif
569 #ifdef USE_DSOUND3D
570 static HRESULT WINAPI IDirectSound3DBufferImpl_SetAllParameters(
571 LPDIRECTSOUND3DBUFFER iface,
572 LPCDS3DBUFFER lpcDs3dBuffer,
573 DWORD dwApply)
575 FIXME("stub\n");
576 return DS_OK;
578 #endif
580 #ifdef USE_DSOUND3D
581 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeAngles(
582 LPDIRECTSOUND3DBUFFER iface,
583 DWORD dwInsideConeAngle,
584 DWORD dwOutsideConeAngle,
585 DWORD dwApply)
587 FIXME("stub\n");
588 return DS_OK;
590 #endif
592 #ifdef USE_DSOUND3D
593 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOrientation(
594 LPDIRECTSOUND3DBUFFER iface,
595 D3DVALUE x, D3DVALUE y, D3DVALUE z,
596 DWORD dwApply)
598 FIXME("stub\n");
599 return DS_OK;
601 #endif
603 #ifdef USE_DSOUND3D
604 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOutsideVolume(
605 LPDIRECTSOUND3DBUFFER iface,
606 LONG lConeOutsideVolume,
607 DWORD dwApply)
609 FIXME("stub\n");
610 return DS_OK;
612 #endif
614 #ifdef USE_DSOUND3D
615 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMaxDistance(
616 LPDIRECTSOUND3DBUFFER iface,
617 D3DVALUE fMaxDistance,
618 DWORD dwApply)
620 FIXME("stub\n");
621 return DS_OK;
623 #endif
625 #ifdef USE_DSOUND3D
626 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMinDistance(
627 LPDIRECTSOUND3DBUFFER iface,
628 D3DVALUE fMinDistance,
629 DWORD dwApply)
631 FIXME("stub\n");
632 return DS_OK;
634 #endif
636 #ifdef USE_DSOUND3D
637 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMode(
638 LPDIRECTSOUND3DBUFFER iface,
639 DWORD dwMode,
640 DWORD dwApply)
642 ICOM_THIS(IDirectSound3DBufferImpl,iface);
643 TRACE("mode = %lx\n", dwMode);
644 This->ds3db.dwMode = dwMode;
645 return DS_OK;
647 #endif
649 #ifdef USE_DSOUND3D
650 static HRESULT WINAPI IDirectSound3DBufferImpl_SetPosition(
651 LPDIRECTSOUND3DBUFFER iface,
652 D3DVALUE x, D3DVALUE y, D3DVALUE z,
653 DWORD dwApply)
655 FIXME("stub\n");
656 return DS_OK;
658 #endif
660 #ifdef USE_DSOUND3D
661 static HRESULT WINAPI IDirectSound3DBufferImpl_SetVelocity(
662 LPDIRECTSOUND3DBUFFER iface,
663 D3DVALUE x, D3DVALUE y, D3DVALUE z,
664 DWORD dwApply)
666 FIXME("stub\n");
667 return DS_OK;
669 #endif
671 #ifdef USE_DSOUND3D
672 static ICOM_VTABLE(IDirectSound3DBuffer) ds3dbvt =
674 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
675 /* IUnknown methods */
676 IDirectSound3DBufferImpl_QueryInterface,
677 IDirectSound3DBufferImpl_AddRef,
678 IDirectSound3DBufferImpl_Release,
679 /* IDirectSound3DBuffer methods */
680 IDirectSound3DBufferImpl_GetAllParameters,
681 IDirectSound3DBufferImpl_GetConeAngles,
682 IDirectSound3DBufferImpl_GetConeOrientation,
683 IDirectSound3DBufferImpl_GetConeOutsideVolume,
684 IDirectSound3DBufferImpl_GetMaxDistance,
685 IDirectSound3DBufferImpl_GetMinDistance,
686 IDirectSound3DBufferImpl_GetMode,
687 IDirectSound3DBufferImpl_GetPosition,
688 IDirectSound3DBufferImpl_GetVelocity,
689 IDirectSound3DBufferImpl_SetAllParameters,
690 IDirectSound3DBufferImpl_SetConeAngles,
691 IDirectSound3DBufferImpl_SetConeOrientation,
692 IDirectSound3DBufferImpl_SetConeOutsideVolume,
693 IDirectSound3DBufferImpl_SetMaxDistance,
694 IDirectSound3DBufferImpl_SetMinDistance,
695 IDirectSound3DBufferImpl_SetMode,
696 IDirectSound3DBufferImpl_SetPosition,
697 IDirectSound3DBufferImpl_SetVelocity,
699 #endif
701 #ifdef USE_DSOUND3D
702 static int DSOUND_Create3DBuffer(IDirectSoundBufferImpl* dsb)
704 DWORD i, temp, iSize, oSize, offset;
705 LPBYTE bIbuf, bObuf, bTbuf = NULL;
706 LPWORD wIbuf, wObuf, wTbuf = NULL;
708 /* Inside DirectX says it's stupid but allowed */
709 if (dsb->wfx.nChannels == 2) {
710 /* Convert to mono */
711 if (dsb->wfx.wBitsPerSample == 16) {
712 iSize = dsb->buflen / 4;
713 wTbuf = malloc(dsb->buflen / 2);
714 if (wTbuf == NULL)
715 return DSERR_OUTOFMEMORY;
716 for (i = 0; i < iSize; i++)
717 wTbuf[i] = (dsb->buffer[i * 2] + dsb->buffer[(i * 2) + 1]) / 2;
718 wIbuf = wTbuf;
719 } else {
720 iSize = dsb->buflen / 2;
721 bTbuf = malloc(dsb->buflen / 2);
722 if (bTbuf == NULL)
723 return DSERR_OUTOFMEMORY;
724 for (i = 0; i < iSize; i++)
725 bTbuf[i] = (dsb->buffer[i * 2] + dsb->buffer[(i * 2) + 1]) / 2;
726 bIbuf = bTbuf;
728 } else {
729 if (dsb->wfx.wBitsPerSample == 16) {
730 iSize = dsb->buflen / 2;
731 wIbuf = (LPWORD) dsb->buffer;
732 } else {
733 bIbuf = (LPBYTE) dsb->buffer;
734 iSize = dsb->buflen;
738 if (primarybuf->wfx.wBitsPerSample == 16) {
739 wObuf = (LPWORD) dsb->ds3db->buffer;
740 oSize = dsb->ds3db->buflen / 2;
741 } else {
742 bObuf = (LPBYTE) dsb->ds3db->buffer;
743 oSize = dsb->ds3db->buflen;
746 offset = primarybuf->wfx.nSamplesPerSec / 100; /* 10ms */
747 if (primarybuf->wfx.wBitsPerSample == 16 && dsb->wfx.wBitsPerSample == 16)
748 for (i = 0; i < iSize; i++) {
749 temp = wIbuf[i];
750 if (i >= offset)
751 temp += wIbuf[i - offset] >> 9;
752 else
753 temp += wIbuf[i + iSize - offset] >> 9;
754 wObuf[i * 2] = temp;
755 wObuf[(i * 2) + 1] = temp;
757 else if (primarybuf->wfx.wBitsPerSample == 8 && dsb->wfx.wBitsPerSample == 8)
758 for (i = 0; i < iSize; i++) {
759 temp = bIbuf[i];
760 if (i >= offset)
761 temp += bIbuf[i - offset] >> 5;
762 else
763 temp += bIbuf[i + iSize - offset] >> 5;
764 bObuf[i * 2] = temp;
765 bObuf[(i * 2) + 1] = temp;
768 if (wTbuf)
769 free(wTbuf);
770 if (bTbuf)
771 free(bTbuf);
773 return DS_OK;
775 #endif
776 /*******************************************************************************
777 * IDirectSound3DListener
780 /* IUnknown methods */
781 static HRESULT WINAPI IDirectSound3DListenerImpl_QueryInterface(
782 LPDIRECTSOUND3DLISTENER iface, REFIID riid, LPVOID *ppobj)
784 ICOM_THIS(IDirectSound3DListenerImpl,iface);
786 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
787 return E_FAIL;
790 static ULONG WINAPI IDirectSound3DListenerImpl_AddRef(LPDIRECTSOUND3DLISTENER iface)
792 ICOM_THIS(IDirectSound3DListenerImpl,iface);
793 This->ref++;
794 return This->ref;
797 static ULONG WINAPI IDirectSound3DListenerImpl_Release(LPDIRECTSOUND3DLISTENER iface)
799 ULONG ulReturn;
800 ICOM_THIS(IDirectSound3DListenerImpl,iface);
802 TRACE("(%p) ref was %ld\n", This, This->ref);
804 ulReturn = --This->ref;
806 /* Free all resources */
807 if( ulReturn == 0 ) {
808 if(This->dsb)
809 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
810 DeleteCriticalSection(&This->lock);
811 HeapFree(GetProcessHeap(),0,This);
814 return ulReturn;
817 /* IDirectSound3DListener methods */
818 static HRESULT WINAPI IDirectSound3DListenerImpl_GetAllParameter(
819 LPDIRECTSOUND3DLISTENER iface,
820 LPDS3DLISTENER lpDS3DL)
822 FIXME("stub\n");
823 return DS_OK;
826 static HRESULT WINAPI IDirectSound3DListenerImpl_GetDistanceFactor(
827 LPDIRECTSOUND3DLISTENER iface,
828 LPD3DVALUE lpfDistanceFactor)
830 FIXME("stub\n");
831 return DS_OK;
834 static HRESULT WINAPI IDirectSound3DListenerImpl_GetDopplerFactor(
835 LPDIRECTSOUND3DLISTENER iface,
836 LPD3DVALUE lpfDopplerFactor)
838 FIXME("stub\n");
839 return DS_OK;
842 static HRESULT WINAPI IDirectSound3DListenerImpl_GetOrientation(
843 LPDIRECTSOUND3DLISTENER iface,
844 LPD3DVECTOR lpvOrientFront,
845 LPD3DVECTOR lpvOrientTop)
847 FIXME("stub\n");
848 return DS_OK;
851 static HRESULT WINAPI IDirectSound3DListenerImpl_GetPosition(
852 LPDIRECTSOUND3DLISTENER iface,
853 LPD3DVECTOR lpvPosition)
855 FIXME("stub\n");
856 return DS_OK;
859 static HRESULT WINAPI IDirectSound3DListenerImpl_GetRolloffFactor(
860 LPDIRECTSOUND3DLISTENER iface,
861 LPD3DVALUE lpfRolloffFactor)
863 FIXME("stub\n");
864 return DS_OK;
867 static HRESULT WINAPI IDirectSound3DListenerImpl_GetVelocity(
868 LPDIRECTSOUND3DLISTENER iface,
869 LPD3DVECTOR lpvVelocity)
871 FIXME("stub\n");
872 return DS_OK;
875 static HRESULT WINAPI IDirectSound3DListenerImpl_SetAllParameters(
876 LPDIRECTSOUND3DLISTENER iface,
877 LPCDS3DLISTENER lpcDS3DL,
878 DWORD dwApply)
880 FIXME("stub\n");
881 return DS_OK;
884 static HRESULT WINAPI IDirectSound3DListenerImpl_SetDistanceFactor(
885 LPDIRECTSOUND3DLISTENER iface,
886 D3DVALUE fDistanceFactor,
887 DWORD dwApply)
889 FIXME("stub\n");
890 return DS_OK;
893 static HRESULT WINAPI IDirectSound3DListenerImpl_SetDopplerFactor(
894 LPDIRECTSOUND3DLISTENER iface,
895 D3DVALUE fDopplerFactor,
896 DWORD dwApply)
898 FIXME("stub\n");
899 return DS_OK;
902 static HRESULT WINAPI IDirectSound3DListenerImpl_SetOrientation(
903 LPDIRECTSOUND3DLISTENER iface,
904 D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront,
905 D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop,
906 DWORD dwApply)
908 FIXME("stub\n");
909 return DS_OK;
912 static HRESULT WINAPI IDirectSound3DListenerImpl_SetPosition(
913 LPDIRECTSOUND3DLISTENER iface,
914 D3DVALUE x, D3DVALUE y, D3DVALUE z,
915 DWORD dwApply)
917 FIXME("stub\n");
918 return DS_OK;
921 static HRESULT WINAPI IDirectSound3DListenerImpl_SetRolloffFactor(
922 LPDIRECTSOUND3DLISTENER iface,
923 D3DVALUE fRolloffFactor,
924 DWORD dwApply)
926 FIXME("stub\n");
927 return DS_OK;
930 static HRESULT WINAPI IDirectSound3DListenerImpl_SetVelocity(
931 LPDIRECTSOUND3DLISTENER iface,
932 D3DVALUE x, D3DVALUE y, D3DVALUE z,
933 DWORD dwApply)
935 FIXME("stub\n");
936 return DS_OK;
939 static HRESULT WINAPI IDirectSound3DListenerImpl_CommitDeferredSettings(
940 LPDIRECTSOUND3DLISTENER iface)
943 FIXME("stub\n");
944 return DS_OK;
947 static ICOM_VTABLE(IDirectSound3DListener) ds3dlvt =
949 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
950 /* IUnknown methods */
951 IDirectSound3DListenerImpl_QueryInterface,
952 IDirectSound3DListenerImpl_AddRef,
953 IDirectSound3DListenerImpl_Release,
954 /* IDirectSound3DListener methods */
955 IDirectSound3DListenerImpl_GetAllParameter,
956 IDirectSound3DListenerImpl_GetDistanceFactor,
957 IDirectSound3DListenerImpl_GetDopplerFactor,
958 IDirectSound3DListenerImpl_GetOrientation,
959 IDirectSound3DListenerImpl_GetPosition,
960 IDirectSound3DListenerImpl_GetRolloffFactor,
961 IDirectSound3DListenerImpl_GetVelocity,
962 IDirectSound3DListenerImpl_SetAllParameters,
963 IDirectSound3DListenerImpl_SetDistanceFactor,
964 IDirectSound3DListenerImpl_SetDopplerFactor,
965 IDirectSound3DListenerImpl_SetOrientation,
966 IDirectSound3DListenerImpl_SetPosition,
967 IDirectSound3DListenerImpl_SetRolloffFactor,
968 IDirectSound3DListenerImpl_SetVelocity,
969 IDirectSound3DListenerImpl_CommitDeferredSettings,
972 /*******************************************************************************
973 * IDirectSoundNotify
975 static HRESULT WINAPI IDirectSoundNotifyImpl_QueryInterface(
976 LPDIRECTSOUNDNOTIFY iface,REFIID riid,LPVOID *ppobj
978 ICOM_THIS(IDirectSoundNotifyImpl,iface);
980 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
981 return E_FAIL;
984 static ULONG WINAPI IDirectSoundNotifyImpl_AddRef(LPDIRECTSOUNDNOTIFY iface) {
985 ICOM_THIS(IDirectSoundNotifyImpl,iface);
986 return ++(This->ref);
989 static ULONG WINAPI IDirectSoundNotifyImpl_Release(LPDIRECTSOUNDNOTIFY iface) {
990 ICOM_THIS(IDirectSoundNotifyImpl,iface);
992 TRACE("(%p) ref was %ld\n", This, This->ref);
994 This->ref--;
995 if (!This->ref) {
996 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
997 HeapFree(GetProcessHeap(),0,This);
998 return 0;
1000 return This->ref;
1003 static HRESULT WINAPI IDirectSoundNotifyImpl_SetNotificationPositions(
1004 LPDIRECTSOUNDNOTIFY iface,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
1006 ICOM_THIS(IDirectSoundNotifyImpl,iface);
1007 int i;
1009 if (TRACE_ON(dsound)) {
1010 TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);
1011 for (i=0;i<howmuch;i++)
1012 TRACE("notify at %ld to 0x%08lx\n",
1013 notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
1015 This->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,This->dsb->notifies,(This->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
1016 memcpy( This->dsb->notifies+This->dsb->nrofnotifies,
1017 notify,
1018 howmuch*sizeof(DSBPOSITIONNOTIFY)
1020 This->dsb->nrofnotifies+=howmuch;
1022 return S_OK;
1025 static ICOM_VTABLE(IDirectSoundNotify) dsnvt =
1027 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1028 IDirectSoundNotifyImpl_QueryInterface,
1029 IDirectSoundNotifyImpl_AddRef,
1030 IDirectSoundNotifyImpl_Release,
1031 IDirectSoundNotifyImpl_SetNotificationPositions,
1034 /*******************************************************************************
1035 * IDirectSoundBuffer
1038 static void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan)
1040 double temp;
1042 /* the AmpFactors are expressed in 16.16 fixed point */
1043 volpan->dwVolAmpFactor = (ULONG) (pow(2.0, volpan->lVolume / 600.0) * 65536);
1044 /* FIXME: dwPan{Left|Right}AmpFactor */
1046 /* FIXME: use calculated vol and pan ampfactors */
1047 temp = (double) (volpan->lVolume - (volpan->lPan > 0 ? volpan->lPan : 0));
1048 volpan->dwTotalLeftAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 65536);
1049 temp = (double) (volpan->lVolume + (volpan->lPan < 0 ? volpan->lPan : 0));
1050 volpan->dwTotalRightAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 65536);
1052 TRACE("left = %lx, right = %lx\n", volpan->dwTotalLeftAmpFactor, volpan->dwTotalRightAmpFactor);
1055 static void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
1057 DWORD sw;
1059 sw = dsb->wfx.nChannels * (dsb->wfx.wBitsPerSample / 8);
1060 if ((dsb->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && dsb->hwbuf) {
1061 DWORD fraglen;
1062 /* let fragment size approximate the timer delay */
1063 fraglen = (dsb->freq * DS_TIME_DEL / 1000) * sw;
1064 /* reduce fragment size until an integer number of them fits in the buffer */
1065 /* (FIXME: this may or may not be a good idea) */
1066 while (dsb->buflen % fraglen) fraglen -= sw;
1067 dsb->dsound->fraglen = fraglen;
1068 TRACE("fraglen=%ld\n", dsb->dsound->fraglen);
1070 /* calculate the 10ms write lead */
1071 dsb->writelead = (dsb->freq / 100) * sw;
1074 static HRESULT DSOUND_PrimaryOpen(IDirectSoundBufferImpl *dsb)
1076 HRESULT err = DS_OK;
1078 /* are we using waveOut stuff? */
1079 if (!dsb->hwbuf) {
1080 LPBYTE newbuf;
1081 DWORD buflen;
1082 HRESULT merr = DS_OK;
1083 /* Start in pause mode, to allow buffers to get filled */
1084 waveOutPause(dsb->dsound->hwo);
1085 if (dsb->state == STATE_PLAYING) dsb->state = STATE_STARTING;
1086 else if (dsb->state == STATE_STOPPING) dsb->state = STATE_STOPPED;
1087 /* use fragments of 10ms (1/100s) each (which should get us within
1088 * the documented write cursor lead of 10-15ms) */
1089 buflen = ((dsb->wfx.nAvgBytesPerSec / 100) & ~3) * DS_HEL_FRAGS;
1090 TRACE("desired buflen=%ld, old buffer=%p\n", buflen, dsb->buffer);
1091 /* reallocate emulated primary buffer */
1092 newbuf = (LPBYTE)HeapReAlloc(GetProcessHeap(),0,dsb->buffer,buflen);
1093 if (newbuf == NULL) {
1094 ERR("failed to allocate primary buffer\n");
1095 merr = DSERR_OUTOFMEMORY;
1096 /* but the old buffer might still exists and must be re-prepared */
1097 } else {
1098 dsb->buffer = newbuf;
1099 dsb->buflen = buflen;
1101 if (dsb->buffer) {
1102 unsigned c;
1103 IDirectSoundImpl *ds = dsb->dsound;
1105 ds->fraglen = dsb->buflen / DS_HEL_FRAGS;
1107 /* prepare fragment headers */
1108 for (c=0; c<DS_HEL_FRAGS; c++) {
1109 ds->pwave[c]->lpData = dsb->buffer + c*ds->fraglen;
1110 ds->pwave[c]->dwBufferLength = ds->fraglen;
1111 ds->pwave[c]->dwUser = (DWORD)dsb;
1112 ds->pwave[c]->dwFlags = 0;
1113 ds->pwave[c]->dwLoops = 0;
1114 err = mmErr(waveOutPrepareHeader(ds->hwo,ds->pwave[c],sizeof(WAVEHDR)));
1115 if (err != DS_OK) {
1116 while (c--)
1117 waveOutUnprepareHeader(ds->hwo,ds->pwave[c],sizeof(WAVEHDR));
1118 break;
1122 ds->pwplay = 0;
1123 ds->pwwrite = 0;
1124 ds->pwqueue = 0;
1125 memset(dsb->buffer, (dsb->wfx.wBitsPerSample == 16) ? 0 : 128, dsb->buflen);
1126 TRACE("fraglen=%ld\n", ds->fraglen);
1127 DSOUND_WaveQueue(dsb->dsound, (DWORD)-1);
1129 if ((err == DS_OK) && (merr != DS_OK))
1130 err = merr;
1132 return err;
1136 static void DSOUND_PrimaryClose(IDirectSoundBufferImpl *dsb)
1138 /* are we using waveOut stuff? */
1139 if (!dsb->hwbuf) {
1140 unsigned c;
1141 IDirectSoundImpl *ds = dsb->dsound;
1143 ds->pwqueue = (DWORD)-1; /* resetting queues */
1144 waveOutReset(ds->hwo);
1145 for (c=0; c<DS_HEL_FRAGS; c++)
1146 waveOutUnprepareHeader(ds->hwo, ds->pwave[c], sizeof(WAVEHDR));
1147 ds->pwqueue = 0;
1151 static HRESULT DSOUND_PrimaryPlay(IDirectSoundBufferImpl *dsb)
1153 HRESULT err = DS_OK;
1154 if (dsb->hwbuf)
1155 err = IDsDriverBuffer_Play(dsb->hwbuf, 0, 0, DSBPLAY_LOOPING);
1156 else
1157 err = mmErr(waveOutRestart(dsb->dsound->hwo));
1158 return err;
1161 static HRESULT DSOUND_PrimaryStop(IDirectSoundBufferImpl *dsb)
1163 HRESULT err = DS_OK;
1164 if (dsb->hwbuf) {
1165 err = IDsDriverBuffer_Stop(dsb->hwbuf);
1166 if (err == DSERR_BUFFERLOST) {
1167 /* Wine-only: the driver wants us to reopen the device */
1168 /* FIXME: check for errors */
1169 IDsDriverBuffer_Release(primarybuf->hwbuf);
1170 waveOutClose(dsb->dsound->hwo);
1171 dsb->dsound->hwo = 0;
1172 waveOutOpen(&(dsb->dsound->hwo), dsb->dsound->drvdesc.dnDevNode,
1173 &(primarybuf->wfx), (DWORD)DSOUND_callback, (DWORD)dsb->dsound,
1174 CALLBACK_FUNCTION | WAVE_DIRECTSOUND);
1175 err = IDsDriver_CreateSoundBuffer(dsb->dsound->driver,&(dsb->wfx),dsb->dsbd.dwFlags,0,
1176 &(dsb->buflen),&(dsb->buffer),
1177 (LPVOID)&(dsb->hwbuf));
1180 else
1181 err = mmErr(waveOutPause(dsb->dsound->hwo));
1182 return err;
1185 /* This sets this format for the <em>Primary Buffer Only</em> */
1186 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
1187 static HRESULT WINAPI IDirectSoundBufferImpl_SetFormat(
1188 LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX wfex
1190 ICOM_THIS(IDirectSoundBufferImpl,iface);
1191 IDirectSoundBufferImpl** dsb;
1192 HRESULT err = DS_OK;
1193 int i;
1195 /* Let's be pedantic! */
1196 if ((wfex == NULL) ||
1197 (wfex->wFormatTag != WAVE_FORMAT_PCM) ||
1198 (wfex->nChannels < 1) || (wfex->nChannels > 2) ||
1199 (wfex->nSamplesPerSec < 1) ||
1200 (wfex->nBlockAlign < 1) || (wfex->nChannels > 4) ||
1201 ((wfex->wBitsPerSample != 8) && (wfex->wBitsPerSample != 16))) {
1202 TRACE("failed pedantic check!\n");
1203 return DSERR_INVALIDPARAM;
1206 /* **** */
1207 EnterCriticalSection(&(This->dsound->lock));
1209 if (primarybuf->wfx.nSamplesPerSec != wfex->nSamplesPerSec) {
1210 dsb = dsound->buffers;
1211 for (i = 0; i < dsound->nrofbuffers; i++, dsb++) {
1212 /* **** */
1213 EnterCriticalSection(&((*dsb)->lock));
1215 (*dsb)->freqAdjust = ((*dsb)->freq << DSOUND_FREQSHIFT) /
1216 wfex->nSamplesPerSec;
1218 LeaveCriticalSection(&((*dsb)->lock));
1219 /* **** */
1223 memcpy(&(primarybuf->wfx), wfex, sizeof(primarybuf->wfx));
1225 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
1226 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1227 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
1228 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
1229 wfex->wBitsPerSample, wfex->cbSize);
1231 primarybuf->wfx.nAvgBytesPerSec =
1232 This->wfx.nSamplesPerSec * This->wfx.nBlockAlign;
1234 if (primarybuf->dsound->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) {
1235 /* FIXME: check for errors */
1236 DSOUND_PrimaryClose(primarybuf);
1237 waveOutClose(This->dsound->hwo);
1238 This->dsound->hwo = 0;
1239 waveOutOpen(&(This->dsound->hwo), This->dsound->drvdesc.dnDevNode,
1240 &(primarybuf->wfx), (DWORD)DSOUND_callback, (DWORD)This->dsound,
1241 CALLBACK_FUNCTION | WAVE_DIRECTSOUND);
1242 DSOUND_PrimaryOpen(primarybuf);
1244 if (primarybuf->hwbuf) {
1245 err = IDsDriverBuffer_SetFormat(primarybuf->hwbuf, &(primarybuf->wfx));
1246 if (err == DSERR_BUFFERLOST) {
1247 /* Wine-only: the driver wants us to recreate the HW buffer */
1248 IDsDriverBuffer_Release(primarybuf->hwbuf);
1249 err = IDsDriver_CreateSoundBuffer(primarybuf->dsound->driver,&(primarybuf->wfx),primarybuf->dsbd.dwFlags,0,
1250 &(primarybuf->buflen),&(primarybuf->buffer),
1251 (LPVOID)&(primarybuf->hwbuf));
1252 if (primarybuf->state == STATE_PLAYING) primarybuf->state = STATE_STARTING;
1253 else if (primarybuf->state == STATE_STOPPING) primarybuf->state = STATE_STOPPED;
1256 DSOUND_RecalcFormat(primarybuf);
1258 LeaveCriticalSection(&(This->dsound->lock));
1259 /* **** */
1261 return DS_OK;
1264 static HRESULT WINAPI IDirectSoundBufferImpl_SetVolume(
1265 LPDIRECTSOUNDBUFFER iface,LONG vol
1267 ICOM_THIS(IDirectSoundBufferImpl,iface);
1269 TRACE("(%p,%ld)\n",This,vol);
1271 /* I'm not sure if we need this for primary buffer */
1272 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
1273 return DSERR_CONTROLUNAVAIL;
1275 if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN))
1276 return DSERR_INVALIDPARAM;
1278 /* **** */
1279 EnterCriticalSection(&(This->lock));
1281 This->volpan.lVolume = vol;
1283 DSOUND_RecalcVolPan(&(This->volpan));
1285 if (This->hwbuf) {
1286 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
1288 else if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
1289 #if 0 /* should we really do this? */
1290 /* the DS volume ranges from 0 (max, 0dB attenuation) to -10000 (min, 100dB attenuation) */
1291 /* the MM volume ranges from 0 to 0xffff in an unspecified logarithmic scale */
1292 WORD cvol = 0xffff + vol*6 + vol/2;
1293 DWORD vol = cvol | ((DWORD)cvol << 16)
1294 waveOutSetVolume(This->dsound->hwo, vol);
1295 #endif
1298 LeaveCriticalSection(&(This->lock));
1299 /* **** */
1301 return DS_OK;
1304 static HRESULT WINAPI IDirectSoundBufferImpl_GetVolume(
1305 LPDIRECTSOUNDBUFFER iface,LPLONG vol
1307 ICOM_THIS(IDirectSoundBufferImpl,iface);
1308 TRACE("(%p,%p)\n",This,vol);
1310 if (vol == NULL)
1311 return DSERR_INVALIDPARAM;
1313 *vol = This->volpan.lVolume;
1314 return DS_OK;
1317 static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency(
1318 LPDIRECTSOUNDBUFFER iface,DWORD freq
1320 ICOM_THIS(IDirectSoundBufferImpl,iface);
1321 TRACE("(%p,%ld)\n",This,freq);
1323 /* You cannot set the frequency of the primary buffer */
1324 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY) ||
1325 (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
1326 return DSERR_CONTROLUNAVAIL;
1328 if (!freq) freq = This->wfx.nSamplesPerSec;
1330 if ((freq < DSBFREQUENCY_MIN) || (freq > DSBFREQUENCY_MAX))
1331 return DSERR_INVALIDPARAM;
1333 /* **** */
1334 EnterCriticalSection(&(This->lock));
1336 This->freq = freq;
1337 This->freqAdjust = (freq << DSOUND_FREQSHIFT) / primarybuf->wfx.nSamplesPerSec;
1338 This->nAvgBytesPerSec = freq * This->wfx.nBlockAlign;
1339 DSOUND_RecalcFormat(This);
1341 LeaveCriticalSection(&(This->lock));
1342 /* **** */
1344 return DS_OK;
1347 static HRESULT WINAPI IDirectSoundBufferImpl_Play(
1348 LPDIRECTSOUNDBUFFER iface,DWORD reserved1,DWORD reserved2,DWORD flags
1350 ICOM_THIS(IDirectSoundBufferImpl,iface);
1351 TRACE("(%p,%08lx,%08lx,%08lx)\n",
1352 This,reserved1,reserved2,flags
1355 /* **** */
1356 EnterCriticalSection(&(This->lock));
1358 This->playflags = flags;
1359 if (This->state == STATE_STOPPED) {
1360 This->leadin = TRUE;
1361 This->startpos = This->buf_mixpos;
1362 This->state = STATE_STARTING;
1363 } else if (This->state == STATE_STOPPING)
1364 This->state = STATE_PLAYING;
1365 if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
1366 IDsDriverBuffer_Play(This->hwbuf, 0, 0, This->playflags);
1367 This->state = STATE_PLAYING;
1370 LeaveCriticalSection(&(This->lock));
1371 /* **** */
1373 return DS_OK;
1376 static HRESULT WINAPI IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface)
1378 ICOM_THIS(IDirectSoundBufferImpl,iface);
1379 TRACE("(%p)\n",This);
1381 /* **** */
1382 EnterCriticalSection(&(This->lock));
1384 if (This->state == STATE_PLAYING)
1385 This->state = STATE_STOPPING;
1386 else if (This->state == STATE_STARTING)
1387 This->state = STATE_STOPPED;
1388 if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
1389 IDsDriverBuffer_Stop(This->hwbuf);
1390 This->state = STATE_STOPPED;
1392 DSOUND_CheckEvent(This, 0);
1394 LeaveCriticalSection(&(This->lock));
1395 /* **** */
1397 return DS_OK;
1400 static DWORD WINAPI IDirectSoundBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface) {
1401 ICOM_THIS(IDirectSoundBufferImpl,iface);
1402 DWORD ref;
1404 TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
1406 ref = InterlockedIncrement(&(This->ref));
1407 if (!ref) {
1408 FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n");
1410 return ref;
1412 static DWORD WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER iface) {
1413 ICOM_THIS(IDirectSoundBufferImpl,iface);
1414 int i;
1415 DWORD ref;
1417 TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
1419 ref = InterlockedDecrement(&(This->ref));
1420 if (ref) return ref;
1422 EnterCriticalSection(&(This->dsound->lock));
1423 for (i=0;i<This->dsound->nrofbuffers;i++)
1424 if (This->dsound->buffers[i] == This)
1425 break;
1427 if (i < This->dsound->nrofbuffers) {
1428 /* Put the last buffer of the list in the (now empty) position */
1429 This->dsound->buffers[i] = This->dsound->buffers[This->dsound->nrofbuffers - 1];
1430 This->dsound->nrofbuffers--;
1431 This->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,This->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*This->dsound->nrofbuffers);
1432 TRACE("buffer count is now %d\n", This->dsound->nrofbuffers);
1433 IDirectSound_Release((LPDIRECTSOUND)This->dsound);
1435 LeaveCriticalSection(&(This->dsound->lock));
1437 DeleteCriticalSection(&(This->lock));
1438 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1439 DSOUND_PrimaryClose(This);
1440 if (This->hwbuf) {
1441 IDsDriverBuffer_Release(This->hwbuf);
1443 if (This->ds3db)
1444 IDirectSound3DBuffer_Release((LPDIRECTSOUND3DBUFFER)This->ds3db);
1445 if (This->parent)
1446 /* this is a duplicate buffer */
1447 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->parent);
1448 else
1449 /* this is a toplevel buffer */
1450 HeapFree(GetProcessHeap(),0,This->buffer);
1452 HeapFree(GetProcessHeap(),0,This);
1454 if (This == primarybuf)
1455 primarybuf = NULL;
1457 return 0;
1460 static DWORD DSOUND_CalcPlayPosition(IDirectSoundBufferImpl *This,
1461 DWORD state, DWORD pplay, DWORD pwrite, DWORD pmix, DWORD bmix)
1463 DWORD bplay;
1465 TRACE("primary playpos=%ld, mixpos=%ld\n", pplay, pmix);
1466 TRACE("this mixpos=%ld, time=%ld\n", bmix, GetTickCount());
1468 /* the actual primary play position (pplay) is always behind last mixed (pmix),
1469 * unless the computer is too slow or something */
1470 /* we need to know how far away we are from there */
1471 #if 0 /* we'll never fill the primary entirely */
1472 if (pmix == pplay) {
1473 if ((state == STATE_PLAYING) || (state == STATE_STOPPING)) {
1474 /* wow, the software mixer is really doing well,
1475 * seems the entire primary buffer is filled! */
1476 pmix += primarybuf->buflen;
1478 /* else: the primary buffer is not playing, so probably empty */
1480 #endif
1481 if (pmix < pplay) pmix += primarybuf->buflen; /* wraparound */
1482 pmix -= pplay;
1483 /* detect buffer underrun */
1484 if (pwrite < pplay) pwrite += primarybuf->buflen; /* wraparound */
1485 pwrite -= pplay;
1486 if (pmix > (DS_SND_QUEUE_MAX * primarybuf->dsound->fraglen + pwrite + primarybuf->writelead)) {
1487 WARN("detected an underrun: primary queue was %ld\n",pmix);
1488 pmix = 0;
1490 /* divide the offset by its sample size */
1491 pmix /= primarybuf->wfx.nBlockAlign;
1492 TRACE("primary back-samples=%ld\n",pmix);
1493 /* adjust for our frequency */
1494 pmix = (pmix * This->freqAdjust) >> DSOUND_FREQSHIFT;
1495 /* multiply by our own sample size */
1496 pmix *= This->wfx.nBlockAlign;
1497 TRACE("this back-offset=%ld\n", pmix);
1498 /* subtract from our last mixed position */
1499 bplay = bmix;
1500 while (bplay < pmix) bplay += This->buflen; /* wraparound */
1501 bplay -= pmix;
1502 if (This->leadin && ((bplay < This->startpos) || (bplay > bmix))) {
1503 /* seems we haven't started playing yet */
1504 TRACE("this still in lead-in phase\n");
1505 bplay = This->startpos;
1507 /* return the result */
1508 return bplay;
1511 static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
1512 LPDIRECTSOUNDBUFFER iface,LPDWORD playpos,LPDWORD writepos
1514 HRESULT hres;
1515 ICOM_THIS(IDirectSoundBufferImpl,iface);
1516 TRACE("(%p,%p,%p)\n",This,playpos,writepos);
1517 if (This->hwbuf) {
1518 hres=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);
1519 if (hres)
1520 return hres;
1522 else if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
1523 if (playpos) {
1524 MMTIME mtime;
1525 mtime.wType = TIME_BYTES;
1526 waveOutGetPosition(This->dsound->hwo, &mtime, sizeof(mtime));
1527 mtime.u.cb = mtime.u.cb % This->buflen;
1528 *playpos = mtime.u.cb;
1530 if (writepos) {
1531 /* the writepos should only be used by apps with WRITEPRIMARY priority,
1532 * in which case our software mixer is disabled anyway */
1533 *writepos = (This->dsound->pwplay + DS_HEL_MARGIN) * This->dsound->fraglen;
1534 while (*writepos >= This->buflen)
1535 *writepos -= This->buflen;
1537 } else {
1538 if (playpos && (This->state != STATE_PLAYING)) {
1539 /* we haven't been merged into the primary buffer (yet) */
1540 *playpos = This->buf_mixpos;
1542 else if (playpos) {
1543 DWORD pplay, pwrite, lplay, splay, pstate;
1544 /* let's get this exact; first, recursively call GetPosition on the primary */
1545 EnterCriticalSection(&(primarybuf->lock));
1546 IDirectSoundBufferImpl_GetCurrentPosition((LPDIRECTSOUNDBUFFER)primarybuf, &pplay, &pwrite);
1547 /* detect HEL mode underrun */
1548 pstate = primarybuf->state;
1549 if (!(primarybuf->hwbuf || primarybuf->dsound->pwqueue)) {
1550 TRACE("detected an underrun\n");
1551 /* pplay = ? */
1552 if (pstate == STATE_PLAYING)
1553 pstate = STATE_STARTING;
1554 else if (pstate == STATE_STOPPING)
1555 pstate = STATE_STOPPED;
1557 /* get data for ourselves while we still have the lock */
1558 pstate &= This->state;
1559 lplay = This->primary_mixpos;
1560 splay = This->buf_mixpos;
1561 if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || primarybuf->hwbuf) {
1562 /* calculate play position using this */
1563 *playpos = DSOUND_CalcPlayPosition(This, pstate, pplay, pwrite, lplay, splay);
1564 } else {
1565 /* (unless the app isn't using GETCURRENTPOSITION2) */
1566 /* don't know exactly how this should be handled...
1567 * the docs says that play cursor is reported as directly
1568 * behind write cursor, hmm... */
1569 /* let's just do what might work for Half-Life */
1570 DWORD wp;
1571 wp = (This->dsound->pwplay + DS_HEL_MARGIN) * This->dsound->fraglen;
1572 while (wp >= primarybuf->buflen)
1573 wp -= primarybuf->buflen;
1574 *playpos = DSOUND_CalcPlayPosition(This, pstate, wp, pwrite, lplay, splay);
1576 LeaveCriticalSection(&(primarybuf->lock));
1578 if (writepos) *writepos = This->buf_mixpos;
1580 if (writepos) {
1581 if (This->state != STATE_STOPPED)
1582 /* apply the documented 10ms lead to writepos */
1583 *writepos += This->writelead;
1584 while (*writepos >= This->buflen) *writepos -= This->buflen;
1586 TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
1587 return DS_OK;
1590 static HRESULT WINAPI IDirectSoundBufferImpl_GetStatus(
1591 LPDIRECTSOUNDBUFFER iface,LPDWORD status
1593 ICOM_THIS(IDirectSoundBufferImpl,iface);
1594 TRACE("(%p,%p), thread is %lx\n",This,status,GetCurrentThreadId());
1596 if (status == NULL)
1597 return DSERR_INVALIDPARAM;
1599 *status = 0;
1600 if ((This->state == STATE_STARTING) || (This->state == STATE_PLAYING)) {
1601 *status |= DSBSTATUS_PLAYING;
1602 if (This->playflags & DSBPLAY_LOOPING)
1603 *status |= DSBSTATUS_LOOPING;
1606 TRACE("status=%lx\n", *status);
1607 return DS_OK;
1611 static HRESULT WINAPI IDirectSoundBufferImpl_GetFormat(
1612 LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
1614 ICOM_THIS(IDirectSoundBufferImpl,iface);
1615 TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
1617 if (wfsize>sizeof(This->wfx))
1618 wfsize = sizeof(This->wfx);
1619 if (lpwf) { /* NULL is valid */
1620 memcpy(lpwf,&(This->wfx),wfsize);
1621 if (wfwritten)
1622 *wfwritten = wfsize;
1623 } else
1624 if (wfwritten)
1625 *wfwritten = sizeof(This->wfx);
1626 else
1627 return DSERR_INVALIDPARAM;
1629 return DS_OK;
1632 static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
1633 LPDIRECTSOUNDBUFFER iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
1635 ICOM_THIS(IDirectSoundBufferImpl,iface);
1636 DWORD capf;
1638 TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx) at %ld\n",
1639 This,
1640 writecursor,
1641 writebytes,
1642 lplpaudioptr1,
1643 audiobytes1,
1644 lplpaudioptr2,
1645 audiobytes2,
1646 flags,
1647 GetTickCount()
1650 if (flags & DSBLOCK_FROMWRITECURSOR) {
1651 DWORD writepos;
1652 /* GetCurrentPosition does too much magic to duplicate here */
1653 IDirectSoundBufferImpl_GetCurrentPosition(iface, NULL, &writepos);
1654 writecursor += writepos;
1656 if (flags & DSBLOCK_ENTIREBUFFER)
1657 writebytes = This->buflen;
1658 if (writebytes > This->buflen)
1659 writebytes = This->buflen;
1661 assert(audiobytes1!=audiobytes2);
1662 assert(lplpaudioptr1!=lplpaudioptr2);
1664 if ((writebytes == This->buflen) &&
1665 ((This->state == STATE_STARTING) ||
1666 (This->state == STATE_PLAYING)))
1667 /* some games, like Half-Life, try to be clever (not) and
1668 * keep one secondary buffer, and mix sounds into it itself,
1669 * locking the entire buffer every time... so we can just forget
1670 * about tracking the last-written-to-position... */
1671 This->probably_valid_to = (DWORD)-1;
1672 else
1673 This->probably_valid_to = writecursor;
1675 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1676 capf = DSDDESC_DONTNEEDPRIMARYLOCK;
1677 else
1678 capf = DSDDESC_DONTNEEDSECONDARYLOCK;
1679 if (!(This->dsound->drvdesc.dwFlags & capf) && This->hwbuf) {
1680 IDsDriverBuffer_Lock(This->hwbuf,
1681 lplpaudioptr1, audiobytes1,
1682 lplpaudioptr2, audiobytes2,
1683 writecursor, writebytes,
1686 else {
1687 BOOL remix = FALSE;
1688 if (writecursor+writebytes <= This->buflen) {
1689 *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
1690 *audiobytes1 = writebytes;
1691 if (lplpaudioptr2)
1692 *(LPBYTE*)lplpaudioptr2 = NULL;
1693 if (audiobytes2)
1694 *audiobytes2 = 0;
1695 TRACE("->%ld.0\n",writebytes);
1696 } else {
1697 *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
1698 *audiobytes1 = This->buflen-writecursor;
1699 if (lplpaudioptr2)
1700 *(LPBYTE*)lplpaudioptr2 = This->buffer;
1701 if (audiobytes2)
1702 *audiobytes2 = writebytes-(This->buflen-writecursor);
1703 TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
1705 /* if the segment between playpos and buf_mixpos is touched,
1706 * we need to cancel some mixing */
1707 if (This->buf_mixpos >= This->playpos) {
1708 if (This->buf_mixpos > writecursor &&
1709 This->playpos <= writecursor+writebytes)
1710 remix = TRUE;
1712 else {
1713 if (This->buf_mixpos > writecursor ||
1714 This->playpos <= writecursor+writebytes)
1715 remix = TRUE;
1717 if (remix) {
1718 TRACE("locking prebuffered region, ouch\n");
1719 DSOUND_MixCancelAt(This, writecursor);
1722 return DS_OK;
1725 static HRESULT WINAPI IDirectSoundBufferImpl_SetCurrentPosition(
1726 LPDIRECTSOUNDBUFFER iface,DWORD newpos
1728 ICOM_THIS(IDirectSoundBufferImpl,iface);
1729 TRACE("(%p,%ld)\n",This,newpos);
1731 /* **** */
1732 EnterCriticalSection(&(This->lock));
1734 This->buf_mixpos = newpos;
1735 if (This->hwbuf)
1736 IDsDriverBuffer_SetPosition(This->hwbuf, This->buf_mixpos);
1738 LeaveCriticalSection(&(This->lock));
1739 /* **** */
1741 return DS_OK;
1744 static HRESULT WINAPI IDirectSoundBufferImpl_SetPan(
1745 LPDIRECTSOUNDBUFFER iface,LONG pan
1747 ICOM_THIS(IDirectSoundBufferImpl,iface);
1749 TRACE("(%p,%ld)\n",This,pan);
1751 if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT))
1752 return DSERR_INVALIDPARAM;
1754 /* You cannot set the pan of the primary buffer */
1755 /* and you cannot use both pan and 3D controls */
1756 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
1757 (This->dsbd.dwFlags & DSBCAPS_CTRL3D) ||
1758 (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
1759 return DSERR_CONTROLUNAVAIL;
1761 /* **** */
1762 EnterCriticalSection(&(This->lock));
1764 This->volpan.lPan = pan;
1766 DSOUND_RecalcVolPan(&(This->volpan));
1768 if (This->hwbuf) {
1769 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
1772 LeaveCriticalSection(&(This->lock));
1773 /* **** */
1775 return DS_OK;
1778 static HRESULT WINAPI IDirectSoundBufferImpl_GetPan(
1779 LPDIRECTSOUNDBUFFER iface,LPLONG pan
1781 ICOM_THIS(IDirectSoundBufferImpl,iface);
1782 TRACE("(%p,%p)\n",This,pan);
1784 if (pan == NULL)
1785 return DSERR_INVALIDPARAM;
1787 *pan = This->volpan.lPan;
1789 return DS_OK;
1792 static HRESULT WINAPI IDirectSoundBufferImpl_Unlock(
1793 LPDIRECTSOUNDBUFFER iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
1795 ICOM_THIS(IDirectSoundBufferImpl,iface);
1796 DWORD capf, probably_valid_to;
1798 TRACE("(%p,%p,%ld,%p,%ld):stub\n", This,p1,x1,p2,x2);
1800 #if 0
1801 /* Preprocess 3D buffers... */
1803 /* This is highly experimental and liable to break things */
1804 if (This->dsbd.dwFlags & DSBCAPS_CTRL3D)
1805 DSOUND_Create3DBuffer(This);
1806 #endif
1808 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1809 capf = DSDDESC_DONTNEEDPRIMARYLOCK;
1810 else
1811 capf = DSDDESC_DONTNEEDSECONDARYLOCK;
1812 if (!(This->dsound->drvdesc.dwFlags & capf) && This->hwbuf) {
1813 IDsDriverBuffer_Unlock(This->hwbuf, p1, x1, p2, x2);
1816 if (p2) probably_valid_to = (((LPBYTE)p2)-This->buffer) + x2;
1817 else probably_valid_to = (((LPBYTE)p1)-This->buffer) + x1;
1818 while (probably_valid_to >= This->buflen)
1819 probably_valid_to -= This->buflen;
1820 if ((probably_valid_to == 0) && ((x1+x2) == This->buflen) &&
1821 ((This->state == STATE_STARTING) ||
1822 (This->state == STATE_PLAYING)))
1823 /* see IDirectSoundBufferImpl_Lock */
1824 probably_valid_to = (DWORD)-1;
1825 This->probably_valid_to = probably_valid_to;
1827 return DS_OK;
1830 static HRESULT WINAPI IDirectSoundBufferImpl_Restore(
1831 LPDIRECTSOUNDBUFFER iface
1833 ICOM_THIS(IDirectSoundBufferImpl,iface);
1834 FIXME("(%p):stub\n",This);
1835 return DS_OK;
1838 static HRESULT WINAPI IDirectSoundBufferImpl_GetFrequency(
1839 LPDIRECTSOUNDBUFFER iface,LPDWORD freq
1841 ICOM_THIS(IDirectSoundBufferImpl,iface);
1842 TRACE("(%p,%p)\n",This,freq);
1844 if (freq == NULL)
1845 return DSERR_INVALIDPARAM;
1847 *freq = This->freq;
1848 TRACE("-> %ld\n", *freq);
1850 return DS_OK;
1853 static HRESULT WINAPI IDirectSoundBufferImpl_Initialize(
1854 LPDIRECTSOUNDBUFFER iface,LPDIRECTSOUND dsound,LPDSBUFFERDESC dbsd
1856 ICOM_THIS(IDirectSoundBufferImpl,iface);
1857 FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
1858 DPRINTF("Re-Init!!!\n");
1859 return DSERR_ALREADYINITIALIZED;
1862 static HRESULT WINAPI IDirectSoundBufferImpl_GetCaps(
1863 LPDIRECTSOUNDBUFFER iface,LPDSBCAPS caps
1865 ICOM_THIS(IDirectSoundBufferImpl,iface);
1866 TRACE("(%p)->(%p)\n",This,caps);
1868 if (caps == NULL)
1869 return DSERR_INVALIDPARAM;
1871 /* I think we should check this value, not set it. See */
1872 /* Inside DirectX, p215. That should apply here, too. */
1873 caps->dwSize = sizeof(*caps);
1875 caps->dwFlags = This->dsbd.dwFlags;
1876 if (This->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
1877 else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
1879 caps->dwBufferBytes = This->buflen;
1881 /* This value represents the speed of the "unlock" command.
1882 As unlock is quite fast (it does not do anything), I put
1883 4096 ko/s = 4 Mo / s */
1884 /* FIXME: hwbuf speed */
1885 caps->dwUnlockTransferRate = 4096;
1886 caps->dwPlayCpuOverhead = 0;
1888 return DS_OK;
1891 static HRESULT WINAPI IDirectSoundBufferImpl_QueryInterface(
1892 LPDIRECTSOUNDBUFFER iface,REFIID riid,LPVOID *ppobj
1894 ICOM_THIS(IDirectSoundBufferImpl,iface);
1896 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
1898 if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
1899 IDirectSoundNotifyImpl *dsn;
1901 dsn = (IDirectSoundNotifyImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn));
1902 dsn->ref = 1;
1903 dsn->dsb = This;
1904 IDirectSoundBuffer_AddRef(iface);
1905 ICOM_VTBL(dsn) = &dsnvt;
1906 *ppobj = (LPVOID)dsn;
1907 return S_OK;
1910 #ifdef USE_DSOUND3D
1911 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1912 IDirectSound3DBufferImpl *ds3db;
1914 *ppobj = This->ds3db;
1915 if (*ppobj) {
1916 IDirectSound3DBuffer_AddRef((LPDIRECTSOUND3DBUFFER)This->ds3db);
1917 return S_OK;
1920 ds3db = (IDirectSound3DBufferImpl*)HeapAlloc(GetProcessHeap(),
1921 0,sizeof(*ds3db));
1922 ds3db->ref = 1;
1923 ds3db->dsb = This;
1924 ICOM_VTBL(ds3db) = &ds3dbvt;
1925 InitializeCriticalSection(&ds3db->lock);
1927 IDirectSoundBuffer_AddRef(iface);
1929 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
1930 ds3db->ds3db.vPosition.u1.x = 0.0;
1931 ds3db->ds3db.vPosition.u2.y = 0.0;
1932 ds3db->ds3db.vPosition.u3.z = 0.0;
1933 ds3db->ds3db.vVelocity.u1.x = 0.0;
1934 ds3db->ds3db.vVelocity.u2.y = 0.0;
1935 ds3db->ds3db.vVelocity.u3.z = 0.0;
1936 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
1937 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
1938 ds3db->ds3db.vConeOrientation.u1.x = 0.0;
1939 ds3db->ds3db.vConeOrientation.u2.y = 0.0;
1940 ds3db->ds3db.vConeOrientation.u3.z = 0.0;
1941 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME; ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
1942 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
1943 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
1944 ds3db->buflen = (This->buflen * primarybuf->wfx.nBlockAlign) /
1945 This->wfx.nBlockAlign;
1946 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
1947 if (ds3db->buffer == NULL) {
1948 ds3db->buflen = 0;
1949 ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
1952 ds3db->iks = (IKsPropertySetImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db->iks)));
1953 ds3db->iks->ref = 1;
1954 ds3db->iks->ds3db = ds3db;
1955 ICOM_VTBL(ds3db->iks) = &iksvt;
1957 return S_OK;
1959 #else
1960 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1961 FIXME("%s: I know about this GUID, but don't support it yet\n",
1962 debugstr_guid( riid ));
1963 *ppobj = NULL;
1964 return E_FAIL;
1966 #endif
1968 #if USE_DSOUND3D
1969 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1970 IDirectSound3DListenerImpl* dsl;
1972 if (This->dsound->listener) {
1973 *ppobj = This->dsound->listener;
1974 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)This->dsound->listener);
1975 return DS_OK;
1978 dsl = (IDirectSound3DListenerImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsl));
1979 dsl->ref = 1;
1980 ICOM_VTBL(dsl) = &ds3dlvt;
1981 *ppobj = (LPVOID)dsl;
1983 dsl->ds3dl.dwSize = sizeof(DS3DLISTENER);
1984 dsl->ds3dl.vPosition.u1.x = 0.0;
1985 dsl->ds3dl.vPosition.u2.y = 0.0;
1986 dsl->ds3dl.vPosition.u3.z = 0.0;
1987 dsl->ds3dl.vVelocity.u1.x = 0.0;
1988 dsl->ds3dl.vVelocity.u2.y = 0.0;
1989 dsl->ds3dl.vVelocity.u3.z = 0.0;
1990 dsl->ds3dl.vOrientFront.u1.x = 0.0;
1991 dsl->ds3dl.vOrientFront.u2.y = 0.0;
1992 dsl->ds3dl.vOrientFront.u3.z = 1.0;
1993 dsl->ds3dl.vOrientTop.u1.x = 0.0;
1994 dsl->ds3dl.vOrientTop.u2.y = 1.0;
1995 dsl->ds3dl.vOrientTop.u3.z = 0.0;
1996 dsl->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
1997 dsl->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
1999 InitializeCriticalSection(&dsl->lock);
2001 dsl->dsb = This;
2002 IDirectSoundBuffer_AddRef(iface);
2004 This->dsound->listener = dsl;
2005 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)dsl);
2007 return S_OK;
2009 #else
2010 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
2011 FIXME("%s: I know about this GUID, but don't support it yet\n",
2012 debugstr_guid( riid ));
2013 *ppobj = NULL;
2014 return E_FAIL;
2016 #endif
2018 FIXME( "Unknown GUID %s\n", debugstr_guid( riid ) );
2020 *ppobj = NULL;
2022 return E_FAIL;
2025 static ICOM_VTABLE(IDirectSoundBuffer) dsbvt =
2027 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2028 IDirectSoundBufferImpl_QueryInterface,
2029 IDirectSoundBufferImpl_AddRef,
2030 IDirectSoundBufferImpl_Release,
2031 IDirectSoundBufferImpl_GetCaps,
2032 IDirectSoundBufferImpl_GetCurrentPosition,
2033 IDirectSoundBufferImpl_GetFormat,
2034 IDirectSoundBufferImpl_GetVolume,
2035 IDirectSoundBufferImpl_GetPan,
2036 IDirectSoundBufferImpl_GetFrequency,
2037 IDirectSoundBufferImpl_GetStatus,
2038 IDirectSoundBufferImpl_Initialize,
2039 IDirectSoundBufferImpl_Lock,
2040 IDirectSoundBufferImpl_Play,
2041 IDirectSoundBufferImpl_SetCurrentPosition,
2042 IDirectSoundBufferImpl_SetFormat,
2043 IDirectSoundBufferImpl_SetVolume,
2044 IDirectSoundBufferImpl_SetPan,
2045 IDirectSoundBufferImpl_SetFrequency,
2046 IDirectSoundBufferImpl_Stop,
2047 IDirectSoundBufferImpl_Unlock,
2048 IDirectSoundBufferImpl_Restore
2051 /*******************************************************************************
2052 * IDirectSound
2055 static HRESULT WINAPI IDirectSoundImpl_SetCooperativeLevel(
2056 LPDIRECTSOUND iface,HWND hwnd,DWORD level
2058 ICOM_THIS(IDirectSoundImpl,iface);
2060 FIXME("(%p,%08lx,%ld):stub\n",This,(DWORD)hwnd,level);
2062 This->priolevel = level;
2064 return DS_OK;
2067 static HRESULT WINAPI IDirectSoundImpl_CreateSoundBuffer(
2068 LPDIRECTSOUND iface,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER ppdsb,LPUNKNOWN lpunk
2070 ICOM_THIS(IDirectSoundImpl,iface);
2071 IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
2072 LPWAVEFORMATEX wfex;
2073 HRESULT err = DS_OK;
2075 TRACE("(%p,%p,%p,%p)\n",This,dsbd,ippdsb,lpunk);
2077 if ((This == NULL) || (dsbd == NULL) || (ippdsb == NULL))
2078 return DSERR_INVALIDPARAM;
2080 if (TRACE_ON(dsound)) {
2081 TRACE("(structsize=%ld)\n",dsbd->dwSize);
2082 TRACE("(flags=0x%08lx:\n",dsbd->dwFlags);
2083 _dump_DSBCAPS(dsbd->dwFlags);
2084 DPRINTF(")\n");
2085 TRACE("(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
2086 TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
2089 wfex = dsbd->lpwfxFormat;
2091 if (wfex)
2092 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
2093 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
2094 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
2095 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
2096 wfex->wBitsPerSample, wfex->cbSize);
2098 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
2099 if (primarybuf) {
2100 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)primarybuf);
2101 *ippdsb = primarybuf;
2102 primarybuf->dsbd.dwFlags = dsbd->dwFlags;
2103 return DS_OK;
2104 } /* Else create primary buffer */
2105 } else {
2106 if (dsbd->dwBufferBytes < DSBSIZE_MIN || dsbd->dwBufferBytes > DSBSIZE_MAX) {
2107 ERR("invalid sound buffer size %ld\n", dsbd->dwBufferBytes);
2108 return DSERR_INVALIDPARAM; /* FIXME: which error? */
2112 *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
2113 if (*ippdsb == NULL)
2114 return DSERR_OUTOFMEMORY;
2115 ICOM_VTBL(*ippdsb) = &dsbvt;
2116 (*ippdsb)->ref = 1;
2117 (*ippdsb)->dsound = This;
2118 (*ippdsb)->parent = NULL;
2119 (*ippdsb)->buffer = NULL;
2121 memcpy(&((*ippdsb)->dsbd),dsbd,sizeof(*dsbd));
2122 if (dsbd->lpwfxFormat)
2123 memcpy(&((*ippdsb)->wfx), dsbd->lpwfxFormat, sizeof((*ippdsb)->wfx));
2125 TRACE("Created buffer at %p\n", *ippdsb);
2127 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
2128 (*ippdsb)->buflen = dsound->wfx.nAvgBytesPerSec;
2129 (*ippdsb)->freq = dsound->wfx.nSamplesPerSec;
2131 /* FIXME: verify that hardware capabilities (DSCAPS_PRIMARY flags) match */
2133 if (This->driver) {
2134 err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
2135 &((*ippdsb)->buflen),&((*ippdsb)->buffer),
2136 (LPVOID*)&((*ippdsb)->hwbuf));
2138 if (err == DS_OK)
2139 err = DSOUND_PrimaryOpen(*ippdsb);
2140 } else {
2141 DWORD capf = 0;
2142 int use_hw;
2144 (*ippdsb)->buflen = dsbd->dwBufferBytes;
2145 (*ippdsb)->freq = dsbd->lpwfxFormat->nSamplesPerSec;
2147 /* Check necessary hardware mixing capabilities */
2148 if (wfex->nChannels==2) capf |= DSCAPS_SECONDARYSTEREO;
2149 else capf |= DSCAPS_SECONDARYMONO;
2150 if (wfex->wBitsPerSample==16) capf |= DSCAPS_SECONDARY16BIT;
2151 else capf |= DSCAPS_SECONDARY8BIT;
2152 use_hw = (This->drvcaps.dwFlags & capf) == capf;
2154 /* FIXME: check hardware sample rate mixing capabilities */
2155 /* FIXME: check app hints for software/hardware buffer (STATIC, LOCHARDWARE, etc) */
2156 /* FIXME: check whether any hardware buffers are left */
2157 /* FIXME: handle DSDHEAP_CREATEHEAP for hardware buffers */
2159 /* Allocate system memory if applicable */
2160 if ((This->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) || !use_hw) {
2161 (*ippdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,(*ippdsb)->buflen);
2162 if ((*ippdsb)->buffer == NULL)
2163 err = DSERR_OUTOFMEMORY;
2166 /* Allocate the hardware buffer */
2167 if (use_hw && (err == DS_OK)) {
2168 err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
2169 &((*ippdsb)->buflen),&((*ippdsb)->buffer),
2170 (LPVOID*)&((*ippdsb)->hwbuf));
2174 if (err != DS_OK) {
2175 if ((*ippdsb)->buffer)
2176 HeapFree(GetProcessHeap(),0,(*ippdsb)->buffer);
2177 HeapFree(GetProcessHeap(),0,(*ippdsb));
2178 *ippdsb = NULL;
2179 return err;
2181 /* calculate fragment size and write lead */
2182 DSOUND_RecalcFormat(*ippdsb);
2184 /* It's not necessary to initialize values to zero since */
2185 /* we allocated this structure with HEAP_ZERO_MEMORY... */
2186 (*ippdsb)->playpos = 0;
2187 (*ippdsb)->buf_mixpos = 0;
2188 (*ippdsb)->state = STATE_STOPPED;
2189 DSOUND_RecalcVolPan(&((*ippdsb)->volpan));
2191 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
2192 (*ippdsb)->freqAdjust = ((*ippdsb)->freq << DSOUND_FREQSHIFT) /
2193 primarybuf->wfx.nSamplesPerSec;
2194 (*ippdsb)->nAvgBytesPerSec = (*ippdsb)->freq *
2195 dsbd->lpwfxFormat->nBlockAlign;
2198 EnterCriticalSection(&(This->lock));
2199 /* register buffer */
2200 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
2201 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl*)*(This->nrofbuffers+1));
2202 if (newbuffers) {
2203 This->buffers = newbuffers;
2204 This->buffers[This->nrofbuffers] = *ippdsb;
2205 This->nrofbuffers++;
2206 TRACE("buffer count is now %d\n", This->nrofbuffers);
2207 } else {
2208 ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
2209 err = DSERR_OUTOFMEMORY;
2212 LeaveCriticalSection(&(This->lock));
2214 IDirectSound_AddRef(iface);
2216 InitializeCriticalSection(&((*ippdsb)->lock));
2218 if (err != DS_OK) {
2219 /* oops... */
2220 IDirectSoundBuffer_Release(*ppdsb);
2221 *ippdsb = NULL;
2222 return err;
2225 #ifdef USE_DSOUND3D
2226 if (dsbd->dwFlags & DSBCAPS_CTRL3D) {
2227 IDirectSound3DBufferImpl *ds3db;
2229 ds3db = (IDirectSound3DBufferImpl*)HeapAlloc(GetProcessHeap(),
2230 0,sizeof(*ds3db));
2231 ICOM_VTBL(ds3db) = &ds3dbvt;
2232 ds3db->ref = 1;
2233 (*ippdsb)->ds3db = ds3db;
2235 ds3db->dsb = (*ippdsb);
2236 IDirectSoundBufferImpl_AddRef((LPDIRECTSOUNDBUFFER)(*ippdsb));
2238 InitializeCriticalSection(&ds3db->lock);
2240 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
2241 ds3db->ds3db.vPosition.u1.x = 0.0;
2242 ds3db->ds3db.vPosition.u2.y = 0.0;
2243 ds3db->ds3db.vPosition.u3.z = 0.0;
2244 ds3db->ds3db.vVelocity.u1.x = 0.0;
2245 ds3db->ds3db.vVelocity.u2.y = 0.0;
2246 ds3db->ds3db.vVelocity.u3.z = 0.0;
2247 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
2248 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
2249 ds3db->ds3db.vConeOrientation.u1.x = 0.0;
2250 ds3db->ds3db.vConeOrientation.u2.y = 0.0;
2251 ds3db->ds3db.vConeOrientation.u3.z = 0.0;
2252 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME;
2253 ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
2254 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
2255 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
2256 ds3db->buflen = ((*ippdsb)->buflen * primarybuf->wfx.nBlockAlign) /
2257 (*ippdsb)->wfx.nBlockAlign;
2258 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
2259 if (ds3db->buffer == NULL) {
2260 ds3db->buflen = 0;
2261 ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
2263 ds3db->iks = (IKsPropertySetImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db->iks)));
2264 ds3db->iks->ref = 1;
2265 ds3db->iks->ds3db = ds3db;
2266 ICOM_VTBL(ds3db->iks) = &iksvt;
2269 #endif
2270 return DS_OK;
2273 static HRESULT WINAPI IDirectSoundImpl_DuplicateSoundBuffer(
2274 LPDIRECTSOUND iface,LPDIRECTSOUNDBUFFER pdsb,LPLPDIRECTSOUNDBUFFER ppdsb
2276 ICOM_THIS(IDirectSoundImpl,iface);
2277 IDirectSoundBufferImpl* ipdsb=(IDirectSoundBufferImpl*)pdsb;
2278 IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
2279 TRACE("(%p,%p,%p)\n",This,ipdsb,ippdsb);
2281 if (ipdsb->hwbuf) {
2282 FIXME("need to duplicate hardware buffer\n");
2285 *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
2287 IDirectSoundBuffer_AddRef(pdsb);
2288 memcpy(*ippdsb, ipdsb, sizeof(IDirectSoundBufferImpl));
2289 (*ippdsb)->ref = 1;
2290 (*ippdsb)->state = STATE_STOPPED;
2291 (*ippdsb)->playpos = 0;
2292 (*ippdsb)->buf_mixpos = 0;
2293 (*ippdsb)->dsound = This;
2294 (*ippdsb)->parent = ipdsb;
2295 memcpy(&((*ippdsb)->wfx), &(ipdsb->wfx), sizeof((*ippdsb)->wfx));
2296 InitializeCriticalSection(&(*ippdsb)->lock);
2297 /* register buffer */
2298 EnterCriticalSection(&(This->lock));
2300 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl**)*(This->nrofbuffers+1));
2301 if (newbuffers) {
2302 This->buffers = newbuffers;
2303 This->buffers[This->nrofbuffers] = *ippdsb;
2304 This->nrofbuffers++;
2305 TRACE("buffer count is now %d\n", This->nrofbuffers);
2306 } else {
2307 ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
2308 /* FIXME: release buffer */
2311 LeaveCriticalSection(&(This->lock));
2312 IDirectSound_AddRef(iface);
2313 return DS_OK;
2317 static HRESULT WINAPI IDirectSoundImpl_GetCaps(LPDIRECTSOUND iface,LPDSCAPS caps) {
2318 ICOM_THIS(IDirectSoundImpl,iface);
2319 TRACE("(%p,%p)\n",This,caps);
2320 TRACE("(flags=0x%08lx)\n",caps->dwFlags);
2322 if (caps == NULL)
2323 return DSERR_INVALIDPARAM;
2325 /* We should check this value, not set it. See Inside DirectX, p215. */
2326 caps->dwSize = sizeof(*caps);
2328 caps->dwFlags = This->drvcaps.dwFlags;
2330 /* FIXME: copy caps from This->drvcaps */
2331 caps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
2332 caps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
2334 caps->dwPrimaryBuffers = 1;
2336 caps->dwMaxHwMixingAllBuffers = 0;
2337 caps->dwMaxHwMixingStaticBuffers = 0;
2338 caps->dwMaxHwMixingStreamingBuffers = 0;
2340 caps->dwFreeHwMixingAllBuffers = 0;
2341 caps->dwFreeHwMixingStaticBuffers = 0;
2342 caps->dwFreeHwMixingStreamingBuffers = 0;
2344 caps->dwMaxHw3DAllBuffers = 0;
2345 caps->dwMaxHw3DStaticBuffers = 0;
2346 caps->dwMaxHw3DStreamingBuffers = 0;
2348 caps->dwFreeHw3DAllBuffers = 0;
2349 caps->dwFreeHw3DStaticBuffers = 0;
2350 caps->dwFreeHw3DStreamingBuffers = 0;
2352 caps->dwTotalHwMemBytes = 0;
2354 caps->dwFreeHwMemBytes = 0;
2356 caps->dwMaxContigFreeHwMemBytes = 0;
2358 caps->dwUnlockTransferRateHwBuffers = 4096; /* But we have none... */
2360 caps->dwPlayCpuOverheadSwBuffers = 1; /* 1% */
2362 return DS_OK;
2365 static ULONG WINAPI IDirectSoundImpl_AddRef(LPDIRECTSOUND iface) {
2366 ICOM_THIS(IDirectSoundImpl,iface);
2367 return ++(This->ref);
2370 static ULONG WINAPI IDirectSoundImpl_Release(LPDIRECTSOUND iface) {
2371 ICOM_THIS(IDirectSoundImpl,iface);
2372 TRACE("(%p), ref was %ld\n",This,This->ref);
2373 if (!--(This->ref)) {
2374 UINT i;
2376 timeKillEvent(This->timerID);
2377 timeEndPeriod(DS_TIME_RES);
2379 if (primarybuf)
2380 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)primarybuf);
2382 if (This->buffers) {
2383 for( i=0;i<This->nrofbuffers;i++)
2384 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->buffers[i]);
2387 if (This->primary)
2388 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->primary);
2390 DeleteCriticalSection(&This->lock);
2391 if (This->driver) {
2392 IDsDriver_Close(This->driver);
2393 } else {
2394 unsigned c;
2395 for (c=0; c<DS_HEL_FRAGS; c++)
2396 HeapFree(GetProcessHeap(),0,This->pwave[c]);
2398 if (This->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
2399 waveOutClose(This->hwo);
2401 if (This->driver)
2402 IDsDriver_Release(This->driver);
2404 HeapFree(GetProcessHeap(),0,This);
2405 dsound = NULL;
2406 return 0;
2408 return This->ref;
2411 static HRESULT WINAPI IDirectSoundImpl_SetSpeakerConfig(
2412 LPDIRECTSOUND iface,DWORD config
2414 ICOM_THIS(IDirectSoundImpl,iface);
2415 FIXME("(%p,0x%08lx):stub\n",This,config);
2416 return DS_OK;
2419 static HRESULT WINAPI IDirectSoundImpl_QueryInterface(
2420 LPDIRECTSOUND iface,REFIID riid,LPVOID *ppobj
2422 ICOM_THIS(IDirectSoundImpl,iface);
2424 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
2426 if (This->listener) {
2427 *ppobj = This->listener;
2428 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)This->listener);
2429 return DS_OK;
2432 This->listener = (IDirectSound3DListenerImpl*)HeapAlloc(
2433 GetProcessHeap(), 0, sizeof(*(This->listener)));
2434 This->listener->ref = 1;
2435 ICOM_VTBL(This->listener) = &ds3dlvt;
2436 *ppobj = (LPVOID)This->listener;
2437 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
2439 This->listener->dsb = NULL;
2441 This->listener->ds3dl.dwSize = sizeof(DS3DLISTENER);
2442 This->listener->ds3dl.vPosition.u1.x = 0.0;
2443 This->listener->ds3dl.vPosition.u2.y = 0.0;
2444 This->listener->ds3dl.vPosition.u3.z = 0.0;
2445 This->listener->ds3dl.vVelocity.u1.x = 0.0;
2446 This->listener->ds3dl.vVelocity.u2.y = 0.0;
2447 This->listener->ds3dl.vVelocity.u3.z = 0.0;
2448 This->listener->ds3dl.vOrientFront.u1.x = 0.0;
2449 This->listener->ds3dl.vOrientFront.u2.y = 0.0;
2450 This->listener->ds3dl.vOrientFront.u3.z = 1.0;
2451 This->listener->ds3dl.vOrientTop.u1.x = 0.0;
2452 This->listener->ds3dl.vOrientTop.u2.y = 1.0;
2453 This->listener->ds3dl.vOrientTop.u3.z = 0.0;
2454 This->listener->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
2455 This->listener->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
2456 This->listener->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
2458 InitializeCriticalSection(&This->listener->lock);
2460 return DS_OK;
2463 FIXME("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
2464 return E_FAIL;
2467 static HRESULT WINAPI IDirectSoundImpl_Compact(
2468 LPDIRECTSOUND iface)
2470 ICOM_THIS(IDirectSoundImpl,iface);
2471 TRACE("(%p)\n", This);
2472 return DS_OK;
2475 static HRESULT WINAPI IDirectSoundImpl_GetSpeakerConfig(
2476 LPDIRECTSOUND iface,
2477 LPDWORD lpdwSpeakerConfig)
2479 ICOM_THIS(IDirectSoundImpl,iface);
2480 TRACE("(%p, %p)\n", This, lpdwSpeakerConfig);
2481 *lpdwSpeakerConfig = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16);
2482 return DS_OK;
2485 static HRESULT WINAPI IDirectSoundImpl_Initialize(
2486 LPDIRECTSOUND iface,
2487 LPCGUID lpcGuid)
2489 ICOM_THIS(IDirectSoundImpl,iface);
2490 TRACE("(%p, %p)\n", This, lpcGuid);
2491 return DS_OK;
2494 static ICOM_VTABLE(IDirectSound) dsvt =
2496 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2497 IDirectSoundImpl_QueryInterface,
2498 IDirectSoundImpl_AddRef,
2499 IDirectSoundImpl_Release,
2500 IDirectSoundImpl_CreateSoundBuffer,
2501 IDirectSoundImpl_GetCaps,
2502 IDirectSoundImpl_DuplicateSoundBuffer,
2503 IDirectSoundImpl_SetCooperativeLevel,
2504 IDirectSoundImpl_Compact,
2505 IDirectSoundImpl_GetSpeakerConfig,
2506 IDirectSoundImpl_SetSpeakerConfig,
2507 IDirectSoundImpl_Initialize
2511 static void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len)
2513 int i;
2514 DWORD offset;
2515 LPDSBPOSITIONNOTIFY event;
2517 if (dsb->nrofnotifies == 0)
2518 return;
2520 TRACE("(%p) buflen = %ld, playpos = %ld, len = %d\n",
2521 dsb, dsb->buflen, dsb->playpos, len);
2522 for (i = 0; i < dsb->nrofnotifies ; i++) {
2523 event = dsb->notifies + i;
2524 offset = event->dwOffset;
2525 TRACE("checking %d, position %ld, event = %d\n",
2526 i, offset, event->hEventNotify);
2527 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
2528 /* OK. [Inside DirectX, p274] */
2529 /* */
2530 /* This also means we can't sort the entries by offset, */
2531 /* because DSBPN_OFFSETSTOP == -1 */
2532 if (offset == DSBPN_OFFSETSTOP) {
2533 if (dsb->state == STATE_STOPPED) {
2534 SetEvent(event->hEventNotify);
2535 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2536 return;
2537 } else
2538 return;
2540 if ((dsb->playpos + len) >= dsb->buflen) {
2541 if ((offset < ((dsb->playpos + len) % dsb->buflen)) ||
2542 (offset >= dsb->playpos)) {
2543 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2544 SetEvent(event->hEventNotify);
2546 } else {
2547 if ((offset >= dsb->playpos) && (offset < (dsb->playpos + len))) {
2548 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2549 SetEvent(event->hEventNotify);
2555 /* WAV format info can be found at: */
2556 /* */
2557 /* http://www.cwi.nl/ftp/audio/AudioFormats.part2 */
2558 /* ftp://ftp.cwi.nl/pub/audio/RIFF-format */
2559 /* */
2560 /* Import points to remember: */
2561 /* */
2562 /* 8-bit WAV is unsigned */
2563 /* 16-bit WAV is signed */
2565 static inline INT16 cvtU8toS16(BYTE byte)
2567 INT16 s = (byte - 128) << 8;
2569 return s;
2572 static inline BYTE cvtS16toU8(INT16 word)
2574 BYTE b = (word + 32768) >> 8;
2576 return b;
2580 /* We should be able to optimize these two inline functions */
2581 /* so that we aren't doing 8->16->8 conversions when it is */
2582 /* not necessary. But this is still a WIP. Optimize later. */
2583 static inline void get_fields(const IDirectSoundBufferImpl *dsb, BYTE *buf, INT *fl, INT *fr)
2585 INT16 *bufs = (INT16 *) buf;
2587 /* TRACE("(%p)\n", buf); */
2588 if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 2) {
2589 *fl = cvtU8toS16(*buf);
2590 *fr = cvtU8toS16(*(buf + 1));
2591 return;
2594 if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 2) {
2595 *fl = *bufs;
2596 *fr = *(bufs + 1);
2597 return;
2600 if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 1) {
2601 *fl = cvtU8toS16(*buf);
2602 *fr = *fl;
2603 return;
2606 if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 1) {
2607 *fl = *bufs;
2608 *fr = *bufs;
2609 return;
2612 FIXME("get_fields found an unsupported configuration\n");
2613 return;
2616 static inline void set_fields(BYTE *buf, INT fl, INT fr)
2618 INT16 *bufs = (INT16 *) buf;
2620 if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 2)) {
2621 *buf = cvtS16toU8(fl);
2622 *(buf + 1) = cvtS16toU8(fr);
2623 return;
2626 if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 2)) {
2627 *bufs = fl;
2628 *(bufs + 1) = fr;
2629 return;
2632 if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 1)) {
2633 *buf = cvtS16toU8((fl + fr) >> 1);
2634 return;
2637 if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 1)) {
2638 *bufs = (fl + fr) >> 1;
2639 return;
2641 FIXME("set_fields found an unsupported configuration\n");
2642 return;
2645 /* Now with PerfectPitch (tm) technology */
2646 static INT DSOUND_MixerNorm(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2648 INT i, size, ipos, ilen, fieldL, fieldR;
2649 BYTE *ibp, *obp;
2650 INT iAdvance = dsb->wfx.nBlockAlign;
2651 INT oAdvance = primarybuf->wfx.nBlockAlign;
2653 ibp = dsb->buffer + dsb->buf_mixpos;
2654 obp = buf;
2656 TRACE("(%p, %p, %p), buf_mixpos=%ld\n", dsb, ibp, obp, dsb->buf_mixpos);
2657 /* Check for the best case */
2658 if ((dsb->freq == primarybuf->wfx.nSamplesPerSec) &&
2659 (dsb->wfx.wBitsPerSample == primarybuf->wfx.wBitsPerSample) &&
2660 (dsb->wfx.nChannels == primarybuf->wfx.nChannels)) {
2661 DWORD bytesleft = dsb->buflen - dsb->buf_mixpos;
2662 TRACE("(%p) Best case\n", dsb);
2663 if (len <= bytesleft )
2664 memcpy(obp, ibp, len);
2665 else { /* wrap */
2666 memcpy(obp, ibp, bytesleft );
2667 memcpy(obp + bytesleft, dsb->buffer, len - bytesleft);
2669 return len;
2672 /* Check for same sample rate */
2673 if (dsb->freq == primarybuf->wfx.nSamplesPerSec) {
2674 TRACE("(%p) Same sample rate %ld = primary %ld\n", dsb,
2675 dsb->freq, primarybuf->wfx.nSamplesPerSec);
2676 ilen = 0;
2677 for (i = 0; i < len; i += oAdvance) {
2678 get_fields(dsb, ibp, &fieldL, &fieldR);
2679 ibp += iAdvance;
2680 ilen += iAdvance;
2681 set_fields(obp, fieldL, fieldR);
2682 obp += oAdvance;
2683 if (ibp >= (BYTE *)(dsb->buffer + dsb->buflen))
2684 ibp = dsb->buffer; /* wrap */
2686 return (ilen);
2689 /* Mix in different sample rates */
2690 /* */
2691 /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
2692 /* Patent Pending :-] */
2694 /* Patent enhancements (c) 2000 Ove KÃ¥ven,
2695 * TransGaming Technologies Inc. */
2697 FIXME("(%p) Adjusting frequency: %ld -> %ld (need optimization)\n",
2698 dsb, dsb->freq, primarybuf->wfx.nSamplesPerSec);
2700 size = len / oAdvance;
2701 ilen = 0;
2702 ipos = dsb->buf_mixpos;
2703 for (i = 0; i < size; i++) {
2704 get_fields(dsb, (dsb->buffer + ipos), &fieldL, &fieldR);
2705 set_fields(obp, fieldL, fieldR);
2706 obp += oAdvance;
2708 dsb->freqAcc += dsb->freqAdjust;
2709 if (dsb->freqAcc >= (1<<DSOUND_FREQSHIFT)) {
2710 ULONG adv = (dsb->freqAcc>>DSOUND_FREQSHIFT) * iAdvance;
2711 dsb->freqAcc &= (1<<DSOUND_FREQSHIFT)-1;
2712 ipos += adv; ilen += adv;
2713 while (ipos >= dsb->buflen)
2714 ipos -= dsb->buflen;
2717 return ilen;
2720 static void DSOUND_MixerVol(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2722 INT i, inc = primarybuf->wfx.wBitsPerSample >> 3;
2723 BYTE *bpc = buf;
2724 INT16 *bps = (INT16 *) buf;
2726 TRACE("(%p) left = %lx, right = %lx\n", dsb,
2727 dsb->volpan.dwTotalLeftAmpFactor, dsb->volpan.dwTotalRightAmpFactor);
2728 if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->volpan.lPan == 0)) &&
2729 (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->volpan.lVolume == 0)) &&
2730 !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
2731 return; /* Nothing to do */
2733 /* If we end up with some bozo coder using panning or 3D sound */
2734 /* with a mono primary buffer, it could sound very weird using */
2735 /* this method. Oh well, tough patooties. */
2737 for (i = 0; i < len; i += inc) {
2738 INT val;
2740 switch (inc) {
2742 case 1:
2743 /* 8-bit WAV is unsigned, but we need to operate */
2744 /* on signed data for this to work properly */
2745 val = *bpc - 128;
2746 val = ((val * (i & inc ? dsb->volpan.dwTotalRightAmpFactor : dsb->volpan.dwTotalLeftAmpFactor)) >> 16);
2747 *bpc = val + 128;
2748 bpc++;
2749 break;
2750 case 2:
2751 /* 16-bit WAV is signed -- much better */
2752 val = *bps;
2753 val = ((val * ((i & inc) ? dsb->volpan.dwTotalRightAmpFactor : dsb->volpan.dwTotalLeftAmpFactor)) >> 16);
2754 *bps = val;
2755 bps++;
2756 break;
2757 default:
2758 /* Very ugly! */
2759 FIXME("MixerVol had a nasty error\n");
2764 #ifdef USE_DSOUND3D
2765 static void DSOUND_Mixer3D(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2767 BYTE *ibp, *obp;
2768 DWORD buflen, buf_mixpos;
2770 buflen = dsb->ds3db->buflen;
2771 buf_mixpos = (dsb->buf_mixpos * primarybuf->wfx.nBlockAlign) / dsb->wfx.nBlockAlign;
2772 ibp = dsb->ds3db->buffer + buf_mixpos;
2773 obp = buf;
2775 if (buf_mixpos > buflen) {
2776 FIXME("Major breakage\n");
2777 return;
2780 if (len <= (buf_mixpos + buflen))
2781 memcpy(obp, ibp, len);
2782 else { /* wrap */
2783 memcpy(obp, ibp, buflen - buf_mixpos);
2784 memcpy(obp + (buflen - buf_mixpos),
2785 dsb->buffer,
2786 len - (buflen - buf_mixpos));
2788 return;
2790 #endif
2792 static void *tmp_buffer;
2793 static size_t tmp_buffer_len = 0;
2795 static void *DSOUND_tmpbuffer(size_t len)
2797 if (len>tmp_buffer_len) {
2798 void *new_buffer = realloc(tmp_buffer, len);
2799 if (new_buffer) {
2800 tmp_buffer = new_buffer;
2801 tmp_buffer_len = len;
2803 return new_buffer;
2805 return tmp_buffer;
2808 static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen)
2810 INT i, len, ilen, temp, field;
2811 INT advance = primarybuf->wfx.wBitsPerSample >> 3;
2812 BYTE *buf, *ibuf, *obuf;
2813 INT16 *ibufs, *obufs;
2815 len = fraglen;
2816 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
2817 temp = MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buflen,
2818 dsb->nAvgBytesPerSec) -
2819 MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buf_mixpos,
2820 dsb->nAvgBytesPerSec);
2821 len = (len > temp) ? temp : len;
2823 len &= ~3; /* 4 byte alignment */
2825 if (len == 0) {
2826 /* This should only happen if we aren't looping and temp < 4 */
2828 /* We skip the remainder, so check for possible events */
2829 DSOUND_CheckEvent(dsb, dsb->buflen - dsb->buf_mixpos);
2830 /* Stop */
2831 dsb->state = STATE_STOPPED;
2832 dsb->playpos = 0;
2833 dsb->buf_mixpos = 0;
2834 dsb->leadin = FALSE;
2835 /* Check for DSBPN_OFFSETSTOP */
2836 DSOUND_CheckEvent(dsb, 0);
2837 return 0;
2840 /* Been seeing segfaults in malloc() for some reason... */
2841 TRACE("allocating buffer (size = %d)\n", len);
2842 if ((buf = ibuf = (BYTE *) DSOUND_tmpbuffer(len)) == NULL)
2843 return 0;
2845 TRACE("MixInBuffer (%p) len = %d, dest = %ld\n", dsb, len, writepos);
2847 ilen = DSOUND_MixerNorm(dsb, ibuf, len);
2848 if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
2849 (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
2850 DSOUND_MixerVol(dsb, ibuf, len);
2852 obuf = primarybuf->buffer + writepos;
2853 for (i = 0; i < len; i += advance) {
2854 obufs = (INT16 *) obuf;
2855 ibufs = (INT16 *) ibuf;
2856 if (primarybuf->wfx.wBitsPerSample == 8) {
2857 /* 8-bit WAV is unsigned */
2858 field = (*ibuf - 128);
2859 field += (*obuf - 128);
2860 field = field > 127 ? 127 : field;
2861 field = field < -128 ? -128 : field;
2862 *obuf = field + 128;
2863 } else {
2864 /* 16-bit WAV is signed */
2865 field = *ibufs;
2866 field += *obufs;
2867 field = field > 32767 ? 32767 : field;
2868 field = field < -32768 ? -32768 : field;
2869 *obufs = field;
2871 ibuf += advance;
2872 obuf += advance;
2873 if (obuf >= (BYTE *)(primarybuf->buffer + primarybuf->buflen))
2874 obuf = primarybuf->buffer;
2876 /* free(buf); */
2878 if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY)
2879 DSOUND_CheckEvent(dsb, ilen);
2881 if (dsb->leadin && (dsb->startpos > dsb->buf_mixpos) && (dsb->startpos <= dsb->buf_mixpos + ilen)) {
2882 /* HACK... leadin should be reset when the PLAY position reaches the startpos,
2883 * not the MIX position... but if the sound buffer is bigger than our prebuffering
2884 * (which must be the case for the streaming buffers that need this hack anyway)
2885 * plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
2886 dsb->leadin = FALSE;
2889 dsb->buf_mixpos += ilen;
2891 if (dsb->buf_mixpos >= dsb->buflen) {
2892 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
2893 dsb->state = STATE_STOPPED;
2894 dsb->playpos = 0;
2895 dsb->buf_mixpos = 0;
2896 dsb->leadin = FALSE;
2897 DSOUND_CheckEvent(dsb, 0); /* For DSBPN_OFFSETSTOP */
2898 } else {
2899 /* wrap */
2900 while (dsb->buf_mixpos >= dsb->buflen)
2901 dsb->buf_mixpos -= dsb->buflen;
2902 if (dsb->leadin && (dsb->startpos <= dsb->buf_mixpos))
2903 dsb->leadin = FALSE; /* HACK: see above */
2907 return len;
2910 static void DSOUND_PhaseCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD len)
2912 INT i, ilen, field;
2913 INT advance = primarybuf->wfx.wBitsPerSample >> 3;
2914 BYTE *buf, *ibuf, *obuf;
2915 INT16 *ibufs, *obufs;
2917 len &= ~3; /* 4 byte alignment */
2919 TRACE("allocating buffer (size = %ld)\n", len);
2920 if ((buf = ibuf = (BYTE *) DSOUND_tmpbuffer(len)) == NULL)
2921 return;
2923 TRACE("PhaseCancel (%p) len = %ld, dest = %ld\n", dsb, len, writepos);
2925 ilen = DSOUND_MixerNorm(dsb, ibuf, len);
2926 if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
2927 (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
2928 DSOUND_MixerVol(dsb, ibuf, len);
2930 /* subtract instead of add, to phase out premixed data */
2931 obuf = primarybuf->buffer + writepos;
2932 for (i = 0; i < len; i += advance) {
2933 obufs = (INT16 *) obuf;
2934 ibufs = (INT16 *) ibuf;
2935 if (primarybuf->wfx.wBitsPerSample == 8) {
2936 /* 8-bit WAV is unsigned */
2937 field = (*ibuf - 128);
2938 field -= (*obuf - 128);
2939 field = field > 127 ? 127 : field;
2940 field = field < -128 ? -128 : field;
2941 *obuf = field + 128;
2942 } else {
2943 /* 16-bit WAV is signed */
2944 field = *ibufs;
2945 field -= *obufs;
2946 field = field > 32767 ? 32767 : field;
2947 field = field < -32768 ? -32768 : field;
2948 *obufs = field;
2950 ibuf += advance;
2951 obuf += advance;
2952 if (obuf >= (BYTE *)(primarybuf->buffer + primarybuf->buflen))
2953 obuf = primarybuf->buffer;
2955 /* free(buf); */
2958 static void DSOUND_MixCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, BOOL cancel)
2960 DWORD size, flen, len, npos, nlen;
2961 INT iAdvance = dsb->wfx.nBlockAlign;
2962 INT oAdvance = primarybuf->wfx.nBlockAlign;
2963 /* determine amount of premixed data to cancel */
2964 DWORD primary_done =
2965 ((dsb->primary_mixpos < writepos) ? primarybuf->buflen : 0) +
2966 dsb->primary_mixpos - writepos;
2968 TRACE("(%p, %ld), buf_mixpos=%ld\n", dsb, writepos, dsb->buf_mixpos);
2970 /* backtrack the mix position */
2971 size = primary_done / oAdvance;
2972 flen = size * dsb->freqAdjust;
2973 len = (flen >> DSOUND_FREQSHIFT) * iAdvance;
2974 flen &= (1<<DSOUND_FREQSHIFT)-1;
2975 while (dsb->freqAcc < flen) {
2976 len += iAdvance;
2977 dsb->freqAcc += 1<<DSOUND_FREQSHIFT;
2979 len %= dsb->buflen;
2980 npos = ((dsb->buf_mixpos < len) ? dsb->buflen : 0) +
2981 dsb->buf_mixpos - len;
2982 if (dsb->leadin && (dsb->startpos > npos) && (dsb->startpos <= npos + len)) {
2983 /* stop backtracking at startpos */
2984 npos = dsb->startpos;
2985 len = ((dsb->buf_mixpos < npos) ? dsb->buflen : 0) +
2986 dsb->buf_mixpos - npos;
2987 flen = dsb->freqAcc;
2988 nlen = len / dsb->wfx.nBlockAlign;
2989 nlen = ((nlen << DSOUND_FREQSHIFT) + flen) / dsb->freqAdjust;
2990 nlen *= primarybuf->wfx.nBlockAlign;
2991 writepos =
2992 ((dsb->primary_mixpos < nlen) ? primarybuf->buflen : 0) +
2993 dsb->primary_mixpos - nlen;
2996 dsb->freqAcc -= flen;
2997 dsb->buf_mixpos = npos;
2998 dsb->primary_mixpos = writepos;
3000 TRACE("new buf_mixpos=%ld, primary_mixpos=%ld (len=%ld)\n",
3001 dsb->buf_mixpos, dsb->primary_mixpos, len);
3003 if (cancel) DSOUND_PhaseCancel(dsb, writepos, len);
3006 static void DSOUND_MixCancelAt(IDirectSoundBufferImpl *dsb, DWORD buf_writepos)
3008 #if 0
3009 DWORD i, size, flen, len, npos, nlen;
3010 INT iAdvance = dsb->wfx.nBlockAlign;
3011 INT oAdvance = primarybuf->wfx.nBlockAlign;
3012 /* determine amount of premixed data to cancel */
3013 DWORD buf_done =
3014 ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
3015 dsb->buf_mixpos - buf_writepos;
3016 #endif
3018 WARN("(%p, %ld), buf_mixpos=%ld\n", dsb, buf_writepos, dsb->buf_mixpos);
3019 /* since this is not implemented yet, just cancel *ALL* prebuffering for now
3020 * (which is faster anyway when there's one a single secondary buffer) */
3021 primarybuf->need_remix = TRUE;
3024 static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD playpos, DWORD writepos, DWORD mixlen)
3026 DWORD len, slen;
3027 /* determine this buffer's write position */
3028 DWORD buf_writepos = DSOUND_CalcPlayPosition(dsb, dsb->state & primarybuf->state, writepos,
3029 writepos, dsb->primary_mixpos, dsb->buf_mixpos);
3030 /* determine how much already-mixed data exists */
3031 DWORD buf_done =
3032 ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
3033 dsb->buf_mixpos - buf_writepos;
3034 DWORD primary_done =
3035 ((dsb->primary_mixpos < writepos) ? primarybuf->buflen : 0) +
3036 dsb->primary_mixpos - writepos;
3037 DWORD adv_done =
3038 ((primarybuf->buf_mixpos < writepos) ? primarybuf->buflen : 0) +
3039 primarybuf->buf_mixpos - writepos;
3040 int still_behind;
3042 TRACE("buf_writepos=%ld, primary_writepos=%ld\n", buf_writepos, writepos);
3043 TRACE("buf_done=%ld, primary_done=%ld\n", buf_done, primary_done);
3044 TRACE("buf_mixpos=%ld, primary_mixpos=%ld, mixlen=%ld\n", dsb->buf_mixpos, dsb->primary_mixpos,
3045 mixlen);
3046 TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb->playflags, dsb->startpos, dsb->leadin);
3048 /* save write position for non-GETCURRENTPOSITION2... */
3049 dsb->playpos = buf_writepos;
3051 /* check whether CalcPlayPosition detected a mixing underrun */
3052 if ((buf_done == 0) && (dsb->primary_mixpos != writepos)) {
3053 /* it did, but did we have more to play? */
3054 if ((dsb->playflags & DSBPLAY_LOOPING) ||
3055 (dsb->buf_mixpos < dsb->buflen)) {
3056 /* yes, have to recover */
3057 ERR("underrun on sound buffer %p\n", dsb);
3058 TRACE("recovering from underrun: primary_mixpos=%ld\n", writepos);
3060 dsb->primary_mixpos = writepos;
3061 primary_done = 0;
3063 /* determine how far ahead we should mix */
3064 if (((dsb->playflags & DSBPLAY_LOOPING) ||
3065 (dsb->leadin && (dsb->probably_valid_to != 0))) &&
3066 !(dsb->dsbd.dwFlags & DSBCAPS_STATIC)) {
3067 /* if this is a streaming buffer, it typically means that
3068 * we should defer mixing past probably_valid_to as long
3069 * as we can, to avoid unnecessary remixing */
3070 /* the heavy-looking calculations shouldn't be that bad,
3071 * as any game isn't likely to be have more than 1 or 2
3072 * streaming buffers in use at any time anyway... */
3073 DWORD probably_valid_left =
3074 (dsb->probably_valid_to == (DWORD)-1) ? dsb->buflen :
3075 ((dsb->probably_valid_to < buf_writepos) ? dsb->buflen : 0) +
3076 dsb->probably_valid_to - buf_writepos;
3077 /* check for leadin condition */
3078 if ((probably_valid_left == 0) &&
3079 (dsb->probably_valid_to == dsb->startpos) &&
3080 dsb->leadin)
3081 probably_valid_left = dsb->buflen;
3082 TRACE("streaming buffer probably_valid_to=%ld, probably_valid_left=%ld\n",
3083 dsb->probably_valid_to, probably_valid_left);
3084 /* check whether the app's time is already up */
3085 if (probably_valid_left < dsb->writelead) {
3086 WARN("probably_valid_to now within writelead, possible streaming underrun\n");
3087 /* once we pass the point of no return,
3088 * no reason to hold back anymore */
3089 dsb->probably_valid_to = (DWORD)-1;
3090 /* we just have to go ahead and mix what we have,
3091 * there's no telling what the app is thinking anyway */
3092 } else {
3093 /* adjust for our frequency and our sample size */
3094 probably_valid_left = MulDiv(probably_valid_left,
3095 1 << DSOUND_FREQSHIFT,
3096 dsb->wfx.nBlockAlign * dsb->freqAdjust) *
3097 primarybuf->wfx.nBlockAlign;
3098 /* check whether to clip mix_len */
3099 if (probably_valid_left < mixlen) {
3100 TRACE("clipping to probably_valid_left=%ld\n", probably_valid_left);
3101 mixlen = probably_valid_left;
3105 /* cut mixlen with what's already been mixed */
3106 if (mixlen < primary_done) {
3107 /* huh? and still CalcPlayPosition didn't
3108 * detect an underrun? */
3109 FIXME("problem with underrun detection (mixlen=%ld < primary_done=%ld)\n", mixlen, primary_done);
3110 return 0;
3112 len = mixlen - primary_done;
3113 TRACE("remaining mixlen=%ld\n", len);
3115 if (len < primarybuf->dsound->fraglen) {
3116 /* smaller than a fragment, wait until it gets larger
3117 * before we take the mixing overhead */
3118 TRACE("mixlen not worth it, deferring mixing\n");
3119 return 0;
3122 /* ok, we know how much to mix, let's go */
3123 still_behind = (adv_done > primary_done);
3124 while (len) {
3125 slen = primarybuf->buflen - dsb->primary_mixpos;
3126 if (slen > len) slen = len;
3127 slen = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, slen);
3129 if ((dsb->primary_mixpos < primarybuf->buf_mixpos) &&
3130 (dsb->primary_mixpos + slen >= primarybuf->buf_mixpos))
3131 still_behind = FALSE;
3133 dsb->primary_mixpos += slen; len -= slen;
3134 while (dsb->primary_mixpos >= primarybuf->buflen)
3135 dsb->primary_mixpos -= primarybuf->buflen;
3137 if ((dsb->state == STATE_STOPPED) || !slen) break;
3139 TRACE("new primary_mixpos=%ld, primary_advbase=%ld\n", dsb->primary_mixpos, primarybuf->buf_mixpos);
3140 TRACE("mixed data len=%ld, still_behind=%d\n", mixlen-len, still_behind);
3141 /* return how far we think the primary buffer can
3142 * advance its underrun detector...*/
3143 if (still_behind) return 0;
3144 if ((mixlen - len) < primary_done) return 0;
3145 slen = ((dsb->primary_mixpos < primarybuf->buf_mixpos) ?
3146 primarybuf->buflen : 0) + dsb->primary_mixpos -
3147 primarybuf->buf_mixpos;
3148 if (slen > mixlen) {
3149 /* the primary_done and still_behind checks above should have worked */
3150 FIXME("problem with advancement calculation (advlen=%ld > mixlen=%ld)\n", slen, mixlen);
3151 slen = 0;
3153 return slen;
3156 static DWORD DSOUND_MixToPrimary(DWORD playpos, DWORD writepos, DWORD mixlen, BOOL recover)
3158 INT i, len, maxlen = 0;
3159 IDirectSoundBufferImpl *dsb;
3161 TRACE("(%ld,%ld,%ld)\n", playpos, writepos, mixlen);
3162 for (i = dsound->nrofbuffers - 1; i >= 0; i--) {
3163 dsb = dsound->buffers[i];
3165 if (!dsb || !(ICOM_VTBL(dsb)))
3166 continue;
3167 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
3168 TRACE("Checking %p, mixlen=%ld\n", dsb, mixlen);
3169 EnterCriticalSection(&(dsb->lock));
3170 if (dsb->state == STATE_STOPPING) {
3171 DSOUND_MixCancel(dsb, writepos, TRUE);
3172 dsb->state = STATE_STOPPED;
3173 } else {
3174 if ((dsb->state == STATE_STARTING) || recover)
3175 dsb->primary_mixpos = writepos;
3176 len = DSOUND_MixOne(dsb, playpos, writepos, mixlen);
3177 if (dsb->state == STATE_STARTING)
3178 dsb->state = STATE_PLAYING;
3179 maxlen = (len > maxlen) ? len : maxlen;
3181 LeaveCriticalSection(&(dsb->lock));
3185 return maxlen;
3188 static void DSOUND_MixReset(DWORD writepos)
3190 INT i;
3191 IDirectSoundBufferImpl *dsb;
3192 int nfiller;
3194 TRACE("(%ld)\n", writepos);
3196 /* the sound of silence */
3197 nfiller = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
3199 /* reset all buffer mix positions */
3200 for (i = dsound->nrofbuffers - 1; i >= 0; i--) {
3201 dsb = dsound->buffers[i];
3203 if (!dsb || !(ICOM_VTBL(dsb)))
3204 continue;
3205 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
3206 TRACE("Resetting %p\n", dsb);
3207 EnterCriticalSection(&(dsb->lock));
3208 if (dsb->state == STATE_STOPPING) {
3209 dsb->state = STATE_STOPPED;
3211 else if (dsb->state == STATE_STARTING) {
3212 /* nothing */
3213 } else {
3214 DSOUND_MixCancel(dsb, writepos, FALSE);
3216 LeaveCriticalSection(&(dsb->lock));
3220 /* wipe out premixed data */
3221 if (primarybuf->buf_mixpos < writepos) {
3222 memset(primarybuf->buffer + writepos, nfiller, primarybuf->buflen - writepos);
3223 memset(primarybuf->buffer, nfiller, primarybuf->buf_mixpos);
3224 } else {
3225 memset(primarybuf->buffer + writepos, nfiller, primarybuf->buf_mixpos - writepos);
3228 /* reset primary mix position */
3229 primarybuf->buf_mixpos = writepos;
3232 static void DSOUND_CheckReset(IDirectSoundImpl *dsound, DWORD writepos)
3234 if (primarybuf->need_remix) {
3235 DSOUND_MixReset(writepos);
3236 primarybuf->need_remix = FALSE;
3237 /* maximize Half-Life performance */
3238 dsound->prebuf = DS_SND_QUEUE_MIN;
3239 } else {
3240 /* if (dsound->prebuf < DS_SND_QUEUE_MAX) dsound->prebuf++; */
3242 TRACE("premix adjust: %d\n", dsound->prebuf);
3245 static void DSOUND_WaveQueue(IDirectSoundImpl *dsound, DWORD mixq)
3247 if (mixq + dsound->pwqueue > DS_HEL_QUEUE) mixq = DS_HEL_QUEUE - dsound->pwqueue;
3248 TRACE("queueing %ld buffers, starting at %d\n", mixq, dsound->pwwrite);
3249 for (; mixq; mixq--) {
3250 waveOutWrite(dsound->hwo, dsound->pwave[dsound->pwwrite], sizeof(WAVEHDR));
3251 dsound->pwwrite++;
3252 if (dsound->pwwrite >= DS_HEL_FRAGS) dsound->pwwrite = 0;
3253 dsound->pwqueue++;
3257 /* #define SYNC_CALLBACK */
3259 static void DSOUND_PerformMix(void)
3261 int nfiller;
3262 BOOL forced;
3263 HRESULT hres;
3265 EnterCriticalSection(&(dsound->lock));
3267 if (!primarybuf || !primarybuf->ref) {
3268 /* seems the primary buffer is currently being released */
3269 LeaveCriticalSection(&(dsound->lock));
3270 return;
3273 /* the sound of silence */
3274 nfiller = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
3276 /* whether the primary is forced to play even without secondary buffers */
3277 forced = ((primarybuf->state == STATE_PLAYING) || (primarybuf->state == STATE_STARTING));
3279 TRACE("entering at %ld\n", GetTickCount());
3280 if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
3281 BOOL paused = ((primarybuf->state == STATE_STOPPED) || (primarybuf->state == STATE_STARTING));
3282 /* FIXME: document variables */
3283 DWORD playpos, writepos, inq, maxq, frag;
3284 if (primarybuf->hwbuf) {
3285 hres = IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, &writepos);
3286 if (hres) {
3287 LeaveCriticalSection(&(dsound->lock));
3288 return;
3290 /* Well, we *could* do Just-In-Time mixing using the writepos,
3291 * but that's a little bit ambitious and unnecessary... */
3292 /* rather add our safety margin to the writepos, if we're playing */
3293 if (!paused) {
3294 writepos += primarybuf->writelead;
3295 while (writepos >= primarybuf->buflen)
3296 writepos -= primarybuf->buflen;
3297 } else writepos = playpos;
3299 else {
3300 playpos = dsound->pwplay * dsound->fraglen;
3301 writepos = playpos;
3302 if (!paused) {
3303 writepos += DS_HEL_MARGIN * dsound->fraglen;
3304 while (writepos >= primarybuf->buflen)
3305 writepos -= primarybuf->buflen;
3308 TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld\n",
3309 playpos,writepos,primarybuf->playpos,primarybuf->buf_mixpos);
3310 /* wipe out just-played sound data */
3311 if (playpos < primarybuf->playpos) {
3312 memset(primarybuf->buffer + primarybuf->playpos, nfiller, primarybuf->buflen - primarybuf->playpos);
3313 memset(primarybuf->buffer, nfiller, playpos);
3314 } else {
3315 memset(primarybuf->buffer + primarybuf->playpos, nfiller, playpos - primarybuf->playpos);
3317 primarybuf->playpos = playpos;
3319 EnterCriticalSection(&(primarybuf->lock));
3321 /* reset mixing if necessary */
3322 DSOUND_CheckReset(dsound, writepos);
3324 /* check how much prebuffering is left */
3325 inq = primarybuf->buf_mixpos;
3326 if (inq < writepos)
3327 inq += primarybuf->buflen;
3328 inq -= writepos;
3330 /* find the maximum we can prebuffer */
3331 if (!paused) {
3332 maxq = playpos;
3333 if (maxq < writepos)
3334 maxq += primarybuf->buflen;
3335 maxq -= writepos;
3336 } else maxq = primarybuf->buflen;
3338 /* clip maxq to dsound->prebuf */
3339 frag = dsound->prebuf * dsound->fraglen;
3340 if (maxq > frag) maxq = frag;
3342 /* check for consistency */
3343 if (inq > maxq) {
3344 /* the playback position must have passed our last
3345 * mixed position, i.e. it's an underrun, or we have
3346 * nothing more to play */
3347 TRACE("reached end of mixed data (inq=%ld, maxq=%ld)\n", inq, maxq);
3348 inq = 0;
3349 /* stop the playback now, to allow buffers to refill */
3350 if (primarybuf->state == STATE_PLAYING) {
3351 primarybuf->state = STATE_STARTING;
3353 else if (primarybuf->state == STATE_STOPPING) {
3354 primarybuf->state = STATE_STOPPED;
3356 else {
3357 /* how can we have an underrun if we aren't playing? */
3358 WARN("unexpected primary state (%ld)\n", primarybuf->state);
3360 #ifdef SYNC_CALLBACK
3361 /* DSOUND_callback may need this lock */
3362 LeaveCriticalSection(&(primarybuf->lock));
3363 #endif
3364 DSOUND_PrimaryStop(primarybuf);
3365 #ifdef SYNC_CALLBACK
3366 EnterCriticalSection(&(primarybuf->lock));
3367 #endif
3368 if (primarybuf->hwbuf) {
3369 /* the Stop is supposed to reset play position to beginning of buffer */
3370 /* unfortunately, OSS is not able to do so, so get current pointer */
3371 hres = IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, NULL);
3372 if (hres) {
3373 LeaveCriticalSection(&(dsound->lock));
3374 LeaveCriticalSection(&(primarybuf->lock));
3375 return;
3377 } else {
3378 playpos = dsound->pwplay * dsound->fraglen;
3380 writepos = playpos;
3381 primarybuf->playpos = playpos;
3382 primarybuf->buf_mixpos = writepos;
3383 inq = 0;
3384 maxq = primarybuf->buflen;
3385 if (maxq > frag) maxq = frag;
3386 memset(primarybuf->buffer, nfiller, primarybuf->buflen);
3387 paused = TRUE;
3390 /* do the mixing */
3391 frag = DSOUND_MixToPrimary(playpos, writepos, maxq, paused);
3392 if (forced) frag = maxq - inq;
3393 primarybuf->buf_mixpos += frag;
3394 while (primarybuf->buf_mixpos >= primarybuf->buflen)
3395 primarybuf->buf_mixpos -= primarybuf->buflen;
3397 if (frag) {
3398 /* buffers have been filled, restart playback */
3399 if (primarybuf->state == STATE_STARTING) {
3400 primarybuf->state = STATE_PLAYING;
3402 else if (primarybuf->state == STATE_STOPPED) {
3403 /* the primarybuf is supposed to play if there's something to play
3404 * even if it is reported as stopped, so don't let this confuse you */
3405 primarybuf->state = STATE_STOPPING;
3407 LeaveCriticalSection(&(primarybuf->lock));
3408 if (paused) {
3409 DSOUND_PrimaryPlay(primarybuf);
3410 TRACE("starting playback\n");
3413 else
3414 LeaveCriticalSection(&(primarybuf->lock));
3415 } else {
3416 /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
3417 if (primarybuf->state == STATE_STARTING) {
3418 DSOUND_PrimaryPlay(primarybuf);
3419 primarybuf->state = STATE_PLAYING;
3421 else if (primarybuf->state == STATE_STOPPING) {
3422 DSOUND_PrimaryStop(primarybuf);
3423 primarybuf->state = STATE_STOPPED;
3426 TRACE("completed processing at %ld\n", GetTickCount());
3427 LeaveCriticalSection(&(dsound->lock));
3430 static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
3432 if (!dsound || !primarybuf) {
3433 ERR("dsound died without killing us?\n");
3434 timeKillEvent(timerID);
3435 timeEndPeriod(DS_TIME_RES);
3436 return;
3439 TRACE("entered\n");
3440 DSOUND_PerformMix();
3443 static void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
3445 IDirectSoundImpl* This = (IDirectSoundImpl*)dwUser;
3446 TRACE("entering at %ld, msg=%08x\n", GetTickCount(), msg);
3447 if (msg == MM_WOM_DONE) {
3448 DWORD inq, mixq, fraglen, buflen, pwplay, playpos, mixpos;
3449 if (This->pwqueue == (DWORD)-1) {
3450 TRACE("completed due to reset\n");
3451 return;
3453 /* it could be a bad idea to enter critical section here... if there's lock contention,
3454 * the resulting scheduling delays might obstruct the winmm player thread */
3455 #ifdef SYNC_CALLBACK
3456 EnterCriticalSection(&(primarybuf->lock));
3457 #endif
3458 /* retrieve current values */
3459 fraglen = dsound->fraglen;
3460 buflen = primarybuf->buflen;
3461 pwplay = dsound->pwplay;
3462 playpos = pwplay * fraglen;
3463 mixpos = primarybuf->buf_mixpos;
3464 /* check remaining mixed data */
3465 inq = ((mixpos < playpos) ? buflen : 0) + mixpos - playpos;
3466 mixq = inq / fraglen;
3467 if ((inq - (mixq * fraglen)) > 0) mixq++;
3468 /* complete the playing buffer */
3469 TRACE("done playing primary pos=%ld\n", playpos);
3470 pwplay++;
3471 if (pwplay >= DS_HEL_FRAGS) pwplay = 0;
3472 /* write new values */
3473 dsound->pwplay = pwplay;
3474 dsound->pwqueue--;
3475 /* queue new buffer if we have data for it */
3476 if (inq>1) DSOUND_WaveQueue(This, inq-1);
3477 #ifdef SYNC_CALLBACK
3478 LeaveCriticalSection(&(primarybuf->lock));
3479 #endif
3481 TRACE("completed\n");
3484 /*******************************************************************************
3485 * DirectSoundCreate (DSOUND.1)
3487 HRESULT WINAPI DirectSoundCreate(REFGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter )
3489 IDirectSoundImpl** ippDS=(IDirectSoundImpl**)ppDS;
3490 PIDSDRIVER drv = NULL;
3491 WAVEOUTCAPSA wcaps;
3492 unsigned wod, wodn;
3493 HRESULT err = DS_OK;
3495 if (lpGUID)
3496 TRACE("(%p,%p,%p)\n",lpGUID,ippDS,pUnkOuter);
3497 else
3498 TRACE("DirectSoundCreate (%p)\n", ippDS);
3500 if (ippDS == NULL)
3501 return DSERR_INVALIDPARAM;
3503 if (primarybuf) {
3504 IDirectSound_AddRef((LPDIRECTSOUND)dsound);
3505 *ippDS = dsound;
3506 return DS_OK;
3509 /* Enumerate WINMM audio devices and find the one we want */
3510 wodn = waveOutGetNumDevs();
3511 if (!wodn) return DSERR_NODRIVER;
3513 /* FIXME: How do we find the GUID of an audio device? */
3514 /* Well, just use the first available device for now... */
3515 wod = 0;
3516 /* Get output device caps */
3517 waveOutGetDevCapsA(wod, &wcaps, sizeof(wcaps));
3518 /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
3519 waveOutMessage(wod, DRV_QUERYDSOUNDIFACE, (DWORD)&drv, 0);
3521 /* Allocate memory */
3522 *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundImpl));
3523 if (*ippDS == NULL)
3524 return DSERR_OUTOFMEMORY;
3526 ICOM_VTBL(*ippDS) = &dsvt;
3527 (*ippDS)->ref = 1;
3529 (*ippDS)->driver = drv;
3530 (*ippDS)->fraglen = 0;
3531 (*ippDS)->priolevel = DSSCL_NORMAL;
3532 (*ippDS)->nrofbuffers = 0;
3533 (*ippDS)->buffers = NULL;
3534 (*ippDS)->primary = NULL;
3535 (*ippDS)->listener = NULL;
3537 (*ippDS)->prebuf = DS_SND_QUEUE_MAX;
3539 /* Get driver description */
3540 if (drv) {
3541 IDsDriver_GetDriverDesc(drv,&((*ippDS)->drvdesc));
3542 } else {
3543 /* if no DirectSound interface available, use WINMM API instead */
3544 (*ippDS)->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
3545 (*ippDS)->drvdesc.dnDevNode = wod; /* FIXME? */
3548 /* Set default wave format (may need it for waveOutOpen) */
3549 (*ippDS)->wfx.wFormatTag = WAVE_FORMAT_PCM;
3550 (*ippDS)->wfx.nChannels = 2;
3551 (*ippDS)->wfx.nSamplesPerSec = 22050;
3552 (*ippDS)->wfx.nAvgBytesPerSec = 44100;
3553 (*ippDS)->wfx.nBlockAlign = 2;
3554 (*ippDS)->wfx.wBitsPerSample = 8;
3556 /* If the driver requests being opened through MMSYSTEM
3557 * (which is recommended by the DDK), it is supposed to happen
3558 * before the DirectSound interface is opened */
3559 if ((*ippDS)->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
3560 /* FIXME: is this right? */
3561 err = mmErr(waveOutOpen(&((*ippDS)->hwo), (*ippDS)->drvdesc.dnDevNode, &((*ippDS)->wfx),
3562 (DWORD)DSOUND_callback, (DWORD)(*ippDS),
3563 CALLBACK_FUNCTION | WAVE_DIRECTSOUND));
3566 if (drv && (err == DS_OK))
3567 err = IDsDriver_Open(drv);
3569 /* FIXME: do we want to handle a temporarily busy device? */
3570 if (err != DS_OK) {
3571 HeapFree(GetProcessHeap(),0,*ippDS);
3572 *ippDS = NULL;
3573 return err;
3576 /* the driver is now open, so it's now allowed to call GetCaps */
3577 if (drv) {
3578 IDsDriver_GetCaps(drv,&((*ippDS)->drvcaps));
3579 } else {
3580 unsigned c;
3582 /* FIXME: look at wcaps */
3583 (*ippDS)->drvcaps.dwFlags =
3584 DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
3585 if (DS_EMULDRIVER)
3586 (*ippDS)->drvcaps.dwFlags |= DSCAPS_EMULDRIVER;
3588 /* Allocate memory for HEL buffer headers */
3589 for (c=0; c<DS_HEL_FRAGS; c++) {
3590 (*ippDS)->pwave[c] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEHDR));
3591 if (!(*ippDS)->pwave[c]) {
3592 /* Argh, out of memory */
3593 while (c--) {
3594 HeapFree(GetProcessHeap(),0,(*ippDS)->pwave[c]);
3595 waveOutClose((*ippDS)->hwo);
3596 HeapFree(GetProcessHeap(),0,*ippDS);
3597 *ippDS = NULL;
3598 return DSERR_OUTOFMEMORY;
3604 InitializeCriticalSection(&((*ippDS)->lock));
3606 if (!dsound) {
3607 dsound = (*ippDS);
3608 if (primarybuf == NULL) {
3609 DSBUFFERDESC dsbd;
3610 HRESULT hr;
3612 dsbd.dwSize = sizeof(DSBUFFERDESC);
3613 dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
3614 dsbd.dwBufferBytes = 0;
3615 dsbd.lpwfxFormat = &(dsound->wfx);
3616 hr = IDirectSound_CreateSoundBuffer(*ppDS, &dsbd, (LPDIRECTSOUNDBUFFER*)&primarybuf, NULL);
3617 if (hr != DS_OK)
3618 return hr;
3620 /* dsound->primary is NULL - don't need to Release */
3621 dsound->primary = primarybuf;
3622 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)primarybuf);
3624 timeBeginPeriod(DS_TIME_RES);
3625 dsound->timerID = timeSetEvent(DS_TIME_DEL, DS_TIME_RES, DSOUND_timer,
3626 (DWORD)dsound, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
3628 return DS_OK;
3631 /***************************************************************************
3632 * DirectSoundCaptureCreate [DSOUND.6]
3634 * Create and initialize a DirectSoundCapture interface
3636 * RETURNS
3637 * Success: DS_OK
3638 * Failure: DSERR_NOAGGREGATION, DSERR_ALLOCATED, DSERR_INVALIDPARAM,
3639 * DSERR_OUTOFMEMORY
3641 HRESULT WINAPI DirectSoundCaptureCreate(
3642 LPCGUID lpcGUID,
3643 LPDIRECTSOUNDCAPTURE* lplpDSC,
3644 LPUNKNOWN pUnkOuter )
3646 TRACE("(%s,%p,%p)\n", debugstr_guid(lpcGUID), lplpDSC, pUnkOuter);
3648 if( pUnkOuter ) {
3649 return DSERR_NOAGGREGATION;
3652 /* Default device? */
3653 if ( !lpcGUID ) {
3654 return DSOUND_CreateDirectSoundCapture( (LPVOID*)lplpDSC );
3657 FIXME( "Unknown GUID %s\n", debugstr_guid(lpcGUID) );
3658 *lplpDSC = NULL;
3660 return DSERR_OUTOFMEMORY;
3663 /***************************************************************************
3664 * DirectSoundCaptureEnumerateA [DSOUND.7]
3666 * Enumerate all DirectSound drivers installed in the system
3668 * RETURNS
3669 * Success: DS_OK
3670 * Failure: DSERR_INVALIDPARAM
3672 HRESULT WINAPI DirectSoundCaptureEnumerateA(
3673 LPDSENUMCALLBACKA lpDSEnumCallback,
3674 LPVOID lpContext)
3676 TRACE("(%p,%p)\n", lpDSEnumCallback, lpContext );
3678 if ( lpDSEnumCallback )
3679 lpDSEnumCallback(NULL,"WINE Primary Sound Capture Driver",
3680 "SoundCap",lpContext);
3683 return DS_OK;
3686 /***************************************************************************
3687 * DirectSoundCaptureEnumerateW [DSOUND.8]
3689 * Enumerate all DirectSound drivers installed in the system
3691 * RETURNS
3692 * Success: DS_OK
3693 * Failure: DSERR_INVALIDPARAM
3695 HRESULT WINAPI DirectSoundCaptureEnumerateW(
3696 LPDSENUMCALLBACKW lpDSEnumCallback,
3697 LPVOID lpContext)
3699 FIXME("(%p,%p):stub\n", lpDSEnumCallback, lpContext );
3700 return DS_OK;
3703 static HRESULT
3704 DSOUND_CreateDirectSoundCapture( LPVOID* ppobj )
3706 *ppobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( IDirectSoundCaptureImpl ) );
3708 if ( *ppobj == NULL ) {
3709 return DSERR_OUTOFMEMORY;
3713 ICOM_THIS(IDirectSoundCaptureImpl,*ppobj);
3715 This->ref = 1;
3716 ICOM_VTBL(This) = &dscvt;
3718 InitializeCriticalSection( &This->lock );
3721 return S_OK;
3724 static HRESULT WINAPI
3725 IDirectSoundCaptureImpl_QueryInterface(
3726 LPDIRECTSOUNDCAPTURE iface,
3727 REFIID riid,
3728 LPVOID* ppobj )
3730 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3732 FIXME( "(%p)->(%s,%p): stub\n", This, debugstr_guid(riid), ppobj );
3734 return E_FAIL;
3737 static ULONG WINAPI
3738 IDirectSoundCaptureImpl_AddRef( LPDIRECTSOUNDCAPTURE iface )
3740 ULONG uRef;
3741 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3743 EnterCriticalSection( &This->lock );
3745 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3746 uRef = ++(This->ref);
3748 LeaveCriticalSection( &This->lock );
3750 return uRef;
3753 static ULONG WINAPI
3754 IDirectSoundCaptureImpl_Release( LPDIRECTSOUNDCAPTURE iface )
3756 ULONG uRef;
3757 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3759 EnterCriticalSection( &This->lock );
3761 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3762 uRef = --(This->ref);
3764 LeaveCriticalSection( &This->lock );
3766 if ( uRef == 0 ) {
3767 DeleteCriticalSection( &This->lock );
3768 HeapFree( GetProcessHeap(), 0, This );
3771 return uRef;
3774 static HRESULT WINAPI
3775 IDirectSoundCaptureImpl_CreateCaptureBuffer(
3776 LPDIRECTSOUNDCAPTURE iface,
3777 LPCDSCBUFFERDESC lpcDSCBufferDesc,
3778 LPDIRECTSOUNDCAPTUREBUFFER* lplpDSCaptureBuffer,
3779 LPUNKNOWN pUnk )
3781 HRESULT hr;
3782 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3784 TRACE( "(%p)->(%p,%p,%p)\n", This, lpcDSCBufferDesc, lplpDSCaptureBuffer, pUnk );
3786 if ( pUnk ) {
3787 return DSERR_INVALIDPARAM;
3790 hr = DSOUND_CreateDirectSoundCaptureBuffer( lpcDSCBufferDesc, (LPVOID*)lplpDSCaptureBuffer );
3792 return hr;
3795 static HRESULT WINAPI
3796 IDirectSoundCaptureImpl_GetCaps(
3797 LPDIRECTSOUNDCAPTURE iface,
3798 LPDSCCAPS lpDSCCaps )
3800 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3802 FIXME( "(%p)->(%p): stub\n", This, lpDSCCaps );
3804 return DS_OK;
3807 static HRESULT WINAPI
3808 IDirectSoundCaptureImpl_Initialize(
3809 LPDIRECTSOUNDCAPTURE iface,
3810 LPCGUID lpcGUID )
3812 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3814 FIXME( "(%p)->(%p): stub\n", This, lpcGUID );
3816 return DS_OK;
3820 static ICOM_VTABLE(IDirectSoundCapture) dscvt =
3822 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3823 /* IUnknown methods */
3824 IDirectSoundCaptureImpl_QueryInterface,
3825 IDirectSoundCaptureImpl_AddRef,
3826 IDirectSoundCaptureImpl_Release,
3828 /* IDirectSoundCapture methods */
3829 IDirectSoundCaptureImpl_CreateCaptureBuffer,
3830 IDirectSoundCaptureImpl_GetCaps,
3831 IDirectSoundCaptureImpl_Initialize
3834 static HRESULT
3835 DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc, LPVOID* ppobj )
3838 FIXME( "(%p,%p): ignoring lpcDSCBufferDesc\n", lpcDSCBufferDesc, ppobj );
3840 *ppobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( IDirectSoundCaptureBufferImpl ) );
3842 if ( *ppobj == NULL ) {
3843 return DSERR_OUTOFMEMORY;
3847 ICOM_THIS(IDirectSoundCaptureBufferImpl,*ppobj);
3849 This->ref = 1;
3850 ICOM_VTBL(This) = &dscbvt;
3852 InitializeCriticalSection( &This->lock );
3855 return S_OK;
3859 static HRESULT WINAPI
3860 IDirectSoundCaptureBufferImpl_QueryInterface(
3861 LPDIRECTSOUNDCAPTUREBUFFER iface,
3862 REFIID riid,
3863 LPVOID* ppobj )
3865 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3867 FIXME( "(%p)->(%s,%p): stub\n", This, debugstr_guid(riid), ppobj );
3869 return E_FAIL;
3872 static ULONG WINAPI
3873 IDirectSoundCaptureBufferImpl_AddRef( LPDIRECTSOUNDCAPTUREBUFFER iface )
3875 ULONG uRef;
3876 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3878 EnterCriticalSection( &This->lock );
3880 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3881 uRef = ++(This->ref);
3883 LeaveCriticalSection( &This->lock );
3885 return uRef;
3888 static ULONG WINAPI
3889 IDirectSoundCaptureBufferImpl_Release( LPDIRECTSOUNDCAPTUREBUFFER iface )
3891 ULONG uRef;
3892 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3894 EnterCriticalSection( &This->lock );
3896 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3897 uRef = --(This->ref);
3899 LeaveCriticalSection( &This->lock );
3901 if ( uRef == 0 ) {
3902 DeleteCriticalSection( &This->lock );
3903 HeapFree( GetProcessHeap(), 0, This );
3906 return uRef;
3909 static HRESULT WINAPI
3910 IDirectSoundCaptureBufferImpl_GetCaps(
3911 LPDIRECTSOUNDCAPTUREBUFFER iface,
3912 LPDSCBCAPS lpDSCBCaps )
3914 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3916 FIXME( "(%p)->(%p): stub\n", This, lpDSCBCaps );
3918 return DS_OK;
3921 static HRESULT WINAPI
3922 IDirectSoundCaptureBufferImpl_GetCurrentPosition(
3923 LPDIRECTSOUNDCAPTUREBUFFER iface,
3924 LPDWORD lpdwCapturePosition,
3925 LPDWORD lpdwReadPosition )
3927 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3929 FIXME( "(%p)->(%p,%p): stub\n", This, lpdwCapturePosition, lpdwReadPosition );
3931 return DS_OK;
3934 static HRESULT WINAPI
3935 IDirectSoundCaptureBufferImpl_GetFormat(
3936 LPDIRECTSOUNDCAPTUREBUFFER iface,
3937 LPWAVEFORMATEX lpwfxFormat,
3938 DWORD dwSizeAllocated,
3939 LPDWORD lpdwSizeWritten )
3941 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3943 FIXME( "(%p)->(%p,0x%08lx,%p): stub\n", This, lpwfxFormat, dwSizeAllocated, lpdwSizeWritten );
3945 return DS_OK;
3948 static HRESULT WINAPI
3949 IDirectSoundCaptureBufferImpl_GetStatus(
3950 LPDIRECTSOUNDCAPTUREBUFFER iface,
3951 LPDWORD lpdwStatus )
3953 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3955 FIXME( "(%p)->(%p): stub\n", This, lpdwStatus );
3957 return DS_OK;
3960 static HRESULT WINAPI
3961 IDirectSoundCaptureBufferImpl_Initialize(
3962 LPDIRECTSOUNDCAPTUREBUFFER iface,
3963 LPDIRECTSOUNDCAPTURE lpDSC,
3964 LPCDSCBUFFERDESC lpcDSCBDesc )
3966 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3968 FIXME( "(%p)->(%p,%p): stub\n", This, lpDSC, lpcDSCBDesc );
3970 return DS_OK;
3973 static HRESULT WINAPI
3974 IDirectSoundCaptureBufferImpl_Lock(
3975 LPDIRECTSOUNDCAPTUREBUFFER iface,
3976 DWORD dwReadCusor,
3977 DWORD dwReadBytes,
3978 LPVOID* lplpvAudioPtr1,
3979 LPDWORD lpdwAudioBytes1,
3980 LPVOID* lplpvAudioPtr2,
3981 LPDWORD lpdwAudioBytes2,
3982 DWORD dwFlags )
3984 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3986 FIXME( "(%p)->(%08lu,%08lu,%p,%p,%p,%p,0x%08lx): stub\n", This, dwReadCusor, dwReadBytes, lplpvAudioPtr1, lpdwAudioBytes1, lplpvAudioPtr2, lpdwAudioBytes2, dwFlags );
3988 return DS_OK;
3991 static HRESULT WINAPI
3992 IDirectSoundCaptureBufferImpl_Start(
3993 LPDIRECTSOUNDCAPTUREBUFFER iface,
3994 DWORD dwFlags )
3996 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3998 FIXME( "(%p)->(0x%08lx): stub\n", This, dwFlags );
4000 return DS_OK;
4003 static HRESULT WINAPI
4004 IDirectSoundCaptureBufferImpl_Stop( LPDIRECTSOUNDCAPTUREBUFFER iface )
4006 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4008 FIXME( "(%p): stub\n", This );
4010 return DS_OK;
4013 static HRESULT WINAPI
4014 IDirectSoundCaptureBufferImpl_Unlock(
4015 LPDIRECTSOUNDCAPTUREBUFFER iface,
4016 LPVOID lpvAudioPtr1,
4017 DWORD dwAudioBytes1,
4018 LPVOID lpvAudioPtr2,
4019 DWORD dwAudioBytes2 )
4021 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4023 FIXME( "(%p)->(%p,%08lu,%p,%08lu): stub\n", This, lpvAudioPtr1, dwAudioBytes1, lpvAudioPtr2, dwAudioBytes2 );
4025 return DS_OK;
4029 static ICOM_VTABLE(IDirectSoundCaptureBuffer) dscbvt =
4031 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
4032 /* IUnknown methods */
4033 IDirectSoundCaptureBufferImpl_QueryInterface,
4034 IDirectSoundCaptureBufferImpl_AddRef,
4035 IDirectSoundCaptureBufferImpl_Release,
4037 /* IDirectSoundCaptureBuffer methods */
4038 IDirectSoundCaptureBufferImpl_GetCaps,
4039 IDirectSoundCaptureBufferImpl_GetCurrentPosition,
4040 IDirectSoundCaptureBufferImpl_GetFormat,
4041 IDirectSoundCaptureBufferImpl_GetStatus,
4042 IDirectSoundCaptureBufferImpl_Initialize,
4043 IDirectSoundCaptureBufferImpl_Lock,
4044 IDirectSoundCaptureBufferImpl_Start,
4045 IDirectSoundCaptureBufferImpl_Stop,
4046 IDirectSoundCaptureBufferImpl_Unlock
4049 /*******************************************************************************
4050 * DirectSound ClassFactory
4052 typedef struct
4054 /* IUnknown fields */
4055 ICOM_VFIELD(IClassFactory);
4056 DWORD ref;
4057 } IClassFactoryImpl;
4059 static HRESULT WINAPI
4060 DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
4061 ICOM_THIS(IClassFactoryImpl,iface);
4063 FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
4064 return E_NOINTERFACE;
4067 static ULONG WINAPI
4068 DSCF_AddRef(LPCLASSFACTORY iface) {
4069 ICOM_THIS(IClassFactoryImpl,iface);
4070 return ++(This->ref);
4073 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) {
4074 ICOM_THIS(IClassFactoryImpl,iface);
4075 /* static class, won't be freed */
4076 return --(This->ref);
4079 static HRESULT WINAPI DSCF_CreateInstance(
4080 LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
4082 ICOM_THIS(IClassFactoryImpl,iface);
4084 TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
4085 if ( IsEqualGUID( &IID_IDirectSound, riid ) ) {
4086 /* FIXME: reuse already created dsound if present? */
4087 return DirectSoundCreate(riid,(LPDIRECTSOUND*)ppobj,pOuter);
4089 return E_NOINTERFACE;
4092 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
4093 ICOM_THIS(IClassFactoryImpl,iface);
4094 FIXME("(%p)->(%d),stub!\n",This,dolock);
4095 return S_OK;
4098 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
4099 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
4100 DSCF_QueryInterface,
4101 DSCF_AddRef,
4102 DSCF_Release,
4103 DSCF_CreateInstance,
4104 DSCF_LockServer
4106 static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 };
4108 /*******************************************************************************
4109 * DllGetClassObject [DSOUND.5]
4110 * Retrieves class object from a DLL object
4112 * NOTES
4113 * Docs say returns STDAPI
4115 * PARAMS
4116 * rclsid [I] CLSID for the class object
4117 * riid [I] Reference to identifier of interface for class object
4118 * ppv [O] Address of variable to receive interface pointer for riid
4120 * RETURNS
4121 * Success: S_OK
4122 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
4123 * E_UNEXPECTED
4125 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
4127 TRACE("(%p,%p,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
4128 if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
4129 *ppv = (LPVOID)&DSOUND_CF;
4130 IClassFactory_AddRef((IClassFactory*)*ppv);
4131 return S_OK;
4134 FIXME("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
4135 return CLASS_E_CLASSNOTAVAILABLE;
4139 /*******************************************************************************
4140 * DllCanUnloadNow [DSOUND.4] Determines whether the DLL is in use.
4142 * RETURNS
4143 * Success: S_OK
4144 * Failure: S_FALSE
4146 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
4148 FIXME("(void): stub\n");
4149 return S_FALSE;