Fix memory leak in error path.
[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 <stdarg.h>
23
24 #define NONAMELESSSTRUCT
25 #define NONAMELESSUNION
26 #include "windef.h"
27 #include "winbase.h"
28 #include "mmsystem.h"
29 #include "winreg.h"
30 #include "winternl.h"
31 #include "mmddk.h"
32 #include "wine/debug.h"
33 #include "dsound.h"
34 #include "dsdriver.h"
35 #include "dsound_private.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
38
39 void DSOUND_RecalcPrimary(IDirectSoundImpl *This)
40 {
41         DWORD nBlockAlign;
42         TRACE("(%p)\n",This);
43
44         nBlockAlign = This->pwfx->nBlockAlign;
45         if (This->hwbuf) {
46                 DWORD fraglen;
47                 /* let fragment size approximate the timer delay */
48                 fraglen = (This->pwfx->nSamplesPerSec * DS_TIME_DEL / 1000) * nBlockAlign;
49                 /* reduce fragment size until an integer number of them fits in the buffer */
50                 /* (FIXME: this may or may not be a good idea) */
51                 while (This->buflen % fraglen) fraglen -= nBlockAlign;
52                 This->fraglen = fraglen;
53                 TRACE("fraglen=%ld\n", This->fraglen);
54         }
55         /* calculate the 10ms write lead */
56         This->writelead = (This->pwfx->nSamplesPerSec / 100) * nBlockAlign;
57 }
58
59 static HRESULT DSOUND_PrimaryOpen(IDirectSoundImpl *This)
60 {
61         HRESULT err = DS_OK;
62         TRACE("(%p)\n",This);
63
64         /* are we using waveOut stuff? */
65         if (!This->driver) {
66                 LPBYTE newbuf;
67                 DWORD buflen;
68                 HRESULT merr = DS_OK;
69                 /* Start in pause mode, to allow buffers to get filled */
70                 waveOutPause(This->hwo);
71                 if (This->state == STATE_PLAYING) This->state = STATE_STARTING;
72                 else if (This->state == STATE_STOPPING) This->state = STATE_STOPPED;
73                 /* use fragments of 10ms (1/100s) each (which should get us within
74                  * the documented write cursor lead of 10-15ms) */
75                 buflen = ((This->pwfx->nSamplesPerSec / 100) * This->pwfx->nBlockAlign) * DS_HEL_FRAGS;
76                 TRACE("desired buflen=%ld, old buffer=%p\n", buflen, This->buffer);
77                 /* reallocate emulated primary buffer */
78
79                 if (This->buffer)
80                         newbuf = HeapReAlloc(GetProcessHeap(),0,This->buffer,buflen);
81                 else
82                         newbuf = HeapAlloc(GetProcessHeap(),0,buflen);
83
84                 if (newbuf == NULL) {
85                         ERR("failed to allocate primary buffer\n");
86                         merr = DSERR_OUTOFMEMORY;
87                         /* but the old buffer might still exist and must be re-prepared */
88                 } else {
89                         This->buffer = newbuf;
90                         This->buflen = buflen;
91                 }
92                 if (This->buffer) {
93                         unsigned c;
94
95                         This->fraglen = This->buflen / DS_HEL_FRAGS;
96
97                         /* prepare fragment headers */
98                         for (c=0; c<DS_HEL_FRAGS; c++) {
99                                 This->pwave[c]->lpData = This->buffer + c*This->fraglen;
100                                 This->pwave[c]->dwBufferLength = This->fraglen;
101                                 This->pwave[c]->dwUser = (DWORD)This;
102                                 This->pwave[c]->dwFlags = 0;
103                                 This->pwave[c]->dwLoops = 0;
104                                 err = mmErr(waveOutPrepareHeader(This->hwo,This->pwave[c],sizeof(WAVEHDR)));
105                                 if (err != DS_OK) {
106                                         while (c--)
107                                                 waveOutUnprepareHeader(This->hwo,This->pwave[c],sizeof(WAVEHDR));
108                                         break;
109                                 }
110                         }
111
112                         This->pwplay = 0;
113                         This->pwwrite = 0;
114                         This->pwqueue = 0;
115                         This->playpos = 0;
116                         This->mixpos = 0;
117                         FillMemory(This->buffer, This->buflen, (This->pwfx->wBitsPerSample == 8) ? 128 : 0);
118                         TRACE("fraglen=%ld\n", This->fraglen);
119                         DSOUND_WaveQueue(This, (DWORD)-1);
120                 }
121                 if ((err == DS_OK) && (merr != DS_OK))
122                         err = merr;
123         } else if (!This->hwbuf) {
124                 err = IDsDriver_CreateSoundBuffer(This->driver,This->pwfx,
125                                                   DSBCAPS_PRIMARYBUFFER,0,
126                                                   &(This->buflen),&(This->buffer),
127                                                   (LPVOID*)&(This->hwbuf));
128                 if (err != DS_OK) {
129                         WARN("IDsDriver_CreateSoundBuffer failed\n");
130                         return err;
131                 }
132
133                 if (dsound->state == STATE_PLAYING) dsound->state = STATE_STARTING;
134                 else if (dsound->state == STATE_STOPPING) dsound->state = STATE_STOPPED;
135         }
136
137         return err;
138 }
139
140
141 static void DSOUND_PrimaryClose(IDirectSoundImpl *This)
142 {
143         TRACE("(%p)\n",This);
144
145         /* are we using waveOut stuff? */
146         if (!This->hwbuf) {
147                 unsigned c;
148
149                 This->pwqueue = (DWORD)-1; /* resetting queues */
150                 waveOutReset(This->hwo);
151                 for (c=0; c<DS_HEL_FRAGS; c++)
152                         waveOutUnprepareHeader(This->hwo, This->pwave[c], sizeof(WAVEHDR));
153                 This->pwqueue = 0;
154         } else {
155                 if (IDsDriverBuffer_Release(This->hwbuf) == 0)
156                         This->hwbuf = 0;
157         }
158 }
159
160 HRESULT DSOUND_PrimaryCreate(IDirectSoundImpl *This)
161 {
162         HRESULT err = DS_OK;
163         TRACE("(%p)\n",This);
164
165         This->buflen = This->pwfx->nAvgBytesPerSec;
166
167         /* FIXME: verify that hardware capabilities (DSCAPS_PRIMARY flags) match */
168
169         if (This->driver) {
170                 err = IDsDriver_CreateSoundBuffer(This->driver,This->pwfx,
171                                                   DSBCAPS_PRIMARYBUFFER,0,
172                                                   &(This->buflen),&(This->buffer),
173                                                   (LPVOID*)&(This->hwbuf));
174                 if (err != DS_OK) {
175                         WARN("IDsDriver_CreateSoundBuffer failed\n");
176                         return err;
177                 }
178         }
179         if (!This->hwbuf) {
180                 /* Allocate memory for HEL buffer headers */
181                 unsigned c;
182                 for (c=0; c<DS_HEL_FRAGS; c++) {
183                         This->pwave[c] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEHDR));
184                         if (!This->pwave[c]) {
185                                 /* Argh, out of memory */
186                                 while (c--) {
187                                         HeapFree(GetProcessHeap(),0,This->pwave[c]);
188                                 }
189                                 WARN("out of memory\n");
190                                 return DSERR_OUTOFMEMORY;
191                         }
192                 }
193         }
194
195         err = DSOUND_PrimaryOpen(This);
196
197         if (err != DS_OK) {
198                 WARN("DSOUND_PrimaryOpen failed\n");
199                 return err;
200         }
201
202         /* calculate fragment size and write lead */
203         DSOUND_RecalcPrimary(This);
204         This->state = STATE_STOPPED;
205         return DS_OK;
206 }
207
208 HRESULT DSOUND_PrimaryDestroy(IDirectSoundImpl *This)
209 {
210         TRACE("(%p)\n",This);
211
212         DSOUND_PrimaryClose(This);
213         if (This->driver) {
214                 if (This->hwbuf) {
215                         if (IDsDriverBuffer_Release(This->hwbuf) == 0)
216                                 This->hwbuf = 0;
217                 }
218         } else {
219                 unsigned c;
220                 for (c=0; c<DS_HEL_FRAGS; c++) {
221                         HeapFree(GetProcessHeap(),0,This->pwave[c]);
222                 }
223         }
224         HeapFree(GetProcessHeap(),0,This->pwfx);
225         This->pwfx=NULL;
226         return DS_OK;
227 }
228
229 HRESULT DSOUND_PrimaryPlay(IDirectSoundImpl *This)
230 {
231         HRESULT err = DS_OK;
232         TRACE("(%p)\n",This);
233
234         if (This->hwbuf) {
235                 err = IDsDriverBuffer_Play(This->hwbuf, 0, 0, DSBPLAY_LOOPING);
236                 if (err != DS_OK)
237                         WARN("IDsDriverBuffer_Play failed\n");
238         } else {
239                 err = mmErr(waveOutRestart(This->hwo));
240                 if (err != DS_OK)
241                         WARN("waveOutRestart failed\n");
242         }
243
244         return err;
245 }
246
247 HRESULT DSOUND_PrimaryStop(IDirectSoundImpl *This)
248 {
249         HRESULT err = DS_OK;
250         TRACE("(%p)\n",This);
251
252         if (This->hwbuf) {
253                 err = IDsDriverBuffer_Stop(This->hwbuf);
254                 if (err == DSERR_BUFFERLOST) {
255                         DWORD flags = CALLBACK_FUNCTION;
256                         if (ds_hw_accel != DS_HW_ACCEL_EMULATION)
257                                 flags |= WAVE_DIRECTSOUND;
258                         /* Wine-only: the driver wants us to reopen the device */
259                         /* FIXME: check for errors */
260                         IDsDriverBuffer_Release(This->hwbuf);
261                         waveOutClose(This->hwo);
262                         This->hwo = 0;
263                         err = mmErr(waveOutOpen(&(This->hwo), This->drvdesc.dnDevNode,
264                                                 This->pwfx, (DWORD)DSOUND_callback, (DWORD)This,
265                                                 flags));
266                         if (err == DS_OK) {
267                                 err = IDsDriver_CreateSoundBuffer(This->driver,This->pwfx,
268                                                                   DSBCAPS_PRIMARYBUFFER,0,
269                                                                   &(This->buflen),&(This->buffer),
270                                                                   (LPVOID)&(This->hwbuf));
271                                 if (err != DS_OK)
272                                         WARN("IDsDriver_CreateSoundBuffer failed\n");
273                         } else {
274                                 WARN("waveOutOpen failed\n");
275                         }
276                 } else if (err != DS_OK) {
277                         WARN("IDsDriverBuffer_Stop failed\n");
278                 }
279         } else {
280                 err = mmErr(waveOutPause(This->hwo));
281                 if (err != DS_OK)
282                         WARN("waveOutPause failed\n");
283         }
284         return err;
285 }
286
287 HRESULT DSOUND_PrimaryGetPosition(IDirectSoundImpl *This, LPDWORD playpos, LPDWORD writepos)
288 {
289         TRACE("(%p,%p,%p)\n",This,playpos,writepos);
290
291         if (This->hwbuf) {
292                 HRESULT err=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);
293                 if (err) {
294                         WARN("IDsDriverBuffer_GetPosition failed\n");
295                         return err;
296                 }
297         } else {
298                 if (playpos) {
299                         MMTIME mtime;
300                         mtime.wType = TIME_BYTES;
301                         waveOutGetPosition(This->hwo, &mtime, sizeof(mtime));
302                         mtime.u.cb = mtime.u.cb % This->buflen;
303                         *playpos = mtime.u.cb;
304                 }
305                 if (writepos) {
306                         /* the writepos should only be used by apps with WRITEPRIMARY priority,
307                          * in which case our software mixer is disabled anyway */
308                         *writepos = (This->pwplay + ds_hel_margin) * This->fraglen;
309                         while (*writepos >= This->buflen)
310                                 *writepos -= This->buflen;
311                 }
312         }
313         TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
314         return DS_OK;
315 }
316
317 /*******************************************************************************
318  *              PrimaryBuffer
319  */
320 /* This sets this format for the <em>Primary Buffer Only</em> */
321 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
322 static HRESULT WINAPI PrimaryBufferImpl_SetFormat(
323         LPDIRECTSOUNDBUFFER8 iface,LPCWAVEFORMATEX wfex
324 ) {
325         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
326         IDirectSoundImpl* dsound = This->dsound;
327         HRESULT err = DS_OK;
328         int i, alloc_size, cp_size;
329         DWORD nSamplesPerSec;
330         TRACE("(%p,%p)\n",This,wfex);
331
332         if (This->dsound->priolevel == DSSCL_NORMAL) {
333                 WARN("failed priority check!\n");
334                 return DSERR_PRIOLEVELNEEDED;
335         }
336
337         /* Let's be pedantic! */
338         if (wfex == NULL) {
339                 WARN("invalid parameter: wfex==NULL!\n");
340                 return DSERR_INVALIDPARAM;
341         }
342         TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
343               "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
344               wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
345               wfex->nAvgBytesPerSec, wfex->nBlockAlign,
346               wfex->wBitsPerSample, wfex->cbSize);
347
348         /* **** */
349         RtlAcquireResourceExclusive(&(dsound->buffer_list_lock), TRUE);
350         EnterCriticalSection(&(dsound->mixlock));
351
352         if (wfex->wFormatTag == WAVE_FORMAT_PCM) {
353             alloc_size = sizeof(WAVEFORMATEX);
354             cp_size = sizeof(PCMWAVEFORMAT);
355         } else
356             alloc_size = cp_size = sizeof(WAVEFORMATEX) + wfex->cbSize;
357
358         dsound->pwfx = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,dsound->pwfx,alloc_size);
359
360         nSamplesPerSec = dsound->pwfx->nSamplesPerSec;
361
362         CopyMemory(dsound->pwfx, wfex, cp_size);
363
364         if (dsound->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) {
365                 DWORD flags = CALLBACK_FUNCTION;
366                 if (ds_hw_accel != DS_HW_ACCEL_EMULATION)
367                         flags |= WAVE_DIRECTSOUND;
368                 /* FIXME: check for errors */
369                 DSOUND_PrimaryClose(dsound);
370                 waveOutClose(dsound->hwo);
371                 dsound->hwo = 0;
372                 err = mmErr(waveOutOpen(&(dsound->hwo), dsound->drvdesc.dnDevNode,
373                                         dsound->pwfx, (DWORD)DSOUND_callback, (DWORD)dsound,
374                                         flags));
375                 if (err == DS_OK) {
376                     err = DSOUND_PrimaryOpen(dsound);
377                     if (err != DS_OK) {
378                         WARN("DSOUND_PrimaryOpen failed\n");
379                         goto done;
380                     }
381                 } else {
382                         WARN("waveOutOpen failed\n");
383                         goto done;
384                 }
385         } else if (dsound->hwbuf) {
386                 err = IDsDriverBuffer_SetFormat(dsound->hwbuf, dsound->pwfx);
387                 if (err == DSERR_BUFFERLOST) {
388                         /* Wine-only: the driver wants us to recreate the HW buffer */
389                         IDsDriverBuffer_Release(dsound->hwbuf);
390                         err = IDsDriver_CreateSoundBuffer(dsound->driver,dsound->pwfx,
391                                                           DSBCAPS_PRIMARYBUFFER,0,
392                                                           &(dsound->buflen),&(dsound->buffer),
393                                                           (LPVOID)&(dsound->hwbuf));
394                         if (err != DS_OK) {
395                                 WARN("IDsDriver_CreateSoundBuffer failed\n");
396                                 goto done;
397                         }
398                         if (dsound->state == STATE_PLAYING) dsound->state = STATE_STARTING;
399                         else if (dsound->state == STATE_STOPPING) dsound->state = STATE_STOPPED;
400                 } else {
401                         WARN("IDsDriverBuffer_SetFormat failed\n");
402                         goto done;
403                 }
404                 /* FIXME: should we set err back to DS_OK in all cases ? */
405         }
406         DSOUND_RecalcPrimary(dsound);
407
408         if (nSamplesPerSec != dsound->pwfx->nSamplesPerSec) {
409                 IDirectSoundBufferImpl** dsb = dsound->buffers;
410                 for (i = 0; i < dsound->nrofbuffers; i++, dsb++) {
411                         /* **** */
412                         EnterCriticalSection(&((*dsb)->lock));
413
414                         (*dsb)->freqAdjust = ((*dsb)->freq << DSOUND_FREQSHIFT) /
415                                 wfex->nSamplesPerSec;
416
417                         LeaveCriticalSection(&((*dsb)->lock));
418                         /* **** */
419                 }
420         }
421
422 done:
423         LeaveCriticalSection(&(dsound->mixlock));
424         RtlReleaseResource(&(dsound->buffer_list_lock));
425         /* **** */
426
427         return err;
428 }
429
430 static HRESULT WINAPI PrimaryBufferImpl_SetVolume(
431         LPDIRECTSOUNDBUFFER8 iface,LONG vol
432 ) {
433         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
434         IDirectSoundImpl* dsound = This->dsound;
435         DWORD ampfactors;
436         DSVOLUMEPAN volpan;
437         HRESULT hres = DS_OK;
438
439         TRACE("(%p,%ld)\n",This,vol);
440
441         if (!(This->dsound->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
442                 WARN("control unavailable\n");
443                 return DSERR_CONTROLUNAVAIL;
444         }
445
446         if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) {
447                 WARN("invalid parameter: vol = %ld\n", vol);
448                 return DSERR_INVALIDPARAM;
449         }
450
451         /* **** */
452         EnterCriticalSection(&(dsound->mixlock));
453
454         waveOutGetVolume(dsound->hwo, &ampfactors);
455         volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
456         volpan.dwTotalRightAmpFactor=ampfactors >> 16;
457         DSOUND_AmpFactorToVolPan(&volpan);
458         if (vol != volpan.lVolume) {
459             volpan.lVolume=vol;
460             DSOUND_RecalcVolPan(&volpan);
461             if (dsound->hwbuf) {
462                 hres = IDsDriverBuffer_SetVolumePan(dsound->hwbuf, &volpan);
463                 if (hres != DS_OK)
464                     WARN("IDsDriverBuffer_SetVolumePan failed\n");
465             } else {
466                 ampfactors = (volpan.dwTotalLeftAmpFactor & 0xffff) | (volpan.dwTotalRightAmpFactor << 16);
467                 waveOutSetVolume(dsound->hwo, ampfactors);
468             }
469         }
470
471         LeaveCriticalSection(&(dsound->mixlock));
472         /* **** */
473
474         return hres;
475 }
476
477 static HRESULT WINAPI PrimaryBufferImpl_GetVolume(
478         LPDIRECTSOUNDBUFFER8 iface,LPLONG vol
479 ) {
480         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
481         DWORD ampfactors;
482         DSVOLUMEPAN volpan;
483         TRACE("(%p,%p)\n",This,vol);
484
485         if (!(This->dsound->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
486                 WARN("control unavailable\n");
487                 return DSERR_CONTROLUNAVAIL;
488         }
489
490         if (vol == NULL) {
491                 WARN("invalid parameter: vol = NULL\n");
492                 return DSERR_INVALIDPARAM;
493         }
494
495         waveOutGetVolume(dsound->hwo, &ampfactors);
496         volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
497         volpan.dwTotalRightAmpFactor=ampfactors >> 16;
498         DSOUND_AmpFactorToVolPan(&volpan);
499         *vol = volpan.lVolume;
500         return DS_OK;
501 }
502
503 static HRESULT WINAPI PrimaryBufferImpl_SetFrequency(
504         LPDIRECTSOUNDBUFFER8 iface,DWORD freq
505 ) {
506         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
507
508         TRACE("(%p,%ld)\n",This,freq);
509
510         /* You cannot set the frequency of the primary buffer */
511         WARN("control unavailable\n");
512         return DSERR_CONTROLUNAVAIL;
513 }
514
515 static HRESULT WINAPI PrimaryBufferImpl_Play(
516         LPDIRECTSOUNDBUFFER8 iface,DWORD reserved1,DWORD reserved2,DWORD flags
517 ) {
518         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
519         IDirectSoundImpl* dsound = This->dsound;
520
521         TRACE("(%p,%08lx,%08lx,%08lx)\n",
522                 This,reserved1,reserved2,flags
523         );
524
525         if (!(flags & DSBPLAY_LOOPING)) {
526                 WARN("invalid parameter: flags = %08lx\n", flags);
527                 return DSERR_INVALIDPARAM;
528         }
529
530         /* **** */
531         EnterCriticalSection(&(dsound->mixlock));
532
533         if (dsound->state == STATE_STOPPED)
534                 dsound->state = STATE_STARTING;
535         else if (dsound->state == STATE_STOPPING)
536                 dsound->state = STATE_PLAYING;
537
538         LeaveCriticalSection(&(dsound->mixlock));
539         /* **** */
540
541         return DS_OK;
542 }
543
544 static HRESULT WINAPI PrimaryBufferImpl_Stop(LPDIRECTSOUNDBUFFER8 iface)
545 {
546         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
547         IDirectSoundImpl* dsound = This->dsound;
548
549         TRACE("(%p)\n",This);
550
551         /* **** */
552         EnterCriticalSection(&(dsound->mixlock));
553
554         if (dsound->state == STATE_PLAYING)
555                 dsound->state = STATE_STOPPING;
556         else if (dsound->state == STATE_STARTING)
557                 dsound->state = STATE_STOPPED;
558
559         LeaveCriticalSection(&(dsound->mixlock));
560         /* **** */
561
562         return DS_OK;
563 }
564
565 static ULONG WINAPI PrimaryBufferImpl_AddRef(LPDIRECTSOUNDBUFFER8 iface)
566 {
567     PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
568     ULONG ref = InterlockedIncrement(&(This->ref));
569     TRACE("(%p) ref was %ld\n", This, ref - 1);
570     return ref;
571 }
572
573 static ULONG WINAPI PrimaryBufferImpl_Release(LPDIRECTSOUNDBUFFER8 iface)
574 {
575     PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
576     DWORD ref = InterlockedDecrement(&(This->ref));
577     TRACE("(%p) ref was %ld\n", This, ref + 1);
578
579     if (!ref) {
580         This->dsound->primary = NULL;
581         HeapFree(GetProcessHeap(), 0, This);
582         TRACE("(%p) released\n", This);
583     }
584     return ref;
585 }
586
587 static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(
588         LPDIRECTSOUNDBUFFER8 iface,LPDWORD playpos,LPDWORD writepos
589 ) {
590         HRESULT hres;
591         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
592         IDirectSoundImpl* dsound = This->dsound;
593
594         TRACE("(%p,%p,%p)\n",This,playpos,writepos);
595         hres = DSOUND_PrimaryGetPosition(dsound, playpos, writepos);
596         if (hres != DS_OK) {
597                 WARN("DSOUND_PrimaryGetPosition failed\n");
598                 return hres;
599         }
600         if (writepos) {
601                 if (dsound->state != STATE_STOPPED)
602                         /* apply the documented 10ms lead to writepos */
603                         *writepos += dsound->writelead;
604                 while (*writepos >= dsound->buflen) *writepos -= dsound->buflen;
605         }
606         TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
607         return DS_OK;
608 }
609
610 static HRESULT WINAPI PrimaryBufferImpl_GetStatus(
611         LPDIRECTSOUNDBUFFER8 iface,LPDWORD status
612 ) {
613         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
614         TRACE("(%p,%p), thread is %04lx\n",This,status,GetCurrentThreadId());
615
616         if (status == NULL) {
617                 WARN("invalid parameter: status == NULL\n");
618                 return DSERR_INVALIDPARAM;
619         }
620
621         *status = 0;
622         if ((This->dsound->state == STATE_STARTING) ||
623             (This->dsound->state == STATE_PLAYING))
624                 *status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING;
625
626         TRACE("status=%lx\n", *status);
627         return DS_OK;
628 }
629
630
631 static HRESULT WINAPI PrimaryBufferImpl_GetFormat(
632     LPDIRECTSOUNDBUFFER8 iface,
633     LPWAVEFORMATEX lpwf,
634     DWORD wfsize,
635     LPDWORD wfwritten)
636 {
637     DWORD size;
638     PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
639     TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
640
641     size = sizeof(WAVEFORMATEX) + This->dsound->pwfx->cbSize;
642
643     if (lpwf) { /* NULL is valid */
644         if (wfsize >= size) {
645             CopyMemory(lpwf,This->dsound->pwfx,size);
646             if (wfwritten)
647                 *wfwritten = size;
648         } else {
649             WARN("invalid parameter: wfsize to small\n");
650             if (wfwritten)
651                 *wfwritten = 0;
652             return DSERR_INVALIDPARAM;
653         }
654     } else {
655         if (wfwritten)
656             *wfwritten = sizeof(WAVEFORMATEX) + This->dsound->pwfx->cbSize;
657         else {
658             WARN("invalid parameter: wfwritten == NULL\n");
659             return DSERR_INVALIDPARAM;
660         }
661     }
662
663     return DS_OK;
664 }
665
666 static HRESULT WINAPI PrimaryBufferImpl_Lock(
667         LPDIRECTSOUNDBUFFER8 iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
668 ) {
669         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
670         IDirectSoundImpl* dsound = This->dsound;
671
672         TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx) at %ld\n",
673                 This,
674                 writecursor,
675                 writebytes,
676                 lplpaudioptr1,
677                 audiobytes1,
678                 lplpaudioptr2,
679                 audiobytes2,
680                 flags,
681                 GetTickCount()
682         );
683
684         if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
685                 WARN("failed priority check!\n");
686                 return DSERR_PRIOLEVELNEEDED;
687         }
688
689         if (flags & DSBLOCK_FROMWRITECURSOR) {
690                 DWORD writepos;
691                 HRESULT hres;
692                 /* GetCurrentPosition does too much magic to duplicate here */
693                 hres = IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writepos);
694                 if (hres != DS_OK) {
695                         WARN("IDirectSoundBuffer_GetCurrentPosition failed\n");
696                         return hres;
697                 }
698                 writecursor += writepos;
699         }
700         while (writecursor >= dsound->buflen)
701                 writecursor -= dsound->buflen;
702         if (flags & DSBLOCK_ENTIREBUFFER)
703                 writebytes = dsound->buflen;
704         if (writebytes > dsound->buflen)
705                 writebytes = dsound->buflen;
706
707         if (!(dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && dsound->hwbuf) {
708                 HRESULT hres;
709                 hres = IDsDriverBuffer_Lock(dsound->hwbuf,
710                                             lplpaudioptr1, audiobytes1,
711                                             lplpaudioptr2, audiobytes2,
712                                             writecursor, writebytes,
713                                             0);
714                 if (hres != DS_OK) {
715                         WARN("IDsDriverBuffer_Lock failed\n");
716                         return hres;
717                 }
718         } else {
719                 if (writecursor+writebytes <= dsound->buflen) {
720                         *(LPBYTE*)lplpaudioptr1 = dsound->buffer+writecursor;
721                         *audiobytes1 = writebytes;
722                         if (lplpaudioptr2)
723                                 *(LPBYTE*)lplpaudioptr2 = NULL;
724                         if (audiobytes2)
725                                 *audiobytes2 = 0;
726                         TRACE("->%ld.0\n",writebytes);
727                 } else {
728                         *(LPBYTE*)lplpaudioptr1 = dsound->buffer+writecursor;
729                         *audiobytes1 = dsound->buflen-writecursor;
730                         if (lplpaudioptr2)
731                                 *(LPBYTE*)lplpaudioptr2 = dsound->buffer;
732                         if (audiobytes2)
733                                 *audiobytes2 = writebytes-(dsound->buflen-writecursor);
734                         TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
735                 }
736         }
737         return DS_OK;
738 }
739
740 static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition(
741         LPDIRECTSOUNDBUFFER8 iface,DWORD newpos
742 ) {
743         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
744         TRACE("(%p,%ld)\n",This,newpos);
745
746         /* You cannot set the position of the primary buffer */
747         WARN("invalid call\n");
748         return DSERR_INVALIDCALL;
749 }
750
751 static HRESULT WINAPI PrimaryBufferImpl_SetPan(
752         LPDIRECTSOUNDBUFFER8 iface,LONG pan
753 ) {
754         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
755         IDirectSoundImpl* dsound = This->dsound;
756         DWORD ampfactors;
757         DSVOLUMEPAN volpan;
758         HRESULT hres = DS_OK;
759
760         TRACE("(%p,%ld)\n",This,pan);
761
762         if (!(This->dsound->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
763                 WARN("control unavailable\n");
764                 return DSERR_CONTROLUNAVAIL;
765         }
766
767         if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) {
768                 WARN("invalid parameter: pan = %ld\n", pan);
769                 return DSERR_INVALIDPARAM;
770         }
771
772         /* **** */
773         EnterCriticalSection(&(dsound->mixlock));
774
775         waveOutGetVolume(dsound->hwo, &ampfactors);
776         volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
777         volpan.dwTotalRightAmpFactor=ampfactors >> 16;
778         DSOUND_AmpFactorToVolPan(&volpan);
779         if (pan != volpan.lPan) {
780             volpan.lPan=pan;
781             DSOUND_RecalcVolPan(&volpan);
782             if (dsound->hwbuf) {
783                 hres = IDsDriverBuffer_SetVolumePan(dsound->hwbuf, &volpan);
784                 if (hres != DS_OK)
785                     WARN("IDsDriverBuffer_SetVolumePan failed\n");
786             } else {
787                 ampfactors = (volpan.dwTotalLeftAmpFactor & 0xffff) | (volpan.dwTotalRightAmpFactor << 16);
788                 waveOutSetVolume(dsound->hwo, ampfactors);
789             }
790         }
791
792         LeaveCriticalSection(&(dsound->mixlock));
793         /* **** */
794
795         return hres;
796 }
797
798 static HRESULT WINAPI PrimaryBufferImpl_GetPan(
799         LPDIRECTSOUNDBUFFER8 iface,LPLONG pan
800 ) {
801         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
802         DWORD ampfactors;
803         DSVOLUMEPAN volpan;
804         TRACE("(%p,%p)\n",This,pan);
805
806         if (!(This->dsound->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
807                 WARN("control unavailable\n");
808                 return DSERR_CONTROLUNAVAIL;
809         }
810
811         if (pan == NULL) {
812                 WARN("invalid parameter: pan == NULL\n");
813                 return DSERR_INVALIDPARAM;
814         }
815
816         waveOutGetVolume(dsound->hwo, &ampfactors);
817         volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
818         volpan.dwTotalRightAmpFactor=ampfactors >> 16;
819         DSOUND_AmpFactorToVolPan(&volpan);
820         *pan = volpan.lPan;
821         return DS_OK;
822 }
823
824 static HRESULT WINAPI PrimaryBufferImpl_Unlock(
825         LPDIRECTSOUNDBUFFER8 iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
826 ) {
827         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
828         IDirectSoundImpl* dsound = This->dsound;
829
830         TRACE("(%p,%p,%ld,%p,%ld)\n", This,p1,x1,p2,x2);
831
832         if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
833                 WARN("failed priority check!\n");
834                 return DSERR_PRIOLEVELNEEDED;
835         }
836
837         if (!(dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && dsound->hwbuf) {
838                 HRESULT hres;
839                 
840                 hres = IDsDriverBuffer_Unlock(dsound->hwbuf, p1, x1, p2, x2);
841                 if (hres != DS_OK) {
842                         WARN("IDsDriverBuffer_Unlock failed\n");
843                         return hres;
844                 }
845         }
846
847         return DS_OK;
848 }
849
850 static HRESULT WINAPI PrimaryBufferImpl_Restore(
851         LPDIRECTSOUNDBUFFER8 iface
852 ) {
853         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
854         FIXME("(%p):stub\n",This);
855         return DS_OK;
856 }
857
858 static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(
859         LPDIRECTSOUNDBUFFER8 iface,LPDWORD freq
860 ) {
861         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
862         TRACE("(%p,%p)\n",This,freq);
863
864         if (freq == NULL) {
865                 WARN("invalid parameter: freq == NULL\n");
866                 return DSERR_INVALIDPARAM;
867         }
868
869         if (!(This->dsound->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
870                 WARN("control unavailable\n");
871                 return DSERR_CONTROLUNAVAIL;
872         }
873
874         *freq = This->dsound->pwfx->nSamplesPerSec;
875         TRACE("-> %ld\n", *freq);
876
877         return DS_OK;
878 }
879
880 static HRESULT WINAPI PrimaryBufferImpl_SetFX(
881         LPDIRECTSOUNDBUFFER8 iface,DWORD dwEffectsCount,LPDSEFFECTDESC pDSFXDesc,LPDWORD pdwResultCodes
882 ) {
883         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
884         DWORD u;
885
886         FIXME("(%p,%lu,%p,%p): stub\n",This,dwEffectsCount,pDSFXDesc,pdwResultCodes);
887
888         if (pdwResultCodes)
889                 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
890
891         WARN("control unavailable\n");
892         return DSERR_CONTROLUNAVAIL;
893 }
894
895 static HRESULT WINAPI PrimaryBufferImpl_AcquireResources(
896         LPDIRECTSOUNDBUFFER8 iface,DWORD dwFlags,DWORD dwEffectsCount,LPDWORD pdwResultCodes
897 ) {
898         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
899         DWORD u;
900
901         FIXME("(%p,%08lu,%lu,%p): stub\n",This,dwFlags,dwEffectsCount,pdwResultCodes);
902
903         if (pdwResultCodes)
904                 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
905
906         WARN("control unavailable\n");
907         return DSERR_CONTROLUNAVAIL;
908 }
909
910 static HRESULT WINAPI PrimaryBufferImpl_GetObjectInPath(
911         LPDIRECTSOUNDBUFFER8 iface,REFGUID rguidObject,DWORD dwIndex,REFGUID rguidInterface,LPVOID* ppObject
912 ) {
913         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
914
915         FIXME("(%p,%s,%lu,%s,%p): stub\n",This,debugstr_guid(rguidObject),dwIndex,debugstr_guid(rguidInterface),ppObject);
916
917         WARN("control unavailable\n");
918         return DSERR_CONTROLUNAVAIL;
919 }
920
921 static HRESULT WINAPI PrimaryBufferImpl_Initialize(
922         LPDIRECTSOUNDBUFFER8 iface,LPDIRECTSOUND dsound,LPCDSBUFFERDESC dbsd
923 ) {
924         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
925         FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
926         DPRINTF("Re-Init!!!\n");
927         WARN("already initialized\n");
928         return DSERR_ALREADYINITIALIZED;
929 }
930
931 static HRESULT WINAPI PrimaryBufferImpl_GetCaps(
932         LPDIRECTSOUNDBUFFER8 iface,LPDSBCAPS caps
933 ) {
934         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
935         TRACE("(%p)->(%p)\n",This,caps);
936
937         if (caps == NULL) {
938                 WARN("invalid parameter: caps == NULL\n");
939                 return DSERR_INVALIDPARAM;
940         }
941
942         if (caps->dwSize < sizeof(*caps)) {
943                 WARN("invalid parameter: caps->dwSize = %ld: < %d\n", caps->dwSize, sizeof(*caps));
944                 return DSERR_INVALIDPARAM;
945         }
946
947         caps->dwFlags = This->dsound->dsbd.dwFlags;
948         if (This->dsound->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
949         else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
950
951         caps->dwBufferBytes = This->dsound->buflen;
952
953         /* This value represents the speed of the "unlock" command.
954            As unlock is quite fast (it does not do anything), I put
955            4096 ko/s = 4 Mo / s */
956         /* FIXME: hwbuf speed */
957         caps->dwUnlockTransferRate = 4096;
958         caps->dwPlayCpuOverhead = 0;
959
960         return DS_OK;
961 }
962
963 static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(
964         LPDIRECTSOUNDBUFFER8 iface,REFIID riid,LPVOID *ppobj
965 ) {
966         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
967         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
968
969         if (ppobj == NULL) {
970                 WARN("invalid parameter\n");
971                 return E_INVALIDARG;
972         }
973
974         *ppobj = NULL;  /* assume failure */
975
976         if ( IsEqualGUID(riid, &IID_IUnknown) ||
977              IsEqualGUID(riid, &IID_IDirectSoundBuffer) ) {
978                 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)This);
979                 *ppobj = This;
980                 return S_OK;
981         }
982
983         /* DirectSoundBuffer and DirectSoundBuffer8 are different and */
984         /* a primary buffer can't have a DirectSoundBuffer8 interface */
985         if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) {
986                 WARN("app requested DirectSoundBuffer8 on primary buffer\n");
987                 return E_NOINTERFACE;
988         }
989
990         if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
991                 ERR("app requested IDirectSoundNotify on primary buffer\n");
992                 /* FIXME: should we support this? */
993                 return E_NOINTERFACE;
994         }
995
996         if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
997                 ERR("app requested IDirectSound3DBuffer on primary buffer\n");
998                 return E_NOINTERFACE;
999         }
1000
1001         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1002                 if (!This->dsound->listener)
1003                         IDirectSound3DListenerImpl_Create(This, &This->dsound->listener);
1004                 if (This->dsound->listener) {
1005                         *ppobj = This->dsound->listener;
1006                         IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
1007                         return S_OK;
1008                 }
1009
1010                 WARN("IID_IDirectSound3DListener failed\n");
1011                 return E_NOINTERFACE;
1012         }
1013
1014         if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
1015                 FIXME("app requested IKsPropertySet on primary buffer\n");
1016                 return E_NOINTERFACE;
1017         }
1018
1019         FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
1020         return E_NOINTERFACE;
1021 }
1022
1023 static IDirectSoundBuffer8Vtbl dspbvt =
1024 {
1025         PrimaryBufferImpl_QueryInterface,
1026         PrimaryBufferImpl_AddRef,
1027         PrimaryBufferImpl_Release,
1028         PrimaryBufferImpl_GetCaps,
1029         PrimaryBufferImpl_GetCurrentPosition,
1030         PrimaryBufferImpl_GetFormat,
1031         PrimaryBufferImpl_GetVolume,
1032         PrimaryBufferImpl_GetPan,
1033         PrimaryBufferImpl_GetFrequency,
1034         PrimaryBufferImpl_GetStatus,
1035         PrimaryBufferImpl_Initialize,
1036         PrimaryBufferImpl_Lock,
1037         PrimaryBufferImpl_Play,
1038         PrimaryBufferImpl_SetCurrentPosition,
1039         PrimaryBufferImpl_SetFormat,
1040         PrimaryBufferImpl_SetVolume,
1041         PrimaryBufferImpl_SetPan,
1042         PrimaryBufferImpl_SetFrequency,
1043         PrimaryBufferImpl_Stop,
1044         PrimaryBufferImpl_Unlock,
1045         PrimaryBufferImpl_Restore,
1046         PrimaryBufferImpl_SetFX,
1047         PrimaryBufferImpl_AcquireResources,
1048         PrimaryBufferImpl_GetObjectInPath
1049 };
1050
1051 HRESULT WINAPI PrimaryBufferImpl_Create(
1052         IDirectSoundImpl *ds,
1053         PrimaryBufferImpl **pdsb,
1054         LPCDSBUFFERDESC dsbd)
1055 {
1056         PrimaryBufferImpl *dsb;
1057
1058         TRACE("%p,%p,%p)\n",ds,pdsb,dsbd);
1059
1060         if (dsbd->lpwfxFormat) {
1061                 WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n");
1062                 *pdsb = NULL;
1063                 return DSERR_INVALIDPARAM;
1064         }
1065
1066         dsb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1067
1068         if (dsb == NULL) {
1069                 WARN("out of memory\n");
1070                 *pdsb = NULL;
1071                 return DSERR_OUTOFMEMORY;
1072         }
1073
1074         dsb->ref = 0;
1075         dsb->dsound = ds;
1076         dsb->lpVtbl = &dspbvt;
1077
1078         CopyMemory(&ds->dsbd, dsbd, sizeof(*dsbd));
1079
1080         TRACE("Created primary buffer at %p\n", dsb);
1081         TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
1082                 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1083                 ds->pwfx->wFormatTag, ds->pwfx->nChannels, ds->pwfx->nSamplesPerSec,
1084                 ds->pwfx->nAvgBytesPerSec, ds->pwfx->nBlockAlign,
1085                 ds->pwfx->wBitsPerSample, ds->pwfx->cbSize);
1086
1087         *pdsb = dsb;
1088         return S_OK;
1089 }