CVS: winex/dlls/winmm/winealsa audio.c,1.44,1.45

[email protected]
Newsgroups gmane.comp.emulators.winex.cvs
Message-ID <[email protected]>
Subject: winex/dlls/winmm/winealsa audio.c,1.44,1.45Update of /var/lib/cvsd/cvsroot/winex/dlls/winmm/winealsa
In directory agravaine:/tmp/cvs-serv24421/dlls/winmm/winealsa

Modified Files:
	audio.c 
Log Message:
Added code to zero-copy sound data into ALSA using a different technique.
Unlike the previous technique, dmix handles this one fine with mmap
enabled. Just in case, though, the old technique is still used if a HW
device is used directly.


Index: audio.c
===================================================================
RCS file: /var/lib/cvsd/cvsroot/winex/dlls/winmm/winealsa/audio.c,v
retrieving revision 1.44
retrieving revision 1.45
diff -u -d -r1.44 -r1.45
--- audio.c	27 Mar 2007 21:10:38 -0000	1.44
+++ audio.c	30 Mar 2007 21:32:29 -0000	1.45
@@ -2035,6 +2035,7 @@
     snd_async_handler_t *     mmap_async_handler;
     snd_pcm_uframes_t         mmap_app_position;
     const snd_pcm_channel_area_t * mmap_areas_last;
+    snd_pcm_uframes_t         mmap_async_commit;
     BOOL playing;
 };
 
@@ -2068,23 +2069,24 @@
 	if (err < 0 && err != -EAGAIN){
 	    err = snd_pcm_prepare(handle);
 	    if (err < 0)
-	        ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err));
+		ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err));
 	    pdbi->mmap_app_position = 0;
 	}
     }
 }
 
-#if 0
 static void DSDB_MMAPCopy(IDsDriverBufferImpl* pdbi)
 {
     snd_pcm_t *        handle = DSDB_get_handle(pdbi);
     snd_pcm_hw_params_t * hw_params = DSDB_get_hw_params(pdbi);
     int                channels;
     snd_pcm_format_t   format;
-    snd_pcm_uframes_t  period_size;
+    snd_pcm_uframes_t  period_size, commit_size;
     snd_pcm_sframes_t  avail;
+    snd_pcm_sframes_t  delay;
+    snd_pcm_state_t    state;
 
-    if ( !pdbi->mmap_buffer || !hw_params || !handle)
+    if ( !hw_params || !handle)
     	return;
 
     channels = snd_pcm_hw_params_get_channels(hw_params);
@@ -2094,22 +2096,44 @@
 
     DSDB_CheckXRUN(pdbi);
 
-    TRACE("avail=%d format=%s channels=%d\n", (int)avail, snd_pcm_format_name(format), channels );
+    if (snd_pcm_delay(handle, &delay) < 0)
+	delay = 0;
+    state = snd_pcm_state(handle);
+    if (state != SND_PCM_STATE_RUNNING) {
+	/* the buffer is supposed to be empty here */
+	delay = 0;
+    }
 
-    while (avail >= period_size)
+    if (pdbi->mmap_async_commit >= delay)
+	commit_size = pdbi->mmap_async_commit - delay;
+    else
+	commit_size = 0;
+
+    if (commit_size > avail)
+	commit_size = avail;
+
+    TRACE("avail=%d delay=%d commit=%d state=%s format=%s channels=%d\n",
+          (int)avail, (int)delay, (int)commit_size,
+          snd_pcm_state_name(state), snd_pcm_format_name(format), channels );
+
+    while (commit_size >= period_size)
     {
 	const snd_pcm_channel_area_t *areas;
 	snd_pcm_uframes_t     ofs;
 	snd_pcm_uframes_t     frames;
 	int                   err;
 
-	frames = avail / period_size * period_size; /* round down to a multiple of period_size */
+	frames = commit_size / period_size * period_size; /* round down to a multiple of period_size */
 
 	EnterCriticalSection(&pdbi->mmap_crst);
 
 	snd_pcm_mmap_begin(handle, &areas, &ofs, &frames);
-	snd_pcm_areas_copy(areas, ofs, pdbi->mmap_areas, ofs, channels, frames, format);
+	TRACE("offset=%d frames=%d\n", (int)ofs, (int)frames);
+	if (pdbi->mmap_buffer)
+	    snd_pcm_areas_copy(areas, ofs, pdbi->mmap_areas, ofs, channels, frames, format);
 	err = snd_pcm_mmap_commit(handle, ofs, frames);
+	if (err > 0)
+	    pdbi->mmap_app_position += err;
 
 	LeaveCriticalSection(&pdbi->mmap_crst);
 
@@ -2117,25 +2141,28 @@
 	    ERR("mmap partially failed.\n");
 
 	avail = snd_pcm_avail_update(handle);
+
+	commit_size -= frames;
+	if (commit_size > avail)
+	    commit_size = avail;
     }
- }
-#endif
+}
 
-#if 0
 static void DSDB_PCMCallback(snd_async_handler_t *ahandler)
 {
     /* snd_pcm_t *               handle = snd_async_handler_get_pcm(ahandler); */
-    /* IDsDriverBufferImpl*      pdbi = snd_async_handler_get_callback_private(ahandler); */
+    IDsDriverBufferImpl*      pdbi = snd_async_handler_get_callback_private(ahandler);
     TRACE("callback called\n");
-    /* DSDB_MMAPCopy(pdbi); */
+    DSDB_MMAPCopy(pdbi);
 }
-#endif
 
 static int DSDB_CreateMMAP(IDsDriverBufferImpl* pdbi)
  {
     snd_pcm_t *               handle = DSDB_get_handle(pdbi);
+    snd_pcm_type_t            pcm_type = snd_pcm_type(handle);
     snd_pcm_hw_params_t *     hw_params = DSDB_get_hw_params(pdbi);
     snd_pcm_format_t          format = snd_pcm_hw_params_get_format(hw_params);
+    snd_pcm_uframes_t         period_size = snd_pcm_hw_params_get_period_size(hw_params, 0);
     snd_pcm_uframes_t         frames = snd_pcm_hw_params_get_buffer_size(hw_params);
     snd_pcm_uframes_t         offset = 0;
     snd_pcm_sframes_t         move;
@@ -2157,6 +2184,7 @@
     pdbi->mmap_app_position = 0;
     pdbi->mmap_buflen_frames = frames;
     pdbi->mmap_buflen_bytes = snd_pcm_frames_to_bytes( handle, frames );
+    pdbi->mmap_async_commit = 0;
 #if 0
     pdbi->mmap_buffer = HeapAlloc(GetProcessHeap(),0,pdbi->mmap_buflen_bytes);
     if (!pdbi->mmap_buffer)
@@ -2186,25 +2214,47 @@
 #endif
 
     /* make sure buffer is filled with silence */
-    err = snd_pcm_mmap_begin(handle, &pdbi->mmap_areas_last, &offset, &frames);
-    if (err < 0) {
-	ERR("mmap_begin failed, reason: %s\n", snd_strerror(err));
-	return E_FAIL;
+    /* since dmix does not support rewind, only do this for hw */
+    if (pdbi->mmap_buffer || pcm_type == SND_PCM_TYPE_HW) {
+	frames = snd_pcm_hw_params_get_buffer_size(hw_params);
+	err = snd_pcm_mmap_begin(handle, &pdbi->mmap_areas_last, &offset, &frames);
+	if (err < 0) {
+	    ERR("mmap_begin failed, reason: %s\n", snd_strerror(err));
+	    return E_FAIL;
+	}
+	snd_pcm_areas_silence(pdbi->mmap_areas_last, offset, channels, frames, format);
+	move = snd_pcm_mmap_commit(handle, offset, frames);
+	snd_pcm_rewind(handle, move);
+    }
+    else {
+	/* just initialize mmap_areas_last */
+	frames = 0;
+	err = snd_pcm_mmap_begin(handle, &pdbi->mmap_areas_last, &offset, &frames);
+	if (err < 0) {
+	    ERR("mmap_begin failed, reason: %s\n", snd_strerror(err));
+	    return E_FAIL;
+	}
+	frames = snd_pcm_hw_params_get_buffer_size(hw_params);
+	snd_pcm_areas_silence(pdbi->mmap_areas_last, offset, channels, frames, format);
+    }
+
+    TRACE("PCM type: %d (%s)\n", pcm_type, snd_pcm_type_name(pcm_type));
+    if (pdbi->mmap_buffer || pcm_type != SND_PCM_TYPE_HW) {
+	/* keep 3 periods ahead */
+	TRACE("enabling async commit\n");
+	pdbi->mmap_async_commit = 2 * period_size;
     }
-    snd_pcm_areas_silence(pdbi->mmap_areas_last, offset, channels, frames, format);
-    move = snd_pcm_mmap_commit(handle, offset, frames);
-    snd_pcm_rewind(handle, move);
 
     InitializeCriticalSection(&pdbi->mmap_crst);
 
-#if 0
-    err = snd_async_add_pcm_handler(&pdbi->mmap_async_handler, handle, DSDB_PCMCallback, pdbi);
-    if ( err < 0 )
-    {
- 	ERR("add_pcm_handler failed. reason: %s\n", snd_strerror(err));
-	return DSERR_GENERIC;
+    if (pdbi->mmap_async_commit) {
+	err = snd_async_add_pcm_handler(&pdbi->mmap_async_handler, handle, DSDB_PCMCallback, pdbi);
+	if ( err < 0 )
+	{
+	    ERR("add_pcm_handler failed. reason: %s\n", snd_strerror(err));
+	    return DSERR_GENERIC;
+	}
     }
-#endif
 
     return DS_OK;
  }
@@ -2214,8 +2264,8 @@
     TRACE("mmap buffer %p destroyed\n", pdbi->mmap_buffer);
 #if 0
     HeapFree(GetProcessHeap(), 0, pdbi->mmap_areas);
-    HeapFree(GetProcessHeap(), 0, pdbi->mmap_buffer);
 #endif
+    HeapFree(GetProcessHeap(), 0, pdbi->mmap_buffer);
     pdbi->mmap_areas = NULL;
     pdbi->mmap_buffer = NULL;
     DeleteCriticalSection(&pdbi->mmap_crst);
@@ -2275,7 +2325,8 @@
     /* this simply retrieves the playing position from the hardware...
      * only works reliably when using "hw" without userspace plugins */
     snd_pcm_delay(handle, &delay);
-    play_pos = This->mmap_app_position - delay;
+    if (snd_pcm_state (handle) != SND_PCM_STATE_RUNNING)
+        delay = 0;
     avail = This->mmap_buflen_frames - delay;
 #else
     /* this propagates playing position from the hardware to ALSA's
@@ -2291,8 +2342,12 @@
             avail = 0;
     }
 
-    /* in theory, delay + avail == This->mmap_buflen_frames, so... */
-    delay = This->mmap_buflen_frames - avail;
+    /* in theory, delay + avail == This->mmap_buflen_frames,
+     * but apparently only when using "hw" directly */
+    /* delay = This->mmap_buflen_frames - avail; */
+    snd_pcm_delay(handle, &delay);
+    if (snd_pcm_state (handle) != SND_PCM_STATE_RUNNING)
+        delay = 0;
 #endif
 
     play_pos = This->mmap_app_position - delay;
@@ -2466,6 +2521,8 @@
     /* get playing position */
 #if 0
     snd_pcm_delay(handle, &delay);
+    if (snd_pcm_state (handle) != SND_PCM_STATE_RUNNING)
+        delay = 0;
     play_pos = This->mmap_app_position - delay;
     avail = This->mmap_buflen_frames - delay;
 #else
@@ -2479,7 +2536,10 @@
             avail = 0;
     }
 
-    delay = This->mmap_buflen_frames - avail;
+    /* delay = This->mmap_buflen_frames - avail; */
+    snd_pcm_delay(handle, &delay);
+    if (snd_pcm_state (handle) != SND_PCM_STATE_RUNNING)
+        delay = 0;
 #endif
 
     play_pos = This->mmap_app_position - delay;
@@ -2588,14 +2648,20 @@
 					       DWORD dwFlags)
 {
     ICOM_THIS(IDsDriverBufferImpl,iface);
+    LPVOID buffer;
 
     TRACE("(%p,...,%ld,%ld,0x%lx)\n",iface,dwWritePosition,dwWriteLen,dwFlags);
-    if (!This->mmap_buffer) {
+    if (!(This->mmap_buffer || This->mmap_async_commit)) {
 	return IDsDriverBufferImpl_HwLock(iface, ppvAudio1, pdwLen1, ppvAudio2, pdwLen2,
 					  dwWritePosition, dwWriteLen, dwFlags);
     }
 
-    *ppvAudio1 = ((LPBYTE)This->mmap_buffer) + dwWritePosition;
+    if (This->mmap_buffer)
+	buffer = This->mmap_buffer;
+    else
+	buffer = This->mmap_areas_last[0].addr;
+
+    *ppvAudio1 = ((LPBYTE)buffer) + dwWritePosition;
     if ((dwWritePosition + dwWriteLen) > This->mmap_buflen_bytes)
 	*pdwLen1 = This->mmap_buflen_bytes - dwWritePosition;
     else
@@ -2603,7 +2669,7 @@
     dwWriteLen -= *pdwLen1;
 
     if (dwWriteLen) {
-	if (ppvAudio2) *ppvAudio2 = ((LPBYTE)This->mmap_buffer);
+	if (ppvAudio2) *ppvAudio2 = (LPBYTE)buffer;
 	if (pdwLen2) *pdwLen2 = dwWriteLen;
     } else {
 	if (ppvAudio2) *ppvAudio2 = NULL;
@@ -2623,18 +2689,27 @@
 {
     ICOM_THIS(IDsDriverBufferImpl,iface);
     DWORD dwWritePos, dwWriteLen;
+    LPVOID buffer;
     LPBYTE ptr1, ptr2;
     DWORD len1, len2, len;
     HRESULT hr;
 
     TRACE("(%p,%p,%ld,%p,%ld)\n",iface,pvAudio1,dwLen1,pvAudio2,dwLen2);
-    if (!This->mmap_buffer) {
+    if (!(This->mmap_buffer || This->mmap_async_commit)) {
 	return IDsDriverBufferImpl_HwUnlock(iface, pvAudio1, dwLen1, pvAudio2, dwLen2);
     }
 
-    dwWritePos = ((LPBYTE)pvAudio1) - ((LPBYTE)This->mmap_buffer);
+    if (This->mmap_buffer)
+	buffer = This->mmap_buffer;
+    else
+	buffer = This->mmap_areas_last[0].addr;
+
+    dwWritePos = ((LPBYTE)pvAudio1) - ((LPBYTE)buffer);
     dwWriteLen = dwLen1 + dwLen2;
 
+    if (This->mmap_async_commit || !This->mmap_buffer)
+	return DS_OK;
+
     /* transfer data to hw buffer */
 
     hr = IDsDriverBufferImpl_HwLock(iface, (LPVOID*)&ptr1, &len1, (LPVOID*)&ptr2, &len2,
@@ -2645,7 +2720,7 @@
      * since this may need to change in the future (to implement SetPosition). */
     len = This->mmap_buflen_bytes - dwWritePos;
     if (len1 < len) len = len1;
-    memcpy(ptr1, ((LPBYTE)This->mmap_buffer) + dwWritePos, len);
+    memcpy(ptr1, ((LPBYTE)buffer) + dwWritePos, len);
     ptr1 += len;
     dwWritePos += len;
     len1 -= len;
@@ -2653,12 +2728,12 @@
 	dwWritePos = 0;
     if (len1) {
 	assert(dwWritePos == 0);
-	memcpy(ptr1, This->mmap_buffer, len1);
+	memcpy(ptr1, buffer, len1);
 	dwWritePos += len1;
     }
     len = This->mmap_buflen_bytes - dwWritePos;
     if (len2 < len) len = len2;
-    memcpy(ptr2, ((LPBYTE)This->mmap_buffer) + dwWritePos, len);
+    memcpy(ptr2, ((LPBYTE)buffer) + dwWritePos, len);
     ptr2 += len;
     dwWritePos += len;
     len2 -= len;
@@ -2666,7 +2741,7 @@
 	dwWritePos = 0;
     if (len2) {
 	assert(dwWritePos == 0);
-	memcpy(ptr2, This->mmap_buffer, len2);
+	memcpy(ptr2, buffer, len2);
 	dwWritePos += len2;
     }
 
@@ -2712,10 +2787,8 @@
     snd_pcm_t *         handle;
     snd_pcm_sframes_t   delay = 0;
     snd_pcm_uframes_t   hw_ptr;
-#if 0
-    snd_pcm_hw_params_t * hw_params = DSDB_get_hw_params(This);
-    snd_pcm_uframes_t   period_size = snd_pcm_hw_params_get_period_size(hw_params, 0);
-#endif
+/*    snd_pcm_hw_params_t * hw_params = DSDB_get_hw_params(This); */
+/*    snd_pcm_uframes_t   period_size = snd_pcm_hw_params_get_period_size(hw_params, 0); */
     snd_pcm_state_t     state;
 
     /** we need to track down buffer underruns */
@@ -2757,7 +2830,16 @@
 	*lpdwPlay = snd_pcm_frames_to_bytes(handle, hw_ptr ) % This->mmap_buflen_bytes;
     if (lpdwWrite) {
 	/* add some safety margin (not strictly necessary, but...) */
-	*lpdwWrite = snd_pcm_frames_to_bytes(handle, hw_ptr + ((state == SND_PCM_STATE_RUNNING) ? 8 : 0) ) % This->mmap_buflen_bytes;
+#if 0 /* seems to cause too many audio glitches */
+	if (state == SND_PCM_STATE_RUNNING) {
+	    if (This->mmap_async_commit)
+		*lpdwWrite = snd_pcm_frames_to_bytes(handle, (hw_ptr + This->mmap_async_commit) / period_size * period_size ) % This->mmap_buflen_bytes;
+	    else
+		*lpdwWrite = snd_pcm_frames_to_bytes(handle, hw_ptr + 8 ) % This->mmap_buflen_bytes;
+	}
+	else
+#endif
+	    *lpdwWrite = snd_pcm_frames_to_bytes(handle, hw_ptr) % This->mmap_buflen_bytes;
     }
 #endif
     LeaveCriticalSection(&This->mmap_crst);
@@ -2783,7 +2865,8 @@
     }
     if ( state == SND_PCM_STATE_PREPARED )
     {
-	/* DSDB_MMAPCopy(This); */
+	if (This->mmap_async_commit)
+	    DSDB_MMAPCopy(This);
 	err = snd_pcm_start(handle);
     }
 
@@ -2802,6 +2885,8 @@
 {
     ICOM_THIS(IDsDriverBufferImpl,iface);
     snd_pcm_t *       handle = DSDB_get_handle(This);
+    snd_pcm_uframes_t frames = 0;
+    snd_pcm_uframes_t offset = 0;
     int               err;
 
     TRACE("(%p)\n",iface);
@@ -2817,6 +2902,15 @@
     This->mmap_app_position = 0;
     snd_pcm_prepare(handle);
 
+    if (This->mmap_async_commit) {
+	/* reinitialize mmap_areas_last */
+	err = snd_pcm_mmap_begin(handle, &This->mmap_areas_last, &offset, &frames);
+	if (err < 0) {
+	    ERR("mmap_begin failed, reason: %s\n", snd_strerror(err));
+	    return E_FAIL;
+	}
+    }
+
     return DS_OK;
 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.