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