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