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