winealsa: improve sound quality for IDsDriverImpl
Jerry Jenkins
Jerry_J_Jenkins at hotmail.com
Sat Sep 27 09:08:28 CDT 2003
The original assumes that the sound buffer will be fed just as the data
played. Thus some random data were committed to the sound driver and
make noises. If we can access the buffer directly, there would be no
reason to commit data, simply mix the data will be OK.
Based on this thought, I need a function to retrieve the buffer. Then I
found that snd_pcm_mmap_begin is the right one. But no documents said
that it is responsible for return the driver’s buffer. Well, I can
verify that, and the result is really delightful for the time being. In
the long run I prefer the configure time check. I can check that at
runtime in DSDB_MMAPCopy and print some messages if the behavior of
snd_pcm_mmap_begin changes, but I can’t prevent others from accessing
the buffer before DSDB_MMAPCopy check it.
BTW, the version of autoconf that I get differs from that of wine’s, the
difference between two version of configures is too big to put here.
Sorry for the inconvenience.
-------------- next part --------------
Index: configure.ac
===================================================================
RCS file: /home/wine/wine/configure.ac,v
retrieving revision 1.180
diff -u -r1.180 configure.ac
--- configure.ac 24 Sep 2003 18:54:40 -0000 1.180
+++ configure.ac 27 Sep 2003 12:29:59 -0000
@@ -610,8 +610,163 @@
if test "$ac_cv_header_sys_asoundlib_h" = "yes" -o "$ac_cv_header_alsa_asoundlib_h" = "yes"
then
AC_CHECK_LIB(asound,snd_pcm_open,
- AC_DEFINE(HAVE_ALSA,1,[Define if you have ALSA including devel headers])
- ALSALIBS="-lasound")
+ AC_DEFINE(HAVE_ALSA,1,[Define if you have ALSA including devel headers])
+ ALSALIBS="-lasound"
+ AC_MSG_CHECKING([for constant ALSA sound buffer])
+ save_LIBS="$LIBS"
+ LIBS="$LIBS $ALSALIBS"
+ AC_TRY_RUN([
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <errno.h>
+ #define ALSA_PCM_NEW_HW_PARAMS_API
+ #define ALSA_PCM_NEW_SW_PARAMS_API
+ #include <alsa/asoundlib.h>
+
+ const char *device = "plughw:0,0"; /* playback device */
+ snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */
+ snd_pcm_access_t accessmode = SND_PCM_ACCESS_MMAP_INTERLEAVED; /* access mode */
+ unsigned int rate = 44100; /* stream rate */
+ unsigned int channels = 1; /* count of channels */
+ /* ring buffer length in us. Don't set too small a value for this parameter.
+ * Or we have to recover ALSA from underrun state in critical environments.
+ */
+ unsigned int buffer_time = 250000;
+ unsigned int period_time = 16000; /* period time in us */
+ snd_pcm_uframes_t period_size; /* period buffer size */
+ int count = 0; /* loop count */
+ /* SINGLE_LOOP_COUNT > buffer_time / period_time, so we will use
+ * the entire buffer at least once, and make sure that it won't
+ * be reallocated after it is played. */
+ #define SINGLE_LOOP_COUNT 20
+ #define DROP_TEST_COUNT 3 /* Perform a strict check */
+
+ #define CHECK(err) if (err < 0) {/*printf(snd_strerror(err));*/exit(err);}
+
+ static int commit_data(snd_pcm_t *handle)
+ {
+ static void *addr = NULL;
+ static const snd_pcm_channel_area_t *areas = NULL;
+
+ int err;
+ snd_pcm_sframes_t size, commitres;
+ snd_pcm_uframes_t offset, frames;
+ const snd_pcm_channel_area_t *my_areas;
+
+ size = snd_pcm_avail_update(handle);
+ /*printf("avail : %ld.\n", size);*/
+ while ((snd_pcm_uframes_t)size >= period_size) {
+ frames = size;
+ err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
+ CHECK(err);
+ if (NULL == addr) {
+ areas = my_areas;
+ addr = my_areas->addr;
+ }
+ else if (areas != my_areas || addr != my_areas->addr) {
+ exit(-1);
+ }
+ /* We don't care about what the data are, just a test */
+ commitres = snd_pcm_mmap_commit(handle, offset, frames);
+ if (commitres < 0 || commitres != (snd_pcm_sframes_t)frames) {
+ if (errno != 0) exit(errno);
+ }
+ /*printf("%8ld commited.\n", commitres);*/
+ size -= commitres;
+ count++;
+ }
+ return 0;
+ }
+
+ static void async_direct_callback(snd_async_handler_t *ahandler)
+ {
+ snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler);
+ int err = commit_data(handle);
+ CHECK(err);
+ }
+
+ int main()
+ {
+ int i, err, dir;
+ snd_pcm_t *handle;
+ snd_pcm_state_t state;
+ snd_async_handler_t *ahandler;
+ snd_pcm_hw_params_t *hwparams;
+ snd_pcm_sw_params_t *swparams;
+
+ snd_pcm_hw_params_alloca(&hwparams);
+ snd_pcm_sw_params_alloca(&swparams);
+
+ err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+ CHECK(err);
+
+ err = snd_pcm_hw_params_any(handle, hwparams);
+ CHECK(err);
+ err = snd_pcm_hw_params_set_access(handle, hwparams, accessmode);
+ CHECK(err);
+ err = snd_pcm_hw_params_set_format(handle, hwparams, format);
+ CHECK(err);
+ err = snd_pcm_hw_params_set_channels(handle, hwparams, channels);
+ CHECK(err);
+ err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &rate, 0);
+ CHECK(err);
+ err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir);
+ CHECK(err);
+ err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir);
+ CHECK(err);
+ err = snd_pcm_hw_params_get_period_size(hwparams, &period_size, &dir);
+ CHECK(err);
+ err = snd_pcm_hw_params(handle, hwparams);
+ CHECK(err);
+
+ err = snd_pcm_sw_params_current(handle, swparams);
+ CHECK(err);
+ err = snd_pcm_sw_params(handle, swparams);
+ CHECK(err);
+
+ err = snd_async_add_pcm_handler(&ahandler, handle, async_direct_callback, NULL);
+ CHECK(err);
+ /* Start testing */
+ for (i = 0; i < DROP_TEST_COUNT; i++ ) {
+ state = snd_pcm_state(handle);
+ if (state == SND_PCM_STATE_SETUP) {
+ err = snd_pcm_prepare(handle);
+ CHECK(err);
+ state = snd_pcm_state(handle);
+ }
+ if (state == SND_PCM_STATE_PREPARED) {
+ err = commit_data(handle);
+ CHECK(err);
+ err = snd_pcm_start(handle);
+ CHECK(err);
+ }
+ else exit(-1);
+
+ count = 0;
+ while(count < SINGLE_LOOP_COUNT) {
+ /*printf("%-8d", count);*/
+ sleep(1);
+ }
+ err = snd_pcm_drop(handle);
+ CHECK(err);
+ }
+ snd_pcm_drop(handle);
+ snd_async_del_handler(ahandler);
+
+ snd_pcm_close(handle);
+ snd_pcm_hw_params_free(hwparams);
+ snd_pcm_sw_params_free(swparams);
+
+ exit(0);
+ }],
+ [ AC_MSG_RESULT([yes])
+ AC_DEFINE(HAVE_CONSTANT_ALSA_SOUND_BUFFER,1,[The sound buffer returned by snd_pcm_mmap_begin is constant])
+ ],
+ [ AC_MSG_RESULT([no]) ]
+ )
+ LIBS="$save_LIBS"
+ )
fi
dnl **** Check for libaudioio (which can be used to get solaris audio support) ****
Index: dlls/winmm/winealsa/audio.c
===================================================================
RCS file: /home/wine/wine/dlls/winmm/winealsa/audio.c,v
retrieving revision 1.20
diff -u -r1.20 audio.c
--- dlls/winmm/winealsa/audio.c 22 Sep 2003 21:13:33 -0000 1.20
+++ dlls/winmm/winealsa/audio.c 27 Sep 2003 12:30:46 -0000
@@ -1703,13 +1703,13 @@
if ( !pdbi->mmap_buffer || !wwo->hw_params || !wwo->p_handle)
return;
+ DSDB_CheckXRUN(pdbi);
+
channels = snd_pcm_hw_params_get_channels(wwo->hw_params);
format = snd_pcm_hw_params_get_format(wwo->hw_params);
period_size = snd_pcm_hw_params_get_period_size(wwo->hw_params, 0);
avail = snd_pcm_avail_update(wwo->p_handle);
- DSDB_CheckXRUN(pdbi);
-
TRACE("avail=%d format=%s channels=%d\n", (int)avail, snd_pcm_format_name(format), channels );
while (avail >= period_size)
@@ -1724,7 +1724,12 @@
EnterCriticalSection(&pdbi->mmap_crst);
snd_pcm_mmap_begin(wwo->p_handle, &areas, &ofs, &frames);
+#ifndef HAVE_CONSTANT_ALSA_SOUND_BUFFER
snd_pcm_areas_copy(areas, ofs, pdbi->mmap_areas, ofs, channels, frames, format);
+#else
+ if (pdbi->mmap_areas != areas || pdbi->mmap_buffer != areas->addr)
+ FIXME("DMA failed. Please reimplement it.\n");
+#endif
err = snd_pcm_mmap_commit(wwo->p_handle, ofs, frames);
LeaveCriticalSection(&pdbi->mmap_crst);
@@ -1734,7 +1739,7 @@
avail = snd_pcm_avail_update(wwo->p_handle);
}
- }
+}
static void DSDB_PCMCallback(snd_async_handler_t *ahandler)
{
@@ -1752,8 +1757,12 @@
int channels = snd_pcm_hw_params_get_channels(wwo->hw_params);
unsigned int bits_per_sample = snd_pcm_format_physical_width(format);
unsigned int bits_per_frame = bits_per_sample * channels;
+#ifndef HAVE_CONSTANT_ALSA_SOUND_BUFFER
snd_pcm_channel_area_t * a;
unsigned int c;
+#else
+ snd_pcm_uframes_t ofs;
+#endif
int err;
if (TRACE_ON(wave))
@@ -1764,6 +1773,7 @@
pdbi->mmap_buflen_frames = frames;
pdbi->mmap_buflen_bytes = snd_pcm_frames_to_bytes( wwo->p_handle, frames );
+#ifndef HAVE_CONSTANT_ALSA_SOUND_BUFFER
pdbi->mmap_buffer = HeapAlloc(GetProcessHeap(),0,pdbi->mmap_buflen_bytes);
if (!pdbi->mmap_buffer)
return DSERR_OUTOFMEMORY;
@@ -1785,24 +1795,49 @@
a->step = bits_per_frame;
TRACE("Area %d: addr=%p first=%d step=%d\n", c, a->addr, a->first, a->step);
}
+#else
+ err = snd_pcm_mmap_begin(wwo->p_handle, (const snd_pcm_channel_area_t**)&pdbi->mmap_areas, &ofs, &frames);
+ snd_pcm_mmap_commit(wwo->p_handle, ofs, 0L);
+ if (err < 0) {
+ ERR("Create mmap buffer failed : %s\n", snd_strerror(err));
+ return DSERR_GENERIC;
+ }
+ pdbi->mmap_buffer = pdbi->mmap_areas->addr;
+ snd_pcm_format_set_silence(format, pdbi->mmap_buffer, frames );
+
+ TRACE("found mmap buffer of %ld frames (%ld bytes) at %p\n",
+ frames, pdbi->mmap_buflen_bytes, pdbi->mmap_buffer);
+#endif
InitializeCriticalSection(&pdbi->mmap_crst);
err = snd_async_add_pcm_handler(&pdbi->mmap_async_handler, wwo->p_handle, DSDB_PCMCallback, pdbi);
if ( err < 0 )
- {
- ERR("add_pcm_handler failed. reason: %s\n", snd_strerror(err));
+ {
+ ERR("add_pcm_handler failed. reason: %s\n", snd_strerror(err));
+ HeapFree(GetProcessHeap(), 0, pdbi->mmap_areas);
+ HeapFree(GetProcessHeap(), 0, pdbi->mmap_buffer);
+ pdbi->mmap_areas = NULL;
+ pdbi->mmap_buffer = NULL;
return DSERR_GENERIC;
- }
+ }
return DS_OK;
}
static void DSDB_DestroyMMAP(IDsDriverBufferImpl* pdbi)
{
+ int err;
TRACE("mmap buffer %p destroyed\n", pdbi->mmap_buffer);
+ if (pdbi->mmap_async_handler != NULL) {
+ err = snd_async_del_handler(pdbi->mmap_async_handler);
+ if (err < 0) ERR(snd_strerror(err));
+ }
+ pdbi->mmap_async_handler = NULL;
+#ifndef HAVE_CONSTANT_ALSA_SOUND_BUFFER
HeapFree(GetProcessHeap(), 0, pdbi->mmap_areas);
HeapFree(GetProcessHeap(), 0, pdbi->mmap_buffer);
+#endif
pdbi->mmap_areas = NULL;
pdbi->mmap_buffer = NULL;
DeleteCriticalSection(&pdbi->mmap_crst);
@@ -1928,13 +1963,15 @@
if ( state == SND_PCM_STATE_SETUP )
{
err = snd_pcm_prepare(wwo->p_handle);
+ if (err < 0) ERR("prepare failed: %s\n", snd_strerror(err));
state = snd_pcm_state(wwo->p_handle);
}
if ( state == SND_PCM_STATE_PREPARED )
- {
+ {
DSDB_MMAPCopy(This);
err = snd_pcm_start(wwo->p_handle);
- }
+ if (err < 0) ERR("start error: %s\n", snd_strerror(err));
+ }
return DS_OK;
}
Index: include/config.h.in
===================================================================
RCS file: /home/wine/wine/include/config.h.in,v
retrieving revision 1.161
diff -u -r1.161 config.h.in
--- include/config.h.in 24 Sep 2003 18:54:40 -0000 1.161
+++ include/config.h.in 27 Sep 2003 12:30:55 -0000
@@ -47,6 +47,9 @@
/* Define to 1 if you have the `connect' function. */
#undef HAVE_CONNECT
+/* The sound buffer returned by snd_pcm_mmap_begin is constant */
+#undef HAVE_CONSTANT_ALSA_SOUND_BUFFER
+
/* Define if we have linux/input.h AND it contains the INPUT event API */
#undef HAVE_CORRECT_LINUXINPUT_H
More information about the wine-patches
mailing list