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