From 7b054f4dce56e47a4148e14748e12ab4b0bd71ea Mon Sep 17 00:00:00 2001 From: Abramo Bagnara Date: Tue, 16 May 2000 15:20:34 +0000 Subject: [PATCH] - splitted mmap in logical steps - optimized mmap transfer - completed mmap helpers - renamed pcm_plugin_build.c to pcm_common.c --- TODO | 1 - include/pcm.h | 32 +- src/pcm/Makefile.am | 2 +- src/pcm/pcm.c | 59 +-- src/pcm/pcm_local.h | 11 +- src/pcm/pcm_mmap.c | 904 ++++++++++++++++++++-------------------- src/pcm/pcm_plug.c | 23 +- src/pcm/plugin/adpcm.c | 44 +- src/pcm/plugin/alaw.c | 28 +- src/pcm/plugin/block.c | 8 +- src/pcm/plugin/copy.c | 96 +---- src/pcm/plugin/linear.c | 18 +- src/pcm/plugin/mmap.c | 83 +--- src/pcm/plugin/mulaw.c | 28 +- src/pcm/plugin/rate.c | 28 +- src/pcm/plugin/route.c | 28 +- src/pcm/plugin/stream.c | 8 +- 17 files changed, 639 insertions(+), 762 deletions(-) diff --git a/TODO b/TODO index d4ae1c7e..2dde5a64 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,5 @@ M plug sync and pos problems M Loopback implementation? -L break up snd_pcm_mmap_* in logical steps L complete mmap emulation (after plug sync and pos thought) L add hsearch_r code from glibc (for compatibility with older distributions) L move OSS emulation to user space (LD_PRELOAD) diff --git a/include/pcm.h b/include/pcm.h index 57d23209..117c7e4d 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -97,7 +97,6 @@ int snd_pcm_channel_info(snd_pcm_t *handle, snd_pcm_channel_info_t *info); int snd_pcm_channel_params(snd_pcm_t *handle, snd_pcm_channel_params_t *params); int snd_pcm_channel_setup(snd_pcm_t *handle, snd_pcm_channel_setup_t *setup); int snd_pcm_voice_setup(snd_pcm_t *handle, int channel, snd_pcm_voice_setup_t *setup); -int snd_pcm_all_voices_setup(snd_pcm_t *handle, int channel, snd_pcm_voice_setup_t *setup); int snd_pcm_channel_status(snd_pcm_t *handle, snd_pcm_channel_status_t *status); int snd_pcm_channel_update(snd_pcm_t *handle, int channel); int snd_pcm_playback_prepare(snd_pcm_t *handle); @@ -126,17 +125,36 @@ int snd_pcm_mmap_data(snd_pcm_t *handle, int channel, void **buffer); int snd_pcm_munmap_control(snd_pcm_t *handle, int channel); int snd_pcm_munmap_data(snd_pcm_t *handle, int channel); int snd_pcm_voices_mask(snd_pcm_t *pcm, int channel, bitset_t *client_vmask); -int snd_pcm_mmap_frags_used(snd_pcm_t *pcm, int channel, ssize_t *frags); -int snd_pcm_mmap_frags_free(snd_pcm_t *pcm, int channel, ssize_t *frags); -int snd_pcm_mmap_bytes_used(snd_pcm_t *pcm, int channel, ssize_t *bytes); -int snd_pcm_mmap_bytes_free(snd_pcm_t *pcm, int channel, ssize_t *bytes); int snd_pcm_mmap_ready(snd_pcm_t *pcm, int channel); ssize_t snd_pcm_mmap_write(snd_pcm_t *handle, const void *buffer, size_t size); ssize_t snd_pcm_mmap_read(snd_pcm_t *handle, void *buffer, size_t size); ssize_t snd_pcm_mmap_writev(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count); ssize_t snd_pcm_mmap_readv(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count); +int snd_pcm_mmap_samples_used(snd_pcm_t *pcm, int channel, ssize_t *samples); +int snd_pcm_mmap_samples_free(snd_pcm_t *pcm, int channel, ssize_t *samples); +ssize_t snd_pcm_mmap_samples_xfer(snd_pcm_t *pcm, int channel, size_t samples); +ssize_t snd_pcm_mmap_samples_offset(snd_pcm_t *pcm, int channel); +int snd_pcm_mmap_commit_samples(snd_pcm_t *pcm, int channel, int samples); +ssize_t snd_pcm_mmap_write_areas(snd_pcm_t *pcm, snd_pcm_voice_area_t *voices, size_t samples); +ssize_t snd_pcm_mmap_write_samples(snd_pcm_t *pcm, const void *buffer, size_t samples); +ssize_t snd_pcm_mmap_read_areas(snd_pcm_t *pcm, snd_pcm_voice_area_t *voices, size_t samples); +ssize_t snd_pcm_mmap_read_samples(snd_pcm_t *pcm, const void *buffer, size_t samples); +int snd_pcm_mmap_get_areas(snd_pcm_t *pcm, int channel, snd_pcm_voice_area_t *areas); + + ssize_t snd_pcm_bytes_per_second(snd_pcm_t *pcm, int channel); +int snd_pcm_area_silence(const snd_pcm_voice_area_t *dst_voice, size_t dst_offset, + size_t samples, int format); +int snd_pcm_areas_silence(const snd_pcm_voice_area_t *dst_voices, size_t dst_offset, + size_t vcount, size_t samples, int format); +int snd_pcm_area_copy(const snd_pcm_voice_area_t *src_voice, size_t src_offset, + const snd_pcm_voice_area_t *dst_voice, size_t dst_offset, + size_t samples, int format); +int snd_pcm_areas_copy(const snd_pcm_voice_area_t *src_voices, size_t src_offset, + const snd_pcm_voice_area_t *dst_voices, size_t dst_offset, + size_t vcount, size_t samples, int format); + /* misc */ int snd_pcm_format_signed(int format); @@ -181,9 +199,7 @@ typedef enum { typedef struct snd_stru_pcm_plugin_voice { void *aptr; /* pointer to the allocated area */ - void *addr; /* address to voice samples */ - unsigned int first; /* offset to first sample in bits */ - unsigned int step; /* samples distance in bits */ + snd_pcm_voice_area_t area; unsigned int enabled:1; /* voice need to be processed */ unsigned int wanted:1; /* voice is wanted */ } snd_pcm_plugin_voice_t; diff --git a/src/pcm/Makefile.am b/src/pcm/Makefile.am index 6820111f..89222aed 100644 --- a/src/pcm/Makefile.am +++ b/src/pcm/Makefile.am @@ -2,7 +2,7 @@ SUBDIRS = plugin EXTRA_LTLIBRARIES = libpcm.la -libpcm_la_SOURCES = pcm.c pcm_hw.c pcm_plug.c pcm_plugin_build.c pcm_misc.c \ +libpcm_la_SOURCES = pcm.c pcm_hw.c pcm_plug.c pcm_common.c pcm_misc.c \ pcm_mmap.c libpcm_la_LIBADD = plugin/libpcmplugin.la noinst_HEADERS = pcm_local.h diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 20f3a473..5455ddf2 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -83,10 +83,6 @@ int snd_pcm_channel_close(snd_pcm_t *pcm, int channel) ret = err; chan->open = 0; chan->valid_setup = 0; - if (chan->valid_voices_setup) { - chan->valid_voices_setup = 0; - free(chan->voices_setup); - } return ret; } @@ -182,6 +178,9 @@ int snd_pcm_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup) if ((err = pcm->ops->channel_setup(pcm, setup)) < 0) return err; memcpy(&chan->setup, setup, sizeof(*setup)); + chan->sample_width = snd_pcm_format_physical_width(setup->format.format); + chan->bits_per_sample = chan->sample_width * setup->format.voices; + chan->samples_per_frag = setup->frag_size * 8 / chan->bits_per_sample; chan->valid_setup = 1; return 0; } @@ -209,61 +208,9 @@ int snd_pcm_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t *setu chan = &pcm->chan[channel]; if (!chan->open || !chan->valid_setup) return -EBADFD; - if (chan->valid_voices_setup) { - if (setup->voice >= chan->setup.format.voices) - return -EINVAL; - memcpy(setup, &chan->voices_setup[setup->voice], sizeof(*setup)); - return 0; - } return pcm->ops->voice_setup(pcm, channel, setup); } -const snd_pcm_voice_setup_t* snd_pcm_channel_cached_voice_setup(snd_pcm_t *pcm, int channel, unsigned int voice) -{ - struct snd_pcm_chan *chan; - if (!pcm) - return 0; - if (channel < 0 || channel > 1) - return 0; - chan = &pcm->chan[channel]; - if (!chan->open || !chan->valid_setup) - return 0; - if (voice >= chan->setup.format.voices) - return 0; - return &chan->voices_setup[voice]; -} - -int snd_pcm_all_voices_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t *setup) -{ - struct snd_pcm_chan *chan; - snd_pcm_voice_setup_t *vs, *v; - unsigned int voice; - int err; - if (!pcm) - return -EFAULT; - if (channel < 0 || channel > 1) - return -EINVAL; - chan = &pcm->chan[channel]; - if (!chan->open || !chan->valid_setup) - return -EBADFD; - vs = calloc(chan->setup.format.voices, sizeof(*setup)); - for (voice = 0, v = vs; voice < chan->setup.format.voices; ++voice, ++v) { - v->voice = voice; - err = snd_pcm_voice_setup(pcm, channel, v); - if (err < 0) { - free(vs); - return err; - } - if (setup) { - memcpy(setup, v, sizeof(*setup)); - setup++; - } - } - chan->voices_setup = vs; - chan->valid_voices_setup = 1; - return 0; -} - int snd_pcm_channel_status(snd_pcm_t *pcm, snd_pcm_channel_status_t *status) { if (!pcm || !status) diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index 5f3ce7b4..5f7f709b 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -82,8 +82,10 @@ struct snd_pcm_chan { int mode; int valid_setup; snd_pcm_channel_setup_t setup; - int valid_voices_setup; - snd_pcm_voice_setup_t *voices_setup; + snd_pcm_voice_area_t *voices; + size_t sample_width; + size_t bits_per_sample; + size_t samples_per_frag; snd_pcm_mmap_control_t *mmap_control; size_t mmap_control_size; int mmap_control_emulation; @@ -141,13 +143,8 @@ void snd_pcm_plug_buf_unlock(snd_pcm_t *pcm, int channel, void *ptr); #define ROUTE_PLUGIN_RESOLUTION 16 int getput_index(int format); -int copy_index(int format); int conv_index(int src_format, int dst_format); -void snd_pcm_plugin_silence_voice(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_voice_t *dst_voice, - size_t samples); - #ifdef PLUGIN_DEBUG #define pdprintf( args... ) fprintf(stderr, "plugin: " ##args) #else diff --git a/src/pcm/pcm_mmap.c b/src/pcm/pcm_mmap.c index f8ad2ebb..cdef4a92 100644 --- a/src/pcm/pcm_mmap.c +++ b/src/pcm/pcm_mmap.c @@ -31,8 +31,8 @@ static void snd_pcm_mmap_clear(snd_pcm_t *pcm, int channel) struct snd_pcm_chan *chan = &pcm->chan[channel]; chan->mmap_control->frag_io = 0; chan->mmap_control->frag_data = 0; - chan->mmap_control->pos_io = 0; - chan->mmap_control->pos_data = 0; + chan->mmap_control->byte_io = 0; + chan->mmap_control->byte_data = 0; } void snd_pcm_mmap_status_change(snd_pcm_t *pcm, int channel, int newstatus) @@ -61,39 +61,69 @@ void snd_pcm_mmap_status_change(snd_pcm_t *pcm, int channel, int newstatus) } } -static ssize_t snd_pcm_mmap_playback_frags_used(snd_pcm_t *pcm) +static inline ssize_t snd_pcm_mmap_playback_frags_used(struct snd_pcm_chan *chan) { - struct snd_pcm_chan *chan; ssize_t frags_used; - chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; frags_used = chan->mmap_control->frag_data - chan->mmap_control->frag_io; if (frags_used < (ssize_t)(chan->setup.frags - chan->setup.frag_boundary)) frags_used += chan->setup.frag_boundary; return frags_used; } -static size_t snd_pcm_mmap_capture_frags_used(snd_pcm_t *pcm) +static inline ssize_t snd_pcm_mmap_playback_bytes_used(struct snd_pcm_chan *chan) +{ + ssize_t bytes_used; + bytes_used = chan->mmap_control->byte_data - chan->mmap_control->byte_io; + if (bytes_used < (ssize_t)(chan->setup.buffer_size - chan->setup.byte_boundary)) + bytes_used += chan->setup.byte_boundary; + return bytes_used; +} + +static ssize_t snd_pcm_mmap_playback_samples_used(snd_pcm_t *pcm) { struct snd_pcm_chan *chan; + chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; + if (chan->setup.mode == SND_PCM_MODE_BLOCK) { + ssize_t frags = snd_pcm_mmap_playback_frags_used(chan); + return frags * chan->samples_per_frag; + } else { + ssize_t bytes = snd_pcm_mmap_playback_bytes_used(chan); + return bytes * 8 / chan->bits_per_sample; + } +} + +static inline size_t snd_pcm_mmap_capture_frags_used(struct snd_pcm_chan *chan) +{ ssize_t frags_used; - chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; frags_used = chan->mmap_control->frag_io - chan->mmap_control->frag_data; if (frags_used < 0) frags_used += chan->setup.frag_boundary; return frags_used; } -static size_t snd_pcm_mmap_playback_frags_free(snd_pcm_t *pcm) +static inline size_t snd_pcm_mmap_capture_bytes_used(struct snd_pcm_chan *chan) { - return pcm->chan[SND_PCM_CHANNEL_PLAYBACK].setup.frags - snd_pcm_mmap_playback_frags_used(pcm); + ssize_t bytes_used; + bytes_used = chan->mmap_control->byte_io - chan->mmap_control->byte_data; + if (bytes_used < 0) + bytes_used += chan->setup.byte_boundary; + return bytes_used; } -static ssize_t snd_pcm_mmap_capture_frags_free(snd_pcm_t *pcm) +static size_t snd_pcm_mmap_capture_samples_used(snd_pcm_t *pcm) { - return pcm->chan[SND_PCM_CHANNEL_CAPTURE].setup.frags - snd_pcm_mmap_capture_frags_used(pcm); + struct snd_pcm_chan *chan; + chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; + if (chan->setup.mode == SND_PCM_MODE_BLOCK) { + size_t frags = snd_pcm_mmap_capture_frags_used(chan); + return frags * chan->samples_per_frag; + } else { + size_t bytes = snd_pcm_mmap_capture_bytes_used(chan); + return bytes * 8 / chan->bits_per_sample; + } } -int snd_pcm_mmap_frags_used(snd_pcm_t *pcm, int channel, ssize_t *frags) +int snd_pcm_mmap_samples_used(snd_pcm_t *pcm, int channel, ssize_t *samples) { struct snd_pcm_chan *chan; if (!pcm) @@ -104,79 +134,60 @@ int snd_pcm_mmap_frags_used(snd_pcm_t *pcm, int channel, ssize_t *frags) if (!chan->open || !chan->mmap_control) return -EBADFD; if (channel == SND_PCM_CHANNEL_PLAYBACK) - *frags = snd_pcm_mmap_playback_frags_used(pcm); + *samples = snd_pcm_mmap_playback_samples_used(pcm); else - *frags = snd_pcm_mmap_capture_frags_used(pcm); + *samples = snd_pcm_mmap_capture_samples_used(pcm); return 0; } -int snd_pcm_mmap_frags_free(snd_pcm_t *pcm, int channel, ssize_t *frags) +static inline size_t snd_pcm_mmap_playback_frags_free(struct snd_pcm_chan *chan) { - struct snd_pcm_chan *chan; - if (!pcm) - return -EFAULT; - if (channel < 0 || channel > 1) - return -EINVAL; - chan = &pcm->chan[channel]; - if (!chan->open || !chan->mmap_control) - return -EBADFD; - if (channel == SND_PCM_CHANNEL_PLAYBACK) - *frags = snd_pcm_mmap_playback_frags_free(pcm); - else - *frags = snd_pcm_mmap_capture_frags_free(pcm); - return 0; + return chan->setup.frags - snd_pcm_mmap_playback_frags_used(chan); } -static ssize_t snd_pcm_mmap_playback_bytes_used(snd_pcm_t *pcm) +static inline size_t snd_pcm_mmap_playback_bytes_free(struct snd_pcm_chan *chan) { - struct snd_pcm_chan *chan; - ssize_t bytes_used; - chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; - bytes_used = chan->mmap_control->pos_data - chan->mmap_control->pos_io; - if (bytes_used < (ssize_t)(chan->setup.buffer_size - chan->setup.pos_boundary)) - bytes_used += chan->setup.pos_boundary; - return bytes_used; + return chan->setup.buffer_size - snd_pcm_mmap_playback_bytes_used(chan); } -static size_t snd_pcm_mmap_capture_bytes_used(snd_pcm_t *pcm) +static size_t snd_pcm_mmap_playback_samples_free(snd_pcm_t *pcm) { struct snd_pcm_chan *chan; - ssize_t bytes_used; - chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; - bytes_used = chan->mmap_control->pos_io - chan->mmap_control->pos_data; - if (bytes_used < 0) - bytes_used += chan->setup.pos_boundary; - return bytes_used; + chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; + if (chan->setup.mode == SND_PCM_MODE_BLOCK) { + size_t frags = snd_pcm_mmap_playback_frags_free(chan); + return frags * chan->samples_per_frag; + } else { + size_t bytes = snd_pcm_mmap_playback_bytes_free(chan); + return bytes * 8 / chan->bits_per_sample; + } } -int snd_pcm_mmap_bytes_used(snd_pcm_t *pcm, int channel, ssize_t *frags) +static inline ssize_t snd_pcm_mmap_capture_frags_free(struct snd_pcm_chan *chan) { - struct snd_pcm_chan *chan; - if (!pcm) - return -EFAULT; - if (channel < 0 || channel > 1) - return -EINVAL; - chan = &pcm->chan[channel]; - if (!chan->open || !chan->mmap_control) - return -EBADFD; - if (channel == SND_PCM_CHANNEL_PLAYBACK) - *frags = snd_pcm_mmap_playback_bytes_used(pcm); - else - *frags = snd_pcm_mmap_capture_bytes_used(pcm); - return 0; + return chan->setup.frags - snd_pcm_mmap_capture_frags_used(chan); } -static size_t snd_pcm_mmap_playback_bytes_free(snd_pcm_t *pcm) + +static inline ssize_t snd_pcm_mmap_capture_bytes_free(struct snd_pcm_chan *chan) { - return pcm->chan[SND_PCM_CHANNEL_PLAYBACK].setup.buffer_size - snd_pcm_mmap_playback_bytes_used(pcm); + return chan->setup.buffer_size - snd_pcm_mmap_capture_bytes_used(chan); } -static ssize_t snd_pcm_mmap_capture_bytes_free(snd_pcm_t *pcm) +static ssize_t snd_pcm_mmap_capture_samples_free(snd_pcm_t *pcm) { - return pcm->chan[SND_PCM_CHANNEL_CAPTURE].setup.buffer_size - snd_pcm_mmap_capture_bytes_used(pcm); + struct snd_pcm_chan *chan; + chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; + if (chan->setup.mode == SND_PCM_MODE_BLOCK) { + ssize_t frags = snd_pcm_mmap_capture_frags_free(chan); + return frags * chan->samples_per_frag; + } else { + ssize_t bytes = snd_pcm_mmap_capture_bytes_free(chan); + return bytes * 8 / chan->bits_per_sample; + } } -int snd_pcm_mmap_bytes_free(snd_pcm_t *pcm, int channel, ssize_t *frags) +int snd_pcm_mmap_samples_free(snd_pcm_t *pcm, int channel, ssize_t *samples) { struct snd_pcm_chan *chan; if (!pcm) @@ -187,9 +198,9 @@ int snd_pcm_mmap_bytes_free(snd_pcm_t *pcm, int channel, ssize_t *frags) if (!chan->open || !chan->mmap_control) return -EBADFD; if (channel == SND_PCM_CHANNEL_PLAYBACK) - *frags = snd_pcm_mmap_playback_bytes_free(pcm); + *samples = snd_pcm_mmap_playback_samples_free(pcm); else - *frags = snd_pcm_mmap_capture_bytes_free(pcm); + *samples = snd_pcm_mmap_capture_samples_free(pcm); return 0; } @@ -200,9 +211,9 @@ static int snd_pcm_mmap_playback_ready(snd_pcm_t *pcm) if (chan->mmap_control->status == SND_PCM_STATUS_XRUN) return -EPIPE; if (chan->setup.mode == SND_PCM_MODE_BLOCK) { - return (chan->setup.frags - snd_pcm_mmap_playback_frags_used(pcm)) >= chan->setup.buf.block.frags_min; + return (chan->setup.frags - snd_pcm_mmap_playback_frags_used(chan)) >= chan->setup.buf.block.frags_min; } else { - return (chan->setup.buffer_size - snd_pcm_mmap_playback_bytes_used(pcm)) >= chan->setup.buf.stream.bytes_min; + return (chan->setup.buffer_size - snd_pcm_mmap_playback_bytes_used(chan)) >= chan->setup.buf.stream.bytes_min; } } @@ -217,10 +228,10 @@ static int snd_pcm_mmap_capture_ready(snd_pcm_t *pcm) return -EPIPE; } if (chan->setup.mode == SND_PCM_MODE_BLOCK) { - if (snd_pcm_mmap_capture_frags_used(pcm) >= chan->setup.buf.block.frags_min) + if (snd_pcm_mmap_capture_frags_used(chan) >= chan->setup.buf.block.frags_min) return 1; } else { - if (snd_pcm_mmap_capture_bytes_used(pcm) >= chan->setup.buf.stream.bytes_min) + if (snd_pcm_mmap_capture_bytes_used(chan) >= chan->setup.buf.stream.bytes_min) return 1; } return ret; @@ -247,53 +258,183 @@ int snd_pcm_mmap_ready(snd_pcm_t *pcm, int channel) } } -/* Bytes transferrable */ -static size_t snd_pcm_mmap_bytes_playback(snd_pcm_t *pcm, size_t size) +static size_t snd_pcm_mmap_playback_frags_xfer(snd_pcm_t *pcm, size_t frags) { struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; snd_pcm_mmap_control_t *ctrl = chan->mmap_control; - size_t bytes_cont, bytes_free; - unsigned int pos_data = ctrl->pos_data; - unsigned int pos_io = ctrl->pos_io; - int bytes_used = pos_data - pos_io; - if (bytes_used < -(int)(chan->setup.buf.stream.bytes_xrun_max + chan->setup.frag_size)) - bytes_used += chan->setup.pos_boundary; - bytes_cont = chan->setup.buffer_size - (pos_data % chan->setup.buffer_size); - if (bytes_cont < size) - size = bytes_cont; - bytes_free = chan->setup.buffer_size - bytes_used; - if (bytes_free < size) - size = (bytes_free / chan->setup.buf.stream.bytes_align) * chan->setup.buf.stream.bytes_align; - return size; + size_t frags_cont; + size_t frag_data = ctrl->frag_data; + size_t frags_free = snd_pcm_mmap_playback_frags_free(chan); + if (frags_free < frags) + frags = frags_free; + frags_cont = chan->setup.frags - (frag_data % chan->setup.frags); + if (frags_cont < frags) + frags = frags_cont; + return frags; } -/* Bytes transferrable */ -static size_t snd_pcm_mmap_bytes_capture(snd_pcm_t *pcm, size_t size) +static size_t snd_pcm_mmap_capture_frags_xfer(snd_pcm_t *pcm, size_t frags) { struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; snd_pcm_mmap_control_t *ctrl = chan->mmap_control; + size_t frags_cont; + size_t frag_data = ctrl->frag_data; + size_t frags_used = snd_pcm_mmap_capture_frags_used(chan); + if (frags_used < frags) + frags = frags_used; + frags_cont = chan->setup.frags - (frag_data % chan->setup.frags); + if (frags_cont < frags) + frags = frags_cont; + return frags; +} + +static size_t snd_pcm_mmap_playback_bytes_xfer(snd_pcm_t *pcm, size_t bytes) +{ + struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; + snd_pcm_mmap_control_t *ctrl = chan->mmap_control; size_t bytes_cont; - unsigned int pos_data = ctrl->pos_data; - unsigned int pos_io = ctrl->pos_io; - int bytes_used = pos_io - pos_data; - if (bytes_used < 0) - bytes_used += chan->setup.pos_boundary; - bytes_cont = chan->setup.buffer_size - (pos_data % chan->setup.buffer_size); - if (bytes_cont < size) - size = bytes_cont; - if ((size_t) bytes_used < size) - size = (bytes_used / chan->setup.buf.stream.bytes_align) * chan->setup.buf.stream.bytes_align; - return size; + size_t byte_data = ctrl->byte_data; + size_t bytes_free = snd_pcm_mmap_playback_bytes_free(chan); + if (bytes_free < bytes) + bytes = bytes_free; + bytes_cont = chan->setup.buffer_size - (byte_data % chan->setup.buffer_size); + if (bytes_cont < bytes) + bytes = bytes_cont; + bytes -= bytes % chan->setup.buf.stream.bytes_align; + return bytes; +} + +static size_t snd_pcm_mmap_capture_bytes_xfer(snd_pcm_t *pcm, size_t bytes) +{ + struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; + snd_pcm_mmap_control_t *ctrl = chan->mmap_control; + size_t bytes_cont; + size_t byte_data = ctrl->byte_data; + size_t bytes_used = snd_pcm_mmap_capture_bytes_used(chan); + if (bytes_used < bytes) + bytes = bytes_used; + bytes_cont = chan->setup.buffer_size - (byte_data % chan->setup.buffer_size); + if (bytes_cont < bytes) + bytes = bytes_cont; + bytes -= bytes % chan->setup.buf.stream.bytes_align; + return bytes; } -typedef int (*transfer_f)(snd_pcm_t *pcm, size_t hwoff, void *data, size_t off, size_t size); +static ssize_t snd_pcm_mmap_playback_samples_xfer(snd_pcm_t *pcm, size_t samples) +{ + struct snd_pcm_chan *chan; + chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; + if (chan->setup.mode == SND_PCM_MODE_BLOCK) { + size_t frags = samples / chan->samples_per_frag; + frags = snd_pcm_mmap_playback_frags_xfer(pcm, frags); + return frags * chan->samples_per_frag; + } else { + size_t bytes = samples * chan->bits_per_sample / 8; + bytes = snd_pcm_mmap_playback_bytes_xfer(pcm, bytes); + return bytes * 8 / chan->bits_per_sample; + } +} + +static ssize_t snd_pcm_mmap_capture_samples_xfer(snd_pcm_t *pcm, size_t samples) +{ + struct snd_pcm_chan *chan; + chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; + if (chan->setup.mode == SND_PCM_MODE_BLOCK) { + size_t frags = samples / chan->samples_per_frag; + frags = snd_pcm_mmap_capture_frags_xfer(pcm, frags); + return frags * chan->samples_per_frag; + } else { + size_t bytes = samples * chan->bits_per_sample / 8; + bytes = snd_pcm_mmap_capture_bytes_xfer(pcm, bytes); + return bytes * 8 / chan->bits_per_sample; + } +} + +ssize_t snd_pcm_mmap_samples_xfer(snd_pcm_t *pcm, int channel, size_t samples) +{ + struct snd_pcm_chan *chan; + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) + return -EINVAL; + chan = &pcm->chan[channel]; + if (!chan->open || !chan->mmap_control) + return -EBADFD; + if (channel == SND_PCM_CHANNEL_PLAYBACK) + return snd_pcm_mmap_playback_samples_xfer(pcm, samples); + else + return snd_pcm_mmap_capture_samples_xfer(pcm, samples); +} + +ssize_t snd_pcm_mmap_samples_offset(snd_pcm_t *pcm, int channel) +{ + struct snd_pcm_chan *chan; + snd_pcm_mmap_control_t *ctrl; + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) + return -EINVAL; + chan = &pcm->chan[channel]; + if (!chan->open) + return -EBADFD; + ctrl = chan->mmap_control; + if (!ctrl) + return -EBADFD; + if (chan->setup.mode == SND_PCM_MODE_BLOCK) + return (ctrl->frag_data % chan->setup.frags) * chan->samples_per_frag; + else + return (ctrl->byte_data % chan->setup.buffer_size) * 8 / chan->bits_per_sample; +} +int snd_pcm_mmap_commit_samples(snd_pcm_t *pcm, int channel, int samples) +{ + struct snd_pcm_chan *chan; + snd_pcm_mmap_control_t *ctrl; + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) + return -EINVAL; + chan = &pcm->chan[channel]; + if (!chan->open) + return -EBADFD; + ctrl = chan->mmap_control; + if (!ctrl) + return -EBADFD; + if (chan->setup.mode == SND_PCM_MODE_BLOCK) { + size_t frag_data, frags; + if (samples % chan->samples_per_frag) + return -EINVAL; + frags = samples / chan->samples_per_frag; + frag_data = ctrl->frag_data + frags; + if (frag_data == chan->setup.frag_boundary) { + ctrl->frag_data = 0; + ctrl->byte_data = 0; + } else { + ctrl->frag_data = frag_data; + ctrl->byte_data = frag_data * chan->setup.frag_size; + } + } else { + size_t byte_data; + size_t bytes = samples * chan->bits_per_sample; + if (bytes % 8) + return -EINVAL; + bytes /= 8; + byte_data = ctrl->byte_data + bytes; + if (byte_data == chan->setup.byte_boundary) { + ctrl->byte_data = 0; + ctrl->frag_data = 0; + } else { + ctrl->byte_data = byte_data; + ctrl->frag_data = byte_data / chan->setup.frag_size; + } + } + return 0; +} -static ssize_t snd_pcm_mmap_write1(snd_pcm_t *pcm, const void *data, size_t count, transfer_f transfer) +ssize_t snd_pcm_mmap_write_areas(snd_pcm_t *pcm, snd_pcm_voice_area_t *voices, size_t samples) { struct snd_pcm_chan *chan; snd_pcm_mmap_control_t *ctrl; - size_t frag_size; size_t offset = 0; size_t result = 0; int err; @@ -302,25 +443,17 @@ static ssize_t snd_pcm_mmap_write1(snd_pcm_t *pcm, const void *data, size_t coun ctrl = chan->mmap_control; if (ctrl->status < SND_PCM_STATUS_PREPARED) return -EBADFD; - frag_size = chan->setup.frag_size; if (chan->setup.mode == SND_PCM_MODE_BLOCK) { - if (count % frag_size != 0) + if (samples % chan->samples_per_frag != 0) return -EINVAL; } else { - int tmp = snd_pcm_format_size(chan->setup.format.format, chan->setup.format.voices); - if (tmp > 0) { - int r = count % tmp; - if (r > 0) { - count -= r; - if (count == 0) - return -EINVAL; - } - } + if (ctrl->status == SND_PCM_STATUS_RUNNING && + chan->mode & SND_PCM_NONBLOCK) + snd_pcm_channel_update(pcm, SND_PCM_CHANNEL_PLAYBACK); } - if (chan->mode & SND_PCM_NONBLOCK) - snd_pcm_channel_update(pcm, SND_PCM_CHANNEL_PLAYBACK); - while (count > 0) { - size_t bytes; + while (samples > 0) { + ssize_t mmap_offset; + size_t samples1; int ready = snd_pcm_mmap_playback_ready(pcm); if (ready < 0) return ready; @@ -339,221 +472,148 @@ static ssize_t snd_pcm_mmap_write1(snd_pcm_t *pcm, const void *data, size_t coun return result > 0 ? result : -EPIPE; assert(snd_pcm_mmap_playback_ready(pcm)); } - if (chan->setup.mode == SND_PCM_MODE_BLOCK) { - size_t frag_data, frag; - frag_data = ctrl->frag_data; - frag = frag_data % chan->setup.frags; - err = transfer(pcm, frag_size * frag, (void *) data, offset, frag_size); - if (err < 0) - return result > 0 ? result : err; - if (ctrl->status == SND_PCM_STATUS_XRUN) - return result > 0 ? result : -EPIPE; - frag_data++; - if (frag_data == chan->setup.frag_boundary) { - ctrl->frag_data = 0; - ctrl->pos_data = 0; - } else { - ctrl->frag_data = frag_data; - ctrl->pos_data += frag_size; - } - bytes = frag_size; - } else { - size_t pos_data; - bytes = snd_pcm_mmap_bytes_playback(pcm, count); - pos_data = ctrl->pos_data; - err = transfer(pcm, pos_data % chan->setup.buffer_size, (void *) data, offset, bytes); - if (err < 0) + samples1 = snd_pcm_mmap_playback_samples_xfer(pcm, samples); + assert(samples1 > 0); + mmap_offset = snd_pcm_mmap_samples_offset(pcm, SND_PCM_CHANNEL_PLAYBACK); + snd_pcm_areas_copy(voices, offset, chan->voices, mmap_offset, chan->setup.format.voices, samples1, chan->setup.format.format); + if (ctrl->status == SND_PCM_STATUS_XRUN) + return result > 0 ? result : -EPIPE; + snd_pcm_mmap_commit_samples(pcm, SND_PCM_CHANNEL_PLAYBACK, samples1); + samples -= samples1; + offset += samples1; + result += samples1; + if (ctrl->status == SND_PCM_STATUS_PREPARED && + (chan->setup.start_mode == SND_PCM_START_DATA || + (chan->setup.start_mode == SND_PCM_START_FULL && + !snd_pcm_mmap_playback_ready(pcm)))) { + err = snd_pcm_channel_go(pcm, SND_PCM_CHANNEL_PLAYBACK); + if (err < 0) return result > 0 ? result : err; - if (ctrl->status == SND_PCM_STATUS_XRUN) - return result > 0 ? result : -EPIPE; - pos_data += bytes; - if (pos_data == chan->setup.pos_boundary) { - ctrl->pos_data = 0; - ctrl->frag_data = 0; - } else { - ctrl->pos_data = pos_data; - ctrl->frag_data = pos_data / chan->setup.frags; - } } - offset += bytes; - count -= bytes; - result += bytes; - } - - if (ctrl->status == SND_PCM_STATUS_PREPARED && - (chan->setup.start_mode == SND_PCM_START_DATA || - (chan->setup.start_mode == SND_PCM_START_FULL && - !snd_pcm_mmap_playback_ready(pcm)))) { - err = snd_pcm_channel_go(pcm, SND_PCM_CHANNEL_PLAYBACK); - if (err < 0) - return result > 0 ? result : err; } return result; } -static int transfer_write(snd_pcm_t *pcm, size_t hwoff, void* data, size_t off, size_t size) +ssize_t snd_pcm_mmap_write_samples(snd_pcm_t *pcm, const void *buffer, size_t samples) { - struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; - const char *buf = data; - unsigned int v, voices; -#define COPY_LABELS -#include "plugin/plugin_ops.h" -#undef COPY_LABELS - void *copy; - snd_pcm_voice_setup_t *vsetup; - int idx; - size_t vsize, ssize; - idx = copy_index(chan->setup.format.format); - if (idx < 0) - return idx; - copy = copy_labels[idx]; - voices = chan->setup.format.voices; - vsetup = chan->voices_setup; - vsize = snd_pcm_format_size(chan->setup.format.format, 1); - ssize = vsize * chan->setup.format.voices; - hwoff /= ssize; - size /= ssize; - for (v = 0; v < voices; ++v, ++vsetup) { - const char *src; - char *dst; - size_t dst_step; - size_t samples = size; - if (vsetup->first % 8 != 0 || - vsetup->step % 8 != 0) - return -EINVAL; - src = buf + off + v * vsize; - dst_step = vsetup->step / 8; - dst = chan->mmap_data + vsetup->addr + (vsetup->first + vsetup->step * hwoff) / 8; - while (samples-- > 0) { - goto *copy; -#define COPY_END after -#include "plugin/plugin_ops.h" -#undef COPY_END - after: - src += ssize; - dst += dst_step; + struct snd_pcm_chan *chan; + unsigned int nvoices; + if (!pcm) + return -EFAULT; + chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; + if (!chan->open || !chan->valid_setup) + return -EBADFD; + if (!chan->mmap_data || !chan->mmap_control) + return -EBADFD; + if (samples > 0 && !buffer) + return -EFAULT; + nvoices = chan->setup.format.voices; + if (!chan->setup.format.interleave && nvoices > 1) + return -EINVAL; + { + snd_pcm_voice_area_t voices[nvoices]; + unsigned int voice; + for (voice = 0; voice < nvoices; ++voice) { + voices[voice].addr = (char*)buffer; + voices[voice].first = chan->sample_width * voice; + voices[voice].step = chan->bits_per_sample; } + return snd_pcm_mmap_write_areas(pcm, voices, samples); } - return 0; } -ssize_t snd_pcm_mmap_write(snd_pcm_t *pcm, const void *buffer, size_t count) +ssize_t snd_pcm_mmap_write(snd_pcm_t *pcm, const void *buffer, size_t bytes) { struct snd_pcm_chan *chan; + unsigned int nvoices; + ssize_t samples; if (!pcm) return -EFAULT; chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; - if (!chan->open || !chan->valid_setup || !chan->valid_voices_setup) + if (!chan->open || !chan->valid_setup) return -EBADFD; if (!chan->mmap_data || !chan->mmap_control) return -EBADFD; - if (count > 0 && !buffer) + if (bytes > 0 && !buffer) return -EFAULT; - if (!chan->setup.format.interleave && chan->setup.format.voices > 1) + nvoices = chan->setup.format.voices; + if (!chan->setup.format.interleave && nvoices > 1) return -EINVAL; - return snd_pcm_mmap_write1(pcm, buffer, count, transfer_write); -} - -static int transfer_writev(snd_pcm_t *pcm, size_t hwoff, void* data, size_t off, size_t size) -{ - struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; - struct iovec *vec = data; - unsigned int v, voices; -#define COPY_LABELS -#include "plugin/plugin_ops.h" -#undef COPY_LABELS - void *copy; - snd_pcm_voice_setup_t *vsetup; - int idx; - size_t ssize; - idx = copy_index(chan->setup.format.format); - if (idx < 0) - return idx; - copy = copy_labels[idx]; - voices = chan->setup.format.voices; - vsetup = chan->voices_setup; - ssize = snd_pcm_format_size(chan->setup.format.format, chan->setup.format.voices); - hwoff /= ssize; - size /= ssize; - off /= voices; - for (v = 0; v < voices; ++v, ++vsetup, ++vec) { - const char *src; - char *dst; - size_t dst_step; - size_t samples = size; - if (vsetup->first % 8 != 0 || - vsetup->step % 8 != 0) - return -EINVAL; - src = vec->iov_base + off; - dst_step = vsetup->step / 8; - dst = chan->mmap_data + vsetup->addr + (vsetup->first + vsetup->step * hwoff) / 8; - while (samples-- > 0) { - goto *copy; -#define COPY_END after -#include "plugin/plugin_ops.h" -#undef COPY_END - after: - src += ssize; - dst += dst_step; - } - } - return 0; + samples = bytes * 8 / chan->bits_per_sample; + samples = snd_pcm_mmap_write_samples(pcm, buffer, samples); + if (samples <= 0) + return samples; + return samples * chan->bits_per_sample / 8; } ssize_t snd_pcm_mmap_writev(snd_pcm_t *pcm, const struct iovec *vector, unsigned long vcount) { struct snd_pcm_chan *chan; size_t result = 0; - unsigned int b; + unsigned int nvoices; if (!pcm) return -EFAULT; chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; - if (!chan->open || !chan->valid_setup || !chan->valid_voices_setup) + if (!chan->open || !chan->valid_setup) return -EBADFD; if (!chan->mmap_data || !chan->mmap_control) return -EBADFD; if (vcount > 0 && !vector) return -EFAULT; + nvoices = chan->setup.format.voices; if (chan->setup.format.interleave) { + unsigned int b; for (b = 0; b < vcount; b++) { - int ret; - ret = snd_pcm_mmap_write1(pcm, vector[b].iov_base, vector[b].iov_len, transfer_write); - if (ret < 0) - return result > 0 ? result : ret; + ssize_t ret; + size_t samples = vector[b].iov_len * 8 / chan->bits_per_sample; + ret = snd_pcm_mmap_write_samples(pcm, vector[b].iov_base, samples); + if (ret < 0) { + if (result <= 0) + return ret; + break; + } result += ret; } } else { - unsigned int voices = chan->setup.format.voices; + snd_pcm_voice_area_t voices[nvoices]; unsigned long bcount; - if (vcount % voices) + unsigned int b; + if (vcount % nvoices) return -EINVAL; - bcount = vcount / voices; + bcount = vcount / nvoices; for (b = 0; b < bcount; b++) { unsigned int v; - int ret; - size_t count = 0; - count = vector[0].iov_len; - for (v = 0; v < voices; ++v) { - if (vector[v].iov_len != count) + ssize_t ret; + size_t bytes = 0; + size_t samples; + bytes = vector[0].iov_len; + for (v = 0; v < nvoices; ++v) { + if (vector[v].iov_len != bytes) return -EINVAL; + voices[v].addr = vector[v].iov_base; + voices[v].first = 0; + voices[v].step = chan->sample_width; + } + samples = bytes * 8 / chan->sample_width; + ret = snd_pcm_mmap_write_areas(pcm, voices, samples); + if (ret < 0) { + if (result <= 0) + return ret; + break; } - ret = snd_pcm_mmap_write1(pcm, vector, count * voices, transfer_writev); - if (ret < 0) - return result > 0 ? result : ret; result += ret; - if ((size_t)ret != count * voices) + if ((size_t)ret != samples) break; - vector += voices; + vector += nvoices; } } - return result; + return result * chan->bits_per_sample / 8; } -static ssize_t snd_pcm_mmap_read1(snd_pcm_t *pcm, void *data, size_t count, transfer_f transfer) +ssize_t snd_pcm_mmap_read_areas(snd_pcm_t *pcm, snd_pcm_voice_area_t *voices, size_t samples) { struct snd_pcm_chan *chan; snd_pcm_mmap_control_t *ctrl; - size_t frag_size; size_t offset = 0; size_t result = 0; int err; @@ -562,20 +622,13 @@ static ssize_t snd_pcm_mmap_read1(snd_pcm_t *pcm, void *data, size_t count, tran ctrl = chan->mmap_control; if (ctrl->status < SND_PCM_STATUS_PREPARED) return -EBADFD; - frag_size = chan->setup.frag_size; if (chan->setup.mode == SND_PCM_MODE_BLOCK) { - if (count % frag_size != 0) + if (samples % chan->samples_per_frag != 0) return -EINVAL; } else { - int tmp = snd_pcm_format_size(chan->setup.format.format, chan->setup.format.voices); - if (tmp > 0) { - int r = count % tmp; - if (r > 0) { - count -= r; - if (count == 0) - return -EINVAL; - } - } + if (ctrl->status == SND_PCM_STATUS_RUNNING && + chan->mode & SND_PCM_NONBLOCK) + snd_pcm_channel_update(pcm, SND_PCM_CHANNEL_CAPTURE); } if (ctrl->status == SND_PCM_STATUS_PREPARED && chan->setup.start_mode == SND_PCM_START_DATA) { @@ -583,10 +636,9 @@ static ssize_t snd_pcm_mmap_read1(snd_pcm_t *pcm, void *data, size_t count, tran if (err < 0) return err; } - if (chan->mode & SND_PCM_NONBLOCK) - snd_pcm_channel_update(pcm, SND_PCM_CHANNEL_CAPTURE); - while (count > 0) { - size_t bytes; + while (samples > 0) { + ssize_t mmap_offset; + size_t samples1; int ready = snd_pcm_mmap_capture_ready(pcm); if (ready < 0) return ready; @@ -605,208 +657,135 @@ static ssize_t snd_pcm_mmap_read1(snd_pcm_t *pcm, void *data, size_t count, tran return result > 0 ? result : -EPIPE; assert(snd_pcm_mmap_capture_ready(pcm)); } - if (chan->setup.mode == SND_PCM_MODE_BLOCK) { - size_t frag_data, frag; - frag_data = ctrl->frag_data; - frag = frag_data % chan->setup.frags; - err = transfer(pcm, frag_size * frag, data, offset, frag_size); - if (err < 0) - return result > 0 ? result : err; - if (ctrl->status == SND_PCM_STATUS_XRUN && - chan->setup.xrun_mode == SND_PCM_XRUN_DRAIN) - return result > 0 ? result : -EPIPE; - frag_data++; - if (frag_data == chan->setup.frag_boundary) { - ctrl->frag_data = 0; - ctrl->pos_data = 0; - } else { - ctrl->frag_data = frag_data; - ctrl->pos_data += frag_size; - } - bytes = frag_size; - } else { - size_t pos_data; - bytes = snd_pcm_mmap_bytes_capture(pcm, count); - pos_data = ctrl->pos_data; - err = transfer(pcm, pos_data % chan->setup.buffer_size, data, offset, bytes); - if (err < 0) - return result > 0 ? result : err; - if (ctrl->status == SND_PCM_STATUS_XRUN && - chan->setup.xrun_mode == SND_PCM_XRUN_DRAIN) - return result > 0 ? result : -EPIPE; - pos_data += bytes; - if (pos_data == chan->setup.pos_boundary) { - ctrl->pos_data = 0; - ctrl->frag_data = 0; - } else { - ctrl->pos_data = pos_data; - ctrl->frag_data = pos_data / chan->setup.frags; - } - } - offset += bytes; - count -= bytes; - result += bytes; + samples1 = snd_pcm_mmap_capture_samples_xfer(pcm, samples); + assert(samples1 > 0); + mmap_offset = snd_pcm_mmap_samples_offset(pcm, SND_PCM_CHANNEL_CAPTURE); + snd_pcm_areas_copy(chan->voices, mmap_offset, voices, offset, chan->setup.format.voices, samples1, chan->setup.format.format); + if (ctrl->status == SND_PCM_STATUS_XRUN && + chan->setup.xrun_mode == SND_PCM_XRUN_DRAIN) + return result > 0 ? result : -EPIPE; + snd_pcm_mmap_commit_samples(pcm, SND_PCM_CHANNEL_CAPTURE, samples1); + samples -= samples1; + offset += samples1; + result += samples1; } - return result; } -static int transfer_read(snd_pcm_t *pcm, size_t hwoff, void* data, size_t off, size_t size) +ssize_t snd_pcm_mmap_read_samples(snd_pcm_t *pcm, const void *buffer, size_t samples) { - struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; - char *buf = data; - unsigned int v, voices; -#define COPY_LABELS -#include "plugin/plugin_ops.h" -#undef COPY_LABELS - void *copy; - snd_pcm_voice_setup_t *vsetup; - int idx; - size_t vsize, ssize; - idx = copy_index(chan->setup.format.format); - if (idx < 0) - return idx; - copy = copy_labels[idx]; - voices = chan->setup.format.voices; - vsetup = chan->voices_setup; - vsize = snd_pcm_format_size(chan->setup.format.format, 1); - ssize = vsize * chan->setup.format.voices; - hwoff /= ssize; - size /= ssize; - for (v = 0; v < voices; ++v, ++vsetup) { - const char *src; - size_t src_step; - char *dst; - size_t samples = size; - if (vsetup->first % 8 != 0 || - vsetup->step % 8 != 0) - return -EINVAL; - src_step = vsetup->step / 8; - src = chan->mmap_data + vsetup->addr + (vsetup->first + vsetup->step * hwoff) / 8; - dst = buf + off + v * vsize; - while (samples-- > 0) { - goto *copy; -#define COPY_END after -#include "plugin/plugin_ops.h" -#undef COPY_END - after: - src += src_step; - dst += ssize; + struct snd_pcm_chan *chan; + unsigned int nvoices; + if (!pcm) + return -EFAULT; + chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; + if (!chan->open || !chan->valid_setup) + return -EBADFD; + if (!chan->mmap_data || !chan->mmap_control) + return -EBADFD; + if (samples > 0 && !buffer) + return -EFAULT; + nvoices = chan->setup.format.voices; + if (!chan->setup.format.interleave && nvoices > 1) + return -EINVAL; + { + snd_pcm_voice_area_t voices[nvoices]; + unsigned int voice; + for (voice = 0; voice < nvoices; ++voice) { + voices[voice].addr = (char*)buffer; + voices[voice].first = chan->sample_width * voice; + voices[voice].step = chan->bits_per_sample; } + return snd_pcm_mmap_read_areas(pcm, voices, samples); } - return 0; } -ssize_t snd_pcm_mmap_read(snd_pcm_t *pcm, void *buffer, size_t count) +ssize_t snd_pcm_mmap_read(snd_pcm_t *pcm, void *buffer, size_t bytes) { struct snd_pcm_chan *chan; + unsigned int nvoices; + ssize_t samples; if (!pcm) return -EFAULT; chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; - if (!chan->open || !chan->valid_setup || !chan->valid_voices_setup) + if (!chan->open || !chan->valid_setup) return -EBADFD; if (!chan->mmap_data || !chan->mmap_control) return -EBADFD; - if (count > 0 && !buffer) + if (bytes > 0 && !buffer) return -EFAULT; - if (!chan->setup.format.interleave && chan->setup.format.voices > 1) + nvoices = chan->setup.format.voices; + if (!chan->setup.format.interleave && nvoices > 1) return -EINVAL; - return snd_pcm_mmap_read1(pcm, buffer, count, transfer_read); -} - -static int transfer_readv(snd_pcm_t *pcm, size_t hwoff, void* data, size_t off, size_t size) -{ - struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; - struct iovec *vec = data; - unsigned int v, voices; -#define COPY_LABELS -#include "plugin/plugin_ops.h" -#undef COPY_LABELS - void *copy; - snd_pcm_voice_setup_t *vsetup; - int idx; - size_t ssize; - idx = copy_index(chan->setup.format.format); - if (idx < 0) - return idx; - copy = copy_labels[idx]; - voices = chan->setup.format.voices; - vsetup = chan->voices_setup; - ssize = snd_pcm_format_size(chan->setup.format.format, chan->setup.format.voices); - hwoff /= ssize; - size /= ssize; - off /= voices; - for (v = 0; v < voices; ++v, ++vsetup, ++vec) { - const char *src; - size_t src_step; - char *dst; - size_t samples = size; - if (vsetup->first % 8 != 0 || - vsetup->step % 8 != 0) - return -EINVAL; - src_step = vsetup->step / 8; - src = chan->mmap_data + vsetup->addr + (vsetup->first + vsetup->step * hwoff) / 8; - dst = vec->iov_base + off; - while (samples-- > 0) { - goto *copy; -#define COPY_END after -#include "plugin/plugin_ops.h" -#undef COPY_END - after: - src += src_step; - dst += ssize; - } - } - return 0; + samples = bytes * 8 / chan->bits_per_sample; + samples = snd_pcm_mmap_read_samples(pcm, buffer, samples); + if (samples <= 0) + return samples; + return samples * chan->bits_per_sample / 8; } ssize_t snd_pcm_mmap_readv(snd_pcm_t *pcm, const struct iovec *vector, unsigned long vcount) { struct snd_pcm_chan *chan; size_t result = 0; - unsigned int b; + unsigned int nvoices; if (!pcm) return -EFAULT; chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; - if (!chan->open || !chan->valid_setup || !chan->valid_voices_setup) + if (!chan->open || !chan->valid_setup) return -EBADFD; if (!chan->mmap_data || !chan->mmap_control) return -EBADFD; if (vcount > 0 && !vector) return -EFAULT; + nvoices = chan->setup.format.voices; if (chan->setup.format.interleave) { + unsigned int b; for (b = 0; b < vcount; b++) { - int ret; - ret = snd_pcm_mmap_read1(pcm, vector[b].iov_base, vector[b].iov_len, transfer_write); - if (ret < 0) - return result > 0 ? result : ret; + ssize_t ret; + size_t samples = vector[b].iov_len * 8 / chan->bits_per_sample; + ret = snd_pcm_mmap_read_samples(pcm, vector[b].iov_base, samples); + if (ret < 0) { + if (result <= 0) + return ret; + break; + } result += ret; } } else { - unsigned int voices = chan->setup.format.voices; + snd_pcm_voice_area_t voices[nvoices]; unsigned long bcount; - if (vcount % voices) + unsigned int b; + if (vcount % nvoices) return -EINVAL; - bcount = vcount / voices; + bcount = vcount / nvoices; for (b = 0; b < bcount; b++) { unsigned int v; - int ret; - size_t count = 0; - count = vector[0].iov_len; - for (v = 0; v < voices; ++v) { - if (vector[v].iov_len != count) + ssize_t ret; + size_t bytes = 0; + size_t samples; + bytes = vector[0].iov_len; + for (v = 0; v < nvoices; ++v) { + if (vector[v].iov_len != bytes) return -EINVAL; + voices[v].addr = vector[v].iov_base; + voices[v].first = 0; + voices[v].step = chan->sample_width; + } + samples = bytes * 8 / chan->sample_width; + ret = snd_pcm_mmap_read_areas(pcm, voices, samples); + if (ret < 0) { + if (result <= 0) + return ret; + break; } - ret = snd_pcm_mmap_read1(pcm, (void *) vector, count * voices, transfer_readv); - if (ret < 0) - return result > 0 ? result : ret; result += ret; - if ((size_t)ret != count * voices) + if ((size_t)ret != samples) break; - vector += voices; + vector += nvoices; } } - return result; + return result * chan->bits_per_sample / 8; } static void *playback_mmap(void *d) @@ -854,7 +833,7 @@ static void *playback_mmap(void *d) } frag = control->frag_io; - if (snd_pcm_mmap_playback_frags_used(pcm) <= 0) { + if (snd_pcm_mmap_playback_frags_used(chan) <= 0) { fprintf(stderr, "underrun\n"); usleep(10000); continue; @@ -934,7 +913,7 @@ static void *capture_mmap(void *d) } frag = control->frag_io; - if (snd_pcm_mmap_capture_frags_free(pcm) <= 0) { + if (snd_pcm_mmap_capture_frags_free(chan) <= 0) { fprintf(stderr, "overrun\n"); usleep(10000); continue; @@ -1003,6 +982,36 @@ int snd_pcm_mmap_control(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **c return 0; } +int snd_pcm_mmap_get_areas(snd_pcm_t *pcm, int channel, snd_pcm_voice_area_t *areas) +{ + struct snd_pcm_chan *chan; + snd_pcm_voice_setup_t s; + snd_pcm_voice_area_t *a, *ap; + unsigned int voice; + int err; + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) + return -EINVAL; + chan = &pcm->chan[channel]; + if (!chan->open || !chan->valid_setup || !chan->mmap_data) + return -EBADFD; + a = calloc(chan->setup.format.voices, sizeof(*areas)); + for (voice = 0, ap = a; voice < chan->setup.format.voices; ++voice, ++ap) { + s.voice = voice; + err = snd_pcm_voice_setup(pcm, channel, &s); + if (err < 0) { + free(a); + return err; + } + if (areas) + areas[voice] = s.area; + *ap = s.area; + } + chan->voices = a; + return 0; +} + int snd_pcm_mmap_data(snd_pcm_t *pcm, int channel, void **data) { struct snd_pcm_chan *chan; @@ -1054,7 +1063,9 @@ int snd_pcm_mmap_data(snd_pcm_t *pcm, int channel, void **data) } chan->mmap_data = *data; chan->mmap_data_size = bsize; - snd_pcm_all_voices_setup(pcm, channel, NULL); + err = snd_pcm_mmap_get_areas(pcm, channel, NULL); + if (err < 0) + return err; return 0; } @@ -1125,6 +1136,7 @@ int snd_pcm_munmap_data(snd_pcm_t *pcm, int channel) if ((err = pcm->ops->munmap_data(pcm, channel, chan->mmap_data, chan->mmap_data_size)) < 0) return err; } + free(chan->voices); chan->mmap_data = 0; chan->mmap_data_size = 0; return 0; diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c index 953e06dc..fd673fea 100644 --- a/src/pcm/pcm_plug.c +++ b/src/pcm/pcm_plug.c @@ -438,6 +438,7 @@ static int snd_pcm_plug_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t } /* compute right sizes */ + slave_params.buffer_size = snd_pcm_plug_slave_size(pcm, channel, slave_params.buffer_size); slave_params.frag_size = snd_pcm_plug_slave_size(pcm, channel, slave_params.frag_size); if (params->mode == SND_PCM_MODE_STREAM) { slave_params.buf.stream.bytes_fill_max = snd_pcm_plug_slave_size(pcm, channel, slave_params.buf.stream.bytes_fill_max); @@ -471,7 +472,7 @@ static int snd_pcm_plug_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *s setup->buffer_size = snd_pcm_plug_client_size(pcm, setup->channel, setup->buffer_size); setup->frag_size = snd_pcm_plug_client_size(pcm, setup->channel, setup->frag_size); /* FIXME: it may overflow */ - setup->pos_boundary = snd_pcm_plug_client_size(pcm, setup->channel, setup->pos_boundary); + setup->byte_boundary = snd_pcm_plug_client_size(pcm, setup->channel, setup->byte_boundary); if (setup->mode == SND_PCM_MODE_STREAM) { setup->buf.stream.bytes_min = snd_pcm_plug_client_size(pcm, setup->channel, setup->buf.stream.bytes_min); setup->buf.stream.bytes_align = snd_pcm_plug_client_size(pcm, setup->channel, setup->buf.stream.bytes_align); @@ -500,8 +501,8 @@ static int snd_pcm_plug_channel_status(snd_pcm_t *pcm, snd_pcm_channel_status_t return 0; /* FIXME: may overflow */ - status->pos_io = snd_pcm_plug_client_size(pcm, status->channel, status->pos_io); - status->pos_data = snd_pcm_plug_client_size(pcm, status->channel, status->pos_data); + status->byte_io = snd_pcm_plug_client_size(pcm, status->channel, status->byte_io); + status->byte_data = snd_pcm_plug_client_size(pcm, status->channel, status->byte_data); status->bytes_used = snd_pcm_plug_client_size(pcm, status->channel, status->bytes_used); status->bytes_free = snd_pcm_plug_client_size(pcm, status->channel, status->bytes_free); return 0; @@ -605,8 +606,8 @@ static int snd_pcm_plug_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_s memset(setup, 0, sizeof(*setup)); setup->voice = voice; chan = &pcm->chan[channel]; - if (!chan->mmap_control) { - setup->addr = -1; + if (!chan->mmap_data) { + setup->area.addr = 0; return 0; } if (voice >= chan->setup.format.voices) @@ -617,14 +618,14 @@ static int snd_pcm_plug_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_s return width; size = chan->mmap_data_size; if (chan->setup.format.interleave) { - setup->addr = 0; - setup->first = voice * width; - setup->step = chan->setup.format.voices * width; + setup->area.addr = chan->mmap_data; + setup->area.first = chan->sample_width; + setup->area.step = chan->bits_per_sample; } else { size /= chan->setup.format.voices; - setup->addr = setup->voice * size; - setup->first = 0; - setup->step = width; + setup->area.addr = chan->mmap_data + setup->voice * size; + setup->area.first = 0; + setup->area.step = width; } return 0; } diff --git a/src/pcm/plugin/adpcm.c b/src/pcm/plugin/adpcm.c index f85e42a1..fa8df67c 100644 --- a/src/pcm/plugin/adpcm.c +++ b/src/pcm/plugin/adpcm.c @@ -231,17 +231,17 @@ static void adpcm_decode(snd_pcm_plugin_t *plugin, adpcm_voice_t *state; if (!src_voices[voice].enabled) { if (dst_voices[voice].wanted) - snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples); + snd_pcm_area_silence(&dst_voices[voice].area, 0, samples, plugin->dst_format.format); dst_voices[voice].enabled = 0; continue; } dst_voices[voice].enabled = 1; - src = src_voices[voice].addr + src_voices[voice].first / 8; - srcbit = src_voices[voice].first % 8; - dst = dst_voices[voice].addr + dst_voices[voice].first / 8; - src_step = src_voices[voice].step / 8; - srcbit_step = src_voices[voice].step % 8; - dst_step = dst_voices[voice].step / 8; + src = src_voices[voice].area.addr + src_voices[voice].area.first / 8; + srcbit = src_voices[voice].area.first % 8; + dst = dst_voices[voice].area.addr + dst_voices[voice].area.first / 8; + src_step = src_voices[voice].area.step / 8; + srcbit_step = src_voices[voice].area.step % 8; + dst_step = dst_voices[voice].area.step / 8; state = &data->voices[voice]; samples1 = samples; while (samples1-- > 0) { @@ -290,17 +290,17 @@ static void adpcm_encode(snd_pcm_plugin_t *plugin, adpcm_voice_t *state; if (!src_voices[voice].enabled) { if (dst_voices[voice].wanted) - snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples); + snd_pcm_area_silence(&dst_voices[voice].area, 0, samples, plugin->dst_format.format); dst_voices[voice].enabled = 0; continue; } dst_voices[voice].enabled = 1; - src = src_voices[voice].addr + src_voices[voice].first / 8; - dst = dst_voices[voice].addr + dst_voices[voice].first / 8; - dstbit = dst_voices[voice].first % 8; - src_step = src_voices[voice].step / 8; - dst_step = dst_voices[voice].step / 8; - dstbit_step = dst_voices[voice].step % 8; + src = src_voices[voice].area.addr + src_voices[voice].area.first / 8; + dst = dst_voices[voice].area.addr + dst_voices[voice].area.first / 8; + dstbit = dst_voices[voice].area.first % 8; + src_step = src_voices[voice].area.step / 8; + dst_step = dst_voices[voice].area.step / 8; + dstbit_step = dst_voices[voice].area.step % 8; state = &data->voices[voice]; samples1 = samples; while (samples1-- > 0) { @@ -340,16 +340,16 @@ static ssize_t adpcm_transfer(snd_pcm_plugin_t *plugin, return 0; for (voice = 0; voice < plugin->src_format.voices; voice++) { if (plugin->src_format.format == SND_PCM_SFMT_IMA_ADPCM) { - if (src_voices[voice].first % 4 != 0 || - src_voices[voice].step % 4 != 0 || - dst_voices[voice].first % 8 != 0 || - dst_voices[voice].step % 8 != 0) + if (src_voices[voice].area.first % 4 != 0 || + src_voices[voice].area.step % 4 != 0 || + dst_voices[voice].area.first % 8 != 0 || + dst_voices[voice].area.step % 8 != 0) return -EINVAL; } else { - if (src_voices[voice].first % 8 != 0 || - src_voices[voice].step % 8 != 0 || - dst_voices[voice].first % 4 != 0 || - dst_voices[voice].step % 4 != 0) + if (src_voices[voice].area.first % 8 != 0 || + src_voices[voice].area.step % 8 != 0 || + dst_voices[voice].area.first % 4 != 0 || + dst_voices[voice].area.step % 4 != 0) return -EINVAL; } } diff --git a/src/pcm/plugin/alaw.c b/src/pcm/plugin/alaw.c index fce035bd..27af3e6b 100644 --- a/src/pcm/plugin/alaw.c +++ b/src/pcm/plugin/alaw.c @@ -161,15 +161,15 @@ static void alaw_decode(snd_pcm_plugin_t *plugin, size_t samples1; if (!src_voices[voice].enabled) { if (dst_voices[voice].wanted) - snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples); + snd_pcm_area_silence(&dst_voices[voice].area, 0, samples, plugin->dst_format.format); dst_voices[voice].enabled = 0; continue; } dst_voices[voice].enabled = 1; - src = src_voices[voice].addr + src_voices[voice].first / 8; - dst = dst_voices[voice].addr + dst_voices[voice].first / 8; - src_step = src_voices[voice].step / 8; - dst_step = dst_voices[voice].step / 8; + src = src_voices[voice].area.addr + src_voices[voice].area.first / 8; + dst = dst_voices[voice].area.addr + dst_voices[voice].area.first / 8; + src_step = src_voices[voice].area.step / 8; + dst_step = dst_voices[voice].area.step / 8; samples1 = samples; while (samples1-- > 0) { signed short sample = alaw2linear(*src); @@ -204,15 +204,15 @@ static void alaw_encode(snd_pcm_plugin_t *plugin, size_t samples1; if (!src_voices[voice].enabled) { if (dst_voices[voice].wanted) - snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples); + snd_pcm_area_silence(&dst_voices[voice].area, 0, samples, plugin->dst_format.format); dst_voices[voice].enabled = 0; continue; } dst_voices[voice].enabled = 1; - src = src_voices[voice].addr + src_voices[voice].first / 8; - dst = dst_voices[voice].addr + dst_voices[voice].first / 8; - src_step = src_voices[voice].step / 8; - dst_step = dst_voices[voice].step / 8; + src = src_voices[voice].area.addr + src_voices[voice].area.first / 8; + dst = dst_voices[voice].area.addr + dst_voices[voice].area.first / 8; + src_step = src_voices[voice].area.step / 8; + dst_step = dst_voices[voice].area.step / 8; samples1 = samples; while (samples1-- > 0) { goto *get; @@ -240,11 +240,11 @@ static ssize_t alaw_transfer(snd_pcm_plugin_t *plugin, if (samples == 0) return 0; for (voice = 0; voice < plugin->src_format.voices; voice++) { - if (src_voices[voice].first % 8 != 0 || - src_voices[voice].step % 8 != 0) + if (src_voices[voice].area.first % 8 != 0 || + src_voices[voice].area.step % 8 != 0) return -EINVAL; - if (dst_voices[voice].first % 8 != 0 || - dst_voices[voice].step % 8 != 0) + if (dst_voices[voice].area.first % 8 != 0 || + dst_voices[voice].area.step % 8 != 0) return -EINVAL; } data = (alaw_t *)plugin->extra_data; diff --git a/src/pcm/plugin/block.c b/src/pcm/plugin/block.c index 7c83e967..e463ebd4 100644 --- a/src/pcm/plugin/block.c +++ b/src/pcm/plugin/block.c @@ -68,12 +68,12 @@ static ssize_t block_transfer(snd_pcm_plugin_t *plugin, return result; count = plugin->src_format.voices; if (plugin->src_format.interleave) { - result = snd_pcm_write(data->slave, src_voices->addr, result); + result = snd_pcm_write(data->slave, src_voices->area.addr, result); } else { result /= count; for (voice = 0; voice < count; voice++) { if (src_voices[voice].enabled) - vec[voice].iov_base = src_voices[voice].addr; + vec[voice].iov_base = src_voices[voice].area.addr; else vec[voice].iov_base = 0; vec[voice].iov_len = result; @@ -90,7 +90,7 @@ static ssize_t block_transfer(snd_pcm_plugin_t *plugin, return result; count = plugin->dst_format.voices; if (plugin->dst_format.interleave) { - result = snd_pcm_read(data->slave, dst_voices->addr, result); + result = snd_pcm_read(data->slave, dst_voices->area.addr, result); for (voice = 0; voice < count; voice++) { dst_voices[voice].enabled = src_voices[voice].enabled; } @@ -99,7 +99,7 @@ static ssize_t block_transfer(snd_pcm_plugin_t *plugin, for (voice = 0; voice < count; voice++) { dst_voices[voice].enabled = src_voices[voice].enabled; if (dst_voices[voice].enabled) - vec[voice].iov_base = dst_voices[voice].addr; + vec[voice].iov_base = dst_voices[voice].area.addr; else vec[voice].iov_base = 0; vec[voice].iov_len = result; diff --git a/src/pcm/plugin/copy.c b/src/pcm/plugin/copy.c index 88a61d75..c9850713 100644 --- a/src/pcm/plugin/copy.c +++ b/src/pcm/plugin/copy.c @@ -35,121 +35,63 @@ #include "../pcm_local.h" #endif -typedef struct copy_private_data { - int copy; -} copy_t; - -static void copy(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_voice_t *src_voices, - snd_pcm_plugin_voice_t *dst_voices, - size_t samples) -{ -#define COPY_LABELS -#include "plugin_ops.h" -#undef COPY_LABELS - copy_t *data = (copy_t *)plugin->extra_data; - void *copy = copy_labels[data->copy]; - int voice; - int nvoices = plugin->src_format.voices; - for (voice = 0; voice < nvoices; ++voice) { - char *src; - char *dst; - int src_step, dst_step; - size_t samples1; - if (!src_voices[voice].enabled) { - if (dst_voices[voice].wanted) - snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples); - dst_voices[voice].enabled = 0; - continue; - } - dst_voices[voice].enabled = 1; - src = src_voices[voice].addr + src_voices[voice].first / 8; - dst = dst_voices[voice].addr + dst_voices[voice].first / 8; - src_step = src_voices[voice].step / 8; - dst_step = dst_voices[voice].step / 8; - samples1 = samples; - while (samples1-- > 0) { - goto *copy; -#define COPY_END after -#include "plugin_ops.h" -#undef COPY_END - after: - src += src_step; - dst += dst_step; - } - } -} - static ssize_t copy_transfer(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, snd_pcm_plugin_voice_t *dst_voices, size_t samples) { - copy_t *data; unsigned int voice; + unsigned int nvoices; if (plugin == NULL || src_voices == NULL || dst_voices == NULL) return -EFAULT; - data = (copy_t *)plugin->extra_data; if (samples == 0) return 0; - for (voice = 0; voice < plugin->src_format.voices; voice++) { - if (src_voices[voice].first % 8 != 0 || - src_voices[voice].step % 8 != 0) + nvoices = plugin->src_format.voices; + for (voice = 0; voice < nvoices; voice++) { + if (src_voices[voice].area.first % 8 != 0 || + src_voices[voice].area.step % 8 != 0) return -EINVAL; - if (dst_voices[voice].first % 8 != 0 || - dst_voices[voice].step % 8 != 0) + if (dst_voices[voice].area.first % 8 != 0 || + dst_voices[voice].area.step % 8 != 0) return -EINVAL; + if (!src_voices->enabled) { + if (dst_voices->wanted) + snd_pcm_area_silence(&dst_voices->area, 0, samples, plugin->dst_format.format); + dst_voices->enabled = 0; + continue; + } + dst_voices[voice].enabled = 1; + snd_pcm_area_copy(&src_voices->area, 0, &dst_voices->area, 0, samples, plugin->src_format.format); } - copy(plugin, src_voices, dst_voices, samples); return samples; } -int copy_index(int format) -{ - int size = snd_pcm_format_physical_width(format); - switch (size) { - case 8: - return 0; - case 16: - return 1; - case 32: - return 2; - case 64: - return 3; - default: - return -EINVAL; - } -} - int snd_pcm_plugin_build_copy(snd_pcm_plugin_handle_t *handle, int channel, snd_pcm_format_t *format, snd_pcm_plugin_t **r_plugin) { int err; - struct copy_private_data *data; snd_pcm_plugin_t *plugin; - int copy; + int width; if (r_plugin == NULL) return -EFAULT; *r_plugin = NULL; - copy = copy_index(format->format); - if (copy < 0) + width = snd_pcm_format_physical_width(format->format); + if (width < 0) return -EINVAL; err = snd_pcm_plugin_build(handle, channel, "copy", format, format, - sizeof(copy_t), + 0, &plugin); if (err < 0) return err; - data = (copy_t *)plugin->extra_data; - data->copy = copy; plugin->transfer = copy_transfer; *r_plugin = plugin; return 0; diff --git a/src/pcm/plugin/linear.c b/src/pcm/plugin/linear.c index 2ba444bf..12974e35 100644 --- a/src/pcm/plugin/linear.c +++ b/src/pcm/plugin/linear.c @@ -63,15 +63,15 @@ static void convert(snd_pcm_plugin_t *plugin, size_t samples1; if (!src_voices[voice].enabled) { if (dst_voices[voice].wanted) - snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples); + snd_pcm_area_silence(&dst_voices[voice].area, 0, samples, plugin->dst_format.format); dst_voices[voice].enabled = 0; continue; } dst_voices[voice].enabled = 1; - src = src_voices[voice].addr + src_voices[voice].first / 8; - dst = dst_voices[voice].addr + dst_voices[voice].first / 8; - src_step = src_voices[voice].step / 8; - dst_step = dst_voices[voice].step / 8; + src = src_voices[voice].area.addr + src_voices[voice].area.first / 8; + dst = dst_voices[voice].area.addr + dst_voices[voice].area.first / 8; + src_step = src_voices[voice].area.step / 8; + dst_step = dst_voices[voice].area.step / 8; samples1 = samples; while (samples1-- > 0) { goto *conv; @@ -99,11 +99,11 @@ static ssize_t linear_transfer(snd_pcm_plugin_t *plugin, if (samples == 0) return 0; for (voice = 0; voice < plugin->src_format.voices; voice++) { - if (src_voices[voice].first % 8 != 0 || - src_voices[voice].step % 8 != 0) + if (src_voices[voice].area.first % 8 != 0 || + src_voices[voice].area.step % 8 != 0) return -EINVAL; - if (dst_voices[voice].first % 8 != 0 || - dst_voices[voice].step % 8 != 0) + if (dst_voices[voice].area.first % 8 != 0 || + dst_voices[voice].area.step % 8 != 0) return -EINVAL; } convert(plugin, src_voices, dst_voices, samples); diff --git a/src/pcm/plugin/mmap.c b/src/pcm/plugin/mmap.c index c1751729..ccc6b3b8 100644 --- a/src/pcm/plugin/mmap.c +++ b/src/pcm/plugin/mmap.c @@ -38,9 +38,7 @@ typedef struct mmap_private_data { snd_pcm_mmap_control_t *control; void *buffer; unsigned int frag; - size_t samples_frag_size; char *silence; - snd_pcm_plugin_voice_t voices[0]; } mmap_t; @@ -50,22 +48,22 @@ static int mmap_src_voices(snd_pcm_plugin_t *plugin, { mmap_t *data; unsigned int voice; - snd_pcm_plugin_voice_t *dv, *sv; + snd_pcm_plugin_voice_t *sv; + snd_pcm_voice_area_t *dv; struct snd_pcm_chan *chan; snd_pcm_channel_setup_t *setup; snd_pcm_mmap_control_t *ctrl; int frag, f; - struct pollfd pfd; int ready; if (plugin == NULL || voices == NULL) return -EINVAL; data = (mmap_t *)plugin->extra_data; - if (samples != data->samples_frag_size) + ctrl = data->control; + chan = &data->slave->chan[plugin->channel]; + if (samples != chan->samples_per_frag) return -EINVAL; - ctrl = data->control; - chan = &plugin->handle->chan[plugin->channel]; setup = &chan->setup; if (ctrl->status < SND_PCM_STATUS_PREPARED) return -EBADFD; @@ -74,11 +72,12 @@ static int mmap_src_voices(snd_pcm_plugin_t *plugin, if (ready < 0) return ready; if (!ready) { + struct pollfd pfd; if (ctrl->status != SND_PCM_STATUS_RUNNING) return -EPIPE; if (chan->mode & SND_PCM_NONBLOCK) return -EAGAIN; - pfd.fd = snd_pcm_file_descriptor(plugin->handle, plugin->channel); + pfd.fd = snd_pcm_file_descriptor(data->slave, plugin->channel); pfd.events = POLLOUT | POLLERR; ready = poll(&pfd, 1, 10000); if (ready < 0) @@ -90,16 +89,16 @@ static int mmap_src_voices(snd_pcm_plugin_t *plugin, frag = ctrl->frag_data; f = frag % setup->frags; - dv = data->voices; sv = plugin->src_voices; + dv = chan->voices; *voices = sv; for (voice = 0; voice < plugin->src_format.voices; ++voice) { sv->enabled = 1; sv->wanted = !data->silence[voice * setup->frags + f]; sv->aptr = 0; - sv->addr = dv->addr + (dv->step * data->samples_frag_size * f) / 8; - sv->first = dv->first; - sv->step = dv->step; + sv->area.addr = dv->addr + (dv->step * chan->samples_per_frag * f) / 8; + sv->area.first = dv->first; + sv->area.step = dv->step; ++sv; ++dv; } @@ -114,21 +113,21 @@ static int mmap_dst_voices(snd_pcm_plugin_t *plugin, mmap_t *data; int err; unsigned int voice; - snd_pcm_plugin_voice_t *dv, *sv; + snd_pcm_plugin_voice_t *dv; + snd_pcm_voice_area_t *sv; struct snd_pcm_chan *chan; snd_pcm_channel_setup_t *setup; snd_pcm_mmap_control_t *ctrl; int frag, f; - struct pollfd pfd; int ready; if (plugin == NULL || voices == NULL) return -EINVAL; data = (mmap_t *)plugin->extra_data; - if (samples != data->samples_frag_size) + chan = &data->slave->chan[plugin->channel]; + if (samples != chan->samples_per_frag) return -EINVAL; - chan = &plugin->handle->chan[plugin->channel]; setup = &chan->setup; ctrl = data->control; if (ctrl->status < SND_PCM_STATUS_PREPARED) @@ -143,17 +142,12 @@ static int mmap_dst_voices(snd_pcm_plugin_t *plugin, if (ready < 0) return ready; if (!ready) { - if (ctrl->status == SND_PCM_STATUS_PREPARED && - chan->setup.start_mode == SND_PCM_START_FULL) { - err = snd_pcm_channel_go(data->slave, plugin->channel); - if (err < 0) - return err; - } + struct pollfd pfd; if (ctrl->status != SND_PCM_STATUS_RUNNING) return -EPIPE; if (chan->mode & SND_PCM_NONBLOCK) return -EAGAIN; - pfd.fd = snd_pcm_file_descriptor(plugin->handle, plugin->channel); + pfd.fd = snd_pcm_file_descriptor(data->slave, plugin->channel); pfd.events = POLLIN | POLLERR; ready = poll(&pfd, 1, 10000); if (ready < 0) @@ -166,16 +160,16 @@ static int mmap_dst_voices(snd_pcm_plugin_t *plugin, frag = ctrl->frag_data; f = frag % setup->frags; - sv = data->voices; + sv = chan->voices; dv = plugin->dst_voices; *voices = dv; for (voice = 0; voice < plugin->dst_format.voices; ++voice) { dv->enabled = 1; dv->wanted = 0; dv->aptr = 0; - dv->addr = sv->addr + (sv->step * data->samples_frag_size * f) / 8; - dv->first = sv->first; - dv->step = sv->step; + dv->area.addr = sv->addr + (sv->step * chan->samples_per_frag * f) / 8; + dv->area.first = sv->first; + dv->area.step = sv->step; ++sv; ++dv; } @@ -218,14 +212,7 @@ static ssize_t mmap_playback_transfer(snd_pcm_plugin_t *plugin, data->silence[voice * setup->frags + f] = 0; } - frag++; - if (frag == setup->frag_boundary) { - ctrl->frag_data = 0; - ctrl->pos_data = 0; - } else { - ctrl->frag_data = frag; - ctrl->pos_data += setup->frag_size; - } + snd_pcm_mmap_commit_samples(data->slave, SND_PCM_CHANNEL_PLAYBACK, samples); if (ctrl->status == SND_PCM_STATUS_PREPARED && (chan->setup.start_mode == SND_PCM_START_DATA || (chan->setup.start_mode == SND_PCM_START_FULL && @@ -262,14 +249,7 @@ static ssize_t mmap_capture_transfer(snd_pcm_plugin_t *plugin, setup = &data->slave->chan[SND_PCM_CHANNEL_CAPTURE].setup; /* FIXME: not here the increment */ - frag++; - if (frag == setup->frag_boundary) { - ctrl->frag_data = 0; - ctrl->pos_data = 0; - } else { - ctrl->frag_data = frag; - ctrl->pos_data += setup->frag_size; - } + snd_pcm_mmap_commit_samples(data->slave, SND_PCM_CHANNEL_CAPTURE, samples); return samples; } @@ -285,8 +265,6 @@ static int mmap_action(snd_pcm_plugin_t *plugin, if (action == INIT) { snd_pcm_channel_setup_t *setup; int result; - unsigned int voice; - snd_pcm_plugin_voice_t *v; if (data->control) snd_pcm_munmap(data->slave, plugin->channel); @@ -294,22 +272,7 @@ static int mmap_action(snd_pcm_plugin_t *plugin, if (result < 0) return result; setup = &data->slave->chan[plugin->channel].setup; - data->samples_frag_size = setup->frag_size / snd_pcm_format_size(setup->format.format, setup->format.voices); - v = data->voices; - for (voice = 0; voice < setup->format.voices; ++voice) { - snd_pcm_voice_setup_t vsetup; - - vsetup.voice = voice; - if ((result = snd_pcm_voice_setup(data->slave, plugin->channel, &vsetup)) < 0) - return result; - if (vsetup.addr < 0) - return -EBADFD; - v->addr = data->buffer + vsetup.addr; - v->first = vsetup.first; - v->step = vsetup.step; - v++; - } if (plugin->channel == SND_PCM_CHANNEL_PLAYBACK) { data->silence = malloc(setup->frags * setup->format.voices); memset(data->silence, 0, setup->frags * setup->format.voices); diff --git a/src/pcm/plugin/mulaw.c b/src/pcm/plugin/mulaw.c index 4e8964ab..ef0fdcff 100644 --- a/src/pcm/plugin/mulaw.c +++ b/src/pcm/plugin/mulaw.c @@ -177,15 +177,15 @@ static void mulaw_decode(snd_pcm_plugin_t *plugin, size_t samples1; if (!src_voices[voice].enabled) { if (dst_voices[voice].wanted) - snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples); + snd_pcm_area_silence(&dst_voices[voice].area, 0, samples, plugin->dst_format.format); dst_voices[voice].enabled = 0; continue; } dst_voices[voice].enabled = 1; - src = src_voices[voice].addr + src_voices[voice].first / 8; - dst = dst_voices[voice].addr + dst_voices[voice].first / 8; - src_step = src_voices[voice].step / 8; - dst_step = dst_voices[voice].step / 8; + src = src_voices[voice].area.addr + src_voices[voice].area.first / 8; + dst = dst_voices[voice].area.addr + dst_voices[voice].area.first / 8; + src_step = src_voices[voice].area.step / 8; + dst_step = dst_voices[voice].area.step / 8; samples1 = samples; while (samples1-- > 0) { signed short sample = ulaw2linear(*src); @@ -220,15 +220,15 @@ static void mulaw_encode(snd_pcm_plugin_t *plugin, size_t samples1; if (!src_voices[voice].enabled) { if (dst_voices[voice].wanted) - snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples); + snd_pcm_area_silence(&dst_voices[voice].area, 0, samples, plugin->dst_format.format); dst_voices[voice].enabled = 0; continue; } dst_voices[voice].enabled = 1; - src = src_voices[voice].addr + src_voices[voice].first / 8; - dst = dst_voices[voice].addr + dst_voices[voice].first / 8; - src_step = src_voices[voice].step / 8; - dst_step = dst_voices[voice].step / 8; + src = src_voices[voice].area.addr + src_voices[voice].area.first / 8; + dst = dst_voices[voice].area.addr + dst_voices[voice].area.first / 8; + src_step = src_voices[voice].area.step / 8; + dst_step = dst_voices[voice].area.step / 8; samples1 = samples; while (samples1-- > 0) { goto *get; @@ -256,11 +256,11 @@ static ssize_t mulaw_transfer(snd_pcm_plugin_t *plugin, if (samples == 0) return 0; for (voice = 0; voice < plugin->src_format.voices; voice++) { - if (src_voices[voice].first % 8 != 0 || - src_voices[voice].step % 8 != 0) + if (src_voices[voice].area.first % 8 != 0 || + src_voices[voice].area.step % 8 != 0) return -EINVAL; - if (dst_voices[voice].first % 8 != 0 || - dst_voices[voice].step % 8 != 0) + if (dst_voices[voice].area.first % 8 != 0 || + dst_voices[voice].area.step % 8 != 0) return -EINVAL; } data = (mulaw_t *)plugin->extra_data; diff --git a/src/pcm/plugin/rate.c b/src/pcm/plugin/rate.c index 3afc0272..43d31cea 100644 --- a/src/pcm/plugin/rate.c +++ b/src/pcm/plugin/rate.c @@ -106,15 +106,15 @@ static void resample_expand(snd_pcm_plugin_t *plugin, S2 = rvoices->last_S2; if (!src_voices[voice].enabled) { if (dst_voices[voice].wanted) - snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], dst_samples); + snd_pcm_area_silence(&dst_voices[voice].area, 0, dst_samples, plugin->dst_format.format); dst_voices[voice].enabled = 0; continue; } dst_voices[voice].enabled = 1; - src = (char *)src_voices[voice].addr + src_voices[voice].first / 8; - dst = (char *)dst_voices[voice].addr + dst_voices[voice].first / 8; - src_step = src_voices[voice].step / 8; - dst_step = dst_voices[voice].step / 8; + src = (char *)src_voices[voice].area.addr + src_voices[voice].area.first / 8; + dst = (char *)dst_voices[voice].area.addr + dst_voices[voice].area.first / 8; + src_step = src_voices[voice].area.step / 8; + dst_step = dst_voices[voice].area.step / 8; src_samples1 = src_samples; dst_samples1 = dst_samples; if (pos & ~MASK) { @@ -190,15 +190,15 @@ static void resample_shrink(snd_pcm_plugin_t *plugin, S2 = rvoices->last_S2; if (!src_voices[voice].enabled) { if (dst_voices[voice].wanted) - snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], dst_samples); + snd_pcm_area_silence(&dst_voices[voice].area, 0, dst_samples, plugin->dst_format.format); dst_voices[voice].enabled = 0; continue; } dst_voices[voice].enabled = 1; - src = (char *)src_voices[voice].addr + src_voices[voice].first / 8; - dst = (char *)dst_voices[voice].addr + dst_voices[voice].first / 8; - src_step = src_voices[voice].step / 8; - dst_step = dst_voices[voice].step / 8; + src = (char *)src_voices[voice].area.addr + src_voices[voice].area.first / 8; + dst = (char *)dst_voices[voice].area.addr + dst_voices[voice].area.first / 8; + src_step = src_voices[voice].area.step / 8; + dst_step = dst_voices[voice].area.step / 8; src_samples1 = src_samples; dst_samples1 = dst_samples; while (dst_samples1 > 0) { @@ -313,11 +313,11 @@ static ssize_t rate_transfer(snd_pcm_plugin_t *plugin, if (samples == 0) return 0; for (voice = 0; voice < plugin->src_format.voices; voice++) { - if (src_voices[voice].first % 8 != 0 || - src_voices[voice].step % 8 != 0) + if (src_voices[voice].area.first % 8 != 0 || + src_voices[voice].area.step % 8 != 0) return -EINVAL; - if (dst_voices[voice].first % 8 != 0 || - dst_voices[voice].step % 8 != 0) + if (dst_voices[voice].area.first % 8 != 0 || + dst_voices[voice].area.step % 8 != 0) return -EINVAL; } diff --git a/src/pcm/plugin/route.c b/src/pcm/plugin/route.c index ba99d8ab..40b0b706 100644 --- a/src/pcm/plugin/route.c +++ b/src/pcm/plugin/route.c @@ -81,7 +81,7 @@ static void route_to_voice_zero(snd_pcm_plugin_t *plugin, ttable_dst_t* ttable UNUSED, size_t samples) { if (dst_voice->wanted) - snd_pcm_plugin_silence_voice(plugin, dst_voice, samples); + snd_pcm_area_silence(&dst_voice->area, 0, samples, plugin->dst_format.format); dst_voice->enabled = 0; } @@ -101,7 +101,7 @@ static void route_to_voice_one(snd_pcm_plugin_t *plugin, int src_step, dst_step; for (srcidx = 0; srcidx < ttable->nsrcs; ++srcidx) { src_voice = &src_voices[ttable->srcs[srcidx].voice]; - if (src_voice->addr != NULL) + if (src_voice->area.addr != NULL) break; } if (srcidx == ttable->nsrcs) { @@ -111,10 +111,10 @@ static void route_to_voice_one(snd_pcm_plugin_t *plugin, dst_voice->enabled = 1; conv = conv_labels[data->conv]; - src = src_voice->addr + src_voice->first / 8; - src_step = src_voice->step / 8; - dst = dst_voice->addr + dst_voice->first / 8; - dst_step = dst_voice->step / 8; + src = src_voice->area.addr + src_voice->area.first / 8; + src_step = src_voice->area.step / 8; + dst = dst_voice->area.addr + dst_voice->area.first / 8; + dst_step = dst_voice->area.step / 8; while (samples-- > 0) { goto *conv; #define CONV_END after @@ -190,8 +190,8 @@ static void route_to_voice(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voice = &src_voices[ttable->srcs[srcidx].voice]; if (!src_voice->enabled) continue; - srcs[srcidx1] = src_voice->addr + src_voices->first / 8; - src_steps[srcidx1] = src_voice->step / 8; + srcs[srcidx1] = src_voice->area.addr + src_voices->area.first / 8; + src_steps[srcidx1] = src_voice->area.step / 8; src_tt[srcidx1] = ttable->srcs[srcidx]; srcidx1++; } @@ -210,8 +210,8 @@ static void route_to_voice(snd_pcm_plugin_t *plugin, add = add_labels[data->sum_type * 2 + ttable->att]; norm = norm_labels[data->sum_type * 8 + ttable->att * 4 + 4 - data->src_sample_size]; put32 = put32_labels[data->put]; - dst = dst_voice->addr + dst_voice->first / 8; - dst_step = dst_voice->step / 8; + dst = dst_voice->area.addr + dst_voice->area.first / 8; + dst_step = dst_voice->area.step / 8; while (samples-- > 0) { ttable_src_t *ttp = src_tt; @@ -513,15 +513,15 @@ static ssize_t route_transfer(snd_pcm_plugin_t *plugin, src_nvoices = plugin->src_format.voices; for (src_voice = 0; src_voice < src_nvoices; ++src_voice) { - if (src_voices[src_voice].first % 8 != 0 || - src_voices[src_voice].step % 8 != 0) + if (src_voices[src_voice].area.first % 8 != 0 || + src_voices[src_voice].area.step % 8 != 0) return -EINVAL; } dst_nvoices = plugin->dst_format.voices; for (dst_voice = 0; dst_voice < dst_nvoices; ++dst_voice) { - if (dst_voices[dst_voice].first % 8 != 0 || - dst_voices[dst_voice].step % 8 != 0) + if (dst_voices[dst_voice].area.first % 8 != 0 || + dst_voices[dst_voice].area.step % 8 != 0) return -EINVAL; } diff --git a/src/pcm/plugin/stream.c b/src/pcm/plugin/stream.c index 57bfe0c4..79ef50e8 100644 --- a/src/pcm/plugin/stream.c +++ b/src/pcm/plugin/stream.c @@ -58,12 +58,12 @@ static ssize_t stream_transfer(snd_pcm_plugin_t *plugin, return result; count = plugin->src_format.voices; if (plugin->src_format.interleave) { - result = snd_pcm_write(data->slave, src_voices->addr, result); + result = snd_pcm_write(data->slave, src_voices->area.addr, result); } else { result /= count; for (voice = 0; voice < count; voice++) { if (src_voices[voice].enabled) - vec[voice].iov_base = src_voices[voice].addr; + vec[voice].iov_base = src_voices[voice].area.addr; else vec[voice].iov_base = 0; vec[voice].iov_len = result; @@ -80,7 +80,7 @@ static ssize_t stream_transfer(snd_pcm_plugin_t *plugin, return result; count = plugin->dst_format.voices; if (plugin->dst_format.interleave) { - result = snd_pcm_read(data->slave, dst_voices->addr, result); + result = snd_pcm_read(data->slave, dst_voices->area.addr, result); for (voice = 0; voice < count; voice++) dst_voices[voice].enabled = src_voices[voice].enabled; } else { @@ -88,7 +88,7 @@ static ssize_t stream_transfer(snd_pcm_plugin_t *plugin, for (voice = 0; voice < count; voice++) { dst_voices[voice].enabled = src_voices[voice].enabled; if (dst_voices[voice].enabled) - vec[voice].iov_base = dst_voices[voice].addr; + vec[voice].iov_base = dst_voices[voice].area.addr; else vec[voice].iov_base = 0; vec[voice].iov_len = result; -- 2.47.1