dsound: Use AudioClient buffer statistics.

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-12 08:43:23 -05:00 committed by Alexandre Julliard
parent 77184106c8
commit 569ed159b0
5 changed files with 84 additions and 234 deletions

View File

@ -158,7 +158,6 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice)
device->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
device->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
device->prebuf = ds_snd_queue_max;
device->guid = GUID_NULL;
/* Set default wave format (may need it for waveOutOpen) */

View File

@ -93,7 +93,6 @@ WCHAR wine_vxd_drv[] = { 'w','i','n','e','m','m','.','v','x','d', 0 };
/* All default settings, you most likely don't want to touch these, see wiki on UsefulRegistryKeys */
int ds_hel_buflen = 32768 * 2;
int ds_snd_queue_max = 10;
static HINSTANCE instance;
/*
@ -146,15 +145,10 @@ void setup_dsound_options(void)
if (!get_config_key( hkey, appkey, "HelBuflen", buffer, MAX_PATH ))
ds_hel_buflen = atoi(buffer);
if (!get_config_key( hkey, appkey, "SndQueueMax", buffer, MAX_PATH ))
ds_snd_queue_max = atoi(buffer);
if (appkey) RegCloseKey( appkey );
if (hkey) RegCloseKey( hkey );
TRACE("ds_hel_buflen = %d\n", ds_hel_buflen);
TRACE("ds_snd_queue_max = %d\n", ds_snd_queue_max);
}
static const char * get_device_id(LPCGUID pGuid)

View File

@ -35,7 +35,6 @@
#define DS_MAX_CHANNELS 6
extern int ds_hel_buflen DECLSPEC_HIDDEN;
extern int ds_snd_queue_max DECLSPEC_HIDDEN;
/*****************************************************************************
* Predeclare the interface implementation structures
@ -76,10 +75,9 @@ struct DirectSoundDevice
DSCAPS drvcaps;
DWORD priolevel, sleeptime;
PWAVEFORMATEX pwfx, primary_pwfx;
UINT playing_offs_bytes, in_mmdev_bytes, prebuf;
DWORD fraglen;
UINT playing_offs_bytes, in_mmdev_bytes;
LPBYTE buffer;
DWORD writelead, buflen, state, playpos, mixpos;
DWORD writelead, buflen, aclen, fraglen, state, playpos, pad;
int nrofbuffers;
IDirectSoundBufferImpl** buffers;
RTL_RWLOCK buffer_list_lock;
@ -211,7 +209,6 @@ 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;
HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos) 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;

View File

@ -613,86 +613,27 @@ static void DSOUND_MixToPrimary(const DirectSoundDevice *device, DWORD writepos,
* Returns: None
*/
static void DSOUND_WaveQueue(DirectSoundDevice *device, BOOL force)
static void DSOUND_WaveQueue(DirectSoundDevice *device, LPBYTE pos, DWORD bytes)
{
DWORD prebuf_frames, prebuf_bytes, read_offs_bytes;
BYTE *buffer;
HRESULT hr;
TRACE("(%p)\n", device);
read_offs_bytes = (device->playing_offs_bytes + device->in_mmdev_bytes) % device->buflen;
TRACE("read_offs_bytes = %u, playing_offs_bytes = %u, in_mmdev_bytes: %u, prebuf = %u\n",
read_offs_bytes, device->playing_offs_bytes, device->in_mmdev_bytes, device->prebuf);
if (!force)
{
if(device->mixpos < device->playing_offs_bytes)
prebuf_bytes = device->mixpos + device->buflen - device->playing_offs_bytes;
else
prebuf_bytes = device->mixpos - device->playing_offs_bytes;
}
else
/* buffer the maximum amount of frags */
prebuf_bytes = device->prebuf * device->fraglen;
/* limit to the queue we have left */
if(device->in_mmdev_bytes + prebuf_bytes > device->prebuf * device->fraglen)
prebuf_bytes = device->prebuf * device->fraglen - device->in_mmdev_bytes;
TRACE("prebuf_bytes = %u\n", prebuf_bytes);
if(!prebuf_bytes)
return;
if(prebuf_bytes + read_offs_bytes > device->buflen){
DWORD chunk_bytes = device->buflen - read_offs_bytes;
prebuf_frames = chunk_bytes / device->pwfx->nBlockAlign;
prebuf_bytes -= chunk_bytes;
}else{
prebuf_frames = prebuf_bytes / device->pwfx->nBlockAlign;
prebuf_bytes = 0;
}
hr = IAudioRenderClient_GetBuffer(device->render, prebuf_frames, &buffer);
hr = IAudioRenderClient_GetBuffer(device->render, bytes / device->pwfx->nBlockAlign, &buffer);
if(FAILED(hr)){
WARN("GetBuffer failed: %08x\n", hr);
return;
goto done;
}
memcpy(buffer, device->buffer + read_offs_bytes,
prebuf_frames * device->pwfx->nBlockAlign);
memcpy(buffer, pos, bytes);
hr = IAudioRenderClient_ReleaseBuffer(device->render, prebuf_frames, 0);
if(FAILED(hr)){
hr = IAudioRenderClient_ReleaseBuffer(device->render, bytes / device->pwfx->nBlockAlign, 0);
if(FAILED(hr))
WARN("ReleaseBuffer failed: %08x\n", hr);
return;
}
device->in_mmdev_bytes += prebuf_frames * device->pwfx->nBlockAlign;
/* check if anything wrapped */
if(prebuf_bytes > 0){
prebuf_frames = prebuf_bytes / device->pwfx->nBlockAlign;
hr = IAudioRenderClient_GetBuffer(device->render, prebuf_frames, &buffer);
if(FAILED(hr)){
WARN("GetBuffer failed: %08x\n", hr);
return;
}
memcpy(buffer, device->buffer, prebuf_frames * device->pwfx->nBlockAlign);
hr = IAudioRenderClient_ReleaseBuffer(device->render, prebuf_frames, 0);
if(FAILED(hr)){
WARN("ReleaseBuffer failed: %08x\n", hr);
return;
}
device->in_mmdev_bytes += prebuf_frames * device->pwfx->nBlockAlign;
}
TRACE("in_mmdev_bytes now = %i\n", device->in_mmdev_bytes);
done:
device->pad += bytes;
}
/**
@ -710,7 +651,8 @@ static void DSOUND_WaveQueue(DirectSoundDevice *device, BOOL force)
*/
static void DSOUND_PerformMix(DirectSoundDevice *device)
{
UINT32 pad, to_mix_frags, to_mix_bytes;
UINT32 pad, maxq, writepos;
DWORD block;
HRESULT hr;
TRACE("(%p)\n", device);
@ -724,147 +666,81 @@ static void DSOUND_PerformMix(DirectSoundDevice *device)
LeaveCriticalSection(&device->mixlock);
return;
}
block = device->pwfx->nBlockAlign;
pad *= block;
device->playpos += device->pad - pad;
device->playpos %= device->buflen;
device->pad = pad;
to_mix_frags = device->prebuf - (pad * device->pwfx->nBlockAlign + device->fraglen - 1) / device->fraglen;
to_mix_bytes = to_mix_frags * device->fraglen;
if(device->in_mmdev_bytes > 0){
DWORD delta_bytes = min(to_mix_bytes, device->in_mmdev_bytes);
device->in_mmdev_bytes -= delta_bytes;
device->playing_offs_bytes += delta_bytes;
device->playing_offs_bytes %= device->buflen;
maxq = device->aclen - pad;
if(!maxq){
/* nothing to do! */
LeaveCriticalSection(&device->mixlock);
return;
}
if (maxq > device->fraglen * 3)
maxq = device->fraglen * 3;
writepos = (device->playpos + pad) % device->buflen;
if (device->priolevel != DSSCL_WRITEPRIMARY) {
BOOL recover = FALSE, all_stopped = FALSE;
DWORD playpos, writepos, writelead, maxq, prebuff_max, prebuff_left, size1, size2;
LPVOID buf1, buf2;
BOOL all_stopped = FALSE;
int nfiller;
DWORD bpp = device->pwfx->wBitsPerSample>>3;
/* the sound of silence */
nfiller = device->pwfx->wBitsPerSample == 8 ? 128 : 0;
/* get the position in the primary buffer */
if (DSOUND_PrimaryGetPosition(device, &playpos, &writepos) != 0){
LeaveCriticalSection(&(device->mixlock));
return;
}
TRACE("primary playpos=%d, writepos=%d, clrpos=%d, mixpos=%d, buflen=%d\n",
playpos,writepos,device->playpos,device->mixpos,device->buflen);
assert(device->playpos < device->buflen);
/* calc maximum prebuff */
prebuff_max = (device->prebuf * device->fraglen);
/* check how close we are to an underrun. It occurs when the writepos overtakes the mixpos */
prebuff_left = DSOUND_BufPtrDiff(device->buflen, device->mixpos, playpos);
writelead = DSOUND_BufPtrDiff(device->buflen, writepos, playpos);
/* check for underrun. underrun occurs when the write position passes the mix position
* also wipe out just-played sound data */
if((prebuff_left > prebuff_max) || (device->state == STATE_STOPPED) || (device->state == STATE_STARTING)){
if (device->state == STATE_STOPPING || device->state == STATE_PLAYING)
WARN("Probable buffer underrun\n");
else TRACE("Buffer starting or buffer underrun\n");
/* recover mixing for all buffers */
recover = TRUE;
/* reset mix position to write position */
device->mixpos = writepos;
ZeroMemory(device->buffer, device->buflen);
} else if (playpos < device->playpos) {
buf1 = device->buffer + device->playpos;
buf2 = device->buffer;
size1 = device->buflen - device->playpos;
size2 = playpos;
FillMemory(buf1, size1, nfiller);
if (playpos && (!buf2 || !size2))
FIXME("%d: (%d, %d)=>(%d, %d) There should be an additional buffer here!!\n", __LINE__, device->playpos, device->mixpos, playpos, writepos);
FillMemory(buf2, size2, nfiller);
} else {
buf1 = device->buffer + device->playpos;
buf2 = NULL;
size1 = playpos - device->playpos;
size2 = 0;
FillMemory(buf1, size1, nfiller);
if (!pad)
WARN("Probable buffer underrun\n");
else if (device->state == STATE_STOPPED ||
device->state == STATE_STARTING) {
TRACE("Buffer restarting\n");
}
device->playpos = playpos;
/* find the maximum we can prebuffer from current write position */
maxq = (writelead < prebuff_max) ? (prebuff_max - writelead) : 0;
TRACE("prebuff_left = %d, prebuff_max = %dx%d=%d, writelead=%d\n",
prebuff_left, device->prebuf, device->fraglen, prebuff_max, writelead);
ZeroMemory(device->mix_buffer, device->mix_buffer_len);
memset(device->mix_buffer, nfiller, maxq);
/* do the mixing */
DSOUND_MixToPrimary(device, writepos, maxq, recover, &all_stopped);
DSOUND_MixToPrimary(device, writepos, maxq, TRUE, &all_stopped);
if (maxq + writepos > device->buflen)
{
if (maxq + writepos > device->buflen) {
DWORD todo = device->buflen - writepos;
DWORD offs_float = (todo / device->pwfx->nBlockAlign) * device->pwfx->nChannels;
device->normfunction(device->mix_buffer, device->buffer + writepos, todo);
device->normfunction(device->mix_buffer + offs_float, device->buffer, maxq - todo);
}
else
DSOUND_WaveQueue(device, device->buffer + writepos, todo);
device->normfunction(device->mix_buffer + todo / bpp, device->buffer, (maxq - todo));
DSOUND_WaveQueue(device, device->buffer, maxq - todo);
} else {
device->normfunction(device->mix_buffer, device->buffer + writepos, maxq);
/* update the mix position, taking wrap-around into account */
device->mixpos = writepos + maxq;
device->mixpos %= device->buflen;
/* update prebuff left */
prebuff_left = DSOUND_BufPtrDiff(device->buflen, device->mixpos, playpos);
/* check if have a whole fragment */
if (prebuff_left >= device->fraglen){
/* update the wave queue */
DSOUND_WaveQueue(device, FALSE);
/* buffers are full. start playing if applicable */
if(device->state == STATE_STARTING){
TRACE("started primary buffer\n");
if(DSOUND_PrimaryPlay(device) != DS_OK){
WARN("DSOUND_PrimaryPlay failed\n");
}
else{
/* we are playing now */
device->state = STATE_PLAYING;
}
}
/* buffers are full. start stopping if applicable */
if(device->state == STATE_STOPPED){
TRACE("restarting primary buffer\n");
if(DSOUND_PrimaryPlay(device) != DS_OK){
WARN("DSOUND_PrimaryPlay failed\n");
}
else{
/* start stopping again. as soon as there is no more data, it will stop */
device->state = STATE_STOPPING;
}
}
DSOUND_WaveQueue(device, device->buffer + writepos, maxq);
}
/* if device was stopping, its for sure stopped when all buffers have stopped */
else if (all_stopped && (device->state == STATE_STOPPING)) {
TRACE("All buffers have stopped. Stopping primary buffer\n");
if (maxq) {
if (device->state == STATE_STARTING ||
device->state == STATE_STOPPED) {
if(DSOUND_PrimaryPlay(device) != DS_OK)
WARN("DSOUND_PrimaryPlay failed\n");
else if (device->state == STATE_STARTING)
device->state = STATE_PLAYING;
else
device->state = STATE_STOPPING;
}
} else if (!pad && !maxq && (all_stopped == TRUE) &&
(device->state == STATE_STOPPING)) {
device->state = STATE_STOPPED;
/* stop the primary buffer now */
DSOUND_PrimaryStop(device);
}
} else if (device->state != STATE_STOPPED) {
DSOUND_WaveQueue(device, TRUE);
if (maxq > device->buflen)
maxq = device->buflen;
if (writepos + maxq > device->buflen) {
DSOUND_WaveQueue(device, device->buffer + writepos, device->buflen - writepos);
DSOUND_WaveQueue(device, device->buffer, writepos + maxq - device->buflen);
} else
DSOUND_WaveQueue(device, device->buffer + writepos, maxq);
/* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
if (device->state == STATE_STARTING) {

View File

@ -40,24 +40,6 @@
WINE_DEFAULT_DEBUG_CHANNEL(dsound);
static DWORD DSOUND_fraglen(DirectSoundDevice *device)
{
REFERENCE_TIME period;
HRESULT hr;
DWORD ret;
hr = IAudioClient_GetDevicePeriod(device->client, &period, NULL);
if(FAILED(hr)){
/* just guess at 10ms */
WARN("GetDevicePeriod failed: %08x\n", hr);
ret = MulDiv(device->pwfx->nBlockAlign, device->pwfx->nSamplesPerSec, 100);
}else
ret = MulDiv(device->pwfx->nSamplesPerSec * device->pwfx->nBlockAlign, period, 10000000);
ret -= ret % device->pwfx->nBlockAlign;
return ret;
}
static DWORD speaker_config_to_channel_mask(DWORD speaker_config)
{
switch (DSSPEAKER_CONFIG(speaker_config)) {
@ -217,11 +199,10 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client
HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
{
UINT prebuf_frames;
REFERENCE_TIME prebuf_rt;
WAVEFORMATEX *wfx = NULL;
HRESULT hres;
REFERENCE_TIME period;
REFERENCE_TIME period, buflen = 800000;
UINT32 frames;
DWORD period_ms;
TRACE("(%p, %d)\n", device, forcewave);
@ -243,6 +224,12 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
device->volume = NULL;
}
if (device->pad) {
device->playpos += device->pad;
device->playpos %= device->buflen;
device->pad = 0;
}
hres = IMMDevice_Activate(device->mmdevice, &IID_IAudioClient,
CLSCTX_INPROC_SERVER, NULL, (void **)&device->client);
if(FAILED(hres)) {
@ -263,12 +250,9 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
HeapFree(GetProcessHeap(), 0, device->pwfx);
device->pwfx = wfx;
prebuf_frames = device->prebuf * DSOUND_fraglen(device) / device->pwfx->nBlockAlign;
prebuf_rt = (10000000 * (UINT64)prebuf_frames) / device->pwfx->nSamplesPerSec;
hres = IAudioClient_Initialize(device->client,
AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST |
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, prebuf_rt, 0, device->pwfx, NULL);
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buflen, 0, device->pwfx, NULL);
if(FAILED(hres)){
IAudioClient_Release(device->client);
device->client = NULL;
@ -318,10 +302,19 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
hres = IAudioClient_GetStreamLatency(device->client, &period);
if (FAILED(hres)) {
WARN("GetStreamLatency failed with %08x\n", hres);
period_ms = 10;
} else
period_ms = (period + 9999) / 10000;
TRACE("period %u ms fraglen %u prebuf %u\n", period_ms, device->fraglen, device->prebuf);
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;
@ -339,17 +332,11 @@ HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
TRACE("(%p)\n", device);
device->fraglen = DSOUND_fraglen(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;
while(device->buflen < device->fraglen * device->prebuf){
device->buflen += ds_hel_buflen;
device->buflen -= device->buflen % device->pwfx->nBlockAlign;
}
HeapFree(GetProcessHeap(), 0, device->mix_buffer);
device->mix_buffer_len = (device->buflen / (device->pwfx->wBitsPerSample / 8)) * sizeof(float);
@ -419,9 +406,6 @@ static void DSOUND_PrimaryClose(DirectSoundDevice *device)
if(FAILED(hr))
WARN("Stop failed: %08x\n", hr);
}
/* clear the queue */
device->in_mmdev_bytes = 0;
}
HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device)