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