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