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