Implemented the DirectSound HAL mode for DMA primary buffers, with a

10ms latency (not noticeable), and about 300ms of prebuffering. Fixed
a few bugs. Added a Wine-only WINMM flag for waveOutOpen for
DirectSound to use (so drivers like wineoss have some way of knowing
what to prepare itself for).
This commit is contained in:
Ove Kaaven 2000-06-24 12:54:49 +00:00 committed by Alexandre Julliard
parent 1f0c216e8f
commit 849c930481
2 changed files with 280 additions and 55 deletions

View File

@ -55,6 +55,8 @@ DEFAULT_DEBUG_CHANNEL(dsound);
/* (Starcraft videos won't work with higher than 32 x10ms) */
#define DS_HEL_MARGIN 4 /* HEL only: number of waveOut fragments ahead to mix in new buffers */
#define DS_HAL_QUEUE 28 /* HAL only: max number of fragments to prebuffer */
/* Linux does not support better timing than 10ms */
#define DS_TIME_RES 10 /* Resolution of multimedia timer */
#define DS_TIME_DEL 10 /* Delay of multimedia timer callback, and duration of HEL fragment */
@ -831,7 +833,7 @@ static ULONG WINAPI IDirectSoundNotifyImpl_Release(LPDIRECTSOUNDNOTIFY iface) {
This->ref--;
if (!This->ref) {
IDirectSoundNotify_Release((LPDIRECTSOUNDBUFFER)This->dsb);
IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
HeapFree(GetProcessHeap(),0,This);
return 0;
}
@ -918,6 +920,8 @@ static HRESULT DSOUND_PrimaryOpen(IDirectSoundBufferImpl *dsb)
LPBYTE newbuf;
DWORD buflen;
HRESULT merr = DS_OK;
/* Start in pause mode, to allow buffers to get filled */
waveOutPause(dsb->dsound->hwo);
/* use fragments of 10ms (1/100s) each (which should get us within
* the documented write cursor lead of 10-15ms) */
buflen = ((dsb->wfx.nAvgBytesPerSec / 100) & ~3) * DS_HEL_FRAGS;
@ -986,6 +990,7 @@ static HRESULT WINAPI IDirectSoundBufferImpl_SetFormat(
) {
ICOM_THIS(IDirectSoundBufferImpl,iface);
IDirectSoundBufferImpl** dsb;
HRESULT err = DS_OK;
int i;
/* Let's be pedantic! */
@ -1018,7 +1023,7 @@ static HRESULT WINAPI IDirectSoundBufferImpl_SetFormat(
memcpy(&(primarybuf->wfx), wfex, sizeof(primarybuf->wfx));
TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld"
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,
@ -1032,13 +1037,19 @@ static HRESULT WINAPI IDirectSoundBufferImpl_SetFormat(
waveOutClose(This->dsound->hwo);
This->dsound->hwo = 0;
waveOutOpen(&(This->dsound->hwo), This->dsound->drvdesc.dnDevNode,
&(primarybuf->wfx), 0, 0, 0);
/* Start in pause mode until buffers are filled */
waveOutPause(This->dsound->hwo);
&(primarybuf->wfx), 0, 0, CALLBACK_NULL | WAVE_DIRECTSOUND);
DSOUND_PrimaryOpen(primarybuf);
}
if (primarybuf->hwbuf)
IDsDriverBuffer_SetFormat(primarybuf->hwbuf, &(primarybuf->wfx));
if (primarybuf->hwbuf) {
err = IDsDriverBuffer_SetFormat(primarybuf->hwbuf, &(primarybuf->wfx));
if (err == DSERR_BUFFERLOST) {
/* Wine-only: the driver wants us to recreate the HW buffer */
IDsDriverBuffer_Release(primarybuf->hwbuf);
err = IDsDriver_CreateSoundBuffer(primarybuf->dsound->driver,&(primarybuf->wfx),primarybuf->dsbd.dwFlags,0,
&(primarybuf->buflen),&(primarybuf->buffer),
(LPVOID)&(primarybuf->hwbuf));
}
}
DSOUND_RecalcFormat(primarybuf);
LeaveCriticalSection(&(This->dsound->lock));
@ -1176,18 +1187,25 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface)
static DWORD WINAPI IDirectSoundBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface) {
ICOM_THIS(IDirectSoundBufferImpl,iface);
/* TRACE("(%p) ref was %ld\n",This, This->ref); */
DWORD ref;
return ++(This->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 IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER iface) {
ICOM_THIS(IDirectSoundBufferImpl,iface);
int i;
DWORD ref;
/* TRACE("(%p) ref was %ld\n",This, This->ref); */
TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
if (--This->ref)
return This->ref;
ref = InterlockedDecrement(&(This->ref));
if (ref) return ref;
EnterCriticalSection(&(This->dsound->lock));
for (i=0;i<This->dsound->nrofbuffers;i++)
@ -1197,8 +1215,9 @@ static DWORD WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER iface) {
if (i < This->dsound->nrofbuffers) {
/* Put the last buffer of the list in the (now empty) position */
This->dsound->buffers[i] = This->dsound->buffers[This->dsound->nrofbuffers - 1];
This->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,This->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*This->dsound->nrofbuffers);
This->dsound->nrofbuffers--;
This->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,This->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*This->dsound->nrofbuffers);
TRACE("buffer count is now %d\n", This->dsound->nrofbuffers);
IDirectSound_Release((LPDIRECTSOUND)This->dsound);
}
LeaveCriticalSection(&(This->dsound->lock));
@ -1258,7 +1277,7 @@ static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
*playpos = 0;
}
else if (playpos) {
DWORD pplay, lplay, splay;
DWORD pplay, lplay, splay, pstate;
/* let's get this exact; first, recursively call GetPosition on the primary */
EnterCriticalSection(&(primarybuf->lock));
if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || primarybuf->hwbuf) {
@ -1270,6 +1289,7 @@ static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
}
/* get last mixed primary play position */
lplay = primarybuf->mixpos;
pstate = primarybuf->state;
/* get our own last mixed position while we still have the lock */
splay = This->mixpos;
LeaveCriticalSection(&(primarybuf->lock));
@ -1279,6 +1299,14 @@ static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
/* the actual primary play position (pplay) is always behind last mixed (lplay)
* (unless the computer is too slow, which we can't fix anyway) */
/* we need to know how far away we are from there */
if (lplay == pplay) {
if ((pstate == STATE_PLAYING) || (pstate == STATE_STOPPING)) {
/* wow, the software mixer is really doing well,
* seems the entire primary buffer is filled! */
lplay += primarybuf->buflen;
}
/* else: the primary buffer is not playing, so probably empty */
}
if (lplay < pplay) lplay += primarybuf->buflen; /* wraparound */
lplay -= pplay;
/* divide the offset by its sample size */
@ -1315,7 +1343,7 @@ static HRESULT WINAPI IDirectSoundBufferImpl_GetStatus(
LPDIRECTSOUNDBUFFER iface,LPDWORD status
) {
ICOM_THIS(IDirectSoundBufferImpl,iface);
TRACE("(%p,%p)\n",This,status);
TRACE("(%p,%p), thread is %lx\n",This,status,GetCurrentThreadId());
if (status == NULL)
return DSERR_INVALIDPARAM;
@ -1326,6 +1354,7 @@ static HRESULT WINAPI IDirectSoundBufferImpl_GetStatus(
if (This->playflags & DSBPLAY_LOOPING)
*status |= DSBSTATUS_LOOPING;
TRACE("status=%lx\n", *status);
return DS_OK;
}
@ -1844,15 +1873,29 @@ static HRESULT WINAPI IDirectSoundImpl_CreateSoundBuffer(
EnterCriticalSection(&(This->lock));
/* register buffer */
if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
This->buffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl*)*(This->nrofbuffers+1));
This->buffers[This->nrofbuffers] = *ippdsb;
This->nrofbuffers++;
IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl*)*(This->nrofbuffers+1));
if (newbuffers) {
This->buffers = newbuffers;
This->buffers[This->nrofbuffers] = *ippdsb;
This->nrofbuffers++;
TRACE("buffer count is now %d\n", This->nrofbuffers);
} else {
ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
err = DSERR_OUTOFMEMORY;
}
}
LeaveCriticalSection(&(This->lock));
IDirectSound_AddRef(iface);
InitializeCriticalSection(&((*ippdsb)->lock));
if (err != DS_OK) {
/* oops... */
IDirectSoundBuffer_Release(*ppdsb);
*ippdsb = NULL;
return err;
}
#if USE_DSOUND3D
if (dsbd->dwFlags & DSBCAPS_CTRL3D) {
@ -1921,11 +1964,20 @@ static HRESULT WINAPI IDirectSoundImpl_DuplicateSoundBuffer(
memcpy(&((*ippdsb)->wfx), &(ipdsb->wfx), sizeof((*ippdsb)->wfx));
/* register buffer */
EnterCriticalSection(&(This->lock));
This->buffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl**)*(This->nrofbuffers+1));
This->buffers[This->nrofbuffers] = *ippdsb;
This->nrofbuffers++;
IDirectSound_AddRef(iface);
{
IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl**)*(This->nrofbuffers+1));
if (newbuffers) {
This->buffers = newbuffers;
This->buffers[This->nrofbuffers] = *ippdsb;
This->nrofbuffers++;
TRACE("buffer count is now %d\n", This->nrofbuffers);
} else {
ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
/* FIXME: release buffer */
}
}
LeaveCriticalSection(&(This->lock));
IDirectSound_AddRef(iface);
return DS_OK;
}
@ -1992,7 +2044,8 @@ static ULONG WINAPI IDirectSoundImpl_Release(LPDIRECTSOUND iface) {
timeKillEvent(This->timerID);
timeEndPeriod(DS_TIME_RES);
IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)primarybuf);
if (primarybuf)
IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)primarybuf);
if (This->buffers) {
for( i=0;i<This->nrofbuffers;i++)
@ -2413,14 +2466,14 @@ static void *DSOUND_tmpbuffer(size_t len)
return tmp_buffer;
}
static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos)
static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen)
{
INT i, len, ilen, temp, field;
INT advance = primarybuf->wfx.wBitsPerSample >> 3;
BYTE *buf, *ibuf, *obuf;
INT16 *ibufs, *obufs;
len = dsound->fraglen;
len = fraglen;
if (!(dsb->playflags & DSBPLAY_LOOPING)) {
temp = MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buflen,
dsb->nAvgBytesPerSec) -
@ -2510,29 +2563,29 @@ static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos)
return len;
}
static DWORD WINAPI DSOUND_MixPrimary(DWORD writepos, BOOL starting)
static DWORD WINAPI DSOUND_MixPrimary(DWORD writepos, DWORD fraglen, BOOL starting)
{
INT i, len, maxlen = 0;
IDirectSoundBufferImpl *dsb;
TRACE("(%ld,%ld,%d)\n", writepos, fraglen, starting);
for (i = dsound->nrofbuffers - 1; i >= 0; i--) {
dsb = dsound->buffers[i];
if (!dsb || !(ICOM_VTBL(dsb)))
continue;
IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)dsb);
if (dsb->buflen && dsb->state && !(starting && (dsb->state != STATE_STARTING))) {
TRACE("Checking %p\n", dsb);
EnterCriticalSection(&(dsb->lock));
if (dsb->state == STATE_STOPPING) {
/* FIXME: perhaps attempt to remove the buffer from the prebuffer */
dsb->state = STATE_STOPPED;
} else {
len = DSOUND_MixInBuffer(dsb, writepos);
len = DSOUND_MixInBuffer(dsb, writepos, fraglen);
maxlen = len > maxlen ? len : maxlen;
}
LeaveCriticalSection(&(dsb->lock));
}
IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)dsb);
}
return maxlen;
@ -2557,6 +2610,7 @@ static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw
{
DWORD len;
int nfiller;
BOOL forced;
if (!dsound || !primarybuf) {
ERR("dsound died without killing us?\n");
@ -2567,20 +2621,180 @@ static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw
EnterCriticalSection(&(dsound->lock));
if (!primarybuf || !primarybuf->ref) {
/* seems the primary buffer is currently being released */
LeaveCriticalSection(&(dsound->lock));
return;
}
/* the sound of silence */
nfiller = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
/* whether the primary is forced to play even without secondary buffers */
forced = ((primarybuf->state == STATE_PLAYING) || (primarybuf->state == STATE_STARTING));
if (primarybuf->hwbuf) {
DWORD playpos, writepos;
IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, &writepos);
/* Well, we *could* do Just-In-Time mixing using the writepos,
* but that's a little bit ambitious and unnecessary...
* let's stick with the fraglen */
/* FIXME: do something clever */
if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
BOOL paused = ((primarybuf->state == STATE_STOPPED) || (primarybuf->state == STATE_STARTING));
DWORD playpos, writepos, inq, maxq, mixq, frag;
IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, &writepos);
/* Well, we *could* do Just-In-Time mixing using the writepos,
* but that's a little bit ambitious and unnecessary... */
/* rather add our safety margin to the writepos, if we're playing */
if (!paused) {
writepos += primarybuf->writelead;
while (writepos >= primarybuf->buflen)
writepos -= primarybuf->buflen;
} else writepos = playpos;
TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld\n",
playpos,writepos,primarybuf->playpos,primarybuf->mixpos);
/* wipe out just-played sound data */
if (playpos < primarybuf->playpos) {
memset(primarybuf->buffer + primarybuf->playpos, nfiller, primarybuf->buflen - primarybuf->playpos);
memset(primarybuf->buffer, nfiller, playpos);
} else {
memset(primarybuf->buffer + primarybuf->playpos, nfiller, playpos - primarybuf->playpos);
}
primarybuf->playpos = playpos;
/* check how much prebuffering is left */
inq = primarybuf->mixpos;
if (inq < writepos)
inq += primarybuf->buflen;
inq -= writepos;
/* find the maximum we can prebuffer */
if (!paused) {
maxq = playpos;
if (maxq < writepos)
maxq += primarybuf->buflen;
maxq -= writepos;
} else maxq = primarybuf->buflen;
/* clip maxq to DS_HAL_QUEUE */
frag = DS_HAL_QUEUE * dsound->fraglen;
if (maxq > frag) maxq = frag;
/* check for consistency */
if (inq > maxq) {
/* the playback position must have passed our last
* mixed position, i.e. it's an underrun, or we have
* nothing more to play */
inq = 0;
/* stop the playback now, to allow buffers to refill */
IDsDriverBuffer_Stop(primarybuf->hwbuf);
if (primarybuf->state == STATE_PLAYING) {
primarybuf->state = STATE_STARTING;
}
else if (primarybuf->state == STATE_STOPPING) {
primarybuf->state = STATE_STOPPED;
}
else {
/* how can we have an underrun if we aren't playing? */
ERR("unexpected primary state (%ld)\n", primarybuf->state);
}
/* the Stop is supposed to reset play position to beginning of buffer */
/* unfortunately, OSS is not able to do so, so get current pointer */
IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, NULL);
writepos = playpos;
primarybuf->playpos = playpos;
primarybuf->mixpos = playpos;
inq = 0;
maxq = primarybuf->buflen;
if (maxq > frag) maxq = frag;
memset(primarybuf->buffer, nfiller, primarybuf->buflen);
paused = TRUE;
}
EnterCriticalSection(&(primarybuf->lock));
/* see if some new buffers have been started that we want to merge into our prebuffer;
* this should minimize latency even when we have a large prebuffer */
if (!paused) {
if (primarybuf->mixpos < writepos) {
/* mix to end of buffer */
len = DSOUND_MixPrimary(writepos, primarybuf->buflen - writepos, TRUE);
if ((len + writepos) < primarybuf->buflen)
goto addmix_complete;
/* mix from beginning of buffer */
if (primarybuf->mixpos)
len = DSOUND_MixPrimary(0, primarybuf->mixpos, TRUE);
} else {
/* mix middle of buffer */
len = DSOUND_MixPrimary(writepos, primarybuf->mixpos - writepos, TRUE);
}
}
addmix_complete:
DSOUND_MarkPlaying();
mixq = maxq - inq;
TRACE("queued %ld, max %ld, mixing %ld, paused %d\n", inq, maxq, mixq, paused);
/* it's too inefficient to mix less than a fragment at a time */
if (mixq >= dsound->fraglen) {
#define FRAG_MIXER \
if (frag > mixq) frag = mixq; \
len = DSOUND_MixPrimary(primarybuf->mixpos, frag, FALSE); \
if (forced) len = frag; \
primarybuf->mixpos += len; \
mixq -= len; inq += len
if ((playpos < writepos) || (paused && (playpos == writepos))) {
if (primarybuf->mixpos) {
/* mix to end of buffer */
frag = primarybuf->buflen - primarybuf->mixpos;
FRAG_MIXER;
if (primarybuf->mixpos < primarybuf->buflen)
goto mix_complete;
primarybuf->mixpos = 0;
}
if (mixq >= dsound->fraglen) {
/* mix from beginning of buffer */
frag = playpos;
if ((!frag) && paused) frag = primarybuf->buflen;
FRAG_MIXER;
}
}
else if (playpos > writepos) {
/* mix middle of buffer */
frag = playpos - primarybuf->mixpos;
FRAG_MIXER;
}
else {
/* this should preferably not happen... */
ERR("mixer malfunction (ambiguous writepos)!\n");
}
#undef FRAG_MIXER
}
mix_complete:
if (inq) {
/* buffers have been filled, restart playback */
if (primarybuf->state == STATE_STARTING) {
IDsDriverBuffer_Play(primarybuf->hwbuf, 0, 0, DSBPLAY_LOOPING);
primarybuf->state = STATE_PLAYING;
}
else if (primarybuf->state == STATE_STOPPED) {
/* the primarybuf is supposed to play if there's something to play
* even if it is reported as stopped, so don't let this confuse you */
IDsDriverBuffer_Play(primarybuf->hwbuf, 0, 0, DSBPLAY_LOOPING);
primarybuf->state = STATE_STOPPING;
}
}
LeaveCriticalSection(&(primarybuf->lock));
} else {
/* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
if (primarybuf->state == STATE_STARTING) {
IDsDriverBuffer_Play(primarybuf->hwbuf, 0, 0, DSBPLAY_LOOPING);
primarybuf->state = STATE_PLAYING;
}
else if (primarybuf->state == STATE_STOPPING) {
IDsDriverBuffer_Stop(primarybuf->hwbuf);
primarybuf->state = STATE_STOPPED;
}
}
} else {
/* using waveOut stuff */
/* if no buffers are playing, we should be in pause mode now */
BOOL in_pause = !dsound->pwqueue;
DWORD writepos;
/* clean out completed fragments */
while (dsound->pwqueue && (dsound->pwave[dsound->pwplay]->dwFlags & WHDR_DONE)) {
@ -2595,12 +2809,18 @@ static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw
}
primarybuf->playpos = dsound->pwplay * dsound->fraglen;
TRACE("primary playpos=%ld, mixpos=%ld\n",primarybuf->playpos,primarybuf->mixpos);
if (!(in_pause || dsound->pwqueue)) {
if (!dsound->pwqueue) {
/* this is either an underrun or we have nothing more to play...
* since playback has already stopped now, we can enter pause mode,
* in order to allow buffers to refill */
waveOutPause(dsound->hwo);
in_pause = TRUE;
if (primarybuf->state == STATE_PLAYING) {
waveOutPause(dsound->hwo);
primarybuf->state = STATE_STARTING;
}
else if (primarybuf->state == STATE_STOPPING) {
waveOutPause(dsound->hwo);
primarybuf->state = STATE_STOPPED;
}
}
/* find next write position, plus some extra margin */
@ -2608,14 +2828,12 @@ static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw
while (writepos >= primarybuf->buflen) writepos -= primarybuf->buflen;
EnterCriticalSection(&(primarybuf->lock));
if (primarybuf->state == STATE_STARTING)
primarybuf->state = STATE_PLAYING;
/* see if some new buffers have been started that we want to merge into our prebuffer;
* this should minimize latency even when we have a large prebuffer */
if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
if (!in_pause) {
if ((primarybuf->state == STATE_PLAYING) || (primarybuf->state == STATE_STOPPING)) {
while (writepos != primarybuf->mixpos) {
len = DSOUND_MixPrimary(writepos, TRUE);
len = DSOUND_MixPrimary(writepos, dsound->fraglen, TRUE);
if (!len) break;
writepos += dsound->fraglen;
if (writepos >= primarybuf->buflen)
@ -2629,10 +2847,9 @@ static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw
* mix a bunch of fragments now as necessary */
while (dsound->pwqueue < DS_HEL_QUEUE) {
if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
len = DSOUND_MixPrimary(primarybuf->mixpos, FALSE);
len = DSOUND_MixPrimary(primarybuf->mixpos, dsound->fraglen, FALSE);
} else len=0;
if (primarybuf->state == STATE_PLAYING)
len = dsound->fraglen;
if (forced) len = dsound->fraglen;
/* if we have nothing to play, don't bother to */
if (!len) break;
if (len < dsound->fraglen) {
@ -2649,13 +2866,20 @@ static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw
if (dsound->pwwrite >= DS_HEL_FRAGS) dsound->pwwrite = 0;
dsound->pwqueue++;
}
if (primarybuf->state == STATE_STOPPING)
primarybuf->state = STATE_STOPPED;
LeaveCriticalSection(&(primarybuf->lock));
if (in_pause && dsound->pwqueue) {
if (dsound->pwqueue) {
/* buffers have been filled, restart playback */
waveOutRestart(dsound->hwo);
if (primarybuf->state == STATE_STARTING) {
waveOutRestart(dsound->hwo);
primarybuf->state = STATE_PLAYING;
}
else if (primarybuf->state == STATE_STOPPED) {
/* the primarybuf is supposed to play if there's something to play
* even if it is reported as stopped, so don't let this confuse you */
waveOutRestart(dsound->hwo);
primarybuf->state = STATE_STOPPING;
}
}
LeaveCriticalSection(&(primarybuf->lock));
}
LeaveCriticalSection(&(dsound->lock));
}
@ -2736,7 +2960,7 @@ HRESULT WINAPI DirectSoundCreate(REFGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pU
if ((*ippDS)->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
/* FIXME: is this right? */
err = mmErr(waveOutOpen(&((*ippDS)->hwo), (*ippDS)->drvdesc.dnDevNode, &((*ippDS)->wfx),
0, 0, CALLBACK_NULL));
0, 0, CALLBACK_NULL | WAVE_DIRECTSOUND));
}
if (drv && (err == DS_OK))
@ -2757,7 +2981,7 @@ HRESULT WINAPI DirectSoundCreate(REFGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pU
/* FIXME: look at wcaps */
(*ippDS)->drvcaps.dwFlags = DSCAPS_EMULDRIVER |
DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO | DSCAPS_CONTINUOUSRATE;
DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
/* Allocate memory for HEL buffer headers */
for (c=0; c<DS_HEL_FRAGS; c++) {
@ -2773,8 +2997,6 @@ HRESULT WINAPI DirectSoundCreate(REFGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pU
}
}
}
/* Start in pause mode until buffers are filled */
waveOutPause((*ippDS)->hwo);
}
InitializeCriticalSection(&((*ippDS)->lock));

View File

@ -222,6 +222,9 @@ typedef LPDRVCALLBACK LPWAVECALLBACK;
#define WAVE_MAPPED 0x0004
#define WAVE_FORMAT_DIRECT 0x0008
#define WAVE_FORMAT_DIRECT_QUERY (WAVE_FORMAT_QUERY | WAVE_FORMAT_DIRECT)
#ifdef __WINE__
#define WAVE_DIRECTSOUND 0x0080
#endif
typedef struct wavehdr_tag {
LPSTR lpData; /* pointer to locked data buffer */