Signed-off-by: Huw Davies huw@codeweavers.com --- dlls/winealsa.drv/alsa.c | 240 +++++++++++++++++++++++++++++++++++ dlls/winealsa.drv/mmdevdrv.c | 229 +++------------------------------ dlls/winealsa.drv/unixlib.h | 9 +- 3 files changed, 268 insertions(+), 210 deletions(-)
diff --git a/dlls/winealsa.drv/alsa.c b/dlls/winealsa.drv/alsa.c index 73971c0f8bb..d455a3787e8 100644 --- a/dlls/winealsa.drv/alsa.c +++ b/dlls/winealsa.drv/alsa.c @@ -960,6 +960,12 @@ static NTSTATUS release_stream(void *args) struct alsa_stream *stream = params->stream; SIZE_T size;
+ if(params->timer_thread){ + stream->please_quit = TRUE; + NtWaitForSingleObject(params->timer_thread, FALSE, NULL); + NtClose(params->timer_thread); + } + snd_pcm_drop(stream->pcm_handle); snd_pcm_close(stream->pcm_handle); if(stream->local_buffer){ @@ -1215,6 +1221,239 @@ static NTSTATUS write_best_effort(void *args) return STATUS_SUCCESS; }
+static snd_pcm_sframes_t alsa_write_buffer_wrap(struct alsa_stream *stream, BYTE *buf, + snd_pcm_uframes_t buflen, snd_pcm_uframes_t offs, + snd_pcm_uframes_t to_write) +{ + snd_pcm_sframes_t ret = 0; + + while(to_write){ + snd_pcm_uframes_t chunk; + snd_pcm_sframes_t tmp; + + if(offs + to_write > buflen) + chunk = buflen - offs; + else + chunk = to_write; + + tmp = alsa_write_best_effort(stream, buf + offs * stream->fmt->nBlockAlign, chunk); + if(tmp < 0) + return ret; + if(!tmp) + break; + + ret += tmp; + to_write -= tmp; + offs += tmp; + offs %= buflen; + } + + return ret; +} + +static UINT buf_ptr_diff(UINT left, UINT right, UINT bufsize) +{ + if(left <= right) + return right - left; + return bufsize - (left - right); +} + +static UINT data_not_in_alsa(struct alsa_stream *stream) +{ + UINT32 diff; + + diff = buf_ptr_diff(stream->lcl_offs_frames, stream->wri_offs_frames, stream->bufsize_frames); + if(diff) + return diff; + + return stream->held_frames - stream->data_in_alsa_frames; +} + +/* Here's the buffer setup: + * + * vvvvvvvv sent to HW already + * vvvvvvvv in ALSA buffer but rewindable + * [dddddddddddddddd] ALSA buffer + * [dddddddddddddddd--------] mmdevapi buffer + * ^^^^^^^^ data_in_alsa_frames + * ^^^^^^^^^^^^^^^^ held_frames + * ^ lcl_offs_frames + * ^ wri_offs_frames + * + * GetCurrentPadding is held_frames + * + * During period callback, we decrement held_frames, fill ALSA buffer, and move + * lcl_offs forward + * + * During Stop, we rewind the ALSA buffer + */ +static void alsa_write_data(struct alsa_stream *stream) +{ + snd_pcm_sframes_t written; + snd_pcm_uframes_t avail, max_copy_frames, data_frames_played; + int err; + + /* this call seems to be required to get an accurate snd_pcm_state() */ + avail = snd_pcm_avail_update(stream->pcm_handle); + + if(snd_pcm_state(stream->pcm_handle) == SND_PCM_STATE_XRUN){ + TRACE("XRun state, recovering\n"); + + avail = stream->alsa_bufsize_frames; + + if((err = snd_pcm_recover(stream->pcm_handle, -EPIPE, 1)) < 0) + WARN("snd_pcm_recover failed: %d (%s)\n", err, snd_strerror(err)); + + if((err = snd_pcm_reset(stream->pcm_handle)) < 0) + WARN("snd_pcm_reset failed: %d (%s)\n", err, snd_strerror(err)); + + if((err = snd_pcm_prepare(stream->pcm_handle)) < 0) + WARN("snd_pcm_prepare failed: %d (%s)\n", err, snd_strerror(err)); + } + + TRACE("avail: %ld\n", avail); + + /* Add a lead-in when starting with too few frames to ensure + * continuous rendering. Additional benefit: Force ALSA to start. */ + if(stream->data_in_alsa_frames == 0 && stream->held_frames < stream->alsa_period_frames) + { + alsa_write_best_effort(stream, stream->silence_buf, + stream->alsa_period_frames - stream->held_frames); + stream->vol_adjusted_frames = 0; + } + + if(stream->started) + max_copy_frames = data_not_in_alsa(stream); + else + max_copy_frames = 0; + + data_frames_played = min(stream->data_in_alsa_frames, avail); + stream->data_in_alsa_frames -= data_frames_played; + + if(stream->held_frames > data_frames_played){ + if(stream->started) + stream->held_frames -= data_frames_played; + }else + stream->held_frames = 0; + + while(avail && max_copy_frames){ + snd_pcm_uframes_t to_write; + + to_write = min(avail, max_copy_frames); + + written = alsa_write_buffer_wrap(stream, stream->local_buffer, + stream->bufsize_frames, stream->lcl_offs_frames, to_write); + if(written <= 0) + break; + + avail -= written; + stream->lcl_offs_frames += written; + stream->lcl_offs_frames %= stream->bufsize_frames; + stream->data_in_alsa_frames += written; + max_copy_frames -= written; + } + + if(stream->event) + NtSetEvent(stream->event, NULL); +} + +static void alsa_read_data(struct alsa_stream *stream) +{ + snd_pcm_sframes_t nread; + UINT32 pos = stream->wri_offs_frames, limit = stream->held_frames; + unsigned int i; + + if(!stream->started) + goto exit; + + /* FIXME: Detect overrun and signal DATA_DISCONTINUITY + * How to count overrun frames and report them as position increase? */ + limit = stream->bufsize_frames - max(limit, pos); + + nread = snd_pcm_readi(stream->pcm_handle, + stream->local_buffer + pos * stream->fmt->nBlockAlign, limit); + TRACE("read %ld from %u limit %u\n", nread, pos, limit); + if(nread < 0){ + int ret; + + if(nread == -EAGAIN) /* no data yet */ + return; + + WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread)); + + ret = snd_pcm_recover(stream->pcm_handle, nread, 0); + if(ret < 0){ + WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret)); + return; + } + + nread = snd_pcm_readi(stream->pcm_handle, + stream->local_buffer + pos * stream->fmt->nBlockAlign, limit); + if(nread < 0){ + WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread)); + return; + } + } + + for(i = 0; i < stream->fmt->nChannels; i++) + if(stream->vols[i] != 0.0f) + break; + if(i == stream->fmt->nChannels){ /* mute */ + int err; + if((err = snd_pcm_format_set_silence(stream->alsa_format, + stream->local_buffer + pos * stream->fmt->nBlockAlign, + nread)) < 0) + WARN("Setting buffer to silence failed: %d (%s)\n", err, + snd_strerror(err)); + } + + stream->wri_offs_frames += nread; + stream->wri_offs_frames %= stream->bufsize_frames; + stream->held_frames += nread; + +exit: + if(stream->event) + NtSetEvent(stream->event, NULL); +} + +static NTSTATUS timer_loop(void *args) +{ + struct timer_loop_params *params = args; + struct alsa_stream *stream = params->stream; + LARGE_INTEGER delay, next; + int adjust; + + alsa_lock(stream); + + delay.QuadPart = -stream->mmdev_period_rt; + NtQueryPerformanceCounter(&stream->last_period_time, NULL); + next.QuadPart = stream->last_period_time.QuadPart + stream->mmdev_period_rt; + + while(!stream->please_quit){ + if(stream->flow == eRender) + alsa_write_data(stream); + else if(stream->flow == eCapture) + alsa_read_data(stream); + alsa_unlock(stream); + + NtDelayExecution(FALSE, &delay); + + alsa_lock(stream); + NtQueryPerformanceCounter(&stream->last_period_time, NULL); + adjust = next.QuadPart - stream->last_period_time.QuadPart; + if(adjust > stream->mmdev_period_rt / 2) + adjust = stream->mmdev_period_rt / 2; + else if(adjust < -stream->mmdev_period_rt / 2) + adjust = -stream->mmdev_period_rt / 2; + delay.QuadPart = -(stream->mmdev_period_rt + adjust); + next.QuadPart += stream->mmdev_period_rt; + } + + alsa_unlock(stream); + + return STATUS_SUCCESS; +} + static NTSTATUS is_format_supported(void *args) { struct is_format_supported_params *params = args; @@ -1522,6 +1761,7 @@ unixlib_entry_t __wine_unix_call_funcs[] = get_endpoint_ids, create_stream, release_stream, + timer_loop, is_format_supported, get_mix_format, get_buffer_size, diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c index c06b08709f2..2748cc552a6 100644 --- a/dlls/winealsa.drv/mmdevdrv.c +++ b/dlls/winealsa.drv/mmdevdrv.c @@ -109,7 +109,7 @@ struct ACImpl { UINT32 channel_count; struct alsa_stream *stream;
- HANDLE timer; + HANDLE timer_thread;
AudioSession *session; AudioSessionWrapper *session_wrapper; @@ -128,8 +128,6 @@ typedef struct _SessionMgr { IMMDevice *device; } SessionMgr;
-static HANDLE g_timer_q; - static CRITICAL_SECTION g_sessions_lock; static CRITICAL_SECTION_DEBUG g_sessions_lock_debug = { @@ -216,9 +214,6 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved) if(NtQueryVirtualMemory(GetCurrentProcess(), dll, MemoryWineUnixFuncs, &alsa_handle, sizeof(alsa_handle), NULL)) return FALSE; - g_timer_q = CreateTimerQueue(); - if(!g_timer_q) - return FALSE; break;
case DLL_PROCESS_DETACH: @@ -252,17 +247,30 @@ static void alsa_unlock(struct alsa_stream *stream) pthread_mutex_unlock(&stream->lock); }
-static HRESULT alsa_stream_release(struct alsa_stream *stream) +static HRESULT alsa_stream_release(struct alsa_stream *stream, HANDLE timer_thread) { struct release_stream_params params;
params.stream = stream; + params.timer_thread = timer_thread;
ALSA_CALL(release_stream, ¶ms);
return params.result; }
+static DWORD WINAPI alsa_timer_thread(void *user) +{ + struct alsa_stream *stream = user; + struct timer_loop_params params; + + params.stream = stream; + + ALSA_CALL(timer_loop, ¶ms); + + return 0; +} + static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name, GUID *guid) { @@ -552,17 +560,6 @@ static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface) ref = InterlockedDecrement(&This->ref); TRACE("(%p) Refcount now %u\n", This, ref); if(!ref){ - if(This->timer){ - HANDLE event; - DWORD wait; - event = CreateEventW(NULL, TRUE, FALSE, NULL); - wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event); - wait = wait && GetLastError() == ERROR_IO_PENDING; - if(event && wait) - WaitForSingleObject(event, INFINITE); - CloseHandle(event); - } - IAudioClient3_Stop(iface); IMMDevice_Release(This->parent); IUnknown_Release(This->pUnkFTMarshal); @@ -573,7 +570,7 @@ static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface) } HeapFree(GetProcessHeap(), 0, This->vols); if (This->stream) - alsa_stream_release(This->stream); + alsa_stream_release(This->stream, This->timer_thread); HeapFree(GetProcessHeap(), 0, This); } return ref; @@ -807,7 +804,7 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
exit: if(FAILED(params.result)){ - alsa_stream_release(stream); + alsa_stream_release(stream, NULL); HeapFree(GetProcessHeap(), 0, This->vols); This->vols = NULL; }else{ @@ -1011,187 +1008,6 @@ static snd_pcm_sframes_t alsa_write_buffer_wrap(struct alsa_stream *stream, BYTE return ret; }
-static UINT buf_ptr_diff(UINT left, UINT right, UINT bufsize) -{ - if(left <= right) - return right - left; - return bufsize - (left - right); -} - -static UINT data_not_in_alsa(struct alsa_stream *stream) -{ - UINT32 diff; - - diff = buf_ptr_diff(stream->lcl_offs_frames, stream->wri_offs_frames, stream->bufsize_frames); - if(diff) - return diff; - - return stream->held_frames - stream->data_in_alsa_frames; -} -/* Here's the buffer setup: - * - * vvvvvvvv sent to HW already - * vvvvvvvv in ALSA buffer but rewindable - * [dddddddddddddddd] ALSA buffer - * [dddddddddddddddd--------] mmdevapi buffer - * ^^^^^^^^ data_in_alsa_frames - * ^^^^^^^^^^^^^^^^ held_frames - * ^ lcl_offs_frames - * ^ wri_offs_frames - * - * GetCurrentPadding is held_frames - * - * During period callback, we decrement held_frames, fill ALSA buffer, and move - * lcl_offs forward - * - * During Stop, we rewind the ALSA buffer - */ -static void alsa_write_data(struct alsa_stream *stream) -{ - snd_pcm_sframes_t written; - snd_pcm_uframes_t avail, max_copy_frames, data_frames_played; - int err; - - /* this call seems to be required to get an accurate snd_pcm_state() */ - avail = snd_pcm_avail_update(stream->pcm_handle); - - if(snd_pcm_state(stream->pcm_handle) == SND_PCM_STATE_XRUN){ - TRACE("XRun state, recovering\n"); - - avail = stream->alsa_bufsize_frames; - - if((err = snd_pcm_recover(stream->pcm_handle, -EPIPE, 1)) < 0) - WARN("snd_pcm_recover failed: %d (%s)\n", err, snd_strerror(err)); - - if((err = snd_pcm_reset(stream->pcm_handle)) < 0) - WARN("snd_pcm_reset failed: %d (%s)\n", err, snd_strerror(err)); - - if((err = snd_pcm_prepare(stream->pcm_handle)) < 0) - WARN("snd_pcm_prepare failed: %d (%s)\n", err, snd_strerror(err)); - } - - TRACE("avail: %ld\n", avail); - - /* Add a lead-in when starting with too few frames to ensure - * continuous rendering. Additional benefit: Force ALSA to start. */ - if(stream->data_in_alsa_frames == 0 && stream->held_frames < stream->alsa_period_frames) - { - alsa_write_best_effort(stream, stream->silence_buf, - stream->alsa_period_frames - stream->held_frames); - stream->vol_adjusted_frames = 0; - } - - if(stream->started) - max_copy_frames = data_not_in_alsa(stream); - else - max_copy_frames = 0; - - data_frames_played = min(stream->data_in_alsa_frames, avail); - stream->data_in_alsa_frames -= data_frames_played; - - if(stream->held_frames > data_frames_played){ - if(stream->started) - stream->held_frames -= data_frames_played; - }else - stream->held_frames = 0; - - while(avail && max_copy_frames){ - snd_pcm_uframes_t to_write; - - to_write = min(avail, max_copy_frames); - - written = alsa_write_buffer_wrap(stream, stream->local_buffer, - stream->bufsize_frames, stream->lcl_offs_frames, to_write); - if(written <= 0) - break; - - avail -= written; - stream->lcl_offs_frames += written; - stream->lcl_offs_frames %= stream->bufsize_frames; - stream->data_in_alsa_frames += written; - max_copy_frames -= written; - } - - if(stream->event) - SetEvent(stream->event); -} - -static void alsa_read_data(struct alsa_stream *stream) -{ - snd_pcm_sframes_t nread; - UINT32 pos = stream->wri_offs_frames, limit = stream->held_frames; - unsigned int i; - - if(!stream->started) - goto exit; - - /* FIXME: Detect overrun and signal DATA_DISCONTINUITY - * How to count overrun frames and report them as position increase? */ - limit = stream->bufsize_frames - max(limit, pos); - - nread = snd_pcm_readi(stream->pcm_handle, - stream->local_buffer + pos * stream->fmt->nBlockAlign, limit); - TRACE("read %ld from %u limit %u\n", nread, pos, limit); - if(nread < 0){ - int ret; - - if(nread == -EAGAIN) /* no data yet */ - return; - - WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread)); - - ret = snd_pcm_recover(stream->pcm_handle, nread, 0); - if(ret < 0){ - WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret)); - return; - } - - nread = snd_pcm_readi(stream->pcm_handle, - stream->local_buffer + pos * stream->fmt->nBlockAlign, limit); - if(nread < 0){ - WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread)); - return; - } - } - - for(i = 0; i < stream->fmt->nChannels; i++) - if(stream->vols[i] != 0.0f) - break; - if(i == stream->fmt->nChannels){ /* mute */ - int err; - if((err = snd_pcm_format_set_silence(stream->alsa_format, - stream->local_buffer + pos * stream->fmt->nBlockAlign, - nread)) < 0) - WARN("Setting buffer to silence failed: %d (%s)\n", err, - snd_strerror(err)); - } - - stream->wri_offs_frames += nread; - stream->wri_offs_frames %= stream->bufsize_frames; - stream->held_frames += nread; - -exit: - if(stream->event) - SetEvent(stream->event); -} - -static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer) -{ - ACImpl *This = user; - struct alsa_stream *stream = This->stream; - - alsa_lock(stream); - - QueryPerformanceCounter(&stream->last_period_time); - - if(stream->flow == eRender) - alsa_write_data(stream); - else if(stream->flow == eCapture) - alsa_read_data(stream); - - alsa_unlock(stream); -} - static snd_pcm_uframes_t interp_elapsed_frames(struct alsa_stream *stream) { LARGE_INTEGER time_freq, current_time, time_diff; @@ -1291,14 +1107,9 @@ static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface) } }
- if(!This->timer){ - if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data, - This, 0, stream->mmdev_period_rt / 10000, WT_EXECUTEINTIMERTHREAD)){ - alsa_unlock(stream); - LeaveCriticalSection(&g_sessions_lock); - WARN("Unable to create timer: %u\n", GetLastError()); - return E_OUTOFMEMORY; - } + if(!This->timer_thread){ + This->timer_thread = CreateThread(NULL, 0, alsa_timer_thread, This->stream, 0, NULL); + SetThreadPriority(This->timer_thread, THREAD_PRIORITY_TIME_CRITICAL); }
stream->started = TRUE; diff --git a/dlls/winealsa.drv/unixlib.h b/dlls/winealsa.drv/unixlib.h index 3fd6d2a73ee..4acbbcbcf11 100644 --- a/dlls/winealsa.drv/unixlib.h +++ b/dlls/winealsa.drv/unixlib.h @@ -37,7 +37,7 @@ struct alsa_stream int alsa_channels; int alsa_channel_map[32];
- BOOL started; + BOOL started, please_quit; REFERENCE_TIME mmdev_period_rt; UINT64 written_frames, last_pos_frames; UINT32 bufsize_frames, held_frames, tmp_buffer_frames, mmdev_period_frames; @@ -87,9 +87,15 @@ struct create_stream_params struct release_stream_params { struct alsa_stream *stream; + HANDLE timer_thread; HRESULT result; };
+struct timer_loop_params +{ + struct alsa_stream *stream; +}; + struct is_format_supported_params { const char *alsa_name; @@ -142,6 +148,7 @@ enum alsa_funcs alsa_get_endpoint_ids, alsa_create_stream, alsa_release_stream, + alsa_timer_loop, alsa_is_format_supported, alsa_get_mix_format, alsa_get_buffer_size,
Signed-off-by: Andrew Eikum aeikum@codeweavers.com
On Wed, Mar 02, 2022 at 10:53:16AM +0000, Huw Davies wrote:
Signed-off-by: Huw Davies huw@codeweavers.com
dlls/winealsa.drv/alsa.c | 240 +++++++++++++++++++++++++++++++++++ dlls/winealsa.drv/mmdevdrv.c | 229 +++------------------------------ dlls/winealsa.drv/unixlib.h | 9 +- 3 files changed, 268 insertions(+), 210 deletions(-)
diff --git a/dlls/winealsa.drv/alsa.c b/dlls/winealsa.drv/alsa.c index 73971c0f8bb..d455a3787e8 100644 --- a/dlls/winealsa.drv/alsa.c +++ b/dlls/winealsa.drv/alsa.c @@ -960,6 +960,12 @@ static NTSTATUS release_stream(void *args) struct alsa_stream *stream = params->stream; SIZE_T size;
- if(params->timer_thread){
stream->please_quit = TRUE;
NtWaitForSingleObject(params->timer_thread, FALSE, NULL);
NtClose(params->timer_thread);
- }
- snd_pcm_drop(stream->pcm_handle); snd_pcm_close(stream->pcm_handle); if(stream->local_buffer){
@@ -1215,6 +1221,239 @@ static NTSTATUS write_best_effort(void *args) return STATUS_SUCCESS; }
+static snd_pcm_sframes_t alsa_write_buffer_wrap(struct alsa_stream *stream, BYTE *buf,
snd_pcm_uframes_t buflen, snd_pcm_uframes_t offs,
snd_pcm_uframes_t to_write)
+{
- snd_pcm_sframes_t ret = 0;
- while(to_write){
snd_pcm_uframes_t chunk;
snd_pcm_sframes_t tmp;
if(offs + to_write > buflen)
chunk = buflen - offs;
else
chunk = to_write;
tmp = alsa_write_best_effort(stream, buf + offs * stream->fmt->nBlockAlign, chunk);
if(tmp < 0)
return ret;
if(!tmp)
break;
ret += tmp;
to_write -= tmp;
offs += tmp;
offs %= buflen;
- }
- return ret;
+}
+static UINT buf_ptr_diff(UINT left, UINT right, UINT bufsize) +{
- if(left <= right)
return right - left;
- return bufsize - (left - right);
+}
+static UINT data_not_in_alsa(struct alsa_stream *stream) +{
- UINT32 diff;
- diff = buf_ptr_diff(stream->lcl_offs_frames, stream->wri_offs_frames, stream->bufsize_frames);
- if(diff)
return diff;
- return stream->held_frames - stream->data_in_alsa_frames;
+}
+/* Here's the buffer setup:
- vvvvvvvv sent to HW already
vvvvvvvv in ALSA buffer but rewindable
- [dddddddddddddddd] ALSA buffer
[dddddddddddddddd--------] mmdevapi buffer
^^^^^^^^ data_in_alsa_frames
^^^^^^^^^^^^^^^^ held_frames
^ lcl_offs_frames
^ wri_offs_frames
- GetCurrentPadding is held_frames
- During period callback, we decrement held_frames, fill ALSA buffer, and move
- lcl_offs forward
- During Stop, we rewind the ALSA buffer
- */
+static void alsa_write_data(struct alsa_stream *stream) +{
- snd_pcm_sframes_t written;
- snd_pcm_uframes_t avail, max_copy_frames, data_frames_played;
- int err;
- /* this call seems to be required to get an accurate snd_pcm_state() */
- avail = snd_pcm_avail_update(stream->pcm_handle);
- if(snd_pcm_state(stream->pcm_handle) == SND_PCM_STATE_XRUN){
TRACE("XRun state, recovering\n");
avail = stream->alsa_bufsize_frames;
if((err = snd_pcm_recover(stream->pcm_handle, -EPIPE, 1)) < 0)
WARN("snd_pcm_recover failed: %d (%s)\n", err, snd_strerror(err));
if((err = snd_pcm_reset(stream->pcm_handle)) < 0)
WARN("snd_pcm_reset failed: %d (%s)\n", err, snd_strerror(err));
if((err = snd_pcm_prepare(stream->pcm_handle)) < 0)
WARN("snd_pcm_prepare failed: %d (%s)\n", err, snd_strerror(err));
- }
- TRACE("avail: %ld\n", avail);
- /* Add a lead-in when starting with too few frames to ensure
* continuous rendering. Additional benefit: Force ALSA to start. */
- if(stream->data_in_alsa_frames == 0 && stream->held_frames < stream->alsa_period_frames)
- {
alsa_write_best_effort(stream, stream->silence_buf,
stream->alsa_period_frames - stream->held_frames);
stream->vol_adjusted_frames = 0;
- }
- if(stream->started)
max_copy_frames = data_not_in_alsa(stream);
- else
max_copy_frames = 0;
- data_frames_played = min(stream->data_in_alsa_frames, avail);
- stream->data_in_alsa_frames -= data_frames_played;
- if(stream->held_frames > data_frames_played){
if(stream->started)
stream->held_frames -= data_frames_played;
- }else
stream->held_frames = 0;
- while(avail && max_copy_frames){
snd_pcm_uframes_t to_write;
to_write = min(avail, max_copy_frames);
written = alsa_write_buffer_wrap(stream, stream->local_buffer,
stream->bufsize_frames, stream->lcl_offs_frames, to_write);
if(written <= 0)
break;
avail -= written;
stream->lcl_offs_frames += written;
stream->lcl_offs_frames %= stream->bufsize_frames;
stream->data_in_alsa_frames += written;
max_copy_frames -= written;
- }
- if(stream->event)
NtSetEvent(stream->event, NULL);
+}
+static void alsa_read_data(struct alsa_stream *stream) +{
- snd_pcm_sframes_t nread;
- UINT32 pos = stream->wri_offs_frames, limit = stream->held_frames;
- unsigned int i;
- if(!stream->started)
goto exit;
- /* FIXME: Detect overrun and signal DATA_DISCONTINUITY
* How to count overrun frames and report them as position increase? */
- limit = stream->bufsize_frames - max(limit, pos);
- nread = snd_pcm_readi(stream->pcm_handle,
stream->local_buffer + pos * stream->fmt->nBlockAlign, limit);
- TRACE("read %ld from %u limit %u\n", nread, pos, limit);
- if(nread < 0){
int ret;
if(nread == -EAGAIN) /* no data yet */
return;
WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
ret = snd_pcm_recover(stream->pcm_handle, nread, 0);
if(ret < 0){
WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
return;
}
nread = snd_pcm_readi(stream->pcm_handle,
stream->local_buffer + pos * stream->fmt->nBlockAlign, limit);
if(nread < 0){
WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread));
return;
}
- }
- for(i = 0; i < stream->fmt->nChannels; i++)
if(stream->vols[i] != 0.0f)
break;
- if(i == stream->fmt->nChannels){ /* mute */
int err;
if((err = snd_pcm_format_set_silence(stream->alsa_format,
stream->local_buffer + pos * stream->fmt->nBlockAlign,
nread)) < 0)
WARN("Setting buffer to silence failed: %d (%s)\n", err,
snd_strerror(err));
- }
- stream->wri_offs_frames += nread;
- stream->wri_offs_frames %= stream->bufsize_frames;
- stream->held_frames += nread;
+exit:
- if(stream->event)
NtSetEvent(stream->event, NULL);
+}
+static NTSTATUS timer_loop(void *args) +{
- struct timer_loop_params *params = args;
- struct alsa_stream *stream = params->stream;
- LARGE_INTEGER delay, next;
- int adjust;
- alsa_lock(stream);
- delay.QuadPart = -stream->mmdev_period_rt;
- NtQueryPerformanceCounter(&stream->last_period_time, NULL);
- next.QuadPart = stream->last_period_time.QuadPart + stream->mmdev_period_rt;
- while(!stream->please_quit){
if(stream->flow == eRender)
alsa_write_data(stream);
else if(stream->flow == eCapture)
alsa_read_data(stream);
alsa_unlock(stream);
NtDelayExecution(FALSE, &delay);
alsa_lock(stream);
NtQueryPerformanceCounter(&stream->last_period_time, NULL);
adjust = next.QuadPart - stream->last_period_time.QuadPart;
if(adjust > stream->mmdev_period_rt / 2)
adjust = stream->mmdev_period_rt / 2;
else if(adjust < -stream->mmdev_period_rt / 2)
adjust = -stream->mmdev_period_rt / 2;
delay.QuadPart = -(stream->mmdev_period_rt + adjust);
next.QuadPart += stream->mmdev_period_rt;
- }
- alsa_unlock(stream);
- return STATUS_SUCCESS;
+}
static NTSTATUS is_format_supported(void *args) { struct is_format_supported_params *params = args; @@ -1522,6 +1761,7 @@ unixlib_entry_t __wine_unix_call_funcs[] = get_endpoint_ids, create_stream, release_stream,
- timer_loop, is_format_supported, get_mix_format, get_buffer_size,
diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c index c06b08709f2..2748cc552a6 100644 --- a/dlls/winealsa.drv/mmdevdrv.c +++ b/dlls/winealsa.drv/mmdevdrv.c @@ -109,7 +109,7 @@ struct ACImpl { UINT32 channel_count; struct alsa_stream *stream;
- HANDLE timer;
HANDLE timer_thread;
AudioSession *session; AudioSessionWrapper *session_wrapper;
@@ -128,8 +128,6 @@ typedef struct _SessionMgr { IMMDevice *device; } SessionMgr;
-static HANDLE g_timer_q;
static CRITICAL_SECTION g_sessions_lock; static CRITICAL_SECTION_DEBUG g_sessions_lock_debug = { @@ -216,9 +214,6 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved) if(NtQueryVirtualMemory(GetCurrentProcess(), dll, MemoryWineUnixFuncs, &alsa_handle, sizeof(alsa_handle), NULL)) return FALSE;
g_timer_q = CreateTimerQueue();
if(!g_timer_q)
return FALSE; break;
case DLL_PROCESS_DETACH:
@@ -252,17 +247,30 @@ static void alsa_unlock(struct alsa_stream *stream) pthread_mutex_unlock(&stream->lock); }
-static HRESULT alsa_stream_release(struct alsa_stream *stream) +static HRESULT alsa_stream_release(struct alsa_stream *stream, HANDLE timer_thread) { struct release_stream_params params;
params.stream = stream;
params.timer_thread = timer_thread;
ALSA_CALL(release_stream, ¶ms);
return params.result;
}
+static DWORD WINAPI alsa_timer_thread(void *user) +{
- struct alsa_stream *stream = user;
- struct timer_loop_params params;
- params.stream = stream;
- ALSA_CALL(timer_loop, ¶ms);
- return 0;
+}
static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name, GUID *guid) { @@ -552,17 +560,6 @@ static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface) ref = InterlockedDecrement(&This->ref); TRACE("(%p) Refcount now %u\n", This, ref); if(!ref){
if(This->timer){
HANDLE event;
DWORD wait;
event = CreateEventW(NULL, TRUE, FALSE, NULL);
wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
wait = wait && GetLastError() == ERROR_IO_PENDING;
if(event && wait)
WaitForSingleObject(event, INFINITE);
CloseHandle(event);
}
IAudioClient3_Stop(iface); IMMDevice_Release(This->parent); IUnknown_Release(This->pUnkFTMarshal);
@@ -573,7 +570,7 @@ static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface) } HeapFree(GetProcessHeap(), 0, This->vols); if (This->stream)
alsa_stream_release(This->stream);
} return ref;alsa_stream_release(This->stream, This->timer_thread); HeapFree(GetProcessHeap(), 0, This);
@@ -807,7 +804,7 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
exit: if(FAILED(params.result)){
alsa_stream_release(stream);
}else{alsa_stream_release(stream, NULL); HeapFree(GetProcessHeap(), 0, This->vols); This->vols = NULL;
@@ -1011,187 +1008,6 @@ static snd_pcm_sframes_t alsa_write_buffer_wrap(struct alsa_stream *stream, BYTE return ret; }
-static UINT buf_ptr_diff(UINT left, UINT right, UINT bufsize) -{
- if(left <= right)
return right - left;
- return bufsize - (left - right);
-}
-static UINT data_not_in_alsa(struct alsa_stream *stream) -{
- UINT32 diff;
- diff = buf_ptr_diff(stream->lcl_offs_frames, stream->wri_offs_frames, stream->bufsize_frames);
- if(diff)
return diff;
- return stream->held_frames - stream->data_in_alsa_frames;
-} -/* Here's the buffer setup:
- vvvvvvvv sent to HW already
vvvvvvvv in ALSA buffer but rewindable
- [dddddddddddddddd] ALSA buffer
[dddddddddddddddd--------] mmdevapi buffer
^^^^^^^^ data_in_alsa_frames
^^^^^^^^^^^^^^^^ held_frames
^ lcl_offs_frames
^ wri_offs_frames
- GetCurrentPadding is held_frames
- During period callback, we decrement held_frames, fill ALSA buffer, and move
- lcl_offs forward
- During Stop, we rewind the ALSA buffer
- */
-static void alsa_write_data(struct alsa_stream *stream) -{
- snd_pcm_sframes_t written;
- snd_pcm_uframes_t avail, max_copy_frames, data_frames_played;
- int err;
- /* this call seems to be required to get an accurate snd_pcm_state() */
- avail = snd_pcm_avail_update(stream->pcm_handle);
- if(snd_pcm_state(stream->pcm_handle) == SND_PCM_STATE_XRUN){
TRACE("XRun state, recovering\n");
avail = stream->alsa_bufsize_frames;
if((err = snd_pcm_recover(stream->pcm_handle, -EPIPE, 1)) < 0)
WARN("snd_pcm_recover failed: %d (%s)\n", err, snd_strerror(err));
if((err = snd_pcm_reset(stream->pcm_handle)) < 0)
WARN("snd_pcm_reset failed: %d (%s)\n", err, snd_strerror(err));
if((err = snd_pcm_prepare(stream->pcm_handle)) < 0)
WARN("snd_pcm_prepare failed: %d (%s)\n", err, snd_strerror(err));
- }
- TRACE("avail: %ld\n", avail);
- /* Add a lead-in when starting with too few frames to ensure
* continuous rendering. Additional benefit: Force ALSA to start. */
- if(stream->data_in_alsa_frames == 0 && stream->held_frames < stream->alsa_period_frames)
- {
alsa_write_best_effort(stream, stream->silence_buf,
stream->alsa_period_frames - stream->held_frames);
stream->vol_adjusted_frames = 0;
- }
- if(stream->started)
max_copy_frames = data_not_in_alsa(stream);
- else
max_copy_frames = 0;
- data_frames_played = min(stream->data_in_alsa_frames, avail);
- stream->data_in_alsa_frames -= data_frames_played;
- if(stream->held_frames > data_frames_played){
if(stream->started)
stream->held_frames -= data_frames_played;
- }else
stream->held_frames = 0;
- while(avail && max_copy_frames){
snd_pcm_uframes_t to_write;
to_write = min(avail, max_copy_frames);
written = alsa_write_buffer_wrap(stream, stream->local_buffer,
stream->bufsize_frames, stream->lcl_offs_frames, to_write);
if(written <= 0)
break;
avail -= written;
stream->lcl_offs_frames += written;
stream->lcl_offs_frames %= stream->bufsize_frames;
stream->data_in_alsa_frames += written;
max_copy_frames -= written;
- }
- if(stream->event)
SetEvent(stream->event);
-}
-static void alsa_read_data(struct alsa_stream *stream) -{
- snd_pcm_sframes_t nread;
- UINT32 pos = stream->wri_offs_frames, limit = stream->held_frames;
- unsigned int i;
- if(!stream->started)
goto exit;
- /* FIXME: Detect overrun and signal DATA_DISCONTINUITY
* How to count overrun frames and report them as position increase? */
- limit = stream->bufsize_frames - max(limit, pos);
- nread = snd_pcm_readi(stream->pcm_handle,
stream->local_buffer + pos * stream->fmt->nBlockAlign, limit);
- TRACE("read %ld from %u limit %u\n", nread, pos, limit);
- if(nread < 0){
int ret;
if(nread == -EAGAIN) /* no data yet */
return;
WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
ret = snd_pcm_recover(stream->pcm_handle, nread, 0);
if(ret < 0){
WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
return;
}
nread = snd_pcm_readi(stream->pcm_handle,
stream->local_buffer + pos * stream->fmt->nBlockAlign, limit);
if(nread < 0){
WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread));
return;
}
- }
- for(i = 0; i < stream->fmt->nChannels; i++)
if(stream->vols[i] != 0.0f)
break;
- if(i == stream->fmt->nChannels){ /* mute */
int err;
if((err = snd_pcm_format_set_silence(stream->alsa_format,
stream->local_buffer + pos * stream->fmt->nBlockAlign,
nread)) < 0)
WARN("Setting buffer to silence failed: %d (%s)\n", err,
snd_strerror(err));
- }
- stream->wri_offs_frames += nread;
- stream->wri_offs_frames %= stream->bufsize_frames;
- stream->held_frames += nread;
-exit:
- if(stream->event)
SetEvent(stream->event);
-}
-static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer) -{
- ACImpl *This = user;
- struct alsa_stream *stream = This->stream;
- alsa_lock(stream);
- QueryPerformanceCounter(&stream->last_period_time);
- if(stream->flow == eRender)
alsa_write_data(stream);
- else if(stream->flow == eCapture)
alsa_read_data(stream);
- alsa_unlock(stream);
-}
static snd_pcm_uframes_t interp_elapsed_frames(struct alsa_stream *stream) { LARGE_INTEGER time_freq, current_time, time_diff; @@ -1291,14 +1107,9 @@ static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface) } }
- if(!This->timer){
if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
This, 0, stream->mmdev_period_rt / 10000, WT_EXECUTEINTIMERTHREAD)){
alsa_unlock(stream);
LeaveCriticalSection(&g_sessions_lock);
WARN("Unable to create timer: %u\n", GetLastError());
return E_OUTOFMEMORY;
}
if(!This->timer_thread){
This->timer_thread = CreateThread(NULL, 0, alsa_timer_thread, This->stream, 0, NULL);
SetThreadPriority(This->timer_thread, THREAD_PRIORITY_TIME_CRITICAL);
}
stream->started = TRUE;
diff --git a/dlls/winealsa.drv/unixlib.h b/dlls/winealsa.drv/unixlib.h index 3fd6d2a73ee..4acbbcbcf11 100644 --- a/dlls/winealsa.drv/unixlib.h +++ b/dlls/winealsa.drv/unixlib.h @@ -37,7 +37,7 @@ struct alsa_stream int alsa_channels; int alsa_channel_map[32];
- BOOL started;
- BOOL started, please_quit; REFERENCE_TIME mmdev_period_rt; UINT64 written_frames, last_pos_frames; UINT32 bufsize_frames, held_frames, tmp_buffer_frames, mmdev_period_frames;
@@ -87,9 +87,15 @@ struct create_stream_params struct release_stream_params { struct alsa_stream *stream;
- HANDLE timer_thread; HRESULT result;
};
+struct timer_loop_params +{
- struct alsa_stream *stream;
+};
struct is_format_supported_params { const char *alsa_name; @@ -142,6 +148,7 @@ enum alsa_funcs alsa_get_endpoint_ids, alsa_create_stream, alsa_release_stream,
- alsa_timer_loop, alsa_is_format_supported, alsa_get_mix_format, alsa_get_buffer_size,
-- 2.25.1