From b7b11c35580d0a835149825cd39d5688a935c84c Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 9 Dec 2001 18:41:23 +0000 Subject: [PATCH] - added snd_pcm_mmap_begin_avail function; it is optimized for use after snd_pcm_avail_update() call - fixed broken hw_ptr management inside snd_pcm_plugin code --- include/pcm.h | 5 +++ src/pcm/pcm.c | 63 ++++++++++++++++++++++++-- src/pcm/pcm_plugin.c | 103 +++++++++++++++++++++++-------------------- test/pcm.c | 85 +++++++++++++++++++++++++++-------- 4 files changed, 187 insertions(+), 69 deletions(-) diff --git a/include/pcm.h b/include/pcm.h index a1cfc829..2bbcb432 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -790,6 +790,11 @@ int snd_pcm_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames); +int snd_pcm_mmap_begin_avail(snd_pcm_t *pcm, + const snd_pcm_channel_area_t **areas, + snd_pcm_uframes_t *offset, + snd_pcm_uframes_t *frames, + snd_pcm_uframes_t avail); int snd_pcm_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t frames); snd_pcm_sframes_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size); diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index c5b872a7..f3ede3a7 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -4845,7 +4845,7 @@ int snd_pcm_mmap_begin(snd_pcm_t *pcm, snd_pcm_uframes_t *frames) { snd_pcm_uframes_t cont; - snd_pcm_uframes_t avail; + snd_pcm_sframes_t avail; snd_pcm_uframes_t f; assert(pcm && areas && offset && frames); if (pcm->stopped_areas && @@ -4855,7 +4855,43 @@ int snd_pcm_mmap_begin(snd_pcm_t *pcm, *areas = pcm->running_areas; *offset = *pcm->appl_ptr % pcm->buffer_size; cont = pcm->buffer_size - *offset; - avail = snd_pcm_mmap_avail(pcm); + avail = snd_pcm_avail_update(pcm); + if (avail < 0) + return avail; + f = *frames; + if (f > (snd_pcm_uframes_t)avail) + f = avail; + if (f > cont) + f = cont; + *frames = f; + return 0; +} + +/** + * \brief Application request to access a portion of direct (mmap) area + * \param pcm PCM handle + * \param areas Returned mmap channel areas + * \param offset Returned mmap area offset in area steps (== frames) + * \param size mmap area portion size in frames (wanted on entry, contiguous available on exit) + * \param avail available frames (result from snd_pcm_avail_update()) + * \return 0 on success otherwise a negative error code + */ +int snd_pcm_mmap_begin_avail(snd_pcm_t *pcm, + const snd_pcm_channel_area_t **areas, + snd_pcm_uframes_t *offset, + snd_pcm_uframes_t *frames, + snd_pcm_uframes_t avail) +{ + snd_pcm_uframes_t cont; + snd_pcm_uframes_t f; + assert(pcm && areas && offset && frames && avail <= pcm->buffer_size); + if (pcm->stopped_areas && + snd_pcm_state(pcm) != SND_PCM_STATE_RUNNING) + *areas = pcm->stopped_areas; + else + *areas = pcm->running_areas; + *offset = *pcm->appl_ptr % pcm->buffer_size; + cont = pcm->buffer_size - *offset; f = *frames; if (f > avail) f = avail; @@ -4879,7 +4915,7 @@ int snd_pcm_mmap_begin(snd_pcm_t *pcm, * count that snd_pcm_mmap_begin() returned. Each call to snd_pcm_mmap_begin() * must be followed by a call to snd_pcm_mmap_commit(). * - * Example: + * Example #1: \code double phase = 0; const snd_pcm_area_t *areas; @@ -4894,6 +4930,27 @@ int snd_pcm_mmap_begin(snd_pcm_t *pcm, err = snd_pcm_mmap_commit(pcm_handle, offset, frames); if (err < 0) error(err); +\endcode + * + * Example #2 (determine available frame count at beginning): +\code + double phase = 0; + const snd_pcm_area_t *areas; + snd_pcm_sframes_t avail; + snd_pcm_uframes_t offset, frames; + + avail = snd_pcm_avail_update(pcm); + if (avail < 0) + error(avail); + frames = frame_buffer_size > avail ? avail : frame_buffer_size; + err = snd_pcm_mmap_begin_avail(pcm_handle, &areas, &offset, &frames, avail); + if (err < 0) + error(err); + // this function fills the areas from offset with count of frames + generate_sine(areas, offset, frames, &phase); + err = snd_pcm_mmap_commit(pcm_handle, offset, frames); + if (err < 0) + error(err); \endcode * * Look to the \ref example_test_pcm "Sine-wave generator" example diff --git a/src/pcm/pcm_plugin.c b/src/pcm/pcm_plugin.c index c127bc35..a4341294 100644 --- a/src/pcm/pcm_plugin.c +++ b/src/pcm/pcm_plugin.c @@ -218,7 +218,6 @@ static snd_pcm_sframes_t snd_pcm_plugin_write_areas(snd_pcm_t *pcm, assert(slave_frames <= snd_pcm_mmap_playback_avail(slave)); snd_atomic_write_begin(&plugin->watom); snd_pcm_mmap_appl_forward(pcm, frames); - snd_pcm_mmap_hw_forward(pcm, frames); err = snd_pcm_mmap_commit(slave, slave_offset, slave_frames); snd_atomic_write_end(&plugin->watom); if (err < 0) @@ -251,7 +250,6 @@ static snd_pcm_sframes_t snd_pcm_plugin_read_areas(snd_pcm_t *pcm, assert(slave_frames <= snd_pcm_mmap_capture_avail(slave)); snd_atomic_write_begin(&plugin->watom); snd_pcm_mmap_appl_forward(pcm, frames); - snd_pcm_mmap_hw_forward(pcm, frames); err = snd_pcm_mmap_commit(slave, slave_offset, slave_frames); snd_atomic_write_end(&plugin->watom); if (err < 0) @@ -303,8 +301,9 @@ int snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm, snd_pcm_plugin_t *plugin = pcm->private_data; snd_pcm_t *slave = plugin->slave; const snd_pcm_channel_area_t *areas; - snd_pcm_uframes_t xfer, hw_offset; + snd_pcm_uframes_t appl_offset; snd_pcm_sframes_t slave_size; + if (pcm->stream == SND_PCM_STREAM_CAPTURE) { snd_atomic_write_begin(&plugin->watom); snd_pcm_mmap_appl_forward(pcm, size); @@ -314,32 +313,30 @@ int snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm, slave_size = snd_pcm_avail_update(slave); if (slave_size < 0) return slave_size; - if (slave_size == 0) + if ((snd_pcm_uframes_t)slave_size < size) return -EIO; areas = snd_pcm_mmap_areas(pcm); - hw_offset = snd_pcm_mmap_hw_offset(pcm); - xfer = 0; + appl_offset = snd_pcm_mmap_offset(pcm); while (size > 0 && slave_size > 0) { snd_pcm_uframes_t frames = size; - snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset; + snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset; const snd_pcm_channel_area_t *slave_areas; snd_pcm_uframes_t slave_offset; snd_pcm_uframes_t slave_frames = ULONG_MAX; + snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames); if (frames > cont) frames = cont; - frames = plugin->write(pcm, areas, hw_offset, frames, + frames = plugin->write(pcm, areas, appl_offset, frames, slave_areas, slave_offset, &slave_frames); snd_atomic_write_begin(&plugin->watom); snd_pcm_mmap_appl_forward(pcm, frames); - snd_pcm_mmap_hw_forward(pcm, frames); snd_pcm_mmap_commit(slave, slave_offset, slave_frames); snd_atomic_write_end(&plugin->watom); - xfer += frames; if (frames == cont) - hw_offset = 0; + appl_offset = 0; else - hw_offset += frames; + appl_offset += frames; size -= frames; slave_size -= slave_frames; } @@ -351,46 +348,56 @@ snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm) { snd_pcm_plugin_t *plugin = pcm->private_data; snd_pcm_t *slave = plugin->slave; - const snd_pcm_channel_area_t *areas; - snd_pcm_uframes_t xfer, hw_offset, size; snd_pcm_sframes_t slave_size; + slave_size = snd_pcm_avail_update(slave); - if (slave_size <= 0) + if (pcm->stream == SND_PCM_STREAM_CAPTURE && + pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED && + pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) + goto _capture; + if (plugin->client_frames) { + plugin->hw_ptr = plugin->client_frames(slave, *slave->hw_ptr); + if (slave_size <= 0) + return slave_size; + return plugin->client_frames(pcm, slave_size); + } else { + plugin->hw_ptr = *slave->hw_ptr; return slave_size; - if (pcm->stream == SND_PCM_STREAM_PLAYBACK || - pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED || - pcm->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) - return (plugin->client_frames ? - plugin->client_frames(pcm, slave_size) : - slave_size); - xfer = snd_pcm_mmap_capture_avail(pcm); - size = pcm->buffer_size - xfer; - areas = snd_pcm_mmap_areas(pcm); - hw_offset = snd_pcm_mmap_hw_offset(pcm); - while (size && slave_size) { - snd_pcm_uframes_t frames = size; - snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset; - const snd_pcm_channel_area_t *slave_areas; - snd_pcm_uframes_t slave_offset; - snd_pcm_uframes_t slave_frames = ULONG_MAX; - snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames); - if (frames > cont) - frames = cont; - frames = plugin->read(pcm, areas, hw_offset, frames, - slave_areas, slave_offset, &slave_frames); - snd_atomic_write_begin(&plugin->watom); - snd_pcm_mmap_hw_forward(pcm, frames); - snd_pcm_mmap_commit(slave, slave_offset, slave_frames); - snd_atomic_write_end(&plugin->watom); - xfer += frames; - if (frames == cont) - hw_offset = 0; - else - hw_offset += frames; - size -= frames; - slave_size -= slave_frames; } - return xfer; + _capture: + { + const snd_pcm_channel_area_t *areas; + snd_pcm_uframes_t xfer, hw_offset, size; + + xfer = snd_pcm_mmap_capture_avail(pcm); + size = pcm->buffer_size - xfer; + areas = snd_pcm_mmap_areas(pcm); + hw_offset = snd_pcm_mmap_hw_offset(pcm); + while (size > 0 && slave_size > 0) { + snd_pcm_uframes_t frames = size; + snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset; + const snd_pcm_channel_area_t *slave_areas; + snd_pcm_uframes_t slave_offset; + snd_pcm_uframes_t slave_frames = ULONG_MAX; + snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames); + if (frames > cont) + frames = cont; + frames = plugin->read(pcm, areas, hw_offset, frames, + slave_areas, slave_offset, &slave_frames); + snd_atomic_write_begin(&plugin->watom); + snd_pcm_mmap_hw_forward(pcm, frames); + snd_pcm_mmap_commit(slave, slave_offset, slave_frames); + snd_atomic_write_end(&plugin->watom); + xfer += frames; + if (frames == cont) + hw_offset = 0; + else + hw_offset += frames; + size -= frames; + slave_size -= slave_frames; + } + return xfer; + } } int snd_pcm_plugin_mmap(snd_pcm_t *pcm) diff --git a/test/pcm.c b/test/pcm.c index 7344b0bf..713c925b 100644 --- a/test/pcm.c +++ b/test/pcm.c @@ -411,29 +411,78 @@ static int direct_loop(snd_pcm_t *handle, { double phase = 0; const snd_pcm_channel_area_t *my_areas; - snd_pcm_uframes_t offset, frames; + snd_pcm_uframes_t offset, frames, size; + snd_pcm_sframes_t avail; + snd_pcm_state_t state; int err, first = 1; while (1) { - frames = period_size; - err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames); - if (err < 0) { - printf("MMAP begin error: %s\n", snd_strerror(err)); - exit(EXIT_FAILURE); - } - generate_sine(my_areas, offset, frames, &phase); - err = snd_pcm_mmap_commit(handle, offset, frames); - if (err < 0) { - printf("MMAP commit error: %s\n", snd_strerror(err)); - exit(EXIT_FAILURE); - } - if (frames == 0 && first) { /* trigger playback */ - first = 0; - err = snd_pcm_start(handle); + state = snd_pcm_state(handle); + if (state == SND_PCM_STATE_XRUN) { + err = xrun_recovery(handle, -EPIPE); if (err < 0) { - printf("Start error: %s\n", snd_strerror(err)); - exit(EXIT_FAILURE); + printf("XRUN recovery failed: %s\n", snd_strerror(err)); + return err; } + first = 1; + } else if (state == SND_PCM_STATE_SUSPENDED) { + err = xrun_recovery(handle, -ESTRPIPE); + if (err < 0) { + printf("SUSPEND recovery failed: %s\n", snd_strerror(err)); + return err; + } + } + avail = snd_pcm_avail_update(handle); + if (avail < 0) { + err = xrun_recovery(handle, avail); + if (err < 0) { + printf("avail update failed: %s\n", snd_strerror(err)); + return err; + } + first = 1; + continue; + } + if (avail < period_size) { + if (first) { + first = 0; + err = snd_pcm_start(handle); + if (err < 0) { + printf("Start error: %s\n", snd_strerror(err)); + exit(EXIT_FAILURE); + } + } else { + err = snd_pcm_wait(handle, -1); + if (err < 0) { + if ((err = xrun_recovery(handle, err)) < 0) { + printf("snd_pcm_wait error: %s\n", snd_strerror(err)); + exit(EXIT_FAILURE); + } + first = 1; + } + } + continue; + } + size = period_size; + while (size > 0) { + frames = size; + err = snd_pcm_mmap_begin_avail(handle, &my_areas, &offset, &frames, avail); + if (err < 0) { + if ((err = xrun_recovery(handle, err)) < 0) { + printf("MMAP begin avail error: %s\n", snd_strerror(err)); + exit(EXIT_FAILURE); + } + first = 1; + } + generate_sine(my_areas, offset, frames, &phase); + err = snd_pcm_mmap_commit(handle, offset, frames); + if (err < 0) { + if ((err = xrun_recovery(handle, err)) < 0) { + printf("MMAP commit error: %s\n", snd_strerror(err)); + exit(EXIT_FAILURE); + } + first = 1; + } + size -= frames; } } }