Added regedit unit test, a couple minor changes to regedit.
[wine] / dlls / dsound / primary.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 static HRESULT mmErr(UINT err)
49 {
50         switch(err) {
51         case MMSYSERR_NOERROR:
52                 return DS_OK;
53         case MMSYSERR_ALLOCATED:
54                 return DSERR_ALLOCATED;
55         case MMSYSERR_INVALHANDLE:
56                 return DSERR_GENERIC; /* FIXME */
57         case MMSYSERR_NODRIVER:
58                 return DSERR_NODRIVER;
59         case MMSYSERR_NOMEM:
60                 return DSERR_OUTOFMEMORY;
61         case MMSYSERR_INVALPARAM:
62                 return DSERR_INVALIDPARAM;
63         default:
64                 FIXME("Unknown MMSYS error %d\n",err);
65                 return DSERR_GENERIC;
66         }
67 }
68
69 void DSOUND_RecalcPrimary(IDirectSoundImpl *This)
70 {
71         DWORD sw;
72
73         sw = This->wfx.nChannels * (This->wfx.wBitsPerSample / 8);
74         if (This->hwbuf) {
75                 DWORD fraglen;
76                 /* let fragment size approximate the timer delay */
77                 fraglen = (This->wfx.nSamplesPerSec * DS_TIME_DEL / 1000) * sw;
78                 /* reduce fragment size until an integer number of them fits in the buffer */
79                 /* (FIXME: this may or may not be a good idea) */
80                 while (This->buflen % fraglen) fraglen -= sw;
81                 This->fraglen = fraglen;
82                 TRACE("fraglen=%ld\n", This->fraglen);
83         }
84         /* calculate the 10ms write lead */
85         This->writelead = (This->wfx.nSamplesPerSec / 100) * sw;
86 }
87
88 static HRESULT DSOUND_PrimaryOpen(IDirectSoundImpl *This)
89 {
90         HRESULT err = DS_OK;
91
92         /* are we using waveOut stuff? */
93         if (!This->hwbuf) {
94                 LPBYTE newbuf;
95                 DWORD buflen;
96                 HRESULT merr = DS_OK;
97                 /* Start in pause mode, to allow buffers to get filled */
98                 waveOutPause(This->hwo);
99                 if (This->state == STATE_PLAYING) This->state = STATE_STARTING;
100                 else if (This->state == STATE_STOPPING) This->state = STATE_STOPPED;
101                 /* use fragments of 10ms (1/100s) each (which should get us within
102                  * the documented write cursor lead of 10-15ms) */
103                 buflen = ((This->wfx.nAvgBytesPerSec / 100) & ~3) * DS_HEL_FRAGS;
104                 TRACE("desired buflen=%ld, old buffer=%p\n", buflen, This->buffer);
105                 /* reallocate emulated primary buffer */
106                 newbuf = (LPBYTE)HeapReAlloc(GetProcessHeap(),0,This->buffer,buflen);
107                 if (newbuf == NULL) {
108                         ERR("failed to allocate primary buffer\n");
109                         merr = DSERR_OUTOFMEMORY;
110                         /* but the old buffer might still exists and must be re-prepared */
111                 } else {
112                         This->buffer = newbuf;
113                         This->buflen = buflen;
114                 }
115                 if (This->buffer) {
116                         unsigned c;
117
118                         This->fraglen = This->buflen / DS_HEL_FRAGS;
119
120                         /* prepare fragment headers */
121                         for (c=0; c<DS_HEL_FRAGS; c++) {
122                                 This->pwave[c]->lpData = This->buffer + c*This->fraglen;
123                                 This->pwave[c]->dwBufferLength = This->fraglen;
124                                 This->pwave[c]->dwUser = (DWORD)This;
125                                 This->pwave[c]->dwFlags = 0;
126                                 This->pwave[c]->dwLoops = 0;
127                                 err = mmErr(waveOutPrepareHeader(This->hwo,This->pwave[c],sizeof(WAVEHDR)));
128                                 if (err != DS_OK) {
129                                         while (c--)
130                                                 waveOutUnprepareHeader(This->hwo,This->pwave[c],sizeof(WAVEHDR));
131                                         break;
132                                 }
133                         }
134
135                         This->pwplay = 0;
136                         This->pwwrite = 0;
137                         This->pwqueue = 0;
138                         memset(This->buffer, (This->wfx.wBitsPerSample == 16) ? 0 : 128, This->buflen);
139                         TRACE("fraglen=%ld\n", This->fraglen);
140                         DSOUND_WaveQueue(This, (DWORD)-1);
141                 }
142                 if ((err == DS_OK) && (merr != DS_OK))
143                         err = merr;
144         }
145         return err;
146 }
147
148
149 static void DSOUND_PrimaryClose(IDirectSoundImpl *This)
150 {
151         /* are we using waveOut stuff? */
152         if (!This->hwbuf) {
153                 unsigned c;
154
155                 This->pwqueue = (DWORD)-1; /* resetting queues */
156                 waveOutReset(This->hwo);
157                 for (c=0; c<DS_HEL_FRAGS; c++)
158                         waveOutUnprepareHeader(This->hwo, This->pwave[c], sizeof(WAVEHDR));
159                 This->pwqueue = 0;
160         }
161 }
162
163 HRESULT DSOUND_PrimaryCreate(IDirectSoundImpl *This)
164 {
165         HRESULT err = DS_OK;
166
167         This->buflen = This->wfx.nAvgBytesPerSec;
168
169         /* FIXME: verify that hardware capabilities (DSCAPS_PRIMARY flags) match */
170
171         if (This->driver) {
172                 err = IDsDriver_CreateSoundBuffer(This->driver,&(This->wfx),
173                                                   DSBCAPS_PRIMARYBUFFER,0,
174                                                   &(This->buflen),&(This->buffer),
175                                                   (LPVOID*)&(This->hwbuf));
176         }
177         if (err == DS_OK)
178                 err = DSOUND_PrimaryOpen(This);
179         if (err != DS_OK)
180                 return err;
181         /* calculate fragment size and write lead */
182         DSOUND_RecalcPrimary(This);
183         This->state = STATE_STOPPED;
184         return DS_OK;
185 }
186
187 HRESULT DSOUND_PrimaryDestroy(IDirectSoundImpl *This)
188 {
189         DSOUND_PrimaryClose(This);
190         if (This->hwbuf) {
191                 IDsDriverBuffer_Release(This->hwbuf);
192         }
193         return DS_OK;
194 }
195
196 HRESULT DSOUND_PrimaryPlay(IDirectSoundImpl *This)
197 {
198         HRESULT err = DS_OK;
199         if (This->hwbuf)
200                 err = IDsDriverBuffer_Play(This->hwbuf, 0, 0, DSBPLAY_LOOPING);
201         else
202                 err = mmErr(waveOutRestart(This->hwo));
203         return err;
204 }
205
206 HRESULT DSOUND_PrimaryStop(IDirectSoundImpl *This)
207 {
208         HRESULT err = DS_OK;
209
210         TRACE("\n");
211
212         if (This->hwbuf) {
213                 err = IDsDriverBuffer_Stop(This->hwbuf);
214                 if (err == DSERR_BUFFERLOST) {
215                         /* Wine-only: the driver wants us to reopen the device */
216                         /* FIXME: check for errors */
217                         IDsDriverBuffer_Release(This->hwbuf);
218                         waveOutClose(This->hwo);
219                         This->hwo = 0;
220                         err = mmErr(waveOutOpen(&(This->hwo), This->drvdesc.dnDevNode,
221                                                 &(This->wfx), (DWORD)DSOUND_callback, (DWORD)This,
222                                                 CALLBACK_FUNCTION | WAVE_DIRECTSOUND));
223                         if (err == DS_OK)
224                                 err = IDsDriver_CreateSoundBuffer(This->driver,&(This->wfx),
225                                                                   DSBCAPS_PRIMARYBUFFER,0,
226                                                                   &(This->buflen),&(This->buffer),
227                                                                   (LPVOID)&(This->hwbuf));
228                 }
229         }
230         else
231                 err = mmErr(waveOutPause(This->hwo));
232         return err;
233 }
234
235 HRESULT DSOUND_PrimaryGetPosition(IDirectSoundImpl *This, LPDWORD playpos, LPDWORD writepos)
236 {
237         if (This->hwbuf) {
238                 HRESULT err=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);
239                 if (err) return err;
240         }
241         else {
242                 if (playpos) {
243                         MMTIME mtime;
244                         mtime.wType = TIME_BYTES;
245                         waveOutGetPosition(This->hwo, &mtime, sizeof(mtime));
246                         mtime.u.cb = mtime.u.cb % This->buflen;
247                         *playpos = mtime.u.cb;
248                 }
249                 if (writepos) {
250                         /* the writepos should only be used by apps with WRITEPRIMARY priority,
251                          * in which case our software mixer is disabled anyway */
252                         *writepos = (This->pwplay + ds_hel_margin) * This->fraglen;
253                         while (*writepos >= This->buflen)
254                                 *writepos -= This->buflen;
255                 }
256         }
257         TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
258         return DS_OK;
259 }
260
261
262 /*******************************************************************************
263  *              IDirectSoundBuffer
264  */
265 /* This sets this format for the <em>Primary Buffer Only</em> */
266 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
267 static HRESULT WINAPI PrimaryBufferImpl_SetFormat(
268         LPDIRECTSOUNDBUFFER8 iface,LPWAVEFORMATEX wfex
269 ) {
270         ICOM_THIS(PrimaryBufferImpl,iface);
271         IDirectSoundImpl* dsound = This->dsound;
272         IDirectSoundBufferImpl** dsb;
273         HRESULT err = DS_OK;
274         int                     i;
275
276         if (This->dsound->priolevel == DSSCL_NORMAL) {
277                 TRACE("failed priority check!\n");
278                 return DSERR_PRIOLEVELNEEDED;
279         }
280
281         /* Let's be pedantic! */
282         if ((wfex == NULL) ||
283             (wfex->wFormatTag != WAVE_FORMAT_PCM) ||
284             (wfex->nChannels < 1) || (wfex->nChannels > 2) ||
285             (wfex->nSamplesPerSec < 1) ||
286             (wfex->nBlockAlign < 1) || (wfex->nChannels > 4) ||
287             ((wfex->wBitsPerSample != 8) && (wfex->wBitsPerSample != 16))) {
288                 TRACE("failed pedantic check!\n");
289                 return DSERR_INVALIDPARAM;
290         }
291
292         /* **** */
293         RtlAcquireResourceExclusive(&(dsound->lock), TRUE);
294
295         if (dsound->wfx.nSamplesPerSec != wfex->nSamplesPerSec) {
296                 dsb = dsound->buffers;
297                 for (i = 0; i < dsound->nrofbuffers; i++, dsb++) {
298                         /* **** */
299                         EnterCriticalSection(&((*dsb)->lock));
300
301                         (*dsb)->freqAdjust = ((*dsb)->freq << DSOUND_FREQSHIFT) /
302                                 wfex->nSamplesPerSec;
303
304                         LeaveCriticalSection(&((*dsb)->lock));
305                         /* **** */
306                 }
307         }
308
309         memcpy(&(dsound->wfx), wfex, sizeof(dsound->wfx));
310
311         TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
312                    "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
313                    wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
314                    wfex->nAvgBytesPerSec, wfex->nBlockAlign,
315                    wfex->wBitsPerSample, wfex->cbSize);
316
317         dsound->wfx.nAvgBytesPerSec =
318                 dsound->wfx.nSamplesPerSec * dsound->wfx.nBlockAlign;
319
320         if (dsound->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) {
321                 /* FIXME: check for errors */
322                 DSOUND_PrimaryClose(dsound);
323                 waveOutClose(dsound->hwo);
324                 dsound->hwo = 0;
325                 err = mmErr(waveOutOpen(&(dsound->hwo), dsound->drvdesc.dnDevNode,
326                                         &(dsound->wfx), (DWORD)DSOUND_callback, (DWORD)dsound,
327                                         CALLBACK_FUNCTION | WAVE_DIRECTSOUND));
328                 if (err == DS_OK)
329                     DSOUND_PrimaryOpen(dsound);
330         }
331         if (dsound->hwbuf) {
332                 err = IDsDriverBuffer_SetFormat(dsound->hwbuf, &(dsound->wfx));
333                 if (err == DSERR_BUFFERLOST) {
334                         /* Wine-only: the driver wants us to recreate the HW buffer */
335                         IDsDriverBuffer_Release(dsound->hwbuf);
336                         err = IDsDriver_CreateSoundBuffer(dsound->driver,&(dsound->wfx),
337                                                           DSBCAPS_PRIMARYBUFFER,0,
338                                                           &(dsound->buflen),&(dsound->buffer),
339                                                           (LPVOID)&(dsound->hwbuf));
340                         if (dsound->state == STATE_PLAYING) dsound->state = STATE_STARTING;
341                         else if (dsound->state == STATE_STOPPING) dsound->state = STATE_STOPPED;
342                 }
343                 /* FIXME: should we set err back to DS_OK in all cases ? */
344         }
345         DSOUND_RecalcPrimary(dsound);
346
347         RtlReleaseResource(&(dsound->lock));
348         /* **** */
349
350         return err;
351 }
352
353 static HRESULT WINAPI PrimaryBufferImpl_SetVolume(
354         LPDIRECTSOUNDBUFFER8 iface,LONG vol
355 ) {
356         ICOM_THIS(PrimaryBufferImpl,iface);
357         IDirectSoundImpl* dsound = This->dsound;
358         LONG oldVol;
359
360         TRACE("(%p,%ld)\n",This,vol);
361
362         /* I'm not sure if we need this for primary buffer */
363         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
364                 return DSERR_CONTROLUNAVAIL;
365
366         if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN))
367                 return DSERR_INVALIDPARAM;
368
369         /* **** */
370         EnterCriticalSection(&(dsound->mixlock));
371
372         oldVol = dsound->volpan.lVolume;
373         dsound->volpan.lVolume = vol;
374         DSOUND_RecalcVolPan(&dsound->volpan);
375
376         if (vol != oldVol) {
377                 if (dsound->hwbuf) {
378                         IDsDriverBuffer_SetVolumePan(dsound->hwbuf, &(dsound->volpan));
379                 }
380                 else {
381 #if 0 /* should we really do this? */
382                         /* the DS volume ranges from 0 (max, 0dB attenuation) to -10000 (min, 100dB attenuation) */
383                         /* the MM volume ranges from 0 to 0xffff in an unspecified logarithmic scale */
384                         WORD cvol = 0xffff + vol*6 + vol/2;
385                         DWORD vol = cvol | ((DWORD)cvol << 16)
386                         waveOutSetVolume(dsound->hwo, vol);
387 #endif
388                 }
389         }
390
391         LeaveCriticalSection(&(dsound->mixlock));
392         /* **** */
393
394         return DS_OK;
395 }
396
397 static HRESULT WINAPI PrimaryBufferImpl_GetVolume(
398         LPDIRECTSOUNDBUFFER8 iface,LPLONG vol
399 ) {
400         ICOM_THIS(PrimaryBufferImpl,iface);
401         TRACE("(%p,%p)\n",This,vol);
402
403         if (vol == NULL)
404                 return DSERR_INVALIDPARAM;
405
406         *vol = This->dsound->volpan.lVolume;
407         return DS_OK;
408 }
409
410 static HRESULT WINAPI PrimaryBufferImpl_SetFrequency(
411         LPDIRECTSOUNDBUFFER8 iface,DWORD freq
412 ) {
413         ICOM_THIS(PrimaryBufferImpl,iface);
414
415         TRACE("(%p,%ld)\n",This,freq);
416
417         /* You cannot set the frequency of the primary buffer */
418         return DSERR_CONTROLUNAVAIL;
419 }
420
421 static HRESULT WINAPI PrimaryBufferImpl_Play(
422         LPDIRECTSOUNDBUFFER8 iface,DWORD reserved1,DWORD reserved2,DWORD flags
423 ) {
424         ICOM_THIS(PrimaryBufferImpl,iface);
425         IDirectSoundImpl* dsound = This->dsound;
426
427         TRACE("(%p,%08lx,%08lx,%08lx)\n",
428                 This,reserved1,reserved2,flags
429         );
430
431         if (!(flags & DSBPLAY_LOOPING))
432                 return DSERR_INVALIDPARAM;
433
434         /* **** */
435         EnterCriticalSection(&(dsound->mixlock));
436
437         if (dsound->state == STATE_STOPPED)
438                 dsound->state = STATE_STARTING;
439         else if (dsound->state == STATE_STOPPING)
440                 dsound->state = STATE_PLAYING;
441
442         LeaveCriticalSection(&(dsound->mixlock));
443         /* **** */
444
445         return DS_OK;
446 }
447
448 static HRESULT WINAPI PrimaryBufferImpl_Stop(LPDIRECTSOUNDBUFFER8 iface)
449 {
450         ICOM_THIS(PrimaryBufferImpl,iface);
451         IDirectSoundImpl* dsound = This->dsound;
452
453         TRACE("(%p)\n",This);
454
455         /* **** */
456         EnterCriticalSection(&(dsound->mixlock));
457
458         if (dsound->state == STATE_PLAYING)
459                 dsound->state = STATE_STOPPING;
460         else if (dsound->state == STATE_STARTING)
461                 dsound->state = STATE_STOPPED;
462
463         LeaveCriticalSection(&(dsound->mixlock));
464         /* **** */
465
466         return DS_OK;
467 }
468
469 static DWORD WINAPI PrimaryBufferImpl_AddRef(LPDIRECTSOUNDBUFFER8 iface) {
470         ICOM_THIS(PrimaryBufferImpl,iface);
471         DWORD ref;
472
473         TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
474
475         ref = InterlockedIncrement(&(This->ref));
476         if (!ref) {
477                 FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n");
478         }
479         return ref;
480 }
481 static DWORD WINAPI PrimaryBufferImpl_Release(LPDIRECTSOUNDBUFFER8 iface) {
482         ICOM_THIS(PrimaryBufferImpl,iface);
483         DWORD ref;
484
485         TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
486
487         ref = InterlockedDecrement(&(This->ref));
488         if (ref) return ref;
489
490         IDirectSound_Release((LPDIRECTSOUND)This->dsound);
491
492 #if 0
493         if (This->iks) {
494                 HeapFree(GetProcessHeap(), 0, This->iks);
495         }
496 #endif
497
498         HeapFree(GetProcessHeap(),0,This);
499
500         return 0;
501 }
502
503 static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(
504         LPDIRECTSOUNDBUFFER8 iface,LPDWORD playpos,LPDWORD writepos
505 ) {
506         ICOM_THIS(PrimaryBufferImpl,iface);
507         IDirectSoundImpl* dsound = This->dsound;
508
509         TRACE("(%p,%p,%p)\n",This,playpos,writepos);
510         DSOUND_PrimaryGetPosition(dsound, playpos, writepos);
511         if (writepos) {
512                 if (dsound->state != STATE_STOPPED)
513                         /* apply the documented 10ms lead to writepos */
514                         *writepos += dsound->writelead;
515                 while (*writepos >= dsound->buflen) *writepos -= dsound->buflen;
516         }
517         TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
518         return DS_OK;
519 }
520
521 static HRESULT WINAPI PrimaryBufferImpl_GetStatus(
522         LPDIRECTSOUNDBUFFER8 iface,LPDWORD status
523 ) {
524         ICOM_THIS(PrimaryBufferImpl,iface);
525         TRACE("(%p,%p), thread is %lx\n",This,status,GetCurrentThreadId());
526
527         if (status == NULL)
528                 return DSERR_INVALIDPARAM;
529
530         *status = 0;
531         if ((This->dsound->state == STATE_STARTING) ||
532             (This->dsound->state == STATE_PLAYING))
533                 *status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING;
534
535         TRACE("status=%lx\n", *status);
536         return DS_OK;
537 }
538
539
540 static HRESULT WINAPI PrimaryBufferImpl_GetFormat(
541         LPDIRECTSOUNDBUFFER8 iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
542 ) {
543         ICOM_THIS(PrimaryBufferImpl,iface);
544         TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
545
546         if (wfsize>sizeof(This->dsound->wfx))
547                 wfsize = sizeof(This->dsound->wfx);
548         if (lpwf) {     /* NULL is valid */
549                 memcpy(lpwf,&(This->dsound->wfx),wfsize);
550                 if (wfwritten)
551                         *wfwritten = wfsize;
552         } else
553                 if (wfwritten)
554                         *wfwritten = sizeof(This->dsound->wfx);
555                 else
556                         return DSERR_INVALIDPARAM;
557
558         return DS_OK;
559 }
560
561 static HRESULT WINAPI PrimaryBufferImpl_Lock(
562         LPDIRECTSOUNDBUFFER8 iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
563 ) {
564         ICOM_THIS(PrimaryBufferImpl,iface);
565         IDirectSoundImpl* dsound = This->dsound;
566
567         TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx) at %ld\n",
568                 This,
569                 writecursor,
570                 writebytes,
571                 lplpaudioptr1,
572                 audiobytes1,
573                 lplpaudioptr2,
574                 audiobytes2,
575                 flags,
576                 GetTickCount()
577         );
578
579         if (dsound->priolevel != DSSCL_WRITEPRIMARY)
580                 return DSERR_PRIOLEVELNEEDED;
581
582         if (flags & DSBLOCK_FROMWRITECURSOR) {
583                 DWORD writepos;
584                 /* GetCurrentPosition does too much magic to duplicate here */
585                 IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writepos);
586                 writecursor += writepos;
587         }
588         while (writecursor >= dsound->buflen)
589                 writecursor -= dsound->buflen;
590         if (flags & DSBLOCK_ENTIREBUFFER)
591                 writebytes = dsound->buflen;
592         if (writebytes > dsound->buflen)
593                 writebytes = dsound->buflen;
594
595         assert(audiobytes1!=audiobytes2);
596         assert(lplpaudioptr1!=lplpaudioptr2);
597
598         if (!(dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && dsound->hwbuf) {
599                 IDsDriverBuffer_Lock(dsound->hwbuf,
600                                      lplpaudioptr1, audiobytes1,
601                                      lplpaudioptr2, audiobytes2,
602                                      writecursor, writebytes,
603                                      0);
604         }
605         else {
606                 if (writecursor+writebytes <= dsound->buflen) {
607                         *(LPBYTE*)lplpaudioptr1 = dsound->buffer+writecursor;
608                         *audiobytes1 = writebytes;
609                         if (lplpaudioptr2)
610                                 *(LPBYTE*)lplpaudioptr2 = NULL;
611                         if (audiobytes2)
612                                 *audiobytes2 = 0;
613                         TRACE("->%ld.0\n",writebytes);
614                 } else {
615                         *(LPBYTE*)lplpaudioptr1 = dsound->buffer+writecursor;
616                         *audiobytes1 = dsound->buflen-writecursor;
617                         if (lplpaudioptr2)
618                                 *(LPBYTE*)lplpaudioptr2 = dsound->buffer;
619                         if (audiobytes2)
620                                 *audiobytes2 = writebytes-(dsound->buflen-writecursor);
621                         TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
622                 }
623         }
624         return DS_OK;
625 }
626
627 static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition(
628         LPDIRECTSOUNDBUFFER8 iface,DWORD newpos
629 ) {
630         ICOM_THIS(PrimaryBufferImpl,iface);
631         TRACE("(%p,%ld)\n",This,newpos);
632
633         /* You cannot set the position of the primary buffer */
634         return DSERR_INVALIDCALL;
635 }
636
637 static HRESULT WINAPI PrimaryBufferImpl_SetPan(
638         LPDIRECTSOUNDBUFFER8 iface,LONG pan
639 ) {
640         ICOM_THIS(PrimaryBufferImpl,iface);
641         TRACE("(%p,%ld)\n",This,pan);
642
643         /* You cannot set the pan of the primary buffer */
644         return DSERR_CONTROLUNAVAIL;
645 }
646
647 static HRESULT WINAPI PrimaryBufferImpl_GetPan(
648         LPDIRECTSOUNDBUFFER8 iface,LPLONG pan
649 ) {
650         ICOM_THIS(PrimaryBufferImpl,iface);
651         TRACE("(%p,%p)\n",This,pan);
652
653         if (pan == NULL)
654                 return DSERR_INVALIDPARAM;
655
656         *pan = This->dsound->volpan.lPan;
657
658         return DS_OK;
659 }
660
661 static HRESULT WINAPI PrimaryBufferImpl_Unlock(
662         LPDIRECTSOUNDBUFFER8 iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
663 ) {
664         ICOM_THIS(PrimaryBufferImpl,iface);
665         IDirectSoundImpl* dsound = This->dsound;
666
667         TRACE("(%p,%p,%ld,%p,%ld):stub\n", This,p1,x1,p2,x2);
668
669         if (dsound->priolevel != DSSCL_WRITEPRIMARY)
670                 return DSERR_PRIOLEVELNEEDED;
671
672         if (!(dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && dsound->hwbuf) {
673                 IDsDriverBuffer_Unlock(dsound->hwbuf, p1, x1, p2, x2);
674         }
675
676         return DS_OK;
677 }
678
679 static HRESULT WINAPI PrimaryBufferImpl_Restore(
680         LPDIRECTSOUNDBUFFER8 iface
681 ) {
682         ICOM_THIS(PrimaryBufferImpl,iface);
683         FIXME("(%p):stub\n",This);
684         return DS_OK;
685 }
686
687 static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(
688         LPDIRECTSOUNDBUFFER8 iface,LPDWORD freq
689 ) {
690         ICOM_THIS(PrimaryBufferImpl,iface);
691         TRACE("(%p,%p)\n",This,freq);
692
693         if (freq == NULL)
694                 return DSERR_INVALIDPARAM;
695
696         *freq = This->dsound->wfx.nSamplesPerSec;
697         TRACE("-> %ld\n", *freq);
698
699         return DS_OK;
700 }
701
702 static HRESULT WINAPI PrimaryBufferImpl_SetFX(
703         LPDIRECTSOUNDBUFFER8 iface,DWORD dwEffectsCount,LPDSEFFECTDESC pDSFXDesc,LPDWORD pdwResultCodes
704 ) {
705         ICOM_THIS(PrimaryBufferImpl,iface);
706         DWORD u;
707
708         FIXME("(%p,%lu,%p,%p): stub\n",This,dwEffectsCount,pDSFXDesc,pdwResultCodes);
709
710         if (pdwResultCodes)
711                 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
712
713         return DSERR_CONTROLUNAVAIL;
714 }
715
716 static HRESULT WINAPI PrimaryBufferImpl_AcquireResources(
717         LPDIRECTSOUNDBUFFER8 iface,DWORD dwFlags,DWORD dwEffectsCount,LPDWORD pdwResultCodes
718 ) {
719         ICOM_THIS(PrimaryBufferImpl,iface);
720         DWORD u;
721
722         FIXME("(%p,%08lu,%lu,%p): stub\n",This,dwFlags,dwEffectsCount,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 PrimaryBufferImpl_GetObjectInPath(
731         LPDIRECTSOUNDBUFFER8 iface,REFGUID rguidObject,DWORD dwIndex,REFGUID rguidInterface,LPVOID* ppObject
732 ) {
733         ICOM_THIS(PrimaryBufferImpl,iface);
734
735         FIXME("(%p,%s,%lu,%s,%p): stub\n",This,debugstr_guid(rguidObject),dwIndex,debugstr_guid(rguidInterface),ppObject);
736
737         return DSERR_CONTROLUNAVAIL;
738 }
739
740 static HRESULT WINAPI PrimaryBufferImpl_Initialize(
741         LPDIRECTSOUNDBUFFER8 iface,LPDIRECTSOUND8 dsound,LPDSBUFFERDESC dbsd
742 ) {
743         ICOM_THIS(PrimaryBufferImpl,iface);
744         FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
745         DPRINTF("Re-Init!!!\n");
746         return DSERR_ALREADYINITIALIZED;
747 }
748
749 static HRESULT WINAPI PrimaryBufferImpl_GetCaps(
750         LPDIRECTSOUNDBUFFER8 iface,LPDSBCAPS caps
751 ) {
752         ICOM_THIS(PrimaryBufferImpl,iface);
753         TRACE("(%p)->(%p)\n",This,caps);
754
755         if (caps == NULL)
756                 return DSERR_INVALIDPARAM;
757
758         /* I think we should check this value, not set it. See */
759         /* Inside DirectX, p215. That should apply here, too. */
760         caps->dwSize = sizeof(*caps);
761
762         caps->dwFlags = This->dsbd.dwFlags;
763         if (This->dsound->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
764         else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
765
766         caps->dwBufferBytes = This->dsound->buflen;
767
768         /* This value represents the speed of the "unlock" command.
769            As unlock is quite fast (it does not do anything), I put
770            4096 ko/s = 4 Mo / s */
771         /* FIXME: hwbuf speed */
772         caps->dwUnlockTransferRate = 4096;
773         caps->dwPlayCpuOverhead = 0;
774
775         return DS_OK;
776 }
777
778 static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(
779         LPDIRECTSOUNDBUFFER8 iface,REFIID riid,LPVOID *ppobj
780 ) {
781         ICOM_THIS(PrimaryBufferImpl,iface);
782
783         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
784
785         if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
786                 ERR("app requested IDirectSoundNotify on primary buffer\n");
787                 /* should we support this? */
788                 *ppobj = NULL;
789                 return E_FAIL;
790         }
791
792         if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
793                 ERR("app requested IDirectSound3DBuffer on primary buffer\n");
794                 *ppobj = NULL;
795                 return E_NOINTERFACE;
796         }
797
798         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
799                 if (!This->dsound->listener)
800                         IDirectSound3DListenerImpl_Create(This, &This->dsound->listener);
801                 *ppobj = This->dsound->listener;
802                 if (This->dsound->listener) {
803                         IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
804                         return DS_OK;
805                 }
806                 return E_FAIL;
807         }
808
809         if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
810 #if 0
811                 if (!This->iks)
812                         IKsPropertySetImpl_Create(This, &This->iks);
813                 *ppobj = This->iks;
814                 if (*ppobj) {
815                         IKsPropertySet_AddRef((LPKSPROPERTYSET)*ppobj);
816                         return S_OK;
817                 }
818                 return E_FAIL;
819 #else
820                 FIXME("app requested IKsPropertySet on primary buffer\n");
821                 *ppobj = NULL;
822                 return E_FAIL;
823 #endif
824         }
825
826         FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
827
828         *ppobj = NULL;
829
830         return E_NOINTERFACE;
831 }
832
833 static ICOM_VTABLE(IDirectSoundBuffer8) dspbvt =
834 {
835         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
836         PrimaryBufferImpl_QueryInterface,
837         PrimaryBufferImpl_AddRef,
838         PrimaryBufferImpl_Release,
839         PrimaryBufferImpl_GetCaps,
840         PrimaryBufferImpl_GetCurrentPosition,
841         PrimaryBufferImpl_GetFormat,
842         PrimaryBufferImpl_GetVolume,
843         PrimaryBufferImpl_GetPan,
844         PrimaryBufferImpl_GetFrequency,
845         PrimaryBufferImpl_GetStatus,
846         PrimaryBufferImpl_Initialize,
847         PrimaryBufferImpl_Lock,
848         PrimaryBufferImpl_Play,
849         PrimaryBufferImpl_SetCurrentPosition,
850         PrimaryBufferImpl_SetFormat,
851         PrimaryBufferImpl_SetVolume,
852         PrimaryBufferImpl_SetPan,
853         PrimaryBufferImpl_SetFrequency,
854         PrimaryBufferImpl_Stop,
855         PrimaryBufferImpl_Unlock,
856         PrimaryBufferImpl_Restore,
857         PrimaryBufferImpl_SetFX,
858         PrimaryBufferImpl_AcquireResources,
859         PrimaryBufferImpl_GetObjectInPath
860 };
861
862 HRESULT WINAPI PrimaryBuffer_Create(
863         IDirectSoundImpl *This,
864         PrimaryBufferImpl **pdsb,
865         LPDSBUFFERDESC dsbd)
866 {
867         PrimaryBufferImpl *dsb;
868
869         if (dsbd->lpwfxFormat)
870                 return DSERR_INVALIDPARAM;
871
872         dsb = (PrimaryBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
873         dsb->ref = 1;
874         dsb->dsound = This;
875         ICOM_VTBL(dsb) = &dspbvt;
876
877         memcpy(&dsb->dsbd, dsbd, sizeof(*dsbd));
878
879         TRACE("Created primary buffer at %p\n", dsb);
880
881         if (dsbd->dwFlags & DSBCAPS_CTRL3D) {
882                 /* FIXME: IDirectSound3DListener */
883         }
884
885         IDirectSound8_AddRef((LPDIRECTSOUND8)This);
886
887         *pdsb = dsb;
888         return S_OK;
889 }