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