mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-12-16 08:56:42 -05:00
- 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
This commit is contained in:
parent
b63e44aab0
commit
b7b11c3558
4 changed files with 187 additions and 69 deletions
|
|
@ -790,6 +790,11 @@ int snd_pcm_mmap_begin(snd_pcm_t *pcm,
|
||||||
const snd_pcm_channel_area_t **areas,
|
const snd_pcm_channel_area_t **areas,
|
||||||
snd_pcm_uframes_t *offset,
|
snd_pcm_uframes_t *offset,
|
||||||
snd_pcm_uframes_t *frames);
|
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,
|
int snd_pcm_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t offset,
|
||||||
snd_pcm_uframes_t frames);
|
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);
|
snd_pcm_sframes_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);
|
||||||
|
|
|
||||||
|
|
@ -4845,7 +4845,7 @@ int snd_pcm_mmap_begin(snd_pcm_t *pcm,
|
||||||
snd_pcm_uframes_t *frames)
|
snd_pcm_uframes_t *frames)
|
||||||
{
|
{
|
||||||
snd_pcm_uframes_t cont;
|
snd_pcm_uframes_t cont;
|
||||||
snd_pcm_uframes_t avail;
|
snd_pcm_sframes_t avail;
|
||||||
snd_pcm_uframes_t f;
|
snd_pcm_uframes_t f;
|
||||||
assert(pcm && areas && offset && frames);
|
assert(pcm && areas && offset && frames);
|
||||||
if (pcm->stopped_areas &&
|
if (pcm->stopped_areas &&
|
||||||
|
|
@ -4855,7 +4855,43 @@ int snd_pcm_mmap_begin(snd_pcm_t *pcm,
|
||||||
*areas = pcm->running_areas;
|
*areas = pcm->running_areas;
|
||||||
*offset = *pcm->appl_ptr % pcm->buffer_size;
|
*offset = *pcm->appl_ptr % pcm->buffer_size;
|
||||||
cont = pcm->buffer_size - *offset;
|
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;
|
f = *frames;
|
||||||
if (f > avail)
|
if (f > avail)
|
||||||
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()
|
* 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().
|
* must be followed by a call to snd_pcm_mmap_commit().
|
||||||
*
|
*
|
||||||
* Example:
|
* Example #1:
|
||||||
\code
|
\code
|
||||||
double phase = 0;
|
double phase = 0;
|
||||||
const snd_pcm_area_t *areas;
|
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);
|
err = snd_pcm_mmap_commit(pcm_handle, offset, frames);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
error(err);
|
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
|
\endcode
|
||||||
*
|
*
|
||||||
* Look to the \ref example_test_pcm "Sine-wave generator" example
|
* Look to the \ref example_test_pcm "Sine-wave generator" example
|
||||||
|
|
|
||||||
|
|
@ -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));
|
assert(slave_frames <= snd_pcm_mmap_playback_avail(slave));
|
||||||
snd_atomic_write_begin(&plugin->watom);
|
snd_atomic_write_begin(&plugin->watom);
|
||||||
snd_pcm_mmap_appl_forward(pcm, frames);
|
snd_pcm_mmap_appl_forward(pcm, frames);
|
||||||
snd_pcm_mmap_hw_forward(pcm, frames);
|
|
||||||
err = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
|
err = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
|
||||||
snd_atomic_write_end(&plugin->watom);
|
snd_atomic_write_end(&plugin->watom);
|
||||||
if (err < 0)
|
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));
|
assert(slave_frames <= snd_pcm_mmap_capture_avail(slave));
|
||||||
snd_atomic_write_begin(&plugin->watom);
|
snd_atomic_write_begin(&plugin->watom);
|
||||||
snd_pcm_mmap_appl_forward(pcm, frames);
|
snd_pcm_mmap_appl_forward(pcm, frames);
|
||||||
snd_pcm_mmap_hw_forward(pcm, frames);
|
|
||||||
err = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
|
err = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
|
||||||
snd_atomic_write_end(&plugin->watom);
|
snd_atomic_write_end(&plugin->watom);
|
||||||
if (err < 0)
|
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_plugin_t *plugin = pcm->private_data;
|
||||||
snd_pcm_t *slave = plugin->slave;
|
snd_pcm_t *slave = plugin->slave;
|
||||||
const snd_pcm_channel_area_t *areas;
|
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;
|
snd_pcm_sframes_t slave_size;
|
||||||
|
|
||||||
if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
|
if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
|
||||||
snd_atomic_write_begin(&plugin->watom);
|
snd_atomic_write_begin(&plugin->watom);
|
||||||
snd_pcm_mmap_appl_forward(pcm, size);
|
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);
|
slave_size = snd_pcm_avail_update(slave);
|
||||||
if (slave_size < 0)
|
if (slave_size < 0)
|
||||||
return slave_size;
|
return slave_size;
|
||||||
if (slave_size == 0)
|
if ((snd_pcm_uframes_t)slave_size < size)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
areas = snd_pcm_mmap_areas(pcm);
|
areas = snd_pcm_mmap_areas(pcm);
|
||||||
hw_offset = snd_pcm_mmap_hw_offset(pcm);
|
appl_offset = snd_pcm_mmap_offset(pcm);
|
||||||
xfer = 0;
|
|
||||||
while (size > 0 && slave_size > 0) {
|
while (size > 0 && slave_size > 0) {
|
||||||
snd_pcm_uframes_t frames = size;
|
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;
|
const snd_pcm_channel_area_t *slave_areas;
|
||||||
snd_pcm_uframes_t slave_offset;
|
snd_pcm_uframes_t slave_offset;
|
||||||
snd_pcm_uframes_t slave_frames = ULONG_MAX;
|
snd_pcm_uframes_t slave_frames = ULONG_MAX;
|
||||||
|
|
||||||
snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
|
snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
|
||||||
if (frames > cont)
|
if (frames > cont)
|
||||||
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);
|
slave_areas, slave_offset, &slave_frames);
|
||||||
snd_atomic_write_begin(&plugin->watom);
|
snd_atomic_write_begin(&plugin->watom);
|
||||||
snd_pcm_mmap_appl_forward(pcm, frames);
|
snd_pcm_mmap_appl_forward(pcm, frames);
|
||||||
snd_pcm_mmap_hw_forward(pcm, frames);
|
|
||||||
snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
|
snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
|
||||||
snd_atomic_write_end(&plugin->watom);
|
snd_atomic_write_end(&plugin->watom);
|
||||||
xfer += frames;
|
|
||||||
if (frames == cont)
|
if (frames == cont)
|
||||||
hw_offset = 0;
|
appl_offset = 0;
|
||||||
else
|
else
|
||||||
hw_offset += frames;
|
appl_offset += frames;
|
||||||
size -= frames;
|
size -= frames;
|
||||||
slave_size -= slave_frames;
|
slave_size -= slave_frames;
|
||||||
}
|
}
|
||||||
|
|
@ -351,23 +348,32 @@ snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm)
|
||||||
{
|
{
|
||||||
snd_pcm_plugin_t *plugin = pcm->private_data;
|
snd_pcm_plugin_t *plugin = pcm->private_data;
|
||||||
snd_pcm_t *slave = plugin->slave;
|
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;
|
snd_pcm_sframes_t slave_size;
|
||||||
|
|
||||||
slave_size = snd_pcm_avail_update(slave);
|
slave_size = snd_pcm_avail_update(slave);
|
||||||
|
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)
|
if (slave_size <= 0)
|
||||||
return slave_size;
|
return slave_size;
|
||||||
if (pcm->stream == SND_PCM_STREAM_PLAYBACK ||
|
return plugin->client_frames(pcm, slave_size);
|
||||||
pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED ||
|
} else {
|
||||||
pcm->access == SND_PCM_ACCESS_RW_NONINTERLEAVED)
|
plugin->hw_ptr = *slave->hw_ptr;
|
||||||
return (plugin->client_frames ?
|
return slave_size;
|
||||||
plugin->client_frames(pcm, slave_size) :
|
}
|
||||||
slave_size);
|
_capture:
|
||||||
|
{
|
||||||
|
const snd_pcm_channel_area_t *areas;
|
||||||
|
snd_pcm_uframes_t xfer, hw_offset, size;
|
||||||
|
|
||||||
xfer = snd_pcm_mmap_capture_avail(pcm);
|
xfer = snd_pcm_mmap_capture_avail(pcm);
|
||||||
size = pcm->buffer_size - xfer;
|
size = pcm->buffer_size - xfer;
|
||||||
areas = snd_pcm_mmap_areas(pcm);
|
areas = snd_pcm_mmap_areas(pcm);
|
||||||
hw_offset = snd_pcm_mmap_hw_offset(pcm);
|
hw_offset = snd_pcm_mmap_hw_offset(pcm);
|
||||||
while (size && slave_size) {
|
while (size > 0 && slave_size > 0) {
|
||||||
snd_pcm_uframes_t frames = size;
|
snd_pcm_uframes_t frames = size;
|
||||||
snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset;
|
snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset;
|
||||||
const snd_pcm_channel_area_t *slave_areas;
|
const snd_pcm_channel_area_t *slave_areas;
|
||||||
|
|
@ -392,6 +398,7 @@ snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm)
|
||||||
}
|
}
|
||||||
return xfer;
|
return xfer;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int snd_pcm_plugin_mmap(snd_pcm_t *pcm)
|
int snd_pcm_plugin_mmap(snd_pcm_t *pcm)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
69
test/pcm.c
69
test/pcm.c
|
|
@ -411,29 +411,78 @@ static int direct_loop(snd_pcm_t *handle,
|
||||||
{
|
{
|
||||||
double phase = 0;
|
double phase = 0;
|
||||||
const snd_pcm_channel_area_t *my_areas;
|
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;
|
int err, first = 1;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
frames = period_size;
|
state = snd_pcm_state(handle);
|
||||||
err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
|
if (state == SND_PCM_STATE_XRUN) {
|
||||||
|
err = xrun_recovery(handle, -EPIPE);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
printf("MMAP begin error: %s\n", snd_strerror(err));
|
printf("XRUN recovery failed: %s\n", snd_strerror(err));
|
||||||
exit(EXIT_FAILURE);
|
return err;
|
||||||
}
|
}
|
||||||
generate_sine(my_areas, offset, frames, &phase);
|
first = 1;
|
||||||
err = snd_pcm_mmap_commit(handle, offset, frames);
|
} else if (state == SND_PCM_STATE_SUSPENDED) {
|
||||||
|
err = xrun_recovery(handle, -ESTRPIPE);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
printf("MMAP commit error: %s\n", snd_strerror(err));
|
printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
|
||||||
exit(EXIT_FAILURE);
|
return err;
|
||||||
}
|
}
|
||||||
if (frames == 0 && first) { /* trigger playback */
|
}
|
||||||
|
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;
|
first = 0;
|
||||||
err = snd_pcm_start(handle);
|
err = snd_pcm_start(handle);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
printf("Start error: %s\n", snd_strerror(err));
|
printf("Start error: %s\n", snd_strerror(err));
|
||||||
exit(EXIT_FAILURE);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue