CVS: winex/dlls/winmm/winealsa audio.c,1.44,1.45
[email protected] 30 Mar 2007 21:32:31 -0000
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;
}