[PATCH 3/8] winealsa: Introduce a stream structure.

Huw Davies huw at codeweavers.com
Wed Feb 16 03:34:49 CST 2022


Signed-off-by: Huw Davies <huw at codeweavers.com>
---
 dlls/winealsa.drv/mmdevdrv.c | 691 ++++++++++++++++++-----------------
 dlls/winealsa.drv/unixlib.h  |  35 ++
 2 files changed, 385 insertions(+), 341 deletions(-)

diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
index 0549752dc3f..f1bc2fd0247 100644
--- a/dlls/winealsa.drv/mmdevdrv.c
+++ b/dlls/winealsa.drv/mmdevdrv.c
@@ -101,42 +101,17 @@ struct ACImpl {
 
     LONG ref;
 
-    snd_pcm_t *pcm_handle;
-    snd_pcm_uframes_t alsa_bufsize_frames, alsa_period_frames, safe_rewind_frames;
-    snd_pcm_hw_params_t *hw_params; /* does not hold state between calls */
-    snd_pcm_format_t alsa_format;
-
-    LARGE_INTEGER last_period_time;
-
     IMMDevice *parent;
     IUnknown *pUnkFTMarshal;
 
     EDataFlow dataflow;
-    WAVEFORMATEX *fmt;
-    DWORD flags;
-    AUDCLNT_SHAREMODE share;
-    HANDLE event;
     float *vols;
     UINT32 channel_count;
+    struct alsa_stream *stream;
 
-    BOOL need_remapping;
-    int alsa_channels;
-    int alsa_channel_map[32];
-
-    BOOL initted, started;
-    REFERENCE_TIME mmdev_period_rt;
-    UINT64 written_frames, last_pos_frames;
-    UINT32 bufsize_frames, held_frames, tmp_buffer_frames, mmdev_period_frames;
-    snd_pcm_uframes_t remapping_buf_frames;
-    UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
-    UINT32 wri_offs_frames; /* where to write fresh data in local_buffer */
-    UINT32 hidden_frames;   /* ALSA reserve to ensure continuous rendering */
-    UINT32 vol_adjusted_frames; /* Frames we've already adjusted the volume of but didn't write yet */
-    UINT32 data_in_alsa_frames;
+    BOOL initted;
 
     HANDLE timer;
-    BYTE *local_buffer, *tmp_buffer, *remapping_buf, *silence_buf;
-    LONG32 getbuf_last; /* <0 when using tmp_buffer */
 
     CRITICAL_SECTION lock;
 
@@ -505,8 +480,15 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient
     This->dataflow = dataflow;
     memcpy(This->alsa_name, alsa_name, len + 1);
 
-    err = snd_pcm_open(&This->pcm_handle, alsa_name, alsa_get_direction(dataflow), SND_PCM_NONBLOCK);
+    This->stream = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This->stream));
+    if(!This->stream){
+        HeapFree(GetProcessHeap(), 0, This);
+        return E_OUTOFMEMORY;
+    }
+
+    err = snd_pcm_open(&This->stream->pcm_handle, alsa_name, alsa_get_direction(dataflow), SND_PCM_NONBLOCK);
     if(err < 0){
+        HeapFree(GetProcessHeap(), 0, This->stream);
         HeapFree(GetProcessHeap(), 0, This);
         WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name, err, snd_strerror(err));
         switch(err){
@@ -517,10 +499,11 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient
         }
     }
 
-    This->hw_params = HeapAlloc(GetProcessHeap(), 0,
+    This->stream->hw_params = HeapAlloc(GetProcessHeap(), 0,
             snd_pcm_hw_params_sizeof());
-    if(!This->hw_params){
-        snd_pcm_close(This->pcm_handle);
+    if(!This->stream->hw_params){
+        snd_pcm_close(This->stream->pcm_handle);
+        HeapFree(GetProcessHeap(), 0, This->stream);
         HeapFree(GetProcessHeap(), 0, This);
         return E_OUTOFMEMORY;
     }
@@ -574,6 +557,7 @@ static ULONG WINAPI AudioClient_AddRef(IAudioClient3 *iface)
 static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
 {
     ACImpl *This = impl_from_IAudioClient3(iface);
+    struct alsa_stream *stream = This->stream;
     ULONG ref;
 
     ref = InterlockedDecrement(&This->ref);
@@ -595,20 +579,21 @@ static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
         IUnknown_Release(This->pUnkFTMarshal);
         This->lock.DebugInfo->Spare[0] = 0;
         DeleteCriticalSection(&This->lock);
-        snd_pcm_drop(This->pcm_handle);
-        snd_pcm_close(This->pcm_handle);
+        snd_pcm_drop(stream->pcm_handle);
+        snd_pcm_close(stream->pcm_handle);
         if(This->initted){
             EnterCriticalSection(&g_sessions_lock);
             list_remove(&This->entry);
             LeaveCriticalSection(&g_sessions_lock);
         }
         HeapFree(GetProcessHeap(), 0, This->vols);
-        HeapFree(GetProcessHeap(), 0, This->local_buffer);
-        HeapFree(GetProcessHeap(), 0, This->remapping_buf);
-        HeapFree(GetProcessHeap(), 0, This->silence_buf);
-        HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
-        HeapFree(GetProcessHeap(), 0, This->hw_params);
-        CoTaskMemFree(This->fmt);
+        HeapFree(GetProcessHeap(), 0, stream->local_buffer);
+        HeapFree(GetProcessHeap(), 0, stream->remapping_buf);
+        HeapFree(GetProcessHeap(), 0, stream->silence_buf);
+        HeapFree(GetProcessHeap(), 0, stream->tmp_buffer);
+        HeapFree(GetProcessHeap(), 0, stream->hw_params);
+        CoTaskMemFree(stream->fmt);
+        HeapFree(GetProcessHeap(), 0, stream);
         HeapFree(GetProcessHeap(), 0, This);
     }
     return ref;
@@ -904,16 +889,16 @@ static HRESULT map_channels(ACImpl *This, const WAVEFORMATEX *fmt, int *alsa_cha
     return need_remap ? S_OK : S_FALSE;
 }
 
-static void silence_buffer(ACImpl *This, BYTE *buffer, UINT32 frames)
+static void silence_buffer(struct alsa_stream *stream, BYTE *buffer, UINT32 frames)
 {
-    WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
-    if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
-            (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
+    WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)stream->fmt;
+    if((stream->fmt->wFormatTag == WAVE_FORMAT_PCM ||
+            (stream->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
              IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
-            This->fmt->wBitsPerSample == 8)
-        memset(buffer, 128, frames * This->fmt->nBlockAlign);
+            stream->fmt->wBitsPerSample == 8)
+        memset(buffer, 128, frames * stream->fmt->nBlockAlign);
     else
-        memset(buffer, 0, frames * This->fmt->nBlockAlign);
+        memset(buffer, 0, frames * stream->fmt->nBlockAlign);
 }
 
 static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
@@ -922,6 +907,7 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
         const GUID *sessionguid)
 {
     ACImpl *This = impl_from_IAudioClient3(iface);
+    struct alsa_stream *stream = This->stream;
     snd_pcm_sw_params_t *sw_params = NULL;
     snd_pcm_format_t format;
     unsigned int rate, alsa_period_us;
@@ -990,15 +976,15 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
 
     dump_fmt(fmt);
 
-    This->need_remapping = map_channels(This, fmt, &This->alsa_channels, This->alsa_channel_map) == S_OK;
+    stream->need_remapping = map_channels(This, fmt, &stream->alsa_channels, stream->alsa_channel_map) == S_OK;
 
-    if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
+    if((err = snd_pcm_hw_params_any(stream->pcm_handle, stream->hw_params)) < 0){
         WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
         goto exit;
     }
 
-    if((err = snd_pcm_hw_params_set_access(This->pcm_handle, This->hw_params,
+    if((err = snd_pcm_hw_params_set_access(stream->pcm_handle, stream->hw_params,
                 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
         WARN("Unable to set access: %d (%s)\n", err, snd_strerror(err));
         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
@@ -1011,7 +997,7 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
         goto exit;
     }
 
-    if((err = snd_pcm_hw_params_set_format(This->pcm_handle, This->hw_params,
+    if((err = snd_pcm_hw_params_set_format(stream->pcm_handle, stream->hw_params,
                 format)) < 0){
         WARN("Unable to set ALSA format to %u: %d (%s)\n", format, err,
                 snd_strerror(err));
@@ -1019,10 +1005,10 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
         goto exit;
     }
 
-    This->alsa_format = format;
+    stream->alsa_format = format;
 
     rate = fmt->nSamplesPerSec;
-    if((err = snd_pcm_hw_params_set_rate_near(This->pcm_handle, This->hw_params,
+    if((err = snd_pcm_hw_params_set_rate_near(stream->pcm_handle, stream->hw_params,
                 &rate, NULL)) < 0){
         WARN("Unable to set rate to %u: %d (%s)\n", rate, err,
                 snd_strerror(err));
@@ -1030,52 +1016,52 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
         goto exit;
     }
 
-    if((err = snd_pcm_hw_params_set_channels(This->pcm_handle, This->hw_params,
-                This->alsa_channels)) < 0){
+    if((err = snd_pcm_hw_params_set_channels(stream->pcm_handle, stream->hw_params,
+               stream->alsa_channels)) < 0){
         WARN("Unable to set channels to %u: %d (%s)\n", fmt->nChannels, err,
                 snd_strerror(err));
         hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
         goto exit;
     }
 
-    This->mmdev_period_rt = period;
-    alsa_period_us = This->mmdev_period_rt / 10;
-    if((err = snd_pcm_hw_params_set_period_time_near(This->pcm_handle,
-                This->hw_params, &alsa_period_us, NULL)) < 0)
+    stream->mmdev_period_rt = period;
+    alsa_period_us = stream->mmdev_period_rt / 10;
+    if((err = snd_pcm_hw_params_set_period_time_near(stream->pcm_handle,
+                stream->hw_params, &alsa_period_us, NULL)) < 0)
         WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us,
                 err, snd_strerror(err));
     /* ALSA updates the output variable alsa_period_us */
 
-    This->mmdev_period_frames = MulDiv(fmt->nSamplesPerSec,
-            This->mmdev_period_rt, 10000000);
+    stream->mmdev_period_frames = MulDiv(fmt->nSamplesPerSec,
+            stream->mmdev_period_rt, 10000000);
 
     /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
-    This->alsa_bufsize_frames = This->mmdev_period_frames * 4;
+    stream->alsa_bufsize_frames = stream->mmdev_period_frames * 4;
     if(err < 0 || alsa_period_us < period / 10)
-        err = snd_pcm_hw_params_set_buffer_size_near(This->pcm_handle,
-                This->hw_params, &This->alsa_bufsize_frames);
+        err = snd_pcm_hw_params_set_buffer_size_near(stream->pcm_handle,
+                stream->hw_params, &stream->alsa_bufsize_frames);
     else{
         unsigned int periods = 4;
-        err = snd_pcm_hw_params_set_periods_near(This->pcm_handle, This->hw_params, &periods, NULL);
+        err = snd_pcm_hw_params_set_periods_near(stream->pcm_handle, stream->hw_params, &periods, NULL);
     }
     if(err < 0)
         WARN("Unable to set buffer size: %d (%s)\n", err, snd_strerror(err));
 
-    if((err = snd_pcm_hw_params(This->pcm_handle, This->hw_params)) < 0){
+    if((err = snd_pcm_hw_params(stream->pcm_handle, stream->hw_params)) < 0){
         WARN("Unable to set hw params: %d (%s)\n", err, snd_strerror(err));
         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
         goto exit;
     }
 
-    if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
-                    &This->alsa_period_frames, NULL)) < 0){
+    if((err = snd_pcm_hw_params_get_period_size(stream->hw_params,
+                    &stream->alsa_period_frames, NULL)) < 0){
         WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
         goto exit;
     }
 
-    if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
-                    &This->alsa_bufsize_frames)) < 0){
+    if((err = snd_pcm_hw_params_get_buffer_size(stream->hw_params,
+                    &stream->alsa_bufsize_frames)) < 0){
         WARN("Unable to get buffer size: %d (%s)\n", err, snd_strerror(err));
         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
         goto exit;
@@ -1087,34 +1073,34 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
         goto exit;
     }
 
-    if((err = snd_pcm_sw_params_current(This->pcm_handle, sw_params)) < 0){
+    if((err = snd_pcm_sw_params_current(stream->pcm_handle, sw_params)) < 0){
         WARN("Unable to get sw_params: %d (%s)\n", err, snd_strerror(err));
         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
         goto exit;
     }
 
-    if((err = snd_pcm_sw_params_set_start_threshold(This->pcm_handle,
+    if((err = snd_pcm_sw_params_set_start_threshold(stream->pcm_handle,
                     sw_params, 1)) < 0){
         WARN("Unable set start threshold to 1: %d (%s)\n", err, snd_strerror(err));
         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
         goto exit;
     }
 
-    if((err = snd_pcm_sw_params_set_stop_threshold(This->pcm_handle,
-                    sw_params, This->alsa_bufsize_frames)) < 0){
+    if((err = snd_pcm_sw_params_set_stop_threshold(stream->pcm_handle,
+                    sw_params, stream->alsa_bufsize_frames)) < 0){
         WARN("Unable set stop threshold to %lu: %d (%s)\n",
-                This->alsa_bufsize_frames, err, snd_strerror(err));
+                stream->alsa_bufsize_frames, err, snd_strerror(err));
         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
         goto exit;
     }
 
-    if((err = snd_pcm_sw_params(This->pcm_handle, sw_params)) < 0){
+    if((err = snd_pcm_sw_params(stream->pcm_handle, sw_params)) < 0){
         WARN("Unable to set sw params: %d (%s)\n", err, snd_strerror(err));
         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
         goto exit;
     }
 
-    if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
+    if((err = snd_pcm_prepare(stream->pcm_handle)) < 0){
         WARN("Unable to prepare device: %d (%s)\n", err, snd_strerror(err));
         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
         goto exit;
@@ -1125,42 +1111,42 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
      * or surprising rounding as seen with 22050x8x1 with Pulse:
      * ALSA period 220 vs.  221 frames in mmdevapi and
      *      buffer 883 vs. 2205 frames in mmdevapi! */
-    This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
+    stream->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
     if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
-        This->bufsize_frames -= This->bufsize_frames % This->mmdev_period_frames;
-    This->hidden_frames = This->alsa_period_frames + This->mmdev_period_frames +
+        stream->bufsize_frames -= stream->bufsize_frames % stream->mmdev_period_frames;
+    stream->hidden_frames = stream->alsa_period_frames + stream->mmdev_period_frames +
         MulDiv(fmt->nSamplesPerSec, EXTRA_SAFE_RT, 10000000);
     /* leave no less than about 1.33ms or 256 bytes of data after a rewind */
-    This->safe_rewind_frames = max(256 / fmt->nBlockAlign, MulDiv(133, fmt->nSamplesPerSec, 100000));
+    stream->safe_rewind_frames = max(256 / fmt->nBlockAlign, MulDiv(133, fmt->nSamplesPerSec, 100000));
 
     /* Check if the ALSA buffer is so small that it will run out before
      * the next MMDevAPI period tick occurs. Allow a little wiggle room
      * with 120% of the period time. */
-    if(This->alsa_bufsize_frames < 1.2 * This->mmdev_period_frames)
+    if(stream->alsa_bufsize_frames < 1.2 * stream->mmdev_period_frames)
         FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
-                This->alsa_bufsize_frames, This->mmdev_period_frames);
+                stream->alsa_bufsize_frames, stream->mmdev_period_frames);
 
-    This->fmt = clone_format(fmt);
-    if(!This->fmt){
+    stream->fmt = clone_format(fmt);
+    if(!stream->fmt){
         hr = E_OUTOFMEMORY;
         goto exit;
     }
 
-    This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
-            This->bufsize_frames * fmt->nBlockAlign);
-    if(!This->local_buffer){
+    stream->local_buffer = HeapAlloc(GetProcessHeap(), 0,
+            stream->bufsize_frames * fmt->nBlockAlign);
+    if(!stream->local_buffer){
         hr = E_OUTOFMEMORY;
         goto exit;
     }
-    silence_buffer(This, This->local_buffer, This->bufsize_frames);
+    silence_buffer(stream, stream->local_buffer, stream->bufsize_frames);
 
-    This->silence_buf = HeapAlloc(GetProcessHeap(), 0,
-            This->alsa_period_frames * This->fmt->nBlockAlign);
-    if(!This->silence_buf){
+    stream->silence_buf = HeapAlloc(GetProcessHeap(), 0,
+            stream->alsa_period_frames * stream->fmt->nBlockAlign);
+    if(!stream->silence_buf){
         hr = E_OUTOFMEMORY;
         goto exit;
     }
-    silence_buffer(This, This->silence_buf, This->alsa_period_frames);
+    silence_buffer(stream, stream->silence_buf, stream->alsa_period_frames);
 
     This->channel_count = fmt->nChannels;
     This->vols = HeapAlloc(GetProcessHeap(), 0, This->channel_count * sizeof(float));
@@ -1172,8 +1158,8 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
     for(i = 0; i < This->channel_count; ++i)
         This->vols[i] = 1.f;
 
-    This->share = mode;
-    This->flags = flags;
+    stream->share = mode;
+    stream->flags = flags;
 
     hr = get_audio_session(sessionguid, This->parent, This->channel_count,
             &This->session);
@@ -1184,18 +1170,18 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
 
     This->initted = TRUE;
 
-    TRACE("ALSA period: %lu frames\n", This->alsa_period_frames);
-    TRACE("ALSA buffer: %lu frames\n", This->alsa_bufsize_frames);
-    TRACE("MMDevice period: %u frames\n", This->mmdev_period_frames);
-    TRACE("MMDevice buffer: %u frames\n", This->bufsize_frames);
+    TRACE("ALSA period: %lu frames\n", stream->alsa_period_frames);
+    TRACE("ALSA buffer: %lu frames\n", stream->alsa_bufsize_frames);
+    TRACE("MMDevice period: %u frames\n", stream->mmdev_period_frames);
+    TRACE("MMDevice buffer: %u frames\n", stream->bufsize_frames);
 
 exit:
     HeapFree(GetProcessHeap(), 0, sw_params);
     if(FAILED(hr)){
-        HeapFree(GetProcessHeap(), 0, This->local_buffer);
-        This->local_buffer = NULL;
-        CoTaskMemFree(This->fmt);
-        This->fmt = NULL;
+        HeapFree(GetProcessHeap(), 0, stream->local_buffer);
+        stream->local_buffer = NULL;
+        CoTaskMemFree(stream->fmt);
+        stream->fmt = NULL;
         HeapFree(GetProcessHeap(), 0, This->vols);
         This->vols = NULL;
     }
@@ -1210,6 +1196,7 @@ static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient3 *iface,
         UINT32 *out)
 {
     ACImpl *This = impl_from_IAudioClient3(iface);
+    struct alsa_stream *stream = This->stream;
 
     TRACE("(%p)->(%p)\n", This, out);
 
@@ -1223,7 +1210,7 @@ static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient3 *iface,
         return AUDCLNT_E_NOT_INITIALIZED;
     }
 
-    *out = This->bufsize_frames;
+    *out = stream->bufsize_frames;
 
     LeaveCriticalSection(&This->lock);
 
@@ -1234,6 +1221,7 @@ static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient3 *iface,
         REFERENCE_TIME *latency)
 {
     ACImpl *This = impl_from_IAudioClient3(iface);
+    struct alsa_stream *stream = This->stream;
 
     TRACE("(%p)->(%p)\n", This, latency);
 
@@ -1253,10 +1241,10 @@ static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient3 *iface,
      * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
      * + alsa_period such that ALSA always has at least one period to play. */
     if(This->dataflow == eRender)
-        *latency = MulDiv(This->hidden_frames, 10000000, This->fmt->nSamplesPerSec);
+        *latency = MulDiv(stream->hidden_frames, 10000000, stream->fmt->nSamplesPerSec);
     else
-        *latency = MulDiv(This->alsa_period_frames, 10000000, This->fmt->nSamplesPerSec)
-                 + This->mmdev_period_rt;
+        *latency = MulDiv(stream->alsa_period_frames, 10000000, stream->fmt->nSamplesPerSec)
+                 + stream->mmdev_period_rt;
 
     LeaveCriticalSection(&This->lock);
 
@@ -1267,6 +1255,7 @@ static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient3 *iface,
         UINT32 *out)
 {
     ACImpl *This = impl_from_IAudioClient3(iface);
+    struct alsa_stream *stream = This->stream;
 
     TRACE("(%p)->(%p)\n", This, out);
 
@@ -1281,7 +1270,7 @@ static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient3 *iface,
     }
 
     /* padding is solely updated at callback time in shared mode */
-    *out = This->held_frames;
+    *out = stream->held_frames;
 
     LeaveCriticalSection(&This->lock);
 
@@ -1334,7 +1323,7 @@ static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface,
 
     EnterCriticalSection(&This->lock);
 
-    if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
+    if((err = snd_pcm_hw_params_any(This->stream->pcm_handle, This->stream->hw_params)) < 0){
         hr = AUDCLNT_E_DEVICE_INVALIDATED;
         goto exit;
     }
@@ -1346,7 +1335,7 @@ static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface,
         goto exit;
     }
 
-    snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
+    snd_pcm_hw_params_get_format_mask(This->stream->hw_params, formats);
     format = alsa_format(fmt);
     if (format == SND_PCM_FORMAT_UNKNOWN ||
         !snd_pcm_format_mask_test(formats, format)){
@@ -1360,13 +1349,13 @@ static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface,
         goto exit;
     }
 
-    if((err = snd_pcm_hw_params_get_rate_min(This->hw_params, &min, NULL)) < 0){
+    if((err = snd_pcm_hw_params_get_rate_min(This->stream->hw_params, &min, NULL)) < 0){
         hr = AUDCLNT_E_DEVICE_INVALIDATED;
         WARN("Unable to get min rate: %d (%s)\n", err, snd_strerror(err));
         goto exit;
     }
 
-    if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max, NULL)) < 0){
+    if((err = snd_pcm_hw_params_get_rate_max(This->stream->hw_params, &max, NULL)) < 0){
         hr = AUDCLNT_E_DEVICE_INVALIDATED;
         WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
         goto exit;
@@ -1377,13 +1366,13 @@ static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface,
         goto exit;
     }
 
-    if((err = snd_pcm_hw_params_get_channels_min(This->hw_params, &min)) < 0){
+    if((err = snd_pcm_hw_params_get_channels_min(This->stream->hw_params, &min)) < 0){
         hr = AUDCLNT_E_DEVICE_INVALIDATED;
         WARN("Unable to get min channels: %d (%s)\n", err, snd_strerror(err));
         goto exit;
     }
 
-    if((err = snd_pcm_hw_params_get_channels_max(This->hw_params, &max)) < 0){
+    if((err = snd_pcm_hw_params_get_channels_max(This->stream->hw_params, &max)) < 0){
         hr = AUDCLNT_E_DEVICE_INVALIDATED;
         WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
         goto exit;
@@ -1469,13 +1458,13 @@ static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
 
     EnterCriticalSection(&This->lock);
 
-    if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
+    if((err = snd_pcm_hw_params_any(This->stream->pcm_handle, This->stream->hw_params)) < 0){
         WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
         hr = AUDCLNT_E_DEVICE_INVALIDATED;
         goto exit;
     }
 
-    snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
+    snd_pcm_hw_params_get_format_mask(This->stream->hw_params, formats);
 
     fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
     if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
@@ -1499,7 +1488,7 @@ static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
         goto exit;
     }
 
-    if((err = snd_pcm_hw_params_get_channels_max(This->hw_params,
+    if((err = snd_pcm_hw_params_get_channels_max(This->stream->hw_params,
                     &max_channels)) < 0){
         WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
         hr = AUDCLNT_E_DEVICE_INVALIDATED;
@@ -1529,7 +1518,7 @@ static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
 
     fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
 
-    if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max_rate,
+    if((err = snd_pcm_hw_params_get_rate_max(This->stream->hw_params, &max_rate,
                     NULL)) < 0){
         WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
         hr = AUDCLNT_E_DEVICE_INVALIDATED;
@@ -1590,97 +1579,98 @@ static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient3 *iface,
     return S_OK;
 }
 
-static BYTE *remap_channels(ACImpl *This, BYTE *buf, snd_pcm_uframes_t frames)
+static BYTE *remap_channels(struct alsa_stream *stream, BYTE *buf, snd_pcm_uframes_t frames)
 {
     snd_pcm_uframes_t i;
     UINT c;
-    UINT bytes_per_sample = This->fmt->wBitsPerSample / 8;
+    UINT bytes_per_sample = stream->fmt->wBitsPerSample / 8;
 
-    if(!This->need_remapping)
+    if(!stream->need_remapping)
         return buf;
 
-    if(!This->remapping_buf){
-        This->remapping_buf = HeapAlloc(GetProcessHeap(), 0,
-                bytes_per_sample * This->alsa_channels * frames);
-        This->remapping_buf_frames = frames;
-    }else if(This->remapping_buf_frames < frames){
-        This->remapping_buf = HeapReAlloc(GetProcessHeap(), 0, This->remapping_buf,
-                bytes_per_sample * This->alsa_channels * frames);
-        This->remapping_buf_frames = frames;
+    if(!stream->remapping_buf){
+        stream->remapping_buf = HeapAlloc(GetProcessHeap(), 0,
+                bytes_per_sample * stream->alsa_channels * frames);
+        stream->remapping_buf_frames = frames;
+    }else if(stream->remapping_buf_frames < frames){
+        stream->remapping_buf = HeapReAlloc(GetProcessHeap(), 0, stream->remapping_buf,
+                bytes_per_sample * stream->alsa_channels * frames);
+        stream->remapping_buf_frames = frames;
     }
 
-    snd_pcm_format_set_silence(This->alsa_format, This->remapping_buf,
-            frames * This->alsa_channels);
+    snd_pcm_format_set_silence(stream->alsa_format, stream->remapping_buf,
+            frames * stream->alsa_channels);
 
-    switch(This->fmt->wBitsPerSample){
+    switch(stream->fmt->wBitsPerSample){
     case 8: {
             UINT8 *tgt_buf, *src_buf;
-            tgt_buf = This->remapping_buf;
+            tgt_buf = stream->remapping_buf;
             src_buf = buf;
             for(i = 0; i < frames; ++i){
-                for(c = 0; c < This->fmt->nChannels; ++c)
-                    tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
-                tgt_buf += This->alsa_channels;
-                src_buf += This->fmt->nChannels;
+                for(c = 0; c < stream->fmt->nChannels; ++c)
+                    tgt_buf[stream->alsa_channel_map[c]] = src_buf[c];
+                tgt_buf += stream->alsa_channels;
+                src_buf += stream->fmt->nChannels;
             }
             break;
         }
     case 16: {
             UINT16 *tgt_buf, *src_buf;
-            tgt_buf = (UINT16*)This->remapping_buf;
+            tgt_buf = (UINT16*)stream->remapping_buf;
             src_buf = (UINT16*)buf;
             for(i = 0; i < frames; ++i){
-                for(c = 0; c < This->fmt->nChannels; ++c)
-                    tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
-                tgt_buf += This->alsa_channels;
-                src_buf += This->fmt->nChannels;
+                for(c = 0; c < stream->fmt->nChannels; ++c)
+                    tgt_buf[stream->alsa_channel_map[c]] = src_buf[c];
+                tgt_buf += stream->alsa_channels;
+                src_buf += stream->fmt->nChannels;
             }
         }
         break;
     case 32: {
             UINT32 *tgt_buf, *src_buf;
-            tgt_buf = (UINT32*)This->remapping_buf;
+            tgt_buf = (UINT32*)stream->remapping_buf;
             src_buf = (UINT32*)buf;
             for(i = 0; i < frames; ++i){
-                for(c = 0; c < This->fmt->nChannels; ++c)
-                    tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
-                tgt_buf += This->alsa_channels;
-                src_buf += This->fmt->nChannels;
+                for(c = 0; c < stream->fmt->nChannels; ++c)
+                    tgt_buf[stream->alsa_channel_map[c]] = src_buf[c];
+                tgt_buf += stream->alsa_channels;
+                src_buf += stream->fmt->nChannels;
             }
         }
         break;
     default: {
             BYTE *tgt_buf, *src_buf;
-            tgt_buf = This->remapping_buf;
+            tgt_buf = stream->remapping_buf;
             src_buf = buf;
             for(i = 0; i < frames; ++i){
-                for(c = 0; c < This->fmt->nChannels; ++c)
-                    memcpy(&tgt_buf[This->alsa_channel_map[c] * bytes_per_sample],
+                for(c = 0; c < stream->fmt->nChannels; ++c)
+                    memcpy(&tgt_buf[stream->alsa_channel_map[c] * bytes_per_sample],
                             &src_buf[c * bytes_per_sample], bytes_per_sample);
-                tgt_buf += This->alsa_channels * bytes_per_sample;
-                src_buf += This->fmt->nChannels * bytes_per_sample;
+                tgt_buf += stream->alsa_channels * bytes_per_sample;
+                src_buf += stream->fmt->nChannels * bytes_per_sample;
             }
         }
         break;
     }
 
-    return This->remapping_buf;
+    return stream->remapping_buf;
 }
 
 static void adjust_buffer_volume(const ACImpl *This, BYTE *buf, snd_pcm_uframes_t frames, BOOL mute)
 {
-    float vol[ARRAY_SIZE(This->alsa_channel_map)];
+    struct alsa_stream *stream = This->stream;
+    float vol[ARRAY_SIZE(stream->alsa_channel_map)];
     BOOL adjust = FALSE;
     UINT32 i, channels;
     BYTE *end;
 
-    if (This->vol_adjusted_frames >= frames)
+    if (stream->vol_adjusted_frames >= frames)
         return;
-    channels = This->fmt->nChannels;
+    channels = stream->fmt->nChannels;
 
     if (mute)
     {
-        int err = snd_pcm_format_set_silence(This->alsa_format, buf, frames * channels);
+        int err = snd_pcm_format_set_silence(stream->alsa_format, buf, frames * channels);
         if (err < 0)
             WARN("Setting buffer to silence failed: %d (%s)\n", err, snd_strerror(err));
         return;
@@ -1698,10 +1688,10 @@ static void adjust_buffer_volume(const ACImpl *This, BYTE *buf, snd_pcm_uframes_
     if (!adjust) return;
 
     /* Skip the frames we've already adjusted before */
-    end = buf + frames * This->fmt->nBlockAlign;
-    buf += This->vol_adjusted_frames * This->fmt->nBlockAlign;
+    end = buf + frames * stream->fmt->nBlockAlign;
+    buf += stream->vol_adjusted_frames * stream->fmt->nBlockAlign;
 
-    switch (This->alsa_format)
+    switch (stream->alsa_format)
     {
 #ifndef WORDS_BIGENDIAN
 #define PROCESS_BUFFER(type) do         \
@@ -1735,7 +1725,7 @@ static void adjust_buffer_volume(const ACImpl *This, BYTE *buf, snd_pcm_uframes_
         BYTE *p;
 
         /* After we adjust the volume, we need to mask out low bits */
-        if (This->alsa_format == SND_PCM_FORMAT_S20_3LE)
+        if (stream->alsa_format == SND_PCM_FORMAT_S20_3LE)
             mask = ~0x0fff;
 
         i = 0;
@@ -1781,7 +1771,7 @@ static void adjust_buffer_volume(const ACImpl *This, BYTE *buf, snd_pcm_uframes_
         break;
     }
     default:
-        TRACE("Unhandled format %i, not adjusting volume.\n", This->alsa_format);
+        TRACE("Unhandled format %i, not adjusting volume.\n", stream->alsa_format);
         break;
     }
 }
@@ -1789,17 +1779,18 @@ static void adjust_buffer_volume(const ACImpl *This, BYTE *buf, snd_pcm_uframes_
 static snd_pcm_sframes_t alsa_write_best_effort(ACImpl *This, BYTE *buf,
         snd_pcm_uframes_t frames, BOOL mute)
 {
+    struct alsa_stream *stream = This->stream;
     snd_pcm_sframes_t written;
 
     adjust_buffer_volume(This, buf, frames, mute);
 
     /* Mark the frames we've already adjusted */
-    if (This->vol_adjusted_frames < frames)
-        This->vol_adjusted_frames = frames;
+    if (stream->vol_adjusted_frames < frames)
+        stream->vol_adjusted_frames = frames;
 
-    buf = remap_channels(This, buf, frames);
+    buf = remap_channels(stream, buf, frames);
 
-    written = snd_pcm_writei(This->pcm_handle, buf, frames);
+    written = snd_pcm_writei(stream->pcm_handle, buf, frames);
     if(written < 0){
         int ret;
 
@@ -1810,17 +1801,17 @@ static snd_pcm_sframes_t alsa_write_best_effort(ACImpl *This, BYTE *buf,
         WARN("writei failed, recovering: %ld (%s)\n", written,
                 snd_strerror(written));
 
-        ret = snd_pcm_recover(This->pcm_handle, written, 0);
+        ret = snd_pcm_recover(stream->pcm_handle, written, 0);
         if(ret < 0){
             WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
             return ret;
         }
 
-        written = snd_pcm_writei(This->pcm_handle, buf, frames);
+        written = snd_pcm_writei(stream->pcm_handle, buf, frames);
     }
 
     if (written > 0)
-        This->vol_adjusted_frames -= written;
+        stream->vol_adjusted_frames -= written;
     return written;
 }
 
@@ -1828,6 +1819,7 @@ static snd_pcm_sframes_t alsa_write_buffer_wrap(ACImpl *This, BYTE *buf,
         snd_pcm_uframes_t buflen, snd_pcm_uframes_t offs,
         snd_pcm_uframes_t to_write)
 {
+    struct alsa_stream *stream = This->stream;
     snd_pcm_sframes_t ret = 0;
 
     while(to_write){
@@ -1839,7 +1831,7 @@ static snd_pcm_sframes_t alsa_write_buffer_wrap(ACImpl *This, BYTE *buf,
         else
             chunk = to_write;
 
-        tmp = alsa_write_best_effort(This, buf + offs * This->fmt->nBlockAlign, chunk, This->session->mute);
+        tmp = alsa_write_best_effort(This, buf + offs * stream->fmt->nBlockAlign, chunk, This->session->mute);
         if(tmp < 0)
             return ret;
         if(!tmp)
@@ -1863,13 +1855,14 @@ static UINT buf_ptr_diff(UINT left, UINT right, UINT bufsize)
 
 static UINT data_not_in_alsa(ACImpl *This)
 {
+    struct alsa_stream *stream = This->stream;
     UINT32 diff;
 
-    diff = buf_ptr_diff(This->lcl_offs_frames, This->wri_offs_frames, This->bufsize_frames);
+    diff = buf_ptr_diff(stream->lcl_offs_frames, stream->wri_offs_frames, stream->bufsize_frames);
     if(diff)
         return diff;
 
-    return This->held_frames - This->data_in_alsa_frames;
+    return stream->held_frames - stream->data_in_alsa_frames;
 }
 /* Here's the buffer setup:
  *
@@ -1891,25 +1884,26 @@ static UINT data_not_in_alsa(ACImpl *This)
  */
 static void alsa_write_data(ACImpl *This)
 {
+    struct alsa_stream *stream = This->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(This->pcm_handle);
+    avail = snd_pcm_avail_update(stream->pcm_handle);
 
-    if(snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_XRUN){
+    if(snd_pcm_state(stream->pcm_handle) == SND_PCM_STATE_XRUN){
         TRACE("XRun state, recovering\n");
 
-        avail = This->alsa_bufsize_frames;
+        avail = stream->alsa_bufsize_frames;
 
-        if((err = snd_pcm_recover(This->pcm_handle, -EPIPE, 1)) < 0)
+        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(This->pcm_handle)) < 0)
+        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(This->pcm_handle)) < 0)
+        if((err = snd_pcm_prepare(stream->pcm_handle)) < 0)
             WARN("snd_pcm_prepare failed: %d (%s)\n", err, snd_strerror(err));
     }
 
@@ -1917,61 +1911,63 @@ static void alsa_write_data(ACImpl *This)
 
     /* Add a lead-in when starting with too few frames to ensure
      * continuous rendering.  Additional benefit: Force ALSA to start. */
-    if(This->data_in_alsa_frames == 0 && This->held_frames < This->alsa_period_frames)
+    if(stream->data_in_alsa_frames == 0 && stream->held_frames < stream->alsa_period_frames)
     {
-        alsa_write_best_effort(This, This->silence_buf, This->alsa_period_frames - This->held_frames, FALSE);
-        This->vol_adjusted_frames = 0;
+        alsa_write_best_effort(This, stream->silence_buf,
+                               stream->alsa_period_frames - stream->held_frames, FALSE);
+        stream->vol_adjusted_frames = 0;
     }
 
-    if(This->started)
+    if(stream->started)
         max_copy_frames = data_not_in_alsa(This);
     else
         max_copy_frames = 0;
 
-    data_frames_played = min(This->data_in_alsa_frames, avail);
-    This->data_in_alsa_frames -= data_frames_played;
+    data_frames_played = min(stream->data_in_alsa_frames, avail);
+    stream->data_in_alsa_frames -= data_frames_played;
 
-    if(This->held_frames > data_frames_played){
-        if(This->started)
-            This->held_frames -= data_frames_played;
+    if(stream->held_frames > data_frames_played){
+        if(stream->started)
+            stream->held_frames -= data_frames_played;
     }else
-        This->held_frames = 0;
+        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(This, This->local_buffer,
-                This->bufsize_frames, This->lcl_offs_frames, to_write);
+        written = alsa_write_buffer_wrap(This, stream->local_buffer,
+                stream->bufsize_frames, stream->lcl_offs_frames, to_write);
         if(written <= 0)
             break;
 
         avail -= written;
-        This->lcl_offs_frames += written;
-        This->lcl_offs_frames %= This->bufsize_frames;
-        This->data_in_alsa_frames += written;
+        stream->lcl_offs_frames += written;
+        stream->lcl_offs_frames %= stream->bufsize_frames;
+        stream->data_in_alsa_frames += written;
         max_copy_frames -= written;
     }
 
-    if(This->event)
-        SetEvent(This->event);
+    if(stream->event)
+        SetEvent(stream->event);
 }
 
 static void alsa_read_data(ACImpl *This)
 {
+    struct alsa_stream *stream = This->stream;
     snd_pcm_sframes_t nread;
-    UINT32 pos = This->wri_offs_frames, limit = This->held_frames;
+    UINT32 pos = stream->wri_offs_frames, limit = stream->held_frames;
 
-    if(!This->started)
+    if(!stream->started)
         goto exit;
 
     /* FIXME: Detect overrun and signal DATA_DISCONTINUITY
      * How to count overrun frames and report them as position increase? */
-    limit = This->bufsize_frames - max(limit, pos);
+    limit = stream->bufsize_frames - max(limit, pos);
 
-    nread = snd_pcm_readi(This->pcm_handle,
-            This->local_buffer + pos * This->fmt->nBlockAlign, limit);
+    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;
@@ -1981,14 +1977,14 @@ static void alsa_read_data(ACImpl *This)
 
         WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
 
-        ret = snd_pcm_recover(This->pcm_handle, nread, 0);
+        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(This->pcm_handle,
-                This->local_buffer + pos * This->fmt->nBlockAlign, limit);
+        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;
@@ -1997,29 +1993,30 @@ static void alsa_read_data(ACImpl *This)
 
     if(This->session->mute){
         int err;
-        if((err = snd_pcm_format_set_silence(This->alsa_format,
-                        This->local_buffer + pos * This->fmt->nBlockAlign,
+        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));
     }
 
-    This->wri_offs_frames += nread;
-    This->wri_offs_frames %= This->bufsize_frames;
-    This->held_frames += nread;
+    stream->wri_offs_frames += nread;
+    stream->wri_offs_frames %= stream->bufsize_frames;
+    stream->held_frames += nread;
 
 exit:
-    if(This->event)
-        SetEvent(This->event);
+    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;
 
     EnterCriticalSection(&This->lock);
 
-    QueryPerformanceCounter(&This->last_period_time);
+    QueryPerformanceCounter(&stream->last_period_time);
 
     if(This->dataflow == eRender)
         alsa_write_data(This);
@@ -2029,17 +2026,18 @@ static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer)
     LeaveCriticalSection(&This->lock);
 }
 
-static snd_pcm_uframes_t interp_elapsed_frames(ACImpl *This)
+static snd_pcm_uframes_t interp_elapsed_frames(struct alsa_stream *stream)
 {
     LARGE_INTEGER time_freq, current_time, time_diff;
     QueryPerformanceFrequency(&time_freq);
     QueryPerformanceCounter(&current_time);
-    time_diff.QuadPart = current_time.QuadPart - This->last_period_time.QuadPart;
-    return MulDiv(time_diff.QuadPart, This->fmt->nSamplesPerSec, time_freq.QuadPart);
+    time_diff.QuadPart = current_time.QuadPart - stream->last_period_time.QuadPart;
+    return MulDiv(time_diff.QuadPart, stream->fmt->nSamplesPerSec, time_freq.QuadPart);
 }
 
 static int alsa_rewind_best_effort(ACImpl *This)
 {
+    struct alsa_stream *stream = This->stream;
     snd_pcm_uframes_t len, leave;
 
     /* we can't use snd_pcm_rewindable, some PCM devices crash. so follow
@@ -2047,25 +2045,25 @@ static int alsa_rewind_best_effort(ACImpl *This)
      * buffer, minus 1.33ms for safety. */
 
     /* amount of data to leave in ALSA buffer */
-    leave = interp_elapsed_frames(This) + This->safe_rewind_frames;
+    leave = interp_elapsed_frames(stream) + stream->safe_rewind_frames;
 
-    if(This->held_frames < leave)
-        This->held_frames = 0;
+    if(stream->held_frames < leave)
+        stream->held_frames = 0;
     else
-        This->held_frames -= leave;
+        stream->held_frames -= leave;
 
-    if(This->data_in_alsa_frames < leave)
+    if(stream->data_in_alsa_frames < leave)
         len = 0;
     else
-        len = This->data_in_alsa_frames - leave;
+        len = stream->data_in_alsa_frames - leave;
 
-    TRACE("rewinding %lu frames, now held %u\n", len, This->held_frames);
+    TRACE("rewinding %lu frames, now held %u\n", len, stream->held_frames);
 
     if(len)
         /* snd_pcm_rewind return value is often broken, assume it succeeded */
-        snd_pcm_rewind(This->pcm_handle, len);
+        snd_pcm_rewind(stream->pcm_handle, len);
 
-    This->data_in_alsa_frames = 0;
+    stream->data_in_alsa_frames = 0;
 
     return len;
 }
@@ -2073,6 +2071,7 @@ static int alsa_rewind_best_effort(ACImpl *This)
 static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface)
 {
     ACImpl *This = impl_from_IAudioClient3(iface);
+    struct alsa_stream *stream = This->stream;
 
     TRACE("(%p)\n", This);
 
@@ -2083,55 +2082,55 @@ static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface)
         return AUDCLNT_E_NOT_INITIALIZED;
     }
 
-    if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
+    if((stream->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !stream->event){
         LeaveCriticalSection(&This->lock);
         return AUDCLNT_E_EVENTHANDLE_NOT_SET;
     }
 
-    if(This->started){
+    if(stream->started){
         LeaveCriticalSection(&This->lock);
         return AUDCLNT_E_NOT_STOPPED;
     }
 
     if(This->dataflow == eCapture){
         /* dump any data that might be leftover in the ALSA capture buffer */
-        snd_pcm_readi(This->pcm_handle, This->local_buffer,
-                This->bufsize_frames);
+        snd_pcm_readi(stream->pcm_handle, stream->local_buffer,
+                stream->bufsize_frames);
     }else{
         snd_pcm_sframes_t avail, written;
         snd_pcm_uframes_t offs;
 
-        avail = snd_pcm_avail_update(This->pcm_handle);
-        avail = min(avail, This->held_frames);
+        avail = snd_pcm_avail_update(stream->pcm_handle);
+        avail = min(avail, stream->held_frames);
 
-        if(This->wri_offs_frames < This->held_frames)
-            offs = This->bufsize_frames - This->held_frames + This->wri_offs_frames;
+        if(stream->wri_offs_frames < stream->held_frames)
+            offs = stream->bufsize_frames - stream->held_frames + stream->wri_offs_frames;
         else
-            offs = This->wri_offs_frames - This->held_frames;
+            offs = stream->wri_offs_frames - stream->held_frames;
 
         /* fill it with data */
-        written = alsa_write_buffer_wrap(This, This->local_buffer,
-                This->bufsize_frames, offs, avail);
+        written = alsa_write_buffer_wrap(This, stream->local_buffer,
+                stream->bufsize_frames, offs, avail);
 
         if(written > 0){
-            This->lcl_offs_frames = (offs + written) % This->bufsize_frames;
-            This->data_in_alsa_frames = written;
+            stream->lcl_offs_frames = (offs + written) % stream->bufsize_frames;
+            stream->data_in_alsa_frames = written;
         }else{
-            This->lcl_offs_frames = offs;
-            This->data_in_alsa_frames = 0;
+            stream->lcl_offs_frames = offs;
+            stream->data_in_alsa_frames = 0;
         }
     }
 
     if(!This->timer){
         if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
-                This, 0, This->mmdev_period_rt / 10000, WT_EXECUTEINTIMERTHREAD)){
+                This, 0, stream->mmdev_period_rt / 10000, WT_EXECUTEINTIMERTHREAD)){
             LeaveCriticalSection(&This->lock);
             WARN("Unable to create timer: %u\n", GetLastError());
             return E_OUTOFMEMORY;
         }
     }
 
-    This->started = TRUE;
+    stream->started = TRUE;
 
     LeaveCriticalSection(&This->lock);
 
@@ -2141,6 +2140,7 @@ static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface)
 static HRESULT WINAPI AudioClient_Stop(IAudioClient3 *iface)
 {
     ACImpl *This = impl_from_IAudioClient3(iface);
+    struct alsa_stream *stream = This->stream;
 
     TRACE("(%p)\n", This);
 
@@ -2151,7 +2151,7 @@ static HRESULT WINAPI AudioClient_Stop(IAudioClient3 *iface)
         return AUDCLNT_E_NOT_INITIALIZED;
     }
 
-    if(!This->started){
+    if(!stream->started){
         LeaveCriticalSection(&This->lock);
         return S_FALSE;
     }
@@ -2159,7 +2159,7 @@ static HRESULT WINAPI AudioClient_Stop(IAudioClient3 *iface)
     if(This->dataflow == eRender)
         alsa_rewind_best_effort(This);
 
-    This->started = FALSE;
+    stream->started = FALSE;
 
     LeaveCriticalSection(&This->lock);
 
@@ -2169,6 +2169,7 @@ static HRESULT WINAPI AudioClient_Stop(IAudioClient3 *iface)
 static HRESULT WINAPI AudioClient_Reset(IAudioClient3 *iface)
 {
     ACImpl *This = impl_from_IAudioClient3(iface);
+    struct alsa_stream *stream = This->stream;
 
     TRACE("(%p)\n", This);
 
@@ -2179,34 +2180,34 @@ static HRESULT WINAPI AudioClient_Reset(IAudioClient3 *iface)
         return AUDCLNT_E_NOT_INITIALIZED;
     }
 
-    if(This->started){
+    if(stream->started){
         LeaveCriticalSection(&This->lock);
         return AUDCLNT_E_NOT_STOPPED;
     }
 
-    if(This->getbuf_last){
+    if(stream->getbuf_last){
         LeaveCriticalSection(&This->lock);
         return AUDCLNT_E_BUFFER_OPERATION_PENDING;
     }
 
-    if(snd_pcm_drop(This->pcm_handle) < 0)
+    if(snd_pcm_drop(stream->pcm_handle) < 0)
         WARN("snd_pcm_drop failed\n");
 
-    if(snd_pcm_reset(This->pcm_handle) < 0)
+    if(snd_pcm_reset(stream->pcm_handle) < 0)
         WARN("snd_pcm_reset failed\n");
 
-    if(snd_pcm_prepare(This->pcm_handle) < 0)
+    if(snd_pcm_prepare(stream->pcm_handle) < 0)
         WARN("snd_pcm_prepare failed\n");
 
     if(This->dataflow == eRender){
-        This->written_frames = 0;
-        This->last_pos_frames = 0;
+        stream->written_frames = 0;
+        stream->last_pos_frames = 0;
     }else{
-        This->written_frames += This->held_frames;
+        stream->written_frames += stream->held_frames;
     }
-    This->held_frames = 0;
-    This->lcl_offs_frames = 0;
-    This->wri_offs_frames = 0;
+    stream->held_frames = 0;
+    stream->lcl_offs_frames = 0;
+    stream->wri_offs_frames = 0;
 
     LeaveCriticalSection(&This->lock);
 
@@ -2217,6 +2218,7 @@ static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient3 *iface,
         HANDLE event)
 {
     ACImpl *This = impl_from_IAudioClient3(iface);
+    struct alsa_stream *stream = This->stream;
 
     TRACE("(%p)->(%p)\n", This, event);
 
@@ -2230,18 +2232,18 @@ static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient3 *iface,
         return AUDCLNT_E_NOT_INITIALIZED;
     }
 
-    if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
+    if(!(stream->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
         LeaveCriticalSection(&This->lock);
         return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
     }
 
-    if (This->event){
+    if (stream->event){
         LeaveCriticalSection(&This->lock);
         FIXME("called twice\n");
         return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
     }
 
-    This->event = event;
+    stream->event = event;
 
     LeaveCriticalSection(&This->lock);
 
@@ -2489,6 +2491,7 @@ static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
         UINT32 frames, BYTE **data)
 {
     ACImpl *This = impl_from_IAudioRenderClient(iface);
+    struct alsa_stream *stream = This->stream;
     UINT32 write_pos;
 
     TRACE("(%p)->(%u, %p)\n", This, frames, data);
@@ -2499,7 +2502,7 @@ static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
 
     EnterCriticalSection(&This->lock);
 
-    if(This->getbuf_last){
+    if(stream->getbuf_last){
         LeaveCriticalSection(&This->lock);
         return AUDCLNT_E_OUT_OF_ORDER;
     }
@@ -2510,50 +2513,50 @@ static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
     }
 
     /* held_frames == GetCurrentPadding_nolock(); */
-    if(This->held_frames + frames > This->bufsize_frames){
+    if(stream->held_frames + frames > stream->bufsize_frames){
         LeaveCriticalSection(&This->lock);
         return AUDCLNT_E_BUFFER_TOO_LARGE;
     }
 
-    write_pos = This->wri_offs_frames;
-    if(write_pos + frames > This->bufsize_frames){
-        if(This->tmp_buffer_frames < frames){
-            HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
-            This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
-                    frames * This->fmt->nBlockAlign);
-            if(!This->tmp_buffer){
+    write_pos = stream->wri_offs_frames;
+    if(write_pos + frames > stream->bufsize_frames){
+        if(stream->tmp_buffer_frames < frames){
+            HeapFree(GetProcessHeap(), 0, stream->tmp_buffer);
+            stream->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
+                    frames * stream->fmt->nBlockAlign);
+            if(!stream->tmp_buffer){
                 LeaveCriticalSection(&This->lock);
                 return E_OUTOFMEMORY;
             }
-            This->tmp_buffer_frames = frames;
+            stream->tmp_buffer_frames = frames;
         }
-        *data = This->tmp_buffer;
-        This->getbuf_last = -frames;
+        *data = stream->tmp_buffer;
+        stream->getbuf_last = -frames;
     }else{
-        *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
-        This->getbuf_last = frames;
+        *data = stream->local_buffer + write_pos * stream->fmt->nBlockAlign;
+        stream->getbuf_last = frames;
     }
 
-    silence_buffer(This, *data, frames);
+    silence_buffer(stream, *data, frames);
 
     LeaveCriticalSection(&This->lock);
 
     return S_OK;
 }
 
-static void alsa_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
+static void alsa_wrap_buffer(struct alsa_stream *stream, BYTE *buffer, UINT32 written_frames)
 {
-    snd_pcm_uframes_t write_offs_frames = This->wri_offs_frames;
-    UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
-    snd_pcm_uframes_t chunk_frames = This->bufsize_frames - write_offs_frames;
-    UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
-    UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
+    snd_pcm_uframes_t write_offs_frames = stream->wri_offs_frames;
+    UINT32 write_offs_bytes = write_offs_frames * stream->fmt->nBlockAlign;
+    snd_pcm_uframes_t chunk_frames = stream->bufsize_frames - write_offs_frames;
+    UINT32 chunk_bytes = chunk_frames * stream->fmt->nBlockAlign;
+    UINT32 written_bytes = written_frames * stream->fmt->nBlockAlign;
 
     if(written_bytes <= chunk_bytes){
-        memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
+        memcpy(stream->local_buffer + write_offs_bytes, buffer, written_bytes);
     }else{
-        memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
-        memcpy(This->local_buffer, buffer + chunk_bytes,
+        memcpy(stream->local_buffer + write_offs_bytes, buffer, chunk_bytes);
+        memcpy(stream->local_buffer, buffer + chunk_bytes,
                 written_bytes - chunk_bytes);
     }
 }
@@ -2562,6 +2565,7 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
         IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
 {
     ACImpl *This = impl_from_IAudioRenderClient(iface);
+    struct alsa_stream *stream = This->stream;
     BYTE *buffer;
 
     TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
@@ -2569,37 +2573,37 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
     EnterCriticalSection(&This->lock);
 
     if(!written_frames){
-        This->getbuf_last = 0;
+        stream->getbuf_last = 0;
         LeaveCriticalSection(&This->lock);
         return S_OK;
     }
 
-    if(!This->getbuf_last){
+    if(!stream->getbuf_last){
         LeaveCriticalSection(&This->lock);
         return AUDCLNT_E_OUT_OF_ORDER;
     }
 
-    if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
+    if(written_frames > (stream->getbuf_last >= 0 ? stream->getbuf_last : -stream->getbuf_last)){
         LeaveCriticalSection(&This->lock);
         return AUDCLNT_E_INVALID_SIZE;
     }
 
-    if(This->getbuf_last >= 0)
-        buffer = This->local_buffer + This->wri_offs_frames * This->fmt->nBlockAlign;
+    if(stream->getbuf_last >= 0)
+        buffer = stream->local_buffer + stream->wri_offs_frames * stream->fmt->nBlockAlign;
     else
-        buffer = This->tmp_buffer;
+        buffer = stream->tmp_buffer;
 
     if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
-        silence_buffer(This, buffer, written_frames);
+        silence_buffer(stream, buffer, written_frames);
 
-    if(This->getbuf_last < 0)
-        alsa_wrap_buffer(This, buffer, written_frames);
+    if(stream->getbuf_last < 0)
+        alsa_wrap_buffer(stream, buffer, written_frames);
 
-    This->wri_offs_frames += written_frames;
-    This->wri_offs_frames %= This->bufsize_frames;
-    This->held_frames += written_frames;
-    This->written_frames += written_frames;
-    This->getbuf_last = 0;
+    stream->wri_offs_frames += written_frames;
+    stream->wri_offs_frames %= stream->bufsize_frames;
+    stream->held_frames += written_frames;
+    stream->written_frames += written_frames;
+    stream->getbuf_last = 0;
 
     LeaveCriticalSection(&This->lock);
 
@@ -2656,6 +2660,7 @@ static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
         UINT64 *qpcpos)
 {
     ACImpl *This = impl_from_IAudioCaptureClient(iface);
+    struct alsa_stream *stream = This->stream;
 
     TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
             devpos, qpcpos);
@@ -2670,49 +2675,49 @@ static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
 
     EnterCriticalSection(&This->lock);
 
-    if(This->getbuf_last){
+    if(stream->getbuf_last){
         LeaveCriticalSection(&This->lock);
         return AUDCLNT_E_OUT_OF_ORDER;
     }
 
     /* hr = GetNextPacketSize(iface, frames); */
-    if(This->held_frames < This->mmdev_period_frames){
+    if(stream->held_frames < stream->mmdev_period_frames){
         *frames = 0;
         LeaveCriticalSection(&This->lock);
         return AUDCLNT_S_BUFFER_EMPTY;
     }
-    *frames = This->mmdev_period_frames;
+    *frames = stream->mmdev_period_frames;
 
-    if(This->lcl_offs_frames + *frames > This->bufsize_frames){
+    if(stream->lcl_offs_frames + *frames > stream->bufsize_frames){
         UINT32 chunk_bytes, offs_bytes, frames_bytes;
-        if(This->tmp_buffer_frames < *frames){
-            HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
-            This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
-                    *frames * This->fmt->nBlockAlign);
-            if(!This->tmp_buffer){
+        if(stream->tmp_buffer_frames < *frames){
+            HeapFree(GetProcessHeap(), 0, stream->tmp_buffer);
+            stream->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
+                    *frames * stream->fmt->nBlockAlign);
+            if(!stream->tmp_buffer){
                 LeaveCriticalSection(&This->lock);
                 return E_OUTOFMEMORY;
             }
-            This->tmp_buffer_frames = *frames;
+            stream->tmp_buffer_frames = *frames;
         }
 
-        *data = This->tmp_buffer;
-        chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
-            This->fmt->nBlockAlign;
-        offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
-        frames_bytes = *frames * This->fmt->nBlockAlign;
-        memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
-        memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
+        *data = stream->tmp_buffer;
+        chunk_bytes = (stream->bufsize_frames - stream->lcl_offs_frames) *
+            stream->fmt->nBlockAlign;
+        offs_bytes = stream->lcl_offs_frames * stream->fmt->nBlockAlign;
+        frames_bytes = *frames * stream->fmt->nBlockAlign;
+        memcpy(stream->tmp_buffer, stream->local_buffer + offs_bytes, chunk_bytes);
+        memcpy(stream->tmp_buffer + chunk_bytes, stream->local_buffer,
                 frames_bytes - chunk_bytes);
     }else
-        *data = This->local_buffer +
-            This->lcl_offs_frames * This->fmt->nBlockAlign;
+        *data = stream->local_buffer +
+            stream->lcl_offs_frames * stream->fmt->nBlockAlign;
 
-    This->getbuf_last = *frames;
+    stream->getbuf_last = *frames;
     *flags = 0;
 
     if(devpos)
-      *devpos = This->written_frames;
+      *devpos = stream->written_frames;
     if(qpcpos){ /* fixme: qpc of recording time */
         LARGE_INTEGER stamp, freq;
         QueryPerformanceCounter(&stamp);
@@ -2729,32 +2734,33 @@ static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
         IAudioCaptureClient *iface, UINT32 done)
 {
     ACImpl *This = impl_from_IAudioCaptureClient(iface);
+    struct alsa_stream *stream = This->stream;
 
     TRACE("(%p)->(%u)\n", This, done);
 
     EnterCriticalSection(&This->lock);
 
     if(!done){
-        This->getbuf_last = 0;
+        stream->getbuf_last = 0;
         LeaveCriticalSection(&This->lock);
         return S_OK;
     }
 
-    if(!This->getbuf_last){
+    if(!stream->getbuf_last){
         LeaveCriticalSection(&This->lock);
         return AUDCLNT_E_OUT_OF_ORDER;
     }
 
-    if(This->getbuf_last != done){
+    if(stream->getbuf_last != done){
         LeaveCriticalSection(&This->lock);
         return AUDCLNT_E_INVALID_SIZE;
     }
 
-    This->written_frames += done;
-    This->held_frames -= done;
-    This->lcl_offs_frames += done;
-    This->lcl_offs_frames %= This->bufsize_frames;
-    This->getbuf_last = 0;
+    stream->written_frames += done;
+    stream->held_frames -= done;
+    stream->lcl_offs_frames += done;
+    stream->lcl_offs_frames %= stream->bufsize_frames;
+    stream->getbuf_last = 0;
 
     LeaveCriticalSection(&This->lock);
 
@@ -2765,6 +2771,7 @@ static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
         IAudioCaptureClient *iface, UINT32 *frames)
 {
     ACImpl *This = impl_from_IAudioCaptureClient(iface);
+    struct alsa_stream *stream = This->stream;
 
     TRACE("(%p)->(%p)\n", This, frames);
 
@@ -2773,7 +2780,7 @@ static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
 
     EnterCriticalSection(&This->lock);
 
-    *frames = This->held_frames < This->mmdev_period_frames ? 0 : This->mmdev_period_frames;
+    *frames = stream->held_frames < stream->mmdev_period_frames ? 0 : stream->mmdev_period_frames;
 
     LeaveCriticalSection(&This->lock);
 
@@ -2829,13 +2836,14 @@ static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
 {
     ACImpl *This = impl_from_IAudioClock(iface);
+    struct alsa_stream *stream = This->stream;
 
     TRACE("(%p)->(%p)\n", This, freq);
 
-    if(This->share == AUDCLNT_SHAREMODE_SHARED)
-        *freq = (UINT64)This->fmt->nSamplesPerSec * This->fmt->nBlockAlign;
+    if(stream->share == AUDCLNT_SHAREMODE_SHARED)
+        *freq = (UINT64)stream->fmt->nSamplesPerSec * stream->fmt->nBlockAlign;
     else
-        *freq = This->fmt->nSamplesPerSec;
+        *freq = stream->fmt->nSamplesPerSec;
 
     return S_OK;
 }
@@ -2844,6 +2852,7 @@ static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
         UINT64 *qpctime)
 {
     ACImpl *This = impl_from_IAudioClock(iface);
+    struct alsa_stream *stream = This->stream;
     UINT64 position;
     snd_pcm_state_t alsa_state;
 
@@ -2855,38 +2864,38 @@ static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
     EnterCriticalSection(&This->lock);
 
     /* avail_update required to get accurate snd_pcm_state() */
-    snd_pcm_avail_update(This->pcm_handle);
-    alsa_state = snd_pcm_state(This->pcm_handle);
+    snd_pcm_avail_update(stream->pcm_handle);
+    alsa_state = snd_pcm_state(stream->pcm_handle);
 
     if(This->dataflow == eRender){
-        position = This->written_frames - This->held_frames;
+        position = stream->written_frames - stream->held_frames;
 
-        if(This->started && alsa_state == SND_PCM_STATE_RUNNING && This->held_frames)
+        if(stream->started && alsa_state == SND_PCM_STATE_RUNNING && stream->held_frames)
             /* we should be using snd_pcm_delay here, but it is broken
              * especially during ALSA device underrun. instead, let's just
              * interpolate between periods with the system timer. */
-            position += interp_elapsed_frames(This);
+            position += interp_elapsed_frames(stream);
 
-        position = min(position, This->written_frames - This->held_frames + This->mmdev_period_frames);
+        position = min(position, stream->written_frames - stream->held_frames + stream->mmdev_period_frames);
 
-        position = min(position, This->written_frames);
+        position = min(position, stream->written_frames);
     }else
-        position = This->written_frames + This->held_frames;
+        position = stream->written_frames + stream->held_frames;
 
     /* ensure monotic growth */
-    if(position < This->last_pos_frames)
-        position = This->last_pos_frames;
+    if(position < stream->last_pos_frames)
+        position = stream->last_pos_frames;
     else
-        This->last_pos_frames = position;
+        stream->last_pos_frames = position;
 
     TRACE("frames written: %u, held: %u, state: 0x%x, position: %u\n",
-            (UINT32)(This->written_frames%1000000000), This->held_frames,
+            (UINT32)(stream->written_frames%1000000000), stream->held_frames,
             alsa_state, (UINT32)(position%1000000000));
 
     LeaveCriticalSection(&This->lock);
 
-    if(This->share == AUDCLNT_SHAREMODE_SHARED)
-        *pos = position * This->fmt->nBlockAlign;
+    if(stream->share == AUDCLNT_SHAREMODE_SHARED)
+        *pos = position * stream->fmt->nBlockAlign;
     else
         *pos = position;
 
@@ -3056,7 +3065,7 @@ static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
 
     LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
         EnterCriticalSection(&client->lock);
-        if(client->started){
+        if(client->stream->started){
             *state = AudioSessionStateActive;
             LeaveCriticalSection(&client->lock);
             LeaveCriticalSection(&g_sessions_lock);
diff --git a/dlls/winealsa.drv/unixlib.h b/dlls/winealsa.drv/unixlib.h
index f3014d0b448..8a20710b7e5 100644
--- a/dlls/winealsa.drv/unixlib.h
+++ b/dlls/winealsa.drv/unixlib.h
@@ -16,6 +16,41 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "audioclient.h"
+
+struct alsa_stream
+{
+    snd_pcm_t *pcm_handle;
+    snd_pcm_uframes_t alsa_bufsize_frames, alsa_period_frames, safe_rewind_frames;
+    snd_pcm_hw_params_t *hw_params; /* does not hold state between calls */
+    snd_pcm_format_t alsa_format;
+
+    LARGE_INTEGER last_period_time;
+
+    WAVEFORMATEX *fmt;
+    DWORD flags;
+    AUDCLNT_SHAREMODE share;
+    HANDLE event;
+
+    BOOL need_remapping;
+    int alsa_channels;
+    int alsa_channel_map[32];
+
+    BOOL started;
+    REFERENCE_TIME mmdev_period_rt;
+    UINT64 written_frames, last_pos_frames;
+    UINT32 bufsize_frames, held_frames, tmp_buffer_frames, mmdev_period_frames;
+    snd_pcm_uframes_t remapping_buf_frames;
+    UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
+    UINT32 wri_offs_frames; /* where to write fresh data in local_buffer */
+    UINT32 hidden_frames;   /* ALSA reserve to ensure continuous rendering */
+    UINT32 vol_adjusted_frames; /* Frames we've already adjusted the volume of but didn't write yet */
+    UINT32 data_in_alsa_frames;
+
+    BYTE *local_buffer, *tmp_buffer, *remapping_buf, *silence_buf;
+    LONG32 getbuf_last; /* <0 when using tmp_buffer */
+};
+
 struct endpoint
 {
     WCHAR *name;
-- 
2.25.1




More information about the wine-devel mailing list