/* DirectSound * * Copyright 1998 Marcus Meissner * Copyright 1998 Rob Riggs * Copyright 2000-2002 TransGaming Technologies, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include <assert.h> #include <stdio.h> #include <sys/types.h> #include <sys/fcntl.h> #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #include <stdlib.h> #include <string.h> #include <math.h> /* Insomnia - pow() function */ #include "windef.h" #include "winbase.h" #include "wingdi.h" #include "winuser.h" #include "winerror.h" #include "mmsystem.h" #include "winternl.h" #include "mmddk.h" #include "wine/windef16.h" #include "wine/debug.h" #include "dsound.h" #include "dsdriver.h" #include "dsound_private.h" WINE_DEFAULT_DEBUG_CHANNEL(dsound); static HRESULT mmErr(UINT err) { switch(err) { case MMSYSERR_NOERROR: return DS_OK; case MMSYSERR_ALLOCATED: return DSERR_ALLOCATED; case MMSYSERR_INVALHANDLE: return DSERR_GENERIC; /* FIXME */ case MMSYSERR_NODRIVER: return DSERR_NODRIVER; case MMSYSERR_NOMEM: return DSERR_OUTOFMEMORY; case MMSYSERR_INVALPARAM: return DSERR_INVALIDPARAM; default: FIXME("Unknown MMSYS error %d\n",err); return DSERR_GENERIC; } } void DSOUND_RecalcPrimary(IDirectSoundImpl *This) { DWORD sw; sw = This->wfx.nChannels * (This->wfx.wBitsPerSample / 8); if (This->hwbuf) { DWORD fraglen; /* let fragment size approximate the timer delay */ fraglen = (This->wfx.nSamplesPerSec * DS_TIME_DEL / 1000) * sw; /* reduce fragment size until an integer number of them fits in the buffer */ /* (FIXME: this may or may not be a good idea) */ while (This->buflen % fraglen) fraglen -= sw; This->fraglen = fraglen; TRACE("fraglen=%ld\n", This->fraglen); } /* calculate the 10ms write lead */ This->writelead = (This->wfx.nSamplesPerSec / 100) * sw; } static HRESULT DSOUND_PrimaryOpen(IDirectSoundImpl *This) { HRESULT err = DS_OK; /* are we using waveOut stuff? */ if (!This->hwbuf) { LPBYTE newbuf; DWORD buflen; HRESULT merr = DS_OK; /* Start in pause mode, to allow buffers to get filled */ waveOutPause(This->hwo); if (This->state == STATE_PLAYING) This->state = STATE_STARTING; else if (This->state == STATE_STOPPING) This->state = STATE_STOPPED; /* use fragments of 10ms (1/100s) each (which should get us within * the documented write cursor lead of 10-15ms) */ buflen = ((This->wfx.nAvgBytesPerSec / 100) & ~3) * DS_HEL_FRAGS; TRACE("desired buflen=%ld, old buffer=%p\n", buflen, This->buffer); /* reallocate emulated primary buffer */ newbuf = (LPBYTE)HeapReAlloc(GetProcessHeap(),0,This->buffer,buflen); if (newbuf == NULL) { ERR("failed to allocate primary buffer\n"); merr = DSERR_OUTOFMEMORY; /* but the old buffer might still exists and must be re-prepared */ } else { This->buffer = newbuf; This->buflen = buflen; } if (This->buffer) { unsigned c; This->fraglen = This->buflen / DS_HEL_FRAGS; /* prepare fragment headers */ for (c=0; c<DS_HEL_FRAGS; c++) { This->pwave[c]->lpData = This->buffer + c*This->fraglen; This->pwave[c]->dwBufferLength = This->fraglen; This->pwave[c]->dwUser = (DWORD)This; This->pwave[c]->dwFlags = 0; This->pwave[c]->dwLoops = 0; err = mmErr(waveOutPrepareHeader(This->hwo,This->pwave[c],sizeof(WAVEHDR))); if (err != DS_OK) { while (c--) waveOutUnprepareHeader(This->hwo,This->pwave[c],sizeof(WAVEHDR)); break; } } This->pwplay = 0; This->pwwrite = 0; This->pwqueue = 0; memset(This->buffer, (This->wfx.wBitsPerSample == 16) ? 0 : 128, This->buflen); TRACE("fraglen=%ld\n", This->fraglen); DSOUND_WaveQueue(This, (DWORD)-1); } if ((err == DS_OK) && (merr != DS_OK)) err = merr; } return err; } static void DSOUND_PrimaryClose(IDirectSoundImpl *This) { /* are we using waveOut stuff? */ if (!This->hwbuf) { unsigned c; This->pwqueue = (DWORD)-1; /* resetting queues */ waveOutReset(This->hwo); for (c=0; c<DS_HEL_FRAGS; c++) waveOutUnprepareHeader(This->hwo, This->pwave[c], sizeof(WAVEHDR)); This->pwqueue = 0; } } HRESULT DSOUND_PrimaryCreate(IDirectSoundImpl *This) { HRESULT err = DS_OK; This->buflen = This->wfx.nAvgBytesPerSec; /* FIXME: verify that hardware capabilities (DSCAPS_PRIMARY flags) match */ if (This->driver) { err = IDsDriver_CreateSoundBuffer(This->driver,&(This->wfx), DSBCAPS_PRIMARYBUFFER,0, &(This->buflen),&(This->buffer), (LPVOID*)&(This->hwbuf)); } if (err == DS_OK) err = DSOUND_PrimaryOpen(This); if (err != DS_OK) return err; /* calculate fragment size and write lead */ DSOUND_RecalcPrimary(This); This->state = STATE_STOPPED; return DS_OK; } HRESULT DSOUND_PrimaryDestroy(IDirectSoundImpl *This) { DSOUND_PrimaryClose(This); if (This->hwbuf) { IDsDriverBuffer_Release(This->hwbuf); } return DS_OK; } HRESULT DSOUND_PrimaryPlay(IDirectSoundImpl *This) { HRESULT err = DS_OK; if (This->hwbuf) err = IDsDriverBuffer_Play(This->hwbuf, 0, 0, DSBPLAY_LOOPING); else err = mmErr(waveOutRestart(This->hwo)); return err; } HRESULT DSOUND_PrimaryStop(IDirectSoundImpl *This) { HRESULT err = DS_OK; TRACE("\n"); if (This->hwbuf) { err = IDsDriverBuffer_Stop(This->hwbuf); if (err == DSERR_BUFFERLOST) { /* Wine-only: the driver wants us to reopen the device */ /* FIXME: check for errors */ IDsDriverBuffer_Release(This->hwbuf); waveOutClose(This->hwo); This->hwo = 0; err = mmErr(waveOutOpen(&(This->hwo), This->drvdesc.dnDevNode, &(This->wfx), (DWORD)DSOUND_callback, (DWORD)This, CALLBACK_FUNCTION | WAVE_DIRECTSOUND)); if (err == DS_OK) err = IDsDriver_CreateSoundBuffer(This->driver,&(This->wfx), DSBCAPS_PRIMARYBUFFER,0, &(This->buflen),&(This->buffer), (LPVOID)&(This->hwbuf)); } } else err = mmErr(waveOutPause(This->hwo)); return err; } HRESULT DSOUND_PrimaryGetPosition(IDirectSoundImpl *This, LPDWORD playpos, LPDWORD writepos) { if (This->hwbuf) { HRESULT err=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos); if (err) return err; } else { if (playpos) { MMTIME mtime; mtime.wType = TIME_BYTES; waveOutGetPosition(This->hwo, &mtime, sizeof(mtime)); mtime.u.cb = mtime.u.cb % This->buflen; *playpos = mtime.u.cb; } if (writepos) { /* the writepos should only be used by apps with WRITEPRIMARY priority, * in which case our software mixer is disabled anyway */ *writepos = (This->pwplay + ds_hel_margin) * This->fraglen; while (*writepos >= This->buflen) *writepos -= This->buflen; } } TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount()); return DS_OK; } /******************************************************************************* * IDirectSoundBuffer */ /* This sets this format for the <em>Primary Buffer Only</em> */ /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */ static HRESULT WINAPI PrimaryBufferImpl_SetFormat( LPDIRECTSOUNDBUFFER8 iface,LPWAVEFORMATEX wfex ) { ICOM_THIS(PrimaryBufferImpl,iface); IDirectSoundImpl* dsound = This->dsound; IDirectSoundBufferImpl** dsb; HRESULT err = DS_OK; int i; if (This->dsound->priolevel == DSSCL_NORMAL) { TRACE("failed priority check!\n"); return DSERR_PRIOLEVELNEEDED; } /* Let's be pedantic! */ if ((wfex == NULL) || (wfex->wFormatTag != WAVE_FORMAT_PCM) || (wfex->nChannels < 1) || (wfex->nChannels > 2) || (wfex->nSamplesPerSec < 1) || (wfex->nBlockAlign < 1) || (wfex->nChannels > 4) || ((wfex->wBitsPerSample != 8) && (wfex->wBitsPerSample != 16))) { TRACE("failed pedantic check!\n"); return DSERR_INVALIDPARAM; } /* **** */ RtlAcquireResourceExclusive(&(dsound->lock), TRUE); if (dsound->wfx.nSamplesPerSec != wfex->nSamplesPerSec) { dsb = dsound->buffers; for (i = 0; i < dsound->nrofbuffers; i++, dsb++) { /* **** */ EnterCriticalSection(&((*dsb)->lock)); (*dsb)->freqAdjust = ((*dsb)->freq << DSOUND_FREQSHIFT) / wfex->nSamplesPerSec; LeaveCriticalSection(&((*dsb)->lock)); /* **** */ } } memcpy(&(dsound->wfx), wfex, sizeof(dsound->wfx)); TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld," "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n", wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec, wfex->nAvgBytesPerSec, wfex->nBlockAlign, wfex->wBitsPerSample, wfex->cbSize); dsound->wfx.nAvgBytesPerSec = dsound->wfx.nSamplesPerSec * dsound->wfx.nBlockAlign; if (dsound->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) { /* FIXME: check for errors */ DSOUND_PrimaryClose(dsound); waveOutClose(dsound->hwo); dsound->hwo = 0; err = mmErr(waveOutOpen(&(dsound->hwo), dsound->drvdesc.dnDevNode, &(dsound->wfx), (DWORD)DSOUND_callback, (DWORD)dsound, CALLBACK_FUNCTION | WAVE_DIRECTSOUND)); if (err == DS_OK) DSOUND_PrimaryOpen(dsound); } if (dsound->hwbuf) { err = IDsDriverBuffer_SetFormat(dsound->hwbuf, &(dsound->wfx)); if (err == DSERR_BUFFERLOST) { /* Wine-only: the driver wants us to recreate the HW buffer */ IDsDriverBuffer_Release(dsound->hwbuf); err = IDsDriver_CreateSoundBuffer(dsound->driver,&(dsound->wfx), DSBCAPS_PRIMARYBUFFER,0, &(dsound->buflen),&(dsound->buffer), (LPVOID)&(dsound->hwbuf)); if (dsound->state == STATE_PLAYING) dsound->state = STATE_STARTING; else if (dsound->state == STATE_STOPPING) dsound->state = STATE_STOPPED; } /* FIXME: should we set err back to DS_OK in all cases ? */ } DSOUND_RecalcPrimary(dsound); RtlReleaseResource(&(dsound->lock)); /* **** */ return err; } static HRESULT WINAPI PrimaryBufferImpl_SetVolume( LPDIRECTSOUNDBUFFER8 iface,LONG vol ) { ICOM_THIS(PrimaryBufferImpl,iface); IDirectSoundImpl* dsound = This->dsound; LONG oldVol; TRACE("(%p,%ld)\n",This,vol); /* I'm not sure if we need this for primary buffer */ if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) return DSERR_CONTROLUNAVAIL; if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) return DSERR_INVALIDPARAM; /* **** */ EnterCriticalSection(&(dsound->mixlock)); oldVol = dsound->volpan.lVolume; dsound->volpan.lVolume = vol; DSOUND_RecalcVolPan(&dsound->volpan); if (vol != oldVol) { if (dsound->hwbuf) { IDsDriverBuffer_SetVolumePan(dsound->hwbuf, &(dsound->volpan)); } else { #if 0 /* should we really do this? */ /* the DS volume ranges from 0 (max, 0dB attenuation) to -10000 (min, 100dB attenuation) */ /* the MM volume ranges from 0 to 0xffff in an unspecified logarithmic scale */ WORD cvol = 0xffff + vol*6 + vol/2; DWORD vol = cvol | ((DWORD)cvol << 16) waveOutSetVolume(dsound->hwo, vol); #endif } } LeaveCriticalSection(&(dsound->mixlock)); /* **** */ return DS_OK; } static HRESULT WINAPI PrimaryBufferImpl_GetVolume( LPDIRECTSOUNDBUFFER8 iface,LPLONG vol ) { ICOM_THIS(PrimaryBufferImpl,iface); TRACE("(%p,%p)\n",This,vol); if (vol == NULL) return DSERR_INVALIDPARAM; *vol = This->dsound->volpan.lVolume; return DS_OK; } static HRESULT WINAPI PrimaryBufferImpl_SetFrequency( LPDIRECTSOUNDBUFFER8 iface,DWORD freq ) { ICOM_THIS(PrimaryBufferImpl,iface); TRACE("(%p,%ld)\n",This,freq); /* You cannot set the frequency of the primary buffer */ return DSERR_CONTROLUNAVAIL; } static HRESULT WINAPI PrimaryBufferImpl_Play( LPDIRECTSOUNDBUFFER8 iface,DWORD reserved1,DWORD reserved2,DWORD flags ) { ICOM_THIS(PrimaryBufferImpl,iface); IDirectSoundImpl* dsound = This->dsound; TRACE("(%p,%08lx,%08lx,%08lx)\n", This,reserved1,reserved2,flags ); if (!(flags & DSBPLAY_LOOPING)) return DSERR_INVALIDPARAM; /* **** */ EnterCriticalSection(&(dsound->mixlock)); if (dsound->state == STATE_STOPPED) dsound->state = STATE_STARTING; else if (dsound->state == STATE_STOPPING) dsound->state = STATE_PLAYING; LeaveCriticalSection(&(dsound->mixlock)); /* **** */ return DS_OK; } static HRESULT WINAPI PrimaryBufferImpl_Stop(LPDIRECTSOUNDBUFFER8 iface) { ICOM_THIS(PrimaryBufferImpl,iface); IDirectSoundImpl* dsound = This->dsound; TRACE("(%p)\n",This); /* **** */ EnterCriticalSection(&(dsound->mixlock)); if (dsound->state == STATE_PLAYING) dsound->state = STATE_STOPPING; else if (dsound->state == STATE_STARTING) dsound->state = STATE_STOPPED; LeaveCriticalSection(&(dsound->mixlock)); /* **** */ return DS_OK; } static DWORD WINAPI PrimaryBufferImpl_AddRef(LPDIRECTSOUNDBUFFER8 iface) { ICOM_THIS(PrimaryBufferImpl,iface); DWORD ref; TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId()); ref = InterlockedIncrement(&(This->ref)); if (!ref) { FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n"); } return ref; } static DWORD WINAPI PrimaryBufferImpl_Release(LPDIRECTSOUNDBUFFER8 iface) { ICOM_THIS(PrimaryBufferImpl,iface); DWORD ref; TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId()); ref = InterlockedDecrement(&(This->ref)); if (ref) return ref; IDirectSound_Release((LPDIRECTSOUND)This->dsound); #if 0 if (This->iks) { HeapFree(GetProcessHeap(), 0, This->iks); } #endif HeapFree(GetProcessHeap(),0,This); return 0; } static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition( LPDIRECTSOUNDBUFFER8 iface,LPDWORD playpos,LPDWORD writepos ) { ICOM_THIS(PrimaryBufferImpl,iface); IDirectSoundImpl* dsound = This->dsound; TRACE("(%p,%p,%p)\n",This,playpos,writepos); DSOUND_PrimaryGetPosition(dsound, playpos, writepos); if (writepos) { if (dsound->state != STATE_STOPPED) /* apply the documented 10ms lead to writepos */ *writepos += dsound->writelead; while (*writepos >= dsound->buflen) *writepos -= dsound->buflen; } TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount()); return DS_OK; } static HRESULT WINAPI PrimaryBufferImpl_GetStatus( LPDIRECTSOUNDBUFFER8 iface,LPDWORD status ) { ICOM_THIS(PrimaryBufferImpl,iface); TRACE("(%p,%p), thread is %lx\n",This,status,GetCurrentThreadId()); if (status == NULL) return DSERR_INVALIDPARAM; *status = 0; if ((This->dsound->state == STATE_STARTING) || (This->dsound->state == STATE_PLAYING)) *status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING; TRACE("status=%lx\n", *status); return DS_OK; } static HRESULT WINAPI PrimaryBufferImpl_GetFormat( LPDIRECTSOUNDBUFFER8 iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten ) { ICOM_THIS(PrimaryBufferImpl,iface); TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten); if (wfsize>sizeof(This->dsound->wfx)) wfsize = sizeof(This->dsound->wfx); if (lpwf) { /* NULL is valid */ memcpy(lpwf,&(This->dsound->wfx),wfsize); if (wfwritten) *wfwritten = wfsize; } else if (wfwritten) *wfwritten = sizeof(This->dsound->wfx); else return DSERR_INVALIDPARAM; return DS_OK; } static HRESULT WINAPI PrimaryBufferImpl_Lock( LPDIRECTSOUNDBUFFER8 iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags ) { ICOM_THIS(PrimaryBufferImpl,iface); IDirectSoundImpl* dsound = This->dsound; TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx) at %ld\n", This, writecursor, writebytes, lplpaudioptr1, audiobytes1, lplpaudioptr2, audiobytes2, flags, GetTickCount() ); if (dsound->priolevel != DSSCL_WRITEPRIMARY) return DSERR_PRIOLEVELNEEDED; if (flags & DSBLOCK_FROMWRITECURSOR) { DWORD writepos; /* GetCurrentPosition does too much magic to duplicate here */ IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writepos); writecursor += writepos; } while (writecursor >= dsound->buflen) writecursor -= dsound->buflen; if (flags & DSBLOCK_ENTIREBUFFER) writebytes = dsound->buflen; if (writebytes > dsound->buflen) writebytes = dsound->buflen; assert(audiobytes1!=audiobytes2); assert(lplpaudioptr1!=lplpaudioptr2); if (!(dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && dsound->hwbuf) { IDsDriverBuffer_Lock(dsound->hwbuf, lplpaudioptr1, audiobytes1, lplpaudioptr2, audiobytes2, writecursor, writebytes, 0); } else { if (writecursor+writebytes <= dsound->buflen) { *(LPBYTE*)lplpaudioptr1 = dsound->buffer+writecursor; *audiobytes1 = writebytes; if (lplpaudioptr2) *(LPBYTE*)lplpaudioptr2 = NULL; if (audiobytes2) *audiobytes2 = 0; TRACE("->%ld.0\n",writebytes); } else { *(LPBYTE*)lplpaudioptr1 = dsound->buffer+writecursor; *audiobytes1 = dsound->buflen-writecursor; if (lplpaudioptr2) *(LPBYTE*)lplpaudioptr2 = dsound->buffer; if (audiobytes2) *audiobytes2 = writebytes-(dsound->buflen-writecursor); TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0); } } return DS_OK; } static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition( LPDIRECTSOUNDBUFFER8 iface,DWORD newpos ) { ICOM_THIS(PrimaryBufferImpl,iface); TRACE("(%p,%ld)\n",This,newpos); /* You cannot set the position of the primary buffer */ return DSERR_INVALIDCALL; } static HRESULT WINAPI PrimaryBufferImpl_SetPan( LPDIRECTSOUNDBUFFER8 iface,LONG pan ) { ICOM_THIS(PrimaryBufferImpl,iface); TRACE("(%p,%ld)\n",This,pan); /* You cannot set the pan of the primary buffer */ return DSERR_CONTROLUNAVAIL; } static HRESULT WINAPI PrimaryBufferImpl_GetPan( LPDIRECTSOUNDBUFFER8 iface,LPLONG pan ) { ICOM_THIS(PrimaryBufferImpl,iface); TRACE("(%p,%p)\n",This,pan); if (pan == NULL) return DSERR_INVALIDPARAM; *pan = This->dsound->volpan.lPan; return DS_OK; } static HRESULT WINAPI PrimaryBufferImpl_Unlock( LPDIRECTSOUNDBUFFER8 iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2 ) { ICOM_THIS(PrimaryBufferImpl,iface); IDirectSoundImpl* dsound = This->dsound; TRACE("(%p,%p,%ld,%p,%ld):stub\n", This,p1,x1,p2,x2); if (dsound->priolevel != DSSCL_WRITEPRIMARY) return DSERR_PRIOLEVELNEEDED; if (!(dsound->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && dsound->hwbuf) { IDsDriverBuffer_Unlock(dsound->hwbuf, p1, x1, p2, x2); } return DS_OK; } static HRESULT WINAPI PrimaryBufferImpl_Restore( LPDIRECTSOUNDBUFFER8 iface ) { ICOM_THIS(PrimaryBufferImpl,iface); FIXME("(%p):stub\n",This); return DS_OK; } static HRESULT WINAPI PrimaryBufferImpl_GetFrequency( LPDIRECTSOUNDBUFFER8 iface,LPDWORD freq ) { ICOM_THIS(PrimaryBufferImpl,iface); TRACE("(%p,%p)\n",This,freq); if (freq == NULL) return DSERR_INVALIDPARAM; *freq = This->dsound->wfx.nSamplesPerSec; TRACE("-> %ld\n", *freq); return DS_OK; } static HRESULT WINAPI PrimaryBufferImpl_SetFX( LPDIRECTSOUNDBUFFER8 iface,DWORD dwEffectsCount,LPDSEFFECTDESC pDSFXDesc,LPDWORD pdwResultCodes ) { ICOM_THIS(PrimaryBufferImpl,iface); DWORD u; FIXME("(%p,%lu,%p,%p): stub\n",This,dwEffectsCount,pDSFXDesc,pdwResultCodes); if (pdwResultCodes) for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN; return DSERR_CONTROLUNAVAIL; } static HRESULT WINAPI PrimaryBufferImpl_AcquireResources( LPDIRECTSOUNDBUFFER8 iface,DWORD dwFlags,DWORD dwEffectsCount,LPDWORD pdwResultCodes ) { ICOM_THIS(PrimaryBufferImpl,iface); DWORD u; FIXME("(%p,%08lu,%lu,%p): stub\n",This,dwFlags,dwEffectsCount,pdwResultCodes); if (pdwResultCodes) for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN; return DSERR_CONTROLUNAVAIL; } static HRESULT WINAPI PrimaryBufferImpl_GetObjectInPath( LPDIRECTSOUNDBUFFER8 iface,REFGUID rguidObject,DWORD dwIndex,REFGUID rguidInterface,LPVOID* ppObject ) { ICOM_THIS(PrimaryBufferImpl,iface); FIXME("(%p,%s,%lu,%s,%p): stub\n",This,debugstr_guid(rguidObject),dwIndex,debugstr_guid(rguidInterface),ppObject); return DSERR_CONTROLUNAVAIL; } static HRESULT WINAPI PrimaryBufferImpl_Initialize( LPDIRECTSOUNDBUFFER8 iface,LPDIRECTSOUND8 dsound,LPDSBUFFERDESC dbsd ) { ICOM_THIS(PrimaryBufferImpl,iface); FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd); DPRINTF("Re-Init!!!\n"); return DSERR_ALREADYINITIALIZED; } static HRESULT WINAPI PrimaryBufferImpl_GetCaps( LPDIRECTSOUNDBUFFER8 iface,LPDSBCAPS caps ) { ICOM_THIS(PrimaryBufferImpl,iface); TRACE("(%p)->(%p)\n",This,caps); if (caps == NULL) return DSERR_INVALIDPARAM; /* I think we should check this value, not set it. See */ /* Inside DirectX, p215. That should apply here, too. */ caps->dwSize = sizeof(*caps); caps->dwFlags = This->dsbd.dwFlags; if (This->dsound->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE; else caps->dwFlags |= DSBCAPS_LOCSOFTWARE; caps->dwBufferBytes = This->dsound->buflen; /* This value represents the speed of the "unlock" command. As unlock is quite fast (it does not do anything), I put 4096 ko/s = 4 Mo / s */ /* FIXME: hwbuf speed */ caps->dwUnlockTransferRate = 4096; caps->dwPlayCpuOverhead = 0; return DS_OK; } static HRESULT WINAPI PrimaryBufferImpl_QueryInterface( LPDIRECTSOUNDBUFFER8 iface,REFIID riid,LPVOID *ppobj ) { ICOM_THIS(PrimaryBufferImpl,iface); TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj); if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) { ERR("app requested IDirectSoundNotify on primary buffer\n"); /* should we support this? */ *ppobj = NULL; return E_FAIL; } if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) { ERR("app requested IDirectSound3DBuffer on primary buffer\n"); *ppobj = NULL; return E_NOINTERFACE; } if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) { if (!This->dsound->listener) IDirectSound3DListenerImpl_Create(This, &This->dsound->listener); *ppobj = This->dsound->listener; if (This->dsound->listener) { IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj); return DS_OK; } return E_FAIL; } if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) { #if 0 if (!This->iks) IKsPropertySetImpl_Create(This, &This->iks); *ppobj = This->iks; if (*ppobj) { IKsPropertySet_AddRef((LPKSPROPERTYSET)*ppobj); return S_OK; } return E_FAIL; #else FIXME("app requested IKsPropertySet on primary buffer\n"); *ppobj = NULL; return E_FAIL; #endif } FIXME( "Unknown IID %s\n", debugstr_guid( riid ) ); *ppobj = NULL; return E_NOINTERFACE; } static ICOM_VTABLE(IDirectSoundBuffer8) dspbvt = { ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE PrimaryBufferImpl_QueryInterface, PrimaryBufferImpl_AddRef, PrimaryBufferImpl_Release, PrimaryBufferImpl_GetCaps, PrimaryBufferImpl_GetCurrentPosition, PrimaryBufferImpl_GetFormat, PrimaryBufferImpl_GetVolume, PrimaryBufferImpl_GetPan, PrimaryBufferImpl_GetFrequency, PrimaryBufferImpl_GetStatus, PrimaryBufferImpl_Initialize, PrimaryBufferImpl_Lock, PrimaryBufferImpl_Play, PrimaryBufferImpl_SetCurrentPosition, PrimaryBufferImpl_SetFormat, PrimaryBufferImpl_SetVolume, PrimaryBufferImpl_SetPan, PrimaryBufferImpl_SetFrequency, PrimaryBufferImpl_Stop, PrimaryBufferImpl_Unlock, PrimaryBufferImpl_Restore, PrimaryBufferImpl_SetFX, PrimaryBufferImpl_AcquireResources, PrimaryBufferImpl_GetObjectInPath }; HRESULT WINAPI PrimaryBuffer_Create( IDirectSoundImpl *This, PrimaryBufferImpl **pdsb, LPDSBUFFERDESC dsbd) { PrimaryBufferImpl *dsb; if (dsbd->lpwfxFormat) return DSERR_INVALIDPARAM; dsb = (PrimaryBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb)); dsb->ref = 1; dsb->dsound = This; ICOM_VTBL(dsb) = &dspbvt; memcpy(&dsb->dsbd, dsbd, sizeof(*dsbd)); TRACE("Created primary buffer at %p\n", dsb); if (dsbd->dwFlags & DSBCAPS_CTRL3D) { /* FIXME: IDirectSound3DListener */ } IDirectSound8_AddRef((LPDIRECTSOUND8)This); *pdsb = dsb; return S_OK; }