Dsound will now try as many devices as are available when trying to
[wine/dcerpc.git] / dlls / dsound / dsound_main.c
blobde8aca187d5b8d80adc02cc8d669fd1af892b95e
1 /* DirectSound
2 *
3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998 Rob Riggs
5 * Copyright 2000-2001 TransGaming Technologies, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * Most thread locking is complete. There may be a few race
23 * conditions still lurking.
25 * Tested with a Soundblaster clone, a Gravis UltraSound Classic,
26 * and a Turtle Beach Tropez+.
28 * TODO:
29 * Implement DirectSoundCapture API
30 * Implement SetCooperativeLevel properly (need to address focus issues)
31 * Implement DirectSound3DBuffers (stubs in place)
32 * Use hardware 3D support if available
33 * Add critical section locking inside Release and AddRef methods
34 * Handle static buffers - put those in hardware, non-static not in hardware
35 * Hardware DuplicateSoundBuffer
36 * Proper volume calculation, and setting volume in HEL primary buffer
37 * Optimize WINMM and negotiate fragment size, decrease DS_HEL_MARGIN
40 #include "config.h"
41 #include <assert.h>
42 #include <stdio.h>
43 #include <sys/types.h>
44 #include <sys/fcntl.h>
45 #include <unistd.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <math.h> /* Insomnia - pow() function */
50 #include "windef.h"
51 #include "winbase.h"
52 #include "wingdi.h"
53 #include "winuser.h"
54 #include "winerror.h"
55 #include "mmsystem.h"
56 #include "mmddk.h"
57 #include "wine/windef16.h"
58 #include "wine/debug.h"
59 #include "dsound.h"
60 #include "dsdriver.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
64 /* these are eligible for tuning... they must be high on slow machines... */
65 /* some stuff may get more responsive with lower values though... */
66 #define DS_EMULDRIVER 1 /* some games (Quake 2, UT) refuse to accept
67 emulated dsound devices. set to 0 ! */
68 #define DS_HEL_FRAGS 48 /* HEL only: number of waveOut fragments in primary buffer
69 * (changing this won't help you) */
70 #define DS_HEL_MARGIN 5 /* HEL only: number of waveOut fragments ahead to mix in new buffers
71 * (keep this close or equal to DS_HEL_QUEUE for best results) */
72 #define DS_HEL_QUEUE 5 /* HEL only: number of waveOut fragments ahead to queue to driver
73 * (this will affect HEL sound reliability and latency) */
75 #define DS_SND_QUEUE_MAX 28 /* max number of fragments to prebuffer */
76 #define DS_SND_QUEUE_MIN 12 /* min number of fragments to prebuffer */
78 /* Linux does not support better timing than 10ms */
79 #define DS_TIME_RES 10 /* Resolution of multimedia timer */
80 #define DS_TIME_DEL 10 /* Delay of multimedia timer callback, and duration of HEL fragment */
82 /*****************************************************************************
83 * Predeclare the interface implementation structures
85 typedef struct IDirectSoundImpl IDirectSoundImpl;
86 typedef struct IDirectSoundBufferImpl IDirectSoundBufferImpl;
87 typedef struct IDirectSoundNotifyImpl IDirectSoundNotifyImpl;
88 typedef struct IDirectSound3DListenerImpl IDirectSound3DListenerImpl;
89 typedef struct IDirectSound3DBufferImpl IDirectSound3DBufferImpl;
90 typedef struct IDirectSoundCaptureImpl IDirectSoundCaptureImpl;
91 typedef struct IDirectSoundCaptureBufferImpl IDirectSoundCaptureBufferImpl;
92 typedef struct IKsPropertySetImpl IKsPropertySetImpl;
94 /*****************************************************************************
95 * IDirectSound implementation structure
97 struct IDirectSoundImpl
99 /* IUnknown fields */
100 ICOM_VFIELD(IDirectSound);
101 DWORD ref;
102 /* IDirectSoundImpl fields */
103 PIDSDRIVER driver;
104 DSDRIVERDESC drvdesc;
105 DSDRIVERCAPS drvcaps;
106 HWAVEOUT hwo;
107 LPWAVEHDR pwave[DS_HEL_FRAGS];
108 UINT timerID, pwplay, pwwrite, pwqueue, prebuf;
109 DWORD fraglen;
110 DWORD priolevel;
111 int nrofbuffers;
112 IDirectSoundBufferImpl** buffers;
113 IDirectSoundBufferImpl* primary;
114 IDirectSound3DListenerImpl* listener;
115 WAVEFORMATEX wfx; /* current main waveformat */
116 CRITICAL_SECTION lock;
119 /*****************************************************************************
120 * IDirectSoundBuffer implementation structure
122 struct IDirectSoundBufferImpl
124 /* FIXME: document */
125 /* IUnknown fields */
126 ICOM_VFIELD(IDirectSoundBuffer);
127 DWORD ref;
128 /* IDirectSoundBufferImpl fields */
129 PIDSDRIVERBUFFER hwbuf;
130 WAVEFORMATEX wfx;
131 LPBYTE buffer;
132 IDirectSound3DBufferImpl* ds3db;
133 DWORD playflags,state,leadin;
134 DWORD playpos,startpos,writelead,buflen;
135 DWORD nAvgBytesPerSec;
136 DWORD freq;
137 DSVOLUMEPAN volpan;
138 IDirectSoundBufferImpl* parent; /* for duplicates */
139 IDirectSoundImpl* dsound;
140 DSBUFFERDESC dsbd;
141 LPDSBPOSITIONNOTIFY notifies;
142 int nrofnotifies;
143 CRITICAL_SECTION lock;
144 /* used for frequency conversion (PerfectPitch) */
145 ULONG freqAdjust, freqAcc;
146 /* used for intelligent (well, sort of) prebuffering */
147 DWORD probably_valid_to;
148 DWORD primary_mixpos, buf_mixpos;
149 BOOL need_remix;
152 #define STATE_STOPPED 0
153 #define STATE_STARTING 1
154 #define STATE_PLAYING 2
155 #define STATE_STOPPING 3
157 /*****************************************************************************
158 * IDirectSoundNotify implementation structure
160 struct IDirectSoundNotifyImpl
162 /* IUnknown fields */
163 ICOM_VFIELD(IDirectSoundNotify);
164 DWORD ref;
165 /* IDirectSoundNotifyImpl fields */
166 IDirectSoundBufferImpl* dsb;
169 /*****************************************************************************
170 * IDirectSound3DListener implementation structure
172 struct IDirectSound3DListenerImpl
174 /* IUnknown fields */
175 ICOM_VFIELD(IDirectSound3DListener);
176 DWORD ref;
177 /* IDirectSound3DListenerImpl fields */
178 IDirectSoundBufferImpl* dsb;
179 DS3DLISTENER ds3dl;
180 CRITICAL_SECTION lock;
183 struct IKsPropertySetImpl
185 /* IUnknown fields */
186 ICOM_VFIELD(IKsPropertySet);
187 DWORD ref;
188 /* IKsPropertySetImpl fields */
189 IDirectSound3DBufferImpl *ds3db; /* backptr, no ref */
192 /*****************************************************************************
193 * IDirectSound3DBuffer implementation structure
195 struct IDirectSound3DBufferImpl
197 /* IUnknown fields */
198 ICOM_VFIELD(IDirectSound3DBuffer);
199 DWORD ref;
200 /* IDirectSound3DBufferImpl fields */
201 IDirectSoundBufferImpl* dsb;
202 DS3DBUFFER ds3db;
203 LPBYTE buffer;
204 DWORD buflen;
205 CRITICAL_SECTION lock;
206 IKsPropertySetImpl* iks;
210 /*****************************************************************************
211 * IDirectSoundCapture implementation structure
213 struct IDirectSoundCaptureImpl
215 /* IUnknown fields */
216 ICOM_VFIELD(IDirectSoundCapture);
217 DWORD ref;
219 /* IDirectSoundCaptureImpl fields */
220 CRITICAL_SECTION lock;
223 /*****************************************************************************
224 * IDirectSoundCapture implementation structure
226 struct IDirectSoundCaptureBufferImpl
228 /* IUnknown fields */
229 ICOM_VFIELD(IDirectSoundCaptureBuffer);
230 DWORD ref;
232 /* IDirectSoundCaptureBufferImpl fields */
233 CRITICAL_SECTION lock;
237 /* #define USE_DSOUND3D 1 */
239 #define DSOUND_FREQSHIFT (14)
241 static IDirectSoundImpl* dsound = NULL;
243 static IDirectSoundBufferImpl* primarybuf = NULL;
245 static void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len);
246 static void DSOUND_MixCancelAt(IDirectSoundBufferImpl *dsb, DWORD buf_writepos);
248 static void DSOUND_WaveQueue(IDirectSoundImpl *dsound, DWORD mixq);
249 static void DSOUND_PerformMix(void);
250 static void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2);
252 static HRESULT DSOUND_CreateDirectSoundCapture( LPVOID* ppobj );
253 static HRESULT DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc, LPVOID* ppobj );
255 static ICOM_VTABLE(IDirectSoundCapture) dscvt;
256 static ICOM_VTABLE(IDirectSoundCaptureBuffer) dscbvt;
258 static HRESULT mmErr(UINT err)
260 switch(err) {
261 case MMSYSERR_NOERROR:
262 return DS_OK;
263 case MMSYSERR_ALLOCATED:
264 return DSERR_ALLOCATED;
265 case MMSYSERR_INVALHANDLE:
266 return DSERR_GENERIC; /* FIXME */
267 case MMSYSERR_NODRIVER:
268 return DSERR_NODRIVER;
269 case MMSYSERR_NOMEM:
270 return DSERR_OUTOFMEMORY;
271 case MMSYSERR_INVALPARAM:
272 return DSERR_INVALIDPARAM;
273 default:
274 FIXME("Unknown MMSYS error %d\n",err);
275 return DSERR_GENERIC;
279 /***************************************************************************
280 * DirectSoundEnumerateA [DSOUND.2]
282 * Enumerate all DirectSound drivers installed in the system
284 * RETURNS
285 * Success: DS_OK
286 * Failure: DSERR_INVALIDPARAM
288 HRESULT WINAPI DirectSoundEnumerateA(
289 LPDSENUMCALLBACKA lpDSEnumCallback,
290 LPVOID lpContext)
292 TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
293 lpDSEnumCallback, lpContext);
295 #ifdef HAVE_OSS
296 if (lpDSEnumCallback != NULL)
297 lpDSEnumCallback(NULL,"WINE DirectSound",
298 "sound",lpContext);
299 #endif
301 return DS_OK;
304 /***************************************************************************
305 * DirectSoundEnumerateW [DSOUND.3]
307 * Enumerate all DirectSound drivers installed in the system
309 * RETURNS
310 * Success: DS_OK
311 * Failure: DSERR_INVALIDPARAM
313 HRESULT WINAPI DirectSoundEnumerateW(
314 LPDSENUMCALLBACKW lpDSEnumCallback,
315 LPVOID lpContext )
317 FIXME("lpDSEnumCallback = %p, lpContext = %p: stub\n",
318 lpDSEnumCallback, lpContext);
320 return DS_OK;
324 static void _dump_DSBCAPS(DWORD xmask) {
325 struct {
326 DWORD mask;
327 char *name;
328 } flags[] = {
329 #define FE(x) { x, #x },
330 FE(DSBCAPS_PRIMARYBUFFER)
331 FE(DSBCAPS_STATIC)
332 FE(DSBCAPS_LOCHARDWARE)
333 FE(DSBCAPS_LOCSOFTWARE)
334 FE(DSBCAPS_CTRL3D)
335 FE(DSBCAPS_CTRLFREQUENCY)
336 FE(DSBCAPS_CTRLPAN)
337 FE(DSBCAPS_CTRLVOLUME)
338 FE(DSBCAPS_CTRLPOSITIONNOTIFY)
339 FE(DSBCAPS_CTRLDEFAULT)
340 FE(DSBCAPS_CTRLALL)
341 FE(DSBCAPS_STICKYFOCUS)
342 FE(DSBCAPS_GLOBALFOCUS)
343 FE(DSBCAPS_GETCURRENTPOSITION2)
344 FE(DSBCAPS_MUTE3DATMAXDISTANCE)
345 #undef FE
347 int i;
349 for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
350 if ((flags[i].mask & xmask) == flags[i].mask)
351 DPRINTF("%s ",flags[i].name);
354 /*******************************************************************************
355 * IKsPropertySet
358 /* IUnknown methods */
359 #ifdef USE_DSOUND3D
360 static HRESULT WINAPI IKsPropertySetImpl_QueryInterface(
361 LPKSPROPERTYSET iface, REFIID riid, LPVOID *ppobj
363 ICOM_THIS(IKsPropertySetImpl,iface);
365 FIXME("(%p,%s,%p), stub!\n",This,debugstr_guid(riid),ppobj);
366 return E_FAIL;
368 #endif
370 #ifdef USE_DSOUND3D
371 static ULONG WINAPI IKsPropertySetImpl_AddRef(LPKSPROPERTYSET iface) {
372 ICOM_THIS(IKsPropertySetImpl,iface);
374 This->ref++;
375 return This->ref;
377 #endif
379 #ifdef USE_DSOUND3D
380 static ULONG WINAPI IKsPropertySetImpl_Release(LPKSPROPERTYSET iface) {
381 ICOM_THIS(IKsPropertySetImpl,iface);
383 This->ref--;
384 return This->ref;
386 #endif
388 #ifdef USE_DSOUND3D
389 static HRESULT WINAPI IKsPropertySetImpl_Get(LPKSPROPERTYSET iface,
390 REFGUID guidPropSet, ULONG dwPropID,
391 LPVOID pInstanceData, ULONG cbInstanceData,
392 LPVOID pPropData, ULONG cbPropData,
393 PULONG pcbReturned
395 ICOM_THIS(IKsPropertySetImpl,iface);
397 FIXME("(%p,%s,%ld,%p,%ld,%p,%ld,%p), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pInstanceData,cbInstanceData,pPropData,cbPropData,pcbReturned);
398 return E_PROP_ID_UNSUPPORTED;
400 #endif
402 #ifdef USE_DSOUND3D
403 static HRESULT WINAPI IKsPropertySetImpl_Set(LPKSPROPERTYSET iface,
404 REFGUID guidPropSet, ULONG dwPropID,
405 LPVOID pInstanceData, ULONG cbInstanceData,
406 LPVOID pPropData, ULONG cbPropData
408 ICOM_THIS(IKsPropertySetImpl,iface);
410 FIXME("(%p,%s,%ld,%p,%ld,%p,%ld), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pInstanceData,cbInstanceData,pPropData,cbPropData);
411 return E_PROP_ID_UNSUPPORTED;
413 #endif
415 #ifdef USE_DSOUND3D
416 static HRESULT WINAPI IKsPropertySetImpl_QuerySupport(LPKSPROPERTYSET iface,
417 REFGUID guidPropSet, ULONG dwPropID, PULONG pTypeSupport
419 ICOM_THIS(IKsPropertySetImpl,iface);
421 FIXME("(%p,%s,%ld,%p), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pTypeSupport);
422 return E_PROP_ID_UNSUPPORTED;
424 #endif
426 #ifdef USE_DSOUND3D
427 static ICOM_VTABLE(IKsPropertySet) iksvt = {
428 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
429 IKsPropertySetImpl_QueryInterface,
430 IKsPropertySetImpl_AddRef,
431 IKsPropertySetImpl_Release,
432 IKsPropertySetImpl_Get,
433 IKsPropertySetImpl_Set,
434 IKsPropertySetImpl_QuerySupport
436 #endif
438 /*******************************************************************************
439 * IDirectSound3DBuffer
442 /* IUnknown methods */
443 #ifdef USE_DSOUND3D
444 static HRESULT WINAPI IDirectSound3DBufferImpl_QueryInterface(
445 LPDIRECTSOUND3DBUFFER iface, REFIID riid, LPVOID *ppobj)
447 ICOM_THIS(IDirectSound3DBufferImpl,iface);
449 if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
450 IDirectSound3DBuffer_AddRef(iface);
451 *ppobj = This->iks;
452 return S_OK;
455 FIXME("(%p,%s,%p), no such interface.\n",This,debugstr_guid(riid),ppobj);
456 return E_FAIL;
458 #endif
460 #ifdef USE_DSOUND3D
461 static ULONG WINAPI IDirectSound3DBufferImpl_AddRef(LPDIRECTSOUND3DBUFFER iface)
463 ICOM_THIS(IDirectSound3DBufferImpl,iface);
464 This->ref++;
465 return This->ref;
467 #endif
469 #ifdef USE_DSOUND3D
470 static ULONG WINAPI IDirectSound3DBufferImpl_Release(LPDIRECTSOUND3DBUFFER iface)
472 ICOM_THIS(IDirectSound3DBufferImpl,iface);
474 TRACE("(%p) ref was %ld\n", This, This->ref);
476 if(--This->ref)
477 return This->ref;
479 if (This->dsb)
480 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
482 DeleteCriticalSection(&This->lock);
484 HeapFree(GetProcessHeap(),0,This->buffer);
485 HeapFree(GetProcessHeap(),0,This);
487 return 0;
489 #endif
491 /* IDirectSound3DBuffer methods */
492 #ifdef USE_DSOUND3D
493 static HRESULT WINAPI IDirectSound3DBufferImpl_GetAllParameters(
494 LPDIRECTSOUND3DBUFFER iface,
495 LPDS3DBUFFER lpDs3dBuffer)
497 FIXME("stub\n");
498 return DS_OK;
500 #endif
502 #ifdef USE_DSOUND3D
503 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeAngles(
504 LPDIRECTSOUND3DBUFFER iface,
505 LPDWORD lpdwInsideConeAngle,
506 LPDWORD lpdwOutsideConeAngle)
508 FIXME("stub\n");
509 return DS_OK;
511 #endif
513 #ifdef USE_DSOUND3D
514 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOrientation(
515 LPDIRECTSOUND3DBUFFER iface,
516 LPD3DVECTOR lpvConeOrientation)
518 FIXME("stub\n");
519 return DS_OK;
521 #endif
523 #ifdef USE_DSOUND3D
524 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOutsideVolume(
525 LPDIRECTSOUND3DBUFFER iface,
526 LPLONG lplConeOutsideVolume)
528 FIXME("stub\n");
529 return DS_OK;
531 #endif
533 #ifdef USE_DSOUND3D
534 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMaxDistance(
535 LPDIRECTSOUND3DBUFFER iface,
536 LPD3DVALUE lpfMaxDistance)
538 FIXME("stub\n");
539 return DS_OK;
541 #endif
543 #ifdef USE_DSOUND3D
544 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMinDistance(
545 LPDIRECTSOUND3DBUFFER iface,
546 LPD3DVALUE lpfMinDistance)
548 FIXME("stub\n");
549 return DS_OK;
551 #endif
553 #ifdef USE_DSOUND3D
554 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMode(
555 LPDIRECTSOUND3DBUFFER iface,
556 LPDWORD lpdwMode)
558 FIXME("stub\n");
559 return DS_OK;
561 #endif
563 #ifdef USE_DSOUND3D
564 static HRESULT WINAPI IDirectSound3DBufferImpl_GetPosition(
565 LPDIRECTSOUND3DBUFFER iface,
566 LPD3DVECTOR lpvPosition)
568 FIXME("stub\n");
569 return DS_OK;
571 #endif
573 #ifdef USE_DSOUND3D
574 static HRESULT WINAPI IDirectSound3DBufferImpl_GetVelocity(
575 LPDIRECTSOUND3DBUFFER iface,
576 LPD3DVECTOR lpvVelocity)
578 FIXME("stub\n");
579 return DS_OK;
581 #endif
583 #ifdef USE_DSOUND3D
584 static HRESULT WINAPI IDirectSound3DBufferImpl_SetAllParameters(
585 LPDIRECTSOUND3DBUFFER iface,
586 LPCDS3DBUFFER lpcDs3dBuffer,
587 DWORD dwApply)
589 FIXME("stub\n");
590 return DS_OK;
592 #endif
594 #ifdef USE_DSOUND3D
595 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeAngles(
596 LPDIRECTSOUND3DBUFFER iface,
597 DWORD dwInsideConeAngle,
598 DWORD dwOutsideConeAngle,
599 DWORD dwApply)
601 FIXME("stub\n");
602 return DS_OK;
604 #endif
606 #ifdef USE_DSOUND3D
607 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOrientation(
608 LPDIRECTSOUND3DBUFFER iface,
609 D3DVALUE x, D3DVALUE y, D3DVALUE z,
610 DWORD dwApply)
612 FIXME("stub\n");
613 return DS_OK;
615 #endif
617 #ifdef USE_DSOUND3D
618 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOutsideVolume(
619 LPDIRECTSOUND3DBUFFER iface,
620 LONG lConeOutsideVolume,
621 DWORD dwApply)
623 FIXME("stub\n");
624 return DS_OK;
626 #endif
628 #ifdef USE_DSOUND3D
629 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMaxDistance(
630 LPDIRECTSOUND3DBUFFER iface,
631 D3DVALUE fMaxDistance,
632 DWORD dwApply)
634 FIXME("stub\n");
635 return DS_OK;
637 #endif
639 #ifdef USE_DSOUND3D
640 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMinDistance(
641 LPDIRECTSOUND3DBUFFER iface,
642 D3DVALUE fMinDistance,
643 DWORD dwApply)
645 FIXME("stub\n");
646 return DS_OK;
648 #endif
650 #ifdef USE_DSOUND3D
651 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMode(
652 LPDIRECTSOUND3DBUFFER iface,
653 DWORD dwMode,
654 DWORD dwApply)
656 ICOM_THIS(IDirectSound3DBufferImpl,iface);
657 TRACE("mode = %lx\n", dwMode);
658 This->ds3db.dwMode = dwMode;
659 return DS_OK;
661 #endif
663 #ifdef USE_DSOUND3D
664 static HRESULT WINAPI IDirectSound3DBufferImpl_SetPosition(
665 LPDIRECTSOUND3DBUFFER iface,
666 D3DVALUE x, D3DVALUE y, D3DVALUE z,
667 DWORD dwApply)
669 FIXME("stub\n");
670 return DS_OK;
672 #endif
674 #ifdef USE_DSOUND3D
675 static HRESULT WINAPI IDirectSound3DBufferImpl_SetVelocity(
676 LPDIRECTSOUND3DBUFFER iface,
677 D3DVALUE x, D3DVALUE y, D3DVALUE z,
678 DWORD dwApply)
680 FIXME("stub\n");
681 return DS_OK;
683 #endif
685 #ifdef USE_DSOUND3D
686 static ICOM_VTABLE(IDirectSound3DBuffer) ds3dbvt =
688 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
689 /* IUnknown methods */
690 IDirectSound3DBufferImpl_QueryInterface,
691 IDirectSound3DBufferImpl_AddRef,
692 IDirectSound3DBufferImpl_Release,
693 /* IDirectSound3DBuffer methods */
694 IDirectSound3DBufferImpl_GetAllParameters,
695 IDirectSound3DBufferImpl_GetConeAngles,
696 IDirectSound3DBufferImpl_GetConeOrientation,
697 IDirectSound3DBufferImpl_GetConeOutsideVolume,
698 IDirectSound3DBufferImpl_GetMaxDistance,
699 IDirectSound3DBufferImpl_GetMinDistance,
700 IDirectSound3DBufferImpl_GetMode,
701 IDirectSound3DBufferImpl_GetPosition,
702 IDirectSound3DBufferImpl_GetVelocity,
703 IDirectSound3DBufferImpl_SetAllParameters,
704 IDirectSound3DBufferImpl_SetConeAngles,
705 IDirectSound3DBufferImpl_SetConeOrientation,
706 IDirectSound3DBufferImpl_SetConeOutsideVolume,
707 IDirectSound3DBufferImpl_SetMaxDistance,
708 IDirectSound3DBufferImpl_SetMinDistance,
709 IDirectSound3DBufferImpl_SetMode,
710 IDirectSound3DBufferImpl_SetPosition,
711 IDirectSound3DBufferImpl_SetVelocity,
713 #endif
715 #ifdef USE_DSOUND3D
716 static int DSOUND_Create3DBuffer(IDirectSoundBufferImpl* dsb)
718 DWORD i, temp, iSize, oSize, offset;
719 LPBYTE bIbuf, bObuf, bTbuf = NULL;
720 LPWORD wIbuf, wObuf, wTbuf = NULL;
722 /* Inside DirectX says it's stupid but allowed */
723 if (dsb->wfx.nChannels == 2) {
724 /* Convert to mono */
725 if (dsb->wfx.wBitsPerSample == 16) {
726 iSize = dsb->buflen / 4;
727 wTbuf = malloc(dsb->buflen / 2);
728 if (wTbuf == NULL)
729 return DSERR_OUTOFMEMORY;
730 for (i = 0; i < iSize; i++)
731 wTbuf[i] = (dsb->buffer[i * 2] + dsb->buffer[(i * 2) + 1]) / 2;
732 wIbuf = wTbuf;
733 } else {
734 iSize = dsb->buflen / 2;
735 bTbuf = malloc(dsb->buflen / 2);
736 if (bTbuf == NULL)
737 return DSERR_OUTOFMEMORY;
738 for (i = 0; i < iSize; i++)
739 bTbuf[i] = (dsb->buffer[i * 2] + dsb->buffer[(i * 2) + 1]) / 2;
740 bIbuf = bTbuf;
742 } else {
743 if (dsb->wfx.wBitsPerSample == 16) {
744 iSize = dsb->buflen / 2;
745 wIbuf = (LPWORD) dsb->buffer;
746 } else {
747 bIbuf = (LPBYTE) dsb->buffer;
748 iSize = dsb->buflen;
752 if (primarybuf->wfx.wBitsPerSample == 16) {
753 wObuf = (LPWORD) dsb->ds3db->buffer;
754 oSize = dsb->ds3db->buflen / 2;
755 } else {
756 bObuf = (LPBYTE) dsb->ds3db->buffer;
757 oSize = dsb->ds3db->buflen;
760 offset = primarybuf->wfx.nSamplesPerSec / 100; /* 10ms */
761 if (primarybuf->wfx.wBitsPerSample == 16 && dsb->wfx.wBitsPerSample == 16)
762 for (i = 0; i < iSize; i++) {
763 temp = wIbuf[i];
764 if (i >= offset)
765 temp += wIbuf[i - offset] >> 9;
766 else
767 temp += wIbuf[i + iSize - offset] >> 9;
768 wObuf[i * 2] = temp;
769 wObuf[(i * 2) + 1] = temp;
771 else if (primarybuf->wfx.wBitsPerSample == 8 && dsb->wfx.wBitsPerSample == 8)
772 for (i = 0; i < iSize; i++) {
773 temp = bIbuf[i];
774 if (i >= offset)
775 temp += bIbuf[i - offset] >> 5;
776 else
777 temp += bIbuf[i + iSize - offset] >> 5;
778 bObuf[i * 2] = temp;
779 bObuf[(i * 2) + 1] = temp;
782 if (wTbuf)
783 free(wTbuf);
784 if (bTbuf)
785 free(bTbuf);
787 return DS_OK;
789 #endif
790 /*******************************************************************************
791 * IDirectSound3DListener
794 /* IUnknown methods */
795 static HRESULT WINAPI IDirectSound3DListenerImpl_QueryInterface(
796 LPDIRECTSOUND3DLISTENER iface, REFIID riid, LPVOID *ppobj)
798 ICOM_THIS(IDirectSound3DListenerImpl,iface);
800 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
801 return E_FAIL;
804 static ULONG WINAPI IDirectSound3DListenerImpl_AddRef(LPDIRECTSOUND3DLISTENER iface)
806 ICOM_THIS(IDirectSound3DListenerImpl,iface);
807 This->ref++;
808 return This->ref;
811 static ULONG WINAPI IDirectSound3DListenerImpl_Release(LPDIRECTSOUND3DLISTENER iface)
813 ULONG ulReturn;
814 ICOM_THIS(IDirectSound3DListenerImpl,iface);
816 TRACE("(%p) ref was %ld\n", This, This->ref);
818 ulReturn = --This->ref;
820 /* Free all resources */
821 if( ulReturn == 0 ) {
822 if(This->dsb)
823 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
824 DeleteCriticalSection(&This->lock);
825 HeapFree(GetProcessHeap(),0,This);
828 return ulReturn;
831 /* IDirectSound3DListener methods */
832 static HRESULT WINAPI IDirectSound3DListenerImpl_GetAllParameter(
833 LPDIRECTSOUND3DLISTENER iface,
834 LPDS3DLISTENER lpDS3DL)
836 FIXME("stub\n");
837 return DS_OK;
840 static HRESULT WINAPI IDirectSound3DListenerImpl_GetDistanceFactor(
841 LPDIRECTSOUND3DLISTENER iface,
842 LPD3DVALUE lpfDistanceFactor)
844 FIXME("stub\n");
845 return DS_OK;
848 static HRESULT WINAPI IDirectSound3DListenerImpl_GetDopplerFactor(
849 LPDIRECTSOUND3DLISTENER iface,
850 LPD3DVALUE lpfDopplerFactor)
852 FIXME("stub\n");
853 return DS_OK;
856 static HRESULT WINAPI IDirectSound3DListenerImpl_GetOrientation(
857 LPDIRECTSOUND3DLISTENER iface,
858 LPD3DVECTOR lpvOrientFront,
859 LPD3DVECTOR lpvOrientTop)
861 FIXME("stub\n");
862 return DS_OK;
865 static HRESULT WINAPI IDirectSound3DListenerImpl_GetPosition(
866 LPDIRECTSOUND3DLISTENER iface,
867 LPD3DVECTOR lpvPosition)
869 FIXME("stub\n");
870 return DS_OK;
873 static HRESULT WINAPI IDirectSound3DListenerImpl_GetRolloffFactor(
874 LPDIRECTSOUND3DLISTENER iface,
875 LPD3DVALUE lpfRolloffFactor)
877 FIXME("stub\n");
878 return DS_OK;
881 static HRESULT WINAPI IDirectSound3DListenerImpl_GetVelocity(
882 LPDIRECTSOUND3DLISTENER iface,
883 LPD3DVECTOR lpvVelocity)
885 FIXME("stub\n");
886 return DS_OK;
889 static HRESULT WINAPI IDirectSound3DListenerImpl_SetAllParameters(
890 LPDIRECTSOUND3DLISTENER iface,
891 LPCDS3DLISTENER lpcDS3DL,
892 DWORD dwApply)
894 FIXME("stub\n");
895 return DS_OK;
898 static HRESULT WINAPI IDirectSound3DListenerImpl_SetDistanceFactor(
899 LPDIRECTSOUND3DLISTENER iface,
900 D3DVALUE fDistanceFactor,
901 DWORD dwApply)
903 FIXME("stub\n");
904 return DS_OK;
907 static HRESULT WINAPI IDirectSound3DListenerImpl_SetDopplerFactor(
908 LPDIRECTSOUND3DLISTENER iface,
909 D3DVALUE fDopplerFactor,
910 DWORD dwApply)
912 FIXME("stub\n");
913 return DS_OK;
916 static HRESULT WINAPI IDirectSound3DListenerImpl_SetOrientation(
917 LPDIRECTSOUND3DLISTENER iface,
918 D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront,
919 D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop,
920 DWORD dwApply)
922 FIXME("stub\n");
923 return DS_OK;
926 static HRESULT WINAPI IDirectSound3DListenerImpl_SetPosition(
927 LPDIRECTSOUND3DLISTENER iface,
928 D3DVALUE x, D3DVALUE y, D3DVALUE z,
929 DWORD dwApply)
931 FIXME("stub\n");
932 return DS_OK;
935 static HRESULT WINAPI IDirectSound3DListenerImpl_SetRolloffFactor(
936 LPDIRECTSOUND3DLISTENER iface,
937 D3DVALUE fRolloffFactor,
938 DWORD dwApply)
940 FIXME("stub\n");
941 return DS_OK;
944 static HRESULT WINAPI IDirectSound3DListenerImpl_SetVelocity(
945 LPDIRECTSOUND3DLISTENER iface,
946 D3DVALUE x, D3DVALUE y, D3DVALUE z,
947 DWORD dwApply)
949 FIXME("stub\n");
950 return DS_OK;
953 static HRESULT WINAPI IDirectSound3DListenerImpl_CommitDeferredSettings(
954 LPDIRECTSOUND3DLISTENER iface)
957 FIXME("stub\n");
958 return DS_OK;
961 static ICOM_VTABLE(IDirectSound3DListener) ds3dlvt =
963 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
964 /* IUnknown methods */
965 IDirectSound3DListenerImpl_QueryInterface,
966 IDirectSound3DListenerImpl_AddRef,
967 IDirectSound3DListenerImpl_Release,
968 /* IDirectSound3DListener methods */
969 IDirectSound3DListenerImpl_GetAllParameter,
970 IDirectSound3DListenerImpl_GetDistanceFactor,
971 IDirectSound3DListenerImpl_GetDopplerFactor,
972 IDirectSound3DListenerImpl_GetOrientation,
973 IDirectSound3DListenerImpl_GetPosition,
974 IDirectSound3DListenerImpl_GetRolloffFactor,
975 IDirectSound3DListenerImpl_GetVelocity,
976 IDirectSound3DListenerImpl_SetAllParameters,
977 IDirectSound3DListenerImpl_SetDistanceFactor,
978 IDirectSound3DListenerImpl_SetDopplerFactor,
979 IDirectSound3DListenerImpl_SetOrientation,
980 IDirectSound3DListenerImpl_SetPosition,
981 IDirectSound3DListenerImpl_SetRolloffFactor,
982 IDirectSound3DListenerImpl_SetVelocity,
983 IDirectSound3DListenerImpl_CommitDeferredSettings,
986 /*******************************************************************************
987 * IDirectSoundNotify
989 static HRESULT WINAPI IDirectSoundNotifyImpl_QueryInterface(
990 LPDIRECTSOUNDNOTIFY iface,REFIID riid,LPVOID *ppobj
992 ICOM_THIS(IDirectSoundNotifyImpl,iface);
994 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
995 return E_FAIL;
998 static ULONG WINAPI IDirectSoundNotifyImpl_AddRef(LPDIRECTSOUNDNOTIFY iface) {
999 ICOM_THIS(IDirectSoundNotifyImpl,iface);
1000 return ++(This->ref);
1003 static ULONG WINAPI IDirectSoundNotifyImpl_Release(LPDIRECTSOUNDNOTIFY iface) {
1004 ICOM_THIS(IDirectSoundNotifyImpl,iface);
1006 TRACE("(%p) ref was %ld\n", This, This->ref);
1008 This->ref--;
1009 if (!This->ref) {
1010 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
1011 HeapFree(GetProcessHeap(),0,This);
1012 return 0;
1014 return This->ref;
1017 static HRESULT WINAPI IDirectSoundNotifyImpl_SetNotificationPositions(
1018 LPDIRECTSOUNDNOTIFY iface,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
1020 ICOM_THIS(IDirectSoundNotifyImpl,iface);
1021 int i;
1023 if (TRACE_ON(dsound)) {
1024 TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);
1025 for (i=0;i<howmuch;i++)
1026 TRACE("notify at %ld to 0x%08lx\n",
1027 notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
1029 This->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,This->dsb->notifies,(This->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
1030 memcpy( This->dsb->notifies+This->dsb->nrofnotifies,
1031 notify,
1032 howmuch*sizeof(DSBPOSITIONNOTIFY)
1034 This->dsb->nrofnotifies+=howmuch;
1036 return S_OK;
1039 static ICOM_VTABLE(IDirectSoundNotify) dsnvt =
1041 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1042 IDirectSoundNotifyImpl_QueryInterface,
1043 IDirectSoundNotifyImpl_AddRef,
1044 IDirectSoundNotifyImpl_Release,
1045 IDirectSoundNotifyImpl_SetNotificationPositions,
1048 /*******************************************************************************
1049 * IDirectSoundBuffer
1052 static void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan)
1054 double temp;
1056 /* the AmpFactors are expressed in 16.16 fixed point */
1057 volpan->dwVolAmpFactor = (ULONG) (pow(2.0, volpan->lVolume / 600.0) * 65536);
1058 /* FIXME: dwPan{Left|Right}AmpFactor */
1060 /* FIXME: use calculated vol and pan ampfactors */
1061 temp = (double) (volpan->lVolume - (volpan->lPan > 0 ? volpan->lPan : 0));
1062 volpan->dwTotalLeftAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 65536);
1063 temp = (double) (volpan->lVolume + (volpan->lPan < 0 ? volpan->lPan : 0));
1064 volpan->dwTotalRightAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 65536);
1066 TRACE("left = %lx, right = %lx\n", volpan->dwTotalLeftAmpFactor, volpan->dwTotalRightAmpFactor);
1069 static void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
1071 DWORD sw;
1073 sw = dsb->wfx.nChannels * (dsb->wfx.wBitsPerSample / 8);
1074 if ((dsb->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && dsb->hwbuf) {
1075 DWORD fraglen;
1076 /* let fragment size approximate the timer delay */
1077 fraglen = (dsb->freq * DS_TIME_DEL / 1000) * sw;
1078 /* reduce fragment size until an integer number of them fits in the buffer */
1079 /* (FIXME: this may or may not be a good idea) */
1080 while (dsb->buflen % fraglen) fraglen -= sw;
1081 dsb->dsound->fraglen = fraglen;
1082 TRACE("fraglen=%ld\n", dsb->dsound->fraglen);
1084 /* calculate the 10ms write lead */
1085 dsb->writelead = (dsb->freq / 100) * sw;
1088 static HRESULT DSOUND_PrimaryOpen(IDirectSoundBufferImpl *dsb)
1090 HRESULT err = DS_OK;
1092 /* are we using waveOut stuff? */
1093 if (!dsb->hwbuf) {
1094 LPBYTE newbuf;
1095 DWORD buflen;
1096 HRESULT merr = DS_OK;
1097 /* Start in pause mode, to allow buffers to get filled */
1098 waveOutPause(dsb->dsound->hwo);
1099 if (dsb->state == STATE_PLAYING) dsb->state = STATE_STARTING;
1100 else if (dsb->state == STATE_STOPPING) dsb->state = STATE_STOPPED;
1101 /* use fragments of 10ms (1/100s) each (which should get us within
1102 * the documented write cursor lead of 10-15ms) */
1103 buflen = ((dsb->wfx.nAvgBytesPerSec / 100) & ~3) * DS_HEL_FRAGS;
1104 TRACE("desired buflen=%ld, old buffer=%p\n", buflen, dsb->buffer);
1105 /* reallocate emulated primary buffer */
1106 newbuf = (LPBYTE)HeapReAlloc(GetProcessHeap(),0,dsb->buffer,buflen);
1107 if (newbuf == NULL) {
1108 ERR("failed to allocate primary buffer\n");
1109 merr = DSERR_OUTOFMEMORY;
1110 /* but the old buffer might still exists and must be re-prepared */
1111 } else {
1112 dsb->buffer = newbuf;
1113 dsb->buflen = buflen;
1115 if (dsb->buffer) {
1116 unsigned c;
1117 IDirectSoundImpl *ds = dsb->dsound;
1119 ds->fraglen = dsb->buflen / DS_HEL_FRAGS;
1121 /* prepare fragment headers */
1122 for (c=0; c<DS_HEL_FRAGS; c++) {
1123 ds->pwave[c]->lpData = dsb->buffer + c*ds->fraglen;
1124 ds->pwave[c]->dwBufferLength = ds->fraglen;
1125 ds->pwave[c]->dwUser = (DWORD)dsb;
1126 ds->pwave[c]->dwFlags = 0;
1127 ds->pwave[c]->dwLoops = 0;
1128 err = mmErr(waveOutPrepareHeader(ds->hwo,ds->pwave[c],sizeof(WAVEHDR)));
1129 if (err != DS_OK) {
1130 while (c--)
1131 waveOutUnprepareHeader(ds->hwo,ds->pwave[c],sizeof(WAVEHDR));
1132 break;
1136 ds->pwplay = 0;
1137 ds->pwwrite = 0;
1138 ds->pwqueue = 0;
1139 memset(dsb->buffer, (dsb->wfx.wBitsPerSample == 16) ? 0 : 128, dsb->buflen);
1140 TRACE("fraglen=%ld\n", ds->fraglen);
1141 DSOUND_WaveQueue(dsb->dsound, (DWORD)-1);
1143 if ((err == DS_OK) && (merr != DS_OK))
1144 err = merr;
1146 return err;
1150 static void DSOUND_PrimaryClose(IDirectSoundBufferImpl *dsb)
1152 /* are we using waveOut stuff? */
1153 if (!dsb->hwbuf) {
1154 unsigned c;
1155 IDirectSoundImpl *ds = dsb->dsound;
1157 ds->pwqueue = (DWORD)-1; /* resetting queues */
1158 waveOutReset(ds->hwo);
1159 for (c=0; c<DS_HEL_FRAGS; c++)
1160 waveOutUnprepareHeader(ds->hwo, ds->pwave[c], sizeof(WAVEHDR));
1161 ds->pwqueue = 0;
1165 static HRESULT DSOUND_PrimaryPlay(IDirectSoundBufferImpl *dsb)
1167 HRESULT err = DS_OK;
1168 if (dsb->hwbuf)
1169 err = IDsDriverBuffer_Play(dsb->hwbuf, 0, 0, DSBPLAY_LOOPING);
1170 else
1171 err = mmErr(waveOutRestart(dsb->dsound->hwo));
1172 return err;
1175 static HRESULT DSOUND_PrimaryStop(IDirectSoundBufferImpl *dsb)
1177 HRESULT err = DS_OK;
1179 TRACE("\n");
1181 if (dsb->hwbuf) {
1182 err = IDsDriverBuffer_Stop(dsb->hwbuf);
1183 if (err == DSERR_BUFFERLOST) {
1184 /* Wine-only: the driver wants us to reopen the device */
1185 /* FIXME: check for errors */
1186 IDsDriverBuffer_Release(primarybuf->hwbuf);
1187 waveOutClose(dsb->dsound->hwo);
1188 dsb->dsound->hwo = 0;
1189 waveOutOpen(&(dsb->dsound->hwo), dsb->dsound->drvdesc.dnDevNode,
1190 &(primarybuf->wfx), (DWORD)DSOUND_callback, (DWORD)dsb->dsound,
1191 CALLBACK_FUNCTION | WAVE_DIRECTSOUND);
1192 err = IDsDriver_CreateSoundBuffer(dsb->dsound->driver,&(dsb->wfx),dsb->dsbd.dwFlags,0,
1193 &(dsb->buflen),&(dsb->buffer),
1194 (LPVOID)&(dsb->hwbuf));
1197 else
1198 err = mmErr(waveOutPause(dsb->dsound->hwo));
1199 return err;
1202 /* This sets this format for the <em>Primary Buffer Only</em> */
1203 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
1204 static HRESULT WINAPI IDirectSoundBufferImpl_SetFormat(
1205 LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX wfex
1207 ICOM_THIS(IDirectSoundBufferImpl,iface);
1208 IDirectSoundBufferImpl** dsb;
1209 HRESULT err = DS_OK;
1210 int i;
1212 /* Let's be pedantic! */
1213 if ((wfex == NULL) ||
1214 (wfex->wFormatTag != WAVE_FORMAT_PCM) ||
1215 (wfex->nChannels < 1) || (wfex->nChannels > 2) ||
1216 (wfex->nSamplesPerSec < 1) ||
1217 (wfex->nBlockAlign < 1) || (wfex->nChannels > 4) ||
1218 ((wfex->wBitsPerSample != 8) && (wfex->wBitsPerSample != 16))) {
1219 TRACE("failed pedantic check!\n");
1220 return DSERR_INVALIDPARAM;
1223 /* **** */
1224 EnterCriticalSection(&(This->dsound->lock));
1226 if (primarybuf->wfx.nSamplesPerSec != wfex->nSamplesPerSec) {
1227 dsb = dsound->buffers;
1228 for (i = 0; i < dsound->nrofbuffers; i++, dsb++) {
1229 /* **** */
1230 EnterCriticalSection(&((*dsb)->lock));
1232 (*dsb)->freqAdjust = ((*dsb)->freq << DSOUND_FREQSHIFT) /
1233 wfex->nSamplesPerSec;
1235 LeaveCriticalSection(&((*dsb)->lock));
1236 /* **** */
1240 memcpy(&(primarybuf->wfx), wfex, sizeof(primarybuf->wfx));
1242 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
1243 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1244 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
1245 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
1246 wfex->wBitsPerSample, wfex->cbSize);
1248 primarybuf->wfx.nAvgBytesPerSec =
1249 This->wfx.nSamplesPerSec * This->wfx.nBlockAlign;
1251 if (primarybuf->dsound->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) {
1252 /* FIXME: check for errors */
1253 DSOUND_PrimaryClose(primarybuf);
1254 waveOutClose(This->dsound->hwo);
1255 This->dsound->hwo = 0;
1256 waveOutOpen(&(This->dsound->hwo), This->dsound->drvdesc.dnDevNode,
1257 &(primarybuf->wfx), (DWORD)DSOUND_callback, (DWORD)This->dsound,
1258 CALLBACK_FUNCTION | WAVE_DIRECTSOUND);
1259 DSOUND_PrimaryOpen(primarybuf);
1261 if (primarybuf->hwbuf) {
1262 err = IDsDriverBuffer_SetFormat(primarybuf->hwbuf, &(primarybuf->wfx));
1263 if (err == DSERR_BUFFERLOST) {
1264 /* Wine-only: the driver wants us to recreate the HW buffer */
1265 IDsDriverBuffer_Release(primarybuf->hwbuf);
1266 err = IDsDriver_CreateSoundBuffer(primarybuf->dsound->driver,&(primarybuf->wfx),primarybuf->dsbd.dwFlags,0,
1267 &(primarybuf->buflen),&(primarybuf->buffer),
1268 (LPVOID)&(primarybuf->hwbuf));
1269 if (primarybuf->state == STATE_PLAYING) primarybuf->state = STATE_STARTING;
1270 else if (primarybuf->state == STATE_STOPPING) primarybuf->state = STATE_STOPPED;
1273 DSOUND_RecalcFormat(primarybuf);
1275 LeaveCriticalSection(&(This->dsound->lock));
1276 /* **** */
1278 return DS_OK;
1281 static HRESULT WINAPI IDirectSoundBufferImpl_SetVolume(
1282 LPDIRECTSOUNDBUFFER iface,LONG vol
1284 ICOM_THIS(IDirectSoundBufferImpl,iface);
1286 TRACE("(%p,%ld)\n",This,vol);
1288 /* I'm not sure if we need this for primary buffer */
1289 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
1290 return DSERR_CONTROLUNAVAIL;
1292 if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN))
1293 return DSERR_INVALIDPARAM;
1295 /* **** */
1296 EnterCriticalSection(&(This->lock));
1298 This->volpan.lVolume = vol;
1300 DSOUND_RecalcVolPan(&(This->volpan));
1302 if (This->hwbuf) {
1303 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
1305 else if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
1306 #if 0 /* should we really do this? */
1307 /* the DS volume ranges from 0 (max, 0dB attenuation) to -10000 (min, 100dB attenuation) */
1308 /* the MM volume ranges from 0 to 0xffff in an unspecified logarithmic scale */
1309 WORD cvol = 0xffff + vol*6 + vol/2;
1310 DWORD vol = cvol | ((DWORD)cvol << 16)
1311 waveOutSetVolume(This->dsound->hwo, vol);
1312 #endif
1315 LeaveCriticalSection(&(This->lock));
1316 /* **** */
1318 return DS_OK;
1321 static HRESULT WINAPI IDirectSoundBufferImpl_GetVolume(
1322 LPDIRECTSOUNDBUFFER iface,LPLONG vol
1324 ICOM_THIS(IDirectSoundBufferImpl,iface);
1325 TRACE("(%p,%p)\n",This,vol);
1327 if (vol == NULL)
1328 return DSERR_INVALIDPARAM;
1330 *vol = This->volpan.lVolume;
1331 return DS_OK;
1334 static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency(
1335 LPDIRECTSOUNDBUFFER iface,DWORD freq
1337 ICOM_THIS(IDirectSoundBufferImpl,iface);
1338 TRACE("(%p,%ld)\n",This,freq);
1340 /* You cannot set the frequency of the primary buffer */
1341 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY) ||
1342 (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
1343 return DSERR_CONTROLUNAVAIL;
1345 if (!freq) freq = This->wfx.nSamplesPerSec;
1347 if ((freq < DSBFREQUENCY_MIN) || (freq > DSBFREQUENCY_MAX))
1348 return DSERR_INVALIDPARAM;
1350 /* **** */
1351 EnterCriticalSection(&(This->lock));
1353 This->freq = freq;
1354 This->freqAdjust = (freq << DSOUND_FREQSHIFT) / primarybuf->wfx.nSamplesPerSec;
1355 This->nAvgBytesPerSec = freq * This->wfx.nBlockAlign;
1356 DSOUND_RecalcFormat(This);
1358 LeaveCriticalSection(&(This->lock));
1359 /* **** */
1361 return DS_OK;
1364 static HRESULT WINAPI IDirectSoundBufferImpl_Play(
1365 LPDIRECTSOUNDBUFFER iface,DWORD reserved1,DWORD reserved2,DWORD flags
1367 ICOM_THIS(IDirectSoundBufferImpl,iface);
1368 TRACE("(%p,%08lx,%08lx,%08lx)\n",
1369 This,reserved1,reserved2,flags
1372 /* **** */
1373 EnterCriticalSection(&(This->lock));
1375 This->playflags = flags;
1376 if (This->state == STATE_STOPPED) {
1377 This->leadin = TRUE;
1378 This->startpos = This->buf_mixpos;
1379 This->state = STATE_STARTING;
1380 } else if (This->state == STATE_STOPPING)
1381 This->state = STATE_PLAYING;
1382 if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
1383 IDsDriverBuffer_Play(This->hwbuf, 0, 0, This->playflags);
1384 This->state = STATE_PLAYING;
1387 LeaveCriticalSection(&(This->lock));
1388 /* **** */
1390 return DS_OK;
1393 static HRESULT WINAPI IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface)
1395 ICOM_THIS(IDirectSoundBufferImpl,iface);
1396 TRACE("(%p)\n",This);
1398 /* **** */
1399 EnterCriticalSection(&(This->lock));
1401 if (This->state == STATE_PLAYING)
1402 This->state = STATE_STOPPING;
1403 else if (This->state == STATE_STARTING)
1404 This->state = STATE_STOPPED;
1405 if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
1406 IDsDriverBuffer_Stop(This->hwbuf);
1407 This->state = STATE_STOPPED;
1409 DSOUND_CheckEvent(This, 0);
1411 LeaveCriticalSection(&(This->lock));
1412 /* **** */
1414 return DS_OK;
1417 static DWORD WINAPI IDirectSoundBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface) {
1418 ICOM_THIS(IDirectSoundBufferImpl,iface);
1419 DWORD ref;
1421 TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
1423 ref = InterlockedIncrement(&(This->ref));
1424 if (!ref) {
1425 FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n");
1427 return ref;
1429 static DWORD WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER iface) {
1430 ICOM_THIS(IDirectSoundBufferImpl,iface);
1431 int i;
1432 DWORD ref;
1434 TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
1436 ref = InterlockedDecrement(&(This->ref));
1437 if (ref) return ref;
1439 EnterCriticalSection(&(This->dsound->lock));
1440 for (i=0;i<This->dsound->nrofbuffers;i++)
1441 if (This->dsound->buffers[i] == This)
1442 break;
1444 if (i < This->dsound->nrofbuffers) {
1445 /* Put the last buffer of the list in the (now empty) position */
1446 This->dsound->buffers[i] = This->dsound->buffers[This->dsound->nrofbuffers - 1];
1447 This->dsound->nrofbuffers--;
1448 This->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,This->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*This->dsound->nrofbuffers);
1449 TRACE("buffer count is now %d\n", This->dsound->nrofbuffers);
1450 IDirectSound_Release((LPDIRECTSOUND)This->dsound);
1452 LeaveCriticalSection(&(This->dsound->lock));
1454 DeleteCriticalSection(&(This->lock));
1455 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1456 DSOUND_PrimaryClose(This);
1457 if (This->hwbuf) {
1458 IDsDriverBuffer_Release(This->hwbuf);
1460 if (This->ds3db)
1461 IDirectSound3DBuffer_Release((LPDIRECTSOUND3DBUFFER)This->ds3db);
1462 if (This->parent)
1463 /* this is a duplicate buffer */
1464 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->parent);
1465 else
1466 /* this is a toplevel buffer */
1467 HeapFree(GetProcessHeap(),0,This->buffer);
1469 HeapFree(GetProcessHeap(),0,This);
1471 if (This == primarybuf)
1472 primarybuf = NULL;
1474 return 0;
1477 static DWORD DSOUND_CalcPlayPosition(IDirectSoundBufferImpl *This,
1478 DWORD state, DWORD pplay, DWORD pwrite, DWORD pmix, DWORD bmix)
1480 DWORD bplay;
1482 TRACE("primary playpos=%ld, mixpos=%ld\n", pplay, pmix);
1483 TRACE("this mixpos=%ld, time=%ld\n", bmix, GetTickCount());
1485 /* the actual primary play position (pplay) is always behind last mixed (pmix),
1486 * unless the computer is too slow or something */
1487 /* we need to know how far away we are from there */
1488 #if 0 /* we'll never fill the primary entirely */
1489 if (pmix == pplay) {
1490 if ((state == STATE_PLAYING) || (state == STATE_STOPPING)) {
1491 /* wow, the software mixer is really doing well,
1492 * seems the entire primary buffer is filled! */
1493 pmix += primarybuf->buflen;
1495 /* else: the primary buffer is not playing, so probably empty */
1497 #endif
1498 if (pmix < pplay) pmix += primarybuf->buflen; /* wraparound */
1499 pmix -= pplay;
1500 /* detect buffer underrun */
1501 if (pwrite < pplay) pwrite += primarybuf->buflen; /* wraparound */
1502 pwrite -= pplay;
1503 if (pmix > (DS_SND_QUEUE_MAX * primarybuf->dsound->fraglen + pwrite + primarybuf->writelead)) {
1504 WARN("detected an underrun: primary queue was %ld\n",pmix);
1505 pmix = 0;
1507 /* divide the offset by its sample size */
1508 pmix /= primarybuf->wfx.nBlockAlign;
1509 TRACE("primary back-samples=%ld\n",pmix);
1510 /* adjust for our frequency */
1511 pmix = (pmix * This->freqAdjust) >> DSOUND_FREQSHIFT;
1512 /* multiply by our own sample size */
1513 pmix *= This->wfx.nBlockAlign;
1514 TRACE("this back-offset=%ld\n", pmix);
1515 /* subtract from our last mixed position */
1516 bplay = bmix;
1517 while (bplay < pmix) bplay += This->buflen; /* wraparound */
1518 bplay -= pmix;
1519 if (This->leadin && ((bplay < This->startpos) || (bplay > bmix))) {
1520 /* seems we haven't started playing yet */
1521 TRACE("this still in lead-in phase\n");
1522 bplay = This->startpos;
1524 /* return the result */
1525 return bplay;
1528 static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
1529 LPDIRECTSOUNDBUFFER iface,LPDWORD playpos,LPDWORD writepos
1531 HRESULT hres;
1532 ICOM_THIS(IDirectSoundBufferImpl,iface);
1533 TRACE("(%p,%p,%p)\n",This,playpos,writepos);
1534 if (This->hwbuf) {
1535 hres=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);
1536 if (hres)
1537 return hres;
1539 else if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
1540 if (playpos) {
1541 MMTIME mtime;
1542 mtime.wType = TIME_BYTES;
1543 waveOutGetPosition(This->dsound->hwo, &mtime, sizeof(mtime));
1544 mtime.u.cb = mtime.u.cb % This->buflen;
1545 *playpos = mtime.u.cb;
1547 if (writepos) {
1548 /* the writepos should only be used by apps with WRITEPRIMARY priority,
1549 * in which case our software mixer is disabled anyway */
1550 *writepos = (This->dsound->pwplay + DS_HEL_MARGIN) * This->dsound->fraglen;
1551 while (*writepos >= This->buflen)
1552 *writepos -= This->buflen;
1554 } else {
1555 if (playpos && (This->state != STATE_PLAYING)) {
1556 /* we haven't been merged into the primary buffer (yet) */
1557 *playpos = This->buf_mixpos;
1559 else if (playpos) {
1560 DWORD pplay, pwrite, lplay, splay, pstate;
1561 /* let's get this exact; first, recursively call GetPosition on the primary */
1562 EnterCriticalSection(&(primarybuf->lock));
1563 IDirectSoundBufferImpl_GetCurrentPosition((LPDIRECTSOUNDBUFFER)primarybuf, &pplay, &pwrite);
1564 /* detect HEL mode underrun */
1565 pstate = primarybuf->state;
1566 if (!(primarybuf->hwbuf || primarybuf->dsound->pwqueue)) {
1567 TRACE("detected an underrun\n");
1568 /* pplay = ? */
1569 if (pstate == STATE_PLAYING)
1570 pstate = STATE_STARTING;
1571 else if (pstate == STATE_STOPPING)
1572 pstate = STATE_STOPPED;
1574 /* get data for ourselves while we still have the lock */
1575 pstate &= This->state;
1576 lplay = This->primary_mixpos;
1577 splay = This->buf_mixpos;
1578 if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || primarybuf->hwbuf) {
1579 /* calculate play position using this */
1580 *playpos = DSOUND_CalcPlayPosition(This, pstate, pplay, pwrite, lplay, splay);
1581 } else {
1582 /* (unless the app isn't using GETCURRENTPOSITION2) */
1583 /* don't know exactly how this should be handled...
1584 * the docs says that play cursor is reported as directly
1585 * behind write cursor, hmm... */
1586 /* let's just do what might work for Half-Life */
1587 DWORD wp;
1588 wp = (This->dsound->pwplay + DS_HEL_MARGIN) * This->dsound->fraglen;
1589 while (wp >= primarybuf->buflen)
1590 wp -= primarybuf->buflen;
1591 *playpos = DSOUND_CalcPlayPosition(This, pstate, wp, pwrite, lplay, splay);
1593 LeaveCriticalSection(&(primarybuf->lock));
1595 if (writepos) *writepos = This->buf_mixpos;
1597 if (writepos) {
1598 if (This->state != STATE_STOPPED)
1599 /* apply the documented 10ms lead to writepos */
1600 *writepos += This->writelead;
1601 while (*writepos >= This->buflen) *writepos -= This->buflen;
1603 TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
1604 return DS_OK;
1607 static HRESULT WINAPI IDirectSoundBufferImpl_GetStatus(
1608 LPDIRECTSOUNDBUFFER iface,LPDWORD status
1610 ICOM_THIS(IDirectSoundBufferImpl,iface);
1611 TRACE("(%p,%p), thread is %lx\n",This,status,GetCurrentThreadId());
1613 if (status == NULL)
1614 return DSERR_INVALIDPARAM;
1616 *status = 0;
1617 if ((This->state == STATE_STARTING) || (This->state == STATE_PLAYING)) {
1618 *status |= DSBSTATUS_PLAYING;
1619 if (This->playflags & DSBPLAY_LOOPING)
1620 *status |= DSBSTATUS_LOOPING;
1623 TRACE("status=%lx\n", *status);
1624 return DS_OK;
1628 static HRESULT WINAPI IDirectSoundBufferImpl_GetFormat(
1629 LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
1631 ICOM_THIS(IDirectSoundBufferImpl,iface);
1632 TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
1634 if (wfsize>sizeof(This->wfx))
1635 wfsize = sizeof(This->wfx);
1636 if (lpwf) { /* NULL is valid */
1637 memcpy(lpwf,&(This->wfx),wfsize);
1638 if (wfwritten)
1639 *wfwritten = wfsize;
1640 } else
1641 if (wfwritten)
1642 *wfwritten = sizeof(This->wfx);
1643 else
1644 return DSERR_INVALIDPARAM;
1646 return DS_OK;
1649 static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
1650 LPDIRECTSOUNDBUFFER iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
1652 ICOM_THIS(IDirectSoundBufferImpl,iface);
1653 DWORD capf;
1655 TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx) at %ld\n",
1656 This,
1657 writecursor,
1658 writebytes,
1659 lplpaudioptr1,
1660 audiobytes1,
1661 lplpaudioptr2,
1662 audiobytes2,
1663 flags,
1664 GetTickCount()
1667 if (flags & DSBLOCK_FROMWRITECURSOR) {
1668 DWORD writepos;
1669 /* GetCurrentPosition does too much magic to duplicate here */
1670 IDirectSoundBufferImpl_GetCurrentPosition(iface, NULL, &writepos);
1671 writecursor += writepos;
1673 if (flags & DSBLOCK_ENTIREBUFFER)
1674 writebytes = This->buflen;
1675 if (writebytes > This->buflen)
1676 writebytes = This->buflen;
1678 assert(audiobytes1!=audiobytes2);
1679 assert(lplpaudioptr1!=lplpaudioptr2);
1681 if ((writebytes == This->buflen) &&
1682 ((This->state == STATE_STARTING) ||
1683 (This->state == STATE_PLAYING)))
1684 /* some games, like Half-Life, try to be clever (not) and
1685 * keep one secondary buffer, and mix sounds into it itself,
1686 * locking the entire buffer every time... so we can just forget
1687 * about tracking the last-written-to-position... */
1688 This->probably_valid_to = (DWORD)-1;
1689 else
1690 This->probably_valid_to = writecursor;
1692 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1693 capf = DSDDESC_DONTNEEDPRIMARYLOCK;
1694 else
1695 capf = DSDDESC_DONTNEEDSECONDARYLOCK;
1696 if (!(This->dsound->drvdesc.dwFlags & capf) && This->hwbuf) {
1697 IDsDriverBuffer_Lock(This->hwbuf,
1698 lplpaudioptr1, audiobytes1,
1699 lplpaudioptr2, audiobytes2,
1700 writecursor, writebytes,
1703 else {
1704 BOOL remix = FALSE;
1705 if (writecursor+writebytes <= This->buflen) {
1706 *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
1707 *audiobytes1 = writebytes;
1708 if (lplpaudioptr2)
1709 *(LPBYTE*)lplpaudioptr2 = NULL;
1710 if (audiobytes2)
1711 *audiobytes2 = 0;
1712 TRACE("->%ld.0\n",writebytes);
1713 } else {
1714 *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
1715 *audiobytes1 = This->buflen-writecursor;
1716 if (lplpaudioptr2)
1717 *(LPBYTE*)lplpaudioptr2 = This->buffer;
1718 if (audiobytes2)
1719 *audiobytes2 = writebytes-(This->buflen-writecursor);
1720 TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
1722 /* if the segment between playpos and buf_mixpos is touched,
1723 * we need to cancel some mixing */
1724 if (This->buf_mixpos >= This->playpos) {
1725 if (This->buf_mixpos > writecursor &&
1726 This->playpos <= writecursor+writebytes)
1727 remix = TRUE;
1729 else {
1730 if (This->buf_mixpos > writecursor ||
1731 This->playpos <= writecursor+writebytes)
1732 remix = TRUE;
1734 if (remix) {
1735 TRACE("locking prebuffered region, ouch\n");
1736 DSOUND_MixCancelAt(This, writecursor);
1739 return DS_OK;
1742 static HRESULT WINAPI IDirectSoundBufferImpl_SetCurrentPosition(
1743 LPDIRECTSOUNDBUFFER iface,DWORD newpos
1745 ICOM_THIS(IDirectSoundBufferImpl,iface);
1746 TRACE("(%p,%ld)\n",This,newpos);
1748 /* **** */
1749 EnterCriticalSection(&(This->lock));
1751 This->buf_mixpos = newpos;
1752 if (This->hwbuf)
1753 IDsDriverBuffer_SetPosition(This->hwbuf, This->buf_mixpos);
1755 LeaveCriticalSection(&(This->lock));
1756 /* **** */
1758 return DS_OK;
1761 static HRESULT WINAPI IDirectSoundBufferImpl_SetPan(
1762 LPDIRECTSOUNDBUFFER iface,LONG pan
1764 ICOM_THIS(IDirectSoundBufferImpl,iface);
1766 TRACE("(%p,%ld)\n",This,pan);
1768 if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT))
1769 return DSERR_INVALIDPARAM;
1771 /* You cannot set the pan of the primary buffer */
1772 /* and you cannot use both pan and 3D controls */
1773 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
1774 (This->dsbd.dwFlags & DSBCAPS_CTRL3D) ||
1775 (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
1776 return DSERR_CONTROLUNAVAIL;
1778 /* **** */
1779 EnterCriticalSection(&(This->lock));
1781 This->volpan.lPan = pan;
1783 DSOUND_RecalcVolPan(&(This->volpan));
1785 if (This->hwbuf) {
1786 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
1789 LeaveCriticalSection(&(This->lock));
1790 /* **** */
1792 return DS_OK;
1795 static HRESULT WINAPI IDirectSoundBufferImpl_GetPan(
1796 LPDIRECTSOUNDBUFFER iface,LPLONG pan
1798 ICOM_THIS(IDirectSoundBufferImpl,iface);
1799 TRACE("(%p,%p)\n",This,pan);
1801 if (pan == NULL)
1802 return DSERR_INVALIDPARAM;
1804 *pan = This->volpan.lPan;
1806 return DS_OK;
1809 static HRESULT WINAPI IDirectSoundBufferImpl_Unlock(
1810 LPDIRECTSOUNDBUFFER iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
1812 ICOM_THIS(IDirectSoundBufferImpl,iface);
1813 DWORD capf, probably_valid_to;
1815 TRACE("(%p,%p,%ld,%p,%ld):stub\n", This,p1,x1,p2,x2);
1817 #if 0
1818 /* Preprocess 3D buffers... */
1820 /* This is highly experimental and liable to break things */
1821 if (This->dsbd.dwFlags & DSBCAPS_CTRL3D)
1822 DSOUND_Create3DBuffer(This);
1823 #endif
1825 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1826 capf = DSDDESC_DONTNEEDPRIMARYLOCK;
1827 else
1828 capf = DSDDESC_DONTNEEDSECONDARYLOCK;
1829 if (!(This->dsound->drvdesc.dwFlags & capf) && This->hwbuf) {
1830 IDsDriverBuffer_Unlock(This->hwbuf, p1, x1, p2, x2);
1833 if (p2) probably_valid_to = (((LPBYTE)p2)-This->buffer) + x2;
1834 else probably_valid_to = (((LPBYTE)p1)-This->buffer) + x1;
1835 while (probably_valid_to >= This->buflen)
1836 probably_valid_to -= This->buflen;
1837 if ((probably_valid_to == 0) && ((x1+x2) == This->buflen) &&
1838 ((This->state == STATE_STARTING) ||
1839 (This->state == STATE_PLAYING)))
1840 /* see IDirectSoundBufferImpl_Lock */
1841 probably_valid_to = (DWORD)-1;
1842 This->probably_valid_to = probably_valid_to;
1844 return DS_OK;
1847 static HRESULT WINAPI IDirectSoundBufferImpl_Restore(
1848 LPDIRECTSOUNDBUFFER iface
1850 ICOM_THIS(IDirectSoundBufferImpl,iface);
1851 FIXME("(%p):stub\n",This);
1852 return DS_OK;
1855 static HRESULT WINAPI IDirectSoundBufferImpl_GetFrequency(
1856 LPDIRECTSOUNDBUFFER iface,LPDWORD freq
1858 ICOM_THIS(IDirectSoundBufferImpl,iface);
1859 TRACE("(%p,%p)\n",This,freq);
1861 if (freq == NULL)
1862 return DSERR_INVALIDPARAM;
1864 *freq = This->freq;
1865 TRACE("-> %ld\n", *freq);
1867 return DS_OK;
1870 static HRESULT WINAPI IDirectSoundBufferImpl_Initialize(
1871 LPDIRECTSOUNDBUFFER iface,LPDIRECTSOUND dsound,LPDSBUFFERDESC dbsd
1873 ICOM_THIS(IDirectSoundBufferImpl,iface);
1874 FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
1875 DPRINTF("Re-Init!!!\n");
1876 return DSERR_ALREADYINITIALIZED;
1879 static HRESULT WINAPI IDirectSoundBufferImpl_GetCaps(
1880 LPDIRECTSOUNDBUFFER iface,LPDSBCAPS caps
1882 ICOM_THIS(IDirectSoundBufferImpl,iface);
1883 TRACE("(%p)->(%p)\n",This,caps);
1885 if (caps == NULL)
1886 return DSERR_INVALIDPARAM;
1888 /* I think we should check this value, not set it. See */
1889 /* Inside DirectX, p215. That should apply here, too. */
1890 caps->dwSize = sizeof(*caps);
1892 caps->dwFlags = This->dsbd.dwFlags;
1893 if (This->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
1894 else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
1896 caps->dwBufferBytes = This->buflen;
1898 /* This value represents the speed of the "unlock" command.
1899 As unlock is quite fast (it does not do anything), I put
1900 4096 ko/s = 4 Mo / s */
1901 /* FIXME: hwbuf speed */
1902 caps->dwUnlockTransferRate = 4096;
1903 caps->dwPlayCpuOverhead = 0;
1905 return DS_OK;
1908 static HRESULT WINAPI IDirectSoundBufferImpl_QueryInterface(
1909 LPDIRECTSOUNDBUFFER iface,REFIID riid,LPVOID *ppobj
1911 ICOM_THIS(IDirectSoundBufferImpl,iface);
1913 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
1915 if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
1916 IDirectSoundNotifyImpl *dsn;
1918 dsn = (IDirectSoundNotifyImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn));
1919 dsn->ref = 1;
1920 dsn->dsb = This;
1921 IDirectSoundBuffer_AddRef(iface);
1922 ICOM_VTBL(dsn) = &dsnvt;
1923 *ppobj = (LPVOID)dsn;
1924 return S_OK;
1927 #ifdef USE_DSOUND3D
1928 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1929 IDirectSound3DBufferImpl *ds3db;
1931 *ppobj = This->ds3db;
1932 if (*ppobj) {
1933 IDirectSound3DBuffer_AddRef((LPDIRECTSOUND3DBUFFER)This->ds3db);
1934 return S_OK;
1937 ds3db = (IDirectSound3DBufferImpl*)HeapAlloc(GetProcessHeap(),
1938 0,sizeof(*ds3db));
1939 ds3db->ref = 1;
1940 ds3db->dsb = This;
1941 ICOM_VTBL(ds3db) = &ds3dbvt;
1942 InitializeCriticalSection(&ds3db->lock);
1944 IDirectSoundBuffer_AddRef(iface);
1946 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
1947 ds3db->ds3db.vPosition.u1.x = 0.0;
1948 ds3db->ds3db.vPosition.u2.y = 0.0;
1949 ds3db->ds3db.vPosition.u3.z = 0.0;
1950 ds3db->ds3db.vVelocity.u1.x = 0.0;
1951 ds3db->ds3db.vVelocity.u2.y = 0.0;
1952 ds3db->ds3db.vVelocity.u3.z = 0.0;
1953 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
1954 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
1955 ds3db->ds3db.vConeOrientation.u1.x = 0.0;
1956 ds3db->ds3db.vConeOrientation.u2.y = 0.0;
1957 ds3db->ds3db.vConeOrientation.u3.z = 0.0;
1958 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME; ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
1959 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
1960 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
1961 ds3db->buflen = (This->buflen * primarybuf->wfx.nBlockAlign) /
1962 This->wfx.nBlockAlign;
1963 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
1964 if (ds3db->buffer == NULL) {
1965 ds3db->buflen = 0;
1966 ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
1969 ds3db->iks = (IKsPropertySetImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db->iks)));
1970 ds3db->iks->ref = 1;
1971 ds3db->iks->ds3db = ds3db;
1972 ICOM_VTBL(ds3db->iks) = &iksvt;
1974 return S_OK;
1976 #else
1977 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1978 FIXME("%s: I know about this GUID, but don't support it yet\n",
1979 debugstr_guid( riid ));
1980 *ppobj = NULL;
1981 return E_FAIL;
1983 #endif
1985 #if USE_DSOUND3D
1986 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1987 IDirectSound3DListenerImpl* dsl;
1989 if (This->dsound->listener) {
1990 *ppobj = This->dsound->listener;
1991 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)This->dsound->listener);
1992 return DS_OK;
1995 dsl = (IDirectSound3DListenerImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsl));
1996 dsl->ref = 1;
1997 ICOM_VTBL(dsl) = &ds3dlvt;
1998 *ppobj = (LPVOID)dsl;
2000 dsl->ds3dl.dwSize = sizeof(DS3DLISTENER);
2001 dsl->ds3dl.vPosition.u1.x = 0.0;
2002 dsl->ds3dl.vPosition.u2.y = 0.0;
2003 dsl->ds3dl.vPosition.u3.z = 0.0;
2004 dsl->ds3dl.vVelocity.u1.x = 0.0;
2005 dsl->ds3dl.vVelocity.u2.y = 0.0;
2006 dsl->ds3dl.vVelocity.u3.z = 0.0;
2007 dsl->ds3dl.vOrientFront.u1.x = 0.0;
2008 dsl->ds3dl.vOrientFront.u2.y = 0.0;
2009 dsl->ds3dl.vOrientFront.u3.z = 1.0;
2010 dsl->ds3dl.vOrientTop.u1.x = 0.0;
2011 dsl->ds3dl.vOrientTop.u2.y = 1.0;
2012 dsl->ds3dl.vOrientTop.u3.z = 0.0;
2013 dsl->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
2014 dsl->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
2016 InitializeCriticalSection(&dsl->lock);
2018 dsl->dsb = This;
2019 IDirectSoundBuffer_AddRef(iface);
2021 This->dsound->listener = dsl;
2022 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)dsl);
2024 return S_OK;
2026 #else
2027 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
2028 FIXME("%s: I know about this GUID, but don't support it yet\n",
2029 debugstr_guid( riid ));
2030 *ppobj = NULL;
2031 return E_FAIL;
2033 #endif
2035 FIXME( "Unknown GUID %s\n", debugstr_guid( riid ) );
2037 *ppobj = NULL;
2039 return E_FAIL;
2042 static ICOM_VTABLE(IDirectSoundBuffer) dsbvt =
2044 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2045 IDirectSoundBufferImpl_QueryInterface,
2046 IDirectSoundBufferImpl_AddRef,
2047 IDirectSoundBufferImpl_Release,
2048 IDirectSoundBufferImpl_GetCaps,
2049 IDirectSoundBufferImpl_GetCurrentPosition,
2050 IDirectSoundBufferImpl_GetFormat,
2051 IDirectSoundBufferImpl_GetVolume,
2052 IDirectSoundBufferImpl_GetPan,
2053 IDirectSoundBufferImpl_GetFrequency,
2054 IDirectSoundBufferImpl_GetStatus,
2055 IDirectSoundBufferImpl_Initialize,
2056 IDirectSoundBufferImpl_Lock,
2057 IDirectSoundBufferImpl_Play,
2058 IDirectSoundBufferImpl_SetCurrentPosition,
2059 IDirectSoundBufferImpl_SetFormat,
2060 IDirectSoundBufferImpl_SetVolume,
2061 IDirectSoundBufferImpl_SetPan,
2062 IDirectSoundBufferImpl_SetFrequency,
2063 IDirectSoundBufferImpl_Stop,
2064 IDirectSoundBufferImpl_Unlock,
2065 IDirectSoundBufferImpl_Restore
2068 /*******************************************************************************
2069 * IDirectSound
2072 static HRESULT WINAPI IDirectSoundImpl_SetCooperativeLevel(
2073 LPDIRECTSOUND iface,HWND hwnd,DWORD level
2075 ICOM_THIS(IDirectSoundImpl,iface);
2077 FIXME("(%p,%08lx,%ld):stub\n",This,(DWORD)hwnd,level);
2079 This->priolevel = level;
2081 return DS_OK;
2084 static HRESULT WINAPI IDirectSoundImpl_CreateSoundBuffer(
2085 LPDIRECTSOUND iface,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER ppdsb,LPUNKNOWN lpunk
2087 ICOM_THIS(IDirectSoundImpl,iface);
2088 IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
2089 LPWAVEFORMATEX wfex;
2090 HRESULT err = DS_OK;
2092 TRACE("(%p,%p,%p,%p)\n",This,dsbd,ippdsb,lpunk);
2094 if ((This == NULL) || (dsbd == NULL) || (ippdsb == NULL))
2095 return DSERR_INVALIDPARAM;
2097 if (TRACE_ON(dsound)) {
2098 TRACE("(structsize=%ld)\n",dsbd->dwSize);
2099 TRACE("(flags=0x%08lx:\n",dsbd->dwFlags);
2100 _dump_DSBCAPS(dsbd->dwFlags);
2101 DPRINTF(")\n");
2102 TRACE("(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
2103 TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
2106 wfex = dsbd->lpwfxFormat;
2108 if (wfex)
2109 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
2110 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
2111 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
2112 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
2113 wfex->wBitsPerSample, wfex->cbSize);
2115 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
2116 if (primarybuf) {
2117 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)primarybuf);
2118 *ippdsb = primarybuf;
2119 primarybuf->dsbd.dwFlags = dsbd->dwFlags;
2120 return DS_OK;
2121 } /* Else create primary buffer */
2122 } else {
2123 if (dsbd->dwBufferBytes < DSBSIZE_MIN || dsbd->dwBufferBytes > DSBSIZE_MAX) {
2124 ERR("invalid sound buffer size %ld\n", dsbd->dwBufferBytes);
2125 return DSERR_INVALIDPARAM; /* FIXME: which error? */
2129 *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
2130 if (*ippdsb == NULL)
2131 return DSERR_OUTOFMEMORY;
2132 ICOM_VTBL(*ippdsb) = &dsbvt;
2133 (*ippdsb)->ref = 1;
2134 (*ippdsb)->dsound = This;
2135 (*ippdsb)->parent = NULL;
2136 (*ippdsb)->buffer = NULL;
2138 memcpy(&((*ippdsb)->dsbd),dsbd,sizeof(*dsbd));
2139 if (dsbd->lpwfxFormat)
2140 memcpy(&((*ippdsb)->wfx), dsbd->lpwfxFormat, sizeof((*ippdsb)->wfx));
2142 TRACE("Created buffer at %p\n", *ippdsb);
2144 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
2145 (*ippdsb)->buflen = dsound->wfx.nAvgBytesPerSec;
2146 (*ippdsb)->freq = dsound->wfx.nSamplesPerSec;
2148 /* FIXME: verify that hardware capabilities (DSCAPS_PRIMARY flags) match */
2150 if (This->driver) {
2151 err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
2152 &((*ippdsb)->buflen),&((*ippdsb)->buffer),
2153 (LPVOID*)&((*ippdsb)->hwbuf));
2155 if (err == DS_OK)
2156 err = DSOUND_PrimaryOpen(*ippdsb);
2157 } else {
2158 DWORD capf = 0;
2159 int use_hw;
2161 (*ippdsb)->buflen = dsbd->dwBufferBytes;
2162 (*ippdsb)->freq = dsbd->lpwfxFormat->nSamplesPerSec;
2164 /* Check necessary hardware mixing capabilities */
2165 if (wfex->nChannels==2) capf |= DSCAPS_SECONDARYSTEREO;
2166 else capf |= DSCAPS_SECONDARYMONO;
2167 if (wfex->wBitsPerSample==16) capf |= DSCAPS_SECONDARY16BIT;
2168 else capf |= DSCAPS_SECONDARY8BIT;
2169 use_hw = (This->drvcaps.dwFlags & capf) == capf;
2171 /* FIXME: check hardware sample rate mixing capabilities */
2172 /* FIXME: check app hints for software/hardware buffer (STATIC, LOCHARDWARE, etc) */
2173 /* FIXME: check whether any hardware buffers are left */
2174 /* FIXME: handle DSDHEAP_CREATEHEAP for hardware buffers */
2176 /* Allocate system memory if applicable */
2177 if ((This->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) || !use_hw) {
2178 (*ippdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,(*ippdsb)->buflen);
2179 if ((*ippdsb)->buffer == NULL)
2180 err = DSERR_OUTOFMEMORY;
2183 /* Allocate the hardware buffer */
2184 if (use_hw && (err == DS_OK)) {
2185 err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
2186 &((*ippdsb)->buflen),&((*ippdsb)->buffer),
2187 (LPVOID*)&((*ippdsb)->hwbuf));
2191 if (err != DS_OK) {
2192 if ((*ippdsb)->buffer)
2193 HeapFree(GetProcessHeap(),0,(*ippdsb)->buffer);
2194 HeapFree(GetProcessHeap(),0,(*ippdsb));
2195 *ippdsb = NULL;
2196 return err;
2198 /* calculate fragment size and write lead */
2199 DSOUND_RecalcFormat(*ippdsb);
2201 /* It's not necessary to initialize values to zero since */
2202 /* we allocated this structure with HEAP_ZERO_MEMORY... */
2203 (*ippdsb)->playpos = 0;
2204 (*ippdsb)->buf_mixpos = 0;
2205 (*ippdsb)->state = STATE_STOPPED;
2206 DSOUND_RecalcVolPan(&((*ippdsb)->volpan));
2208 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
2209 (*ippdsb)->freqAdjust = ((*ippdsb)->freq << DSOUND_FREQSHIFT) /
2210 primarybuf->wfx.nSamplesPerSec;
2211 (*ippdsb)->nAvgBytesPerSec = (*ippdsb)->freq *
2212 dsbd->lpwfxFormat->nBlockAlign;
2215 EnterCriticalSection(&(This->lock));
2216 /* register buffer */
2217 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
2218 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl*)*(This->nrofbuffers+1));
2219 if (newbuffers) {
2220 This->buffers = newbuffers;
2221 This->buffers[This->nrofbuffers] = *ippdsb;
2222 This->nrofbuffers++;
2223 TRACE("buffer count is now %d\n", This->nrofbuffers);
2224 } else {
2225 ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
2226 err = DSERR_OUTOFMEMORY;
2229 LeaveCriticalSection(&(This->lock));
2231 IDirectSound_AddRef(iface);
2233 InitializeCriticalSection(&((*ippdsb)->lock));
2235 if (err != DS_OK) {
2236 /* oops... */
2237 IDirectSoundBuffer_Release(*ppdsb);
2238 *ippdsb = NULL;
2239 return err;
2242 #ifdef USE_DSOUND3D
2243 if (dsbd->dwFlags & DSBCAPS_CTRL3D) {
2244 IDirectSound3DBufferImpl *ds3db;
2246 ds3db = (IDirectSound3DBufferImpl*)HeapAlloc(GetProcessHeap(),
2247 0,sizeof(*ds3db));
2248 ICOM_VTBL(ds3db) = &ds3dbvt;
2249 ds3db->ref = 1;
2250 (*ippdsb)->ds3db = ds3db;
2252 ds3db->dsb = (*ippdsb);
2253 IDirectSoundBufferImpl_AddRef((LPDIRECTSOUNDBUFFER)(*ippdsb));
2255 InitializeCriticalSection(&ds3db->lock);
2257 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
2258 ds3db->ds3db.vPosition.u1.x = 0.0;
2259 ds3db->ds3db.vPosition.u2.y = 0.0;
2260 ds3db->ds3db.vPosition.u3.z = 0.0;
2261 ds3db->ds3db.vVelocity.u1.x = 0.0;
2262 ds3db->ds3db.vVelocity.u2.y = 0.0;
2263 ds3db->ds3db.vVelocity.u3.z = 0.0;
2264 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
2265 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
2266 ds3db->ds3db.vConeOrientation.u1.x = 0.0;
2267 ds3db->ds3db.vConeOrientation.u2.y = 0.0;
2268 ds3db->ds3db.vConeOrientation.u3.z = 0.0;
2269 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME;
2270 ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
2271 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
2272 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
2273 ds3db->buflen = ((*ippdsb)->buflen * primarybuf->wfx.nBlockAlign) /
2274 (*ippdsb)->wfx.nBlockAlign;
2275 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
2276 if (ds3db->buffer == NULL) {
2277 ds3db->buflen = 0;
2278 ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
2280 ds3db->iks = (IKsPropertySetImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db->iks)));
2281 ds3db->iks->ref = 1;
2282 ds3db->iks->ds3db = ds3db;
2283 ICOM_VTBL(ds3db->iks) = &iksvt;
2286 #endif
2287 return DS_OK;
2290 static HRESULT WINAPI IDirectSoundImpl_DuplicateSoundBuffer(
2291 LPDIRECTSOUND iface,LPDIRECTSOUNDBUFFER pdsb,LPLPDIRECTSOUNDBUFFER ppdsb
2293 ICOM_THIS(IDirectSoundImpl,iface);
2294 IDirectSoundBufferImpl* ipdsb=(IDirectSoundBufferImpl*)pdsb;
2295 IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
2296 TRACE("(%p,%p,%p)\n",This,ipdsb,ippdsb);
2298 if (ipdsb->hwbuf) {
2299 FIXME("need to duplicate hardware buffer\n");
2302 *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
2304 IDirectSoundBuffer_AddRef(pdsb);
2305 memcpy(*ippdsb, ipdsb, sizeof(IDirectSoundBufferImpl));
2306 (*ippdsb)->ref = 1;
2307 (*ippdsb)->state = STATE_STOPPED;
2308 (*ippdsb)->playpos = 0;
2309 (*ippdsb)->buf_mixpos = 0;
2310 (*ippdsb)->dsound = This;
2311 (*ippdsb)->parent = ipdsb;
2312 memcpy(&((*ippdsb)->wfx), &(ipdsb->wfx), sizeof((*ippdsb)->wfx));
2313 InitializeCriticalSection(&(*ippdsb)->lock);
2314 /* register buffer */
2315 EnterCriticalSection(&(This->lock));
2317 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl**)*(This->nrofbuffers+1));
2318 if (newbuffers) {
2319 This->buffers = newbuffers;
2320 This->buffers[This->nrofbuffers] = *ippdsb;
2321 This->nrofbuffers++;
2322 TRACE("buffer count is now %d\n", This->nrofbuffers);
2323 } else {
2324 ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
2325 /* FIXME: release buffer */
2328 LeaveCriticalSection(&(This->lock));
2329 IDirectSound_AddRef(iface);
2330 return DS_OK;
2334 static HRESULT WINAPI IDirectSoundImpl_GetCaps(LPDIRECTSOUND iface,LPDSCAPS caps) {
2335 ICOM_THIS(IDirectSoundImpl,iface);
2336 TRACE("(%p,%p)\n",This,caps);
2337 TRACE("(flags=0x%08lx)\n",caps->dwFlags);
2339 if (caps == NULL)
2340 return DSERR_INVALIDPARAM;
2342 /* We should check this value, not set it. See Inside DirectX, p215. */
2343 caps->dwSize = sizeof(*caps);
2345 caps->dwFlags = This->drvcaps.dwFlags;
2347 /* FIXME: copy caps from This->drvcaps */
2348 caps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
2349 caps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
2351 caps->dwPrimaryBuffers = 1;
2353 caps->dwMaxHwMixingAllBuffers = 0;
2354 caps->dwMaxHwMixingStaticBuffers = 0;
2355 caps->dwMaxHwMixingStreamingBuffers = 0;
2357 caps->dwFreeHwMixingAllBuffers = 0;
2358 caps->dwFreeHwMixingStaticBuffers = 0;
2359 caps->dwFreeHwMixingStreamingBuffers = 0;
2361 caps->dwMaxHw3DAllBuffers = 0;
2362 caps->dwMaxHw3DStaticBuffers = 0;
2363 caps->dwMaxHw3DStreamingBuffers = 0;
2365 caps->dwFreeHw3DAllBuffers = 0;
2366 caps->dwFreeHw3DStaticBuffers = 0;
2367 caps->dwFreeHw3DStreamingBuffers = 0;
2369 caps->dwTotalHwMemBytes = 0;
2371 caps->dwFreeHwMemBytes = 0;
2373 caps->dwMaxContigFreeHwMemBytes = 0;
2375 caps->dwUnlockTransferRateHwBuffers = 4096; /* But we have none... */
2377 caps->dwPlayCpuOverheadSwBuffers = 1; /* 1% */
2379 return DS_OK;
2382 static ULONG WINAPI IDirectSoundImpl_AddRef(LPDIRECTSOUND iface) {
2383 ICOM_THIS(IDirectSoundImpl,iface);
2384 return ++(This->ref);
2387 static ULONG WINAPI IDirectSoundImpl_Release(LPDIRECTSOUND iface) {
2388 ICOM_THIS(IDirectSoundImpl,iface);
2389 TRACE("(%p), ref was %ld\n",This,This->ref);
2390 if (!--(This->ref)) {
2391 UINT i;
2393 timeKillEvent(This->timerID);
2394 timeEndPeriod(DS_TIME_RES);
2396 if (primarybuf)
2397 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)primarybuf);
2399 if (This->buffers) {
2400 for( i=0;i<This->nrofbuffers;i++)
2401 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->buffers[i]);
2404 if (This->primary)
2405 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->primary);
2407 DeleteCriticalSection(&This->lock);
2408 if (This->driver) {
2409 IDsDriver_Close(This->driver);
2410 } else {
2411 unsigned c;
2412 for (c=0; c<DS_HEL_FRAGS; c++)
2413 HeapFree(GetProcessHeap(),0,This->pwave[c]);
2415 if (This->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
2416 waveOutClose(This->hwo);
2418 if (This->driver)
2419 IDsDriver_Release(This->driver);
2421 HeapFree(GetProcessHeap(),0,This);
2422 dsound = NULL;
2423 return 0;
2425 return This->ref;
2428 static HRESULT WINAPI IDirectSoundImpl_SetSpeakerConfig(
2429 LPDIRECTSOUND iface,DWORD config
2431 ICOM_THIS(IDirectSoundImpl,iface);
2432 FIXME("(%p,0x%08lx):stub\n",This,config);
2433 return DS_OK;
2436 static HRESULT WINAPI IDirectSoundImpl_QueryInterface(
2437 LPDIRECTSOUND iface,REFIID riid,LPVOID *ppobj
2439 ICOM_THIS(IDirectSoundImpl,iface);
2441 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
2443 if (This->listener) {
2444 *ppobj = This->listener;
2445 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)This->listener);
2446 return DS_OK;
2449 This->listener = (IDirectSound3DListenerImpl*)HeapAlloc(
2450 GetProcessHeap(), 0, sizeof(*(This->listener)));
2451 This->listener->ref = 1;
2452 ICOM_VTBL(This->listener) = &ds3dlvt;
2453 *ppobj = (LPVOID)This->listener;
2454 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
2456 This->listener->dsb = NULL;
2458 This->listener->ds3dl.dwSize = sizeof(DS3DLISTENER);
2459 This->listener->ds3dl.vPosition.u1.x = 0.0;
2460 This->listener->ds3dl.vPosition.u2.y = 0.0;
2461 This->listener->ds3dl.vPosition.u3.z = 0.0;
2462 This->listener->ds3dl.vVelocity.u1.x = 0.0;
2463 This->listener->ds3dl.vVelocity.u2.y = 0.0;
2464 This->listener->ds3dl.vVelocity.u3.z = 0.0;
2465 This->listener->ds3dl.vOrientFront.u1.x = 0.0;
2466 This->listener->ds3dl.vOrientFront.u2.y = 0.0;
2467 This->listener->ds3dl.vOrientFront.u3.z = 1.0;
2468 This->listener->ds3dl.vOrientTop.u1.x = 0.0;
2469 This->listener->ds3dl.vOrientTop.u2.y = 1.0;
2470 This->listener->ds3dl.vOrientTop.u3.z = 0.0;
2471 This->listener->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
2472 This->listener->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
2473 This->listener->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
2475 InitializeCriticalSection(&This->listener->lock);
2477 return DS_OK;
2480 FIXME("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
2481 return E_FAIL;
2484 static HRESULT WINAPI IDirectSoundImpl_Compact(
2485 LPDIRECTSOUND iface)
2487 ICOM_THIS(IDirectSoundImpl,iface);
2488 TRACE("(%p)\n", This);
2489 return DS_OK;
2492 static HRESULT WINAPI IDirectSoundImpl_GetSpeakerConfig(
2493 LPDIRECTSOUND iface,
2494 LPDWORD lpdwSpeakerConfig)
2496 ICOM_THIS(IDirectSoundImpl,iface);
2497 TRACE("(%p, %p)\n", This, lpdwSpeakerConfig);
2498 *lpdwSpeakerConfig = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16);
2499 return DS_OK;
2502 static HRESULT WINAPI IDirectSoundImpl_Initialize(
2503 LPDIRECTSOUND iface,
2504 LPCGUID lpcGuid)
2506 ICOM_THIS(IDirectSoundImpl,iface);
2507 TRACE("(%p, %p)\n", This, lpcGuid);
2508 return DS_OK;
2511 static ICOM_VTABLE(IDirectSound) dsvt =
2513 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2514 IDirectSoundImpl_QueryInterface,
2515 IDirectSoundImpl_AddRef,
2516 IDirectSoundImpl_Release,
2517 IDirectSoundImpl_CreateSoundBuffer,
2518 IDirectSoundImpl_GetCaps,
2519 IDirectSoundImpl_DuplicateSoundBuffer,
2520 IDirectSoundImpl_SetCooperativeLevel,
2521 IDirectSoundImpl_Compact,
2522 IDirectSoundImpl_GetSpeakerConfig,
2523 IDirectSoundImpl_SetSpeakerConfig,
2524 IDirectSoundImpl_Initialize
2528 static void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len)
2530 int i;
2531 DWORD offset;
2532 LPDSBPOSITIONNOTIFY event;
2534 if (dsb->nrofnotifies == 0)
2535 return;
2537 TRACE("(%p) buflen = %ld, playpos = %ld, len = %d\n",
2538 dsb, dsb->buflen, dsb->playpos, len);
2539 for (i = 0; i < dsb->nrofnotifies ; i++) {
2540 event = dsb->notifies + i;
2541 offset = event->dwOffset;
2542 TRACE("checking %d, position %ld, event = %d\n",
2543 i, offset, event->hEventNotify);
2544 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
2545 /* OK. [Inside DirectX, p274] */
2546 /* */
2547 /* This also means we can't sort the entries by offset, */
2548 /* because DSBPN_OFFSETSTOP == -1 */
2549 if (offset == DSBPN_OFFSETSTOP) {
2550 if (dsb->state == STATE_STOPPED) {
2551 SetEvent(event->hEventNotify);
2552 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2553 return;
2554 } else
2555 return;
2557 if ((dsb->playpos + len) >= dsb->buflen) {
2558 if ((offset < ((dsb->playpos + len) % dsb->buflen)) ||
2559 (offset >= dsb->playpos)) {
2560 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2561 SetEvent(event->hEventNotify);
2563 } else {
2564 if ((offset >= dsb->playpos) && (offset < (dsb->playpos + len))) {
2565 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2566 SetEvent(event->hEventNotify);
2572 /* WAV format info can be found at: */
2573 /* */
2574 /* http://www.cwi.nl/ftp/audio/AudioFormats.part2 */
2575 /* ftp://ftp.cwi.nl/pub/audio/RIFF-format */
2576 /* */
2577 /* Import points to remember: */
2578 /* */
2579 /* 8-bit WAV is unsigned */
2580 /* 16-bit WAV is signed */
2582 static inline INT16 cvtU8toS16(BYTE byte)
2584 INT16 s = (byte - 128) << 8;
2586 return s;
2589 static inline BYTE cvtS16toU8(INT16 word)
2591 BYTE b = (word + 32768) >> 8;
2593 return b;
2597 /* We should be able to optimize these two inline functions */
2598 /* so that we aren't doing 8->16->8 conversions when it is */
2599 /* not necessary. But this is still a WIP. Optimize later. */
2600 static inline void get_fields(const IDirectSoundBufferImpl *dsb, BYTE *buf, INT *fl, INT *fr)
2602 INT16 *bufs = (INT16 *) buf;
2604 /* TRACE("(%p)\n", buf); */
2605 if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 2) {
2606 *fl = cvtU8toS16(*buf);
2607 *fr = cvtU8toS16(*(buf + 1));
2608 return;
2611 if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 2) {
2612 *fl = *bufs;
2613 *fr = *(bufs + 1);
2614 return;
2617 if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 1) {
2618 *fl = cvtU8toS16(*buf);
2619 *fr = *fl;
2620 return;
2623 if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 1) {
2624 *fl = *bufs;
2625 *fr = *bufs;
2626 return;
2629 FIXME("get_fields found an unsupported configuration\n");
2630 return;
2633 static inline void set_fields(BYTE *buf, INT fl, INT fr)
2635 INT16 *bufs = (INT16 *) buf;
2637 if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 2)) {
2638 *buf = cvtS16toU8(fl);
2639 *(buf + 1) = cvtS16toU8(fr);
2640 return;
2643 if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 2)) {
2644 *bufs = fl;
2645 *(bufs + 1) = fr;
2646 return;
2649 if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 1)) {
2650 *buf = cvtS16toU8((fl + fr) >> 1);
2651 return;
2654 if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 1)) {
2655 *bufs = (fl + fr) >> 1;
2656 return;
2658 FIXME("set_fields found an unsupported configuration\n");
2659 return;
2662 /* Now with PerfectPitch (tm) technology */
2663 static INT DSOUND_MixerNorm(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2665 INT i, size, ipos, ilen, fieldL, fieldR;
2666 BYTE *ibp, *obp;
2667 INT iAdvance = dsb->wfx.nBlockAlign;
2668 INT oAdvance = primarybuf->wfx.nBlockAlign;
2670 ibp = dsb->buffer + dsb->buf_mixpos;
2671 obp = buf;
2673 TRACE("(%p, %p, %p), buf_mixpos=%ld\n", dsb, ibp, obp, dsb->buf_mixpos);
2674 /* Check for the best case */
2675 if ((dsb->freq == primarybuf->wfx.nSamplesPerSec) &&
2676 (dsb->wfx.wBitsPerSample == primarybuf->wfx.wBitsPerSample) &&
2677 (dsb->wfx.nChannels == primarybuf->wfx.nChannels)) {
2678 DWORD bytesleft = dsb->buflen - dsb->buf_mixpos;
2679 TRACE("(%p) Best case\n", dsb);
2680 if (len <= bytesleft )
2681 memcpy(obp, ibp, len);
2682 else { /* wrap */
2683 memcpy(obp, ibp, bytesleft );
2684 memcpy(obp + bytesleft, dsb->buffer, len - bytesleft);
2686 return len;
2689 /* Check for same sample rate */
2690 if (dsb->freq == primarybuf->wfx.nSamplesPerSec) {
2691 TRACE("(%p) Same sample rate %ld = primary %ld\n", dsb,
2692 dsb->freq, primarybuf->wfx.nSamplesPerSec);
2693 ilen = 0;
2694 for (i = 0; i < len; i += oAdvance) {
2695 get_fields(dsb, ibp, &fieldL, &fieldR);
2696 ibp += iAdvance;
2697 ilen += iAdvance;
2698 set_fields(obp, fieldL, fieldR);
2699 obp += oAdvance;
2700 if (ibp >= (BYTE *)(dsb->buffer + dsb->buflen))
2701 ibp = dsb->buffer; /* wrap */
2703 return (ilen);
2706 /* Mix in different sample rates */
2707 /* */
2708 /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
2709 /* Patent Pending :-] */
2711 /* Patent enhancements (c) 2000 Ove KÃ¥ven,
2712 * TransGaming Technologies Inc. */
2714 FIXME("(%p) Adjusting frequency: %ld -> %ld (need optimization)\n",
2715 dsb, dsb->freq, primarybuf->wfx.nSamplesPerSec);
2717 size = len / oAdvance;
2718 ilen = 0;
2719 ipos = dsb->buf_mixpos;
2720 for (i = 0; i < size; i++) {
2721 get_fields(dsb, (dsb->buffer + ipos), &fieldL, &fieldR);
2722 set_fields(obp, fieldL, fieldR);
2723 obp += oAdvance;
2725 dsb->freqAcc += dsb->freqAdjust;
2726 if (dsb->freqAcc >= (1<<DSOUND_FREQSHIFT)) {
2727 ULONG adv = (dsb->freqAcc>>DSOUND_FREQSHIFT) * iAdvance;
2728 dsb->freqAcc &= (1<<DSOUND_FREQSHIFT)-1;
2729 ipos += adv; ilen += adv;
2730 while (ipos >= dsb->buflen)
2731 ipos -= dsb->buflen;
2734 return ilen;
2737 static void DSOUND_MixerVol(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2739 INT i, inc = primarybuf->wfx.wBitsPerSample >> 3;
2740 BYTE *bpc = buf;
2741 INT16 *bps = (INT16 *) buf;
2743 TRACE("(%p) left = %lx, right = %lx\n", dsb,
2744 dsb->volpan.dwTotalLeftAmpFactor, dsb->volpan.dwTotalRightAmpFactor);
2745 if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->volpan.lPan == 0)) &&
2746 (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->volpan.lVolume == 0)) &&
2747 !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
2748 return; /* Nothing to do */
2750 /* If we end up with some bozo coder using panning or 3D sound */
2751 /* with a mono primary buffer, it could sound very weird using */
2752 /* this method. Oh well, tough patooties. */
2754 for (i = 0; i < len; i += inc) {
2755 INT val;
2757 switch (inc) {
2759 case 1:
2760 /* 8-bit WAV is unsigned, but we need to operate */
2761 /* on signed data for this to work properly */
2762 val = *bpc - 128;
2763 val = ((val * (i & inc ? dsb->volpan.dwTotalRightAmpFactor : dsb->volpan.dwTotalLeftAmpFactor)) >> 16);
2764 *bpc = val + 128;
2765 bpc++;
2766 break;
2767 case 2:
2768 /* 16-bit WAV is signed -- much better */
2769 val = *bps;
2770 val = ((val * ((i & inc) ? dsb->volpan.dwTotalRightAmpFactor : dsb->volpan.dwTotalLeftAmpFactor)) >> 16);
2771 *bps = val;
2772 bps++;
2773 break;
2774 default:
2775 /* Very ugly! */
2776 FIXME("MixerVol had a nasty error\n");
2781 #ifdef USE_DSOUND3D
2782 static void DSOUND_Mixer3D(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2784 BYTE *ibp, *obp;
2785 DWORD buflen, buf_mixpos;
2787 buflen = dsb->ds3db->buflen;
2788 buf_mixpos = (dsb->buf_mixpos * primarybuf->wfx.nBlockAlign) / dsb->wfx.nBlockAlign;
2789 ibp = dsb->ds3db->buffer + buf_mixpos;
2790 obp = buf;
2792 if (buf_mixpos > buflen) {
2793 FIXME("Major breakage\n");
2794 return;
2797 if (len <= (buf_mixpos + buflen))
2798 memcpy(obp, ibp, len);
2799 else { /* wrap */
2800 memcpy(obp, ibp, buflen - buf_mixpos);
2801 memcpy(obp + (buflen - buf_mixpos),
2802 dsb->buffer,
2803 len - (buflen - buf_mixpos));
2805 return;
2807 #endif
2809 static void *tmp_buffer;
2810 static size_t tmp_buffer_len = 0;
2812 static void *DSOUND_tmpbuffer(size_t len)
2814 if (len>tmp_buffer_len) {
2815 void *new_buffer = realloc(tmp_buffer, len);
2816 if (new_buffer) {
2817 tmp_buffer = new_buffer;
2818 tmp_buffer_len = len;
2820 return new_buffer;
2822 return tmp_buffer;
2825 static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen)
2827 INT i, len, ilen, temp, field;
2828 INT advance = primarybuf->wfx.wBitsPerSample >> 3;
2829 BYTE *buf, *ibuf, *obuf;
2830 INT16 *ibufs, *obufs;
2832 len = fraglen;
2833 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
2834 temp = MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buflen,
2835 dsb->nAvgBytesPerSec) -
2836 MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buf_mixpos,
2837 dsb->nAvgBytesPerSec);
2838 len = (len > temp) ? temp : len;
2840 len &= ~3; /* 4 byte alignment */
2842 if (len == 0) {
2843 /* This should only happen if we aren't looping and temp < 4 */
2845 /* We skip the remainder, so check for possible events */
2846 DSOUND_CheckEvent(dsb, dsb->buflen - dsb->buf_mixpos);
2847 /* Stop */
2848 dsb->state = STATE_STOPPED;
2849 dsb->playpos = 0;
2850 dsb->buf_mixpos = 0;
2851 dsb->leadin = FALSE;
2852 /* Check for DSBPN_OFFSETSTOP */
2853 DSOUND_CheckEvent(dsb, 0);
2854 return 0;
2857 /* Been seeing segfaults in malloc() for some reason... */
2858 TRACE("allocating buffer (size = %d)\n", len);
2859 if ((buf = ibuf = (BYTE *) DSOUND_tmpbuffer(len)) == NULL)
2860 return 0;
2862 TRACE("MixInBuffer (%p) len = %d, dest = %ld\n", dsb, len, writepos);
2864 ilen = DSOUND_MixerNorm(dsb, ibuf, len);
2865 if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
2866 (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
2867 DSOUND_MixerVol(dsb, ibuf, len);
2869 obuf = primarybuf->buffer + writepos;
2870 for (i = 0; i < len; i += advance) {
2871 obufs = (INT16 *) obuf;
2872 ibufs = (INT16 *) ibuf;
2873 if (primarybuf->wfx.wBitsPerSample == 8) {
2874 /* 8-bit WAV is unsigned */
2875 field = (*ibuf - 128);
2876 field += (*obuf - 128);
2877 field = field > 127 ? 127 : field;
2878 field = field < -128 ? -128 : field;
2879 *obuf = field + 128;
2880 } else {
2881 /* 16-bit WAV is signed */
2882 field = *ibufs;
2883 field += *obufs;
2884 field = field > 32767 ? 32767 : field;
2885 field = field < -32768 ? -32768 : field;
2886 *obufs = field;
2888 ibuf += advance;
2889 obuf += advance;
2890 if (obuf >= (BYTE *)(primarybuf->buffer + primarybuf->buflen))
2891 obuf = primarybuf->buffer;
2893 /* free(buf); */
2895 if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY)
2896 DSOUND_CheckEvent(dsb, ilen);
2898 if (dsb->leadin && (dsb->startpos > dsb->buf_mixpos) && (dsb->startpos <= dsb->buf_mixpos + ilen)) {
2899 /* HACK... leadin should be reset when the PLAY position reaches the startpos,
2900 * not the MIX position... but if the sound buffer is bigger than our prebuffering
2901 * (which must be the case for the streaming buffers that need this hack anyway)
2902 * plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
2903 dsb->leadin = FALSE;
2906 dsb->buf_mixpos += ilen;
2908 if (dsb->buf_mixpos >= dsb->buflen) {
2909 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
2910 dsb->state = STATE_STOPPED;
2911 dsb->playpos = 0;
2912 dsb->buf_mixpos = 0;
2913 dsb->leadin = FALSE;
2914 DSOUND_CheckEvent(dsb, 0); /* For DSBPN_OFFSETSTOP */
2915 } else {
2916 /* wrap */
2917 while (dsb->buf_mixpos >= dsb->buflen)
2918 dsb->buf_mixpos -= dsb->buflen;
2919 if (dsb->leadin && (dsb->startpos <= dsb->buf_mixpos))
2920 dsb->leadin = FALSE; /* HACK: see above */
2924 return len;
2927 static void DSOUND_PhaseCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD len)
2929 INT i, ilen, field;
2930 INT advance = primarybuf->wfx.wBitsPerSample >> 3;
2931 BYTE *buf, *ibuf, *obuf;
2932 INT16 *ibufs, *obufs;
2934 len &= ~3; /* 4 byte alignment */
2936 TRACE("allocating buffer (size = %ld)\n", len);
2937 if ((buf = ibuf = (BYTE *) DSOUND_tmpbuffer(len)) == NULL)
2938 return;
2940 TRACE("PhaseCancel (%p) len = %ld, dest = %ld\n", dsb, len, writepos);
2942 ilen = DSOUND_MixerNorm(dsb, ibuf, len);
2943 if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
2944 (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
2945 DSOUND_MixerVol(dsb, ibuf, len);
2947 /* subtract instead of add, to phase out premixed data */
2948 obuf = primarybuf->buffer + writepos;
2949 for (i = 0; i < len; i += advance) {
2950 obufs = (INT16 *) obuf;
2951 ibufs = (INT16 *) ibuf;
2952 if (primarybuf->wfx.wBitsPerSample == 8) {
2953 /* 8-bit WAV is unsigned */
2954 field = (*ibuf - 128);
2955 field -= (*obuf - 128);
2956 field = field > 127 ? 127 : field;
2957 field = field < -128 ? -128 : field;
2958 *obuf = field + 128;
2959 } else {
2960 /* 16-bit WAV is signed */
2961 field = *ibufs;
2962 field -= *obufs;
2963 field = field > 32767 ? 32767 : field;
2964 field = field < -32768 ? -32768 : field;
2965 *obufs = field;
2967 ibuf += advance;
2968 obuf += advance;
2969 if (obuf >= (BYTE *)(primarybuf->buffer + primarybuf->buflen))
2970 obuf = primarybuf->buffer;
2972 /* free(buf); */
2975 static void DSOUND_MixCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, BOOL cancel)
2977 DWORD size, flen, len, npos, nlen;
2978 INT iAdvance = dsb->wfx.nBlockAlign;
2979 INT oAdvance = primarybuf->wfx.nBlockAlign;
2980 /* determine amount of premixed data to cancel */
2981 DWORD primary_done =
2982 ((dsb->primary_mixpos < writepos) ? primarybuf->buflen : 0) +
2983 dsb->primary_mixpos - writepos;
2985 TRACE("(%p, %ld), buf_mixpos=%ld\n", dsb, writepos, dsb->buf_mixpos);
2987 /* backtrack the mix position */
2988 size = primary_done / oAdvance;
2989 flen = size * dsb->freqAdjust;
2990 len = (flen >> DSOUND_FREQSHIFT) * iAdvance;
2991 flen &= (1<<DSOUND_FREQSHIFT)-1;
2992 while (dsb->freqAcc < flen) {
2993 len += iAdvance;
2994 dsb->freqAcc += 1<<DSOUND_FREQSHIFT;
2996 len %= dsb->buflen;
2997 npos = ((dsb->buf_mixpos < len) ? dsb->buflen : 0) +
2998 dsb->buf_mixpos - len;
2999 if (dsb->leadin && (dsb->startpos > npos) && (dsb->startpos <= npos + len)) {
3000 /* stop backtracking at startpos */
3001 npos = dsb->startpos;
3002 len = ((dsb->buf_mixpos < npos) ? dsb->buflen : 0) +
3003 dsb->buf_mixpos - npos;
3004 flen = dsb->freqAcc;
3005 nlen = len / dsb->wfx.nBlockAlign;
3006 nlen = ((nlen << DSOUND_FREQSHIFT) + flen) / dsb->freqAdjust;
3007 nlen *= primarybuf->wfx.nBlockAlign;
3008 writepos =
3009 ((dsb->primary_mixpos < nlen) ? primarybuf->buflen : 0) +
3010 dsb->primary_mixpos - nlen;
3013 dsb->freqAcc -= flen;
3014 dsb->buf_mixpos = npos;
3015 dsb->primary_mixpos = writepos;
3017 TRACE("new buf_mixpos=%ld, primary_mixpos=%ld (len=%ld)\n",
3018 dsb->buf_mixpos, dsb->primary_mixpos, len);
3020 if (cancel) DSOUND_PhaseCancel(dsb, writepos, len);
3023 static void DSOUND_MixCancelAt(IDirectSoundBufferImpl *dsb, DWORD buf_writepos)
3025 #if 0
3026 DWORD i, size, flen, len, npos, nlen;
3027 INT iAdvance = dsb->wfx.nBlockAlign;
3028 INT oAdvance = primarybuf->wfx.nBlockAlign;
3029 /* determine amount of premixed data to cancel */
3030 DWORD buf_done =
3031 ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
3032 dsb->buf_mixpos - buf_writepos;
3033 #endif
3035 WARN("(%p, %ld), buf_mixpos=%ld\n", dsb, buf_writepos, dsb->buf_mixpos);
3036 /* since this is not implemented yet, just cancel *ALL* prebuffering for now
3037 * (which is faster anyway when there's one a single secondary buffer) */
3038 primarybuf->need_remix = TRUE;
3041 static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD playpos, DWORD writepos, DWORD mixlen)
3043 DWORD len, slen;
3044 /* determine this buffer's write position */
3045 DWORD buf_writepos = DSOUND_CalcPlayPosition(dsb, dsb->state & primarybuf->state, writepos,
3046 writepos, dsb->primary_mixpos, dsb->buf_mixpos);
3047 /* determine how much already-mixed data exists */
3048 DWORD buf_done =
3049 ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
3050 dsb->buf_mixpos - buf_writepos;
3051 DWORD primary_done =
3052 ((dsb->primary_mixpos < writepos) ? primarybuf->buflen : 0) +
3053 dsb->primary_mixpos - writepos;
3054 DWORD adv_done =
3055 ((primarybuf->buf_mixpos < writepos) ? primarybuf->buflen : 0) +
3056 primarybuf->buf_mixpos - writepos;
3057 int still_behind;
3059 TRACE("buf_writepos=%ld, primary_writepos=%ld\n", buf_writepos, writepos);
3060 TRACE("buf_done=%ld, primary_done=%ld\n", buf_done, primary_done);
3061 TRACE("buf_mixpos=%ld, primary_mixpos=%ld, mixlen=%ld\n", dsb->buf_mixpos, dsb->primary_mixpos,
3062 mixlen);
3063 TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb->playflags, dsb->startpos, dsb->leadin);
3065 /* save write position for non-GETCURRENTPOSITION2... */
3066 dsb->playpos = buf_writepos;
3068 /* check whether CalcPlayPosition detected a mixing underrun */
3069 if ((buf_done == 0) && (dsb->primary_mixpos != writepos)) {
3070 /* it did, but did we have more to play? */
3071 if ((dsb->playflags & DSBPLAY_LOOPING) ||
3072 (dsb->buf_mixpos < dsb->buflen)) {
3073 /* yes, have to recover */
3074 ERR("underrun on sound buffer %p\n", dsb);
3075 TRACE("recovering from underrun: primary_mixpos=%ld\n", writepos);
3077 dsb->primary_mixpos = writepos;
3078 primary_done = 0;
3080 /* determine how far ahead we should mix */
3081 if (((dsb->playflags & DSBPLAY_LOOPING) ||
3082 (dsb->leadin && (dsb->probably_valid_to != 0))) &&
3083 !(dsb->dsbd.dwFlags & DSBCAPS_STATIC)) {
3084 /* if this is a streaming buffer, it typically means that
3085 * we should defer mixing past probably_valid_to as long
3086 * as we can, to avoid unnecessary remixing */
3087 /* the heavy-looking calculations shouldn't be that bad,
3088 * as any game isn't likely to be have more than 1 or 2
3089 * streaming buffers in use at any time anyway... */
3090 DWORD probably_valid_left =
3091 (dsb->probably_valid_to == (DWORD)-1) ? dsb->buflen :
3092 ((dsb->probably_valid_to < buf_writepos) ? dsb->buflen : 0) +
3093 dsb->probably_valid_to - buf_writepos;
3094 /* check for leadin condition */
3095 if ((probably_valid_left == 0) &&
3096 (dsb->probably_valid_to == dsb->startpos) &&
3097 dsb->leadin)
3098 probably_valid_left = dsb->buflen;
3099 TRACE("streaming buffer probably_valid_to=%ld, probably_valid_left=%ld\n",
3100 dsb->probably_valid_to, probably_valid_left);
3101 /* check whether the app's time is already up */
3102 if (probably_valid_left < dsb->writelead) {
3103 WARN("probably_valid_to now within writelead, possible streaming underrun\n");
3104 /* once we pass the point of no return,
3105 * no reason to hold back anymore */
3106 dsb->probably_valid_to = (DWORD)-1;
3107 /* we just have to go ahead and mix what we have,
3108 * there's no telling what the app is thinking anyway */
3109 } else {
3110 /* adjust for our frequency and our sample size */
3111 probably_valid_left = MulDiv(probably_valid_left,
3112 1 << DSOUND_FREQSHIFT,
3113 dsb->wfx.nBlockAlign * dsb->freqAdjust) *
3114 primarybuf->wfx.nBlockAlign;
3115 /* check whether to clip mix_len */
3116 if (probably_valid_left < mixlen) {
3117 TRACE("clipping to probably_valid_left=%ld\n", probably_valid_left);
3118 mixlen = probably_valid_left;
3122 /* cut mixlen with what's already been mixed */
3123 if (mixlen < primary_done) {
3124 /* huh? and still CalcPlayPosition didn't
3125 * detect an underrun? */
3126 FIXME("problem with underrun detection (mixlen=%ld < primary_done=%ld)\n", mixlen, primary_done);
3127 return 0;
3129 len = mixlen - primary_done;
3130 TRACE("remaining mixlen=%ld\n", len);
3132 if (len < primarybuf->dsound->fraglen) {
3133 /* smaller than a fragment, wait until it gets larger
3134 * before we take the mixing overhead */
3135 TRACE("mixlen not worth it, deferring mixing\n");
3136 return 0;
3139 /* ok, we know how much to mix, let's go */
3140 still_behind = (adv_done > primary_done);
3141 while (len) {
3142 slen = primarybuf->buflen - dsb->primary_mixpos;
3143 if (slen > len) slen = len;
3144 slen = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, slen);
3146 if ((dsb->primary_mixpos < primarybuf->buf_mixpos) &&
3147 (dsb->primary_mixpos + slen >= primarybuf->buf_mixpos))
3148 still_behind = FALSE;
3150 dsb->primary_mixpos += slen; len -= slen;
3151 while (dsb->primary_mixpos >= primarybuf->buflen)
3152 dsb->primary_mixpos -= primarybuf->buflen;
3154 if ((dsb->state == STATE_STOPPED) || !slen) break;
3156 TRACE("new primary_mixpos=%ld, primary_advbase=%ld\n", dsb->primary_mixpos, primarybuf->buf_mixpos);
3157 TRACE("mixed data len=%ld, still_behind=%d\n", mixlen-len, still_behind);
3158 /* return how far we think the primary buffer can
3159 * advance its underrun detector...*/
3160 if (still_behind) return 0;
3161 if ((mixlen - len) < primary_done) return 0;
3162 slen = ((dsb->primary_mixpos < primarybuf->buf_mixpos) ?
3163 primarybuf->buflen : 0) + dsb->primary_mixpos -
3164 primarybuf->buf_mixpos;
3165 if (slen > mixlen) {
3166 /* the primary_done and still_behind checks above should have worked */
3167 FIXME("problem with advancement calculation (advlen=%ld > mixlen=%ld)\n", slen, mixlen);
3168 slen = 0;
3170 return slen;
3173 static DWORD DSOUND_MixToPrimary(DWORD playpos, DWORD writepos, DWORD mixlen, BOOL recover)
3175 INT i, len, maxlen = 0;
3176 IDirectSoundBufferImpl *dsb;
3178 TRACE("(%ld,%ld,%ld)\n", playpos, writepos, mixlen);
3179 for (i = dsound->nrofbuffers - 1; i >= 0; i--) {
3180 dsb = dsound->buffers[i];
3182 if (!dsb || !(ICOM_VTBL(dsb)))
3183 continue;
3184 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
3185 TRACE("Checking %p, mixlen=%ld\n", dsb, mixlen);
3186 EnterCriticalSection(&(dsb->lock));
3187 if (dsb->state == STATE_STOPPING) {
3188 DSOUND_MixCancel(dsb, writepos, TRUE);
3189 dsb->state = STATE_STOPPED;
3190 } else {
3191 if ((dsb->state == STATE_STARTING) || recover)
3192 dsb->primary_mixpos = writepos;
3193 len = DSOUND_MixOne(dsb, playpos, writepos, mixlen);
3194 if (dsb->state == STATE_STARTING)
3195 dsb->state = STATE_PLAYING;
3196 maxlen = (len > maxlen) ? len : maxlen;
3198 LeaveCriticalSection(&(dsb->lock));
3202 return maxlen;
3205 static void DSOUND_MixReset(DWORD writepos)
3207 INT i;
3208 IDirectSoundBufferImpl *dsb;
3209 int nfiller;
3211 TRACE("(%ld)\n", writepos);
3213 /* the sound of silence */
3214 nfiller = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
3216 /* reset all buffer mix positions */
3217 for (i = dsound->nrofbuffers - 1; i >= 0; i--) {
3218 dsb = dsound->buffers[i];
3220 if (!dsb || !(ICOM_VTBL(dsb)))
3221 continue;
3222 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
3223 TRACE("Resetting %p\n", dsb);
3224 EnterCriticalSection(&(dsb->lock));
3225 if (dsb->state == STATE_STOPPING) {
3226 dsb->state = STATE_STOPPED;
3228 else if (dsb->state == STATE_STARTING) {
3229 /* nothing */
3230 } else {
3231 DSOUND_MixCancel(dsb, writepos, FALSE);
3233 LeaveCriticalSection(&(dsb->lock));
3237 /* wipe out premixed data */
3238 if (primarybuf->buf_mixpos < writepos) {
3239 memset(primarybuf->buffer + writepos, nfiller, primarybuf->buflen - writepos);
3240 memset(primarybuf->buffer, nfiller, primarybuf->buf_mixpos);
3241 } else {
3242 memset(primarybuf->buffer + writepos, nfiller, primarybuf->buf_mixpos - writepos);
3245 /* reset primary mix position */
3246 primarybuf->buf_mixpos = writepos;
3249 static void DSOUND_CheckReset(IDirectSoundImpl *dsound, DWORD writepos)
3251 if (primarybuf->need_remix) {
3252 DSOUND_MixReset(writepos);
3253 primarybuf->need_remix = FALSE;
3254 /* maximize Half-Life performance */
3255 dsound->prebuf = DS_SND_QUEUE_MIN;
3256 } else {
3257 /* if (dsound->prebuf < DS_SND_QUEUE_MAX) dsound->prebuf++; */
3259 TRACE("premix adjust: %d\n", dsound->prebuf);
3262 static void DSOUND_WaveQueue(IDirectSoundImpl *dsound, DWORD mixq)
3264 if (mixq + dsound->pwqueue > DS_HEL_QUEUE) mixq = DS_HEL_QUEUE - dsound->pwqueue;
3265 TRACE("queueing %ld buffers, starting at %d\n", mixq, dsound->pwwrite);
3266 for (; mixq; mixq--) {
3267 waveOutWrite(dsound->hwo, dsound->pwave[dsound->pwwrite], sizeof(WAVEHDR));
3268 dsound->pwwrite++;
3269 if (dsound->pwwrite >= DS_HEL_FRAGS) dsound->pwwrite = 0;
3270 dsound->pwqueue++;
3274 /* #define SYNC_CALLBACK */
3276 static void DSOUND_PerformMix(void)
3278 int nfiller;
3279 BOOL forced;
3280 HRESULT hres;
3282 EnterCriticalSection(&(dsound->lock));
3284 if (!primarybuf || !primarybuf->ref) {
3285 /* seems the primary buffer is currently being released */
3286 LeaveCriticalSection(&(dsound->lock));
3287 return;
3290 /* the sound of silence */
3291 nfiller = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
3293 /* whether the primary is forced to play even without secondary buffers */
3294 forced = ((primarybuf->state == STATE_PLAYING) || (primarybuf->state == STATE_STARTING));
3296 TRACE("entering at %ld\n", GetTickCount());
3297 if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
3298 BOOL paused = ((primarybuf->state == STATE_STOPPED) || (primarybuf->state == STATE_STARTING));
3299 /* FIXME: document variables */
3300 DWORD playpos, writepos, inq, maxq, frag;
3301 if (primarybuf->hwbuf) {
3302 hres = IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, &writepos);
3303 if (hres) {
3304 LeaveCriticalSection(&(dsound->lock));
3305 return;
3307 /* Well, we *could* do Just-In-Time mixing using the writepos,
3308 * but that's a little bit ambitious and unnecessary... */
3309 /* rather add our safety margin to the writepos, if we're playing */
3310 if (!paused) {
3311 writepos += primarybuf->writelead;
3312 while (writepos >= primarybuf->buflen)
3313 writepos -= primarybuf->buflen;
3314 } else writepos = playpos;
3316 else {
3317 playpos = dsound->pwplay * dsound->fraglen;
3318 writepos = playpos;
3319 if (!paused) {
3320 writepos += DS_HEL_MARGIN * dsound->fraglen;
3321 while (writepos >= primarybuf->buflen)
3322 writepos -= primarybuf->buflen;
3325 TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld\n",
3326 playpos,writepos,primarybuf->playpos,primarybuf->buf_mixpos);
3327 /* wipe out just-played sound data */
3328 if (playpos < primarybuf->playpos) {
3329 memset(primarybuf->buffer + primarybuf->playpos, nfiller, primarybuf->buflen - primarybuf->playpos);
3330 memset(primarybuf->buffer, nfiller, playpos);
3331 } else {
3332 memset(primarybuf->buffer + primarybuf->playpos, nfiller, playpos - primarybuf->playpos);
3334 primarybuf->playpos = playpos;
3336 EnterCriticalSection(&(primarybuf->lock));
3338 /* reset mixing if necessary */
3339 DSOUND_CheckReset(dsound, writepos);
3341 /* check how much prebuffering is left */
3342 inq = primarybuf->buf_mixpos;
3343 if (inq < writepos)
3344 inq += primarybuf->buflen;
3345 inq -= writepos;
3347 /* find the maximum we can prebuffer */
3348 if (!paused) {
3349 maxq = playpos;
3350 if (maxq < writepos)
3351 maxq += primarybuf->buflen;
3352 maxq -= writepos;
3353 } else maxq = primarybuf->buflen;
3355 /* clip maxq to dsound->prebuf */
3356 frag = dsound->prebuf * dsound->fraglen;
3357 if (maxq > frag) maxq = frag;
3359 /* check for consistency */
3360 if (inq > maxq) {
3361 /* the playback position must have passed our last
3362 * mixed position, i.e. it's an underrun, or we have
3363 * nothing more to play */
3364 TRACE("reached end of mixed data (inq=%ld, maxq=%ld)\n", inq, maxq);
3365 inq = 0;
3366 /* stop the playback now, to allow buffers to refill */
3367 if (primarybuf->state == STATE_PLAYING) {
3368 primarybuf->state = STATE_STARTING;
3370 else if (primarybuf->state == STATE_STOPPING) {
3371 primarybuf->state = STATE_STOPPED;
3373 else {
3374 /* how can we have an underrun if we aren't playing? */
3375 WARN("unexpected primary state (%ld)\n", primarybuf->state);
3377 #ifdef SYNC_CALLBACK
3378 /* DSOUND_callback may need this lock */
3379 LeaveCriticalSection(&(primarybuf->lock));
3380 #endif
3381 DSOUND_PrimaryStop(primarybuf);
3382 #ifdef SYNC_CALLBACK
3383 EnterCriticalSection(&(primarybuf->lock));
3384 #endif
3385 if (primarybuf->hwbuf) {
3386 /* the Stop is supposed to reset play position to beginning of buffer */
3387 /* unfortunately, OSS is not able to do so, so get current pointer */
3388 hres = IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, NULL);
3389 if (hres) {
3390 LeaveCriticalSection(&(dsound->lock));
3391 LeaveCriticalSection(&(primarybuf->lock));
3392 return;
3394 } else {
3395 playpos = dsound->pwplay * dsound->fraglen;
3397 writepos = playpos;
3398 primarybuf->playpos = playpos;
3399 primarybuf->buf_mixpos = writepos;
3400 inq = 0;
3401 maxq = primarybuf->buflen;
3402 if (maxq > frag) maxq = frag;
3403 memset(primarybuf->buffer, nfiller, primarybuf->buflen);
3404 paused = TRUE;
3407 /* do the mixing */
3408 frag = DSOUND_MixToPrimary(playpos, writepos, maxq, paused);
3409 if (forced) frag = maxq - inq;
3410 primarybuf->buf_mixpos += frag;
3411 while (primarybuf->buf_mixpos >= primarybuf->buflen)
3412 primarybuf->buf_mixpos -= primarybuf->buflen;
3414 if (frag) {
3415 /* buffers have been filled, restart playback */
3416 if (primarybuf->state == STATE_STARTING) {
3417 primarybuf->state = STATE_PLAYING;
3419 else if (primarybuf->state == STATE_STOPPED) {
3420 /* the primarybuf is supposed to play if there's something to play
3421 * even if it is reported as stopped, so don't let this confuse you */
3422 primarybuf->state = STATE_STOPPING;
3424 LeaveCriticalSection(&(primarybuf->lock));
3425 if (paused) {
3426 DSOUND_PrimaryPlay(primarybuf);
3427 TRACE("starting playback\n");
3430 else
3431 LeaveCriticalSection(&(primarybuf->lock));
3432 } else {
3433 /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
3434 if (primarybuf->state == STATE_STARTING) {
3435 DSOUND_PrimaryPlay(primarybuf);
3436 primarybuf->state = STATE_PLAYING;
3438 else if (primarybuf->state == STATE_STOPPING) {
3439 DSOUND_PrimaryStop(primarybuf);
3440 primarybuf->state = STATE_STOPPED;
3443 TRACE("completed processing at %ld\n", GetTickCount());
3444 LeaveCriticalSection(&(dsound->lock));
3447 static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
3449 if (!dsound || !primarybuf) {
3450 ERR("dsound died without killing us?\n");
3451 timeKillEvent(timerID);
3452 timeEndPeriod(DS_TIME_RES);
3453 return;
3456 TRACE("entered\n");
3457 DSOUND_PerformMix();
3460 static void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
3462 IDirectSoundImpl* This = (IDirectSoundImpl*)dwUser;
3463 TRACE("entering at %ld, msg=%08x\n", GetTickCount(), msg);
3464 if (msg == MM_WOM_DONE) {
3465 DWORD inq, mixq, fraglen, buflen, pwplay, playpos, mixpos;
3466 if (This->pwqueue == (DWORD)-1) {
3467 TRACE("completed due to reset\n");
3468 return;
3470 /* it could be a bad idea to enter critical section here... if there's lock contention,
3471 * the resulting scheduling delays might obstruct the winmm player thread */
3472 #ifdef SYNC_CALLBACK
3473 EnterCriticalSection(&(primarybuf->lock));
3474 #endif
3475 /* retrieve current values */
3476 fraglen = dsound->fraglen;
3477 buflen = primarybuf->buflen;
3478 pwplay = dsound->pwplay;
3479 playpos = pwplay * fraglen;
3480 mixpos = primarybuf->buf_mixpos;
3481 /* check remaining mixed data */
3482 inq = ((mixpos < playpos) ? buflen : 0) + mixpos - playpos;
3483 mixq = inq / fraglen;
3484 if ((inq - (mixq * fraglen)) > 0) mixq++;
3485 /* complete the playing buffer */
3486 TRACE("done playing primary pos=%ld\n", playpos);
3487 pwplay++;
3488 if (pwplay >= DS_HEL_FRAGS) pwplay = 0;
3489 /* write new values */
3490 dsound->pwplay = pwplay;
3491 dsound->pwqueue--;
3492 /* queue new buffer if we have data for it */
3493 if (inq>1) DSOUND_WaveQueue(This, inq-1);
3494 #ifdef SYNC_CALLBACK
3495 LeaveCriticalSection(&(primarybuf->lock));
3496 #endif
3498 TRACE("completed\n");
3501 /*******************************************************************************
3502 * DirectSoundCreate (DSOUND.1)
3504 HRESULT WINAPI DirectSoundCreate(REFGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter )
3506 IDirectSoundImpl** ippDS=(IDirectSoundImpl**)ppDS;
3507 PIDSDRIVER drv = NULL;
3508 WAVEOUTCAPSA wcaps;
3509 unsigned wod, wodn;
3510 HRESULT err = DS_OK;
3512 if (lpGUID)
3513 TRACE("(%p,%p,%p)\n",lpGUID,ippDS,pUnkOuter);
3514 else
3515 TRACE("DirectSoundCreate (%p)\n", ippDS);
3517 if (ippDS == NULL)
3518 return DSERR_INVALIDPARAM;
3520 if (primarybuf) {
3521 IDirectSound_AddRef((LPDIRECTSOUND)dsound);
3522 *ippDS = dsound;
3523 return DS_OK;
3526 /* Enumerate WINMM audio devices and find the one we want */
3527 wodn = waveOutGetNumDevs();
3528 if (!wodn) return DSERR_NODRIVER;
3530 /* FIXME: How do we find the GUID of an audio device? */
3531 wod = 0; /* start at the first audio device */
3533 /* Get output device caps */
3534 waveOutGetDevCapsA(wod, &wcaps, sizeof(wcaps));
3535 /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
3536 waveOutMessage(wod, DRV_QUERYDSOUNDIFACE, (DWORD)&drv, 0);
3538 /* Allocate memory */
3539 *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundImpl));
3540 if (*ippDS == NULL)
3541 return DSERR_OUTOFMEMORY;
3543 ICOM_VTBL(*ippDS) = &dsvt;
3544 (*ippDS)->ref = 1;
3546 (*ippDS)->driver = drv;
3547 (*ippDS)->fraglen = 0;
3548 (*ippDS)->priolevel = DSSCL_NORMAL;
3549 (*ippDS)->nrofbuffers = 0;
3550 (*ippDS)->buffers = NULL;
3551 (*ippDS)->primary = NULL;
3552 (*ippDS)->listener = NULL;
3554 (*ippDS)->prebuf = DS_SND_QUEUE_MAX;
3556 /* Get driver description */
3557 if (drv) {
3558 IDsDriver_GetDriverDesc(drv,&((*ippDS)->drvdesc));
3559 } else {
3560 /* if no DirectSound interface available, use WINMM API instead */
3561 (*ippDS)->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
3562 (*ippDS)->drvdesc.dnDevNode = wod; /* FIXME? */
3565 /* Set default wave format (may need it for waveOutOpen) */
3566 (*ippDS)->wfx.wFormatTag = WAVE_FORMAT_PCM;
3567 (*ippDS)->wfx.nChannels = 2;
3568 (*ippDS)->wfx.nSamplesPerSec = 22050;
3569 (*ippDS)->wfx.nAvgBytesPerSec = 44100;
3570 (*ippDS)->wfx.nBlockAlign = 2;
3571 (*ippDS)->wfx.wBitsPerSample = 8;
3573 /* If the driver requests being opened through MMSYSTEM
3574 * (which is recommended by the DDK), it is supposed to happen
3575 * before the DirectSound interface is opened */
3576 if ((*ippDS)->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
3578 /* FIXME: is this right? */
3580 (*ippDS)->drvdesc.dnDevNode = 0;
3581 err = DSERR_ALLOCATED;
3583 /* if this device is busy try the next one */
3584 while((err == DSERR_ALLOCATED) &&
3585 ((*ippDS)->drvdesc.dnDevNode < wodn))
3587 err = mmErr(waveOutOpen(&((*ippDS)->hwo),
3588 (*ippDS)->drvdesc.dnDevNode, &((*ippDS)->wfx),
3589 (DWORD)DSOUND_callback, (DWORD)(*ippDS),
3590 CALLBACK_FUNCTION | WAVE_DIRECTSOUND));
3591 (*ippDS)->drvdesc.dnDevNode++; /* next wave device */
3594 (*ippDS)->drvdesc.dnDevNode--; /* take away last increment */
3598 if (drv && (err == DS_OK))
3599 err = IDsDriver_Open(drv);
3601 /* FIXME: do we want to handle a temporarily busy device? */
3602 if (err != DS_OK) {
3603 HeapFree(GetProcessHeap(),0,*ippDS);
3604 *ippDS = NULL;
3605 return err;
3608 /* the driver is now open, so it's now allowed to call GetCaps */
3609 if (drv) {
3610 IDsDriver_GetCaps(drv,&((*ippDS)->drvcaps));
3611 } else {
3612 unsigned c;
3614 /* FIXME: look at wcaps */
3615 (*ippDS)->drvcaps.dwFlags =
3616 DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
3617 if (DS_EMULDRIVER)
3618 (*ippDS)->drvcaps.dwFlags |= DSCAPS_EMULDRIVER;
3620 /* Allocate memory for HEL buffer headers */
3621 for (c=0; c<DS_HEL_FRAGS; c++) {
3622 (*ippDS)->pwave[c] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEHDR));
3623 if (!(*ippDS)->pwave[c]) {
3624 /* Argh, out of memory */
3625 while (c--) {
3626 HeapFree(GetProcessHeap(),0,(*ippDS)->pwave[c]);
3627 waveOutClose((*ippDS)->hwo);
3628 HeapFree(GetProcessHeap(),0,*ippDS);
3629 *ippDS = NULL;
3630 return DSERR_OUTOFMEMORY;
3636 InitializeCriticalSection(&((*ippDS)->lock));
3638 if (!dsound) {
3639 dsound = (*ippDS);
3640 if (primarybuf == NULL) {
3641 DSBUFFERDESC dsbd;
3642 HRESULT hr;
3644 dsbd.dwSize = sizeof(DSBUFFERDESC);
3645 dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
3646 dsbd.dwBufferBytes = 0;
3647 dsbd.lpwfxFormat = &(dsound->wfx);
3648 hr = IDirectSound_CreateSoundBuffer(*ppDS, &dsbd, (LPDIRECTSOUNDBUFFER*)&primarybuf, NULL);
3649 if (hr != DS_OK)
3650 return hr;
3652 /* dsound->primary is NULL - don't need to Release */
3653 dsound->primary = primarybuf;
3654 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)primarybuf);
3656 timeBeginPeriod(DS_TIME_RES);
3657 dsound->timerID = timeSetEvent(DS_TIME_DEL, DS_TIME_RES, DSOUND_timer,
3658 (DWORD)dsound, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
3660 return DS_OK;
3663 /***************************************************************************
3664 * DirectSoundCaptureCreate [DSOUND.6]
3666 * Create and initialize a DirectSoundCapture interface
3668 * RETURNS
3669 * Success: DS_OK
3670 * Failure: DSERR_NOAGGREGATION, DSERR_ALLOCATED, DSERR_INVALIDPARAM,
3671 * DSERR_OUTOFMEMORY
3673 HRESULT WINAPI DirectSoundCaptureCreate(
3674 LPCGUID lpcGUID,
3675 LPDIRECTSOUNDCAPTURE* lplpDSC,
3676 LPUNKNOWN pUnkOuter )
3678 TRACE("(%s,%p,%p)\n", debugstr_guid(lpcGUID), lplpDSC, pUnkOuter);
3680 if( pUnkOuter ) {
3681 return DSERR_NOAGGREGATION;
3684 /* Default device? */
3685 if ( !lpcGUID ) {
3686 return DSOUND_CreateDirectSoundCapture( (LPVOID*)lplpDSC );
3689 FIXME( "Unknown GUID %s\n", debugstr_guid(lpcGUID) );
3690 *lplpDSC = NULL;
3692 return DSERR_OUTOFMEMORY;
3695 /***************************************************************************
3696 * DirectSoundCaptureEnumerateA [DSOUND.7]
3698 * Enumerate all DirectSound drivers installed in the system
3700 * RETURNS
3701 * Success: DS_OK
3702 * Failure: DSERR_INVALIDPARAM
3704 HRESULT WINAPI DirectSoundCaptureEnumerateA(
3705 LPDSENUMCALLBACKA lpDSEnumCallback,
3706 LPVOID lpContext)
3708 TRACE("(%p,%p)\n", lpDSEnumCallback, lpContext );
3710 if ( lpDSEnumCallback )
3711 lpDSEnumCallback(NULL,"WINE Primary Sound Capture Driver",
3712 "SoundCap",lpContext);
3715 return DS_OK;
3718 /***************************************************************************
3719 * DirectSoundCaptureEnumerateW [DSOUND.8]
3721 * Enumerate all DirectSound drivers installed in the system
3723 * RETURNS
3724 * Success: DS_OK
3725 * Failure: DSERR_INVALIDPARAM
3727 HRESULT WINAPI DirectSoundCaptureEnumerateW(
3728 LPDSENUMCALLBACKW lpDSEnumCallback,
3729 LPVOID lpContext)
3731 FIXME("(%p,%p):stub\n", lpDSEnumCallback, lpContext );
3732 return DS_OK;
3735 static HRESULT
3736 DSOUND_CreateDirectSoundCapture( LPVOID* ppobj )
3738 *ppobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( IDirectSoundCaptureImpl ) );
3740 if ( *ppobj == NULL ) {
3741 return DSERR_OUTOFMEMORY;
3745 ICOM_THIS(IDirectSoundCaptureImpl,*ppobj);
3747 This->ref = 1;
3748 ICOM_VTBL(This) = &dscvt;
3750 InitializeCriticalSection( &This->lock );
3753 return S_OK;
3756 static HRESULT WINAPI
3757 IDirectSoundCaptureImpl_QueryInterface(
3758 LPDIRECTSOUNDCAPTURE iface,
3759 REFIID riid,
3760 LPVOID* ppobj )
3762 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3764 FIXME( "(%p)->(%s,%p): stub\n", This, debugstr_guid(riid), ppobj );
3766 return E_FAIL;
3769 static ULONG WINAPI
3770 IDirectSoundCaptureImpl_AddRef( LPDIRECTSOUNDCAPTURE iface )
3772 ULONG uRef;
3773 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3775 EnterCriticalSection( &This->lock );
3777 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3778 uRef = ++(This->ref);
3780 LeaveCriticalSection( &This->lock );
3782 return uRef;
3785 static ULONG WINAPI
3786 IDirectSoundCaptureImpl_Release( LPDIRECTSOUNDCAPTURE iface )
3788 ULONG uRef;
3789 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3791 EnterCriticalSection( &This->lock );
3793 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3794 uRef = --(This->ref);
3796 LeaveCriticalSection( &This->lock );
3798 if ( uRef == 0 ) {
3799 DeleteCriticalSection( &This->lock );
3800 HeapFree( GetProcessHeap(), 0, This );
3803 return uRef;
3806 static HRESULT WINAPI
3807 IDirectSoundCaptureImpl_CreateCaptureBuffer(
3808 LPDIRECTSOUNDCAPTURE iface,
3809 LPCDSCBUFFERDESC lpcDSCBufferDesc,
3810 LPDIRECTSOUNDCAPTUREBUFFER* lplpDSCaptureBuffer,
3811 LPUNKNOWN pUnk )
3813 HRESULT hr;
3814 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3816 TRACE( "(%p)->(%p,%p,%p)\n", This, lpcDSCBufferDesc, lplpDSCaptureBuffer, pUnk );
3818 if ( pUnk ) {
3819 return DSERR_INVALIDPARAM;
3822 hr = DSOUND_CreateDirectSoundCaptureBuffer( lpcDSCBufferDesc, (LPVOID*)lplpDSCaptureBuffer );
3824 return hr;
3827 static HRESULT WINAPI
3828 IDirectSoundCaptureImpl_GetCaps(
3829 LPDIRECTSOUNDCAPTURE iface,
3830 LPDSCCAPS lpDSCCaps )
3832 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3834 FIXME( "(%p)->(%p): stub\n", This, lpDSCCaps );
3836 return DS_OK;
3839 static HRESULT WINAPI
3840 IDirectSoundCaptureImpl_Initialize(
3841 LPDIRECTSOUNDCAPTURE iface,
3842 LPCGUID lpcGUID )
3844 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3846 FIXME( "(%p)->(%p): stub\n", This, lpcGUID );
3848 return DS_OK;
3852 static ICOM_VTABLE(IDirectSoundCapture) dscvt =
3854 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3855 /* IUnknown methods */
3856 IDirectSoundCaptureImpl_QueryInterface,
3857 IDirectSoundCaptureImpl_AddRef,
3858 IDirectSoundCaptureImpl_Release,
3860 /* IDirectSoundCapture methods */
3861 IDirectSoundCaptureImpl_CreateCaptureBuffer,
3862 IDirectSoundCaptureImpl_GetCaps,
3863 IDirectSoundCaptureImpl_Initialize
3866 static HRESULT
3867 DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc, LPVOID* ppobj )
3870 FIXME( "(%p,%p): ignoring lpcDSCBufferDesc\n", lpcDSCBufferDesc, ppobj );
3872 *ppobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( IDirectSoundCaptureBufferImpl ) );
3874 if ( *ppobj == NULL ) {
3875 return DSERR_OUTOFMEMORY;
3879 ICOM_THIS(IDirectSoundCaptureBufferImpl,*ppobj);
3881 This->ref = 1;
3882 ICOM_VTBL(This) = &dscbvt;
3884 InitializeCriticalSection( &This->lock );
3887 return S_OK;
3891 static HRESULT WINAPI
3892 IDirectSoundCaptureBufferImpl_QueryInterface(
3893 LPDIRECTSOUNDCAPTUREBUFFER iface,
3894 REFIID riid,
3895 LPVOID* ppobj )
3897 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3899 FIXME( "(%p)->(%s,%p): stub\n", This, debugstr_guid(riid), ppobj );
3901 return E_FAIL;
3904 static ULONG WINAPI
3905 IDirectSoundCaptureBufferImpl_AddRef( LPDIRECTSOUNDCAPTUREBUFFER iface )
3907 ULONG uRef;
3908 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3910 EnterCriticalSection( &This->lock );
3912 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3913 uRef = ++(This->ref);
3915 LeaveCriticalSection( &This->lock );
3917 return uRef;
3920 static ULONG WINAPI
3921 IDirectSoundCaptureBufferImpl_Release( LPDIRECTSOUNDCAPTUREBUFFER iface )
3923 ULONG uRef;
3924 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3926 EnterCriticalSection( &This->lock );
3928 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3929 uRef = --(This->ref);
3931 LeaveCriticalSection( &This->lock );
3933 if ( uRef == 0 ) {
3934 DeleteCriticalSection( &This->lock );
3935 HeapFree( GetProcessHeap(), 0, This );
3938 return uRef;
3941 static HRESULT WINAPI
3942 IDirectSoundCaptureBufferImpl_GetCaps(
3943 LPDIRECTSOUNDCAPTUREBUFFER iface,
3944 LPDSCBCAPS lpDSCBCaps )
3946 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3948 FIXME( "(%p)->(%p): stub\n", This, lpDSCBCaps );
3950 return DS_OK;
3953 static HRESULT WINAPI
3954 IDirectSoundCaptureBufferImpl_GetCurrentPosition(
3955 LPDIRECTSOUNDCAPTUREBUFFER iface,
3956 LPDWORD lpdwCapturePosition,
3957 LPDWORD lpdwReadPosition )
3959 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3961 FIXME( "(%p)->(%p,%p): stub\n", This, lpdwCapturePosition, lpdwReadPosition );
3963 return DS_OK;
3966 static HRESULT WINAPI
3967 IDirectSoundCaptureBufferImpl_GetFormat(
3968 LPDIRECTSOUNDCAPTUREBUFFER iface,
3969 LPWAVEFORMATEX lpwfxFormat,
3970 DWORD dwSizeAllocated,
3971 LPDWORD lpdwSizeWritten )
3973 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3975 FIXME( "(%p)->(%p,0x%08lx,%p): stub\n", This, lpwfxFormat, dwSizeAllocated, lpdwSizeWritten );
3977 return DS_OK;
3980 static HRESULT WINAPI
3981 IDirectSoundCaptureBufferImpl_GetStatus(
3982 LPDIRECTSOUNDCAPTUREBUFFER iface,
3983 LPDWORD lpdwStatus )
3985 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3987 FIXME( "(%p)->(%p): stub\n", This, lpdwStatus );
3989 return DS_OK;
3992 static HRESULT WINAPI
3993 IDirectSoundCaptureBufferImpl_Initialize(
3994 LPDIRECTSOUNDCAPTUREBUFFER iface,
3995 LPDIRECTSOUNDCAPTURE lpDSC,
3996 LPCDSCBUFFERDESC lpcDSCBDesc )
3998 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4000 FIXME( "(%p)->(%p,%p): stub\n", This, lpDSC, lpcDSCBDesc );
4002 return DS_OK;
4005 static HRESULT WINAPI
4006 IDirectSoundCaptureBufferImpl_Lock(
4007 LPDIRECTSOUNDCAPTUREBUFFER iface,
4008 DWORD dwReadCusor,
4009 DWORD dwReadBytes,
4010 LPVOID* lplpvAudioPtr1,
4011 LPDWORD lpdwAudioBytes1,
4012 LPVOID* lplpvAudioPtr2,
4013 LPDWORD lpdwAudioBytes2,
4014 DWORD dwFlags )
4016 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4018 FIXME( "(%p)->(%08lu,%08lu,%p,%p,%p,%p,0x%08lx): stub\n", This, dwReadCusor, dwReadBytes, lplpvAudioPtr1, lpdwAudioBytes1, lplpvAudioPtr2, lpdwAudioBytes2, dwFlags );
4020 return DS_OK;
4023 static HRESULT WINAPI
4024 IDirectSoundCaptureBufferImpl_Start(
4025 LPDIRECTSOUNDCAPTUREBUFFER iface,
4026 DWORD dwFlags )
4028 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4030 FIXME( "(%p)->(0x%08lx): stub\n", This, dwFlags );
4032 return DS_OK;
4035 static HRESULT WINAPI
4036 IDirectSoundCaptureBufferImpl_Stop( LPDIRECTSOUNDCAPTUREBUFFER iface )
4038 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4040 FIXME( "(%p): stub\n", This );
4042 return DS_OK;
4045 static HRESULT WINAPI
4046 IDirectSoundCaptureBufferImpl_Unlock(
4047 LPDIRECTSOUNDCAPTUREBUFFER iface,
4048 LPVOID lpvAudioPtr1,
4049 DWORD dwAudioBytes1,
4050 LPVOID lpvAudioPtr2,
4051 DWORD dwAudioBytes2 )
4053 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4055 FIXME( "(%p)->(%p,%08lu,%p,%08lu): stub\n", This, lpvAudioPtr1, dwAudioBytes1, lpvAudioPtr2, dwAudioBytes2 );
4057 return DS_OK;
4061 static ICOM_VTABLE(IDirectSoundCaptureBuffer) dscbvt =
4063 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
4064 /* IUnknown methods */
4065 IDirectSoundCaptureBufferImpl_QueryInterface,
4066 IDirectSoundCaptureBufferImpl_AddRef,
4067 IDirectSoundCaptureBufferImpl_Release,
4069 /* IDirectSoundCaptureBuffer methods */
4070 IDirectSoundCaptureBufferImpl_GetCaps,
4071 IDirectSoundCaptureBufferImpl_GetCurrentPosition,
4072 IDirectSoundCaptureBufferImpl_GetFormat,
4073 IDirectSoundCaptureBufferImpl_GetStatus,
4074 IDirectSoundCaptureBufferImpl_Initialize,
4075 IDirectSoundCaptureBufferImpl_Lock,
4076 IDirectSoundCaptureBufferImpl_Start,
4077 IDirectSoundCaptureBufferImpl_Stop,
4078 IDirectSoundCaptureBufferImpl_Unlock
4081 /*******************************************************************************
4082 * DirectSound ClassFactory
4084 typedef struct
4086 /* IUnknown fields */
4087 ICOM_VFIELD(IClassFactory);
4088 DWORD ref;
4089 } IClassFactoryImpl;
4091 static HRESULT WINAPI
4092 DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
4093 ICOM_THIS(IClassFactoryImpl,iface);
4095 FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
4096 return E_NOINTERFACE;
4099 static ULONG WINAPI
4100 DSCF_AddRef(LPCLASSFACTORY iface) {
4101 ICOM_THIS(IClassFactoryImpl,iface);
4102 return ++(This->ref);
4105 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) {
4106 ICOM_THIS(IClassFactoryImpl,iface);
4107 /* static class, won't be freed */
4108 return --(This->ref);
4111 static HRESULT WINAPI DSCF_CreateInstance(
4112 LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
4114 ICOM_THIS(IClassFactoryImpl,iface);
4116 TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
4117 if ( IsEqualGUID( &IID_IDirectSound, riid ) ) {
4118 /* FIXME: reuse already created dsound if present? */
4119 return DirectSoundCreate(riid,(LPDIRECTSOUND*)ppobj,pOuter);
4121 return E_NOINTERFACE;
4124 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
4125 ICOM_THIS(IClassFactoryImpl,iface);
4126 FIXME("(%p)->(%d),stub!\n",This,dolock);
4127 return S_OK;
4130 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
4131 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
4132 DSCF_QueryInterface,
4133 DSCF_AddRef,
4134 DSCF_Release,
4135 DSCF_CreateInstance,
4136 DSCF_LockServer
4138 static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 };
4140 /*******************************************************************************
4141 * DllGetClassObject [DSOUND.5]
4142 * Retrieves class object from a DLL object
4144 * NOTES
4145 * Docs say returns STDAPI
4147 * PARAMS
4148 * rclsid [I] CLSID for the class object
4149 * riid [I] Reference to identifier of interface for class object
4150 * ppv [O] Address of variable to receive interface pointer for riid
4152 * RETURNS
4153 * Success: S_OK
4154 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
4155 * E_UNEXPECTED
4157 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
4159 TRACE("(%p,%p,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
4160 if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
4161 *ppv = (LPVOID)&DSOUND_CF;
4162 IClassFactory_AddRef((IClassFactory*)*ppv);
4163 return S_OK;
4166 FIXME("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
4167 return CLASS_E_CLASSNOTAVAILABLE;
4171 /*******************************************************************************
4172 * DllCanUnloadNow [DSOUND.4] Determines whether the DLL is in use.
4174 * RETURNS
4175 * Success: S_OK
4176 * Failure: S_FALSE
4178 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
4180 FIXME("(void): stub\n");
4181 return S_FALSE;