Be more stringent in the 'Lock' invalid RECT check.
[wine] / dlls / dsound / primary.c
1 /*                      DirectSound
2  *
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998 Rob Riggs
5  * Copyright 2000-2002 TransGaming Technologies, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23 #include <assert.h>
24 #include <stdio.h>
25 #include <sys/types.h>
26 #include <sys/fcntl.h>
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
30 #include <stdlib.h>
31 #include <string.h>
32 #include <math.h>       /* Insomnia - pow() function */
33
34 #include "windef.h"
35 #include "winbase.h"
36 #include "wingdi.h"
37 #include "winuser.h"
38 #include "winerror.h"
39 #include "mmsystem.h"
40 #include "winternl.h"
41 #include "mmddk.h"
42 #include "wine/windef16.h"
43 #include "wine/debug.h"
44 #include "dsound.h"
45 #include "dsdriver.h"
46 #include "dsound_private.h"
47
48 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
49
50 void DSOUND_RecalcPrimary(IDirectSoundImpl *This)
51 {
52         DWORD sw;
53         TRACE("(%p)\n",This);
54
55         sw = This->wfx.nChannels * (This->wfx.wBitsPerSample / 8);
56         if (This->hwbuf) {
57                 DWORD fraglen;
58                 /* let fragment size approximate the timer delay */
59                 fraglen = (This->wfx.nSamplesPerSec * DS_TIME_DEL / 1000) * sw;
60                 /* reduce fragment size until an integer number of them fits in the buffer */
61                 /* (FIXME: this may or may not be a good idea) */
62                 while (This->buflen % fraglen) fraglen -= sw;
63                 This->fraglen = fraglen;
64                 TRACE("fraglen=%ld\n", This->fraglen);
65         }
66         /* calculate the 10ms write lead */
67         This->writelead = (This->wfx.nSamplesPerSec / 100) * sw;
68 }
69
70 static HRESULT DSOUND_PrimaryOpen(IDirectSoundImpl *This)
71 {
72         HRESULT err = DS_OK;
73         TRACE("(%p)\n",This);
74
75         /* are we using waveOut stuff? */
76         if (!This->hwbuf) {
77                 LPBYTE newbuf;
78                 DWORD buflen;
79                 HRESULT merr = DS_OK;
80                 /* Start in pause mode, to allow buffers to get filled */
81                 waveOutPause(This->hwo);
82                 if (This->state == STATE_PLAYING) This->state = STATE_STARTING;
83                 else if (This->state == STATE_STOPPING) This->state = STATE_STOPPED;
84                 /* use fragments of 10ms (1/100s) each (which should get us within
85                  * the documented write cursor lead of 10-15ms) */
86                 buflen = ((This->wfx.nAvgBytesPerSec / 100) & ~3) * DS_HEL_FRAGS;
87                 TRACE("desired buflen=%ld, old buffer=%p\n", buflen, This->buffer);
88                 /* reallocate emulated primary buffer */
89                 newbuf = (LPBYTE)HeapReAlloc(GetProcessHeap(),0,This->buffer,buflen);
90                 if (newbuf == NULL) {
91                         ERR("failed to allocate primary buffer\n");
92                         merr = DSERR_OUTOFMEMORY;
93                         /* but the old buffer might still exist and must be re-prepared */
94                 } else {
95                         This->buffer = newbuf;
96                         This->buflen = buflen;
97                 }
98                 if (This->buffer) {
99                         unsigned c;
100
101                         This->fraglen = This->buflen / DS_HEL_FRAGS;
102
103                         /* prepare fragment headers */
104                         for (c=0; c<DS_HEL_FRAGS; c++) {
105                                 This->pwave[c]->lpData = This->buffer + c*This->fraglen;
106                                 This->pwave[c]->dwBufferLength = This->fraglen;
107                                 This->pwave[c]->dwUser = (DWORD)This;
108                                 This->pwave[c]->dwFlags = 0;
109                                 This->pwave[c]->dwLoops = 0;
110                                 err = mmErr(waveOutPrepareHeader(This->hwo,This->pwave[c],sizeof(WAVEHDR)));
111                                 if (err != DS_OK) {
112                                         while (c--)
113                                                 waveOutUnprepareHeader(This->hwo,This->pwave[c],sizeof(WAVEHDR));
114                                         break;
115                                 }
116                         }
117
118                         This->pwplay = 0;
119                         This->pwwrite = 0;
120                         This->pwqueue = 0;
121                         This->playpos = 0;
122                         This->mixpos = 0;
123                         memset(This->buffer, (This->wfx.wBitsPerSample == 16) ? 0 : 128, This->buflen);
124                         TRACE("fraglen=%ld\n", This->fraglen);
125                         DSOUND_WaveQueue(This, (DWORD)-1);
126                 }
127                 if ((err == DS_OK) && (merr != DS_OK))
128                         err = merr;
129         }
130         return err;
131 }
132
133
134 static void DSOUND_PrimaryClose(IDirectSoundImpl *This)
135 {
136         TRACE("(%p)\n",This);
137
138         /* are we using waveOut stuff? */
139         if (!This->hwbuf) {
140                 unsigned c;
141
142                 This->pwqueue = (DWORD)-1; /* resetting queues */
143                 waveOutReset(This->hwo);
144                 for (c=0; c<DS_HEL_FRAGS; c++)
145                         waveOutUnprepareHeader(This->hwo, This->pwave[c], sizeof(WAVEHDR));
146                 This->pwqueue = 0;
147         }
148 }
149
150 HRESULT DSOUND_PrimaryCreate(IDirectSoundImpl *This)
151 {
152         HRESULT err = DS_OK;
153         TRACE("(%p)\n",This);
154
155         This->buflen = This->wfx.nAvgBytesPerSec;
156
157         /* FIXME: verify that hardware capabilities (DSCAPS_PRIMARY flags) match */
158
159         if (This->driver) {
160                 err = IDsDriver_CreateSoundBuffer(This->driver,&(This->wfx),
161                                                   DSBCAPS_PRIMARYBUFFER,0,
162                                                   &(This->buflen),&(This->buffer),
163                                                   (LPVOID*)&(This->hwbuf));
164                 if (err != DS_OK) {
165                         WARN("IDsDriver_CreateSoundBuffer failed\n");
166                         return err;
167                 }
168         }
169         if (!This->hwbuf) {
170                 /* Allocate memory for HEL buffer headers */
171                 unsigned c;
172                 for (c=0; c<DS_HEL_FRAGS; c++) {
173                         This->pwave[c] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEHDR));
174                         if (!This->pwave[c]) {
175                                 /* Argh, out of memory */
176                                 while (c--) {
177                                         HeapFree(GetProcessHeap(),0,This->pwave[c]);
178                                 }
179                                 WARN("out of memory\n");
180                                 return DSERR_OUTOFMEMORY;
181                         }
182                 }
183         }
184
185         err = DSOUND_PrimaryOpen(This);
186
187         if (err != DS_OK) {
188                 WARN("DSOUND_PrimaryOpen failed\n");
189                 return err;
190         }
191
192         /* calculate fragment size and write lead */
193         DSOUND_RecalcPrimary(This);
194         This->state = STATE_STOPPED;
195         return DS_OK;
196 }
197
198 HRESULT DSOUND_PrimaryDestroy(IDirectSoundImpl *This)
199 {
200         TRACE("(%p)\n",This);
201
202         DSOUND_PrimaryClose(This);
203         if (This->hwbuf) {
204                 if (IDsDriverBuffer_Release(This->hwbuf) == 0)
205                         This->hwbuf = 0;
206         } else {
207                 unsigned c;
208                 for (c=0; c<DS_HEL_FRAGS; c++) {
209                         HeapFree(GetProcessHeap(),0,This->pwave[c]);
210                 }
211         }
212         return DS_OK;
213 }
214
215 HRESULT DSOUND_PrimaryPlay(IDirectSoundImpl *This)
216 {
217         HRESULT err = DS_OK;
218         TRACE("(%p)\n",This);
219
220         if (This->hwbuf) {
221                 err = IDsDriverBuffer_Play(This->hwbuf, 0, 0, DSBPLAY_LOOPING);
222                 if (err != DS_OK)
223                         WARN("IDsDriverBuffer_Play failed\n");
224         } else {
225                 err = mmErr(waveOutRestart(This->hwo));
226                 if (err != DS_OK)
227                         WARN("waveOutRestart failed\n");
228         }
229
230         return err;
231 }
232
233 HRESULT DSOUND_PrimaryStop(IDirectSoundImpl *This)
234 {
235         HRESULT err = DS_OK;
236         TRACE("(%p)\n",This);
237
238         if (This->hwbuf) {
239                 err = IDsDriverBuffer_Stop(This->hwbuf);
240                 if (err == DSERR_BUFFERLOST) {
241                         DWORD flags = CALLBACK_FUNCTION;
242                         if (ds_hw_accel != DS_HW_ACCEL_EMULATION)
243                                 flags |= WAVE_DIRECTSOUND;
244                         /* Wine-only: the driver wants us to reopen the device */
245                         /* FIXME: check for errors */
246                         IDsDriverBuffer_Release(This->hwbuf);
247                         waveOutClose(This->hwo);
248                         This->hwo = 0;
249                         err = mmErr(waveOutOpen(&(This->hwo), This->drvdesc.dnDevNode,
250                                                 &(This->wfx), (DWORD)DSOUND_callback, (DWORD)This,
251                                                 flags));
252                         if (err == DS_OK) {
253                                 err = IDsDriver_CreateSoundBuffer(This->driver,&(This->wfx),
254                                                                   DSBCAPS_PRIMARYBUFFER,0,
255                                                                   &(This->buflen),&(This->buffer),
256                                                                   (LPVOID)&(This->hwbuf));
257                                 if (err != DS_OK)
258                                         WARN("IDsDriver_CreateSoundBuffer failed\n");
259                         } else {
260                                 WARN("waveOutOpen failed\n");
261                         }
262                 } else if (err != DS_OK) {
263                         WARN("IDsDriverBuffer_Stop failed\n");
264                 }
265         } else {
266                 err = mmErr(waveOutPause(This->hwo));
267                 if (err != DS_OK)
268                         WARN("waveOutPause failed\n");
269         }
270         return err;
271 }
272
273 HRESULT DSOUND_PrimaryGetPosition(IDirectSoundImpl *This, LPDWORD playpos, LPDWORD writepos)
274 {
275         TRACE("(%p,%p,%p)\n",This,playpos,writepos);
276
277         if (This->hwbuf) {
278                 HRESULT err=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);
279                 if (err) {
280                         WARN("IDsDriverBuffer_GetPosition failed\n");
281                         return err;
282                 }
283         }
284         else {
285                 if (playpos) {
286                         MMTIME mtime;
287                         mtime.wType = TIME_BYTES;
288                         waveOutGetPosition(This->hwo, &mtime, sizeof(mtime));
289                         mtime.u.cb = mtime.u.cb % This->buflen;
290                         *playpos = mtime.u.cb;
291                 }
292                 if (writepos) {
293                         /* the writepos should only be used by apps with WRITEPRIMARY priority,
294                          * in which case our software mixer is disabled anyway */
295                         *writepos = (This->pwplay + ds_hel_margin) * This->fraglen;
296                         while (*writepos >= This->buflen)
297                                 *writepos -= This->buflen;
298                 }
299         }
300         TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
301         return DS_OK;
302 }
303
304
305 /*******************************************************************************
306  *              IDirectSoundBuffer
307  */
308 /* This sets this format for the <em>Primary Buffer Only</em> */
309 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
310 static HRESULT WINAPI PrimaryBufferImpl_SetFormat(
311         LPDIRECTSOUNDBUFFER8 iface,LPWAVEFORMATEX wfex
312 ) {
313         ICOM_THIS(PrimaryBufferImpl,iface);
314         IDirectSoundImpl* dsound = This->dsound;
315         IDirectSoundBufferImpl** dsb;
316         HRESULT err = DS_OK;
317         int                     i;
318         TRACE("(%p,%p)\n",This,wfex);
319
320         if (This->dsound->priolevel == DSSCL_NORMAL) {
321                 WARN("failed priority check!\n");
322                 return DSERR_PRIOLEVELNEEDED;
323         }
324
325         /* Let's be pedantic! */
326         if (wfex == NULL) {
327                 WARN("invalid parameter: wfex==NULL!\n");
328                 return DSERR_INVALIDPARAM;
329         }
330         TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
331               "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
332               wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
333               wfex->nAvgBytesPerSec, wfex->nBlockAlign,
334               wfex->wBitsPerSample, wfex->cbSize);
335
336         if ((wfex->wFormatTag != WAVE_FORMAT_PCM) ||
337             (wfex->nChannels < 1) || (wfex->nChannels > 2) ||
338             (wfex->nSamplesPerSec < 1) ||
339             ((wfex->wBitsPerSample != 8) && (wfex->wBitsPerSample != 16))) {
340                 WARN("invalid paramemer: unsupported format!\n");
341                 return DSERR_INVALIDPARAM;
342         }
343
344         /* **** */
345         RtlAcquireResourceExclusive(&(dsound->lock), TRUE);
346
347         if (dsound->wfx.nSamplesPerSec != wfex->nSamplesPerSec) {
348                 dsb = dsound->buffers;
349                 for (i = 0; i < dsound->nrofbuffers; i++, dsb++) {
350                         /* **** */
351                         EnterCriticalSection(&((*dsb)->lock));
352
353                         (*dsb)->freqAdjust = ((*dsb)->freq << DSOUND_FREQSHIFT) /
354                                 wfex->nSamplesPerSec;
355
356                         LeaveCriticalSection(&((*dsb)->lock));
357                         /* **** */
358                 }
359         }
360
361         dsound->wfx.nSamplesPerSec = wfex->nSamplesPerSec;
362         dsound->wfx.nChannels = wfex->nChannels;
363         dsound->wfx.wBitsPerSample = wfex->wBitsPerSample;
364         dsound->wfx.nBlockAlign = dsound->wfx.wBitsPerSample / 8 * dsound->wfx.nChannels;
365         dsound->wfx.nAvgBytesPerSec =
366                 dsound->wfx.nSamplesPerSec * dsound->wfx.nBlockAlign;
367
368         if (dsound->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) {
369                 DWORD flags = CALLBACK_FUNCTION;
370                 if (ds_hw_accel != DS_HW_ACCEL_EMULATION)
371                         flags |= WAVE_DIRECTSOUND;
372                 /* FIXME: check for errors */
373                 DSOUND_PrimaryClose(dsound);
374                 waveOutClose(dsound->hwo);
375                 dsound->hwo = 0;
376                 err = mmErr(waveOutOpen(&(dsound->hwo), dsound->drvdesc.dnDevNode,
377                                         &(dsound->wfx), (DWORD)DSOUND_callback, (DWORD)dsound,
378                                         flags));
379                 if (err == DS_OK) {
380                     err = DSOUND_PrimaryOpen(dsound);
381                     if (err != DS_OK) {
382                             WARN("DSOUND_PrimaryOpen failed\n");
383                             RtlReleaseResource(&(dsound->lock));
384                             return err;
385                     }
386                 } else {
387                         WARN("waveOutOpen failed\n");
388                         RtlReleaseResource(&(dsound->lock));
389                         return err;
390                 }
391         }
392         if (dsound->hwbuf) {
393                 err = IDsDriverBuffer_SetFormat(dsound->hwbuf, &(dsound->wfx));
394                 if (err == DSERR_BUFFERLOST) {
395                         /* Wine-only: the driver wants us to recreate the HW buffer */
396                         IDsDriverBuffer_Release(dsound->hwbuf);
397                         err = IDsDriver_CreateSoundBuffer(dsound->driver,&(dsound->wfx),
398                                                           DSBCAPS_PRIMARYBUFFER,0,
399                                                           &(dsound->buflen),&(dsound->buffer),
400                                                           (LPVOID)&(dsound->hwbuf));
401                         if (err != DS_OK) {
402                                 WARN("IDsDriver_CreateSoundBuffer failed\n");
403                                 RtlReleaseResource(&(dsound->lock));
404                                 return err;
405                         }
406                         if (dsound->state == STATE_PLAYING) dsound->state = STATE_STARTING;
407                         else if (dsound->state == STATE_STOPPING) dsound->state = STATE_STOPPED;
408                 } else {
409                         WARN("IDsDriverBuffer_SetFormat failed\n");
410                         RtlReleaseResource(&(dsound->lock));
411                         return err;
412                 }
413                 /* FIXME: should we set err back to DS_OK in all cases ? */
414         }
415         DSOUND_RecalcPrimary(dsound);
416
417         RtlReleaseResource(&(dsound->lock));
418         /* **** */
419
420         return err;
421 }
422
423 static HRESULT WINAPI PrimaryBufferImpl_SetVolume(
424         LPDIRECTSOUNDBUFFER8 iface,LONG vol
425 ) {
426         ICOM_THIS(PrimaryBufferImpl,iface);
427         IDirectSoundImpl* dsound = This->dsound;
428         LONG oldVol;
429
430         TRACE("(%p,%ld)\n",This,vol);
431
432         /* I'm not sure if we need this for primary buffer */
433         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
434                 WARN("control unavailable\n");
435                 return DSERR_CONTROLUNAVAIL;
436         }
437
438         if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) {
439                 WARN("invalid parameter: vol = %ld\n", vol);
440                 return DSERR_INVALIDPARAM;
441         }
442
443         /* **** */
444         EnterCriticalSection(&(dsound->mixlock));
445
446         oldVol = dsound->volpan.lVolume;
447         dsound->volpan.lVolume = vol;
448         DSOUND_RecalcVolPan(&dsound->volpan);
449
450         if (vol != oldVol) {
451                 if (dsound->hwbuf) {
452                         HRESULT hres;
453                         hres = IDsDriverBuffer_SetVolumePan(dsound->hwbuf, &(dsound->volpan));
454                         if (hres != DS_OK) {
455                                 LeaveCriticalSection(&(dsound->mixlock));
456                                 WARN("IDsDriverBuffer_SetVolumePan failed\n");
457                                 return hres;
458                         }
459                 }
460                 else {
461 #if 0 /* should we really do this? */
462                         /* the DS volume ranges from 0 (max, 0dB attenuation) to -10000 (min, 100dB attenuation) */
463                         /* the MM volume ranges from 0 to 0xffff in an unspecified logarithmic scale */
464                         WORD cvol = 0xffff + vol*6 + vol/2;
465                         DWORD vol = cvol | ((DWORD)cvol << 16)
466                         waveOutSetVolume(dsound->hwo, vol);
467 #endif
468                 }
469         }
470
471         LeaveCriticalSection(&(dsound->mixlock));
472         /* **** */
473
474         return DS_OK;
475 }
476
477 static HRESULT WINAPI PrimaryBufferImpl_GetVolume(
478         LPDIRECTSOUNDBUFFER8 iface,LPLONG vol
479 ) {
480         ICOM_THIS(PrimaryBufferImpl,iface);
481         TRACE("(%p,%p)\n",This,vol);
482
483         if (vol == NULL) {
484                 WARN("invalid parameter: vol = NULL\n");
485                 return DSERR_INVALIDPARAM;
486         }
487
488         *vol = This->dsound->volpan.lVolume;
489         return DS_OK;
490 }
491
492 static HRESULT WINAPI PrimaryBufferImpl_SetFrequency(
493         LPDIRECTSOUNDBUFFER8 iface,DWORD freq
494 ) {
495         ICOM_THIS(PrimaryBufferImpl,iface);
496
497         TRACE("(%p,%ld)\n",This,freq);
498
499         /* You cannot set the frequency of the primary buffer */
500         WARN("control unavailable\n");
501         return DSERR_CONTROLUNAVAIL;
502 }
503
504 static HRESULT WINAPI PrimaryBufferImpl_Play(
505         LPDIRECTSOUNDBUFFER8 iface,DWORD reserved1,DWORD reserved2,DWORD flags
506 ) {
507         ICOM_THIS(PrimaryBufferImpl,iface);
508         IDirectSoundImpl* dsound = This->dsound;
509
510         TRACE("(%p,%08lx,%08lx,%08lx)\n",
511                 This,reserved1,reserved2,flags
512         );
513
514         if (!(flags & DSBPLAY_LOOPING)) {
515                 WARN("invalid parameter: flags = %08lx\n", flags);
516                 return DSERR_INVALIDPARAM;
517         }
518
519         /* **** */
520         EnterCriticalSection(&(dsound->mixlock));
521
522         if (dsound->state == STATE_STOPPED)
523                 dsound->state = STATE_STARTING;
524         else if (dsound->state == STATE_STOPPING)
525                 dsound->state = STATE_PLAYING;
526
527         LeaveCriticalSection(&(dsound->mixlock));
528         /* **** */
529
530         return DS_OK;
531 }
532
533 static HRESULT WINAPI PrimaryBufferImpl_Stop(LPDIRECTSOUNDBUFFER8 iface)
534 {
535         ICOM_THIS(PrimaryBufferImpl,iface);
536         IDirectSoundImpl* dsound = This->dsound;
537
538         TRACE("(%p)\n",This);
539
540         /* **** */
541         EnterCriticalSection(&(dsound->mixlock));
542
543         if (dsound->state == STATE_PLAYING)
544                 dsound->state = STATE_STOPPING;
545         else if (dsound->state == STATE_STARTING)
546                 dsound->state = STATE_STOPPED;
547
548         LeaveCriticalSection(&(dsound->mixlock));
549         /* **** */
550
551         return DS_OK;
552 }
553
554 static DWORD WINAPI PrimaryBufferImpl_AddRef(LPDIRECTSOUNDBUFFER8 iface) {
555         ICOM_THIS(PrimaryBufferImpl,iface);
556         DWORD ref;
557
558         TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
559
560         ref = InterlockedIncrement(&(This->ref));
561         if (!ref) {
562                 FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n");
563         }
564         return ref;
565 }
566 static DWORD WINAPI PrimaryBufferImpl_Release(LPDIRECTSOUNDBUFFER8 iface) {
567         ICOM_THIS(PrimaryBufferImpl,iface);
568         DWORD ref;
569
570         TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
571
572         ref = InterlockedDecrement(&(This->ref));
573
574         /* the listener has a reference to us which must be removed */
575         if ((ref == 0) || ((ref == 1) && (This->dsound->listener))) {
576                 IDirectSound_Release((LPDIRECTSOUND)This->dsound);
577
578                 if (This->dsound->listener) {
579                         IDirectSound3DListener_Release((LPDIRECTSOUND3DLISTENER)This->dsound->listener);
580                         This->dsound->listener = NULL;
581                         ref--;
582                 }
583 #if 0
584                 if (This->iks) {
585                         HeapFree(GetProcessHeap(), 0, This->iks);
586                 }
587 #endif
588                 HeapFree(GetProcessHeap(),0,This);
589         }
590
591         return ref;
592 }
593
594 static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(
595         LPDIRECTSOUNDBUFFER8 iface,LPDWORD playpos,LPDWORD writepos
596 ) {
597         HRESULT hres;
598         ICOM_THIS(PrimaryBufferImpl,iface);
599         IDirectSoundImpl* dsound = This->dsound;
600
601         TRACE("(%p,%p,%p)\n",This,playpos,writepos);
602         hres = DSOUND_PrimaryGetPosition(dsound, playpos, writepos);
603         if (hres != DS_OK) {
604                 WARN("DSOUND_PrimaryGetPosition failed\n");
605                 return hres;
606         }
607         if (writepos) {
608                 if (dsound->state != STATE_STOPPED)
609                         /* apply the documented 10ms lead to writepos */
610                         *writepos += dsound->writelead;
611                 while (*writepos >= dsound->buflen) *writepos -= dsound->buflen;
612         }
613         TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
614         return DS_OK;
615 }
616
617 static HRESULT WINAPI PrimaryBufferImpl_GetStatus(
618         LPDIRECTSOUNDBUFFER8 iface,LPDWORD status
619 ) {
620         ICOM_THIS(PrimaryBufferImpl,iface);
621         TRACE("(%p,%p), thread is %lx\n",This,status,GetCurrentThreadId());
622
623         if (status == NULL) {
624                 WARN("invalid parameter: status == NULL\n");
625                 return DSERR_INVALIDPARAM;
626         }
627
628         *status = 0;
629         if ((This->dsound->state == STATE_STARTING) ||
630             (This->dsound->state == STATE_PLAYING))
631                 *status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING;
632
633         TRACE("status=%lx\n", *status);
634         return DS_OK;
635 }
636
637
638 static HRESULT WINAPI PrimaryBufferImpl_GetFormat(
639         LPDIRECTSOUNDBUFFER8 iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
640 ) {
641         ICOM_THIS(PrimaryBufferImpl,iface);
642         TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
643
644         if (wfsize>sizeof(This->dsound->wfx))
645                 wfsize = sizeof(This->dsound->wfx);
646         if (lpwf) {     /* NULL is valid */
647                 memcpy(lpwf,&(This->dsound->wfx),wfsize);
648                 if (wfwritten)
649                         *wfwritten = wfsize;
650         } else {
651                 if (wfwritten)
652                         *wfwritten = sizeof(This->dsound->wfx);
653                 else {
654                         WARN("invalid parameter: wfwritten == NULL\n");
655                         return DSERR_INVALIDPARAM;
656                 }
657         }
658
659         return DS_OK;
660 }
661
662 static HRESULT WINAPI PrimaryBufferImpl_Lock(
663         LPDIRECTSOUNDBUFFER8 iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
664 ) {
665         ICOM_THIS(PrimaryBufferImpl,iface);
666         IDirectSoundImpl* dsound = This->dsound;
667
668         TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx) at %ld\n",
669                 This,
670                 writecursor,
671                 writebytes,
672                 lplpaudioptr1,
673                 audiobytes1,
674                 lplpaudioptr2,
675                 audiobytes2,
676                 flags,
677                 GetTickCount()
678         );
679
680         if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
681                 WARN("failed priority check!\n");
682                 return DSERR_PRIOLEVELNEEDED;
683         }
684
685         if (flags & DSBLOCK_FROMWRITECURSOR) {
686                 DWORD writepos;
687                 HRESULT hres;
688                 /* GetCurrentPosition does too much magic to duplicate here */
689                 hres = IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writepos);
690                 if (hres != DS_OK) {
691                         WARN("IDirectSoundBuffer_GetCurrentPosition failed\n");
692                         return hres;
693                 }
694                 writecursor += writepos;
695         }
696         while (writecursor >= dsound->buflen)
697                 writecursor -= dsound->buflen;
698         if (flags & DSBLOCK_ENTIREBUFFER)
699                 writebytes = dsound->buflen;
700         if (writebytes > dsound->buflen)
701                 writebytes = dsound->buflen;
702
703         assert(audiobytes1!=audiobytes2);
704         assert(lplpaudioptr1!=lplpaudioptr2);
705
706         if (!(dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && dsound->hwbuf) {
707                 HRESULT hres;
708                 hres = IDsDriverBuffer_Lock(dsound->hwbuf,
709                                             lplpaudioptr1, audiobytes1,
710                                             lplpaudioptr2, audiobytes2,
711                                             writecursor, writebytes,
712                                             0);
713                 if (hres != DS_OK) {
714                         WARN("IDsDriverBuffer_Lock failed\n");
715                         return hres;
716                 }
717         } else {
718                 if (writecursor+writebytes <= dsound->buflen) {
719                         *(LPBYTE*)lplpaudioptr1 = dsound->buffer+writecursor;
720                         *audiobytes1 = writebytes;
721                         if (lplpaudioptr2)
722                                 *(LPBYTE*)lplpaudioptr2 = NULL;
723                         if (audiobytes2)
724                                 *audiobytes2 = 0;
725                         TRACE("->%ld.0\n",writebytes);
726                 } else {
727                         *(LPBYTE*)lplpaudioptr1 = dsound->buffer+writecursor;
728                         *audiobytes1 = dsound->buflen-writecursor;
729                         if (lplpaudioptr2)
730                                 *(LPBYTE*)lplpaudioptr2 = dsound->buffer;
731                         if (audiobytes2)
732                                 *audiobytes2 = writebytes-(dsound->buflen-writecursor);
733                         TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
734                 }
735         }
736         return DS_OK;
737 }
738
739 static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition(
740         LPDIRECTSOUNDBUFFER8 iface,DWORD newpos
741 ) {
742         ICOM_THIS(PrimaryBufferImpl,iface);
743         TRACE("(%p,%ld)\n",This,newpos);
744
745         /* You cannot set the position of the primary buffer */
746         WARN("invalid call\n");
747         return DSERR_INVALIDCALL;
748 }
749
750 static HRESULT WINAPI PrimaryBufferImpl_SetPan(
751         LPDIRECTSOUNDBUFFER8 iface,LONG pan
752 ) {
753         ICOM_THIS(PrimaryBufferImpl,iface);
754         TRACE("(%p,%ld)\n",This,pan);
755
756         /* You cannot set the pan of the primary buffer */
757         WARN("control unavailable\n");
758         return DSERR_CONTROLUNAVAIL;
759 }
760
761 static HRESULT WINAPI PrimaryBufferImpl_GetPan(
762         LPDIRECTSOUNDBUFFER8 iface,LPLONG pan
763 ) {
764         ICOM_THIS(PrimaryBufferImpl,iface);
765         TRACE("(%p,%p)\n",This,pan);
766
767         if (pan == NULL) {
768                 WARN("invalid parameter: pan == NULL\n");
769                 return DSERR_INVALIDPARAM;
770         }
771
772         *pan = This->dsound->volpan.lPan;
773
774         return DS_OK;
775 }
776
777 static HRESULT WINAPI PrimaryBufferImpl_Unlock(
778         LPDIRECTSOUNDBUFFER8 iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
779 ) {
780         ICOM_THIS(PrimaryBufferImpl,iface);
781         IDirectSoundImpl* dsound = This->dsound;
782
783         TRACE("(%p,%p,%ld,%p,%ld):stub\n", This,p1,x1,p2,x2);
784
785         if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
786                 WARN("failed priority check!\n");
787                 return DSERR_PRIOLEVELNEEDED;
788         }
789
790         if (!(dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && dsound->hwbuf) {
791                 HRESULT hres;
792                 
793                 hres = IDsDriverBuffer_Unlock(dsound->hwbuf, p1, x1, p2, x2);
794                 if (hres != DS_OK) {
795                         WARN("IDsDriverBuffer_Unlock failed\n");
796                         return hres;
797                 }
798         }
799
800         return DS_OK;
801 }
802
803 static HRESULT WINAPI PrimaryBufferImpl_Restore(
804         LPDIRECTSOUNDBUFFER8 iface
805 ) {
806         ICOM_THIS(PrimaryBufferImpl,iface);
807         FIXME("(%p):stub\n",This);
808         return DS_OK;
809 }
810
811 static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(
812         LPDIRECTSOUNDBUFFER8 iface,LPDWORD freq
813 ) {
814         ICOM_THIS(PrimaryBufferImpl,iface);
815         TRACE("(%p,%p)\n",This,freq);
816
817         if (freq == NULL) {
818                 WARN("invalid parameter: freq == NULL\n");
819                 return DSERR_INVALIDPARAM;
820         }
821
822         *freq = This->dsound->wfx.nSamplesPerSec;
823         TRACE("-> %ld\n", *freq);
824
825         return DS_OK;
826 }
827
828 static HRESULT WINAPI PrimaryBufferImpl_SetFX(
829         LPDIRECTSOUNDBUFFER8 iface,DWORD dwEffectsCount,LPDSEFFECTDESC pDSFXDesc,LPDWORD pdwResultCodes
830 ) {
831         ICOM_THIS(PrimaryBufferImpl,iface);
832         DWORD u;
833
834         FIXME("(%p,%lu,%p,%p): stub\n",This,dwEffectsCount,pDSFXDesc,pdwResultCodes);
835
836         if (pdwResultCodes)
837                 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
838
839         WARN("control unavailable\n");
840         return DSERR_CONTROLUNAVAIL;
841 }
842
843 static HRESULT WINAPI PrimaryBufferImpl_AcquireResources(
844         LPDIRECTSOUNDBUFFER8 iface,DWORD dwFlags,DWORD dwEffectsCount,LPDWORD pdwResultCodes
845 ) {
846         ICOM_THIS(PrimaryBufferImpl,iface);
847         DWORD u;
848
849         FIXME("(%p,%08lu,%lu,%p): stub\n",This,dwFlags,dwEffectsCount,pdwResultCodes);
850
851         if (pdwResultCodes)
852                 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
853
854         WARN("control unavailable\n");
855         return DSERR_CONTROLUNAVAIL;
856 }
857
858 static HRESULT WINAPI PrimaryBufferImpl_GetObjectInPath(
859         LPDIRECTSOUNDBUFFER8 iface,REFGUID rguidObject,DWORD dwIndex,REFGUID rguidInterface,LPVOID* ppObject
860 ) {
861         ICOM_THIS(PrimaryBufferImpl,iface);
862
863         FIXME("(%p,%s,%lu,%s,%p): stub\n",This,debugstr_guid(rguidObject),dwIndex,debugstr_guid(rguidInterface),ppObject);
864
865         WARN("control unavailable\n");
866         return DSERR_CONTROLUNAVAIL;
867 }
868
869 static HRESULT WINAPI PrimaryBufferImpl_Initialize(
870         LPDIRECTSOUNDBUFFER8 iface,LPDIRECTSOUND8 dsound,LPDSBUFFERDESC dbsd
871 ) {
872         ICOM_THIS(PrimaryBufferImpl,iface);
873         FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
874         DPRINTF("Re-Init!!!\n");
875         WARN("already initialized\n");
876         return DSERR_ALREADYINITIALIZED;
877 }
878
879 static HRESULT WINAPI PrimaryBufferImpl_GetCaps(
880         LPDIRECTSOUNDBUFFER8 iface,LPDSBCAPS caps
881 ) {
882         ICOM_THIS(PrimaryBufferImpl,iface);
883         TRACE("(%p)->(%p)\n",This,caps);
884
885         if (caps == NULL) {
886                 WARN("invalid parameter: caps == NULL\n");
887                 return DSERR_INVALIDPARAM;
888         }
889
890         if (caps->dwSize < sizeof(*caps)) {
891                 WARN("invalid parameter: caps->dwSize = %ld: < %d\n", caps->dwSize, sizeof(*caps));
892                 return DSERR_INVALIDPARAM;
893         }
894
895         caps->dwFlags = This->dsbd.dwFlags;
896         if (This->dsound->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
897         else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
898
899         caps->dwBufferBytes = This->dsound->buflen;
900
901         /* This value represents the speed of the "unlock" command.
902            As unlock is quite fast (it does not do anything), I put
903            4096 ko/s = 4 Mo / s */
904         /* FIXME: hwbuf speed */
905         caps->dwUnlockTransferRate = 4096;
906         caps->dwPlayCpuOverhead = 0;
907
908         return DS_OK;
909 }
910
911 static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(
912         LPDIRECTSOUNDBUFFER8 iface,REFIID riid,LPVOID *ppobj
913 ) {
914         ICOM_THIS(PrimaryBufferImpl,iface);
915
916         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
917
918         if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
919                 ERR("app requested IDirectSoundNotify on primary buffer\n");
920                 /* FIXME: should we support this? */
921                 *ppobj = NULL;
922                 return E_FAIL;
923         }
924
925         if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
926                 ERR("app requested IDirectSound3DBuffer on primary buffer\n");
927                 *ppobj = NULL;
928                 return E_NOINTERFACE;
929         }
930
931         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
932                 if (!This->dsound->listener)
933                         IDirectSound3DListenerImpl_Create(This, &This->dsound->listener);
934                 *ppobj = This->dsound->listener;
935                 if (This->dsound->listener) {
936                         IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
937                         return DS_OK;
938                 }
939                 WARN("IID_IDirectSound3DListener failed\n");
940                 *ppobj = NULL;
941                 return E_FAIL;
942         }
943
944         if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
945 #if 0
946                 if (!This->iks)
947                         IKsPropertySetImpl_Create(This, &This->iks);
948                 *ppobj = This->iks;
949                 if (*ppobj) {
950                         IKsPropertySet_AddRef((LPKSPROPERTYSET)*ppobj);
951                         return S_OK;
952                 }
953                 return E_FAIL;
954 #else
955                 FIXME("app requested IKsPropertySet on primary buffer\n");
956                 *ppobj = NULL;
957                 return E_FAIL;
958 #endif
959         }
960
961         FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
962
963         *ppobj = NULL;
964
965         return E_NOINTERFACE;
966 }
967
968 static ICOM_VTABLE(IDirectSoundBuffer8) dspbvt =
969 {
970         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
971         PrimaryBufferImpl_QueryInterface,
972         PrimaryBufferImpl_AddRef,
973         PrimaryBufferImpl_Release,
974         PrimaryBufferImpl_GetCaps,
975         PrimaryBufferImpl_GetCurrentPosition,
976         PrimaryBufferImpl_GetFormat,
977         PrimaryBufferImpl_GetVolume,
978         PrimaryBufferImpl_GetPan,
979         PrimaryBufferImpl_GetFrequency,
980         PrimaryBufferImpl_GetStatus,
981         PrimaryBufferImpl_Initialize,
982         PrimaryBufferImpl_Lock,
983         PrimaryBufferImpl_Play,
984         PrimaryBufferImpl_SetCurrentPosition,
985         PrimaryBufferImpl_SetFormat,
986         PrimaryBufferImpl_SetVolume,
987         PrimaryBufferImpl_SetPan,
988         PrimaryBufferImpl_SetFrequency,
989         PrimaryBufferImpl_Stop,
990         PrimaryBufferImpl_Unlock,
991         PrimaryBufferImpl_Restore,
992         PrimaryBufferImpl_SetFX,
993         PrimaryBufferImpl_AcquireResources,
994         PrimaryBufferImpl_GetObjectInPath
995 };
996
997 HRESULT WINAPI PrimaryBuffer_Create(
998         IDirectSoundImpl *This,
999         PrimaryBufferImpl **pdsb,
1000         LPDSBUFFERDESC dsbd)
1001 {
1002         PrimaryBufferImpl *dsb;
1003
1004         TRACE("%p,%p,%p)\n",This,pdsb,dsbd);
1005
1006         if (dsbd->lpwfxFormat) {
1007                 WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n");
1008                 *pdsb = NULL;
1009                 return DSERR_INVALIDPARAM;
1010         }
1011
1012         dsb = (PrimaryBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1013
1014         if (dsb == NULL) {
1015                 WARN("out of memory\n");
1016                 *pdsb = NULL;
1017                 return DSERR_OUTOFMEMORY;
1018         }
1019
1020         dsb->ref = 1;
1021         dsb->dsound = This;
1022         dsb->lpVtbl = &dspbvt;
1023
1024         memcpy(&dsb->dsbd, dsbd, sizeof(*dsbd));
1025
1026         TRACE("Created primary buffer at %p\n", dsb);
1027         TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
1028                 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1029                 This->wfx.wFormatTag, This->wfx.nChannels, This->wfx.nSamplesPerSec,
1030                 This->wfx.nAvgBytesPerSec, This->wfx.nBlockAlign,
1031                 This->wfx.wBitsPerSample, This->wfx.cbSize);
1032
1033         if (dsbd->dwFlags & DSBCAPS_CTRL3D) {
1034                 HRESULT hres;
1035                 hres = IDirectSound3DListenerImpl_Create(dsb, &This->listener);
1036                 if (hres != DS_OK) {
1037                         WARN("IDirectSound3DListenerImpl_Create failed\n");
1038                 } else {
1039                         IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)This->listener);
1040                 }
1041         }
1042
1043         IDirectSound8_AddRef((LPDIRECTSOUND8)This);
1044
1045         *pdsb = dsb;
1046         return S_OK;
1047 }