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