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