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