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