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