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