Fix the case of product and company names.
[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         TRACE("(%p,%ld)\n",This,pan);
749
750         /* You cannot set the pan of the primary buffer */
751         WARN("control unavailable\n");
752         return DSERR_CONTROLUNAVAIL;
753 }
754
755 static HRESULT WINAPI PrimaryBufferImpl_GetPan(
756         LPDIRECTSOUNDBUFFER8 iface,LPLONG pan
757 ) {
758         ICOM_THIS(PrimaryBufferImpl,iface);
759         TRACE("(%p,%p)\n",This,pan);
760
761         if (pan == NULL) {
762                 WARN("invalid parameter: pan == NULL\n");
763                 return DSERR_INVALIDPARAM;
764         }
765
766         *pan = This->dsound->volpan.lPan;
767
768         return DS_OK;
769 }
770
771 static HRESULT WINAPI PrimaryBufferImpl_Unlock(
772         LPDIRECTSOUNDBUFFER8 iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
773 ) {
774         ICOM_THIS(PrimaryBufferImpl,iface);
775         IDirectSoundImpl* dsound = This->dsound;
776
777         TRACE("(%p,%p,%ld,%p,%ld):stub\n", This,p1,x1,p2,x2);
778
779         if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
780                 WARN("failed priority check!\n");
781                 return DSERR_PRIOLEVELNEEDED;
782         }
783
784         if (!(dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && dsound->hwbuf) {
785                 HRESULT hres;
786                 
787                 hres = IDsDriverBuffer_Unlock(dsound->hwbuf, p1, x1, p2, x2);
788                 if (hres != DS_OK) {
789                         WARN("IDsDriverBuffer_Unlock failed\n");
790                         return hres;
791                 }
792         }
793
794         return DS_OK;
795 }
796
797 static HRESULT WINAPI PrimaryBufferImpl_Restore(
798         LPDIRECTSOUNDBUFFER8 iface
799 ) {
800         ICOM_THIS(PrimaryBufferImpl,iface);
801         FIXME("(%p):stub\n",This);
802         return DS_OK;
803 }
804
805 static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(
806         LPDIRECTSOUNDBUFFER8 iface,LPDWORD freq
807 ) {
808         ICOM_THIS(PrimaryBufferImpl,iface);
809         TRACE("(%p,%p)\n",This,freq);
810
811         if (freq == NULL) {
812                 WARN("invalid parameter: freq == NULL\n");
813                 return DSERR_INVALIDPARAM;
814         }
815
816         if (!(This->dsound->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
817                 WARN("control unavailable\n");
818                 return DSERR_CONTROLUNAVAIL;
819         }
820
821         *freq = This->dsound->wfx.nSamplesPerSec;
822         TRACE("-> %ld\n", *freq);
823
824         return DS_OK;
825 }
826
827 static HRESULT WINAPI PrimaryBufferImpl_SetFX(
828         LPDIRECTSOUNDBUFFER8 iface,DWORD dwEffectsCount,LPDSEFFECTDESC pDSFXDesc,LPDWORD pdwResultCodes
829 ) {
830         ICOM_THIS(PrimaryBufferImpl,iface);
831         DWORD u;
832
833         FIXME("(%p,%lu,%p,%p): stub\n",This,dwEffectsCount,pDSFXDesc,pdwResultCodes);
834
835         if (pdwResultCodes)
836                 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
837
838         WARN("control unavailable\n");
839         return DSERR_CONTROLUNAVAIL;
840 }
841
842 static HRESULT WINAPI PrimaryBufferImpl_AcquireResources(
843         LPDIRECTSOUNDBUFFER8 iface,DWORD dwFlags,DWORD dwEffectsCount,LPDWORD pdwResultCodes
844 ) {
845         ICOM_THIS(PrimaryBufferImpl,iface);
846         DWORD u;
847
848         FIXME("(%p,%08lu,%lu,%p): stub\n",This,dwFlags,dwEffectsCount,pdwResultCodes);
849
850         if (pdwResultCodes)
851                 for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN;
852
853         WARN("control unavailable\n");
854         return DSERR_CONTROLUNAVAIL;
855 }
856
857 static HRESULT WINAPI PrimaryBufferImpl_GetObjectInPath(
858         LPDIRECTSOUNDBUFFER8 iface,REFGUID rguidObject,DWORD dwIndex,REFGUID rguidInterface,LPVOID* ppObject
859 ) {
860         ICOM_THIS(PrimaryBufferImpl,iface);
861
862         FIXME("(%p,%s,%lu,%s,%p): stub\n",This,debugstr_guid(rguidObject),dwIndex,debugstr_guid(rguidInterface),ppObject);
863
864         WARN("control unavailable\n");
865         return DSERR_CONTROLUNAVAIL;
866 }
867
868 static HRESULT WINAPI PrimaryBufferImpl_Initialize(
869         LPDIRECTSOUNDBUFFER8 iface,LPDIRECTSOUND8 dsound,LPDSBUFFERDESC dbsd
870 ) {
871         ICOM_THIS(PrimaryBufferImpl,iface);
872         FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
873         DPRINTF("Re-Init!!!\n");
874         WARN("already initialized\n");
875         return DSERR_ALREADYINITIALIZED;
876 }
877
878 static HRESULT WINAPI PrimaryBufferImpl_GetCaps(
879         LPDIRECTSOUNDBUFFER8 iface,LPDSBCAPS caps
880 ) {
881         ICOM_THIS(PrimaryBufferImpl,iface);
882         TRACE("(%p)->(%p)\n",This,caps);
883
884         if (caps == NULL) {
885                 WARN("invalid parameter: caps == NULL\n");
886                 return DSERR_INVALIDPARAM;
887         }
888
889         if (caps->dwSize < sizeof(*caps)) {
890                 WARN("invalid parameter: caps->dwSize = %ld: < %d\n", caps->dwSize, sizeof(*caps));
891                 return DSERR_INVALIDPARAM;
892         }
893
894         caps->dwFlags = This->dsound->dsbd.dwFlags;
895         if (This->dsound->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
896         else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
897
898         caps->dwBufferBytes = This->dsound->buflen;
899
900         /* This value represents the speed of the "unlock" command.
901            As unlock is quite fast (it does not do anything), I put
902            4096 ko/s = 4 Mo / s */
903         /* FIXME: hwbuf speed */
904         caps->dwUnlockTransferRate = 4096;
905         caps->dwPlayCpuOverhead = 0;
906
907         return DS_OK;
908 }
909
910 static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(
911         LPDIRECTSOUNDBUFFER8 iface,REFIID riid,LPVOID *ppobj
912 ) {
913         ICOM_THIS(PrimaryBufferImpl,iface);
914         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
915
916         if (ppobj == NULL) {
917                 WARN("invalid parameter\n");
918                 return E_INVALIDARG;
919         }
920
921         *ppobj = NULL;  /* assume failure */
922
923         if ( IsEqualGUID(riid, &IID_IUnknown) || 
924              IsEqualGUID(riid, &IID_IDirectSoundBuffer) ) {
925                 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)This);
926                 *ppobj = This;
927                 return S_OK;
928         }
929
930         /* DirectSoundBuffer and DirectSoundBuffer8 are different and */
931         /* a primary buffer can't have a DirectSoundBuffer8 interface */
932         if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) {
933                 WARN("app requested DirectSoundBuffer8 on primary buffer\n");
934                 return E_NOINTERFACE;
935         }
936
937         if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
938                 ERR("app requested IDirectSoundNotify on primary buffer\n");
939                 /* FIXME: should we support this? */
940                 return E_NOINTERFACE;
941         }
942
943         if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
944                 ERR("app requested IDirectSound3DBuffer on primary buffer\n");
945                 return E_NOINTERFACE;
946         }
947
948         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
949                 if (!This->dsound->listener)
950                         IDirectSound3DListenerImpl_Create(This, &This->dsound->listener);
951                 if (This->dsound->listener) {
952                         *ppobj = This->dsound->listener;
953                         IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
954                         return S_OK;
955                 }
956
957                 WARN("IID_IDirectSound3DListener failed\n");
958                 return E_NOINTERFACE;
959         }
960
961         if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
962                 FIXME("app requested IKsPropertySet on primary buffer\n");
963                 return E_NOINTERFACE;
964         }
965
966         FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
967         return E_NOINTERFACE;
968 }
969
970 static ICOM_VTABLE(IDirectSoundBuffer8) dspbvt =
971 {
972         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
973         PrimaryBufferImpl_QueryInterface,
974         PrimaryBufferImpl_AddRef,
975         PrimaryBufferImpl_Release,
976         PrimaryBufferImpl_GetCaps,
977         PrimaryBufferImpl_GetCurrentPosition,
978         PrimaryBufferImpl_GetFormat,
979         PrimaryBufferImpl_GetVolume,
980         PrimaryBufferImpl_GetPan,
981         PrimaryBufferImpl_GetFrequency,
982         PrimaryBufferImpl_GetStatus,
983         PrimaryBufferImpl_Initialize,
984         PrimaryBufferImpl_Lock,
985         PrimaryBufferImpl_Play,
986         PrimaryBufferImpl_SetCurrentPosition,
987         PrimaryBufferImpl_SetFormat,
988         PrimaryBufferImpl_SetVolume,
989         PrimaryBufferImpl_SetPan,
990         PrimaryBufferImpl_SetFrequency,
991         PrimaryBufferImpl_Stop,
992         PrimaryBufferImpl_Unlock,
993         PrimaryBufferImpl_Restore,
994         PrimaryBufferImpl_SetFX,
995         PrimaryBufferImpl_AcquireResources,
996         PrimaryBufferImpl_GetObjectInPath
997 };
998
999 HRESULT WINAPI PrimaryBufferImpl_Create(
1000         IDirectSoundImpl *ds,
1001         PrimaryBufferImpl **pdsb,
1002         LPDSBUFFERDESC dsbd)
1003 {
1004         PrimaryBufferImpl *dsb;
1005
1006         TRACE("%p,%p,%p)\n",ds,pdsb,dsbd);
1007
1008         if (dsbd->lpwfxFormat) {
1009                 WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n");
1010                 *pdsb = NULL;
1011                 return DSERR_INVALIDPARAM;
1012         }
1013
1014         dsb = (PrimaryBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1015
1016         if (dsb == NULL) {
1017                 WARN("out of memory\n");
1018                 *pdsb = NULL;
1019                 return DSERR_OUTOFMEMORY;
1020         }
1021
1022         dsb->ref = 0;
1023         dsb->dsound = ds;
1024         dsb->lpVtbl = &dspbvt;
1025
1026         memcpy(&ds->dsbd, dsbd, sizeof(*dsbd));
1027
1028         TRACE("Created primary buffer at %p\n", dsb);
1029         TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
1030                 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1031                 ds->wfx.wFormatTag, ds->wfx.nChannels, ds->wfx.nSamplesPerSec,
1032                 ds->wfx.nAvgBytesPerSec, ds->wfx.nBlockAlign,
1033                 ds->wfx.wBitsPerSample, ds->wfx.cbSize);
1034
1035         IDirectSound_AddRef((LPDIRECTSOUND)ds);
1036         *pdsb = dsb;
1037         return S_OK;
1038 }