dsound: Prevent reopening device from leaving dsound object in invalid state.

Signed-off-by: Andrew Eikum <aeikum@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Maarten Lankhorst 2016-05-17 13:40:33 -05:00 committed by Alexandre Julliard
parent 474033a4b8
commit 2fb97be187
3 changed files with 180 additions and 215 deletions

View File

@ -161,24 +161,20 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice)
device->guid = GUID_NULL;
/* Set default wave format (may need it for waveOutOpen) */
device->pwfx = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEFORMATEXTENSIBLE));
device->primary_pwfx = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEFORMATEXTENSIBLE));
if (!device->pwfx || !device->primary_pwfx) {
if (!device->primary_pwfx) {
WARN("out of memory\n");
HeapFree(GetProcessHeap(),0,device->primary_pwfx);
HeapFree(GetProcessHeap(),0,device->pwfx);
HeapFree(GetProcessHeap(),0,device);
return DSERR_OUTOFMEMORY;
}
device->pwfx->wFormatTag = WAVE_FORMAT_PCM;
device->pwfx->nSamplesPerSec = 22050;
device->pwfx->wBitsPerSample = 8;
device->pwfx->nChannels = 2;
device->pwfx->nBlockAlign = device->pwfx->wBitsPerSample * device->pwfx->nChannels / 8;
device->pwfx->nAvgBytesPerSec = device->pwfx->nSamplesPerSec * device->pwfx->nBlockAlign;
device->pwfx->cbSize = 0;
memcpy(device->primary_pwfx, device->pwfx, sizeof(*device->pwfx));
device->primary_pwfx->wFormatTag = WAVE_FORMAT_PCM;
device->primary_pwfx->nSamplesPerSec = 22050;
device->primary_pwfx->wBitsPerSample = 8;
device->primary_pwfx->nChannels = 2;
device->primary_pwfx->nBlockAlign = device->primary_pwfx->wBitsPerSample * device->primary_pwfx->nChannels / 8;
device->primary_pwfx->nAvgBytesPerSec = device->primary_pwfx->nSamplesPerSec * device->primary_pwfx->nBlockAlign;
device->primary_pwfx->cbSize = 0;
InitializeCriticalSection(&(device->mixlock));
device->mixlock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DirectSoundDevice.mixlock");
@ -228,12 +224,12 @@ static ULONG DirectSoundDevice_Release(DirectSoundDevice * device)
if (hr != DS_OK)
WARN("DSOUND_PrimaryDestroy failed\n");
if(device->client)
if(device->client) {
IAudioClient_Stop(device->client);
IAudioClient_Release(device->client);
}
if(device->render)
IAudioRenderClient_Release(device->render);
if(device->clock)
IAudioClock_Release(device->clock);
if(device->volume)
IAudioStreamVolume_Release(device->volume);
@ -323,6 +319,7 @@ static HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGU
device->mmdevice = mmdevice;
device->guid = devGUID;
device->sleepev = CreateEventW(0, 0, 0, 0);
device->buflen = ds_hel_buflen;
hr = DSOUND_ReopenDevice(device, FALSE);
if (FAILED(hr))
@ -381,13 +378,9 @@ static HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGU
ZeroMemory(&device->volpan, sizeof(device->volpan));
hr = DSOUND_PrimaryCreate(device);
if (hr == DS_OK) {
device->thread_finished = CreateEventW(0, 0, 0, 0);
device->thread = CreateThread(0, 0, DSOUND_mixthread, device, 0, 0);
SetThreadPriority(device->thread, THREAD_PRIORITY_TIME_CRITICAL);
} else
WARN("DSOUND_PrimaryCreate failed: %08x\n", hr);
device->thread_finished = CreateEventW(0, 0, 0, 0);
device->thread = CreateThread(0, 0, DSOUND_mixthread, device, 0, 0);
SetThreadPriority(device->thread, THREAD_PRIORITY_TIME_CRITICAL);
*ppDevice = device;
list_add_tail(&DSOUND_renderers, &device->entry);
@ -851,7 +844,6 @@ static HRESULT WINAPI IDirectSound8Impl_SetCooperativeLevel(IDirectSound8 *iface
{
IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
DirectSoundDevice *device = This->device;
DWORD oldlevel;
HRESULT hr = S_OK;
TRACE("(%p,%p,%s)\n", This, hwnd, dumpCooperativeLevel(level));
@ -868,15 +860,10 @@ static HRESULT WINAPI IDirectSound8Impl_SetCooperativeLevel(IDirectSound8 *iface
RtlAcquireResourceExclusive(&device->buffer_list_lock, TRUE);
EnterCriticalSection(&device->mixlock);
oldlevel = device->priolevel;
device->priolevel = level;
if ((level == DSSCL_WRITEPRIMARY) != (oldlevel == DSSCL_WRITEPRIMARY)) {
if ((level == DSSCL_WRITEPRIMARY) != (device->priolevel == DSSCL_WRITEPRIMARY))
hr = DSOUND_ReopenDevice(device, level == DSSCL_WRITEPRIMARY);
if (FAILED(hr))
device->priolevel = oldlevel;
else
DSOUND_PrimaryOpen(device);
}
if (SUCCEEDED(hr))
device->priolevel = level;
LeaveCriticalSection(&device->mixlock);
RtlReleaseResource(&device->buffer_list_lock);
return hr;

View File

@ -100,7 +100,6 @@ struct DirectSoundDevice
IMMDevice *mmdevice;
IAudioClient *client;
IAudioClock *clock;
IAudioStreamVolume *volume;
IAudioRenderClient *render;
@ -204,13 +203,11 @@ void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) DECLSPEC_HIDDEN;
/* primary.c */
HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device) DECLSPEC_HIDDEN;
HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device) DECLSPEC_HIDDEN;
HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device) DECLSPEC_HIDDEN;
HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device) DECLSPEC_HIDDEN;
LPWAVEFORMATEX DSOUND_CopyFormat(LPCWAVEFORMATEX wfex) DECLSPEC_HIDDEN;
HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave) DECLSPEC_HIDDEN;
HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device) DECLSPEC_HIDDEN;
HRESULT primarybuffer_create(DirectSoundDevice *device, IDirectSoundBufferImpl **ppdsb,
const DSBUFFERDESC *dsbd) DECLSPEC_HIDDEN;
void primarybuffer_destroy(IDirectSoundBufferImpl *This) DECLSPEC_HIDDEN;

View File

@ -197,16 +197,8 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client
return S_OK;
}
HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
static void DSOUND_ReleaseDevice(DirectSoundDevice *device)
{
WAVEFORMATEX *wfx = NULL;
HRESULT hres;
REFERENCE_TIME period, buflen = 800000;
UINT32 frames;
DWORD period_ms;
TRACE("(%p, %d)\n", device, forcewave);
if(device->client){
IAudioClient_Release(device->client);
device->client = NULL;
@ -215,10 +207,6 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
IAudioRenderClient_Release(device->render);
device->render = NULL;
}
if(device->clock){
IAudioClock_Release(device->clock);
device->clock = NULL;
}
if(device->volume){
IAudioStreamVolume_Release(device->volume);
device->volume = NULL;
@ -229,145 +217,75 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
device->playpos %= device->buflen;
device->pad = 0;
}
}
hres = IMMDevice_Activate(device->mmdevice, &IID_IAudioClient,
CLSCTX_INPROC_SERVER, NULL, (void **)&device->client);
if(FAILED(hres)) {
WARN("Activate failed: %08x\n", hres);
return hres;
static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device, WAVEFORMATEX *wfx, DWORD frames, BOOL forcewave)
{
IDirectSoundBufferImpl** dsb = device->buffers;
LPBYTE newbuf;
DWORD new_buflen;
BOOL mixfloat = FALSE;
int i;
TRACE("(%p)\n", device);
new_buflen = device->buflen;
new_buflen -= new_buflen % wfx->nBlockAlign;
if (wfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
(wfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
IsEqualGUID(&((WAVEFORMATEXTENSIBLE*)wfx)->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
mixfloat = TRUE;
/* reallocate emulated primary buffer */
if (forcewave) {
if (device->buffer)
newbuf = HeapReAlloc(GetProcessHeap(), 0, device->buffer, new_buflen);
else
newbuf = HeapAlloc(GetProcessHeap(), 0, new_buflen);
if (!newbuf) {
ERR("failed to allocate primary buffer\n");
return DSERR_OUTOFMEMORY;
}
FillMemory(newbuf, new_buflen, (wfx->wBitsPerSample == 8) ? 128 : 0);
} else if (!mixfloat) {
DWORD alloc_len = frames * sizeof(float);
if (device->buffer)
newbuf = HeapReAlloc(GetProcessHeap(), 0, device->buffer, alloc_len);
else
newbuf = HeapAlloc(GetProcessHeap(), 0, alloc_len);
if (!newbuf) {
ERR("failed to allocate primary buffer\n");
return DSERR_OUTOFMEMORY;
}
FillMemory(newbuf, alloc_len, (wfx->wBitsPerSample == 8) ? 128 : 0);
} else {
HeapFree(GetProcessHeap(), 0, device->buffer);
newbuf = NULL;
}
device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, 0);
DSOUND_ParseSpeakerConfig(device);
hres = DSOUND_WaveFormat(device, device->client, forcewave, &wfx);
if (FAILED(hres)) {
IAudioClient_Release(device->client);
device->client = NULL;
return hres;
}
device->buffer = newbuf;
device->buflen = new_buflen;
HeapFree(GetProcessHeap(), 0, device->pwfx);
device->pwfx = wfx;
hres = IAudioClient_Initialize(device->client,
AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST |
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buflen, 0, device->pwfx, NULL);
if(FAILED(hres)){
IAudioClient_Release(device->client);
device->client = NULL;
WARN("Initialize failed: %08x\n", hres);
return hres;
}
IAudioClient_SetEventHandle(device->client, device->sleepev);
if (device->state == STATE_PLAYING)
device->state = STATE_STARTING;
else if (device->state == STATE_STOPPING)
device->state = STATE_STOPPED;
hres = IAudioClient_GetService(device->client, &IID_IAudioRenderClient,
(void**)&device->render);
if(FAILED(hres)){
IAudioClient_Release(device->client);
device->client = NULL;
WARN("GetService failed: %08x\n", hres);
return hres;
}
hres = IAudioClient_GetService(device->client, &IID_IAudioClock,
(void**)&device->clock);
if(FAILED(hres)){
IAudioClient_Release(device->client);
IAudioRenderClient_Release(device->render);
device->client = NULL;
device->render = NULL;
WARN("GetService failed: %08x\n", hres);
return hres;
}
hres = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume,
(void**)&device->volume);
if(FAILED(hres)){
IAudioClient_Release(device->client);
IAudioRenderClient_Release(device->render);
IAudioClock_Release(device->clock);
device->client = NULL;
device->render = NULL;
device->clock = NULL;
WARN("GetService failed: %08x\n", hres);
return hres;
}
/* Now kick off the timer so the event fires periodically */
hres = IAudioClient_Start(device->client);
if (FAILED(hres))
WARN("starting failed with %08x\n", hres);
hres = IAudioClient_GetStreamLatency(device->client, &period);
if (FAILED(hres)) {
WARN("GetStreamLatency failed with %08x\n", hres);
period = 100000;
}
period_ms = (period + 9999) / 10000;
hres = IAudioClient_GetBufferSize(device->client, &frames);
if (FAILED(hres)) {
WARN("GetBufferSize failed with %08x\n", hres);
frames = (UINT64)device->pwfx->nSamplesPerSec * buflen / 10000000;
}
device->fraglen = MulDiv(device->pwfx->nSamplesPerSec, period, 10000000) * device->pwfx->nBlockAlign;
device->aclen = frames * device->pwfx->nBlockAlign;
TRACE("period %u ms fraglen %u buflen %u\n", period_ms, device->fraglen, device->aclen);
if (period_ms < 3)
device->sleeptime = 5;
else
device->sleeptime = period_ms * 5 / 2;
return S_OK;
}
HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
{
IDirectSoundBufferImpl** dsb = device->buffers;
LPBYTE newbuf;
int i;
TRACE("(%p)\n", device);
/* 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)
device->buflen = ds_hel_buflen;
device->buflen -= device->buflen % device->pwfx->nBlockAlign;
if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
/* reallocate emulated primary buffer */
if (device->buffer)
newbuf = HeapReAlloc(GetProcessHeap(),0,device->buffer, device->buflen);
else
newbuf = HeapAlloc(GetProcessHeap(),0, device->buflen);
if (!newbuf) {
ERR("failed to allocate primary buffer\n");
return DSERR_OUTOFMEMORY;
/* but the old buffer might still exist and must be re-prepared */
}
device->writelead = (device->pwfx->nSamplesPerSec / 100) * device->pwfx->nBlockAlign;
device->buffer = newbuf;
device->writelead = (wfx->nSamplesPerSec / 100) * wfx->nBlockAlign;
TRACE("buflen: %u, fraglen: %u\n", device->buflen, device->fraglen);
if(device->pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
(device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
IsEqualGUID(&((WAVEFORMATEXTENSIBLE*)device->pwfx)->SubFormat,
&KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
device->normfunction = NULL;
if (!mixfloat)
device->normfunction = normfunctions[wfx->wBitsPerSample/8 - 1];
else
device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];
device->normfunction = NULL;
FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
device->playpos = 0;
for (i = 0; i < device->nrofbuffers; i++) {
@ -379,35 +297,105 @@ HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
return DS_OK;
}
static void DSOUND_PrimaryClose(DirectSoundDevice *device)
HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
{
HRESULT hr;
HRESULT hres;
REFERENCE_TIME period;
UINT32 frames;
DWORD period_ms;
IAudioClient *client = NULL;
IAudioRenderClient *render = NULL;
IAudioStreamVolume *volume = NULL;
DWORD fraglen;
WAVEFORMATEX *wfx = NULL;
DWORD oldspeakerconfig = device->speaker_config;
TRACE("(%p)\n", device);
TRACE("(%p, %d)\n", device, forcewave);
if(device->client){
hr = IAudioClient_Stop(device->client);
if(FAILED(hr))
WARN("Stop failed: %08x\n", hr);
hres = IMMDevice_Activate(device->mmdevice, &IID_IAudioClient,
CLSCTX_INPROC_SERVER, NULL, (void **)&client);
if(FAILED(hres)){
WARN("Activate failed: %08x\n", hres);
return hres;
}
}
HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device)
{
HRESULT err = DS_OK;
TRACE("(%p)\n", device);
hres = DSOUND_WaveFormat(device, client, forcewave, &wfx);
if (FAILED(hres)) {
IAudioClient_Release(client);
return hres;
}
device->buflen = ds_hel_buflen;
err = DSOUND_PrimaryOpen(device);
hres = IAudioClient_Initialize(client,
AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST |
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 800000, 0, wfx, NULL);
if(FAILED(hres)){
IAudioClient_Release(client);
ERR("Initialize failed: %08x\n", hres);
return hres;
}
if (err != DS_OK) {
WARN("DSOUND_PrimaryOpen failed\n");
return err;
}
IAudioClient_SetEventHandle(client, device->sleepev);
device->state = STATE_STOPPED;
return DS_OK;
hres = IAudioClient_GetService(client, &IID_IAudioRenderClient, (void**)&render);
if(FAILED(hres))
goto err_service;
hres = IAudioClient_GetService(client, &IID_IAudioStreamVolume, (void**)&volume);
if(FAILED(hres))
goto err_service;
/* Now kick off the timer so the event fires periodically */
hres = IAudioClient_Start(client);
if (FAILED(hres)) {
WARN("Start failed with %08x\n", hres);
goto err;
}
hres = IAudioClient_GetStreamLatency(client, &period);
if (FAILED(hres)) {
WARN("GetStreamLatency failed with %08x\n", hres);
goto err;
}
hres = IAudioClient_GetBufferSize(client, &frames);
if (FAILED(hres)) {
WARN("GetBufferSize failed with %08x\n", hres);
goto err;
}
period_ms = (period + 9999) / 10000;
fraglen = MulDiv(wfx->nSamplesPerSec, period, 10000000) * wfx->nBlockAlign;
TRACE("period %u ms fraglen %u buflen %u\n", period_ms, fraglen, frames * wfx->nBlockAlign);
hres = DSOUND_PrimaryOpen(device, wfx, frames, forcewave);
if(FAILED(hres))
goto err;
DSOUND_ReleaseDevice(device);
device->client = client;
device->render = render;
device->volume = volume;
device->fraglen = fraglen;
device->aclen = frames * wfx->nBlockAlign;
if (period_ms < 3)
device->sleeptime = 5;
else
device->sleeptime = period_ms * 5 / 2;
return S_OK;
err_service:
ERR("GetService failed: %08x\n", hres);
err:
device->speaker_config = oldspeakerconfig;
DSOUND_ParseSpeakerConfig(device);
if (volume)
IAudioStreamVolume_Release(volume);
if (render)
IAudioRenderClient_Release(render);
if (client)
IAudioClient_Release(client);
HeapFree(GetProcessHeap(), 0, wfx);
return hres;
}
HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
@ -417,8 +405,6 @@ HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
/* **** */
EnterCriticalSection(&(device->mixlock));
DSOUND_PrimaryClose(device);
if(device->primary && (device->primary->ref || device->primary->numIfaces))
WARN("Destroying primary buffer while references held (%u %u)\n", device->primary->ref, device->primary->numIfaces);
@ -470,10 +456,14 @@ WAVEFORMATEX *DSOUND_CopyFormat(const WAVEFORMATEX *wfex)
WAVEFORMATEX *pwfx;
if(wfex->wFormatTag == WAVE_FORMAT_PCM){
pwfx = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEX));
if (!pwfx)
return NULL;
CopyMemory(pwfx, wfex, sizeof(PCMWAVEFORMAT));
pwfx->cbSize = 0;
}else{
pwfx = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEX) + wfex->cbSize);
if (!pwfx)
return NULL;
CopyMemory(pwfx, wfex, sizeof(WAVEFORMATEX) + wfex->cbSize);
}
@ -490,7 +480,6 @@ HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX passe
HRESULT err = S_OK;
WAVEFORMATEX *old_fmt;
WAVEFORMATEXTENSIBLE *fmtex, *passed_fmtex = (WAVEFORMATEXTENSIBLE*)passed_fmt;
BOOL forced = (device->priolevel == DSSCL_WRITEPRIMARY);
TRACE("(%p,%p)\n", device, passed_fmt);
@ -540,28 +529,20 @@ HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX passe
fmtex->Samples.wValidBitsPerSample = fmtex->Format.wBitsPerSample;
}
DSOUND_PrimaryClose(device);
err = DSOUND_ReopenDevice(device, forced);
err = DSOUND_ReopenDevice(device, TRUE);
if (FAILED(err)) {
ERR("No formats could be opened\n");
goto done;
}
err = DSOUND_PrimaryOpen(device);
if (err != DS_OK) {
ERR("DSOUND_PrimaryOpen failed\n");
goto done;
}
done:
if (err != DS_OK)
HeapFree(GetProcessHeap(), 0, device->primary_pwfx);
device->primary_pwfx = old_fmt;
else
} else
HeapFree(GetProcessHeap(), 0, old_fmt);
} else {
HeapFree(GetProcessHeap(), 0, device->primary_pwfx);
device->primary_pwfx = DSOUND_CopyFormat(passed_fmt);
WAVEFORMATEX *wfx = DSOUND_CopyFormat(passed_fmt);
if (wfx) {
HeapFree(GetProcessHeap(), 0, device->primary_pwfx);
device->primary_pwfx = wfx;
} else
err = DSERR_OUTOFMEMORY;
}
out: