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