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