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