Fixed a race condition on RPC worker thread creation, and a typo.
[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 <stdio.h>
25 #include <sys/types.h>
26 #include <sys/fcntl.h>
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
30 #include <stdlib.h>
31 #include <string.h>
32 #include <math.h>       /* Insomnia - pow() function */
33
34 #include "windef.h"
35 #include "winbase.h"
36 #include "wingdi.h"
37 #include "winuser.h"
38 #include "winerror.h"
39 #include "mmsystem.h"
40 #include "winternl.h"
41 #include "mmddk.h"
42 #include "wine/windef16.h"
43 #include "wine/debug.h"
44 #include "dsound.h"
45 #include "dsdriver.h"
46 #include "dsound_private.h"
47
48 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
49
50 /*******************************************************************************
51  *              IDirectSoundNotify
52  */
53 static HRESULT WINAPI IDirectSoundNotifyImpl_QueryInterface(
54         LPDIRECTSOUNDNOTIFY iface,REFIID riid,LPVOID *ppobj
55 ) {
56         ICOM_THIS(IDirectSoundNotifyImpl,iface);
57
58         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
59         if (This->dsb)
60                 return IDirectSoundBuffer8_QueryInterface((LPDIRECTSOUNDBUFFER8)This->dsb, riid, ppobj);
61         else if (This->dscb)
62                 return IDirectSoundCaptureBuffer8_QueryInterface((LPDIRECTSOUNDCAPTUREBUFFER8)This->dscb, riid, ppobj);
63         return DSERR_GENERIC;
64 }
65
66 static ULONG WINAPI IDirectSoundNotifyImpl_AddRef(LPDIRECTSOUNDNOTIFY iface) {
67         ICOM_THIS(IDirectSoundNotifyImpl,iface);
68         DWORD ref;
69
70         TRACE("(%p) ref was %ld\n", This, This->ref);
71
72         ref = InterlockedIncrement(&(This->ref));
73         return ref;
74 }
75
76 static ULONG WINAPI IDirectSoundNotifyImpl_Release(LPDIRECTSOUNDNOTIFY iface) {
77         ICOM_THIS(IDirectSoundNotifyImpl,iface);
78         DWORD ref;
79
80         TRACE("(%p) ref was %ld\n", This, This->ref);
81
82         ref = InterlockedDecrement(&(This->ref));
83         if (!ref) {
84                 if (This->dsb)
85                         IDirectSoundBuffer8_Release((LPDIRECTSOUNDBUFFER8)This->dsb);
86                 else if (This->dscb)
87                         IDirectSoundCaptureBuffer8_Release((LPDIRECTSOUNDCAPTUREBUFFER8)This->dscb);
88                 HeapFree(GetProcessHeap(),0,This);
89                 return 0;
90         }
91         return ref;
92 }
93
94 static HRESULT WINAPI IDirectSoundNotifyImpl_SetNotificationPositions(
95         LPDIRECTSOUNDNOTIFY iface,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
96 ) {
97         ICOM_THIS(IDirectSoundNotifyImpl,iface);
98         int     i;
99
100         if (TRACE_ON(dsound)) {
101             TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);
102             for (i=0;i<howmuch;i++)
103                     TRACE("notify at %ld to 0x%08lx\n",
104                             notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
105         }
106         if (This->dsb) {
107                 This->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,This->dsb->notifies,(This->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
108                 memcpy( This->dsb->notifies+This->dsb->nrofnotifies,
109                         notify,
110                         howmuch*sizeof(DSBPOSITIONNOTIFY)
111                 );
112                 This->dsb->nrofnotifies+=howmuch;
113         } else if (This->dscb) {
114                 TRACE("notifies = %p, nrofnotifies = %d\n", This->dscb->notifies, This->dscb->nrofnotifies);
115                 This->dscb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,This->dscb->notifies,(This->dscb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
116                 memcpy( This->dscb->notifies+This->dscb->nrofnotifies,
117                         notify,
118                         howmuch*sizeof(DSBPOSITIONNOTIFY)
119                 );
120                 This->dscb->nrofnotifies+=howmuch;
121                 TRACE("notifies = %p, nrofnotifies = %d\n", This->dscb->notifies, This->dscb->nrofnotifies);
122         }
123         else
124                 return DSERR_INVALIDPARAM;
125
126         return S_OK;
127 }
128
129 ICOM_VTABLE(IDirectSoundNotify) dsnvt =
130 {
131         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
132         IDirectSoundNotifyImpl_QueryInterface,
133         IDirectSoundNotifyImpl_AddRef,
134         IDirectSoundNotifyImpl_Release,
135         IDirectSoundNotifyImpl_SetNotificationPositions,
136 };
137
138 /*******************************************************************************
139  *              IDirectSoundBuffer
140  */
141
142 static HRESULT WINAPI IDirectSoundBufferImpl_SetFormat(
143         LPDIRECTSOUNDBUFFER8 iface,LPWAVEFORMATEX wfex
144 ) {
145         ICOM_THIS(IDirectSoundBufferImpl,iface);
146
147         TRACE("(%p,%p)\n",This,wfex);
148         /* This method is not available on secondary buffers */
149         return DSERR_INVALIDCALL;
150 }
151
152 static HRESULT WINAPI IDirectSoundBufferImpl_SetVolume(
153         LPDIRECTSOUNDBUFFER8 iface,LONG vol
154 ) {
155         ICOM_THIS(IDirectSoundBufferImpl,iface);
156         LONG oldVol;
157
158         TRACE("(%p,%ld)\n",This,vol);
159
160         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
161                 return DSERR_CONTROLUNAVAIL;
162
163         if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN))
164                 return DSERR_INVALIDPARAM;
165
166         /* **** */
167         EnterCriticalSection(&(This->lock));
168
169         if (This->dsbd.dwFlags & DSBCAPS_CTRL3D) {
170                 oldVol = This->ds3db->lVolume;
171                 This->ds3db->lVolume = vol;
172         } else {
173                 oldVol = This->volpan.lVolume;
174                 This->volpan.lVolume = vol;
175                 if (vol != oldVol) DSOUND_RecalcVolPan(&(This->volpan));
176         }
177
178         if (vol != oldVol) {
179                 if (This->hwbuf) {
180                         IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
181                 }
182                 else DSOUND_ForceRemix(This);
183         }
184
185         LeaveCriticalSection(&(This->lock));
186         /* **** */
187
188         return DS_OK;
189 }
190
191 static HRESULT WINAPI IDirectSoundBufferImpl_GetVolume(
192         LPDIRECTSOUNDBUFFER8 iface,LPLONG vol
193 ) {
194         ICOM_THIS(IDirectSoundBufferImpl,iface);
195         TRACE("(%p,%p)\n",This,vol);
196
197         if (vol == NULL)
198                 return DSERR_INVALIDPARAM;
199
200         if (This->dsbd.dwFlags & DSBCAPS_CTRL3D)
201                 *vol = This->ds3db->lVolume;
202         else
203                 *vol = This->volpan.lVolume;
204         return DS_OK;
205 }
206
207 static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency(
208         LPDIRECTSOUNDBUFFER8 iface,DWORD freq
209 ) {
210         ICOM_THIS(IDirectSoundBufferImpl,iface);
211         DWORD oldFreq;
212
213         TRACE("(%p,%ld)\n",This,freq);
214
215         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY))
216                 return DSERR_CONTROLUNAVAIL;
217
218         if (freq == DSBFREQUENCY_ORIGINAL)
219                 freq = This->wfx.nSamplesPerSec;
220
221         if ((freq < DSBFREQUENCY_MIN) || (freq > DSBFREQUENCY_MAX))
222                 return DSERR_INVALIDPARAM;
223
224         /* **** */
225         EnterCriticalSection(&(This->lock));
226
227         oldFreq = This->freq;
228         This->freq = freq;
229         if (freq != oldFreq) {
230                 This->freqAdjust = (freq << DSOUND_FREQSHIFT) / This->dsound->wfx.nSamplesPerSec;
231                 This->nAvgBytesPerSec = freq * This->wfx.nBlockAlign;
232                 DSOUND_RecalcFormat(This);
233                 if (!This->hwbuf) DSOUND_ForceRemix(This);
234         }
235
236         LeaveCriticalSection(&(This->lock));
237         /* **** */
238
239         return DS_OK;
240 }
241
242 static HRESULT WINAPI IDirectSoundBufferImpl_Play(
243         LPDIRECTSOUNDBUFFER8 iface,DWORD reserved1,DWORD reserved2,DWORD flags
244 ) {
245         ICOM_THIS(IDirectSoundBufferImpl,iface);
246         TRACE("(%p,%08lx,%08lx,%08lx)\n",
247                 This,reserved1,reserved2,flags
248         );
249
250         /* **** */
251         EnterCriticalSection(&(This->lock));
252
253         This->playflags = flags;
254         if (This->state == STATE_STOPPED) {
255                 This->leadin = TRUE;
256                 This->startpos = This->buf_mixpos;
257                 This->state = STATE_STARTING;
258         } else if (This->state == STATE_STOPPING)
259                 This->state = STATE_PLAYING;
260         if (This->hwbuf) {
261                 IDsDriverBuffer_Play(This->hwbuf, 0, 0, This->playflags);
262                 This->state = STATE_PLAYING;
263         }
264
265         LeaveCriticalSection(&(This->lock));
266         /* **** */
267
268         return DS_OK;
269 }
270
271 static HRESULT WINAPI IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER8 iface)
272 {
273         ICOM_THIS(IDirectSoundBufferImpl,iface);
274         TRACE("(%p)\n",This);
275
276         /* **** */
277         EnterCriticalSection(&(This->lock));
278
279         if (This->state == STATE_PLAYING)
280                 This->state = STATE_STOPPING;
281         else if (This->state == STATE_STARTING)
282                 This->state = STATE_STOPPED;
283         if (This->hwbuf) {
284                 IDsDriverBuffer_Stop(This->hwbuf);
285                 This->state = STATE_STOPPED;
286         }
287         DSOUND_CheckEvent(This, 0);
288
289         LeaveCriticalSection(&(This->lock));
290         /* **** */
291
292         return DS_OK;
293 }
294
295 static DWORD WINAPI IDirectSoundBufferImpl_AddRef(LPDIRECTSOUNDBUFFER8 iface) {
296         ICOM_THIS(IDirectSoundBufferImpl,iface);
297         DWORD ref;
298
299         TRACE("(%p) ref was %ld, thread is %04lx\n",This, This->ref, GetCurrentThreadId());
300
301         ref = InterlockedIncrement(&(This->ref));
302         if (!ref) {
303                 FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n");
304         }
305         return ref;
306 }
307 static DWORD WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER8 iface) {
308         ICOM_THIS(IDirectSoundBufferImpl,iface);
309         int     i;
310         DWORD ref;
311
312         TRACE("(%p) ref was %ld, thread is %04lx\n",This, This->ref, GetCurrentThreadId());
313
314         ref = InterlockedDecrement(&(This->ref));
315         if (ref) return ref;
316
317         RtlAcquireResourceExclusive(&(This->dsound->lock), TRUE);
318         for (i=0;i<This->dsound->nrofbuffers;i++)
319                 if (This->dsound->buffers[i] == This)
320                         break;
321
322         if (i < This->dsound->nrofbuffers) {
323                 /* Put the last buffer of the list in the (now empty) position */
324                 This->dsound->buffers[i] = This->dsound->buffers[This->dsound->nrofbuffers - 1];
325                 This->dsound->nrofbuffers--;
326                 This->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,This->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER8)*This->dsound->nrofbuffers);
327                 TRACE("buffer count is now %d\n", This->dsound->nrofbuffers);
328                 IDirectSound_Release((LPDIRECTSOUND)This->dsound);
329         }
330         RtlReleaseResource(&(This->dsound->lock));
331
332         DeleteCriticalSection(&(This->lock));
333         if (This->hwbuf) {
334                 IDsDriverBuffer_Release(This->hwbuf);
335                 if (This->dsound->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY)
336                         HeapFree(GetProcessHeap(),0,This->buffer);
337         }
338         else if (!This->parent)
339                 HeapFree(GetProcessHeap(),0,This->buffer);
340         if (This->ds3db) {
341                 DeleteCriticalSection(&This->ds3db->lock);
342                 HeapFree(GetProcessHeap(), 0, This->ds3db);
343         }
344         if (This->iks) {
345                 HeapFree(GetProcessHeap(), 0, This->iks);
346         }
347         if (This->parent)
348                 IDirectSoundBuffer8_Release((LPDIRECTSOUNDBUFFER8)This->parent);
349
350         HeapFree(GetProcessHeap(),0,This);
351
352         return 0;
353 }
354
355 DWORD DSOUND_CalcPlayPosition(IDirectSoundBufferImpl *This,
356                               DWORD state, DWORD pplay, DWORD pwrite, DWORD pmix, DWORD bmix)
357 {
358         DWORD bplay;
359
360         TRACE("primary playpos=%ld, mixpos=%ld\n", pplay, pmix);
361         TRACE("this mixpos=%ld, time=%ld\n", bmix, GetTickCount());
362
363         /* the actual primary play position (pplay) is always behind last mixed (pmix),
364          * unless the computer is too slow or something */
365         /* we need to know how far away we are from there */
366 #if 0 /* we'll never fill the primary entirely */
367         if (pmix == pplay) {
368                 if ((state == STATE_PLAYING) || (state == STATE_STOPPING)) {
369                         /* wow, the software mixer is really doing well,
370                          * seems the entire primary buffer is filled! */
371                         pmix += This->dsound->buflen;
372                 }
373                 /* else: the primary buffer is not playing, so probably empty */
374         }
375 #endif
376         if (pmix < pplay) pmix += This->dsound->buflen; /* wraparound */
377         pmix -= pplay;
378         /* detect buffer underrun */
379         if (pwrite < pplay) pwrite += This->dsound->buflen; /* wraparound */
380         pwrite -= pplay;
381         if (pmix > (ds_snd_queue_max * This->dsound->fraglen + pwrite + This->dsound->writelead)) {
382                 WARN("detected an underrun: primary queue was %ld\n",pmix);
383                 pmix = 0;
384         }
385         /* divide the offset by its sample size */
386         pmix /= This->dsound->wfx.nBlockAlign;
387         TRACE("primary back-samples=%ld\n",pmix);
388         /* adjust for our frequency */
389         pmix = (pmix * This->freqAdjust) >> DSOUND_FREQSHIFT;
390         /* multiply by our own sample size */
391         pmix *= This->wfx.nBlockAlign;
392         TRACE("this back-offset=%ld\n", pmix);
393         /* subtract from our last mixed position */
394         bplay = bmix;
395         while (bplay < pmix) bplay += This->buflen; /* wraparound */
396         bplay -= pmix;
397         if (This->leadin && ((bplay < This->startpos) || (bplay > bmix))) {
398                 /* seems we haven't started playing yet */
399                 TRACE("this still in lead-in phase\n");
400                 bplay = This->startpos;
401         }
402         /* return the result */
403         return bplay;
404 }
405
406 static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
407         LPDIRECTSOUNDBUFFER8 iface,LPDWORD playpos,LPDWORD writepos
408 ) {
409         HRESULT hres;
410         ICOM_THIS(IDirectSoundBufferImpl,iface);
411         TRACE("(%p,%p,%p)\n",This,playpos,writepos);
412         if (This->hwbuf) {
413                 hres=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);
414                 if (hres)
415                     return hres;
416         }
417         else {
418                 if (playpos && (This->state != STATE_PLAYING)) {
419                         /* we haven't been merged into the primary buffer (yet) */
420                         *playpos = This->buf_mixpos;
421                 }
422                 else if (playpos) {
423                         DWORD pplay, pwrite, lplay, splay, pstate;
424                         /* let's get this exact; first, recursively call GetPosition on the primary */
425                         EnterCriticalSection(&(This->dsound->mixlock));
426                         DSOUND_PrimaryGetPosition(This->dsound, &pplay, &pwrite);
427                         /* detect HEL mode underrun */
428                         pstate = This->dsound->state;
429                         if (!(This->dsound->hwbuf || This->dsound->pwqueue)) {
430                                 TRACE("detected an underrun\n");
431                                 /* pplay = ? */
432                                 if (pstate == STATE_PLAYING)
433                                         pstate = STATE_STARTING;
434                                 else if (pstate == STATE_STOPPING)
435                                         pstate = STATE_STOPPED;
436                         }
437                         /* get data for ourselves while we still have the lock */
438                         pstate &= This->state;
439                         lplay = This->primary_mixpos;
440                         splay = This->buf_mixpos;
441                         if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || This->dsound->hwbuf) {
442                                 /* calculate play position using this */
443                                 *playpos = DSOUND_CalcPlayPosition(This, pstate, pplay, pwrite, lplay, splay);
444                         } else {
445                                 /* (unless the app isn't using GETCURRENTPOSITION2) */
446                                 /* don't know exactly how this should be handled...
447                                  * the docs says that play cursor is reported as directly
448                                  * behind write cursor, hmm... */
449                                 /* let's just do what might work for Half-Life */
450                                 DWORD wp;
451                                 wp = (This->dsound->pwplay + ds_hel_margin) * This->dsound->fraglen;
452                                 while (wp >= This->dsound->buflen)
453                                         wp -= This->dsound->buflen;
454                                 *playpos = DSOUND_CalcPlayPosition(This, pstate, wp, pwrite, lplay, splay);
455                         }
456                         LeaveCriticalSection(&(This->dsound->mixlock));
457                 }
458                 if (writepos) *writepos = This->buf_mixpos;
459         }
460         if (writepos) {
461                 if (This->state != STATE_STOPPED)
462                         /* apply the documented 10ms lead to writepos */
463                         *writepos += This->writelead;
464                 while (*writepos >= This->buflen) *writepos -= This->buflen;
465         }
466         if (playpos) This->last_playpos = *playpos;
467         TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
468         return DS_OK;
469 }
470
471 static HRESULT WINAPI IDirectSoundBufferImpl_GetStatus(
472         LPDIRECTSOUNDBUFFER8 iface,LPDWORD status
473 ) {
474         ICOM_THIS(IDirectSoundBufferImpl,iface);
475         TRACE("(%p,%p), thread is %04lx\n",This,status,GetCurrentThreadId());
476
477         if (status == NULL)
478                 return DSERR_INVALIDPARAM;
479
480         *status = 0;
481         if ((This->state == STATE_STARTING) || (This->state == STATE_PLAYING)) {
482                 *status |= DSBSTATUS_PLAYING;
483                 if (This->playflags & DSBPLAY_LOOPING)
484                         *status |= DSBSTATUS_LOOPING;
485         }
486
487         TRACE("status=%lx\n", *status);
488         return DS_OK;
489 }
490
491
492 static HRESULT WINAPI IDirectSoundBufferImpl_GetFormat(
493         LPDIRECTSOUNDBUFFER8 iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
494 ) {
495         ICOM_THIS(IDirectSoundBufferImpl,iface);
496         TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
497
498         if (wfsize>sizeof(This->wfx))
499                 wfsize = sizeof(This->wfx);
500         if (lpwf) {     /* NULL is valid */
501                 memcpy(lpwf,&(This->wfx),wfsize);
502                 if (wfwritten)
503                         *wfwritten = wfsize;
504         } else
505                 if (wfwritten)
506                         *wfwritten = sizeof(This->wfx);
507                 else
508                         return DSERR_INVALIDPARAM;
509
510         return DS_OK;
511 }
512
513 static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
514         LPDIRECTSOUNDBUFFER8 iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
515 ) {
516         ICOM_THIS(IDirectSoundBufferImpl,iface);
517
518         TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx) at %ld\n",
519                 This,
520                 writecursor,
521                 writebytes,
522                 lplpaudioptr1,
523                 audiobytes1,
524                 lplpaudioptr2,
525                 audiobytes2,
526                 flags,
527                 GetTickCount()
528         );
529
530         if (flags & DSBLOCK_FROMWRITECURSOR) {
531                 DWORD writepos;
532                 /* GetCurrentPosition does too much magic to duplicate here */
533                 IDirectSoundBufferImpl_GetCurrentPosition(iface, NULL, &writepos);
534                 writecursor += writepos;
535         }
536         while (writecursor >= This->buflen)
537                 writecursor -= This->buflen;
538         if (flags & DSBLOCK_ENTIREBUFFER)
539                 writebytes = This->buflen;
540         if (writebytes > This->buflen)
541                 writebytes = This->buflen;
542
543         assert(audiobytes1!=audiobytes2);
544         assert(lplpaudioptr1!=lplpaudioptr2);
545
546         EnterCriticalSection(&(This->lock));
547
548         if ((writebytes == This->buflen) &&
549             ((This->state == STATE_STARTING) ||
550              (This->state == STATE_PLAYING)))
551                 /* some games, like Half-Life, try to be clever (not) and
552                  * keep one secondary buffer, and mix sounds into it itself,
553                  * locking the entire buffer every time... so we can just forget
554                  * about tracking the last-written-to-position... */
555                 This->probably_valid_to = (DWORD)-1;
556         else
557                 This->probably_valid_to = writecursor;
558
559         if (!(This->dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDSECONDARYLOCK) && This->hwbuf) {
560                 IDsDriverBuffer_Lock(This->hwbuf,
561                                      lplpaudioptr1, audiobytes1,
562                                      lplpaudioptr2, audiobytes2,
563                                      writecursor, writebytes,
564                                      0);
565         }
566         else {
567                 BOOL remix = FALSE;
568                 if (writecursor+writebytes <= This->buflen) {
569                         *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
570                         *audiobytes1 = writebytes;
571                         if (lplpaudioptr2)
572                                 *(LPBYTE*)lplpaudioptr2 = NULL;
573                         if (audiobytes2)
574                                 *audiobytes2 = 0;
575                         TRACE("->%ld.0\n",writebytes);
576                 } else {
577                         *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
578                         *audiobytes1 = This->buflen-writecursor;
579                         if (lplpaudioptr2)
580                                 *(LPBYTE*)lplpaudioptr2 = This->buffer;
581                         if (audiobytes2)
582                                 *audiobytes2 = writebytes-(This->buflen-writecursor);
583                         TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
584                 }
585                 if (This->state == STATE_PLAYING) {
586                         /* if the segment between playpos and buf_mixpos is touched,
587                          * we need to cancel some mixing */
588                         /* we'll assume that the app always calls GetCurrentPosition before
589                          * locking a playing buffer, so that last_playpos is up-to-date */
590                         if (This->buf_mixpos >= This->last_playpos) {
591                                 if (This->buf_mixpos > writecursor &&
592                                     This->last_playpos < writecursor+writebytes)
593                                         remix = TRUE;
594                         }
595                         else {
596                                 if (This->buf_mixpos > writecursor ||
597                                     This->last_playpos < writecursor+writebytes)
598                                         remix = TRUE;
599                         }
600                         if (remix) {
601                                 TRACE("locking prebuffered region, ouch\n");
602                                 DSOUND_MixCancelAt(This, writecursor);
603                         }
604                 }
605         }
606
607         LeaveCriticalSection(&(This->lock));
608         return DS_OK;
609 }
610
611 static HRESULT WINAPI IDirectSoundBufferImpl_SetCurrentPosition(
612         LPDIRECTSOUNDBUFFER8 iface,DWORD newpos
613 ) {
614         ICOM_THIS(IDirectSoundBufferImpl,iface);
615         TRACE("(%p,%ld)\n",This,newpos);
616
617         /* **** */
618         EnterCriticalSection(&(This->lock));
619
620         while (newpos >= This->buflen)
621                 newpos -= This->buflen;
622         This->buf_mixpos = newpos;
623         if (This->hwbuf)
624                 IDsDriverBuffer_SetPosition(This->hwbuf, This->buf_mixpos);
625
626         LeaveCriticalSection(&(This->lock));
627         /* **** */
628
629         return DS_OK;
630 }
631
632 static HRESULT WINAPI IDirectSoundBufferImpl_SetPan(
633         LPDIRECTSOUNDBUFFER8 iface,LONG pan
634 ) {
635         ICOM_THIS(IDirectSoundBufferImpl,iface);
636         LONG oldPan;
637
638         TRACE("(%p,%ld)\n",This,pan);
639
640         if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT))
641                 return DSERR_INVALIDPARAM;
642
643         /* You cannot use both pan and 3D controls */
644         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
645             (This->dsbd.dwFlags & DSBCAPS_CTRL3D))
646                 return DSERR_CONTROLUNAVAIL;
647
648         /* **** */
649         EnterCriticalSection(&(This->lock));
650
651         oldPan = This->volpan.lPan;
652         This->volpan.lPan = pan;
653
654         if (pan != oldPan) {
655                 DSOUND_RecalcVolPan(&(This->volpan));
656
657                 if (This->hwbuf) {
658                         IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
659                 }
660                 else DSOUND_ForceRemix(This);
661         }
662
663         LeaveCriticalSection(&(This->lock));
664         /* **** */
665
666         return DS_OK;
667 }
668
669 static HRESULT WINAPI IDirectSoundBufferImpl_GetPan(
670         LPDIRECTSOUNDBUFFER8 iface,LPLONG pan
671 ) {
672         ICOM_THIS(IDirectSoundBufferImpl,iface);
673         TRACE("(%p,%p)\n",This,pan);
674
675         if (pan == NULL)
676                 return DSERR_INVALIDPARAM;
677
678         *pan = This->volpan.lPan;
679
680         return DS_OK;
681 }
682
683 static HRESULT WINAPI IDirectSoundBufferImpl_Unlock(
684         LPDIRECTSOUNDBUFFER8 iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
685 ) {
686         ICOM_THIS(IDirectSoundBufferImpl,iface);
687         DWORD probably_valid_to;
688
689         TRACE("(%p,%p,%ld,%p,%ld)\n", This,p1,x1,p2,x2);
690
691 #if 0
692         /* Preprocess 3D buffers... */
693
694         /* This is highly experimental and liable to break things */
695         if (This->dsbd.dwFlags & DSBCAPS_CTRL3D)
696                 DSOUND_Create3DBuffer(This);
697 #endif
698
699         if (!(This->dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDSECONDARYLOCK) && This->hwbuf) {
700                 IDsDriverBuffer_Unlock(This->hwbuf, p1, x1, p2, x2);
701         }
702
703         if (p2) probably_valid_to = (((LPBYTE)p2)-This->buffer) + x2;
704         else probably_valid_to = (((LPBYTE)p1)-This->buffer) + x1;
705         while (probably_valid_to >= This->buflen)
706                 probably_valid_to -= This->buflen;
707         if ((probably_valid_to == 0) && ((x1+x2) == This->buflen) &&
708             ((This->state == STATE_STARTING) ||
709              (This->state == STATE_PLAYING)))
710                 /* see IDirectSoundBufferImpl_Lock */
711                 probably_valid_to = (DWORD)-1;
712         This->probably_valid_to = probably_valid_to;
713
714         return DS_OK;
715 }
716
717 static HRESULT WINAPI IDirectSoundBufferImpl_Restore(
718         LPDIRECTSOUNDBUFFER8 iface
719 ) {
720         ICOM_THIS(IDirectSoundBufferImpl,iface);
721         FIXME("(%p):stub\n",This);
722         return DS_OK;
723 }
724
725 static HRESULT WINAPI IDirectSoundBufferImpl_GetFrequency(
726         LPDIRECTSOUNDBUFFER8 iface,LPDWORD freq
727 ) {
728         ICOM_THIS(IDirectSoundBufferImpl,iface);
729         TRACE("(%p,%p)\n",This,freq);
730
731         if (freq == NULL)
732                 return DSERR_INVALIDPARAM;
733
734         *freq = This->freq;
735         TRACE("-> %ld\n", *freq);
736
737         return DS_OK;
738 }
739
740 static HRESULT WINAPI IDirectSoundBufferImpl_SetFX(
741         LPDIRECTSOUNDBUFFER8 iface,DWORD dwEffectsCount,LPDSEFFECTDESC pDSFXDesc,LPDWORD pdwResultCodes
742 ) {
743         ICOM_THIS(IDirectSoundBufferImpl,iface);
744         DWORD u;
745
746         FIXME("(%p,%lu,%p,%p): stub\n",This,dwEffectsCount,pDSFXDesc,pdwResultCodes);
747
748         if (pdwResultCodes)
749                 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
750
751         return DSERR_CONTROLUNAVAIL;
752 }
753
754 static HRESULT WINAPI IDirectSoundBufferImpl_AcquireResources(
755         LPDIRECTSOUNDBUFFER8 iface,DWORD dwFlags,DWORD dwEffectsCount,LPDWORD pdwResultCodes
756 ) {
757         ICOM_THIS(IDirectSoundBufferImpl,iface);
758         DWORD u;
759
760         FIXME("(%p,%08lu,%lu,%p): stub\n",This,dwFlags,dwEffectsCount,pdwResultCodes);
761
762         if (pdwResultCodes)
763                 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
764
765         return DSERR_CONTROLUNAVAIL;
766 }
767
768 static HRESULT WINAPI IDirectSoundBufferImpl_GetObjectInPath(
769         LPDIRECTSOUNDBUFFER8 iface,REFGUID rguidObject,DWORD dwIndex,REFGUID rguidInterface,LPVOID* ppObject
770 ) {
771         ICOM_THIS(IDirectSoundBufferImpl,iface);
772
773         FIXME("(%p,%s,%lu,%s,%p): stub\n",This,debugstr_guid(rguidObject),dwIndex,debugstr_guid(rguidInterface),ppObject);
774
775         return DSERR_CONTROLUNAVAIL;
776 }
777
778 static HRESULT WINAPI IDirectSoundBufferImpl_Initialize(
779         LPDIRECTSOUNDBUFFER8 iface,LPDIRECTSOUND8 dsound,LPDSBUFFERDESC dbsd
780 ) {
781         ICOM_THIS(IDirectSoundBufferImpl,iface);
782         FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
783         DPRINTF("Re-Init!!!\n");
784         return DSERR_ALREADYINITIALIZED;
785 }
786
787 static HRESULT WINAPI IDirectSoundBufferImpl_GetCaps(
788         LPDIRECTSOUNDBUFFER8 iface,LPDSBCAPS caps
789 ) {
790         ICOM_THIS(IDirectSoundBufferImpl,iface);
791         TRACE("(%p)->(%p)\n",This,caps);
792
793         if (caps == NULL || caps->dwSize!=sizeof(*caps))
794                 return DSERR_INVALIDPARAM;
795
796         caps->dwFlags = This->dsbd.dwFlags;
797         if (This->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
798         else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
799
800         caps->dwBufferBytes = This->buflen;
801
802         /* This value represents the speed of the "unlock" command.
803            As unlock is quite fast (it does not do anything), I put
804            4096 ko/s = 4 Mo / s */
805         /* FIXME: hwbuf speed */
806         caps->dwUnlockTransferRate = 4096;
807         caps->dwPlayCpuOverhead = 0;
808
809         return DS_OK;
810 }
811
812 static HRESULT WINAPI IDirectSoundBufferImpl_QueryInterface(
813         LPDIRECTSOUNDBUFFER8 iface,REFIID riid,LPVOID *ppobj
814 ) {
815         ICOM_THIS(IDirectSoundBufferImpl,iface);
816
817         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
818
819         if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
820                 IDirectSoundNotifyImpl  *dsn;
821
822                 dsn = (IDirectSoundNotifyImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn));
823                 dsn->ref = 1;
824                 dsn->dsb = This;
825                 dsn->dscb = 0;
826                 IDirectSoundBuffer8_AddRef(iface);
827                 dsn->lpVtbl = &dsnvt;
828                 *ppobj = (LPVOID)dsn;
829                 return S_OK;
830         }
831
832         if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
833                 if (!This->ds3db)
834                         IDirectSound3DBufferImpl_Create(This, &This->ds3db);
835                 *ppobj = This->ds3db;
836                 if (*ppobj) {
837                         IDirectSound3DBuffer_AddRef((LPDIRECTSOUND3DBUFFER)*ppobj);
838                         return S_OK;
839                 }
840                 return E_FAIL;
841         }
842
843         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
844                 ERR("app requested IDirectSound3DListener on secondary buffer\n");
845                 *ppobj = NULL;
846                 return E_FAIL;
847         }
848
849         if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
850                 if (!This->iks)
851                         IKsPropertySetImpl_Create(This, &This->iks);
852                 *ppobj = This->iks;
853                 if (*ppobj) {
854                         IKsPropertySet_AddRef((LPKSPROPERTYSET)*ppobj);
855                         return S_OK;
856                 }
857                 return E_FAIL;
858         }
859
860         if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) {
861             IDirectSoundBuffer8_AddRef(iface);
862             *ppobj = This;
863             return NO_ERROR;
864         }
865
866         FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
867
868         *ppobj = NULL;
869
870         return E_NOINTERFACE;
871 }
872
873 static ICOM_VTABLE(IDirectSoundBuffer8) dsbvt =
874 {
875         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
876         IDirectSoundBufferImpl_QueryInterface,
877         IDirectSoundBufferImpl_AddRef,
878         IDirectSoundBufferImpl_Release,
879         IDirectSoundBufferImpl_GetCaps,
880         IDirectSoundBufferImpl_GetCurrentPosition,
881         IDirectSoundBufferImpl_GetFormat,
882         IDirectSoundBufferImpl_GetVolume,
883         IDirectSoundBufferImpl_GetPan,
884         IDirectSoundBufferImpl_GetFrequency,
885         IDirectSoundBufferImpl_GetStatus,
886         IDirectSoundBufferImpl_Initialize,
887         IDirectSoundBufferImpl_Lock,
888         IDirectSoundBufferImpl_Play,
889         IDirectSoundBufferImpl_SetCurrentPosition,
890         IDirectSoundBufferImpl_SetFormat,
891         IDirectSoundBufferImpl_SetVolume,
892         IDirectSoundBufferImpl_SetPan,
893         IDirectSoundBufferImpl_SetFrequency,
894         IDirectSoundBufferImpl_Stop,
895         IDirectSoundBufferImpl_Unlock,
896         IDirectSoundBufferImpl_Restore,
897         IDirectSoundBufferImpl_SetFX,
898         IDirectSoundBufferImpl_AcquireResources,
899         IDirectSoundBufferImpl_GetObjectInPath
900 };
901
902 HRESULT WINAPI SecondaryBuffer_Create(
903         IDirectSoundImpl *This,
904         IDirectSoundBufferImpl **pdsb,
905         LPDSBUFFERDESC dsbd)
906 {
907         IDirectSoundBufferImpl *dsb;
908         LPWAVEFORMATEX wfex = dsbd->lpwfxFormat;
909         HRESULT err = DS_OK;
910         DWORD capf = 0;
911         int use_hw;
912
913         if (dsbd->dwBufferBytes < DSBSIZE_MIN || dsbd->dwBufferBytes > DSBSIZE_MAX) {
914                 ERR("invalid sound buffer size %ld\n", dsbd->dwBufferBytes);
915                 return DSERR_INVALIDPARAM; /* FIXME: which error? */
916         }
917
918         dsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
919         dsb->ref = 1;
920         dsb->dsound = This;
921         dsb->parent = NULL;
922         dsb->lpVtbl = &dsbvt;
923
924         memcpy(&dsb->dsbd, dsbd, sizeof(*dsbd));
925         if (wfex)
926                 memcpy(&dsb->wfx, wfex, sizeof(dsb->wfx));
927
928         TRACE("Created buffer at %p\n", dsb);
929
930         dsb->buflen = dsbd->dwBufferBytes;
931         dsb->freq = dsbd->lpwfxFormat->nSamplesPerSec;
932
933         /* Check necessary hardware mixing capabilities */
934         if (wfex->nChannels==2) capf |= DSCAPS_SECONDARYSTEREO;
935         else capf |= DSCAPS_SECONDARYMONO;
936         if (wfex->wBitsPerSample==16) capf |= DSCAPS_SECONDARY16BIT;
937         else capf |= DSCAPS_SECONDARY8BIT;
938         use_hw = (This->drvcaps.dwFlags & capf) == capf;
939
940         /* FIXME: check hardware sample rate mixing capabilities */
941         /* FIXME: check app hints for software/hardware buffer (STATIC, LOCHARDWARE, etc) */
942         /* FIXME: check whether any hardware buffers are left */
943         /* FIXME: handle DSDHEAP_CREATEHEAP for hardware buffers */
944
945         /* Allocate system memory if applicable */
946         if ((This->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) || !use_hw) {
947                 dsb->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,dsb->buflen);
948                 if (dsb->buffer == NULL)
949                         err = DSERR_OUTOFMEMORY;
950         }
951
952         /* Allocate the hardware buffer */
953         if (use_hw && (err == DS_OK)) {
954                 err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
955                                                   &(dsb->buflen),&(dsb->buffer),
956                                                   (LPVOID*)&(dsb->hwbuf));
957         }
958
959         if (err != DS_OK) {
960                 if (dsb->buffer)
961                         HeapFree(GetProcessHeap(),0,dsb->buffer);
962                 HeapFree(GetProcessHeap(),0,dsb);
963                 dsb = NULL;
964                 return err;
965         }
966         /* calculate fragment size and write lead */
967         DSOUND_RecalcFormat(dsb);
968
969         /* It's not necessary to initialize values to zero since */
970         /* we allocated this structure with HEAP_ZERO_MEMORY... */
971         dsb->playpos = 0;
972         dsb->buf_mixpos = 0;
973         dsb->state = STATE_STOPPED;
974
975         dsb->freqAdjust = (dsb->freq << DSOUND_FREQSHIFT) /
976                 This->wfx.nSamplesPerSec;
977         dsb->nAvgBytesPerSec = dsb->freq *
978                 dsbd->lpwfxFormat->nBlockAlign;
979
980         if (dsbd->dwFlags & DSBCAPS_CTRL3D) {
981                 IDirectSound3DBufferImpl_Create(dsb, &dsb->ds3db);
982         }
983         else
984                 DSOUND_RecalcVolPan(&(dsb->volpan));
985
986         InitializeCriticalSection(&(dsb->lock));
987
988         /* register buffer */
989         RtlAcquireResourceExclusive(&(This->lock), TRUE);
990         if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
991                 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl*)*(This->nrofbuffers+1));
992                 if (newbuffers) {
993                         This->buffers = newbuffers;
994                         This->buffers[This->nrofbuffers] = dsb;
995                         This->nrofbuffers++;
996                         TRACE("buffer count is now %d\n", This->nrofbuffers);
997                 } else {
998                         ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
999                         err = DSERR_OUTOFMEMORY;
1000                 }
1001         }
1002         RtlReleaseResource(&(This->lock));
1003         IDirectSound8_AddRef((LPDIRECTSOUND8)This);
1004
1005         if (err != DS_OK) {
1006                 /* oops... */
1007                 IDirectSoundBuffer8_Release((LPDIRECTSOUNDBUFFER8)dsb);
1008                 *pdsb = NULL;
1009                 return err;
1010         }
1011
1012         *pdsb = dsb;
1013         return S_OK;
1014 }