From 62017964bead565a1da427faaf083e5103c27440 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=B6rg=20H=C3=B6hle?= Date: Fri, 9 Dec 2011 17:12:54 +0100 Subject: [PATCH] winealsa: Implement IAudioClock::GetPosition() using snd_pcm_delay. --- dlls/winealsa.drv/mmdevdrv.c | 50 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c index bfff1595e55..359997e67f0 100644 --- a/dlls/winealsa.drv/mmdevdrv.c +++ b/dlls/winealsa.drv/mmdevdrv.c @@ -109,7 +109,7 @@ struct ACImpl { BOOL initted, started; REFERENCE_TIME mmdev_period_rt; - UINT64 written_frames; + UINT64 written_frames, last_pos_frames; UINT32 bufsize_frames, held_frames, tmp_buffer_frames; UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */ @@ -1790,6 +1790,7 @@ static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface) if(snd_pcm_prepare(This->pcm_handle) < 0) WARN("snd_pcm_prepare failed\n"); + This->last_pos_frames = 0; This->held_frames = 0; This->written_frames = 0; This->lcl_offs_frames = 0; @@ -2291,8 +2292,12 @@ static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos, UINT64 *qpctime) { ACImpl *This = impl_from_IAudioClock(iface); - UINT32 pad; - HRESULT hr; + UINT64 written_frames, position; + UINT32 held_frames; + int err; + snd_pcm_state_t alsa_state; + snd_pcm_uframes_t avail_frames; + snd_pcm_sframes_t delay_frames; TRACE("(%p)->(%p, %p)\n", This, pos, qpctime); @@ -2301,19 +2306,42 @@ static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos, EnterCriticalSection(&This->lock); - hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad); - if(FAILED(hr)){ - LeaveCriticalSection(&This->lock); - return hr; + /* call required to get accurate snd_pcm_state() */ + avail_frames = snd_pcm_avail_update(This->pcm_handle); + alsa_state = snd_pcm_state(This->pcm_handle); + written_frames = This->written_frames; + held_frames = This->held_frames; + + err = snd_pcm_delay(This->pcm_handle, &delay_frames); + if(err < 0){ + /* old Pulse, shortly after start */ + WARN("snd_pcm_delay failed in state %u: %d (%s)\n", alsa_state, err, snd_strerror(err)); } - if(This->dataflow == eRender) - *pos = This->written_frames - pad; - else if(This->dataflow == eCapture) - *pos = This->written_frames + pad; + if(This->dataflow == eRender){ + position = written_frames - held_frames; /* maximum */ + if(!This->started || alsa_state > SND_PCM_STATE_RUNNING) + ; /* mmdevapi stopped or ALSA underrun: pretend everything was played */ + else if(err<0 || delay_frames > position - This->last_pos_frames) + /* Pulse bug: past underrun, despite recovery, avail_frames & delay + * may be larger than alsa_bufsize_frames, as if cumulating frames. */ + /* Pulse bug: EIO(-5) shortly after starting: nothing played */ + position = This->last_pos_frames; + else if(delay_frames > 0) + position -= delay_frames; + }else + position = written_frames + held_frames; + + /* ensure monotic growth */ + This->last_pos_frames = position; LeaveCriticalSection(&This->lock); + TRACE("frames written: %u, held: %u, avail: %ld, delay: %ld state %d, pos: %u\n", + (UINT32)(written_frames%1000000000), held_frames, + avail_frames, delay_frames, alsa_state, (UINT32)(position%1000000000)); + *pos = position; + if(qpctime){ LARGE_INTEGER stamp, freq; QueryPerformanceCounter(&stamp); -- 2.11.4.GIT