kernel32: FindExSearchLimitToDirectories has no effect on FindFirstFileEx.
[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 "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 /** Calculate how long a fragment length of about 10 ms should be in frames
40  *
41  * nSamplesPerSec: Frequency rate in samples per second
42  * nBlockAlign: Size of a single blockalign
43  *
44  * Returns:
45  * Size in bytes of a single fragment
46  */
47 DWORD DSOUND_fraglen(DWORD nSamplesPerSec, DWORD nBlockAlign)
48 {
49     DWORD fraglen = 512 * nBlockAlign;
50
51     /* Compensate for only being roughly accurate */
52     if (nSamplesPerSec <= 26000)
53         fraglen /= 2;
54
55     if (nSamplesPerSec <= 12000)
56         fraglen /= 2;
57
58     if (nSamplesPerSec >= 80000)
59         fraglen *= 2;
60
61     return fraglen;
62 }
63
64 static void DSOUND_RecalcPrimary(DirectSoundDevice *device)
65 {
66     TRACE("(%p)\n", device);
67
68     device->fraglen = DSOUND_fraglen(device->pwfx->nSamplesPerSec, device->pwfx->nBlockAlign);
69     device->helfrags = device->buflen / device->fraglen;
70     TRACE("fraglen=%d helfrags=%d\n", device->fraglen, device->helfrags);
71
72     if (device->hwbuf && device->drvdesc.dwFlags & DSDDESC_DONTNEEDWRITELEAD)
73         device->writelead = 0;
74     else
75         /* calculate the 10ms write lead */
76         device->writelead = (device->pwfx->nSamplesPerSec / 100) * device->pwfx->nBlockAlign;
77 }
78
79 HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
80 {
81         HRESULT hres = DS_OK;
82         TRACE("(%p, %d)\n", device, forcewave);
83
84         if (device->driver)
85         {
86                 IDsDriver_Close(device->driver);
87                 if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
88                         waveOutClose(device->hwo);
89                 IDsDriver_Release(device->driver);
90                 device->driver = NULL;
91                 device->buffer = NULL;
92                 device->hwo = 0;
93         }
94         else if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
95                 waveOutClose(device->hwo);
96
97         /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
98         if (ds_hw_accel != DS_HW_ACCEL_EMULATION && !forcewave)
99                 waveOutMessage((HWAVEOUT)device->drvdesc.dnDevNode, DRV_QUERYDSOUNDIFACE, (DWORD_PTR)&device->driver, 0);
100
101         /* Get driver description */
102         if (device->driver) {
103                 DWORD wod = device->drvdesc.dnDevNode;
104                 hres = IDsDriver_GetDriverDesc(device->driver,&(device->drvdesc));
105                 device->drvdesc.dnDevNode = wod;
106                 if (FAILED(hres)) {
107                         WARN("IDsDriver_GetDriverDesc failed: %08x\n", hres);
108                         IDsDriver_Release(device->driver);
109                         device->driver = NULL;
110                 }
111         }
112
113         /* if no DirectSound interface available, use WINMM API instead */
114         if (!device->driver)
115                 device->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
116
117         if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
118         {
119                 DWORD flags = CALLBACK_FUNCTION;
120
121                 if (device->driver)
122                         flags |= WAVE_DIRECTSOUND;
123
124                 hres = mmErr(waveOutOpen(&(device->hwo), device->drvdesc.dnDevNode, device->pwfx, (DWORD_PTR)DSOUND_callback, (DWORD)device, flags));
125                 if (FAILED(hres)) {
126                         WARN("waveOutOpen failed\n");
127                         if (device->driver)
128                         {
129                                 IDsDriver_Release(device->driver);
130                                 device->driver = NULL;
131                         }
132                         return hres;
133                 }
134         }
135
136         if (device->driver)
137                 hres = IDsDriver_Open(device->driver);
138
139         return hres;
140 }
141
142 static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
143 {
144         DWORD buflen;
145         HRESULT err = DS_OK;
146         TRACE("(%p)\n", device);
147
148         /* on original windows, the buffer it set to a fixed size, no matter what the settings are.
149            on windows this size is always fixed (tested on win-xp) */
150         if (!device->buflen)
151                 device->buflen = ds_hel_buflen;
152         buflen = device->buflen;
153         buflen -= buflen % device->pwfx->nBlockAlign;
154         device->buflen = buflen;
155
156         if (device->driver)
157         {
158                 err = IDsDriver_CreateSoundBuffer(device->driver,device->pwfx,
159                                                   DSBCAPS_PRIMARYBUFFER,0,
160                                                   &(device->buflen),&(device->buffer),
161                                                   (LPVOID*)&(device->hwbuf));
162
163                 if (err != DS_OK) {
164                         WARN("IDsDriver_CreateSoundBuffer failed (%08x), falling back to waveout\n", err);
165                         err = DSOUND_ReopenDevice(device, TRUE);
166                         if (FAILED(err))
167                         {
168                                 WARN("Falling back to waveout failed too! Giving up\n");
169                                 return err;
170                         }
171                 }
172                 DSOUND_RecalcPrimary(device);
173                 device->prebuf = ds_snd_queue_max;
174                 if (device->helfrags < ds_snd_queue_min)
175                 {
176                         WARN("Too little sound buffer to be effective (%d/%d) falling back to waveout\n", device->buflen, ds_snd_queue_min * device->fraglen);
177                         device->buflen = buflen;
178                         IDsDriverBuffer_Release(device->hwbuf);
179                         device->hwbuf = NULL;
180                         err = DSOUND_ReopenDevice(device, TRUE);
181                         if (FAILED(err))
182                         {
183                                 WARN("Falling back to waveout failed too! Giving up\n");
184                                 return err;
185                         }
186                 }
187                 else if (device->helfrags < ds_snd_queue_max)
188                         device->prebuf = device->helfrags;
189         }
190
191         if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
192         else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
193
194         /* are we using waveOut stuff? */
195         if (!device->driver) {
196                 LPBYTE newbuf;
197                 LPWAVEHDR headers = NULL;
198                 DWORD overshot;
199                 unsigned int c;
200
201                 /* Start in pause mode, to allow buffers to get filled */
202                 waveOutPause(device->hwo);
203
204                 TRACE("desired buflen=%d, old buffer=%p\n", buflen, device->buffer);
205
206                 /* reallocate emulated primary buffer */
207                 if (device->buffer)
208                         newbuf = HeapReAlloc(GetProcessHeap(),0,device->buffer, buflen);
209                 else
210                         newbuf = HeapAlloc(GetProcessHeap(),0, buflen);
211
212                 if (!newbuf) {
213                         ERR("failed to allocate primary buffer\n");
214                         return DSERR_OUTOFMEMORY;
215                         /* but the old buffer might still exist and must be re-prepared */
216                 }
217
218                 DSOUND_RecalcPrimary(device);
219                 if (device->pwave)
220                         headers = HeapReAlloc(GetProcessHeap(),0,device->pwave, device->helfrags * sizeof(WAVEHDR));
221                 else
222                         headers = HeapAlloc(GetProcessHeap(),0,device->helfrags * sizeof(WAVEHDR));
223
224                 if (!headers) {
225                         ERR("failed to allocate wave headers\n");
226                         HeapFree(GetProcessHeap(), 0, newbuf);
227                         DSOUND_RecalcPrimary(device);
228                         return DSERR_OUTOFMEMORY;
229                 }
230
231                 device->buffer = newbuf;
232                 device->pwave = headers;
233
234                 /* prepare fragment headers */
235                 for (c=0; c<device->helfrags; c++) {
236                         device->pwave[c].lpData = (char*)device->buffer + c*device->fraglen;
237                         device->pwave[c].dwBufferLength = device->fraglen;
238                         device->pwave[c].dwUser = (DWORD)device;
239                         device->pwave[c].dwFlags = 0;
240                         device->pwave[c].dwLoops = 0;
241                         err = mmErr(waveOutPrepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR)));
242                         if (err != DS_OK) {
243                                 while (c--)
244                                         waveOutUnprepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR));
245                                 break;
246                         }
247                 }
248
249                 overshot = device->buflen % device->fraglen;
250                 /* sanity */
251                 if(overshot)
252                 {
253                         overshot -= overshot % device->pwfx->nBlockAlign;
254                         device->pwave[device->helfrags - 1].dwBufferLength += overshot;
255                 }
256
257                 TRACE("fraglen=%d, overshot=%d\n", device->fraglen, overshot);
258         }
259         FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
260         device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
261         return err;
262 }
263
264
265 static void DSOUND_PrimaryClose(DirectSoundDevice *device)
266 {
267         TRACE("(%p)\n", device);
268
269         /* are we using waveOut stuff? */
270         if (!device->hwbuf) {
271                 unsigned c;
272
273                 /* get out of CS when calling the wave system */
274                 LeaveCriticalSection(&(device->mixlock));
275                 /* **** */
276                 device->pwqueue = (DWORD)-1; /* resetting queues */
277                 waveOutReset(device->hwo);
278                 for (c=0; c<device->helfrags; c++)
279                         waveOutUnprepareHeader(device->hwo, &device->pwave[c], sizeof(WAVEHDR));
280                 /* **** */
281                 EnterCriticalSection(&(device->mixlock));
282
283                 /* clear the queue */
284                 device->pwqueue = 0;
285         } else {
286                 ULONG ref = IDsDriverBuffer_Release(device->hwbuf);
287                 if (!ref)
288                         device->hwbuf = 0;
289                 else
290                         ERR("Still %d references on primary buffer, refcount leak?\n", ref);
291         }
292 }
293
294 HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device)
295 {
296         HRESULT err = DS_OK;
297         TRACE("(%p)\n", device);
298
299         device->buflen = ds_hel_buflen;
300         err = DSOUND_PrimaryOpen(device);
301
302         if (err != DS_OK) {
303                 WARN("DSOUND_PrimaryOpen failed\n");
304                 return err;
305         }
306
307         device->state = STATE_STOPPED;
308         return DS_OK;
309 }
310
311 HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
312 {
313         TRACE("(%p)\n", device);
314
315         /* **** */
316         EnterCriticalSection(&(device->mixlock));
317
318         DSOUND_PrimaryClose(device);
319         if (device->driver) {
320                 if (device->hwbuf) {
321                         if (IDsDriverBuffer_Release(device->hwbuf) == 0)
322                                 device->hwbuf = 0;
323                 }
324         } else
325                 HeapFree(GetProcessHeap(),0,device->pwave);
326         HeapFree(GetProcessHeap(),0,device->pwfx);
327         device->pwfx=NULL;
328
329         LeaveCriticalSection(&(device->mixlock));
330         /* **** */
331
332         return DS_OK;
333 }
334
335 HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device)
336 {
337         HRESULT err = DS_OK;
338         TRACE("(%p)\n", device);
339
340         if (device->hwbuf) {
341                 err = IDsDriverBuffer_Play(device->hwbuf, 0, 0, DSBPLAY_LOOPING);
342                 if (err != DS_OK)
343                         WARN("IDsDriverBuffer_Play failed\n");
344         } else {
345                 err = mmErr(waveOutRestart(device->hwo));
346                 if (err != DS_OK)
347                         WARN("waveOutRestart failed\n");
348         }
349
350         return err;
351 }
352
353 HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device)
354 {
355         HRESULT err = DS_OK;
356         TRACE("(%p)\n", device);
357
358         if (device->hwbuf) {
359                 err = IDsDriverBuffer_Stop(device->hwbuf);
360                 if (err == DSERR_BUFFERLOST) {
361                         DSOUND_PrimaryClose(device);
362                         err = DSOUND_ReopenDevice(device, FALSE);
363                         if (FAILED(err))
364                                 ERR("DSOUND_ReopenDevice failed\n");
365                         else
366                         {
367                                 err = DSOUND_PrimaryOpen(device);
368                                 if (FAILED(err))
369                                         WARN("DSOUND_PrimaryOpen failed\n");
370                         }
371                 } else if (err != DS_OK) {
372                         WARN("IDsDriverBuffer_Stop failed\n");
373                 }
374         } else {
375
376                 /* don't call the wave system with the lock set */
377                 LeaveCriticalSection(&(device->mixlock));
378                 /* **** */
379
380                 err = mmErr(waveOutPause(device->hwo));
381
382                 /* **** */
383                 EnterCriticalSection(&(device->mixlock));
384
385                 if (err != DS_OK)
386                         WARN("waveOutPause failed\n");
387         }
388
389         return err;
390 }
391
392 HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos)
393 {
394         TRACE("(%p,%p,%p)\n", device, playpos, writepos);
395
396         if (device->hwbuf) {
397                 HRESULT err=IDsDriverBuffer_GetPosition(device->hwbuf,playpos,writepos);
398                 if (err) {
399                         WARN("IDsDriverBuffer_GetPosition failed\n");
400                         return err;
401                 }
402         } else {
403                 TRACE("pwplay=%i, pwqueue=%i\n", device->pwplay, device->pwqueue);
404
405                 /* check if playpos was requested */
406                 if (playpos)
407                         /* use the cached play position */
408                         *playpos = device->pwplay * device->fraglen;
409
410                 /* check if writepos was requested */
411                 if (writepos)
412                         /* the writepos is the first non-queued position */
413                         *writepos = ((device->pwplay + device->pwqueue) % device->helfrags) * device->fraglen;
414         }
415         TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:-1, writepos?*writepos:-1, device, GetTickCount());
416         return DS_OK;
417 }
418
419 HRESULT DSOUND_PrimarySetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex, BOOL forced)
420 {
421         HRESULT err = DSERR_BUFFERLOST;
422         int i, alloc_size, cp_size;
423         DWORD nSamplesPerSec, bpp, chans;
424         TRACE("(%p,%p)\n", device, wfex);
425
426         if (device->priolevel == DSSCL_NORMAL) {
427                 WARN("failed priority check!\n");
428                 return DSERR_PRIOLEVELNEEDED;
429         }
430
431         /* Let's be pedantic! */
432         if (wfex == NULL) {
433                 WARN("invalid parameter: wfex==NULL!\n");
434                 return DSERR_INVALIDPARAM;
435         }
436         TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
437               "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
438               wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
439               wfex->nAvgBytesPerSec, wfex->nBlockAlign,
440               wfex->wBitsPerSample, wfex->cbSize);
441
442         /* **** */
443         RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
444         EnterCriticalSection(&(device->mixlock));
445
446         if (wfex->wFormatTag == WAVE_FORMAT_PCM) {
447             alloc_size = sizeof(WAVEFORMATEX);
448             cp_size = sizeof(PCMWAVEFORMAT);
449         } else
450             alloc_size = cp_size = sizeof(WAVEFORMATEX) + wfex->cbSize;
451
452         device->pwfx = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,device->pwfx,alloc_size);
453
454         nSamplesPerSec = device->pwfx->nSamplesPerSec;
455         bpp = device->pwfx->wBitsPerSample;
456         chans = device->pwfx->nChannels;
457
458         CopyMemory(device->pwfx, wfex, cp_size);
459
460         if (!(device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) && device->hwbuf) {
461                 err = IDsDriverBuffer_SetFormat(device->hwbuf, device->pwfx);
462
463                 /* On bad format, try to re-create, big chance it will work then, only do this if we <HAVE> to */
464                 if (forced && (device->pwfx->nSamplesPerSec/100 != wfex->nSamplesPerSec/100 || err == DSERR_BADFORMAT))
465                 {
466                         err = DSERR_BUFFERLOST;
467                         CopyMemory(device->pwfx, wfex, cp_size);
468                 }
469
470                 if (err != DSERR_BUFFERLOST && FAILED(err)) {
471                         WARN("IDsDriverBuffer_SetFormat failed\n");
472                         if (!forced)
473                                 err = DS_OK;
474                         goto done;
475                 }
476
477                 if (err == S_FALSE)
478                 {
479                         /* ALSA specific: S_FALSE tells that recreation was successful,
480                          * but size and location may be changed, and buffer has to be restarted
481                          * I put it here, so if frequency doesn't match the error will be changed to DSERR_BUFFERLOST
482                          * and the entire re-initialization will occur anyway
483                          */
484                         IDsDriverBuffer_Lock(device->hwbuf, (LPVOID *)&device->buffer, &device->buflen, NULL, NULL, 0, 0, DSBLOCK_ENTIREBUFFER);
485                         IDsDriverBuffer_Unlock(device->hwbuf, device->buffer, 0, NULL, 0);
486
487                         if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
488                         else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
489                         device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
490                         err = DS_OK;
491                 }
492                 DSOUND_RecalcPrimary(device);
493         }
494
495         if (err == DSERR_BUFFERLOST)
496         {
497                 DSOUND_PrimaryClose(device);
498
499                 err = DSOUND_ReopenDevice(device, FALSE);
500                 if (FAILED(err))
501                 {
502                         WARN("DSOUND_ReopenDevice failed: %08x\n", err);
503                         goto done;
504                 }
505                 err = DSOUND_PrimaryOpen(device);
506                 if (err != DS_OK) {
507                         WARN("DSOUND_PrimaryOpen failed\n");
508                         goto done;
509                 }
510
511                 if (wfex->nSamplesPerSec/100 != device->pwfx->nSamplesPerSec/100 && forced && device->buffer)
512                 {
513                         DSOUND_PrimaryClose(device);
514                         device->pwfx->nSamplesPerSec = wfex->nSamplesPerSec;
515                         err = DSOUND_ReopenDevice(device, TRUE);
516                         if (FAILED(err))
517                                 WARN("DSOUND_ReopenDevice(2) failed: %08x\n", err);
518                         else if (FAILED((err = DSOUND_PrimaryOpen(device))))
519                                 WARN("DSOUND_PrimaryOpen(2) failed: %08x\n", err);
520                 }
521         }
522
523         if (nSamplesPerSec != device->pwfx->nSamplesPerSec || bpp != device->pwfx->wBitsPerSample || chans != device->pwfx->nChannels) {
524                 IDirectSoundBufferImpl** dsb = device->buffers;
525                 for (i = 0; i < device->nrofbuffers; i++, dsb++) {
526                         /* **** */
527                         RtlAcquireResourceExclusive(&(*dsb)->lock, TRUE);
528
529                         (*dsb)->freqAdjust = ((DWORD64)(*dsb)->freq << DSOUND_FREQSHIFT) / device->pwfx->nSamplesPerSec;
530                         DSOUND_RecalcFormat((*dsb));
531                         DSOUND_MixToTemporary((*dsb), 0, (*dsb)->buflen);
532                         (*dsb)->primary_mixpos = 0;
533
534                         RtlReleaseResource(&(*dsb)->lock);
535                         /* **** */
536                 }
537         }
538
539 done:
540         LeaveCriticalSection(&(device->mixlock));
541         RtlReleaseResource(&(device->buffer_list_lock));
542         /* **** */
543
544         return err;
545 }
546
547 /*******************************************************************************
548  *              PrimaryBuffer
549  */
550 /* This sets this format for the <em>Primary Buffer Only</em> */
551 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
552 static HRESULT WINAPI PrimaryBufferImpl_SetFormat(
553     LPDIRECTSOUNDBUFFER iface,
554     LPCWAVEFORMATEX wfex)
555 {
556     DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
557     TRACE("(%p,%p)\n", iface, wfex);
558     return DSOUND_PrimarySetFormat(device, wfex, device->priolevel == DSSCL_WRITEPRIMARY);
559 }
560
561 static HRESULT WINAPI PrimaryBufferImpl_SetVolume(
562         LPDIRECTSOUNDBUFFER iface,LONG vol
563 ) {
564         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
565         DWORD ampfactors;
566         DSVOLUMEPAN volpan;
567         HRESULT hres = DS_OK;
568         TRACE("(%p,%d)\n", iface, vol);
569
570         if (!(device->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
571                 WARN("control unavailable\n");
572                 return DSERR_CONTROLUNAVAIL;
573         }
574
575         if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) {
576                 WARN("invalid parameter: vol = %d\n", vol);
577                 return DSERR_INVALIDPARAM;
578         }
579
580         /* **** */
581         EnterCriticalSection(&(device->mixlock));
582
583         waveOutGetVolume(device->hwo, &ampfactors);
584         volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
585         volpan.dwTotalRightAmpFactor=ampfactors >> 16;
586         DSOUND_AmpFactorToVolPan(&volpan);
587         if (vol != volpan.lVolume) {
588             volpan.lVolume=vol;
589             DSOUND_RecalcVolPan(&volpan);
590             if (device->hwbuf) {
591                 hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &volpan);
592                 if (hres != DS_OK)
593                     WARN("IDsDriverBuffer_SetVolumePan failed\n");
594             } else {
595                 ampfactors = (volpan.dwTotalLeftAmpFactor & 0xffff) | (volpan.dwTotalRightAmpFactor << 16);
596                 waveOutSetVolume(device->hwo, ampfactors);
597             }
598         }
599
600         LeaveCriticalSection(&(device->mixlock));
601         /* **** */
602
603         return hres;
604 }
605
606 static HRESULT WINAPI PrimaryBufferImpl_GetVolume(
607         LPDIRECTSOUNDBUFFER iface,LPLONG vol
608 ) {
609         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
610         DWORD ampfactors;
611         DSVOLUMEPAN volpan;
612         TRACE("(%p,%p)\n", iface, vol);
613
614         if (!(device->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
615                 WARN("control unavailable\n");
616                 return DSERR_CONTROLUNAVAIL;
617         }
618
619         if (vol == NULL) {
620                 WARN("invalid parameter: vol = NULL\n");
621                 return DSERR_INVALIDPARAM;
622         }
623
624         waveOutGetVolume(device->hwo, &ampfactors);
625         volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
626         volpan.dwTotalRightAmpFactor=ampfactors >> 16;
627         DSOUND_AmpFactorToVolPan(&volpan);
628         *vol = volpan.lVolume;
629         return DS_OK;
630 }
631
632 static HRESULT WINAPI PrimaryBufferImpl_SetFrequency(
633         LPDIRECTSOUNDBUFFER iface,DWORD freq
634 ) {
635         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
636         TRACE("(%p,%d)\n",This,freq);
637
638         /* You cannot set the frequency of the primary buffer */
639         WARN("control unavailable\n");
640         return DSERR_CONTROLUNAVAIL;
641 }
642
643 static HRESULT WINAPI PrimaryBufferImpl_Play(
644         LPDIRECTSOUNDBUFFER iface,DWORD reserved1,DWORD reserved2,DWORD flags
645 ) {
646         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
647         TRACE("(%p,%08x,%08x,%08x)\n", iface, reserved1, reserved2, flags);
648
649         if (!(flags & DSBPLAY_LOOPING)) {
650                 WARN("invalid parameter: flags = %08x\n", flags);
651                 return DSERR_INVALIDPARAM;
652         }
653
654         /* **** */
655         EnterCriticalSection(&(device->mixlock));
656
657         if (device->state == STATE_STOPPED)
658                 device->state = STATE_STARTING;
659         else if (device->state == STATE_STOPPING)
660                 device->state = STATE_PLAYING;
661
662         LeaveCriticalSection(&(device->mixlock));
663         /* **** */
664
665         return DS_OK;
666 }
667
668 static HRESULT WINAPI PrimaryBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface)
669 {
670         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
671         TRACE("(%p)\n", iface);
672
673         /* **** */
674         EnterCriticalSection(&(device->mixlock));
675
676         if (device->state == STATE_PLAYING)
677                 device->state = STATE_STOPPING;
678         else if (device->state == STATE_STARTING)
679                 device->state = STATE_STOPPED;
680
681         LeaveCriticalSection(&(device->mixlock));
682         /* **** */
683
684         return DS_OK;
685 }
686
687 static ULONG WINAPI PrimaryBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface)
688 {
689     PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
690     ULONG ref = InterlockedIncrement(&(This->ref));
691     TRACE("(%p) ref was %d\n", This, ref - 1);
692     return ref;
693 }
694
695 static ULONG WINAPI PrimaryBufferImpl_Release(LPDIRECTSOUNDBUFFER iface)
696 {
697     PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
698     DWORD ref = InterlockedDecrement(&(This->ref));
699     TRACE("(%p) ref was %d\n", This, ref + 1);
700
701     if (!ref) {
702         This->device->primary = NULL;
703         HeapFree(GetProcessHeap(), 0, This);
704         TRACE("(%p) released\n", This);
705     }
706     return ref;
707 }
708
709 static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(
710         LPDIRECTSOUNDBUFFER iface,LPDWORD playpos,LPDWORD writepos
711 ) {
712         HRESULT hres;
713         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
714         TRACE("(%p,%p,%p)\n", iface, playpos, writepos);
715
716         /* **** */
717         EnterCriticalSection(&(device->mixlock));
718
719         hres = DSOUND_PrimaryGetPosition(device, playpos, writepos);
720         if (hres != DS_OK) {
721                 WARN("DSOUND_PrimaryGetPosition failed\n");
722                 LeaveCriticalSection(&(device->mixlock));
723                 return hres;
724         }
725         if (writepos) {
726                 if (device->state != STATE_STOPPED)
727                         /* apply the documented 10ms lead to writepos */
728                         *writepos += device->writelead;
729                 while (*writepos >= device->buflen) *writepos -= device->buflen;
730         }
731
732         LeaveCriticalSection(&(device->mixlock));
733         /* **** */
734
735         TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount());
736         return DS_OK;
737 }
738
739 static HRESULT WINAPI PrimaryBufferImpl_GetStatus(
740         LPDIRECTSOUNDBUFFER iface,LPDWORD status
741 ) {
742         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
743         TRACE("(%p,%p)\n", iface, status);
744
745         if (status == NULL) {
746                 WARN("invalid parameter: status == NULL\n");
747                 return DSERR_INVALIDPARAM;
748         }
749
750         *status = 0;
751         if ((device->state == STATE_STARTING) ||
752             (device->state == STATE_PLAYING))
753                 *status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING;
754
755         TRACE("status=%x\n", *status);
756         return DS_OK;
757 }
758
759
760 static HRESULT WINAPI PrimaryBufferImpl_GetFormat(
761     LPDIRECTSOUNDBUFFER iface,
762     LPWAVEFORMATEX lpwf,
763     DWORD wfsize,
764     LPDWORD wfwritten)
765 {
766     DWORD size;
767     DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
768     TRACE("(%p,%p,%d,%p)\n", iface, lpwf, wfsize, wfwritten);
769
770     size = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
771
772     if (lpwf) { /* NULL is valid */
773         if (wfsize >= size) {
774             CopyMemory(lpwf,device->pwfx,size);
775             if (wfwritten)
776                 *wfwritten = size;
777         } else {
778             WARN("invalid parameter: wfsize too small\n");
779             if (wfwritten)
780                 *wfwritten = 0;
781             return DSERR_INVALIDPARAM;
782         }
783     } else {
784         if (wfwritten)
785             *wfwritten = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
786         else {
787             WARN("invalid parameter: wfwritten == NULL\n");
788             return DSERR_INVALIDPARAM;
789         }
790     }
791
792     return DS_OK;
793 }
794
795 static HRESULT WINAPI PrimaryBufferImpl_Lock(
796         LPDIRECTSOUNDBUFFER iface,DWORD writecursor,DWORD writebytes,LPVOID *lplpaudioptr1,LPDWORD audiobytes1,LPVOID *lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
797 ) {
798         HRESULT hres;
799         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
800         TRACE("(%p,%d,%d,%p,%p,%p,%p,0x%08x) at %d\n",
801                 iface,
802                 writecursor,
803                 writebytes,
804                 lplpaudioptr1,
805                 audiobytes1,
806                 lplpaudioptr2,
807                 audiobytes2,
808                 flags,
809                 GetTickCount()
810         );
811
812         if (device->priolevel != DSSCL_WRITEPRIMARY) {
813                 WARN("failed priority check!\n");
814                 return DSERR_PRIOLEVELNEEDED;
815         }
816
817         /* when this flag is set, writecursor is meaningless and must be calculated */
818         if (flags & DSBLOCK_FROMWRITECURSOR) {
819                 /* GetCurrentPosition does too much magic to duplicate here */
820                 hres = IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writecursor);
821                 if (hres != DS_OK) {
822                         WARN("IDirectSoundBuffer_GetCurrentPosition failed\n");
823                         return hres;
824                 }
825         }
826
827         /* when this flag is set, writebytes is meaningless and must be set */
828         if (flags & DSBLOCK_ENTIREBUFFER)
829                 writebytes = device->buflen;
830
831         if (writecursor >= device->buflen) {
832                 WARN("Invalid parameter, writecursor: %u >= buflen: %u\n",
833                      writecursor, device->buflen);
834                 return DSERR_INVALIDPARAM;
835         }
836
837         if (writebytes > device->buflen) {
838                 WARN("Invalid parameter, writebytes: %u > buflen: %u\n",
839                      writebytes, device->buflen);
840                 return DSERR_INVALIDPARAM;
841         }
842
843         if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
844                 hres = IDsDriverBuffer_Lock(device->hwbuf,
845                                             lplpaudioptr1, audiobytes1,
846                                             lplpaudioptr2, audiobytes2,
847                                             writecursor, writebytes,
848                                             0);
849                 if (hres != DS_OK) {
850                         WARN("IDsDriverBuffer_Lock failed\n");
851                         return hres;
852                 }
853         } else {
854                 if (writecursor+writebytes <= device->buflen) {
855                         *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
856                         *audiobytes1 = writebytes;
857                         if (lplpaudioptr2)
858                                 *(LPBYTE*)lplpaudioptr2 = NULL;
859                         if (audiobytes2)
860                                 *audiobytes2 = 0;
861                         TRACE("->%d.0\n",writebytes);
862                 } else {
863                         *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
864                         *audiobytes1 = device->buflen-writecursor;
865                         if (lplpaudioptr2)
866                                 *(LPBYTE*)lplpaudioptr2 = device->buffer;
867                         if (audiobytes2)
868                                 *audiobytes2 = writebytes-(device->buflen-writecursor);
869                         TRACE("->%d.%d\n",*audiobytes1,audiobytes2?*audiobytes2:0);
870                 }
871         }
872         return DS_OK;
873 }
874
875 static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition(
876         LPDIRECTSOUNDBUFFER iface,DWORD newpos
877 ) {
878         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
879         TRACE("(%p,%d)\n",This,newpos);
880
881         /* You cannot set the position of the primary buffer */
882         WARN("invalid call\n");
883         return DSERR_INVALIDCALL;
884 }
885
886 static HRESULT WINAPI PrimaryBufferImpl_SetPan(
887         LPDIRECTSOUNDBUFFER iface,LONG pan
888 ) {
889         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
890         DWORD ampfactors;
891         DSVOLUMEPAN volpan;
892         HRESULT hres = DS_OK;
893         TRACE("(%p,%d)\n", iface, pan);
894
895         if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
896                 WARN("control unavailable\n");
897                 return DSERR_CONTROLUNAVAIL;
898         }
899
900         if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) {
901                 WARN("invalid parameter: pan = %d\n", pan);
902                 return DSERR_INVALIDPARAM;
903         }
904
905         /* **** */
906         EnterCriticalSection(&(device->mixlock));
907
908         waveOutGetVolume(device->hwo, &ampfactors);
909         volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
910         volpan.dwTotalRightAmpFactor=ampfactors >> 16;
911         DSOUND_AmpFactorToVolPan(&volpan);
912         if (pan != volpan.lPan) {
913             volpan.lPan=pan;
914             DSOUND_RecalcVolPan(&volpan);
915             if (device->hwbuf) {
916                 hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &volpan);
917                 if (hres != DS_OK)
918                     WARN("IDsDriverBuffer_SetVolumePan failed\n");
919             } else {
920                 ampfactors = (volpan.dwTotalLeftAmpFactor & 0xffff) | (volpan.dwTotalRightAmpFactor << 16);
921                 waveOutSetVolume(device->hwo, ampfactors);
922             }
923         }
924
925         LeaveCriticalSection(&(device->mixlock));
926         /* **** */
927
928         return hres;
929 }
930
931 static HRESULT WINAPI PrimaryBufferImpl_GetPan(
932         LPDIRECTSOUNDBUFFER iface,LPLONG pan
933 ) {
934         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
935         DWORD ampfactors;
936         DSVOLUMEPAN volpan;
937         TRACE("(%p,%p)\n", iface, pan);
938
939         if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
940                 WARN("control unavailable\n");
941                 return DSERR_CONTROLUNAVAIL;
942         }
943
944         if (pan == NULL) {
945                 WARN("invalid parameter: pan == NULL\n");
946                 return DSERR_INVALIDPARAM;
947         }
948
949         waveOutGetVolume(device->hwo, &ampfactors);
950         volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
951         volpan.dwTotalRightAmpFactor=ampfactors >> 16;
952         DSOUND_AmpFactorToVolPan(&volpan);
953         *pan = volpan.lPan;
954         return DS_OK;
955 }
956
957 static HRESULT WINAPI PrimaryBufferImpl_Unlock(
958         LPDIRECTSOUNDBUFFER iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
959 ) {
960         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
961         TRACE("(%p,%p,%d,%p,%d)\n", iface, p1, x1, p2, x2);
962
963         if (device->priolevel != DSSCL_WRITEPRIMARY) {
964                 WARN("failed priority check!\n");
965                 return DSERR_PRIOLEVELNEEDED;
966         }
967
968         if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
969                 HRESULT hres;
970                 
971                 hres = IDsDriverBuffer_Unlock(device->hwbuf, p1, x1, p2, x2);
972                 if (hres != DS_OK) {
973                         WARN("IDsDriverBuffer_Unlock failed\n");
974                         return hres;
975                 }
976         }
977
978         return DS_OK;
979 }
980
981 static HRESULT WINAPI PrimaryBufferImpl_Restore(
982         LPDIRECTSOUNDBUFFER iface
983 ) {
984         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
985         FIXME("(%p):stub\n",This);
986         return DS_OK;
987 }
988
989 static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(
990         LPDIRECTSOUNDBUFFER iface,LPDWORD freq
991 ) {
992         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
993         TRACE("(%p,%p)\n", iface, freq);
994
995         if (freq == NULL) {
996                 WARN("invalid parameter: freq == NULL\n");
997                 return DSERR_INVALIDPARAM;
998         }
999
1000         if (!(device->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
1001                 WARN("control unavailable\n");
1002                 return DSERR_CONTROLUNAVAIL;
1003         }
1004
1005         *freq = device->pwfx->nSamplesPerSec;
1006         TRACE("-> %d\n", *freq);
1007
1008         return DS_OK;
1009 }
1010
1011 static HRESULT WINAPI PrimaryBufferImpl_Initialize(
1012         LPDIRECTSOUNDBUFFER iface,LPDIRECTSOUND dsound,LPCDSBUFFERDESC dbsd
1013 ) {
1014         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
1015         WARN("(%p) already initialized\n", This);
1016         return DSERR_ALREADYINITIALIZED;
1017 }
1018
1019 static HRESULT WINAPI PrimaryBufferImpl_GetCaps(
1020         LPDIRECTSOUNDBUFFER iface,LPDSBCAPS caps
1021 ) {
1022         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
1023         TRACE("(%p,%p)\n", iface, caps);
1024
1025         if (caps == NULL) {
1026                 WARN("invalid parameter: caps == NULL\n");
1027                 return DSERR_INVALIDPARAM;
1028         }
1029
1030         if (caps->dwSize < sizeof(*caps)) {
1031                 WARN("invalid parameter: caps->dwSize = %d\n", caps->dwSize);
1032                 return DSERR_INVALIDPARAM;
1033         }
1034
1035         caps->dwFlags = device->dsbd.dwFlags;
1036         caps->dwBufferBytes = device->buflen;
1037
1038         /* Windows reports these as zero */
1039         caps->dwUnlockTransferRate = 0;
1040         caps->dwPlayCpuOverhead = 0;
1041
1042         return DS_OK;
1043 }
1044
1045 static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(
1046         LPDIRECTSOUNDBUFFER iface,REFIID riid,LPVOID *ppobj
1047 ) {
1048         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
1049         DirectSoundDevice *device = This->device;
1050         TRACE("(%p,%s,%p)\n", iface, debugstr_guid(riid), ppobj);
1051
1052         if (ppobj == NULL) {
1053                 WARN("invalid parameter\n");
1054                 return E_INVALIDARG;
1055         }
1056
1057         *ppobj = NULL;  /* assume failure */
1058
1059         if ( IsEqualGUID(riid, &IID_IUnknown) ||
1060              IsEqualGUID(riid, &IID_IDirectSoundBuffer) ) {
1061                 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)This);
1062                 *ppobj = This;
1063                 return S_OK;
1064         }
1065
1066         /* DirectSoundBuffer and DirectSoundBuffer8 are different and */
1067         /* a primary buffer can't have a DirectSoundBuffer8 interface */
1068         if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) {
1069                 WARN("app requested DirectSoundBuffer8 on primary buffer\n");
1070                 return E_NOINTERFACE;
1071         }
1072
1073         if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
1074                 ERR("app requested IDirectSoundNotify on primary buffer\n");
1075                 /* FIXME: should we support this? */
1076                 return E_NOINTERFACE;
1077         }
1078
1079         if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1080                 ERR("app requested IDirectSound3DBuffer on primary buffer\n");
1081                 return E_NOINTERFACE;
1082         }
1083
1084         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1085                 if (!device->listener)
1086                         IDirectSound3DListenerImpl_Create(device, &device->listener);
1087                 if (device->listener) {
1088                         *ppobj = device->listener;
1089                         IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
1090                         return S_OK;
1091                 }
1092
1093                 WARN("IID_IDirectSound3DListener failed\n");
1094                 return E_NOINTERFACE;
1095         }
1096
1097         if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
1098                 FIXME("app requested IKsPropertySet on primary buffer\n");
1099                 return E_NOINTERFACE;
1100         }
1101
1102         FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
1103         return E_NOINTERFACE;
1104 }
1105
1106 static const IDirectSoundBufferVtbl dspbvt =
1107 {
1108         PrimaryBufferImpl_QueryInterface,
1109         PrimaryBufferImpl_AddRef,
1110         PrimaryBufferImpl_Release,
1111         PrimaryBufferImpl_GetCaps,
1112         PrimaryBufferImpl_GetCurrentPosition,
1113         PrimaryBufferImpl_GetFormat,
1114         PrimaryBufferImpl_GetVolume,
1115         PrimaryBufferImpl_GetPan,
1116         PrimaryBufferImpl_GetFrequency,
1117         PrimaryBufferImpl_GetStatus,
1118         PrimaryBufferImpl_Initialize,
1119         PrimaryBufferImpl_Lock,
1120         PrimaryBufferImpl_Play,
1121         PrimaryBufferImpl_SetCurrentPosition,
1122         PrimaryBufferImpl_SetFormat,
1123         PrimaryBufferImpl_SetVolume,
1124         PrimaryBufferImpl_SetPan,
1125         PrimaryBufferImpl_SetFrequency,
1126         PrimaryBufferImpl_Stop,
1127         PrimaryBufferImpl_Unlock,
1128         PrimaryBufferImpl_Restore
1129 };
1130
1131 HRESULT PrimaryBufferImpl_Create(
1132         DirectSoundDevice * device,
1133         PrimaryBufferImpl ** ppdsb,
1134         LPCDSBUFFERDESC dsbd)
1135 {
1136         PrimaryBufferImpl *dsb;
1137         TRACE("%p,%p,%p)\n",device,ppdsb,dsbd);
1138
1139         if (dsbd->lpwfxFormat) {
1140                 WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n");
1141                 *ppdsb = NULL;
1142                 return DSERR_INVALIDPARAM;
1143         }
1144
1145         dsb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1146
1147         if (dsb == NULL) {
1148                 WARN("out of memory\n");
1149                 *ppdsb = NULL;
1150                 return DSERR_OUTOFMEMORY;
1151         }
1152
1153         dsb->ref = 0;
1154         dsb->device = device;
1155         dsb->lpVtbl = &dspbvt;
1156
1157         CopyMemory(&device->dsbd, dsbd, sizeof(*dsbd));
1158
1159         TRACE("Created primary buffer at %p\n", dsb);
1160         TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
1161                 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1162                 device->pwfx->wFormatTag, device->pwfx->nChannels,
1163                 device->pwfx->nSamplesPerSec, device->pwfx->nAvgBytesPerSec,
1164                 device->pwfx->nBlockAlign, device->pwfx->wBitsPerSample,
1165                 device->pwfx->cbSize);
1166
1167         *ppdsb = dsb;
1168         return S_OK;
1169 }