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