secur32: Update ntlm_auth version detection to detect new samba4 version numbers.
[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 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         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
669         TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx) at %ld\n",
670                 iface,
671                 writecursor,
672                 writebytes,
673                 lplpaudioptr1,
674                 audiobytes1,
675                 lplpaudioptr2,
676                 audiobytes2,
677                 flags,
678                 GetTickCount()
679         );
680
681         if (device->priolevel != DSSCL_WRITEPRIMARY) {
682                 WARN("failed priority check!\n");
683                 return DSERR_PRIOLEVELNEEDED;
684         }
685
686         if (flags & DSBLOCK_FROMWRITECURSOR) {
687                 DWORD writepos;
688                 HRESULT hres;
689                 /* GetCurrentPosition does too much magic to duplicate here */
690                 hres = IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writepos);
691                 if (hres != DS_OK) {
692                         WARN("IDirectSoundBuffer_GetCurrentPosition failed\n");
693                         return hres;
694                 }
695                 writecursor += writepos;
696         }
697         while (writecursor >= device->buflen)
698                 writecursor -= device->buflen;
699         if (flags & DSBLOCK_ENTIREBUFFER)
700                 writebytes = device->buflen;
701         if (writebytes > device->buflen)
702                 writebytes = device->buflen;
703
704         if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
705                 HRESULT hres;
706                 hres = IDsDriverBuffer_Lock(device->hwbuf,
707                                             lplpaudioptr1, audiobytes1,
708                                             lplpaudioptr2, audiobytes2,
709                                             writecursor, writebytes,
710                                             0);
711                 if (hres != DS_OK) {
712                         WARN("IDsDriverBuffer_Lock failed\n");
713                         return hres;
714                 }
715         } else {
716                 if (writecursor+writebytes <= device->buflen) {
717                         *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
718                         *audiobytes1 = writebytes;
719                         if (lplpaudioptr2)
720                                 *(LPBYTE*)lplpaudioptr2 = NULL;
721                         if (audiobytes2)
722                                 *audiobytes2 = 0;
723                         TRACE("->%ld.0\n",writebytes);
724                 } else {
725                         *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
726                         *audiobytes1 = device->buflen-writecursor;
727                         if (lplpaudioptr2)
728                                 *(LPBYTE*)lplpaudioptr2 = device->buffer;
729                         if (audiobytes2)
730                                 *audiobytes2 = writebytes-(device->buflen-writecursor);
731                         TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
732                 }
733         }
734         return DS_OK;
735 }
736
737 static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition(
738         LPDIRECTSOUNDBUFFER8 iface,DWORD newpos
739 ) {
740         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
741         TRACE("(%p,%ld)\n",This,newpos);
742
743         /* You cannot set the position of the primary buffer */
744         WARN("invalid call\n");
745         return DSERR_INVALIDCALL;
746 }
747
748 static HRESULT WINAPI PrimaryBufferImpl_SetPan(
749         LPDIRECTSOUNDBUFFER8 iface,LONG pan
750 ) {
751         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
752         DWORD ampfactors;
753         DSVOLUMEPAN volpan;
754         HRESULT hres = DS_OK;
755         TRACE("(%p,%ld)\n", iface, pan);
756
757         if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
758                 WARN("control unavailable\n");
759                 return DSERR_CONTROLUNAVAIL;
760         }
761
762         if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) {
763                 WARN("invalid parameter: pan = %ld\n", pan);
764                 return DSERR_INVALIDPARAM;
765         }
766
767         /* **** */
768         EnterCriticalSection(&(device->mixlock));
769
770         waveOutGetVolume(device->hwo, &ampfactors);
771         volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
772         volpan.dwTotalRightAmpFactor=ampfactors >> 16;
773         DSOUND_AmpFactorToVolPan(&volpan);
774         if (pan != volpan.lPan) {
775             volpan.lPan=pan;
776             DSOUND_RecalcVolPan(&volpan);
777             if (device->hwbuf) {
778                 hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &volpan);
779                 if (hres != DS_OK)
780                     WARN("IDsDriverBuffer_SetVolumePan failed\n");
781             } else {
782                 ampfactors = (volpan.dwTotalLeftAmpFactor & 0xffff) | (volpan.dwTotalRightAmpFactor << 16);
783                 waveOutSetVolume(device->hwo, ampfactors);
784             }
785         }
786
787         LeaveCriticalSection(&(device->mixlock));
788         /* **** */
789
790         return hres;
791 }
792
793 static HRESULT WINAPI PrimaryBufferImpl_GetPan(
794         LPDIRECTSOUNDBUFFER8 iface,LPLONG pan
795 ) {
796         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
797         DWORD ampfactors;
798         DSVOLUMEPAN volpan;
799         TRACE("(%p,%p)\n", iface, pan);
800
801         if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
802                 WARN("control unavailable\n");
803                 return DSERR_CONTROLUNAVAIL;
804         }
805
806         if (pan == NULL) {
807                 WARN("invalid parameter: pan == NULL\n");
808                 return DSERR_INVALIDPARAM;
809         }
810
811         waveOutGetVolume(device->hwo, &ampfactors);
812         volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
813         volpan.dwTotalRightAmpFactor=ampfactors >> 16;
814         DSOUND_AmpFactorToVolPan(&volpan);
815         *pan = volpan.lPan;
816         return DS_OK;
817 }
818
819 static HRESULT WINAPI PrimaryBufferImpl_Unlock(
820         LPDIRECTSOUNDBUFFER8 iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
821 ) {
822         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
823         TRACE("(%p,%p,%ld,%p,%ld)\n", iface, p1, x1, p2, x2);
824
825         if (device->priolevel != DSSCL_WRITEPRIMARY) {
826                 WARN("failed priority check!\n");
827                 return DSERR_PRIOLEVELNEEDED;
828         }
829
830         if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
831                 HRESULT hres;
832                 
833                 hres = IDsDriverBuffer_Unlock(device->hwbuf, p1, x1, p2, x2);
834                 if (hres != DS_OK) {
835                         WARN("IDsDriverBuffer_Unlock failed\n");
836                         return hres;
837                 }
838         }
839
840         return DS_OK;
841 }
842
843 static HRESULT WINAPI PrimaryBufferImpl_Restore(
844         LPDIRECTSOUNDBUFFER8 iface
845 ) {
846         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
847         FIXME("(%p):stub\n",This);
848         return DS_OK;
849 }
850
851 static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(
852         LPDIRECTSOUNDBUFFER8 iface,LPDWORD freq
853 ) {
854         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
855         TRACE("(%p,%p)\n", iface, freq);
856
857         if (freq == NULL) {
858                 WARN("invalid parameter: freq == NULL\n");
859                 return DSERR_INVALIDPARAM;
860         }
861
862         if (!(device->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
863                 WARN("control unavailable\n");
864                 return DSERR_CONTROLUNAVAIL;
865         }
866
867         *freq = device->pwfx->nSamplesPerSec;
868         TRACE("-> %ld\n", *freq);
869
870         return DS_OK;
871 }
872
873 static HRESULT WINAPI PrimaryBufferImpl_SetFX(
874         LPDIRECTSOUNDBUFFER8 iface,DWORD dwEffectsCount,LPDSEFFECTDESC pDSFXDesc,LPDWORD pdwResultCodes
875 ) {
876         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
877         DWORD u;
878         FIXME("(%p,%lu,%p,%p): stub\n",This,dwEffectsCount,pDSFXDesc,pdwResultCodes);
879
880         if (pdwResultCodes)
881                 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
882
883         WARN("control unavailable\n");
884         return DSERR_CONTROLUNAVAIL;
885 }
886
887 static HRESULT WINAPI PrimaryBufferImpl_AcquireResources(
888         LPDIRECTSOUNDBUFFER8 iface,DWORD dwFlags,DWORD dwEffectsCount,LPDWORD pdwResultCodes
889 ) {
890         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
891         DWORD u;
892         FIXME("(%p,%08lu,%lu,%p): stub\n",This,dwFlags,dwEffectsCount,pdwResultCodes);
893
894         if (pdwResultCodes)
895                 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
896
897         WARN("control unavailable\n");
898         return DSERR_CONTROLUNAVAIL;
899 }
900
901 static HRESULT WINAPI PrimaryBufferImpl_GetObjectInPath(
902         LPDIRECTSOUNDBUFFER8 iface,REFGUID rguidObject,DWORD dwIndex,REFGUID rguidInterface,LPVOID* ppObject
903 ) {
904         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
905         FIXME("(%p,%s,%lu,%s,%p): stub\n",This,debugstr_guid(rguidObject),dwIndex,debugstr_guid(rguidInterface),ppObject);
906
907         WARN("control unavailable\n");
908         return DSERR_CONTROLUNAVAIL;
909 }
910
911 static HRESULT WINAPI PrimaryBufferImpl_Initialize(
912         LPDIRECTSOUNDBUFFER8 iface,LPDIRECTSOUND dsound,LPCDSBUFFERDESC dbsd
913 ) {
914         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
915         FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
916         DPRINTF("Re-Init!!!\n");
917         WARN("already initialized\n");
918         return DSERR_ALREADYINITIALIZED;
919 }
920
921 static HRESULT WINAPI PrimaryBufferImpl_GetCaps(
922         LPDIRECTSOUNDBUFFER8 iface,LPDSBCAPS caps
923 ) {
924         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
925         TRACE("(%p,%p)\n", iface, caps);
926
927         if (caps == NULL) {
928                 WARN("invalid parameter: caps == NULL\n");
929                 return DSERR_INVALIDPARAM;
930         }
931
932         if (caps->dwSize < sizeof(*caps)) {
933                 WARN("invalid parameter: caps->dwSize = %ld: < %d\n", caps->dwSize, sizeof(*caps));
934                 return DSERR_INVALIDPARAM;
935         }
936
937         caps->dwFlags = device->dsbd.dwFlags;
938         if (device->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
939         else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
940
941         caps->dwBufferBytes = device->buflen;
942
943         /* This value represents the speed of the "unlock" command.
944            As unlock is quite fast (it does not do anything), I put
945            4096 ko/s = 4 Mo / s */
946         /* FIXME: hwbuf speed */
947         caps->dwUnlockTransferRate = 4096;
948         caps->dwPlayCpuOverhead = 0;
949
950         return DS_OK;
951 }
952
953 static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(
954         LPDIRECTSOUNDBUFFER8 iface,REFIID riid,LPVOID *ppobj
955 ) {
956         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
957         DirectSoundDevice *device = This->device;
958         TRACE("(%p,%s,%p)\n", iface, debugstr_guid(riid), ppobj);
959
960         if (ppobj == NULL) {
961                 WARN("invalid parameter\n");
962                 return E_INVALIDARG;
963         }
964
965         *ppobj = NULL;  /* assume failure */
966
967         if ( IsEqualGUID(riid, &IID_IUnknown) ||
968              IsEqualGUID(riid, &IID_IDirectSoundBuffer) ) {
969                 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)This);
970                 *ppobj = This;
971                 return S_OK;
972         }
973
974         /* DirectSoundBuffer and DirectSoundBuffer8 are different and */
975         /* a primary buffer can't have a DirectSoundBuffer8 interface */
976         if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) {
977                 WARN("app requested DirectSoundBuffer8 on primary buffer\n");
978                 return E_NOINTERFACE;
979         }
980
981         if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
982                 ERR("app requested IDirectSoundNotify on primary buffer\n");
983                 /* FIXME: should we support this? */
984                 return E_NOINTERFACE;
985         }
986
987         if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
988                 ERR("app requested IDirectSound3DBuffer on primary buffer\n");
989                 return E_NOINTERFACE;
990         }
991
992         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
993                 if (!device->listener)
994                         IDirectSound3DListenerImpl_Create(device, &device->listener);
995                 if (device->listener) {
996                         *ppobj = device->listener;
997                         IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
998                         return S_OK;
999                 }
1000
1001                 WARN("IID_IDirectSound3DListener failed\n");
1002                 return E_NOINTERFACE;
1003         }
1004
1005         if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
1006                 FIXME("app requested IKsPropertySet on primary buffer\n");
1007                 return E_NOINTERFACE;
1008         }
1009
1010         FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
1011         return E_NOINTERFACE;
1012 }
1013
1014 static const IDirectSoundBuffer8Vtbl dspbvt =
1015 {
1016         PrimaryBufferImpl_QueryInterface,
1017         PrimaryBufferImpl_AddRef,
1018         PrimaryBufferImpl_Release,
1019         PrimaryBufferImpl_GetCaps,
1020         PrimaryBufferImpl_GetCurrentPosition,
1021         PrimaryBufferImpl_GetFormat,
1022         PrimaryBufferImpl_GetVolume,
1023         PrimaryBufferImpl_GetPan,
1024         PrimaryBufferImpl_GetFrequency,
1025         PrimaryBufferImpl_GetStatus,
1026         PrimaryBufferImpl_Initialize,
1027         PrimaryBufferImpl_Lock,
1028         PrimaryBufferImpl_Play,
1029         PrimaryBufferImpl_SetCurrentPosition,
1030         PrimaryBufferImpl_SetFormat,
1031         PrimaryBufferImpl_SetVolume,
1032         PrimaryBufferImpl_SetPan,
1033         PrimaryBufferImpl_SetFrequency,
1034         PrimaryBufferImpl_Stop,
1035         PrimaryBufferImpl_Unlock,
1036         PrimaryBufferImpl_Restore,
1037         PrimaryBufferImpl_SetFX,
1038         PrimaryBufferImpl_AcquireResources,
1039         PrimaryBufferImpl_GetObjectInPath
1040 };
1041
1042 HRESULT PrimaryBufferImpl_Create(
1043         DirectSoundDevice * device,
1044         PrimaryBufferImpl ** ppdsb,
1045         LPCDSBUFFERDESC dsbd)
1046 {
1047         PrimaryBufferImpl *dsb;
1048         TRACE("%p,%p,%p)\n",device,ppdsb,dsbd);
1049
1050         if (dsbd->lpwfxFormat) {
1051                 WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n");
1052                 *ppdsb = NULL;
1053                 return DSERR_INVALIDPARAM;
1054         }
1055
1056         dsb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1057
1058         if (dsb == NULL) {
1059                 WARN("out of memory\n");
1060                 *ppdsb = NULL;
1061                 return DSERR_OUTOFMEMORY;
1062         }
1063
1064         dsb->ref = 0;
1065         dsb->device = device;
1066         dsb->lpVtbl = &dspbvt;
1067
1068         CopyMemory(&device->dsbd, dsbd, sizeof(*dsbd));
1069
1070         TRACE("Created primary buffer at %p\n", dsb);
1071         TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
1072                 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1073                 device->pwfx->wFormatTag, device->pwfx->nChannels,
1074                 device->pwfx->nSamplesPerSec, device->pwfx->nAvgBytesPerSec,
1075                 device->pwfx->nBlockAlign, device->pwfx->wBitsPerSample,
1076                 device->pwfx->cbSize);
1077
1078         *ppdsb = dsb;
1079         return S_OK;
1080 }