Use system metrics values in TOOLBAR_DrawPattern instead of hardcoded
[wine] / dlls / dsound / buffer.c
1 /*                      DirectSound
2  *
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998 Rob Riggs
5  * Copyright 2000-2002 TransGaming Technologies, Inc.
6  *
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.
11  *
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.
16  *
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
20  */
21
22 #include "config.h"
23 #include <assert.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <sys/fcntl.h>
28 #ifdef HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31 #include <stdlib.h>
32 #include <string.h>
33 #include <math.h>
34
35 #define NONAMELESSSTRUCT
36 #define NONAMELESSUNION
37 #include "windef.h"
38 #include "winbase.h"
39 #include "wingdi.h"
40 #include "winuser.h"
41 #include "winerror.h"
42 #include "mmsystem.h"
43 #include "winreg.h"
44 #include "winternl.h"
45 #include "mmddk.h"
46 #include "wine/windef16.h"
47 #include "wine/debug.h"
48 #include "dsound.h"
49 #include "dsdriver.h"
50 #include "dsound_private.h"
51
52 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
53
54 /*******************************************************************************
55  *              IDirectSoundNotify
56  */
57 static HRESULT WINAPI IDirectSoundNotifyImpl_QueryInterface(
58         LPDIRECTSOUNDNOTIFY iface,REFIID riid,LPVOID *ppobj
59 ) {
60         ICOM_THIS(IDirectSoundNotifyImpl,iface);
61         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
62
63         if (This->dsb == NULL) {
64                 WARN("invalid parameter\n");
65                 return E_INVALIDARG;
66         }
67
68         return IDirectSoundBuffer_QueryInterface((LPDIRECTSOUNDBUFFER)This->dsb, riid, ppobj);
69 }
70
71 static ULONG WINAPI IDirectSoundNotifyImpl_AddRef(LPDIRECTSOUNDNOTIFY iface) {
72         ICOM_THIS(IDirectSoundNotifyImpl,iface);
73         DWORD ref;
74
75         TRACE("(%p) ref was %ld, thread is %04lx\n",This, This->ref, GetCurrentThreadId());
76
77         ref = InterlockedIncrement(&(This->ref));
78         return ref;
79 }
80
81 static ULONG WINAPI IDirectSoundNotifyImpl_Release(LPDIRECTSOUNDNOTIFY iface) {
82         ICOM_THIS(IDirectSoundNotifyImpl,iface);
83         DWORD ref;
84
85         TRACE("(%p) ref was %ld, thread is %04lx\n",This, This->ref, GetCurrentThreadId());
86
87         ref = InterlockedDecrement(&(This->ref));
88         if (ref == 0) {
89                 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
90                 This->dsb->notify = NULL;
91                 HeapFree(GetProcessHeap(),0,This);
92                 TRACE("(%p) released\n",This);
93         }
94         return ref;
95 }
96
97 static HRESULT WINAPI IDirectSoundNotifyImpl_SetNotificationPositions(
98         LPDIRECTSOUNDNOTIFY iface,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
99 ) {
100         ICOM_THIS(IDirectSoundNotifyImpl,iface);
101         TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);
102
103         if (howmuch > 0 && notify == NULL) {
104             WARN("invalid parameter: notify == NULL\n");
105             return DSERR_INVALIDPARAM;
106         }
107
108         if (TRACE_ON(dsound)) {
109             int i;
110             for (i=0;i<howmuch;i++)
111                 TRACE("notify at %ld to 0x%08lx\n",
112                     notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
113         }
114
115         if (This->dsb->hwnotify) {
116             HRESULT hres;
117             hres = IDsDriverNotify_SetNotificationPositions(This->dsb->hwnotify, howmuch, notify);
118             if (hres != DS_OK)
119                     WARN("IDsDriverNotify_SetNotificationPositions failed\n");
120             return hres;
121         } else if (howmuch > 0) {
122             /* Make an internal copy of the caller-supplied array.
123              * Replace the existing copy if one is already present. */
124             if (This->dsb->notifies)
125                     This->dsb->notifies = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
126                         This->dsb->notifies, howmuch * sizeof(DSBPOSITIONNOTIFY));
127             else
128                     This->dsb->notifies = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
129                         howmuch * sizeof(DSBPOSITIONNOTIFY));
130
131             if (This->dsb->notifies == NULL) {
132                     WARN("out of memory\n");
133                     return DSERR_OUTOFMEMORY;
134             }
135             memcpy(This->dsb->notifies, notify, howmuch * sizeof(DSBPOSITIONNOTIFY));
136             This->dsb->nrofnotifies = howmuch;
137         } else {
138            if (This->dsb->notifies) {
139                HeapFree(GetProcessHeap(), 0, This->dsb->notifies);
140                This->dsb->notifies = NULL;
141            }
142            This->dsb->nrofnotifies = 0;
143         }
144
145         return S_OK;
146 }
147
148 ICOM_VTABLE(IDirectSoundNotify) dsnvt =
149 {
150     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
151     IDirectSoundNotifyImpl_QueryInterface,
152     IDirectSoundNotifyImpl_AddRef,
153     IDirectSoundNotifyImpl_Release,
154     IDirectSoundNotifyImpl_SetNotificationPositions,
155 };
156
157 HRESULT WINAPI IDirectSoundNotifyImpl_Create(
158     IDirectSoundBufferImpl * dsb,
159     IDirectSoundNotifyImpl **pdsn)
160 {
161     IDirectSoundNotifyImpl * dsn;
162     TRACE("(%p,%p)\n",dsb,pdsn);
163
164     dsn = (IDirectSoundNotifyImpl*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(dsn));
165
166     if (dsn == NULL) {
167         WARN("out of memory\n");
168         return DSERR_OUTOFMEMORY;
169     }
170
171     dsn->ref = 0;
172     dsn->lpVtbl = &dsnvt;
173     dsn->dsb = dsb;
174     dsb->notify = dsn;
175     IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)dsb);
176
177     *pdsn = dsn;
178     return DS_OK;
179 }
180
181 HRESULT WINAPI IDirectSoundNotifyImpl_Destroy(
182     IDirectSoundNotifyImpl *pdsn)
183 {
184     TRACE("(%p)\n",pdsn);
185
186     while (IDirectSoundNotifyImpl_Release((LPDIRECTSOUNDNOTIFY)pdsn) > 0);
187
188     return DS_OK;
189 }
190
191 /*******************************************************************************
192  *              IDirectSoundBuffer
193  */
194
195 static HRESULT WINAPI IDirectSoundBufferImpl_SetFormat(
196         LPDIRECTSOUNDBUFFER8 iface,LPCWAVEFORMATEX wfex
197 ) {
198         ICOM_THIS(IDirectSoundBufferImpl,iface);
199
200         TRACE("(%p,%p)\n",This,wfex);
201         /* This method is not available on secondary buffers */
202         WARN("invalid call\n");
203         return DSERR_INVALIDCALL;
204 }
205
206 static HRESULT WINAPI IDirectSoundBufferImpl_SetVolume(
207         LPDIRECTSOUNDBUFFER8 iface,LONG vol
208 ) {
209         ICOM_THIS(IDirectSoundBufferImpl,iface);
210         LONG oldVol;
211
212         TRACE("(%p,%ld)\n",This,vol);
213
214         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
215                 WARN("control unavailable: This->dsbd.dwFlags = 0x%08lx\n", This->dsbd.dwFlags);
216                 return DSERR_CONTROLUNAVAIL;
217         }
218
219         if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) {
220                 WARN("invalid parameter: vol = %ld\n", vol);
221                 return DSERR_INVALIDPARAM;
222         }
223
224         /* **** */
225         EnterCriticalSection(&(This->lock));
226
227         if (This->dsbd.dwFlags & DSBCAPS_CTRL3D) {
228                 oldVol = This->ds3db_lVolume;
229                 This->ds3db_lVolume = vol;
230         } else {
231                 oldVol = This->volpan.lVolume;
232                 This->volpan.lVolume = vol;
233                 if (vol != oldVol)
234                         DSOUND_RecalcVolPan(&(This->volpan));
235         }
236
237         if (vol != oldVol) {
238                 if (This->hwbuf) {
239                         HRESULT hres;
240                         hres = IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
241                         if (hres != DS_OK)
242                                 WARN("IDsDriverBuffer_SetVolumePan failed\n");
243                 } else
244                         DSOUND_ForceRemix(This);
245         }
246
247         LeaveCriticalSection(&(This->lock));
248         /* **** */
249
250         return DS_OK;
251 }
252
253 static HRESULT WINAPI IDirectSoundBufferImpl_GetVolume(
254         LPDIRECTSOUNDBUFFER8 iface,LPLONG vol
255 ) {
256         ICOM_THIS(IDirectSoundBufferImpl,iface);
257         TRACE("(%p,%p)\n",This,vol);
258
259         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
260                 WARN("control unavailable\n");
261                 return DSERR_CONTROLUNAVAIL;
262         }
263
264         if (vol == NULL) {
265                 WARN("invalid parameter: vol == NULL\n");
266                 return DSERR_INVALIDPARAM;
267         }
268
269         *vol = This->volpan.lVolume;
270
271         return DS_OK;
272 }
273
274 static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency(
275         LPDIRECTSOUNDBUFFER8 iface,DWORD freq
276 ) {
277         ICOM_THIS(IDirectSoundBufferImpl,iface);
278         DWORD oldFreq;
279
280         TRACE("(%p,%ld)\n",This,freq);
281
282         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
283                 WARN("control unavailable\n");
284                 return DSERR_CONTROLUNAVAIL;
285         }
286
287         if (freq == DSBFREQUENCY_ORIGINAL)
288                 freq = This->wfx.nSamplesPerSec;
289
290         if ((freq < DSBFREQUENCY_MIN) || (freq > DSBFREQUENCY_MAX)) {
291                 WARN("invalid parameter: freq = %ld\n", freq);
292                 return DSERR_INVALIDPARAM;
293         }
294
295         /* **** */
296         EnterCriticalSection(&(This->lock));
297
298         oldFreq = This->freq;
299         This->freq = freq;
300         if (freq != oldFreq) {
301                 This->freqAdjust = (freq << DSOUND_FREQSHIFT) / This->dsound->wfx.nSamplesPerSec;
302                 This->nAvgBytesPerSec = freq * This->wfx.nBlockAlign;
303                 DSOUND_RecalcFormat(This);
304                 if (!This->hwbuf)
305                         DSOUND_ForceRemix(This);
306         }
307
308         LeaveCriticalSection(&(This->lock));
309         /* **** */
310
311         return DS_OK;
312 }
313
314 static HRESULT WINAPI IDirectSoundBufferImpl_Play(
315         LPDIRECTSOUNDBUFFER8 iface,DWORD reserved1,DWORD reserved2,DWORD flags
316 ) {
317         HRESULT hres = DS_OK;
318         ICOM_THIS(IDirectSoundBufferImpl,iface);
319         TRACE("(%p,%08lx,%08lx,%08lx)\n",This,reserved1,reserved2,flags);
320
321         /* **** */
322         EnterCriticalSection(&(This->lock));
323
324         This->playflags = flags;
325         if (This->state == STATE_STOPPED) {
326                 This->leadin = TRUE;
327                 This->startpos = This->buf_mixpos;
328                 This->state = STATE_STARTING;
329         } else if (This->state == STATE_STOPPING)
330                 This->state = STATE_PLAYING;
331         if (This->hwbuf) {
332                 hres = IDsDriverBuffer_Play(This->hwbuf, 0, 0, This->playflags);
333                 if (hres != DS_OK)
334                         WARN("IDsDriverBuffer_Play failed\n");
335                 else
336                         This->state = STATE_PLAYING;
337         }
338
339         LeaveCriticalSection(&(This->lock));
340         /* **** */
341
342         return hres;
343 }
344
345 static HRESULT WINAPI IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER8 iface)
346 {
347         HRESULT hres = DS_OK;
348         ICOM_THIS(IDirectSoundBufferImpl,iface);
349         TRACE("(%p)\n",This);
350
351         /* **** */
352         EnterCriticalSection(&(This->lock));
353
354         if (This->state == STATE_PLAYING)
355                 This->state = STATE_STOPPING;
356         else if (This->state == STATE_STARTING)
357                 This->state = STATE_STOPPED;
358         if (This->hwbuf) {
359                 hres = IDsDriverBuffer_Stop(This->hwbuf);
360                 if (hres != DS_OK)
361                         WARN("IDsDriverBuffer_Stop failed\n");
362                 else
363                         This->state = STATE_STOPPED;
364         }
365         DSOUND_CheckEvent(This, 0);
366
367         LeaveCriticalSection(&(This->lock));
368         /* **** */
369
370         return hres;
371 }
372
373 static DWORD WINAPI IDirectSoundBufferImpl_AddRef(LPDIRECTSOUNDBUFFER8 iface) {
374         ICOM_THIS(IDirectSoundBufferImpl,iface);
375         DWORD ref;
376
377         TRACE("(%p) ref was %ld, thread is %04lx\n",This, This->ref, GetCurrentThreadId());
378
379         ref = InterlockedIncrement(&(This->ref));
380         if (!ref) {
381                 FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n");
382         }
383         return ref;
384 }
385
386 static DWORD WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER8 iface) {
387         ICOM_THIS(IDirectSoundBufferImpl,iface);
388         int     i;
389         DWORD ref;
390
391         TRACE("(%p) ref was %ld, thread is %04lx\n",This, This->ref, GetCurrentThreadId());
392
393         ref = InterlockedDecrement(&(This->ref));
394         if (ref) return ref;
395
396         RtlAcquireResourceExclusive(&(This->dsound->lock), TRUE);
397         for (i=0;i<This->dsound->nrofbuffers;i++)
398                 if (This->dsound->buffers[i] == This)
399                         break;
400         if (i < This->dsound->nrofbuffers) {
401                 /* Put the last buffer of the list in the (now empty) position */
402                 This->dsound->buffers[i] = This->dsound->buffers[This->dsound->nrofbuffers - 1];
403                 This->dsound->nrofbuffers--;
404                 This->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,This->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER8)*This->dsound->nrofbuffers);
405                 TRACE("(%p) buffer count is now %d\n", This, This->dsound->nrofbuffers);
406         }
407         RtlReleaseResource(&(This->dsound->lock));
408
409         DeleteCriticalSection(&(This->lock));
410
411         if (This->hwbuf) {
412                 IDsDriverBuffer_Release(This->hwbuf);
413                 if (This->dsound->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) {
414                         This->buffer->ref--;
415                         if (This->buffer->ref==0) {
416                                 HeapFree(GetProcessHeap(),0,This->buffer->memory);
417                                 HeapFree(GetProcessHeap(),0,This->buffer);
418                         }
419                 }
420         } else {
421                 This->buffer->ref--;
422                 if (This->buffer->ref==0) {
423                         HeapFree(GetProcessHeap(),0,This->buffer->memory);
424                         HeapFree(GetProcessHeap(),0,This->buffer);
425                 }
426         }
427
428         if (This->notifies != NULL)
429                 HeapFree(GetProcessHeap(), 0, This->notifies);
430
431         HeapFree(GetProcessHeap(),0,This);
432
433         TRACE("(%p) released\n",This);
434         return 0;
435 }
436
437 DWORD DSOUND_CalcPlayPosition(IDirectSoundBufferImpl *This,
438                               DWORD state, DWORD pplay, DWORD pwrite, DWORD pmix, DWORD bmix)
439 {
440         DWORD bplay;
441
442         TRACE("primary playpos=%ld, mixpos=%ld\n", pplay, pmix);
443         TRACE("this mixpos=%ld, time=%ld\n", bmix, GetTickCount());
444
445         /* the actual primary play position (pplay) is always behind last mixed (pmix),
446          * unless the computer is too slow or something */
447         /* we need to know how far away we are from there */
448 #if 0 /* we'll never fill the primary entirely */
449         if (pmix == pplay) {
450                 if ((state == STATE_PLAYING) || (state == STATE_STOPPING)) {
451                         /* wow, the software mixer is really doing well,
452                          * seems the entire primary buffer is filled! */
453                         pmix += This->dsound->buflen;
454                 }
455                 /* else: the primary buffer is not playing, so probably empty */
456         }
457 #endif
458         if (pmix < pplay) pmix += This->dsound->buflen; /* wraparound */
459         pmix -= pplay;
460         /* detect buffer underrun */
461         if (pwrite < pplay) pwrite += This->dsound->buflen; /* wraparound */
462         pwrite -= pplay;
463         if (pmix > (ds_snd_queue_max * This->dsound->fraglen + pwrite + This->dsound->writelead)) {
464                 WARN("detected an underrun: primary queue was %ld\n",pmix);
465                 pmix = 0;
466         }
467         /* divide the offset by its sample size */
468         pmix /= This->dsound->wfx.nBlockAlign;
469         TRACE("primary back-samples=%ld\n",pmix);
470         /* adjust for our frequency */
471         pmix = (pmix * This->freqAdjust) >> DSOUND_FREQSHIFT;
472         /* multiply by our own sample size */
473         pmix *= This->wfx.nBlockAlign;
474         TRACE("this back-offset=%ld\n", pmix);
475         /* subtract from our last mixed position */
476         bplay = bmix;
477         while (bplay < pmix) bplay += This->buflen; /* wraparound */
478         bplay -= pmix;
479         if (This->leadin && ((bplay < This->startpos) || (bplay > bmix))) {
480                 /* seems we haven't started playing yet */
481                 TRACE("this still in lead-in phase\n");
482                 bplay = This->startpos;
483         }
484         /* return the result */
485         return bplay;
486 }
487
488 static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
489         LPDIRECTSOUNDBUFFER8 iface,LPDWORD playpos,LPDWORD writepos
490 ) {
491         HRESULT hres;
492         ICOM_THIS(IDirectSoundBufferImpl,iface);
493         TRACE("(%p,%p,%p)\n",This,playpos,writepos);
494         if (This->hwbuf) {
495                 hres=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);
496                 if (hres != DS_OK) {
497                     WARN("IDsDriverBuffer_GetPosition failed\n");
498                     return hres;
499                 }
500         }
501         else {
502                 if (playpos && (This->state != STATE_PLAYING)) {
503                         /* we haven't been merged into the primary buffer (yet) */
504                         *playpos = This->buf_mixpos;
505                 }
506                 else if (playpos) {
507                         DWORD pplay, pwrite, lplay, splay, pstate;
508                         /* let's get this exact; first, recursively call GetPosition on the primary */
509                         EnterCriticalSection(&(This->dsound->mixlock));
510                         if (DSOUND_PrimaryGetPosition(This->dsound, &pplay, &pwrite) != DS_OK)
511                                 WARN("DSOUND_PrimaryGetPosition failed\n");
512                         /* detect HEL mode underrun */
513                         pstate = This->dsound->state;
514                         if (!(This->dsound->hwbuf || This->dsound->pwqueue)) {
515                                 TRACE("detected an underrun\n");
516                                 /* pplay = ? */
517                                 if (pstate == STATE_PLAYING)
518                                         pstate = STATE_STARTING;
519                                 else if (pstate == STATE_STOPPING)
520                                         pstate = STATE_STOPPED;
521                         }
522                         /* get data for ourselves while we still have the lock */
523                         pstate &= This->state;
524                         lplay = This->primary_mixpos;
525                         splay = This->buf_mixpos;
526                         if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || This->dsound->hwbuf) {
527                                 /* calculate play position using this */
528                                 *playpos = DSOUND_CalcPlayPosition(This, pstate, pplay, pwrite, lplay, splay);
529                         } else {
530                                 /* (unless the app isn't using GETCURRENTPOSITION2) */
531                                 /* don't know exactly how this should be handled...
532                                  * the docs says that play cursor is reported as directly
533                                  * behind write cursor, hmm... */
534                                 /* let's just do what might work for Half-Life */
535                                 DWORD wp;
536                                 wp = (This->dsound->pwplay + ds_hel_margin) * This->dsound->fraglen;
537                                 while (wp >= This->dsound->buflen)
538                                         wp -= This->dsound->buflen;
539                                 *playpos = DSOUND_CalcPlayPosition(This, pstate, wp, pwrite, lplay, splay);
540                         }
541                         LeaveCriticalSection(&(This->dsound->mixlock));
542                 }
543                 if (writepos) *writepos = This->buf_mixpos;
544         }
545         if (writepos) {
546                 if (This->state != STATE_STOPPED)
547                         /* apply the documented 10ms lead to writepos */
548                         *writepos += This->writelead;
549                 while (*writepos >= This->buflen) *writepos -= This->buflen;
550         }
551         if (playpos) This->last_playpos = *playpos;
552         TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
553         return DS_OK;
554 }
555
556 static HRESULT WINAPI IDirectSoundBufferImpl_GetStatus(
557         LPDIRECTSOUNDBUFFER8 iface,LPDWORD status
558 ) {
559         ICOM_THIS(IDirectSoundBufferImpl,iface);
560         TRACE("(%p,%p), thread is %04lx\n",This,status,GetCurrentThreadId());
561
562         if (status == NULL) {
563                 WARN("invalid parameter: status = NULL\n");
564                 return DSERR_INVALIDPARAM;
565         }
566
567         *status = 0;
568         if ((This->state == STATE_STARTING) || (This->state == STATE_PLAYING)) {
569                 *status |= DSBSTATUS_PLAYING;
570                 if (This->playflags & DSBPLAY_LOOPING)
571                         *status |= DSBSTATUS_LOOPING;
572         }
573
574         TRACE("status=%lx\n", *status);
575         return DS_OK;
576 }
577
578
579 static HRESULT WINAPI IDirectSoundBufferImpl_GetFormat(
580         LPDIRECTSOUNDBUFFER8 iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
581 ) {
582         ICOM_THIS(IDirectSoundBufferImpl,iface);
583         TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
584
585         if (wfsize>sizeof(This->wfx))
586                 wfsize = sizeof(This->wfx);
587         if (lpwf) {     /* NULL is valid */
588                 memcpy(lpwf,&(This->wfx),wfsize);
589                 if (wfwritten)
590                         *wfwritten = wfsize;
591         } else {
592                 if (wfwritten)
593                         *wfwritten = sizeof(This->wfx);
594                 else {
595                         WARN("invalid parameter: wfwritten == NULL\n");
596                         return DSERR_INVALIDPARAM;
597                 }
598         }
599
600         return DS_OK;
601 }
602
603 static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
604         LPDIRECTSOUNDBUFFER8 iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
605 ) {
606         HRESULT hres = DS_OK;
607         ICOM_THIS(IDirectSoundBufferImpl,iface);
608
609         TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx) at %ld\n",
610                 This,
611                 writecursor,
612                 writebytes,
613                 lplpaudioptr1,
614                 audiobytes1,
615                 lplpaudioptr2,
616                 audiobytes2,
617                 flags,
618                 GetTickCount()
619         );
620
621         if (flags & DSBLOCK_FROMWRITECURSOR) {
622                 DWORD writepos;
623                 /* GetCurrentPosition does too much magic to duplicate here */
624                 hres = IDirectSoundBufferImpl_GetCurrentPosition(iface, NULL, &writepos);
625                 if (hres != DS_OK) {
626                         WARN("IDirectSoundBufferImpl_GetCurrentPosition failed\n");
627                         return hres;
628                 }
629                 writecursor += writepos;
630         }
631         while (writecursor >= This->buflen)
632                 writecursor -= This->buflen;
633         if (flags & DSBLOCK_ENTIREBUFFER)
634                 writebytes = This->buflen;
635         if (writebytes > This->buflen)
636                 writebytes = This->buflen;
637
638         assert(audiobytes1!=audiobytes2);
639         assert(lplpaudioptr1!=lplpaudioptr2);
640
641         EnterCriticalSection(&(This->lock));
642
643         if ((writebytes == This->buflen) &&
644             ((This->state == STATE_STARTING) ||
645              (This->state == STATE_PLAYING)))
646                 /* some games, like Half-Life, try to be clever (not) and
647                  * keep one secondary buffer, and mix sounds into it itself,
648                  * locking the entire buffer every time... so we can just forget
649                  * about tracking the last-written-to-position... */
650                 This->probably_valid_to = (DWORD)-1;
651         else
652                 This->probably_valid_to = writecursor;
653
654         if (!(This->dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDSECONDARYLOCK) && This->hwbuf) {
655                 hres = IDsDriverBuffer_Lock(This->hwbuf,
656                                      lplpaudioptr1, audiobytes1,
657                                      lplpaudioptr2, audiobytes2,
658                                      writecursor, writebytes,
659                                      0);
660                 if (hres != DS_OK) {
661                         WARN("IDsDriverBuffer_Lock failed\n");
662                         LeaveCriticalSection(&(This->lock));
663                         return hres;
664                 }
665         } else {
666                 BOOL remix = FALSE;
667                 if (writecursor+writebytes <= This->buflen) {
668                         *(LPBYTE*)lplpaudioptr1 = This->buffer->memory+writecursor;
669                         *audiobytes1 = writebytes;
670                         if (lplpaudioptr2)
671                                 *(LPBYTE*)lplpaudioptr2 = NULL;
672                         if (audiobytes2)
673                                 *audiobytes2 = 0;
674                         TRACE("->%ld.0\n",writebytes);
675                 } else {
676                         *(LPBYTE*)lplpaudioptr1 = This->buffer->memory+writecursor;
677                         *audiobytes1 = This->buflen-writecursor;
678                         if (lplpaudioptr2)
679                                 *(LPBYTE*)lplpaudioptr2 = This->buffer->memory;
680                         if (audiobytes2)
681                                 *audiobytes2 = writebytes-(This->buflen-writecursor);
682                         TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
683                 }
684                 if (This->state == STATE_PLAYING) {
685                         /* if the segment between playpos and buf_mixpos is touched,
686                          * we need to cancel some mixing */
687                         /* we'll assume that the app always calls GetCurrentPosition before
688                          * locking a playing buffer, so that last_playpos is up-to-date */
689                         if (This->buf_mixpos >= This->last_playpos) {
690                                 if (This->buf_mixpos > writecursor &&
691                                     This->last_playpos < writecursor+writebytes)
692                                         remix = TRUE;
693                         }
694                         else {
695                                 if (This->buf_mixpos > writecursor ||
696                                     This->last_playpos < writecursor+writebytes)
697                                         remix = TRUE;
698                         }
699                         if (remix) {
700                                 TRACE("locking prebuffered region, ouch\n");
701                                 DSOUND_MixCancelAt(This, writecursor);
702                         }
703                 }
704         }
705
706         LeaveCriticalSection(&(This->lock));
707         return DS_OK;
708 }
709
710 static HRESULT WINAPI IDirectSoundBufferImpl_SetCurrentPosition(
711         LPDIRECTSOUNDBUFFER8 iface,DWORD newpos
712 ) {
713         HRESULT hres = DS_OK;
714         ICOM_THIS(IDirectSoundBufferImpl,iface);
715         TRACE("(%p,%ld)\n",This,newpos);
716
717         /* **** */
718         EnterCriticalSection(&(This->lock));
719
720         while (newpos >= This->buflen)
721                 newpos -= This->buflen;
722         This->buf_mixpos = newpos;
723         if (This->hwbuf) {
724                 hres = IDsDriverBuffer_SetPosition(This->hwbuf, This->buf_mixpos);
725                 if (hres != DS_OK)
726                         WARN("IDsDriverBuffer_SetPosition failed\n");
727         }
728
729         LeaveCriticalSection(&(This->lock));
730         /* **** */
731
732         return hres;
733 }
734
735 static HRESULT WINAPI IDirectSoundBufferImpl_SetPan(
736         LPDIRECTSOUNDBUFFER8 iface,LONG pan
737 ) {
738         HRESULT hres = DS_OK;
739         ICOM_THIS(IDirectSoundBufferImpl,iface);
740         LONG oldPan;
741
742         TRACE("(%p,%ld)\n",This,pan);
743
744         if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) {
745                 WARN("invalid parameter: pan = %ld\n", pan);
746                 return DSERR_INVALIDPARAM;
747         }
748
749         /* You cannot use both pan and 3D controls */
750         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
751             (This->dsbd.dwFlags & DSBCAPS_CTRL3D)) {
752                 WARN("control unavailable\n");
753                 return DSERR_CONTROLUNAVAIL;
754         }
755
756         /* **** */
757         EnterCriticalSection(&(This->lock));
758
759         oldPan = This->volpan.lPan;
760         This->volpan.lPan = pan;
761
762         if (pan != oldPan) {
763                 DSOUND_RecalcVolPan(&(This->volpan));
764
765                 if (This->hwbuf) {
766                         hres = IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
767                         if (hres != DS_OK)
768                                 WARN("IDsDriverBuffer_SetVolumePan failed\n");
769                 } else
770                         DSOUND_ForceRemix(This);
771         }
772
773         LeaveCriticalSection(&(This->lock));
774         /* **** */
775
776         return hres;
777 }
778
779 static HRESULT WINAPI IDirectSoundBufferImpl_GetPan(
780         LPDIRECTSOUNDBUFFER8 iface,LPLONG pan
781 ) {
782         ICOM_THIS(IDirectSoundBufferImpl,iface);
783         TRACE("(%p,%p)\n",This,pan);
784
785         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
786                 WARN("control unavailable\n");
787                 return DSERR_CONTROLUNAVAIL;
788         }
789
790         if (pan == NULL) {
791                 WARN("invalid parameter: pan = NULL\n");
792                 return DSERR_INVALIDPARAM;
793         }
794
795         *pan = This->volpan.lPan;
796
797         return DS_OK;
798 }
799
800 static HRESULT WINAPI IDirectSoundBufferImpl_Unlock(
801         LPDIRECTSOUNDBUFFER8 iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
802 ) {
803         ICOM_THIS(IDirectSoundBufferImpl,iface);
804         DWORD probably_valid_to;
805
806         TRACE("(%p,%p,%ld,%p,%ld)\n", This,p1,x1,p2,x2);
807
808         if (!(This->dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDSECONDARYLOCK) && This->hwbuf) {
809                 HRESULT hres;
810                 hres = IDsDriverBuffer_Unlock(This->hwbuf, p1, x1, p2, x2);
811                 if (hres != DS_OK) {
812                         WARN("IDsDriverBuffer_Unlock failed\n");
813                         return hres;
814                 }
815         }
816
817         if (p2) probably_valid_to = (((LPBYTE)p2)-This->buffer->memory) + x2;
818         else probably_valid_to = (((LPBYTE)p1)-This->buffer->memory) + x1;
819         while (probably_valid_to >= This->buflen)
820                 probably_valid_to -= This->buflen;
821         if ((probably_valid_to == 0) && ((x1+x2) == This->buflen) &&
822             ((This->state == STATE_STARTING) ||
823              (This->state == STATE_PLAYING)))
824                 /* see IDirectSoundBufferImpl_Lock */
825                 probably_valid_to = (DWORD)-1;
826         This->probably_valid_to = probably_valid_to;
827
828         return DS_OK;
829 }
830
831 static HRESULT WINAPI IDirectSoundBufferImpl_Restore(
832         LPDIRECTSOUNDBUFFER8 iface
833 ) {
834         ICOM_THIS(IDirectSoundBufferImpl,iface);
835         FIXME("(%p):stub\n",This);
836         return DS_OK;
837 }
838
839 static HRESULT WINAPI IDirectSoundBufferImpl_GetFrequency(
840         LPDIRECTSOUNDBUFFER8 iface,LPDWORD freq
841 ) {
842         ICOM_THIS(IDirectSoundBufferImpl,iface);
843         TRACE("(%p,%p)\n",This,freq);
844
845         if (freq == NULL) {
846                 WARN("invalid parameter: freq = NULL\n");
847                 return DSERR_INVALIDPARAM;
848         }
849
850         *freq = This->freq;
851         TRACE("-> %ld\n", *freq);
852
853         return DS_OK;
854 }
855
856 static HRESULT WINAPI IDirectSoundBufferImpl_SetFX(
857         LPDIRECTSOUNDBUFFER8 iface,DWORD dwEffectsCount,LPDSEFFECTDESC pDSFXDesc,LPDWORD pdwResultCodes
858 ) {
859         ICOM_THIS(IDirectSoundBufferImpl,iface);
860         DWORD u;
861
862         FIXME("(%p,%lu,%p,%p): stub\n",This,dwEffectsCount,pDSFXDesc,pdwResultCodes);
863
864         if (pdwResultCodes)
865                 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
866
867         WARN("control unavailable\n");
868         return DSERR_CONTROLUNAVAIL;
869 }
870
871 static HRESULT WINAPI IDirectSoundBufferImpl_AcquireResources(
872         LPDIRECTSOUNDBUFFER8 iface,DWORD dwFlags,DWORD dwEffectsCount,LPDWORD pdwResultCodes
873 ) {
874         ICOM_THIS(IDirectSoundBufferImpl,iface);
875         DWORD u;
876
877         FIXME("(%p,%08lu,%lu,%p): stub\n",This,dwFlags,dwEffectsCount,pdwResultCodes);
878
879         if (pdwResultCodes)
880                 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
881
882         WARN("control unavailable\n");
883         return DSERR_CONTROLUNAVAIL;
884 }
885
886 static HRESULT WINAPI IDirectSoundBufferImpl_GetObjectInPath(
887         LPDIRECTSOUNDBUFFER8 iface,REFGUID rguidObject,DWORD dwIndex,REFGUID rguidInterface,LPVOID* ppObject
888 ) {
889         ICOM_THIS(IDirectSoundBufferImpl,iface);
890
891         FIXME("(%p,%s,%lu,%s,%p): stub\n",This,debugstr_guid(rguidObject),dwIndex,debugstr_guid(rguidInterface),ppObject);
892
893         WARN("control unavailable\n");
894         return DSERR_CONTROLUNAVAIL;
895 }
896
897 static HRESULT WINAPI IDirectSoundBufferImpl_Initialize(
898         LPDIRECTSOUNDBUFFER8 iface,LPDIRECTSOUND dsound,LPCDSBUFFERDESC dbsd
899 ) {
900         ICOM_THIS(IDirectSoundBufferImpl,iface);
901         FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
902         DPRINTF("Re-Init!!!\n");
903         WARN("already initialized\n");
904         return DSERR_ALREADYINITIALIZED;
905 }
906
907 static HRESULT WINAPI IDirectSoundBufferImpl_GetCaps(
908         LPDIRECTSOUNDBUFFER8 iface,LPDSBCAPS caps
909 ) {
910         ICOM_THIS(IDirectSoundBufferImpl,iface);
911         TRACE("(%p)->(%p)\n",This,caps);
912
913         if (caps == NULL) {
914                 WARN("invalid parameter: caps == NULL\n");
915                 return DSERR_INVALIDPARAM;
916         }
917
918         if (caps->dwSize < sizeof(*caps)) {
919                 WARN("invalid parameter: caps->dwSize = %ld < %d\n",caps->dwSize, sizeof(*caps));
920                 return DSERR_INVALIDPARAM;
921         }
922
923         caps->dwFlags = This->dsbd.dwFlags;
924         if (This->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
925         else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
926
927         caps->dwBufferBytes = This->buflen;
928
929         /* This value represents the speed of the "unlock" command.
930            As unlock is quite fast (it does not do anything), I put
931            4096 ko/s = 4 Mo / s */
932         /* FIXME: hwbuf speed */
933         caps->dwUnlockTransferRate = 4096;
934         caps->dwPlayCpuOverhead = 0;
935
936         return DS_OK;
937 }
938
939 static HRESULT WINAPI IDirectSoundBufferImpl_QueryInterface(
940         LPDIRECTSOUNDBUFFER8 iface,REFIID riid,LPVOID *ppobj
941 ) {
942         ICOM_THIS(IDirectSoundBufferImpl,iface);
943
944         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
945
946         if (ppobj == NULL) {
947                 WARN("invalid parameter\n");
948                 return E_INVALIDARG;
949         }
950
951         *ppobj = NULL;  /* assume failure */
952
953         if ( IsEqualGUID(riid, &IID_IUnknown) ||
954              IsEqualGUID(riid, &IID_IDirectSoundBuffer) ||
955              IsEqualGUID(riid, &IID_IDirectSoundBuffer8) ) {
956                 if (!This->dsb)
957                         SecondaryBufferImpl_Create(This, &(This->dsb));
958                 if (This->dsb) {
959                         IDirectSoundBuffer8_AddRef((LPDIRECTSOUNDBUFFER8)This->dsb);
960                         *ppobj = This->dsb;
961                         return S_OK;
962                 }
963                 WARN("IID_IDirectSoundBuffer\n");
964                 return E_NOINTERFACE;
965         }
966
967         if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ||
968              IsEqualGUID( &IID_IDirectSoundNotify8, riid ) ) {
969                 if (!This->notify)
970                         IDirectSoundNotifyImpl_Create(This, &(This->notify));
971                 if (This->notify) {
972                         IDirectSoundNotify_AddRef((LPDIRECTSOUNDNOTIFY)This->notify);
973                         *ppobj = This->notify;
974                         return S_OK;
975                 }
976                 WARN("IID_IDirectSoundNotify\n");
977                 return E_NOINTERFACE;
978         }
979
980         if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
981                 if (!This->ds3db)
982                         IDirectSound3DBufferImpl_Create(This, &(This->ds3db));
983                 if (This->ds3db) {
984                         IDirectSound3DBuffer_AddRef((LPDIRECTSOUND3DBUFFER)This->ds3db);
985                         *ppobj = This->ds3db;
986                         return S_OK;
987                 }
988                 WARN("IID_IDirectSound3DBuffer\n");
989                 return E_NOINTERFACE;
990         }
991
992         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
993                 ERR("app requested IDirectSound3DListener on secondary buffer\n");
994                 return E_NOINTERFACE;
995         }
996
997         if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
998                 /* only supported on hardware 3D secondary buffers */
999                 if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) &&
1000                      (This->dsbd.dwFlags & DSBCAPS_CTRL3D) &&
1001                      (This->hwbuf != NULL) ) {
1002                         if (!This->iks)
1003                                 IKsBufferPropertySetImpl_Create(This, &(This->iks));
1004                         if (This->iks) {
1005                                 IKsPropertySet_AddRef((LPKSPROPERTYSET)*ppobj);
1006                                 *ppobj = This->iks;
1007                                 return S_OK;
1008                         }
1009                 }
1010                 WARN("IID_IKsPropertySet\n");
1011                 return E_NOINTERFACE;
1012         }
1013
1014         FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
1015
1016         return E_NOINTERFACE;
1017 }
1018
1019 static ICOM_VTABLE(IDirectSoundBuffer8) dsbvt =
1020 {
1021         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1022         IDirectSoundBufferImpl_QueryInterface,
1023         IDirectSoundBufferImpl_AddRef,
1024         IDirectSoundBufferImpl_Release,
1025         IDirectSoundBufferImpl_GetCaps,
1026         IDirectSoundBufferImpl_GetCurrentPosition,
1027         IDirectSoundBufferImpl_GetFormat,
1028         IDirectSoundBufferImpl_GetVolume,
1029         IDirectSoundBufferImpl_GetPan,
1030         IDirectSoundBufferImpl_GetFrequency,
1031         IDirectSoundBufferImpl_GetStatus,
1032         IDirectSoundBufferImpl_Initialize,
1033         IDirectSoundBufferImpl_Lock,
1034         IDirectSoundBufferImpl_Play,
1035         IDirectSoundBufferImpl_SetCurrentPosition,
1036         IDirectSoundBufferImpl_SetFormat,
1037         IDirectSoundBufferImpl_SetVolume,
1038         IDirectSoundBufferImpl_SetPan,
1039         IDirectSoundBufferImpl_SetFrequency,
1040         IDirectSoundBufferImpl_Stop,
1041         IDirectSoundBufferImpl_Unlock,
1042         IDirectSoundBufferImpl_Restore,
1043         IDirectSoundBufferImpl_SetFX,
1044         IDirectSoundBufferImpl_AcquireResources,
1045         IDirectSoundBufferImpl_GetObjectInPath
1046 };
1047
1048 HRESULT WINAPI IDirectSoundBufferImpl_Create(
1049         IDirectSoundImpl *ds,
1050         IDirectSoundBufferImpl **pdsb,
1051         LPCDSBUFFERDESC dsbd)
1052 {
1053         IDirectSoundBufferImpl *dsb;
1054         LPWAVEFORMATEX wfex = dsbd->lpwfxFormat;
1055         HRESULT err = DS_OK;
1056         DWORD capf = 0;
1057         int use_hw;
1058         TRACE("(%p,%p,%p)\n",ds,pdsb,dsbd);
1059
1060         if (dsbd->dwBufferBytes < DSBSIZE_MIN || dsbd->dwBufferBytes > DSBSIZE_MAX) {
1061                 WARN("invalid parameter: dsbd->dwBufferBytes = %ld\n", dsbd->dwBufferBytes);
1062                 *pdsb = NULL;
1063                 return DSERR_INVALIDPARAM; /* FIXME: which error? */
1064         }
1065
1066         dsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1067
1068         if (dsb == 0) {
1069                 WARN("out of memory\n");
1070                 *pdsb = NULL;
1071                 return DSERR_OUTOFMEMORY;
1072         }
1073         dsb->ref = 0;
1074         dsb->dsb = 0;
1075         dsb->dsound = ds;
1076         dsb->lpVtbl = &dsbvt;
1077         dsb->iks = NULL;
1078
1079         memcpy(&dsb->dsbd, dsbd, sizeof(*dsbd));
1080         if (wfex)
1081                 memcpy(&dsb->wfx, wfex, sizeof(dsb->wfx));
1082
1083         TRACE("Created buffer at %p\n", dsb);
1084
1085         dsb->buflen = dsbd->dwBufferBytes;
1086         dsb->freq = dsbd->lpwfxFormat->nSamplesPerSec;
1087
1088         dsb->notify = NULL;
1089         dsb->notifies = NULL;
1090         dsb->nrofnotifies = 0;
1091         dsb->hwnotify = 0;
1092
1093         /* Check necessary hardware mixing capabilities */
1094         if (wfex->nChannels==2) capf |= DSCAPS_SECONDARYSTEREO;
1095         else capf |= DSCAPS_SECONDARYMONO;
1096         if (wfex->wBitsPerSample==16) capf |= DSCAPS_SECONDARY16BIT;
1097         else capf |= DSCAPS_SECONDARY8BIT;
1098
1099         use_hw = (ds->drvcaps.dwFlags & capf) == capf;
1100         TRACE("use_hw = 0x%08x, capf = 0x%08lx, ds->drvcaps.dwFlags = 0x%08lx\n", use_hw, capf, ds->drvcaps.dwFlags);
1101
1102         /* FIXME: check hardware sample rate mixing capabilities */
1103         /* FIXME: check app hints for software/hardware buffer (STATIC, LOCHARDWARE, etc) */
1104         /* FIXME: check whether any hardware buffers are left */
1105         /* FIXME: handle DSDHEAP_CREATEHEAP for hardware buffers */
1106
1107         /* Allocate system memory if applicable */
1108         if ((ds->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) || !use_hw) {
1109                 dsb->buffer = HeapAlloc(GetProcessHeap(),0,sizeof(*(dsb->buffer)));
1110                 if (dsb->buffer == NULL) {
1111                         WARN("out of memory\n");
1112                         HeapFree(GetProcessHeap(),0,dsb);
1113                         *pdsb = NULL;
1114                         return DSERR_OUTOFMEMORY;
1115                 }
1116
1117                 dsb->buffer->memory = (LPBYTE)HeapAlloc(GetProcessHeap(),0,dsb->buflen);
1118                 if (dsb->buffer->memory == NULL) {
1119                         WARN("out of memory\n");
1120                         HeapFree(GetProcessHeap(),0,dsb->buffer);
1121                         HeapFree(GetProcessHeap(),0,dsb);
1122                         *pdsb = NULL;
1123                         return DSERR_OUTOFMEMORY;
1124                 }
1125                 dsb->buffer->ref = 1;
1126         }
1127
1128         /* Allocate the hardware buffer */
1129         if (use_hw) {
1130                 err = IDsDriver_CreateSoundBuffer(ds->driver,wfex,dsbd->dwFlags,0,
1131                                                   &(dsb->buflen),&(dsb->buffer->memory),
1132                                                   (LPVOID*)&(dsb->hwbuf));
1133                 /* fall back to software buffer on failure */
1134                 if (err != DS_OK) {
1135                         TRACE("IDsDriver_CreateSoundBuffer failed, falling back to software buffer\n");
1136                         use_hw = 0;
1137                         if (ds->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) {
1138                                 dsb->buffer = HeapAlloc(GetProcessHeap(),0,sizeof(*(dsb->buffer)));
1139                                 if (dsb->buffer == NULL) {
1140                                         WARN("out of memory\n");
1141                                         HeapFree(GetProcessHeap(),0,dsb);
1142                                         *pdsb = NULL;
1143                                         return DSERR_OUTOFMEMORY;
1144                                 }
1145
1146                                 dsb->buffer->memory = (LPBYTE)HeapAlloc(GetProcessHeap(),0,dsb->buflen);
1147                                 if (dsb->buffer->memory == NULL) {
1148                                         WARN("out of memory\n");
1149                                         HeapFree(GetProcessHeap(),0,dsb->buffer);
1150                                         HeapFree(GetProcessHeap(),0,dsb);
1151                                         *pdsb = NULL;
1152                                         return DSERR_OUTOFMEMORY;
1153                                 }
1154                                 dsb->buffer->ref = 1;
1155                         }
1156                 }
1157         }
1158
1159         /* calculate fragment size and write lead */
1160         DSOUND_RecalcFormat(dsb);
1161
1162         /* It's not necessary to initialize values to zero since */
1163         /* we allocated this structure with HEAP_ZERO_MEMORY... */
1164         dsb->playpos = 0;
1165         dsb->buf_mixpos = 0;
1166         dsb->state = STATE_STOPPED;
1167
1168         dsb->freqAdjust = (dsb->freq << DSOUND_FREQSHIFT) /
1169                 ds->wfx.nSamplesPerSec;
1170         dsb->nAvgBytesPerSec = dsb->freq *
1171                 dsbd->lpwfxFormat->nBlockAlign;
1172
1173         if (dsb->dsbd.dwFlags & DSBCAPS_CTRL3D) {
1174                 dsb->ds3db_ds3db.dwSize = sizeof(DS3DBUFFER);
1175                 dsb->ds3db_ds3db.vPosition.x = 0.0;
1176                 dsb->ds3db_ds3db.vPosition.y = 0.0;
1177                 dsb->ds3db_ds3db.vPosition.z = 0.0;
1178                 dsb->ds3db_ds3db.vVelocity.x = 0.0;
1179                 dsb->ds3db_ds3db.vVelocity.y = 0.0;
1180                 dsb->ds3db_ds3db.vVelocity.z = 0.0;
1181                 dsb->ds3db_ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
1182                 dsb->ds3db_ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
1183                 dsb->ds3db_ds3db.vConeOrientation.x = 0.0;
1184                 dsb->ds3db_ds3db.vConeOrientation.y = 0.0;
1185                 dsb->ds3db_ds3db.vConeOrientation.z = 0.0;
1186                 dsb->ds3db_ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME;
1187                 dsb->ds3db_ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
1188                 dsb->ds3db_ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
1189                 dsb->ds3db_ds3db.dwMode = DS3DMODE_NORMAL;
1190
1191                 dsb->ds3db_need_recalc = FALSE;
1192                 DSOUND_Calc3DBuffer(dsb);
1193         } else
1194                 DSOUND_RecalcVolPan(&(dsb->volpan));
1195
1196         InitializeCriticalSection(&(dsb->lock));
1197
1198         /* register buffer */
1199         RtlAcquireResourceExclusive(&(ds->lock), TRUE);
1200         if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
1201                 IDirectSoundBufferImpl **newbuffers;
1202                 if (ds->buffers)
1203                         newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,ds->buffers,sizeof(IDirectSoundBufferImpl*)*(ds->nrofbuffers+1));
1204                 else
1205                         newbuffers = (IDirectSoundBufferImpl**)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundBufferImpl*)*(ds->nrofbuffers+1));
1206
1207                 if (newbuffers) {
1208                         ds->buffers = newbuffers;
1209                         ds->buffers[ds->nrofbuffers] = dsb;
1210                         ds->nrofbuffers++;
1211                         TRACE("buffer count is now %d\n", ds->nrofbuffers);
1212                 } else {
1213                         ERR("out of memory for buffer list! Current buffer count is %d\n", ds->nrofbuffers);
1214                         if (dsb->buffer->memory)
1215                                 HeapFree(GetProcessHeap(),0,dsb->buffer->memory);
1216                         if (dsb->buffer)
1217                                 HeapFree(GetProcessHeap(),0,dsb->buffer);
1218                         DeleteCriticalSection(&(dsb->lock));
1219                         RtlReleaseResource(&(ds->lock));
1220                         HeapFree(GetProcessHeap(),0,dsb);
1221                         *pdsb = NULL;
1222                         return DSERR_OUTOFMEMORY;
1223                 }
1224         }
1225         RtlReleaseResource(&(ds->lock));
1226         *pdsb = dsb;
1227         return S_OK;
1228 }
1229
1230 HRESULT WINAPI IDirectSoundBufferImpl_Destroy(
1231     IDirectSoundBufferImpl *pdsb)
1232 {
1233     TRACE("(%p)\n",pdsb);
1234
1235     /* This keeps the *_Destroy functions from possibly deleting
1236      * this object until it is ready to be deleted */
1237     IDirectSoundBufferImpl_AddRef((LPDIRECTSOUNDBUFFER8)pdsb);
1238
1239     if (pdsb->iks) {
1240         WARN("iks not NULL\n");
1241         IKsBufferPropertySetImpl_Destroy(pdsb->iks);
1242         pdsb->iks = NULL;
1243     }
1244
1245     if (pdsb->ds3db) {
1246         WARN("ds3db not NULL\n");
1247         IDirectSound3DBufferImpl_Destroy(pdsb->ds3db);
1248         pdsb->ds3db = NULL;
1249     }
1250
1251     if (pdsb->notify) {
1252         WARN("notify not NULL\n");
1253         IDirectSoundNotifyImpl_Destroy(pdsb->notify);
1254         pdsb->notify = NULL;
1255     }
1256
1257     if (pdsb->dsb) {
1258         WARN("dsb not NULL\n");
1259         SecondaryBufferImpl_Destroy(pdsb->dsb);
1260         pdsb->dsb = NULL;
1261     }
1262
1263     while (IDirectSoundBuffer8_Release((LPDIRECTSOUNDBUFFER8)pdsb) > 0);
1264
1265     return S_OK;
1266 }
1267
1268 /*******************************************************************************
1269  *              SecondaryBuffer
1270  */
1271
1272 static HRESULT WINAPI SecondaryBufferImpl_QueryInterface(
1273         LPDIRECTSOUNDBUFFER8 iface,REFIID riid,LPVOID *ppobj)
1274 {
1275         ICOM_THIS(SecondaryBufferImpl,iface);
1276         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
1277
1278         return IDirectSoundBufferImpl_QueryInterface((LPDIRECTSOUNDBUFFER8)This->dsb,riid,ppobj);
1279 }
1280
1281 static DWORD WINAPI SecondaryBufferImpl_AddRef(LPDIRECTSOUNDBUFFER8 iface)
1282 {
1283         ICOM_THIS(IDirectSoundBufferImpl,iface);
1284         DWORD ref;
1285         TRACE("(%p) ref was %ld, thread is %04lx\n",This, This->ref, GetCurrentThreadId());
1286
1287         ref = InterlockedIncrement(&(This->ref));
1288         if (!ref) {
1289                 FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n");
1290         }
1291         return ref;
1292 }
1293
1294 static DWORD WINAPI SecondaryBufferImpl_Release(LPDIRECTSOUNDBUFFER8 iface)
1295 {
1296         ICOM_THIS(IDirectSoundBufferImpl,iface);
1297         DWORD ref;
1298         TRACE("(%p) ref was %ld, thread is %04lx\n",This, This->ref, GetCurrentThreadId());
1299
1300         ref = InterlockedDecrement(&(This->ref));
1301         if (!ref) {
1302                 This->dsb->dsb = NULL;
1303                 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER8)This->dsb);
1304                 HeapFree(GetProcessHeap(),0,This);
1305                 TRACE("(%p) released\n",This);
1306         }
1307         return ref;
1308 }
1309
1310 static HRESULT WINAPI SecondaryBufferImpl_GetCaps(
1311         LPDIRECTSOUNDBUFFER8 iface,LPDSBCAPS caps)
1312 {
1313         ICOM_THIS(SecondaryBufferImpl,iface);
1314         TRACE("(%p)->(%p)\n",This,caps);
1315
1316         return IDirectSoundBufferImpl_GetCaps((LPDIRECTSOUNDBUFFER8)This->dsb,caps);
1317 }
1318
1319 static HRESULT WINAPI SecondaryBufferImpl_GetCurrentPosition(
1320         LPDIRECTSOUNDBUFFER8 iface,LPDWORD playpos,LPDWORD writepos)
1321 {
1322         ICOM_THIS(SecondaryBufferImpl,iface);
1323         TRACE("(%p,%p,%p)\n",This,playpos,writepos);
1324
1325         return IDirectSoundBufferImpl_GetCurrentPosition((LPDIRECTSOUNDBUFFER8)This->dsb,playpos,writepos);
1326 }
1327
1328 static HRESULT WINAPI SecondaryBufferImpl_GetFormat(
1329         LPDIRECTSOUNDBUFFER8 iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten)
1330 {
1331         ICOM_THIS(SecondaryBufferImpl,iface);
1332         TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
1333
1334         return IDirectSoundBufferImpl_GetFormat((LPDIRECTSOUNDBUFFER8)This->dsb,lpwf,wfsize,wfwritten);
1335 }
1336
1337 static HRESULT WINAPI SecondaryBufferImpl_GetVolume(
1338         LPDIRECTSOUNDBUFFER8 iface,LPLONG vol)
1339 {
1340         ICOM_THIS(SecondaryBufferImpl,iface);
1341         TRACE("(%p,%p)\n",This,vol);
1342
1343         return IDirectSoundBufferImpl_GetVolume((LPDIRECTSOUNDBUFFER8)This->dsb,vol);
1344 }
1345
1346 static HRESULT WINAPI SecondaryBufferImpl_GetPan(
1347         LPDIRECTSOUNDBUFFER8 iface,LPLONG pan)
1348 {
1349         ICOM_THIS(SecondaryBufferImpl,iface);
1350         TRACE("(%p,%p)\n",This,pan);
1351
1352         return IDirectSoundBufferImpl_GetPan((LPDIRECTSOUNDBUFFER8)This->dsb,pan);
1353 }
1354
1355 static HRESULT WINAPI SecondaryBufferImpl_GetFrequency(
1356         LPDIRECTSOUNDBUFFER8 iface,LPDWORD freq)
1357 {
1358         ICOM_THIS(SecondaryBufferImpl,iface);
1359         TRACE("(%p,%p)\n",This,freq);
1360
1361         return IDirectSoundBufferImpl_GetFrequency((LPDIRECTSOUNDBUFFER8)This->dsb,freq);
1362 }
1363
1364 static HRESULT WINAPI SecondaryBufferImpl_GetStatus(
1365         LPDIRECTSOUNDBUFFER8 iface,LPDWORD status)
1366 {
1367         ICOM_THIS(SecondaryBufferImpl,iface);
1368         TRACE("(%p,%p)\n",This,status);
1369
1370         return IDirectSoundBufferImpl_GetStatus((LPDIRECTSOUNDBUFFER8)This->dsb,status);
1371 }
1372
1373 static HRESULT WINAPI SecondaryBufferImpl_Initialize(
1374         LPDIRECTSOUNDBUFFER8 iface,LPDIRECTSOUND dsound,LPCDSBUFFERDESC dbsd)
1375 {
1376         ICOM_THIS(SecondaryBufferImpl,iface);
1377         TRACE("(%p,%p,%p)\n",This,dsound,dbsd);
1378
1379         return IDirectSoundBufferImpl_Initialize((LPDIRECTSOUNDBUFFER8)This->dsb,dsound,dbsd);
1380 }
1381
1382 static HRESULT WINAPI SecondaryBufferImpl_Lock(
1383     LPDIRECTSOUNDBUFFER8 iface,
1384     DWORD writecursor,
1385     DWORD writebytes,
1386     LPVOID lplpaudioptr1,
1387     LPDWORD audiobytes1,
1388     LPVOID lplpaudioptr2,
1389     LPDWORD audiobytes2,
1390     DWORD dwFlags)
1391 {
1392     ICOM_THIS(SecondaryBufferImpl,iface);
1393     TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx)\n",
1394         This,writecursor,writebytes,lplpaudioptr1,audiobytes1,lplpaudioptr2,audiobytes2,dwFlags);
1395
1396     return IDirectSoundBufferImpl_Lock((LPDIRECTSOUNDBUFFER8)This->dsb,
1397         writecursor,writebytes,lplpaudioptr1,audiobytes1,lplpaudioptr2,audiobytes2,dwFlags);
1398 }
1399
1400 static HRESULT WINAPI SecondaryBufferImpl_Play(
1401         LPDIRECTSOUNDBUFFER8 iface,DWORD reserved1,DWORD reserved2,DWORD flags)
1402 {
1403         ICOM_THIS(SecondaryBufferImpl,iface);
1404         TRACE("(%p,%08lx,%08lx,%08lx)\n",This,reserved1,reserved2,flags);
1405
1406         return IDirectSoundBufferImpl_Play((LPDIRECTSOUNDBUFFER8)This->dsb,reserved1,reserved2,flags);
1407 }
1408
1409 static HRESULT WINAPI SecondaryBufferImpl_SetCurrentPosition(
1410         LPDIRECTSOUNDBUFFER8 iface,DWORD newpos)
1411 {
1412         ICOM_THIS(SecondaryBufferImpl,iface);
1413         TRACE("(%p,%ld)\n",This,newpos);
1414
1415         return IDirectSoundBufferImpl_SetCurrentPosition((LPDIRECTSOUNDBUFFER8)This->dsb,newpos);
1416 }
1417
1418 static HRESULT WINAPI SecondaryBufferImpl_SetFormat(
1419         LPDIRECTSOUNDBUFFER8 iface,LPCWAVEFORMATEX wfex)
1420 {
1421         ICOM_THIS(SecondaryBufferImpl,iface);
1422         TRACE("(%p,%p)\n",This,wfex);
1423
1424         return IDirectSoundBufferImpl_SetFormat((LPDIRECTSOUNDBUFFER8)This->dsb,wfex);
1425 }
1426
1427 static HRESULT WINAPI SecondaryBufferImpl_SetVolume(
1428         LPDIRECTSOUNDBUFFER8 iface,LONG vol)
1429 {
1430         ICOM_THIS(SecondaryBufferImpl,iface);
1431         TRACE("(%p,%ld)\n",This,vol);
1432
1433         return IDirectSoundBufferImpl_SetVolume((LPDIRECTSOUNDBUFFER8)This->dsb,vol);
1434 }
1435
1436 static HRESULT WINAPI SecondaryBufferImpl_SetPan(
1437         LPDIRECTSOUNDBUFFER8 iface,LONG pan)
1438 {
1439         ICOM_THIS(SecondaryBufferImpl,iface);
1440         TRACE("(%p,%ld)\n",This,pan);
1441
1442         return IDirectSoundBufferImpl_SetPan((LPDIRECTSOUNDBUFFER8)This->dsb,pan);
1443 }
1444
1445 static HRESULT WINAPI SecondaryBufferImpl_SetFrequency(
1446         LPDIRECTSOUNDBUFFER8 iface,DWORD freq)
1447 {
1448         ICOM_THIS(SecondaryBufferImpl,iface);
1449         TRACE("(%p,%ld)\n",This,freq);
1450
1451         return IDirectSoundBufferImpl_SetFrequency((LPDIRECTSOUNDBUFFER8)This->dsb,freq);
1452 }
1453
1454 static HRESULT WINAPI SecondaryBufferImpl_Stop(LPDIRECTSOUNDBUFFER8 iface)
1455 {
1456         ICOM_THIS(SecondaryBufferImpl,iface);
1457         TRACE("(%p)\n",This);
1458
1459         return IDirectSoundBufferImpl_Stop((LPDIRECTSOUNDBUFFER8)This->dsb);
1460 }
1461
1462 static HRESULT WINAPI SecondaryBufferImpl_Unlock(
1463     LPDIRECTSOUNDBUFFER8 iface,
1464     LPVOID lpvAudioPtr1,
1465     DWORD dwAudioBytes1,
1466     LPVOID lpvAudioPtr2,
1467     DWORD dwAudioBytes2)
1468 {
1469     ICOM_THIS(SecondaryBufferImpl,iface);
1470     TRACE("(%p,%p,%ld,%p,%ld)\n",
1471         This, lpvAudioPtr1, dwAudioBytes1, lpvAudioPtr2, dwAudioBytes2);
1472
1473     return IDirectSoundBufferImpl_Unlock((LPDIRECTSOUNDBUFFER8)This->dsb,
1474         lpvAudioPtr1,dwAudioBytes1,lpvAudioPtr2,dwAudioBytes2);
1475 }
1476
1477 static HRESULT WINAPI SecondaryBufferImpl_Restore(
1478         LPDIRECTSOUNDBUFFER8 iface)
1479 {
1480         ICOM_THIS(SecondaryBufferImpl,iface);
1481         TRACE("(%p)\n",This);
1482
1483         return IDirectSoundBufferImpl_Restore((LPDIRECTSOUNDBUFFER8)This->dsb);
1484 }
1485
1486 static HRESULT WINAPI SecondaryBufferImpl_SetFX(
1487         LPDIRECTSOUNDBUFFER8 iface,DWORD dwEffectsCount,LPDSEFFECTDESC pDSFXDesc,LPDWORD pdwResultCodes)
1488 {
1489         ICOM_THIS(SecondaryBufferImpl,iface);
1490         TRACE("(%p,%lu,%p,%p)\n",This,dwEffectsCount,pDSFXDesc,pdwResultCodes);
1491
1492         return IDirectSoundBufferImpl_SetFX((LPDIRECTSOUNDBUFFER8)This->dsb,dwEffectsCount,pDSFXDesc,pdwResultCodes);
1493 }
1494
1495 static HRESULT WINAPI SecondaryBufferImpl_AcquireResources(
1496         LPDIRECTSOUNDBUFFER8 iface,DWORD dwFlags,DWORD dwEffectsCount,LPDWORD pdwResultCodes)
1497 {
1498         ICOM_THIS(SecondaryBufferImpl,iface);
1499         TRACE("(%p,%08lu,%lu,%p)\n",This,dwFlags,dwEffectsCount,pdwResultCodes);
1500
1501         return IDirectSoundBufferImpl_AcquireResources((LPDIRECTSOUNDBUFFER8)This->dsb,dwFlags,dwEffectsCount,pdwResultCodes);
1502 }
1503
1504 static HRESULT WINAPI SecondaryBufferImpl_GetObjectInPath(
1505         LPDIRECTSOUNDBUFFER8 iface,REFGUID rguidObject,DWORD dwIndex,REFGUID rguidInterface,LPVOID* ppObject)
1506 {
1507         ICOM_THIS(SecondaryBufferImpl,iface);
1508         TRACE("(%p,%s,%lu,%s,%p)\n",This,debugstr_guid(rguidObject),dwIndex,debugstr_guid(rguidInterface),ppObject);
1509
1510         return IDirectSoundBufferImpl_GetObjectInPath((LPDIRECTSOUNDBUFFER8)This->dsb,rguidObject,dwIndex,rguidInterface,ppObject);
1511 }
1512
1513 static ICOM_VTABLE(IDirectSoundBuffer8) sbvt =
1514 {
1515         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1516         SecondaryBufferImpl_QueryInterface,
1517         SecondaryBufferImpl_AddRef,
1518         SecondaryBufferImpl_Release,
1519         SecondaryBufferImpl_GetCaps,
1520         SecondaryBufferImpl_GetCurrentPosition,
1521         SecondaryBufferImpl_GetFormat,
1522         SecondaryBufferImpl_GetVolume,
1523         SecondaryBufferImpl_GetPan,
1524         SecondaryBufferImpl_GetFrequency,
1525         SecondaryBufferImpl_GetStatus,
1526         SecondaryBufferImpl_Initialize,
1527         SecondaryBufferImpl_Lock,
1528         SecondaryBufferImpl_Play,
1529         SecondaryBufferImpl_SetCurrentPosition,
1530         SecondaryBufferImpl_SetFormat,
1531         SecondaryBufferImpl_SetVolume,
1532         SecondaryBufferImpl_SetPan,
1533         SecondaryBufferImpl_SetFrequency,
1534         SecondaryBufferImpl_Stop,
1535         SecondaryBufferImpl_Unlock,
1536         SecondaryBufferImpl_Restore,
1537         SecondaryBufferImpl_SetFX,
1538         SecondaryBufferImpl_AcquireResources,
1539         SecondaryBufferImpl_GetObjectInPath
1540 };
1541
1542 HRESULT WINAPI SecondaryBufferImpl_Create(
1543         IDirectSoundBufferImpl *dsb,
1544         SecondaryBufferImpl **psb)
1545 {
1546         SecondaryBufferImpl *sb;
1547         TRACE("(%p,%p)\n",dsb,psb);
1548
1549         sb = (SecondaryBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*sb));
1550
1551         if (sb == 0) {
1552                 WARN("out of memory\n");
1553                 *psb = NULL;
1554                 return DSERR_OUTOFMEMORY;
1555         }
1556         sb->ref = 0;
1557         sb->dsb = dsb;
1558         sb->lpVtbl = &sbvt;
1559
1560         IDirectSoundBuffer8_AddRef((LPDIRECTSOUNDBUFFER8)dsb);
1561         *psb = sb;
1562         return S_OK;
1563 }
1564
1565 HRESULT WINAPI SecondaryBufferImpl_Destroy(
1566     SecondaryBufferImpl *pdsb)
1567 {
1568     TRACE("(%p)\n",pdsb);
1569
1570     while (SecondaryBufferImpl_Release((LPDIRECTSOUNDBUFFER8)pdsb) > 0);
1571
1572     return S_OK;
1573 }