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.
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