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