3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998 Rob Riggs
5 * Copyright 2000-2002 TransGaming Technologies, Inc.
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.
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.
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
24 #define NONAMELESSSTRUCT
25 #define NONAMELESSUNION
32 #include "wine/debug.h"
35 #include "dsound_private.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
39 static void DSOUND_RecalcPrimary(DirectSoundDevice *device)
43 TRACE("(%p)\n", device);
45 nBlockAlign = device->pwfx->nBlockAlign;
46 /* Alsa doesn't have continuous buffers, instead it has buffers with power of 2,
47 * If DS_TIME_DEL is about 10 ms, 512 * nBlockAlign is roughly correct */
48 fraglen = 512 * nBlockAlign;
50 /* Compensate for only being roughly accurate */
51 if (device->pwfx->nSamplesPerSec <= 26000)
54 if (device->pwfx->nSamplesPerSec <= 12000)
57 if (device->pwfx->nSamplesPerSec >= 80000)
60 device->fraglen = fraglen;
61 device->helfrags = device->buflen / fraglen;
62 TRACE("fraglen=%d helfrags=%d\n", device->fraglen, device->helfrags);
64 if (device->hwbuf && device->drvdesc.dwFlags & DSDDESC_DONTNEEDWRITELEAD)
65 device->writelead = 0;
67 /* calculate the 10ms write lead */
68 device->writelead = (device->pwfx->nSamplesPerSec / 100) * nBlockAlign;
71 HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
74 TRACE("(%p, %d)\n", device, forcewave);
78 IDsDriver_Close(device->driver);
79 if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
80 waveOutClose(device->hwo);
81 IDsDriver_Release(device->driver);
82 device->driver = NULL;
83 device->buffer = NULL;
86 else if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
87 waveOutClose(device->hwo);
89 /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
90 if (ds_hw_accel != DS_HW_ACCEL_EMULATION && !forcewave)
91 waveOutMessage((HWAVEOUT)device->drvdesc.dnDevNode, DRV_QUERYDSOUNDIFACE, (DWORD_PTR)&device->driver, 0);
93 /* Get driver description */
95 DWORD wod = device->drvdesc.dnDevNode;
96 hres = IDsDriver_GetDriverDesc(device->driver,&(device->drvdesc));
97 device->drvdesc.dnDevNode = wod;
99 WARN("IDsDriver_GetDriverDesc failed: %08x\n", hres);
100 IDsDriver_Release(device->driver);
101 device->driver = NULL;
105 /* if no DirectSound interface available, use WINMM API instead */
107 device->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
109 if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
111 DWORD flags = CALLBACK_FUNCTION;
114 flags |= WAVE_DIRECTSOUND;
116 hres = mmErr(waveOutOpen(&(device->hwo), device->drvdesc.dnDevNode, device->pwfx, (DWORD_PTR)DSOUND_callback, (DWORD)device, flags));
118 WARN("waveOutOpen failed\n");
121 IDsDriver_Release(device->driver);
122 device->driver = NULL;
129 hres = IDsDriver_Open(device->driver);
134 static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
137 TRACE("(%p)\n", device);
141 err = IDsDriver_CreateSoundBuffer(device->driver,device->pwfx,
142 DSBCAPS_PRIMARYBUFFER,0,
143 &(device->buflen),&(device->buffer),
144 (LPVOID*)&(device->hwbuf));
147 WARN("IDsDriver_CreateSoundBuffer failed (%08x), falling back to waveout\n", err);
148 err = DSOUND_ReopenDevice(device, TRUE);
151 WARN("Falling back to waveout failed too! Giving up\n");
157 if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
158 else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
160 /* are we using waveOut stuff? */
161 if (!device->driver) {
163 LPWAVEHDR headers = NULL;
164 DWORD buflen, overshot, oldbuflen;
167 /* Start in pause mode, to allow buffers to get filled */
168 waveOutPause(device->hwo);
170 /* on original windows, the buffer it set to a fixed size, no matter what the settings are.
171 on windows this size is always fixed (tested on win-xp) */
173 buflen = ds_hel_buflen;
174 else /* In case we move from hw accelerated to waveout */
175 buflen = device->buflen;
176 buflen -= buflen % device->pwfx->nBlockAlign;
178 TRACE("desired buflen=%d, old buffer=%p\n", buflen, device->buffer);
180 /* reallocate emulated primary buffer */
182 newbuf = HeapReAlloc(GetProcessHeap(),0,device->buffer,buflen);
184 newbuf = HeapAlloc(GetProcessHeap(),0,buflen);
187 ERR("failed to allocate primary buffer\n");
188 return DSERR_OUTOFMEMORY;
189 /* but the old buffer might still exist and must be re-prepared */
192 oldbuflen = device->buflen;
193 device->buflen = buflen;
194 DSOUND_RecalcPrimary(device);
196 headers = HeapReAlloc(GetProcessHeap(),0,device->pwave, device->helfrags * sizeof(WAVEHDR));
198 headers = HeapAlloc(GetProcessHeap(),0,device->helfrags * sizeof(WAVEHDR));
201 ERR("failed to allocate wave headers\n");
202 HeapFree(GetProcessHeap(), 0, newbuf);
203 device->buflen = oldbuflen;
204 DSOUND_RecalcPrimary(device);
205 return DSERR_OUTOFMEMORY;
208 device->buffer = newbuf;
209 device->pwave = headers;
211 /* prepare fragment headers */
212 for (c=0; c<device->helfrags; c++) {
213 device->pwave[c].lpData = (char*)device->buffer + c*device->fraglen;
214 device->pwave[c].dwBufferLength = device->fraglen;
215 device->pwave[c].dwUser = (DWORD)device;
216 device->pwave[c].dwFlags = 0;
217 device->pwave[c].dwLoops = 0;
218 err = mmErr(waveOutPrepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR)));
221 waveOutUnprepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR));
226 overshot = device->buflen % device->fraglen;
230 overshot -= overshot % device->pwfx->nBlockAlign;
232 WARN("helfrags (%d x %d) doesn't fit entirely in buflen (%d) overshot: %d\n", device->helfrags, device->fraglen, device->buflen, overshot);
233 device->pwave[device->helfrags - 1].dwBufferLength += overshot;
236 TRACE("fraglen=%d\n", device->fraglen);
238 FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
239 device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
240 DSOUND_RecalcPrimary(device);
246 static void DSOUND_PrimaryClose(DirectSoundDevice *device)
248 TRACE("(%p)\n", device);
250 /* are we using waveOut stuff? */
251 if (!device->hwbuf) {
254 /* get out of CS when calling the wave system */
255 LeaveCriticalSection(&(device->mixlock));
257 device->pwqueue = (DWORD)-1; /* resetting queues */
258 waveOutReset(device->hwo);
259 for (c=0; c<device->helfrags; c++)
260 waveOutUnprepareHeader(device->hwo, &device->pwave[c], sizeof(WAVEHDR));
262 EnterCriticalSection(&(device->mixlock));
264 /* clear the queue */
267 ULONG ref = IDsDriverBuffer_Release(device->hwbuf);
271 ERR("Still %d references on primary buffer, refcount leak?\n", ref);
275 HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device)
278 TRACE("(%p)\n", device);
280 device->buflen = ds_hel_buflen;
281 err = DSOUND_PrimaryOpen(device);
284 WARN("DSOUND_PrimaryOpen failed\n");
288 device->state = STATE_STOPPED;
292 HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
294 TRACE("(%p)\n", device);
297 EnterCriticalSection(&(device->mixlock));
299 DSOUND_PrimaryClose(device);
300 if (device->driver) {
302 if (IDsDriverBuffer_Release(device->hwbuf) == 0)
306 HeapFree(GetProcessHeap(),0,device->pwave);
307 HeapFree(GetProcessHeap(),0,device->pwfx);
310 LeaveCriticalSection(&(device->mixlock));
316 HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device)
319 TRACE("(%p)\n", device);
322 err = IDsDriverBuffer_Play(device->hwbuf, 0, 0, DSBPLAY_LOOPING);
324 WARN("IDsDriverBuffer_Play failed\n");
326 err = mmErr(waveOutRestart(device->hwo));
328 WARN("waveOutRestart failed\n");
334 HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device)
337 TRACE("(%p)\n", device);
340 err = IDsDriverBuffer_Stop(device->hwbuf);
341 if (err == DSERR_BUFFERLOST) {
342 DSOUND_PrimaryClose(device);
343 err = DSOUND_ReopenDevice(device, FALSE);
345 ERR("DSOUND_ReopenDevice failed\n");
348 err = DSOUND_PrimaryOpen(device);
350 WARN("DSOUND_PrimaryOpen failed\n");
352 } else if (err != DS_OK) {
353 WARN("IDsDriverBuffer_Stop failed\n");
357 /* don't call the wave system with the lock set */
358 LeaveCriticalSection(&(device->mixlock));
361 err = mmErr(waveOutPause(device->hwo));
364 EnterCriticalSection(&(device->mixlock));
367 WARN("waveOutPause failed\n");
373 HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos)
375 TRACE("(%p,%p,%p)\n", device, playpos, writepos);
378 HRESULT err=IDsDriverBuffer_GetPosition(device->hwbuf,playpos,writepos);
380 WARN("IDsDriverBuffer_GetPosition failed\n");
384 TRACE("pwplay=%i, pwqueue=%i\n", device->pwplay, device->pwqueue);
386 /* check if playpos was requested */
388 /* use the cached play position */
389 *playpos = device->pwplay * device->fraglen;
391 /* check if writepos was requested */
393 /* the writepos is the first non-queued position */
394 *writepos = ((device->pwplay + device->pwqueue) % device->helfrags) * device->fraglen;
396 TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:-1, writepos?*writepos:-1, device, GetTickCount());
400 HRESULT DSOUND_PrimarySetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex, BOOL forced)
402 HRESULT err = DSERR_BUFFERLOST;
403 int i, alloc_size, cp_size;
404 DWORD nSamplesPerSec, bpp, chans;
405 TRACE("(%p,%p)\n", device, wfex);
407 if (device->priolevel == DSSCL_NORMAL) {
408 WARN("failed priority check!\n");
409 return DSERR_PRIOLEVELNEEDED;
412 /* Let's be pedantic! */
414 WARN("invalid parameter: wfex==NULL!\n");
415 return DSERR_INVALIDPARAM;
417 TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
418 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
419 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
420 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
421 wfex->wBitsPerSample, wfex->cbSize);
424 RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
425 EnterCriticalSection(&(device->mixlock));
427 if (wfex->wFormatTag == WAVE_FORMAT_PCM) {
428 alloc_size = sizeof(WAVEFORMATEX);
429 cp_size = sizeof(PCMWAVEFORMAT);
431 alloc_size = cp_size = sizeof(WAVEFORMATEX) + wfex->cbSize;
433 device->pwfx = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,device->pwfx,alloc_size);
435 nSamplesPerSec = device->pwfx->nSamplesPerSec;
436 bpp = device->pwfx->wBitsPerSample;
437 chans = device->pwfx->nChannels;
439 CopyMemory(device->pwfx, wfex, cp_size);
441 if (!(device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) && device->hwbuf) {
442 err = IDsDriverBuffer_SetFormat(device->hwbuf, device->pwfx);
444 /* On bad format, try to re-create, big chance it will work then, only do this if we <HAVE> to */
445 if (forced && (device->pwfx->nSamplesPerSec/100 != wfex->nSamplesPerSec/100 || err == DSERR_BADFORMAT))
447 err = DSERR_BUFFERLOST;
448 CopyMemory(device->pwfx, wfex, cp_size);
451 if (err != DSERR_BUFFERLOST && FAILED(err)) {
452 WARN("IDsDriverBuffer_SetFormat failed\n");
460 /* ALSA specific: S_FALSE tells that recreation was successful,
461 * but size and location may be changed, and buffer has to be restarted
462 * I put it here, so if frequency doesn't match the error will be changed to DSERR_BUFFERLOST
463 * and the entire re-initialization will occur anyway
465 IDsDriverBuffer_Lock(device->hwbuf, (LPVOID *)&device->buffer, &device->buflen, NULL, NULL, 0, 0, DSBLOCK_ENTIREBUFFER);
466 IDsDriverBuffer_Unlock(device->hwbuf, device->buffer, 0, NULL, 0);
468 if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
469 else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
470 device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
473 DSOUND_RecalcPrimary(device);
476 if (err == DSERR_BUFFERLOST)
478 DSOUND_PrimaryClose(device);
480 err = DSOUND_ReopenDevice(device, FALSE);
483 WARN("DSOUND_ReopenDevice failed: %08x\n", err);
486 err = DSOUND_PrimaryOpen(device);
488 WARN("DSOUND_PrimaryOpen failed\n");
492 if (wfex->nSamplesPerSec/100 != device->pwfx->nSamplesPerSec/100 && forced && device->buffer)
494 DSOUND_PrimaryClose(device);
495 device->pwfx->nSamplesPerSec = wfex->nSamplesPerSec;
496 err = DSOUND_ReopenDevice(device, TRUE);
498 WARN("DSOUND_ReopenDevice(2) failed: %08x\n", err);
499 else if (FAILED((err = DSOUND_PrimaryOpen(device))))
500 WARN("DSOUND_PrimaryOpen(2) failed: %08x\n", err);
504 if (nSamplesPerSec != device->pwfx->nSamplesPerSec || bpp != device->pwfx->wBitsPerSample || chans != device->pwfx->nChannels) {
505 IDirectSoundBufferImpl** dsb = device->buffers;
506 for (i = 0; i < device->nrofbuffers; i++, dsb++) {
508 RtlAcquireResourceExclusive(&(*dsb)->lock, TRUE);
510 (*dsb)->freqAdjust = ((DWORD64)(*dsb)->freq << DSOUND_FREQSHIFT) / device->pwfx->nSamplesPerSec;
511 DSOUND_RecalcFormat((*dsb));
512 DSOUND_MixToTemporary((*dsb), 0, (*dsb)->buflen);
513 (*dsb)->primary_mixpos = 0;
515 RtlReleaseResource(&(*dsb)->lock);
521 LeaveCriticalSection(&(device->mixlock));
522 RtlReleaseResource(&(device->buffer_list_lock));
528 /*******************************************************************************
531 /* This sets this format for the <em>Primary Buffer Only</em> */
532 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
533 static HRESULT WINAPI PrimaryBufferImpl_SetFormat(
534 LPDIRECTSOUNDBUFFER iface,
535 LPCWAVEFORMATEX wfex)
537 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
538 TRACE("(%p,%p)\n", iface, wfex);
539 return DSOUND_PrimarySetFormat(device, wfex, device->priolevel == DSSCL_WRITEPRIMARY);
542 static HRESULT WINAPI PrimaryBufferImpl_SetVolume(
543 LPDIRECTSOUNDBUFFER iface,LONG vol
545 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
548 HRESULT hres = DS_OK;
549 TRACE("(%p,%d)\n", iface, vol);
551 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
552 WARN("control unavailable\n");
553 return DSERR_CONTROLUNAVAIL;
556 if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) {
557 WARN("invalid parameter: vol = %d\n", vol);
558 return DSERR_INVALIDPARAM;
562 EnterCriticalSection(&(device->mixlock));
564 waveOutGetVolume(device->hwo, &factors);
565 volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
566 volpan.dwTotalRightAmpFactor=ampfactors >> 16;
567 DSOUND_AmpFactorToVolPan(&volpan);
568 if (vol != volpan.lVolume) {
570 DSOUND_RecalcVolPan(&volpan);
572 hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &volpan);
574 WARN("IDsDriverBuffer_SetVolumePan failed\n");
576 ampfactors = (volpan.dwTotalLeftAmpFactor & 0xffff) | (volpan.dwTotalRightAmpFactor << 16);
577 waveOutSetVolume(device->hwo, ampfactors);
581 LeaveCriticalSection(&(device->mixlock));
587 static HRESULT WINAPI PrimaryBufferImpl_GetVolume(
588 LPDIRECTSOUNDBUFFER iface,LPLONG vol
590 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
593 TRACE("(%p,%p)\n", iface, vol);
595 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
596 WARN("control unavailable\n");
597 return DSERR_CONTROLUNAVAIL;
601 WARN("invalid parameter: vol = NULL\n");
602 return DSERR_INVALIDPARAM;
605 waveOutGetVolume(device->hwo, &factors);
606 volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
607 volpan.dwTotalRightAmpFactor=ampfactors >> 16;
608 DSOUND_AmpFactorToVolPan(&volpan);
609 *vol = volpan.lVolume;
613 static HRESULT WINAPI PrimaryBufferImpl_SetFrequency(
614 LPDIRECTSOUNDBUFFER iface,DWORD freq
616 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
617 TRACE("(%p,%d)\n",This,freq);
619 /* You cannot set the frequency of the primary buffer */
620 WARN("control unavailable\n");
621 return DSERR_CONTROLUNAVAIL;
624 static HRESULT WINAPI PrimaryBufferImpl_Play(
625 LPDIRECTSOUNDBUFFER iface,DWORD reserved1,DWORD reserved2,DWORD flags
627 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
628 TRACE("(%p,%08x,%08x,%08x)\n", iface, reserved1, reserved2, flags);
630 if (!(flags & DSBPLAY_LOOPING)) {
631 WARN("invalid parameter: flags = %08x\n", flags);
632 return DSERR_INVALIDPARAM;
636 EnterCriticalSection(&(device->mixlock));
638 if (device->state == STATE_STOPPED)
639 device->state = STATE_STARTING;
640 else if (device->state == STATE_STOPPING)
641 device->state = STATE_PLAYING;
643 LeaveCriticalSection(&(device->mixlock));
649 static HRESULT WINAPI PrimaryBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface)
651 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
652 TRACE("(%p)\n", iface);
655 EnterCriticalSection(&(device->mixlock));
657 if (device->state == STATE_PLAYING)
658 device->state = STATE_STOPPING;
659 else if (device->state == STATE_STARTING)
660 device->state = STATE_STOPPED;
662 LeaveCriticalSection(&(device->mixlock));
668 static ULONG WINAPI PrimaryBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface)
670 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
671 ULONG ref = InterlockedIncrement(&(This->ref));
672 TRACE("(%p) ref was %d\n", This, ref - 1);
676 static ULONG WINAPI PrimaryBufferImpl_Release(LPDIRECTSOUNDBUFFER iface)
678 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
679 DWORD ref = InterlockedDecrement(&(This->ref));
680 TRACE("(%p) ref was %d\n", This, ref + 1);
683 This->device->primary = NULL;
684 HeapFree(GetProcessHeap(), 0, This);
685 TRACE("(%p) released\n", This);
690 static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(
691 LPDIRECTSOUNDBUFFER iface,LPDWORD playpos,LPDWORD writepos
694 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
695 TRACE("(%p,%p,%p)\n", iface, playpos, writepos);
698 EnterCriticalSection(&(device->mixlock));
700 hres = DSOUND_PrimaryGetPosition(device, playpos, writepos);
702 WARN("DSOUND_PrimaryGetPosition failed\n");
703 LeaveCriticalSection(&(device->mixlock));
707 if (device->state != STATE_STOPPED)
708 /* apply the documented 10ms lead to writepos */
709 *writepos += device->writelead;
710 while (*writepos >= device->buflen) *writepos -= device->buflen;
713 LeaveCriticalSection(&(device->mixlock));
716 TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount());
720 static HRESULT WINAPI PrimaryBufferImpl_GetStatus(
721 LPDIRECTSOUNDBUFFER iface,LPDWORD status
723 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
724 TRACE("(%p,%p)\n", iface, status);
726 if (status == NULL) {
727 WARN("invalid parameter: status == NULL\n");
728 return DSERR_INVALIDPARAM;
732 if ((device->state == STATE_STARTING) ||
733 (device->state == STATE_PLAYING))
734 *status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING;
736 TRACE("status=%x\n", *status);
741 static HRESULT WINAPI PrimaryBufferImpl_GetFormat(
742 LPDIRECTSOUNDBUFFER iface,
748 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
749 TRACE("(%p,%p,%d,%p)\n", iface, lpwf, wfsize, wfwritten);
751 size = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
753 if (lpwf) { /* NULL is valid */
754 if (wfsize >= size) {
755 CopyMemory(lpwf,device->pwfx,size);
759 WARN("invalid parameter: wfsize too small\n");
762 return DSERR_INVALIDPARAM;
766 *wfwritten = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
768 WARN("invalid parameter: wfwritten == NULL\n");
769 return DSERR_INVALIDPARAM;
776 static HRESULT WINAPI PrimaryBufferImpl_Lock(
777 LPDIRECTSOUNDBUFFER iface,DWORD writecursor,DWORD writebytes,LPVOID *lplpaudioptr1,LPDWORD audiobytes1,LPVOID *lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
780 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
781 TRACE("(%p,%d,%d,%p,%p,%p,%p,0x%08x) at %d\n",
793 if (device->priolevel != DSSCL_WRITEPRIMARY) {
794 WARN("failed priority check!\n");
795 return DSERR_PRIOLEVELNEEDED;
798 /* when this flag is set, writecursor is meaningless and must be calculated */
799 if (flags & DSBLOCK_FROMWRITECURSOR) {
800 /* GetCurrentPosition does too much magic to duplicate here */
801 hres = IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writecursor);
803 WARN("IDirectSoundBuffer_GetCurrentPosition failed\n");
808 /* when this flag is set, writebytes is meaningless and must be set */
809 if (flags & DSBLOCK_ENTIREBUFFER)
810 writebytes = device->buflen;
812 if (writecursor >= device->buflen) {
813 WARN("Invalid parameter, writecursor: %u >= buflen: %u\n",
814 writecursor, device->buflen);
815 return DSERR_INVALIDPARAM;
818 if (writebytes > device->buflen) {
819 WARN("Invalid parameter, writebytes: %u > buflen: %u\n",
820 writebytes, device->buflen);
821 return DSERR_INVALIDPARAM;
824 if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
825 hres = IDsDriverBuffer_Lock(device->hwbuf,
826 lplpaudioptr1, audiobytes1,
827 lplpaudioptr2, audiobytes2,
828 writecursor, writebytes,
831 WARN("IDsDriverBuffer_Lock failed\n");
835 if (writecursor+writebytes <= device->buflen) {
836 *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
837 *audiobytes1 = writebytes;
839 *(LPBYTE*)lplpaudioptr2 = NULL;
842 TRACE("->%d.0\n",writebytes);
844 *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
845 *audiobytes1 = device->buflen-writecursor;
847 *(LPBYTE*)lplpaudioptr2 = device->buffer;
849 *audiobytes2 = writebytes-(device->buflen-writecursor);
850 TRACE("->%d.%d\n",*audiobytes1,audiobytes2?*audiobytes2:0);
856 static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition(
857 LPDIRECTSOUNDBUFFER iface,DWORD newpos
859 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
860 TRACE("(%p,%d)\n",This,newpos);
862 /* You cannot set the position of the primary buffer */
863 WARN("invalid call\n");
864 return DSERR_INVALIDCALL;
867 static HRESULT WINAPI PrimaryBufferImpl_SetPan(
868 LPDIRECTSOUNDBUFFER iface,LONG pan
870 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
873 HRESULT hres = DS_OK;
874 TRACE("(%p,%d)\n", iface, pan);
876 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
877 WARN("control unavailable\n");
878 return DSERR_CONTROLUNAVAIL;
881 if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) {
882 WARN("invalid parameter: pan = %d\n", pan);
883 return DSERR_INVALIDPARAM;
887 EnterCriticalSection(&(device->mixlock));
889 waveOutGetVolume(device->hwo, &factors);
890 volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
891 volpan.dwTotalRightAmpFactor=ampfactors >> 16;
892 DSOUND_AmpFactorToVolPan(&volpan);
893 if (pan != volpan.lPan) {
895 DSOUND_RecalcVolPan(&volpan);
897 hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &volpan);
899 WARN("IDsDriverBuffer_SetVolumePan failed\n");
901 ampfactors = (volpan.dwTotalLeftAmpFactor & 0xffff) | (volpan.dwTotalRightAmpFactor << 16);
902 waveOutSetVolume(device->hwo, ampfactors);
906 LeaveCriticalSection(&(device->mixlock));
912 static HRESULT WINAPI PrimaryBufferImpl_GetPan(
913 LPDIRECTSOUNDBUFFER iface,LPLONG pan
915 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
918 TRACE("(%p,%p)\n", iface, pan);
920 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
921 WARN("control unavailable\n");
922 return DSERR_CONTROLUNAVAIL;
926 WARN("invalid parameter: pan == NULL\n");
927 return DSERR_INVALIDPARAM;
930 waveOutGetVolume(device->hwo, &factors);
931 volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
932 volpan.dwTotalRightAmpFactor=ampfactors >> 16;
933 DSOUND_AmpFactorToVolPan(&volpan);
938 static HRESULT WINAPI PrimaryBufferImpl_Unlock(
939 LPDIRECTSOUNDBUFFER iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
941 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
942 TRACE("(%p,%p,%d,%p,%d)\n", iface, p1, x1, p2, x2);
944 if (device->priolevel != DSSCL_WRITEPRIMARY) {
945 WARN("failed priority check!\n");
946 return DSERR_PRIOLEVELNEEDED;
949 if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
952 hres = IDsDriverBuffer_Unlock(device->hwbuf, p1, x1, p2, x2);
954 WARN("IDsDriverBuffer_Unlock failed\n");
962 static HRESULT WINAPI PrimaryBufferImpl_Restore(
963 LPDIRECTSOUNDBUFFER iface
965 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
966 FIXME("(%p):stub\n",This);
970 static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(
971 LPDIRECTSOUNDBUFFER iface,LPDWORD freq
973 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
974 TRACE("(%p,%p)\n", iface, freq);
977 WARN("invalid parameter: freq == NULL\n");
978 return DSERR_INVALIDPARAM;
981 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
982 WARN("control unavailable\n");
983 return DSERR_CONTROLUNAVAIL;
986 *freq = device->pwfx->nSamplesPerSec;
987 TRACE("-> %d\n", *freq);
992 static HRESULT WINAPI PrimaryBufferImpl_Initialize(
993 LPDIRECTSOUNDBUFFER iface,LPDIRECTSOUND dsound,LPCDSBUFFERDESC dbsd
995 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
996 WARN("(%p) already initialized\n", This);
997 return DSERR_ALREADYINITIALIZED;
1000 static HRESULT WINAPI PrimaryBufferImpl_GetCaps(
1001 LPDIRECTSOUNDBUFFER iface,LPDSBCAPS caps
1003 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
1004 TRACE("(%p,%p)\n", iface, caps);
1007 WARN("invalid parameter: caps == NULL\n");
1008 return DSERR_INVALIDPARAM;
1011 if (caps->dwSize < sizeof(*caps)) {
1012 WARN("invalid parameter: caps->dwSize = %d\n", caps->dwSize);
1013 return DSERR_INVALIDPARAM;
1016 caps->dwFlags = device->dsbd.dwFlags;
1017 caps->dwBufferBytes = device->buflen;
1019 /* Windows reports these as zero */
1020 caps->dwUnlockTransferRate = 0;
1021 caps->dwPlayCpuOverhead = 0;
1026 static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(
1027 LPDIRECTSOUNDBUFFER iface,REFIID riid,LPVOID *ppobj
1029 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
1030 DirectSoundDevice *device = This->device;
1031 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(riid), ppobj);
1033 if (ppobj == NULL) {
1034 WARN("invalid parameter\n");
1035 return E_INVALIDARG;
1038 *ppobj = NULL; /* assume failure */
1040 if ( IsEqualGUID(riid, &IID_IUnknown) ||
1041 IsEqualGUID(riid, &IID_IDirectSoundBuffer) ) {
1042 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)This);
1047 /* DirectSoundBuffer and DirectSoundBuffer8 are different and */
1048 /* a primary buffer can't have a DirectSoundBuffer8 interface */
1049 if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) {
1050 WARN("app requested DirectSoundBuffer8 on primary buffer\n");
1051 return E_NOINTERFACE;
1054 if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
1055 ERR("app requested IDirectSoundNotify on primary buffer\n");
1056 /* FIXME: should we support this? */
1057 return E_NOINTERFACE;
1060 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1061 ERR("app requested IDirectSound3DBuffer on primary buffer\n");
1062 return E_NOINTERFACE;
1065 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1066 if (!device->listener)
1067 IDirectSound3DListenerImpl_Create(device, &device->listener);
1068 if (device->listener) {
1069 *ppobj = device->listener;
1070 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
1074 WARN("IID_IDirectSound3DListener failed\n");
1075 return E_NOINTERFACE;
1078 if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
1079 FIXME("app requested IKsPropertySet on primary buffer\n");
1080 return E_NOINTERFACE;
1083 FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
1084 return E_NOINTERFACE;
1087 static const IDirectSoundBufferVtbl dspbvt =
1089 PrimaryBufferImpl_QueryInterface,
1090 PrimaryBufferImpl_AddRef,
1091 PrimaryBufferImpl_Release,
1092 PrimaryBufferImpl_GetCaps,
1093 PrimaryBufferImpl_GetCurrentPosition,
1094 PrimaryBufferImpl_GetFormat,
1095 PrimaryBufferImpl_GetVolume,
1096 PrimaryBufferImpl_GetPan,
1097 PrimaryBufferImpl_GetFrequency,
1098 PrimaryBufferImpl_GetStatus,
1099 PrimaryBufferImpl_Initialize,
1100 PrimaryBufferImpl_Lock,
1101 PrimaryBufferImpl_Play,
1102 PrimaryBufferImpl_SetCurrentPosition,
1103 PrimaryBufferImpl_SetFormat,
1104 PrimaryBufferImpl_SetVolume,
1105 PrimaryBufferImpl_SetPan,
1106 PrimaryBufferImpl_SetFrequency,
1107 PrimaryBufferImpl_Stop,
1108 PrimaryBufferImpl_Unlock,
1109 PrimaryBufferImpl_Restore
1112 HRESULT PrimaryBufferImpl_Create(
1113 DirectSoundDevice * device,
1114 PrimaryBufferImpl ** ppdsb,
1115 LPCDSBUFFERDESC dsbd)
1117 PrimaryBufferImpl *dsb;
1118 TRACE("%p,%p,%p)\n",device,ppdsb,dsbd);
1120 if (dsbd->lpwfxFormat) {
1121 WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n");
1123 return DSERR_INVALIDPARAM;
1126 dsb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1129 WARN("out of memory\n");
1131 return DSERR_OUTOFMEMORY;
1135 dsb->device = device;
1136 dsb->lpVtbl = &dspbvt;
1138 CopyMemory(&device->dsbd, dsbd, sizeof(*dsbd));
1140 TRACE("Created primary buffer at %p\n", dsb);
1141 TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
1142 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1143 device->pwfx->wFormatTag, device->pwfx->nChannels,
1144 device->pwfx->nSamplesPerSec, device->pwfx->nAvgBytesPerSec,
1145 device->pwfx->nBlockAlign, device->pwfx->wBitsPerSample,
1146 device->pwfx->cbSize);