Use system metrics values in TOOLBAR_DrawPattern instead of hardcoded
[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                 HeapFree(GetProcessHeap(),0,This);
605                 TRACE("(%p) released\n",This);
606         }
607
608         return ref;
609 }
610
611 static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(
612         LPDIRECTSOUNDBUFFER8 iface,LPDWORD playpos,LPDWORD writepos
613 ) {
614         HRESULT hres;
615         ICOM_THIS(PrimaryBufferImpl,iface);
616         IDirectSoundImpl* dsound = This->dsound;
617
618         TRACE("(%p,%p,%p)\n",This,playpos,writepos);
619         hres = DSOUND_PrimaryGetPosition(dsound, playpos, writepos);
620         if (hres != DS_OK) {
621                 WARN("DSOUND_PrimaryGetPosition failed\n");
622                 return hres;
623         }
624         if (writepos) {
625                 if (dsound->state != STATE_STOPPED)
626                         /* apply the documented 10ms lead to writepos */
627                         *writepos += dsound->writelead;
628                 while (*writepos >= dsound->buflen) *writepos -= dsound->buflen;
629         }
630         TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
631         return DS_OK;
632 }
633
634 static HRESULT WINAPI PrimaryBufferImpl_GetStatus(
635         LPDIRECTSOUNDBUFFER8 iface,LPDWORD status
636 ) {
637         ICOM_THIS(PrimaryBufferImpl,iface);
638         TRACE("(%p,%p), thread is %04lx\n",This,status,GetCurrentThreadId());
639
640         if (status == NULL) {
641                 WARN("invalid parameter: status == NULL\n");
642                 return DSERR_INVALIDPARAM;
643         }
644
645         *status = 0;
646         if ((This->dsound->state == STATE_STARTING) ||
647             (This->dsound->state == STATE_PLAYING))
648                 *status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING;
649
650         TRACE("status=%lx\n", *status);
651         return DS_OK;
652 }
653
654
655 static HRESULT WINAPI PrimaryBufferImpl_GetFormat(
656         LPDIRECTSOUNDBUFFER8 iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
657 ) {
658         ICOM_THIS(PrimaryBufferImpl,iface);
659         TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
660
661         if (wfsize>sizeof(This->dsound->wfx))
662                 wfsize = sizeof(This->dsound->wfx);
663         if (lpwf) {     /* NULL is valid */
664                 memcpy(lpwf,&(This->dsound->wfx),wfsize);
665                 if (wfwritten)
666                         *wfwritten = wfsize;
667         } else {
668                 if (wfwritten)
669                         *wfwritten = sizeof(This->dsound->wfx);
670                 else {
671                         WARN("invalid parameter: wfwritten == NULL\n");
672                         return DSERR_INVALIDPARAM;
673                 }
674         }
675
676         return DS_OK;
677 }
678
679 static HRESULT WINAPI PrimaryBufferImpl_Lock(
680         LPDIRECTSOUNDBUFFER8 iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
681 ) {
682         ICOM_THIS(PrimaryBufferImpl,iface);
683         IDirectSoundImpl* dsound = This->dsound;
684
685         TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx) at %ld\n",
686                 This,
687                 writecursor,
688                 writebytes,
689                 lplpaudioptr1,
690                 audiobytes1,
691                 lplpaudioptr2,
692                 audiobytes2,
693                 flags,
694                 GetTickCount()
695         );
696
697         if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
698                 WARN("failed priority check!\n");
699                 return DSERR_PRIOLEVELNEEDED;
700         }
701
702         if (flags & DSBLOCK_FROMWRITECURSOR) {
703                 DWORD writepos;
704                 HRESULT hres;
705                 /* GetCurrentPosition does too much magic to duplicate here */
706                 hres = IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writepos);
707                 if (hres != DS_OK) {
708                         WARN("IDirectSoundBuffer_GetCurrentPosition failed\n");
709                         return hres;
710                 }
711                 writecursor += writepos;
712         }
713         while (writecursor >= dsound->buflen)
714                 writecursor -= dsound->buflen;
715         if (flags & DSBLOCK_ENTIREBUFFER)
716                 writebytes = dsound->buflen;
717         if (writebytes > dsound->buflen)
718                 writebytes = dsound->buflen;
719
720         assert(audiobytes1!=audiobytes2);
721         assert(lplpaudioptr1!=lplpaudioptr2);
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         LONG oldPan;
773
774         TRACE("(%p,%ld)\n",This,pan);
775
776         if (!(This->dsound->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
777                 WARN("control unavailable\n");
778                 return DSERR_CONTROLUNAVAIL;
779         }
780
781         if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) {
782                 WARN("invalid parameter: pan = %ld\n", pan);
783                 return DSERR_INVALIDPARAM;
784         }
785
786         /* **** */
787         EnterCriticalSection(&(dsound->mixlock));
788
789         oldPan = dsound->volpan.lPan;
790         dsound->volpan.lPan = pan;
791         DSOUND_RecalcVolPan(&dsound->volpan);
792
793         if (pan != oldPan) {
794                 if (dsound->hwbuf) {
795                         HRESULT hres;
796                         hres = IDsDriverBuffer_SetVolumePan(dsound->hwbuf, &(dsound->volpan));
797                         if (hres != DS_OK) {
798                                 LeaveCriticalSection(&(dsound->mixlock));
799                                 WARN("IDsDriverBuffer_SetVolumePan failed\n");
800                                 return hres;
801                         }
802                 }
803                 else {
804                         DWORD vol = (dsound->volpan.dwTotalLeftAmpFactor & 0xffff) | (dsound->volpan.dwTotalRightAmpFactor << 16);
805                         waveOutSetVolume(dsound->hwo, vol);
806                 }
807         }
808
809         LeaveCriticalSection(&(dsound->mixlock));
810         /* **** */
811
812         return DS_OK;
813 }
814
815 static HRESULT WINAPI PrimaryBufferImpl_GetPan(
816         LPDIRECTSOUNDBUFFER8 iface,LPLONG pan
817 ) {
818         ICOM_THIS(PrimaryBufferImpl,iface);
819         TRACE("(%p,%p)\n",This,pan);
820
821         if (!(This->dsound->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
822                 WARN("control unavailable\n");
823                 return DSERR_CONTROLUNAVAIL;
824         }
825
826         if (pan == NULL) {
827                 WARN("invalid parameter: pan == NULL\n");
828                 return DSERR_INVALIDPARAM;
829         }
830
831         *pan = This->dsound->volpan.lPan;
832
833         return DS_OK;
834 }
835
836 static HRESULT WINAPI PrimaryBufferImpl_Unlock(
837         LPDIRECTSOUNDBUFFER8 iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
838 ) {
839         ICOM_THIS(PrimaryBufferImpl,iface);
840         IDirectSoundImpl* dsound = This->dsound;
841
842         TRACE("(%p,%p,%ld,%p,%ld)\n", This,p1,x1,p2,x2);
843
844         if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
845                 WARN("failed priority check!\n");
846                 return DSERR_PRIOLEVELNEEDED;
847         }
848
849         if (!(dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && dsound->hwbuf) {
850                 HRESULT hres;
851                 
852                 hres = IDsDriverBuffer_Unlock(dsound->hwbuf, p1, x1, p2, x2);
853                 if (hres != DS_OK) {
854                         WARN("IDsDriverBuffer_Unlock failed\n");
855                         return hres;
856                 }
857         }
858
859         return DS_OK;
860 }
861
862 static HRESULT WINAPI PrimaryBufferImpl_Restore(
863         LPDIRECTSOUNDBUFFER8 iface
864 ) {
865         ICOM_THIS(PrimaryBufferImpl,iface);
866         FIXME("(%p):stub\n",This);
867         return DS_OK;
868 }
869
870 static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(
871         LPDIRECTSOUNDBUFFER8 iface,LPDWORD freq
872 ) {
873         ICOM_THIS(PrimaryBufferImpl,iface);
874         TRACE("(%p,%p)\n",This,freq);
875
876         if (freq == NULL) {
877                 WARN("invalid parameter: freq == NULL\n");
878                 return DSERR_INVALIDPARAM;
879         }
880
881         if (!(This->dsound->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
882                 WARN("control unavailable\n");
883                 return DSERR_CONTROLUNAVAIL;
884         }
885
886         *freq = This->dsound->wfx.nSamplesPerSec;
887         TRACE("-> %ld\n", *freq);
888
889         return DS_OK;
890 }
891
892 static HRESULT WINAPI PrimaryBufferImpl_SetFX(
893         LPDIRECTSOUNDBUFFER8 iface,DWORD dwEffectsCount,LPDSEFFECTDESC pDSFXDesc,LPDWORD pdwResultCodes
894 ) {
895         ICOM_THIS(PrimaryBufferImpl,iface);
896         DWORD u;
897
898         FIXME("(%p,%lu,%p,%p): stub\n",This,dwEffectsCount,pDSFXDesc,pdwResultCodes);
899
900         if (pdwResultCodes)
901                 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
902
903         WARN("control unavailable\n");
904         return DSERR_CONTROLUNAVAIL;
905 }
906
907 static HRESULT WINAPI PrimaryBufferImpl_AcquireResources(
908         LPDIRECTSOUNDBUFFER8 iface,DWORD dwFlags,DWORD dwEffectsCount,LPDWORD pdwResultCodes
909 ) {
910         ICOM_THIS(PrimaryBufferImpl,iface);
911         DWORD u;
912
913         FIXME("(%p,%08lu,%lu,%p): stub\n",This,dwFlags,dwEffectsCount,pdwResultCodes);
914
915         if (pdwResultCodes)
916                 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
917
918         WARN("control unavailable\n");
919         return DSERR_CONTROLUNAVAIL;
920 }
921
922 static HRESULT WINAPI PrimaryBufferImpl_GetObjectInPath(
923         LPDIRECTSOUNDBUFFER8 iface,REFGUID rguidObject,DWORD dwIndex,REFGUID rguidInterface,LPVOID* ppObject
924 ) {
925         ICOM_THIS(PrimaryBufferImpl,iface);
926
927         FIXME("(%p,%s,%lu,%s,%p): stub\n",This,debugstr_guid(rguidObject),dwIndex,debugstr_guid(rguidInterface),ppObject);
928
929         WARN("control unavailable\n");
930         return DSERR_CONTROLUNAVAIL;
931 }
932
933 static HRESULT WINAPI PrimaryBufferImpl_Initialize(
934         LPDIRECTSOUNDBUFFER8 iface,LPDIRECTSOUND dsound,LPCDSBUFFERDESC dbsd
935 ) {
936         ICOM_THIS(PrimaryBufferImpl,iface);
937         FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
938         DPRINTF("Re-Init!!!\n");
939         WARN("already initialized\n");
940         return DSERR_ALREADYINITIALIZED;
941 }
942
943 static HRESULT WINAPI PrimaryBufferImpl_GetCaps(
944         LPDIRECTSOUNDBUFFER8 iface,LPDSBCAPS caps
945 ) {
946         ICOM_THIS(PrimaryBufferImpl,iface);
947         TRACE("(%p)->(%p)\n",This,caps);
948
949         if (caps == NULL) {
950                 WARN("invalid parameter: caps == NULL\n");
951                 return DSERR_INVALIDPARAM;
952         }
953
954         if (caps->dwSize < sizeof(*caps)) {
955                 WARN("invalid parameter: caps->dwSize = %ld: < %d\n", caps->dwSize, sizeof(*caps));
956                 return DSERR_INVALIDPARAM;
957         }
958
959         caps->dwFlags = This->dsound->dsbd.dwFlags;
960         if (This->dsound->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
961         else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
962
963         caps->dwBufferBytes = This->dsound->buflen;
964
965         /* This value represents the speed of the "unlock" command.
966            As unlock is quite fast (it does not do anything), I put
967            4096 ko/s = 4 Mo / s */
968         /* FIXME: hwbuf speed */
969         caps->dwUnlockTransferRate = 4096;
970         caps->dwPlayCpuOverhead = 0;
971
972         return DS_OK;
973 }
974
975 static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(
976         LPDIRECTSOUNDBUFFER8 iface,REFIID riid,LPVOID *ppobj
977 ) {
978         ICOM_THIS(PrimaryBufferImpl,iface);
979         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
980
981         if (ppobj == NULL) {
982                 WARN("invalid parameter\n");
983                 return E_INVALIDARG;
984         }
985
986         *ppobj = NULL;  /* assume failure */
987
988         if ( IsEqualGUID(riid, &IID_IUnknown) || 
989              IsEqualGUID(riid, &IID_IDirectSoundBuffer) ) {
990                 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)This);
991                 *ppobj = This;
992                 return S_OK;
993         }
994
995         /* DirectSoundBuffer and DirectSoundBuffer8 are different and */
996         /* a primary buffer can't have a DirectSoundBuffer8 interface */
997         if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) {
998                 WARN("app requested DirectSoundBuffer8 on primary buffer\n");
999                 return E_NOINTERFACE;
1000         }
1001
1002         if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
1003                 ERR("app requested IDirectSoundNotify on primary buffer\n");
1004                 /* FIXME: should we support this? */
1005                 return E_NOINTERFACE;
1006         }
1007
1008         if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1009                 ERR("app requested IDirectSound3DBuffer on primary buffer\n");
1010                 return E_NOINTERFACE;
1011         }
1012
1013         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1014                 if (!This->dsound->listener)
1015                         IDirectSound3DListenerImpl_Create(This, &This->dsound->listener);
1016                 if (This->dsound->listener) {
1017                         *ppobj = This->dsound->listener;
1018                         IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
1019                         return S_OK;
1020                 }
1021
1022                 WARN("IID_IDirectSound3DListener failed\n");
1023                 return E_NOINTERFACE;
1024         }
1025
1026         if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
1027                 FIXME("app requested IKsPropertySet on primary buffer\n");
1028                 return E_NOINTERFACE;
1029         }
1030
1031         FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
1032         return E_NOINTERFACE;
1033 }
1034
1035 static ICOM_VTABLE(IDirectSoundBuffer8) dspbvt =
1036 {
1037         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1038         PrimaryBufferImpl_QueryInterface,
1039         PrimaryBufferImpl_AddRef,
1040         PrimaryBufferImpl_Release,
1041         PrimaryBufferImpl_GetCaps,
1042         PrimaryBufferImpl_GetCurrentPosition,
1043         PrimaryBufferImpl_GetFormat,
1044         PrimaryBufferImpl_GetVolume,
1045         PrimaryBufferImpl_GetPan,
1046         PrimaryBufferImpl_GetFrequency,
1047         PrimaryBufferImpl_GetStatus,
1048         PrimaryBufferImpl_Initialize,
1049         PrimaryBufferImpl_Lock,
1050         PrimaryBufferImpl_Play,
1051         PrimaryBufferImpl_SetCurrentPosition,
1052         PrimaryBufferImpl_SetFormat,
1053         PrimaryBufferImpl_SetVolume,
1054         PrimaryBufferImpl_SetPan,
1055         PrimaryBufferImpl_SetFrequency,
1056         PrimaryBufferImpl_Stop,
1057         PrimaryBufferImpl_Unlock,
1058         PrimaryBufferImpl_Restore,
1059         PrimaryBufferImpl_SetFX,
1060         PrimaryBufferImpl_AcquireResources,
1061         PrimaryBufferImpl_GetObjectInPath
1062 };
1063
1064 HRESULT WINAPI PrimaryBufferImpl_Create(
1065         IDirectSoundImpl *ds,
1066         PrimaryBufferImpl **pdsb,
1067         LPCDSBUFFERDESC dsbd)
1068 {
1069         PrimaryBufferImpl *dsb;
1070
1071         TRACE("%p,%p,%p)\n",ds,pdsb,dsbd);
1072
1073         if (dsbd->lpwfxFormat) {
1074                 WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n");
1075                 *pdsb = NULL;
1076                 return DSERR_INVALIDPARAM;
1077         }
1078
1079         dsb = (PrimaryBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1080
1081         if (dsb == NULL) {
1082                 WARN("out of memory\n");
1083                 *pdsb = NULL;
1084                 return DSERR_OUTOFMEMORY;
1085         }
1086
1087         dsb->ref = 0;
1088         dsb->dsound = ds;
1089         dsb->lpVtbl = &dspbvt;
1090
1091         memcpy(&ds->dsbd, dsbd, sizeof(*dsbd));
1092
1093         TRACE("Created primary buffer at %p\n", dsb);
1094         TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
1095                 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1096                 ds->wfx.wFormatTag, ds->wfx.nChannels, ds->wfx.nSamplesPerSec,
1097                 ds->wfx.nAvgBytesPerSec, ds->wfx.nBlockAlign,
1098                 ds->wfx.wBitsPerSample, ds->wfx.cbSize);
1099
1100         *pdsb = dsb;
1101         return S_OK;
1102 }