📄 buffer.c
字号:
}
} else {
This->buffer->ref--;
if (This->buffer->ref==0) {
HeapFree(GetProcessHeap(),0,This->buffer->memory);
HeapFree(GetProcessHeap(),0,This->buffer);
}
}
HeapFree(GetProcessHeap(), 0, This->notifies);
HeapFree(GetProcessHeap(), 0, This->pwfx);
HeapFree(GetProcessHeap(), 0, This);
TRACE("(%p) released\n", This);
}
return ref;
}
DWORD DSOUND_CalcPlayPosition(IDirectSoundBufferImpl *This, DWORD pplay, DWORD pwrite)
{
DWORD bplay = This->buf_mixpos;
DWORD pmix = This->primary_mixpos;
TRACE("(%p, pplay=%lu, pwrite=%lu)\n", This, pplay, pwrite);
/* the actual primary play position (pplay) is always behind last mixed (pmix),
* unless the computer is too slow or something */
/* we need to know how far away we are from there */
if (pmix < pplay) pmix += This->dsound->device->buflen; /* wraparound */
pmix -= pplay;
/* detect buffer underrun */
if (pwrite < pplay) pwrite += This->dsound->device->buflen; /* wraparound */
pwrite -= pplay;
if (pmix > (ds_snd_queue_max * This->dsound->device->fraglen + pwrite + This->dsound->device->writelead)) {
WARN("detected an underrun: primary queue was %ld\n",pmix);
pmix = 0;
}
/* divide the offset by its sample size */
pmix /= This->dsound->device->pwfx->nBlockAlign;
TRACE("primary back-samples=%ld\n",pmix);
/* adjust for our frequency */
pmix = (pmix * This->freqAdjust) >> DSOUND_FREQSHIFT;
/* multiply by our own sample size */
pmix *= This->pwfx->nBlockAlign;
TRACE("this back-offset=%ld\n", pmix);
/* subtract from our last mixed position */
while (bplay < pmix) bplay += This->buflen; /* wraparound */
bplay -= pmix;
if (This->leadin && ((bplay < This->startpos) || (bplay > This->buf_mixpos))) {
/* seems we haven't started playing yet */
TRACE("this still in lead-in phase\n");
bplay = This->startpos;
}
/* return the result */
return bplay;
}
static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
LPDIRECTSOUNDBUFFER8 iface,LPDWORD playpos,LPDWORD writepos
) {
HRESULT hres;
IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;
TRACE("(%p,%p,%p)\n",This,playpos,writepos);
if (This->hwbuf) {
hres=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);
if (hres != DS_OK) {
WARN("IDsDriverBuffer_GetPosition failed\n");
return hres;
}
} else {
if (playpos && (This->state != STATE_PLAYING)) {
/* we haven't been merged into the primary buffer (yet) */
*playpos = This->buf_mixpos;
} else if (playpos) {
DWORD pplay, pwrite;
/* let's get this exact; first, recursively call GetPosition on the primary */
EnterCriticalSection(&(This->dsound->device->mixlock));
if (DSOUND_PrimaryGetPosition(This->dsound->device, &pplay, &pwrite) != DS_OK)
WARN("DSOUND_PrimaryGetPosition failed\n");
/* detect HEL mode underrun */
if (!(This->dsound->device->hwbuf || This->dsound->device->pwqueue))
TRACE("detected an underrun\n");
if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || This->dsound->device->hwbuf) {
/* calculate play position using this */
*playpos = DSOUND_CalcPlayPosition(This, pplay, pwrite);
} else {
/* (unless the app isn't using GETCURRENTPOSITION2) */
/* don't know exactly how this should be handled...
* the docs says that play cursor is reported as directly
* behind write cursor, hmm... */
/* let's just do what might work for Half-Life */
DWORD wp;
wp = (This->dsound->device->pwplay + ds_hel_margin) * This->dsound->device->fraglen;
wp %= This->dsound->device->buflen;
*playpos = DSOUND_CalcPlayPosition(This, wp, pwrite);
}
LeaveCriticalSection(&(This->dsound->device->mixlock));
}
if (writepos)
*writepos = This->buf_mixpos;
}
if (writepos) {
if (This->state != STATE_STOPPED) {
/* apply the documented 10ms lead to writepos */
*writepos += This->writelead;
}
*writepos %= This->buflen;
}
if (playpos)
This->last_playpos = *playpos;
TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
return DS_OK;
}
static HRESULT WINAPI IDirectSoundBufferImpl_GetStatus(
LPDIRECTSOUNDBUFFER8 iface,LPDWORD status
) {
IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;
TRACE("(%p,%p), thread is %04lx\n",This,status,GetCurrentThreadId());
if (status == NULL) {
WARN("invalid parameter: status = NULL\n");
return DSERR_INVALIDPARAM;
}
*status = 0;
if ((This->state == STATE_STARTING) || (This->state == STATE_PLAYING)) {
*status |= DSBSTATUS_PLAYING;
if (This->playflags & DSBPLAY_LOOPING)
*status |= DSBSTATUS_LOOPING;
}
TRACE("status=%lx\n", *status);
return DS_OK;
}
static HRESULT WINAPI IDirectSoundBufferImpl_GetFormat(
LPDIRECTSOUNDBUFFER8 iface,
LPWAVEFORMATEX lpwf,
DWORD wfsize,
LPDWORD wfwritten)
{
DWORD size;
IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;
TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
size = sizeof(WAVEFORMATEX) + This->pwfx->cbSize;
if (lpwf) { /* NULL is valid */
if (wfsize >= size) {
CopyMemory(lpwf,This->pwfx,size);
if (wfwritten)
*wfwritten = size;
} else {
WARN("invalid parameter: wfsize too small\n");
if (wfwritten)
*wfwritten = 0;
return DSERR_INVALIDPARAM;
}
} else {
if (wfwritten)
*wfwritten = sizeof(WAVEFORMATEX) + This->pwfx->cbSize;
else {
WARN("invalid parameter: wfwritten == NULL\n");
return DSERR_INVALIDPARAM;
}
}
return DS_OK;
}
static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
LPDIRECTSOUNDBUFFER8 iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
) {
HRESULT hres = DS_OK;
IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;
TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx) at %ld\n",
This,
writecursor,
writebytes,
lplpaudioptr1,
audiobytes1,
lplpaudioptr2,
audiobytes2,
flags,
GetTickCount()
);
if (flags & DSBLOCK_FROMWRITECURSOR) {
DWORD writepos;
/* GetCurrentPosition does too much magic to duplicate here */
hres = IDirectSoundBufferImpl_GetCurrentPosition(iface, NULL, &writepos);
if (hres != DS_OK) {
WARN("IDirectSoundBufferImpl_GetCurrentPosition failed\n");
return hres;
}
writecursor += writepos;
}
writecursor %= This->buflen;
if (flags & DSBLOCK_ENTIREBUFFER)
writebytes = This->buflen;
if (writebytes > This->buflen)
writebytes = This->buflen;
EnterCriticalSection(&(This->lock));
if ((writebytes == This->buflen) &&
((This->state == STATE_STARTING) ||
(This->state == STATE_PLAYING)))
/* some games, like Half-Life, try to be clever (not) and
* keep one secondary buffer, and mix sounds into it itself,
* locking the entire buffer every time... so we can just forget
* about tracking the last-written-to-position... */
This->probably_valid_to = (DWORD)-1;
else
This->probably_valid_to = writecursor;
if (!(This->dsound->device->drvdesc.dwFlags & DSDDESC_DONTNEEDSECONDARYLOCK) && This->hwbuf) {
hres = IDsDriverBuffer_Lock(This->hwbuf,
lplpaudioptr1, audiobytes1,
lplpaudioptr2, audiobytes2,
writecursor, writebytes,
0);
if (hres != DS_OK) {
WARN("IDsDriverBuffer_Lock failed\n");
LeaveCriticalSection(&(This->lock));
return hres;
}
} else {
BOOL remix = FALSE;
if (writecursor+writebytes <= This->buflen) {
*(LPBYTE*)lplpaudioptr1 = This->buffer->memory+writecursor;
*audiobytes1 = writebytes;
if (lplpaudioptr2)
*(LPBYTE*)lplpaudioptr2 = NULL;
if (audiobytes2)
*audiobytes2 = 0;
TRACE("->%ld.0\n",writebytes);
} else {
*(LPBYTE*)lplpaudioptr1 = This->buffer->memory+writecursor;
*audiobytes1 = This->buflen-writecursor;
if (lplpaudioptr2)
*(LPBYTE*)lplpaudioptr2 = This->buffer->memory;
if (audiobytes2)
*audiobytes2 = writebytes-(This->buflen-writecursor);
TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
}
if (This->state == STATE_PLAYING) {
/* if the segment between playpos and buf_mixpos is touched,
* we need to cancel some mixing */
/* we'll assume that the app always calls GetCurrentPosition before
* locking a playing buffer, so that last_playpos is up-to-date */
if (This->buf_mixpos >= This->last_playpos) {
if (This->buf_mixpos > writecursor &&
This->last_playpos < writecursor+writebytes)
remix = TRUE;
} else {
if (This->buf_mixpos > writecursor ||
This->last_playpos < writecursor+writebytes)
remix = TRUE;
}
if (remix) {
TRACE("locking prebuffered region, ouch\n");
DSOUND_MixCancelAt(This, writecursor);
}
}
}
LeaveCriticalSection(&(This->lock));
return DS_OK;
}
static HRESULT WINAPI IDirectSoundBufferImpl_SetCurrentPosition(
LPDIRECTSOUNDBUFFER8 iface,DWORD newpos
) {
HRESULT hres = DS_OK;
IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;
TRACE("(%p,%ld)\n",This,newpos);
/* **** */
EnterCriticalSection(&(This->lock));
newpos %= This->buflen;
This->buf_mixpos = newpos;
if (This->hwbuf) {
hres = IDsDriverBuffer_SetPosition(This->hwbuf, This->buf_mixpos);
if (hres != DS_OK)
WARN("IDsDriverBuffer_SetPosition failed\n");
}
LeaveCriticalSection(&(This->lock));
/* **** */
return hres;
}
static HRESULT WINAPI IDirectSoundBufferImpl_SetPan(
LPDIRECTSOUNDBUFFER8 iface,LONG pan
) {
HRESULT hres = DS_OK;
IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;
TRACE("(%p,%ld)\n",This,pan);
if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) {
WARN("invalid parameter: pan = %ld\n", pan);
return DSERR_INVALIDPARAM;
}
/* You cannot use both pan and 3D controls */
if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
(This->dsbd.dwFlags & DSBCAPS_CTRL3D)) {
WARN("control unavailable\n");
return DSERR_CONTROLUNAVAIL;
}
/* **** */
EnterCriticalSection(&(This->lock));
if (This->volpan.lPan != pan) {
This->volpan.lPan = pan;
DSOUND_RecalcVolPan(&(This->volpan));
if (This->hwbuf) {
hres = IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
if (hres != DS_OK)
WARN("IDsDriverBuffer_SetVolumePan failed\n");
} else
DSOUND_ForceRemix(This);
}
LeaveCriticalSection(&(This->lock));
/* **** */
return hres;
}
static HRESULT WINAPI IDirectSoundBufferImpl_GetPan(
LPDIRECTSOUNDBUFFER8 iface,LPLONG pan
) {
IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;
TRACE("(%p,%p)\n",This,pan);
if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
WARN("control unavailable\n");
return DSERR_CONTROLUNAVAIL;
}
if (pan == NULL) {
WARN("invalid parameter: pan = NULL\n");
return DSERR_INVALIDPARAM;
}
*pan = This->volpan.lPan;
return DS_OK;
}
static HRESULT WINAPI IDirectSoundBufferImpl_Unlock(
LPDIRECTSOUNDBUFFER8 iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
) {
IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;
DWORD probably_valid_to;
HRESULT hres = DS_OK;
TRACE("(%p,%p,%ld,%p,%ld)\n", This,p1,x1,p2,x2);
/* **** */
EnterCriticalSection(&(This->lock));
if (!(This->dsound->device->drvdesc.dwFlags & DSDDESC_DONTNEEDSECONDARYLOCK) && This->hwbuf) {
hres = IDsDriverBuffer_Unlock(This->hwbuf, p1, x1, p2, x2);
if (hres != DS_OK)
WARN("IDsDriverBuffer_Unlock failed\n");
}
if (hres == DS_OK) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -