dsound: Transparantly fall back to software mode if hardware mode is unavailable.

This commit is contained in:
Maarten Lankhorst 2007-08-21 21:04:32 +02:00 committed by Alexandre Julliard
parent a247ca5308
commit 2ececbeda9
1 changed files with 100 additions and 161 deletions

View File

@ -57,10 +57,6 @@ static void DSOUND_RecalcPrimary(DirectSoundDevice *device)
if (device->pwfx->nSamplesPerSec >= 80000)
fraglen *= 2;
/* If in emulation mode, reduce fragment size until an integer number of them fits in the buffer */
if (!device->driver)
while (device->buflen % fraglen)
fraglen -= nBlockAlign;
device->fraglen = fraglen;
device->helfrags = device->buflen / fraglen;
TRACE("fraglen=%d helfrags=%d\n", device->fraglen, device->helfrags);
@ -77,20 +73,51 @@ static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
HRESULT err = DS_OK;
TRACE("(%p)\n", device);
if (device->driver)
{
err = IDsDriver_CreateSoundBuffer(device->driver,device->pwfx,
DSBCAPS_PRIMARYBUFFER,0,
&(device->buflen),&(device->buffer),
(LPVOID*)&(device->hwbuf));
if (err != DS_OK) {
WARN("IDsDriver_CreateSoundBuffer failed, falling back to waveout\n");
/* Wine-only: close wine directsound driver, then reopen without WAVE_DIRECTSOUND */
device->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
IDsDriver_Close(device->driver);
waveOutClose(device->hwo);
IDsDriver_Release(device->driver);
device->driver = NULL;
device->buffer = NULL;
device->hwo = 0;
err = mmErr(waveOutOpen(&(device->hwo), device->drvdesc.dnDevNode, device->pwfx, (DWORD_PTR)DSOUND_callback, (DWORD)device, CALLBACK_FUNCTION));
if (err != DS_OK)
{
WARN("Falling back to waveout failed too! Giving up\n");
return err;
}
}
}
if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
/* are we using waveOut stuff? */
if (!device->driver) {
LPBYTE newbuf;
LPWAVEHDR headers = NULL;
DWORD buflen;
HRESULT merr = DS_OK;
DWORD buflen, overshot, oldbuflen;
unsigned int c;
/* Start in pause mode, to allow buffers to get filled */
waveOutPause(device->hwo);
if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
/* on original windows, the buffer it set to a fixed size, no matter what the settings are.
on windows this size is always fixed (tested on win-xp) */
if (!device->buflen)
buflen = ds_hel_buflen;
else /* In case we move from hw accelerated to waveout */
buflen = device->buflen;
buflen -= ds_hel_buflen % device->pwfx->nBlockAlign;
TRACE("desired buflen=%d, old buffer=%p\n", buflen, device->buffer);
@ -101,18 +128,14 @@ static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
else
newbuf = HeapAlloc(GetProcessHeap(),0,buflen);
if (newbuf == NULL) {
if (!newbuf) {
ERR("failed to allocate primary buffer\n");
merr = DSERR_OUTOFMEMORY;
return DSERR_OUTOFMEMORY;
/* but the old buffer might still exist and must be re-prepared */
} else {
device->playpos = 0;
device->mixpos = 0;
device->buffer = newbuf;
device->buflen = buflen;
}
oldbuflen = device->buflen;
device->buflen = buflen;
DSOUND_RecalcPrimary(device);
if (device->pwave)
headers = HeapReAlloc(GetProcessHeap(),0,device->pwave, device->helfrags * sizeof(WAVEHDR));
@ -121,18 +144,15 @@ static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
if (!headers) {
ERR("failed to allocate wave headers\n");
merr = DSERR_OUTOFMEMORY;
HeapFree(GetProcessHeap(), 0, newbuf);
device->buflen = oldbuflen;
DSOUND_RecalcPrimary(device);
return DSERR_OUTOFMEMORY;
}
else if (device->buffer) {
unsigned c;
device->buffer = newbuf;
device->pwave = headers;
/* sanity */
if(device->buflen % device->helfrags){
ERR("Bad helfrags resolution\n");
}
/* prepare fragment headers */
for (c=0; c<device->helfrags; c++) {
device->pwave[c].lpData = (char*)device->buffer + c*device->fraglen;
@ -148,33 +168,18 @@ static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
}
}
device->pwplay = 0;
device->pwqueue = 0;
device->playpos = 0;
device->mixpos = 0;
FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
TRACE("fraglen=%d\n", device->fraglen);
}
if ((err == DS_OK) && (merr != DS_OK))
err = merr;
} else if (!device->hwbuf) {
device->playpos = 0;
device->mixpos = 0;
err = IDsDriver_CreateSoundBuffer(device->driver,device->pwfx,
DSBCAPS_PRIMARYBUFFER,0,
&(device->buflen),&(device->buffer),
(LPVOID*)&(device->hwbuf));
if (err != DS_OK) {
WARN("IDsDriver_CreateSoundBuffer failed\n");
return err;
overshot = device->buflen % device->helfrags;
/* sanity */
if(overshot)
{
WARN("helfrags (%d x %d) doesn't fit entirely in buflen (%d) overshot: %d\n", device->helfrags, device->fraglen, device->buflen, overshot);
device->pwave[device->helfrags - 1].dwBufferLength += overshot;
}
if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
device->playpos = 0;
device->mixpos = 0;
FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
TRACE("fraglen=%d\n", device->fraglen);
}
FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
DSOUND_RecalcPrimary(device);
return err;
@ -212,35 +217,7 @@ HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device)
HRESULT err = DS_OK;
TRACE("(%p)\n", device);
device->playpos = 0;
device->mixpos = 0;
device->buflen = device->pwfx->nAvgBytesPerSec;
/* FIXME: verify that hardware capabilities (DSCAPS_PRIMARY flags) match */
if (device->driver) {
err = IDsDriver_CreateSoundBuffer(device->driver,device->pwfx,
DSBCAPS_PRIMARYBUFFER,0,
&(device->buflen),&(device->buffer),
(LPVOID*)&(device->hwbuf));
if (err != DS_OK) {
WARN("IDsDriver_CreateSoundBuffer failed, falling back to waveout\n");
/* Wine-only: close wine directsound driver, then reopen without WAVE_DIRECTSOUND */
device->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
waveOutClose(device->hwo);
IDsDriver_Release(device->driver);
device->driver = NULL;
device->buffer = NULL;
device->hwo = 0;
err = mmErr(waveOutOpen(&(device->hwo), device->drvdesc.dnDevNode, device->pwfx, (DWORD_PTR)DSOUND_callback, (DWORD)device, CALLBACK_FUNCTION));
if (err != DS_OK)
{
WARN("Falling back to waveout failed too! Giving up\n");
return err;
}
}
}
device->buflen = ds_hel_buflen;
err = DSOUND_PrimaryOpen(device);
if (err != DS_OK) {
@ -302,34 +279,10 @@ HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device)
if (device->hwbuf) {
err = IDsDriverBuffer_Stop(device->hwbuf);
if (err == DSERR_BUFFERLOST) {
DWORD flags = CALLBACK_FUNCTION | WAVE_DIRECTSOUND;
/* Wine-only: the driver wants us to reopen the device */
/* FIXME: check for errors */
IDsDriverBuffer_Release(device->hwbuf);
waveOutClose(device->hwo);
device->hwo = 0;
err = mmErr(waveOutOpen(&(device->hwo), device->drvdesc.dnDevNode,
device->pwfx, (DWORD_PTR)DSOUND_callback, (DWORD)device,
flags));
if (err == DS_OK) {
device->playpos = 0;
device->mixpos = 0;
err = IDsDriver_CreateSoundBuffer(device->driver,device->pwfx,
DSBCAPS_PRIMARYBUFFER,0,
&(device->buflen),&(device->buffer),
(LPVOID)&(device->hwbuf));
if (err != DS_OK)
WARN("IDsDriver_CreateSoundBuffer failed\n");
else if (device->state == STATE_STOPPING)
device->state = STATE_STOPPED;
else if (device->state == STATE_PLAYING)
device->state = STATE_STARTING;
if (err == DS_OK)
FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
} else {
WARN("waveOutOpen failed\n");
}
DSOUND_PrimaryClose(device);
err = DSOUND_PrimaryOpen(device);
if (FAILED(err))
WARN("DSOUND_PrimaryOpen failed\n");
} else if (err != DS_OK) {
WARN("IDsDriverBuffer_Stop failed\n");
}
@ -423,50 +376,36 @@ HRESULT DSOUND_PrimarySetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex)
CopyMemory(device->pwfx, wfex, cp_size);
if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) {
DWORD flags = CALLBACK_FUNCTION;
if (device->driver)
flags |= WAVE_DIRECTSOUND;
/* FIXME: check for errors */
if (!(device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) && device->hwbuf) {
err = IDsDriverBuffer_SetFormat(device->hwbuf, device->pwfx);
if (err != DSERR_BUFFERLOST && FAILED(err)) {
WARN("IDsDriverBuffer_SetFormat failed\n");
goto done;
}
else DSOUND_RecalcPrimary(device);
}
if (err == DSERR_BUFFERLOST || device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT)
{
DSOUND_PrimaryClose(device);
if (!device->driver)
{
waveOutClose(device->hwo);
device->hwo = 0;
err = mmErr(waveOutOpen(&(device->hwo), device->drvdesc.dnDevNode,
device->pwfx, (DWORD_PTR)DSOUND_callback, (DWORD)device,
flags));
if (err == DS_OK) {
err = mmErr(waveOutOpen(&(device->hwo), device->drvdesc.dnDevNode, device->pwfx, (DWORD_PTR)DSOUND_callback, (DWORD)device, CALLBACK_FUNCTION));
if (FAILED(err))
{
WARN("waveOutOpen failed\n");
goto done;
}
}
err = DSOUND_PrimaryOpen(device);
if (err != DS_OK) {
WARN("DSOUND_PrimaryOpen failed\n");
goto done;
}
} else {
WARN("waveOutOpen failed\n");
goto done;
}
} else if (device->hwbuf) {
err = IDsDriverBuffer_SetFormat(device->hwbuf, device->pwfx);
if (err == DSERR_BUFFERLOST) {
/* Wine-only: the driver wants us to recreate the HW buffer */
IDsDriverBuffer_Release(device->hwbuf);
device->playpos = 0;
device->mixpos = 0;
err = IDsDriver_CreateSoundBuffer(device->driver,device->pwfx,
DSBCAPS_PRIMARYBUFFER,0,
&(device->buflen),&(device->buffer),
(LPVOID)&(device->hwbuf));
if (err != DS_OK) {
WARN("IDsDriver_CreateSoundBuffer failed\n");
goto done;
}
if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
} else if (FAILED(err)) {
WARN("IDsDriverBuffer_SetFormat failed\n");
goto done;
}
/* FIXME: should we set err back to DS_OK in all cases ? */
DSOUND_RecalcPrimary(device);
}
if (nSamplesPerSec != device->pwfx->nSamplesPerSec || bpp != device->pwfx->wBitsPerSample || chans != device->pwfx->nChannels) {