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