From 098b4b6a96ab94fcf03874bdb6808a1afae7437d Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 29 Mar 2000 20:26:06 +0000 Subject: [PATCH] New plugin interface with readv/writev support. Initial code. --- include/pcm.h | 116 ++++++-- src/pcm/pcm_local.h | 5 +- src/pcm/pcm_plugin.c | 535 ++++++++++++++++++++++++---------- src/pcm/pcm_plugin_build.c | 216 +++++++++++--- src/pcm/plugin/Makefile.am | 3 +- src/pcm/plugin/adpcm.c | 561 +++++++++++------------------------- src/pcm/plugin/alaw.c | 464 +++++++++++------------------ src/pcm/plugin/block.c | 80 +++-- src/pcm/plugin/interleave.c | 113 ++++---- src/pcm/plugin/linear.c | 139 +++------ src/pcm/plugin/mmap.c | 253 ++++++++++------ src/pcm/plugin/mulaw.c | 459 +++++++++++------------------ src/pcm/plugin/rate.c | 472 +++++++++++++++--------------- src/pcm/plugin/route.c | 4 +- src/pcm/plugin/stream.c | 76 ++++- 15 files changed, 1787 insertions(+), 1709 deletions(-) diff --git a/include/pcm.h b/include/pcm.h index 29d71761..4bd9a211 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -65,38 +65,69 @@ const char *snd_pcm_get_format_name(int format); #endif /* - * Plug-In interface (ala C++) + * PCM Plug-In interface */ typedef struct snd_stru_pcm_plugin snd_pcm_plugin_t; +#define snd_pcm_plugin_handle_t snd_pcm_t typedef enum { INIT = 0, PREPARE = 1, DRAIN = 2, - FLUSH = 3 + FLUSH = 3, } snd_pcm_plugin_action_t; -#define snd_pcm_plugin_extra_data(plugin) (((char *)plugin) + sizeof(*plugin)) +typedef struct snd_stru_pcm_plugin_voice { + void *aptr; /* pointer to the allocated area */ + void *addr; /* address to voice samples */ + unsigned int offset; /* offset to first voice in bits */ + unsigned int next; /* offset to next voice in bits */ +} snd_pcm_plugin_voice_t; struct snd_stru_pcm_plugin { - char *name; /* plug-in name */ - int (*transfer_src_ptr)(snd_pcm_plugin_t *plugin, char **src_ptr, size_t *src_size); + char *name; /* plug-in name */ + snd_pcm_format_t src_format; /* source format */ + snd_pcm_format_t dst_format; /* destination format */ + int src_width; /* sample width in bits */ + int dst_width; /* sample width in bits */ + ssize_t (*src_samples)(snd_pcm_plugin_t *plugin, size_t dst_samples); + ssize_t (*dst_samples)(snd_pcm_plugin_t *plugin, size_t src_samples); + int (*src_voices)(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_voice_t **voices, + size_t samples, + void *(*plugin_alloc)(snd_pcm_plugin_handle_t *handle, size_t size)); + int (*dst_voices)(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_voice_t **voices, + size_t samples, + void *(*plugin_alloc)(snd_pcm_plugin_handle_t *handle, size_t size)); ssize_t (*transfer)(snd_pcm_plugin_t *plugin, - char *src_ptr, size_t src_size, - char *dst_ptr, size_t dst_size); - ssize_t (*src_size)(snd_pcm_plugin_t *plugin, size_t dst_size); - ssize_t (*dst_size)(snd_pcm_plugin_t *plugin, size_t src_size); + const snd_pcm_plugin_voice_t *src_voices, + const snd_pcm_plugin_voice_t *dst_voices, + size_t samples); int (*action)(snd_pcm_plugin_t *plugin, snd_pcm_plugin_action_t action, unsigned long data); + int (*parameter_set)(snd_pcm_plugin_t *plugin, + const char *name, + unsigned long value); + int (*parameter_get)(snd_pcm_plugin_t *plugin, + const char *name, + unsigned long *value); snd_pcm_plugin_t *prev; snd_pcm_plugin_t *next; + snd_pcm_plugin_handle_t *handle; void *private_data; void (*private_free)(snd_pcm_plugin_t *plugin, void *private_data); + snd_pcm_plugin_voice_t *voices; + void *extra_data; }; -snd_pcm_plugin_t *snd_pcm_plugin_build(const char *name, int extra); +snd_pcm_plugin_t *snd_pcm_plugin_build(snd_pcm_plugin_handle_t *handle, + const char *name, + snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + int extra); int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin); int snd_pcm_plugin_clear(snd_pcm_t *handle, int channel); int snd_pcm_plugin_insert(snd_pcm_t *handle, int channel, snd_pcm_plugin_t *plugin); @@ -105,8 +136,10 @@ int snd_pcm_plugin_remove_to(snd_pcm_t *handle, int channel, snd_pcm_plugin_t *p int snd_pcm_plugin_remove_first(snd_pcm_t *handle, int channel); snd_pcm_plugin_t *snd_pcm_plugin_first(snd_pcm_t *handle, int channel); snd_pcm_plugin_t *snd_pcm_plugin_last(snd_pcm_t *handle, int channel); -ssize_t snd_pcm_plugin_transfer_size(snd_pcm_t *handle, int channel, size_t drv_size); -ssize_t snd_pcm_plugin_hardware_size(snd_pcm_t *handle, int channel, size_t trf_size); +ssize_t snd_pcm_plugin_client_samples(snd_pcm_t *handle, int channel, size_t drv_samples); +ssize_t snd_pcm_plugin_hardware_samples(snd_pcm_t *handle, int channel, size_t clt_samples); +ssize_t snd_pcm_plugin_client_size(snd_pcm_t *handle, int channel, size_t drv_size); +ssize_t snd_pcm_plugin_hardware_size(snd_pcm_t *handle, int channel, size_t clt_size); int snd_pcm_plugin_info(snd_pcm_t *handle, snd_pcm_channel_info_t *info); int snd_pcm_plugin_params(snd_pcm_t *handle, snd_pcm_channel_params_t *params); int snd_pcm_plugin_setup(snd_pcm_t *handle, snd_pcm_channel_setup_t *setup); @@ -114,41 +147,74 @@ int snd_pcm_plugin_status(snd_pcm_t *handle, snd_pcm_channel_status_t *status); int snd_pcm_plugin_prepare(snd_pcm_t *handle, int channel); int snd_pcm_plugin_playback_drain(snd_pcm_t *handle); int snd_pcm_plugin_flush(snd_pcm_t *handle, int channel); +ssize_t snd_pcm_plugin_transfer_size(snd_pcm_t *handle, int channel); int snd_pcm_plugin_pointer(snd_pcm_t *pcm, int channel, void **ptr, size_t *size); ssize_t snd_pcm_plugin_write(snd_pcm_t *handle, const void *buffer, size_t size); ssize_t snd_pcm_plugin_read(snd_pcm_t *handle, void *bufer, size_t size); +int snd_pcm_plugin_pointerv(snd_pcm_t *pcm, int channel, struct iovec **vector, int *count); +ssize_t snd_pcm_plugin_writev(snd_pcm_t *pcm, const struct iovec *vector, int count); +ssize_t snd_pcm_plugin_readv(snd_pcm_t *pcm, const struct iovec *vector, int count); +ssize_t snd_pcm_plugin_write_continue(snd_pcm_t *pcm); + +/* + * Plug-In helpers + */ + +ssize_t snd_pcm_plugin_src_samples_to_size(snd_pcm_plugin_t *plugin, size_t samples); +ssize_t snd_pcm_plugin_dst_samples_to_size(snd_pcm_plugin_t *plugin, size_t samples); +ssize_t snd_pcm_plugin_src_size_to_samples(snd_pcm_plugin_t *plugin, size_t size); +ssize_t snd_pcm_plugin_dst_size_to_samples(snd_pcm_plugin_t *plugin, size_t size); +int snd_pcm_plugin_src_voices(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_voice_t **voices, + size_t samples); +int snd_pcm_plugin_dst_voices(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_voice_t **voices, + size_t samples); /* * Plug-In constructors */ /* basic I/O */ -int snd_pcm_plugin_build_stream(snd_pcm_t *handle, int channel, snd_pcm_plugin_t **r_plugin); -int snd_pcm_plugin_build_block(snd_pcm_t *handle, int channel, snd_pcm_plugin_t **r_plugin); -int snd_pcm_plugin_build_mmap(snd_pcm_t *handle, int channel, snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_stream(snd_pcm_plugin_handle_t *handle, int channel, + snd_pcm_format_t *format, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_block(snd_pcm_plugin_handle_t *handle, int channel, + snd_pcm_format_t *format, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_mmap(snd_pcm_plugin_handle_t *handle, int channel, + snd_pcm_format_t *format, + snd_pcm_plugin_t **r_plugin); /* conversion plugins */ -int snd_pcm_plugin_build_interleave(snd_pcm_format_t *src_format, +int snd_pcm_plugin_build_interleave(snd_pcm_plugin_handle_t *handle, + snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin); -int snd_pcm_plugin_build_linear(snd_pcm_format_t *src_format, +int snd_pcm_plugin_build_linear(snd_pcm_plugin_handle_t *handle, + snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin); -int snd_pcm_plugin_build_mulaw(snd_pcm_format_t *src_format, +int snd_pcm_plugin_build_mulaw(snd_pcm_plugin_handle_t *handle, + snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin); -int snd_pcm_plugin_build_alaw(snd_pcm_format_t *src_format, +int snd_pcm_plugin_build_alaw(snd_pcm_plugin_handle_t *handle, + snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin); -int snd_pcm_plugin_build_adpcm(snd_pcm_format_t *src_format, +int snd_pcm_plugin_build_adpcm(snd_pcm_plugin_handle_t *handle, + snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin); -int snd_pcm_plugin_build_rate(snd_pcm_format_t *src_format, +int snd_pcm_plugin_build_rate(snd_pcm_plugin_handle_t *handle, + snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin); -int snd_pcm_plugin_build_route(snd_pcm_format_t *src_format, - snd_pcm_format_t *dst_format, - int *ttable, - snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_route(snd_pcm_plugin_handle_t *handle, + snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + int *ttable, + snd_pcm_plugin_t **r_plugin); /* * Loopback interface diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index 0ba30893..f58c54e1 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -39,6 +39,7 @@ struct snd_pcm { int plugin_alloc_lock[4]; void *plugin_alloc_xptr[2]; long plugin_alloc_xsize[2]; + int plugin_alloc_xchannel; }; unsigned int snd_pcm_plugin_formats(unsigned int formats); @@ -46,8 +47,8 @@ int snd_pcm_plugin_hwparams(snd_pcm_channel_params_t *params, snd_pcm_channel_info_t *hwinfo, snd_pcm_channel_params_t *hwparams); int snd_pcm_plugin_format(snd_pcm_t *pcm, - snd_pcm_channel_params_t *params, - snd_pcm_channel_params_t *hwparams, + snd_pcm_channel_params_t *params, + snd_pcm_channel_params_t *hwparams, snd_pcm_channel_info_t *hwinfo); #if 0 diff --git a/src/pcm/pcm_plugin.c b/src/pcm/pcm_plugin.c index ef1952ec..66ff8e65 100644 --- a/src/pcm/pcm_plugin.c +++ b/src/pcm/pcm_plugin.c @@ -25,18 +25,45 @@ #include #include #include +#include #include "pcm_local.h" -snd_pcm_plugin_t *snd_pcm_plugin_build(const char *name, int extra) +static void *snd_pcm_plugin_buf_alloc(snd_pcm_t *pcm, size_t size); +static void snd_pcm_plugin_buf_free(snd_pcm_t *pcm, void *ptr); +static void *snd_pcm_plugin_ptr_alloc(snd_pcm_t *pcm, size_t size); + +snd_pcm_plugin_t *snd_pcm_plugin_build(snd_pcm_plugin_handle_t *handle, + const char *name, + snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + int extra) { snd_pcm_plugin_t *plugin; + int voices = 0; if (extra < 0) return NULL; - plugin = (snd_pcm_plugin_t *)calloc(1, sizeof(*plugin) + extra); + if (src_format) + voices = src_format->voices; + if (dst_format && dst_format->voices > voices) + voices = dst_format->voices; + plugin = (snd_pcm_plugin_t *)calloc(1, sizeof(*plugin) + voices * sizeof(snd_pcm_plugin_voice_t) + extra); if (plugin == NULL) return NULL; plugin->name = name ? strdup(name) : NULL; + if (src_format) { + memcpy(&plugin->src_format, src_format, sizeof(snd_pcm_format_t)); + if ((plugin->src_width = snd_pcm_format_width(src_format->format)) < 0) + return NULL; + } + if (dst_format) { + memcpy(&plugin->dst_format, dst_format, sizeof(snd_pcm_format_t)); + if ((plugin->dst_width = snd_pcm_format_width(dst_format->format)) < 0) + return NULL; + } + plugin->handle = handle; + plugin->voices = (snd_pcm_plugin_voice_t *)((char *)plugin + sizeof(*plugin)); + plugin->extra_data = (char *)plugin->voices + voices * sizeof(snd_pcm_plugin_voice_t); return plugin; } @@ -167,7 +194,7 @@ double snd_pcm_plugin_transfer_ratio(snd_pcm_t *pcm, int channel) { ssize_t transfer; - transfer = snd_pcm_plugin_transfer_size(pcm, channel, 1000000); + transfer = snd_pcm_plugin_client_size(pcm, channel, 1000000); if (transfer < 0) return 0; return (double)transfer / (double)1000000; @@ -255,14 +282,14 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params) if (params->mode == SND_PCM_MODE_STREAM) { pdprintf("params stream plugin\n"); - err = snd_pcm_plugin_build_stream(pcm, params->channel, &plugin); + err = snd_pcm_plugin_build_stream(pcm, params->channel, &hwparams.format, &plugin); } else if (params->mode == SND_PCM_MODE_BLOCK) { if (hwinfo.flags & SND_PCM_CHNINFO_MMAP) { pdprintf("params mmap plugin\n"); - err = snd_pcm_plugin_build_mmap(pcm, params->channel, &plugin); + err = snd_pcm_plugin_build_mmap(pcm, params->channel, &hwparams.format, &plugin); } else { pdprintf("params block plugin\n"); - err = snd_pcm_plugin_build_block(pcm, params->channel, &plugin); + err = snd_pcm_plugin_build_block(pcm, params->channel, &hwparams.format, &plugin); } } else { return -EINVAL; @@ -313,11 +340,11 @@ int snd_pcm_plugin_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup) return err; if (setup->mode == SND_PCM_MODE_STREAM) { pdprintf("params setup: queue_size = %i\n", setup->buf.stream.queue_size); - setup->buf.stream.queue_size = snd_pcm_plugin_transfer_size(pcm, setup->channel, setup->buf.stream.queue_size); + setup->buf.stream.queue_size = snd_pcm_plugin_client_size(pcm, setup->channel, setup->buf.stream.queue_size); pdprintf("params setup: queue_size = %i\n", setup->buf.stream.queue_size); } else if (setup->mode == SND_PCM_MODE_BLOCK) { pdprintf("params setup: frag_size = %i\n", setup->buf.block.frag_size); - setup->buf.block.frag_size = snd_pcm_plugin_transfer_size(pcm, setup->channel, setup->buf.block.frag_size); + setup->buf.block.frag_size = snd_pcm_plugin_client_size(pcm, setup->channel, setup->buf.block.frag_size); pdprintf("params setup: frag_size = %i\n", setup->buf.block.frag_size); } else { return -EINVAL; @@ -338,9 +365,10 @@ int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_channel_status_t *status) ratio = snd_pcm_plugin_transfer_ratio(pcm, status->channel); if (ratio <= 0) return -EINVAL; - status->scount = snd_pcm_plugin_transfer_size(pcm, status->channel, status->scount); - status->count = snd_pcm_plugin_transfer_size(pcm, status->channel, status->count); - status->free = snd_pcm_plugin_transfer_size(pcm, status->channel, status->free); + /* FIXME: scount may overflow */ + status->scount = snd_pcm_plugin_client_size(pcm, status->channel, status->scount); + status->count = snd_pcm_plugin_client_size(pcm, status->channel, status->count); + status->free = snd_pcm_plugin_client_size(pcm, status->channel, status->free); return 0; } @@ -372,46 +400,298 @@ int snd_pcm_plugin_flush(snd_pcm_t *pcm, int channel) return snd_pcm_channel_flush(pcm, channel); } +ssize_t snd_pcm_plugin_transfer_size(snd_pcm_t *pcm, int channel) +{ + ssize_t result; + + if ((result = snd_pcm_transfer_size(pcm, channel)) < 0) + return result; + return snd_pcm_plugin_client_size(pcm, channel, result); +} + int snd_pcm_plugin_pointer(snd_pcm_t *pcm, int channel, void **ptr, size_t *size) { - snd_pcm_plugin_t *plugin; - int err; + snd_pcm_plugin_t *plugin = NULL; + snd_pcm_plugin_voice_t *voices; + size_t samples; + int width; - if (!ptr || !size) + if (!ptr || !size || *size < 1) return -EINVAL; *ptr = NULL; if (!pcm || channel < 0 || channel > 1) return -EINVAL; - plugin = pcm->plugin_first[channel]; - if (!plugin) - return -EINVAL; - if (plugin->transfer_src_ptr) { - err = plugin->transfer_src_ptr(plugin, (char **)ptr, size); - if (err >= 0) - return 0; + if ((*size = snd_pcm_plugin_transfer_size(pcm, channel)) < 0) + return *size; + if (channel == SND_PCM_CHANNEL_PLAYBACK && plugin->src_voices) { + plugin = pcm->plugin_first[channel]; + if (!plugin) + goto __skip; + if (!plugin->src_format.interleave) + goto __skip; + if ((width = snd_pcm_format_width(plugin->src_format.format)) < 0) + return width; + samples = *size * width; + if ((samples % (plugin->src_format.voices * 8)) != 0) + return -EINVAL; + samples /= (plugin->src_format.voices * 8); + pcm->plugin_alloc_xchannel = SND_PCM_CHANNEL_PLAYBACK; + if (plugin->src_voices(plugin, &voices, samples, + snd_pcm_plugin_ptr_alloc) < 0) + goto __skip; + *ptr = voices->addr; + return 0; + } else if (channel == SND_PCM_CHANNEL_CAPTURE && plugin->dst_voices) { + plugin = pcm->plugin_last[channel]; + if (!plugin) + goto __skip; + if (plugin->dst_format.interleave) + goto __skip; + if ((width = snd_pcm_format_width(plugin->dst_format.format)) < 0) + return width; + samples = *size * width; + if ((samples % (plugin->dst_format.voices * 8)) != 0) + return -EINVAL; + samples /= (plugin->src_format.voices * 8); + pcm->plugin_alloc_xchannel = SND_PCM_CHANNEL_CAPTURE; + if (plugin->dst_voices(plugin, &voices, *size, + snd_pcm_plugin_ptr_alloc) < 0) + goto __skip; + *ptr = voices->addr; + return 0; } - if (pcm->plugin_alloc_xptr[channel]) { - if (pcm->plugin_alloc_xsize[channel] >= *size) { - *ptr = (char *)pcm->plugin_alloc_xptr[channel]; - return 0; + __skip: + *ptr = snd_pcm_plugin_ptr_alloc(pcm, *size); + if (*ptr == NULL) + return -ENOMEM; + return 0; +} + +ssize_t snd_pcm_plugin_write(snd_pcm_t *pcm, const void *buffer, size_t count) +{ + snd_pcm_plugin_t *plugin; + + if ((plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK)) == NULL) + return snd_pcm_write(pcm, buffer, count); + if (plugin->src_format.interleave) { + struct iovec vec; + vec.iov_base = (void *)buffer; + vec.iov_len = count; + return snd_pcm_plugin_writev(pcm, &vec, 1); + } else { + int idx, voices = plugin->src_format.voices; + int size = count / voices; + struct iovec vec[voices]; + for (idx = 0; idx < voices; idx++) { + vec[idx].iov_base = (char *)buffer + (size * idx); + vec[idx].iov_len = size; } - *ptr = (char *)realloc(pcm->plugin_alloc_xptr[channel], *size); + return snd_pcm_plugin_writev(pcm, vec, voices); + } +} + +ssize_t snd_pcm_plugin_read(snd_pcm_t *pcm, void *buffer, size_t count) +{ + snd_pcm_plugin_t *plugin; + + if ((plugin = snd_pcm_plugin_last(pcm, SND_PCM_CHANNEL_CAPTURE)) == NULL) + return snd_pcm_write(pcm, buffer, count); + if (plugin->dst_format.interleave) { + struct iovec vec; + vec.iov_base = buffer; + vec.iov_len = count; + return snd_pcm_plugin_readv(pcm, &vec, 1); } else { - *ptr = (char *)malloc(*size); - if (*ptr != NULL) - pcm->plugin_alloc_xsize[channel] = *size; + int idx, voices = plugin->dst_format.voices; + int size = count / voices; + struct iovec vec[voices]; + for (idx = 0; idx < voices; idx++) { + vec[idx].iov_base = (char *)buffer + (size * idx); + vec[idx].iov_len = size; + } + return snd_pcm_plugin_readv(pcm, vec, voices); } - if (*ptr == NULL) - return -ENOMEM; - pcm->plugin_alloc_xptr[channel] = *ptr; +} + +static int snd_pcm_plugin_load_vector(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_voice_t **voices, + const struct iovec *vector, + int count, + snd_pcm_format_t *format) +{ + snd_pcm_plugin_voice_t *v = plugin->voices; + int width, cvoices, voice; + + *voices = NULL; + if ((width = snd_pcm_format_width(format->format)) < 0) + return width; + cvoices = format->voices; + if (format->interleave) { + if (count != 1) + return -EINVAL; + for (voice = 0; voice < cvoices; voice++, v++) { + v->aptr = NULL; + if ((v->addr = vector->iov_base) == NULL) + return -EINVAL; + v->offset = voice * width; + v->next = cvoices * width; + } + } else { + if (count != cvoices) + return -EINVAL; + for (voice = 0; voice < cvoices; voice++, v++) { + v->aptr = NULL; + v->addr = vector[voice].iov_base; + v->offset = 0; + v->next = width; + } + } + *voices = plugin->voices; return 0; } -static void *snd_pcm_plugin_malloc(snd_pcm_t *pcm, long size) +static inline int snd_pcm_plugin_load_src_vector(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_voice_t **voices, + const struct iovec *vector, + int count) +{ + return snd_pcm_plugin_load_vector(plugin, voices, vector, count, &plugin->src_format); +} + +static inline int snd_pcm_plugin_load_dst_vector(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_voice_t **voices, + const struct iovec *vector, + int count) +{ + return snd_pcm_plugin_load_vector(plugin, voices, vector, count, &plugin->dst_format); +} + +ssize_t snd_pcm_plugin_writev(snd_pcm_t *pcm, const struct iovec *vector, int count) +{ + snd_pcm_plugin_t *plugin, *next; + snd_pcm_plugin_voice_t *src_voices, *dst_voices; + size_t samples; + ssize_t size; + int idx, err; + + if ((plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK)) == NULL) + return snd_pcm_writev(pcm, vector, count); + if ((err = snd_pcm_plugin_load_src_vector(plugin, &src_voices, vector, count)) < 0) + return err; + size = 0; + for (idx = 0; idx < count; idx++) + size += vector[idx].iov_len; + size = snd_pcm_plugin_src_size_to_samples(plugin, size); + if (size < 0) + return size; + samples = size; + while (plugin) { + if ((next = plugin->next) != NULL) { + if (next->src_voices) { + if ((err = next->src_voices(next, &dst_voices, samples, snd_pcm_plugin_buf_alloc)) < 0) { + snd_pcm_plugin_buf_free(pcm, src_voices->aptr); + return err; + } + } else { + if ((err = snd_pcm_plugin_src_voices(next, &dst_voices, samples)) < 0) { + snd_pcm_plugin_buf_free(pcm, src_voices->aptr); + return err; + } + } + } else { + dst_voices = NULL; + } + pdprintf("write plugin: %s, %i\n", plugin->name, samples); + if ((size = plugin->transfer(plugin, src_voices, dst_voices, samples))<0) { + snd_pcm_plugin_buf_free(pcm, src_voices->aptr); + if (dst_voices) + snd_pcm_plugin_buf_free(pcm, dst_voices->aptr); + return size; + } + snd_pcm_plugin_buf_free(pcm, src_voices->aptr); + plugin = plugin->next; + src_voices = dst_voices; + samples = size; + } + samples = snd_pcm_plugin_client_samples(pcm, SND_PCM_CHANNEL_PLAYBACK, samples); + size = snd_pcm_plugin_src_samples_to_size(pcm->plugin_first[SND_PCM_CHANNEL_PLAYBACK], samples); + if (size < 0) + return size; + pdprintf("writev result = %i\n", size); + return size; +} + +ssize_t snd_pcm_plugin_readv(snd_pcm_t *pcm, const struct iovec *vector, int count) +{ + snd_pcm_plugin_t *plugin, *next; + snd_pcm_plugin_voice_t *src_voices = NULL, *dst_voices; + size_t samples; + ssize_t size; + int idx, err; + + if ((plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_CAPTURE)) == NULL) + return snd_pcm_readv(pcm, vector, count); + if (vector == NULL) + return -EINVAL; + size = 0; + for (idx = 0; idx < count; idx++) + size += vector[idx].iov_len; + if (size < 0) + return size; + samples = snd_pcm_plugin_dst_size_to_samples(pcm->plugin_last[SND_PCM_CHANNEL_CAPTURE], size); + samples = snd_pcm_plugin_hardware_samples(pcm, SND_PCM_CHANNEL_CAPTURE, samples); + while (plugin && samples > 0) { + if ((next = plugin->next) != NULL) { + if (plugin->dst_voices) { + if ((err = plugin->dst_voices(plugin, &dst_voices, samples, snd_pcm_plugin_buf_alloc)) < 0) { + if (src_voices) + snd_pcm_plugin_buf_free(pcm, src_voices->aptr); + return err; + } + } else { + if ((err = snd_pcm_plugin_dst_voices(plugin, &dst_voices, samples)) < 0) { + if (src_voices) + snd_pcm_plugin_buf_free(pcm, src_voices->aptr); + return err; + } + } + } else { + if ((err = snd_pcm_plugin_load_dst_vector(plugin, &dst_voices, vector, count)) < 0) { + if (src_voices) + snd_pcm_plugin_buf_free(pcm, src_voices->aptr); + return err; + } + } + pdprintf("read plugin: %s, %i\n", plugin->name, samples); + if ((size = plugin->transfer(plugin, src_voices, dst_voices, samples))<0) { + if (src_voices) + snd_pcm_plugin_buf_free(pcm, src_voices->aptr); + snd_pcm_plugin_buf_free(pcm, dst_voices->aptr); + return size; + } + if (src_voices) + snd_pcm_plugin_buf_free(pcm, src_voices->aptr); + plugin = plugin->next; + src_voices = dst_voices; + samples = size; + } + snd_pcm_plugin_buf_free(pcm, dst_voices->aptr); + size = snd_pcm_plugin_dst_samples_to_size(pcm->plugin_last[SND_PCM_CHANNEL_CAPTURE], samples); + pdprintf("readv result = %i\n", size); + return size; +} + +/* + * Plugin helpers + */ + +static void *snd_pcm_plugin_buf_alloc(snd_pcm_t *pcm, size_t size) { int idx; void *ptr; + if (pcm == NULL || size <= 0) + return NULL; for (idx = 0; idx < 4; idx++) { if (pcm->plugin_alloc_lock[idx]) continue; @@ -447,146 +727,89 @@ static void *snd_pcm_plugin_malloc(snd_pcm_t *pcm, long size) return NULL; } -static int snd_pcm_plugin_alloc_unlock(snd_pcm_t *pcm, void *ptr) +static void snd_pcm_plugin_buf_free(snd_pcm_t *pcm, void *ptr) { int idx; + if (pcm == NULL || ptr == NULL) + return; for (idx = 0; idx < 4; idx++) { if (pcm->plugin_alloc_ptr[idx] == ptr) { pcm->plugin_alloc_lock[idx] = 0; - return 0; + return; } } - return -ENOENT; } -ssize_t snd_pcm_plugin_write(snd_pcm_t *pcm, const void *buffer, size_t count) +static void *snd_pcm_plugin_ptr_alloc(snd_pcm_t *pcm, size_t size) { - snd_pcm_plugin_t *plugin, *next; - char *dst_ptr, *dst_ptr1 = NULL, *src_ptr, *src_ptr1 = NULL; - size_t dst_size, src_size; - ssize_t size = 0, result = 0; - int err; + void *ptr; + int channel = pcm->plugin_alloc_xchannel; - if ((plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK)) == NULL) - return snd_pcm_write(pcm, buffer, count); - src_ptr = (char *)buffer; - dst_size = src_size = count; - while (plugin) { - next = plugin->next; - if (plugin->dst_size) { - dst_size = plugin->dst_size(plugin, dst_size); - if (dst_size < 0) { - result = dst_size; - goto __free; - } - } - if (next != NULL) { - if (next->transfer_src_ptr) { - if ((err = next->transfer_src_ptr(next, &dst_ptr, &dst_size)) < 0) { - if (dst_ptr == NULL) - goto __alloc; - result = err; - goto __free; - } - } else { - __alloc: - dst_ptr = dst_ptr1 = (char *)snd_pcm_plugin_malloc(pcm, dst_size); - if (dst_ptr == NULL) { - result = -ENOMEM; - goto __free; - } - } - } else { - dst_ptr = src_ptr; - dst_size = src_size; - } - pdprintf("write plugin: %s, %i, %i\n", plugin->name, src_size, dst_size); - if ((size = plugin->transfer(plugin, src_ptr, src_size, - dst_ptr, dst_size))<0) { - result = size; - goto __free; - } - if (src_ptr1) - snd_pcm_plugin_alloc_unlock(pcm, src_ptr1); - plugin = next; - src_ptr = dst_ptr; - src_ptr1 = dst_ptr1; - dst_ptr1 = NULL; - src_size = dst_size = size; + if (pcm->plugin_alloc_xptr[channel]) { + if (pcm->plugin_alloc_xsize[channel] >= size) + return pcm->plugin_alloc_xptr[channel]; + ptr = realloc(pcm->plugin_alloc_xptr[channel], size); + } else { + ptr = malloc(size); } - result = snd_pcm_plugin_transfer_size(pcm, SND_PCM_CHANNEL_PLAYBACK, size); - pdprintf("size = %i, result = %i, count = %i\n", size, result, count); - __free: - if (dst_ptr1) - snd_pcm_plugin_alloc_unlock(pcm, dst_ptr1); - if (src_ptr1) - snd_pcm_plugin_alloc_unlock(pcm, src_ptr1); - return result; + if (ptr == NULL) + return NULL; + pcm->plugin_alloc_xptr[channel] = (char *)ptr; + pcm->plugin_alloc_xsize[channel] = size; + return ptr; } -ssize_t snd_pcm_plugin_read(snd_pcm_t *pcm, void *buffer, size_t count) +static int snd_pcm_plugin_xvoices(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_voice_t **voices, + size_t samples, + snd_pcm_format_t *format) { - snd_pcm_plugin_t *plugin, *next; - char *dst_ptr, *dst_ptr1 = NULL, *src_ptr, *src_ptr1 = NULL; - size_t dst_size, src_size; - ssize_t size = 0, result = 0; - int err; - - if ((plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_CAPTURE)) == NULL) - return snd_pcm_read(pcm, buffer, count); - src_ptr = NULL; - src_size = 0; - dst_size = snd_pcm_plugin_hardware_size(pcm, SND_PCM_CHANNEL_CAPTURE, count); - if (dst_size < 0) - return dst_size; - while (plugin) { - next = plugin->next; - if (plugin->dst_size) { - dst_size = plugin->dst_size(plugin, dst_size); - if (dst_size < 0) { - result = dst_size; - goto __free; - } - } - if (next != NULL) { - if (next->transfer_src_ptr) { - if ((err = next->transfer_src_ptr(next, &dst_ptr, &dst_size)) < 0) { - if (dst_ptr == NULL) - goto __alloc; - result = err; - goto __free; - } - } else { - __alloc: - dst_ptr = dst_ptr1 = (char *)snd_pcm_plugin_malloc(pcm, dst_size); - if (dst_ptr == NULL) { - result = -ENOMEM; - goto __free; - } - } + char *ptr; + int width, voice; + long size; + snd_pcm_plugin_voice_t *v; + + *voices = NULL; + if ((width = snd_pcm_format_width(format->format)) < 0) + return width; + size = format->voices * samples * width; + if ((size % 8) != 0) + return -EINVAL; + size /= 8; + ptr = (char *)snd_pcm_plugin_buf_alloc(plugin->handle, size); + if (ptr == NULL) + return -ENOMEM; + if ((size % format->voices) != 0) + return -EINVAL; + size /= format->voices; + v = plugin->voices; + for (voice = 0; voice < format->voices; voice++, v++) { + v->aptr = ptr; + if (format->interleave) { + v->addr = ptr; + v->offset = voice * width; + v->next = format->voices * width; } else { - dst_ptr = buffer; - } - pdprintf("read plugin: %s, %i, %i\n", plugin->name, src_size, dst_size); - if ((size = plugin->transfer(plugin, src_ptr, src_size, - dst_ptr, dst_size))<0) { - result = size; - goto __free; + v->addr = ptr + (voice * size); + v->offset = 0; + v->next = width; } - if (dst_ptr1) - snd_pcm_plugin_alloc_unlock(pcm, dst_ptr1); - plugin = plugin->next; - src_ptr = dst_ptr; - src_ptr1 = dst_ptr1; - dst_ptr1 = NULL; - src_size = dst_size = size; } - result = size; - __free: - if (dst_ptr1) - snd_pcm_plugin_alloc_unlock(pcm, dst_ptr1); - if (src_ptr1) - snd_pcm_plugin_alloc_unlock(pcm, src_ptr1); - return result; + *voices = plugin->voices; + return 0; +} + +int snd_pcm_plugin_src_voices(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_voice_t **voices, + size_t samples) +{ + return snd_pcm_plugin_xvoices(plugin, voices, samples, &plugin->src_format); +} + +int snd_pcm_plugin_dst_voices(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_voice_t **voices, + size_t samples) +{ + return snd_pcm_plugin_xvoices(plugin, voices, samples, &plugin->dst_format); } diff --git a/src/pcm/pcm_plugin_build.c b/src/pcm/pcm_plugin_build.c index 5c008ba0..7da4f233 100644 --- a/src/pcm/pcm_plugin_build.c +++ b/src/pcm/pcm_plugin_build.c @@ -25,82 +25,199 @@ #endif #include "../include/driver.h" #include "../include/pcm.h" -typedef snd_pcm_runtime_t PLUGIN_BASE; #define snd_pcm_plugin_first(pb, channel) ((pb)->oss.plugin_first) #define snd_pcm_plugin_last(pb, channel) ((pb)->oss.plugin_last) #define snd_pcm_plugin_append(pb, channel, plugin) snd_pcm_oss_plugin_append(pb, plugin) #define my_calloc(size) snd_kcalloc(size, GFP_KERNEL) #define my_free(ptr) snd_kfree(ptr) +#define my_strdup(str) snd_kmalloc_strdup(str, GFP_KERNEL) #else #include #include +#include +#include #include "pcm_local.h" -typedef snd_pcm_t PLUGIN_BASE; #define my_calloc(size) calloc(1, size) #define my_free(ptr) free(ptr) +#define my_strdup(str) strdup(str) #endif +ssize_t snd_pcm_plugin_src_samples_to_size(snd_pcm_plugin_t *plugin, size_t samples) +{ + ssize_t result; + + if (plugin == NULL) + return -EINVAL; + result = samples * plugin->src_format.voices * plugin->src_width; + if ((result % 8) != 0) + return -EINVAL; + return result / 8; +} + +ssize_t snd_pcm_plugin_dst_samples_to_size(snd_pcm_plugin_t *plugin, size_t samples) +{ + ssize_t result; + + if (plugin == NULL) + return -EINVAL; + result = samples * plugin->dst_format.voices * plugin->dst_width; + if ((result % 8) != 0) + return -EINVAL; + return result / 8; +} + +ssize_t snd_pcm_plugin_src_size_to_samples(snd_pcm_plugin_t *plugin, size_t size) +{ + ssize_t result; + long tmp; + + if (plugin == NULL) + return -EINVAL; + result = size * 8; + tmp = plugin->src_format.voices * plugin->src_width; + if ((result % tmp) != 0) + return -EINVAL; + return result / tmp; +} + +ssize_t snd_pcm_plugin_dst_size_to_samples(snd_pcm_plugin_t *plugin, size_t size) +{ + ssize_t result; + long tmp; + + if (plugin == NULL) + return -EINVAL; + result = size * 8; + tmp = plugin->dst_format.voices * plugin->dst_width; + if ((result % tmp) != 0) + return -EINVAL; + return result / tmp; +} -ssize_t snd_pcm_plugin_transfer_size(PLUGIN_BASE *pb, int channel, size_t drv_size) +ssize_t snd_pcm_plugin_client_samples(snd_pcm_plugin_handle_t *pb, int channel, size_t drv_samples) { snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next; if (pb == NULL || (channel != SND_PCM_CHANNEL_PLAYBACK && - channel != SND_PCM_CHANNEL_CAPTURE)) + channel != SND_PCM_CHANNEL_CAPTURE)) return -EINVAL; - if (drv_size == 0) + if (drv_samples == 0) return 0; - if (drv_size < 0) + if (drv_samples < 0) return -EINVAL; if (channel == SND_PCM_CHANNEL_PLAYBACK) { - plugin = snd_pcm_plugin_last(pb, channel); - while (plugin) { + plugin = snd_pcm_plugin_last(pb, SND_PCM_CHANNEL_PLAYBACK); + while (plugin && drv_samples > 0) { plugin_prev = plugin->prev; - if (plugin->src_size) - drv_size = plugin->src_size(plugin, drv_size); + if (plugin->src_samples) + drv_samples = plugin->src_samples(plugin, drv_samples); plugin = plugin_prev; } } else if (channel == SND_PCM_CHANNEL_CAPTURE) { - plugin = snd_pcm_plugin_first(pb, channel); - while (plugin) { + plugin = snd_pcm_plugin_first(pb, SND_PCM_CHANNEL_CAPTURE); + while (plugin && drv_samples > 0) { plugin_next = plugin->next; - if (plugin->dst_size) - drv_size = plugin->dst_size(plugin, drv_size); + if (plugin->dst_samples) + drv_samples = plugin->dst_samples(plugin, drv_samples); plugin = plugin_next; } } - return drv_size; + return drv_samples; } -ssize_t snd_pcm_plugin_hardware_size(PLUGIN_BASE *pb, int channel, size_t trf_size) +ssize_t snd_pcm_plugin_hardware_samples(snd_pcm_plugin_handle_t *pb, int channel, size_t clt_samples) { snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next; if (pb == NULL || (channel != SND_PCM_CHANNEL_PLAYBACK && - channel != SND_PCM_CHANNEL_CAPTURE)) + channel != SND_PCM_CHANNEL_CAPTURE)) return -EINVAL; - if (trf_size == 0) + if (clt_samples == 0) return 0; - if (trf_size < 0) + if (clt_samples < 0) return -EINVAL; if (channel == SND_PCM_CHANNEL_PLAYBACK) { - plugin = snd_pcm_plugin_first(pb, channel); - while (plugin) { + plugin = snd_pcm_plugin_first(pb, SND_PCM_CHANNEL_PLAYBACK); + while (plugin && clt_samples > 0) { plugin_next = plugin->next; - if (plugin->dst_size) - trf_size = plugin->dst_size(plugin, trf_size); + if (plugin->dst_samples) + clt_samples = plugin->dst_samples(plugin, clt_samples); plugin = plugin_next; } + if (clt_samples < 0) + return clt_samples; } else if (channel == SND_PCM_CHANNEL_CAPTURE) { - plugin = snd_pcm_plugin_last(pb, channel); + plugin = snd_pcm_plugin_last(pb, SND_PCM_CHANNEL_CAPTURE); while (plugin) { plugin_prev = plugin->prev; - if (plugin->src_size) - trf_size = plugin->src_size(plugin, trf_size); + if (plugin->src_samples) + clt_samples = plugin->src_samples(plugin, clt_samples); plugin = plugin_prev; } } - return trf_size; + return clt_samples; +} + +ssize_t snd_pcm_plugin_client_size(snd_pcm_plugin_handle_t *pb, int channel, size_t drv_size) +{ + snd_pcm_plugin_t *plugin; + ssize_t result = 0; + + if (pb == NULL || (channel != SND_PCM_CHANNEL_PLAYBACK && + channel != SND_PCM_CHANNEL_CAPTURE)) + return -EINVAL; + if (drv_size == 0) + return 0; + if (drv_size < 0) + return -EINVAL; + if (channel == SND_PCM_CHANNEL_PLAYBACK) { + plugin = snd_pcm_plugin_last(pb, SND_PCM_CHANNEL_PLAYBACK); + result = snd_pcm_plugin_src_size_to_samples(plugin, drv_size); + result = snd_pcm_plugin_client_samples(pb, SND_PCM_CHANNEL_PLAYBACK, result); + if (result < 0) + return result; + plugin = snd_pcm_plugin_first(pb, SND_PCM_CHANNEL_PLAYBACK); + result = snd_pcm_plugin_src_samples_to_size(plugin, result); + } else if (channel == SND_PCM_CHANNEL_CAPTURE) { + plugin = snd_pcm_plugin_first(pb, SND_PCM_CHANNEL_CAPTURE); + result = snd_pcm_plugin_src_size_to_samples(plugin, drv_size); + result = snd_pcm_plugin_client_samples(pb, SND_PCM_CHANNEL_PLAYBACK, result); + plugin = snd_pcm_plugin_last(pb, SND_PCM_CHANNEL_CAPTURE); + result = snd_pcm_plugin_dst_samples_to_size(plugin, result); + } + return result; +} + +ssize_t snd_pcm_plugin_hardware_size(snd_pcm_plugin_handle_t *pb, int channel, size_t clt_size) +{ + snd_pcm_plugin_t *plugin; + ssize_t result = 0; + + if (pb == NULL || (channel != SND_PCM_CHANNEL_PLAYBACK && + channel != SND_PCM_CHANNEL_CAPTURE)) + return -EINVAL; + if (clt_size == 0) + return 0; + if (clt_size < 0) + return -EINVAL; + if (channel == SND_PCM_CHANNEL_PLAYBACK) { + plugin = snd_pcm_plugin_first(pb, SND_PCM_CHANNEL_PLAYBACK); + result = snd_pcm_plugin_src_size_to_samples(plugin, clt_size); + result = snd_pcm_plugin_hardware_samples(pb, SND_PCM_CHANNEL_PLAYBACK, result); + if (result < 0) + return result; + plugin = snd_pcm_plugin_last(pb, SND_PCM_CHANNEL_PLAYBACK); + result = snd_pcm_plugin_dst_samples_to_size(plugin, result); + } else if (channel == SND_PCM_CHANNEL_CAPTURE) { + plugin = snd_pcm_plugin_last(pb, SND_PCM_CHANNEL_CAPTURE); + result = snd_pcm_plugin_src_size_to_samples(plugin, clt_size); + result = snd_pcm_plugin_hardware_samples(pb, SND_PCM_CHANNEL_PLAYBACK, result); + if (result < 0) + return result; + plugin = snd_pcm_plugin_first(pb, SND_PCM_CHANNEL_CAPTURE); + result = snd_pcm_plugin_dst_samples_to_size(plugin, result); + } + return result; } @@ -232,7 +349,7 @@ int snd_pcm_plugin_hwparams(snd_pcm_channel_params_t *params, #define ROUTE_PLUGIN_RESOLUTION 16 -int snd_pcm_plugin_format(PLUGIN_BASE *pb, +int snd_pcm_plugin_format(snd_pcm_plugin_handle_t *pb, snd_pcm_channel_params_t *params, snd_pcm_channel_params_t *hwparams, snd_pcm_channel_info_t *hwinfo) @@ -272,6 +389,7 @@ int snd_pcm_plugin_format(PLUGIN_BASE *pb, /* voices reduction */ if (srcparams->format.voices > dstparams.format.voices) { +#if 0 int sv = srcparams->format.voices; int dv = dstparams.format.voices; int *ttable = my_calloc(dv*sv*sizeof(*ttable)); @@ -303,6 +421,10 @@ int snd_pcm_plugin_format(PLUGIN_BASE *pb, return err; } srcparams->format.voices = tmpparams.format.voices; +#else + snd_pcm_plugin_free(plugin); + return -EIO; +#endif } /* Convert to interleaved format if needed */ @@ -310,7 +432,8 @@ int snd_pcm_plugin_format(PLUGIN_BASE *pb, srcparams->format.voices > 1 && srcparams->format.rate != dstparams.format.rate) { tmpparams.format.interleave = 1; - err = snd_pcm_plugin_build_interleave(&srcparams->format, + err = snd_pcm_plugin_build_interleave(pb, + &srcparams->format, &tmpparams.format, &plugin); pdprintf("params interleave change: src=%i, dst=%i returns %i\n", srcparams->format.interleave, tmpparams.format.interleave, err); @@ -336,7 +459,8 @@ int snd_pcm_plugin_format(PLUGIN_BASE *pb, snd_pcm_format_width(srcparams->format.format) <= 16 && snd_pcm_format_width(srcparams->format.format) >= snd_pcm_format_width(srcparams->format.format)) { tmpparams.format.rate = dstparams.format.rate; - err = snd_pcm_plugin_build_rate(&srcparams->format, + err = snd_pcm_plugin_build_rate(pb, + &srcparams->format, &tmpparams.format, &plugin); pdprintf("params rate down resampling: src=%i, dst=%i returns %i\n", srcparams->format.rate, tmpparams.format.rate, err); @@ -359,18 +483,21 @@ int snd_pcm_plugin_format(PLUGIN_BASE *pb, tmpparams.format.format = SND_PCM_SFMT_S16_LE; switch (srcparams->format.format) { case SND_PCM_SFMT_MU_LAW: - err = snd_pcm_plugin_build_mulaw(&srcparams->format, + err = snd_pcm_plugin_build_mulaw(pb, + &srcparams->format, &tmpparams.format, &plugin); break; #ifndef __KERNEL__ case SND_PCM_SFMT_A_LAW: - err = snd_pcm_plugin_build_alaw(&srcparams->format, + err = snd_pcm_plugin_build_alaw(pb, + &srcparams->format, &tmpparams.format, &plugin); break; case SND_PCM_SFMT_IMA_ADPCM: - err = snd_pcm_plugin_build_adpcm(&srcparams->format, + err = snd_pcm_plugin_build_adpcm(pb, + &srcparams->format, &tmpparams.format, &plugin); break; @@ -394,27 +521,31 @@ int snd_pcm_plugin_format(PLUGIN_BASE *pb, tmpparams.format.format = dstparams.format.format; if (srcparams->format.format == SND_PCM_SFMT_MU_LAW || tmpparams.format.format == SND_PCM_SFMT_MU_LAW) { - err = snd_pcm_plugin_build_mulaw(&srcparams->format, + err = snd_pcm_plugin_build_mulaw(pb, + &srcparams->format, &tmpparams.format, &plugin); } #ifndef __KERNEL__ else if (srcparams->format.format == SND_PCM_SFMT_A_LAW || tmpparams.format.format == SND_PCM_SFMT_A_LAW) { - err = snd_pcm_plugin_build_alaw(&srcparams->format, + err = snd_pcm_plugin_build_alaw(pb, + &srcparams->format, &tmpparams.format, &plugin); } else if (srcparams->format.format == SND_PCM_SFMT_IMA_ADPCM || tmpparams.format.format == SND_PCM_SFMT_IMA_ADPCM) { - err = snd_pcm_plugin_build_adpcm(&srcparams->format, + err = snd_pcm_plugin_build_adpcm(pb, + &srcparams->format, &tmpparams.format, &plugin); } #endif else if (snd_pcm_format_linear(srcparams->format.format) && snd_pcm_format_linear(tmpparams.format.format)) { - err = snd_pcm_plugin_build_linear(&srcparams->format, + err = snd_pcm_plugin_build_linear(pb, + &srcparams->format, &tmpparams.format, &plugin); } @@ -434,7 +565,8 @@ int snd_pcm_plugin_format(PLUGIN_BASE *pb, /* rate resampling */ if (srcparams->format.rate != dstparams.format.rate) { tmpparams.format.rate = dstparams.format.rate; - err = snd_pcm_plugin_build_rate(&srcparams->format, + err = snd_pcm_plugin_build_rate(pb, + &srcparams->format, &tmpparams.format, &plugin); pdprintf("params rate resampling: src=%i, dst=%i return %i\n", srcparams->format.rate, tmpparams.format.rate, err); @@ -452,6 +584,7 @@ int snd_pcm_plugin_format(PLUGIN_BASE *pb, /* voices extension */ if (srcparams->format.voices < dstparams.format.voices) { +#if 0 int sv = srcparams->format.voices; int dv = dstparams.format.voices; int *ttable = my_calloc(dv * sv * sizeof(*ttable)); @@ -483,6 +616,10 @@ int snd_pcm_plugin_format(PLUGIN_BASE *pb, return err; } srcparams->format.voices = tmpparams.format.voices; +#else + snd_pcm_plugin_free(plugin); + return -EIO; +#endif } /* interleave change */ @@ -490,7 +627,8 @@ int snd_pcm_plugin_format(PLUGIN_BASE *pb, hwinfo->mode == SND_PCM_MODE_BLOCK && srcparams->format.interleave != dstparams.format.interleave) { tmpparams.format.interleave = dstparams.format.interleave; - err = snd_pcm_plugin_build_interleave(&srcparams->format, + err = snd_pcm_plugin_build_interleave(pb, + &srcparams->format, &tmpparams.format, &plugin); pdprintf("params interleave change: src=%i, dst=%i return %i\n", srcparams->format.interleave, tmpparams.format.interleave, err); diff --git a/src/pcm/plugin/Makefile.am b/src/pcm/plugin/Makefile.am index b0189bea..d350e71f 100644 --- a/src/pcm/plugin/Makefile.am +++ b/src/pcm/plugin/Makefile.am @@ -1,7 +1,8 @@ EXTRA_LTLIBRARIES = libpcmplugin.la libpcmplugin_la_SOURCES = block.c mmap.c stream.c linear.c interleave.c \ - mulaw.c alaw.c adpcm.c rate.c route.c + mulaw.c alaw.c adpcm.c rate.c +# route.c all: libpcmplugin.la diff --git a/src/pcm/plugin/adpcm.c b/src/pcm/plugin/adpcm.c index 870691d4..455ff0b4 100644 --- a/src/pcm/plugin/adpcm.c +++ b/src/pcm/plugin/adpcm.c @@ -86,7 +86,7 @@ static void adpcm_init_state(adpcm_state_t * state_ptr) state_ptr->io_shift = 4; } -static inline char adpcm_encoder(int sl, adpcm_state_t * state) +static char adpcm_encoder(int sl, adpcm_state_t * state) { short diff; /* Difference between sl and predicted sample */ short pred_diff; /* Predicted difference to next sample */ @@ -147,7 +147,7 @@ static inline char adpcm_encoder(int sl, adpcm_state_t * state) } -static inline int adpcm_decoder(unsigned char code, adpcm_state_t * state) +static int adpcm_decoder(unsigned char code, adpcm_state_t * state) { short pred_diff; /* Predicted difference to next sample */ short step; /* holds previous StepSize value */ @@ -197,397 +197,174 @@ static inline int adpcm_decoder(unsigned char code, adpcm_state_t * state) * Basic Ima-ADPCM plugin */ -typedef enum { - _S8_ADPCM, - _U8_ADPCM, - _S16LE_ADPCM, - _U16LE_ADPCM, - _S16BE_ADPCM, - _U16BE_ADPCM, - _ADPCM_S8, - _ADPCM_U8, - _ADPCM_S16LE, - _ADPCM_U16LE, - _ADPCM_S16BE, - _ADPCM_U16BE -} combination_t; - -struct adpcm_private_data { - combination_t cmd; - adpcm_state_t state; -}; - -static void adpcm_conv_u8bit_adpcm(adpcm_state_t * state_ptr, unsigned char *src_ptr, unsigned char *dst_ptr, size_t size) -{ - unsigned int pcm; - - while (size-- > 0) { - pcm = ((*src_ptr++) ^ 0x80) << 8; - - state_ptr->io_buffer |= adpcm_encoder((signed short)(pcm), state_ptr) << state_ptr->io_shift; - if (!(state_ptr->io_shift)) { - *dst_ptr++ = state_ptr->io_buffer & 0xff; - state_ptr->io_buffer = 0; - } - state_ptr->io_shift ^= 4; - } - if (!(state_ptr->io_shift)) { - *dst_ptr = state_ptr->io_buffer & 0xf0; - } -} - -static void adpcm_conv_s8bit_adpcm(adpcm_state_t * state_ptr, unsigned char *src_ptr, unsigned char *dst_ptr, size_t size) -{ - unsigned int pcm; - - while (size-- > 0) { - pcm = *src_ptr++ << 8; - - state_ptr->io_buffer |= adpcm_encoder((signed short)(pcm), state_ptr) << state_ptr->io_shift; - if (!(state_ptr->io_shift)) { - *dst_ptr++ = state_ptr->io_buffer & 0xff; - state_ptr->io_buffer = 0; - } - state_ptr->io_shift ^= 4; - } - if (!(state_ptr->io_shift)) { - *dst_ptr = state_ptr->io_buffer & 0xf0; - } -} - -static void adpcm_conv_s16bit_adpcm(adpcm_state_t * state_ptr, unsigned short *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) { - state_ptr->io_buffer |= adpcm_encoder((signed short)(*src_ptr++), state_ptr) << state_ptr->io_shift; - if (!(state_ptr->io_shift)) { - *dst_ptr++ = state_ptr->io_buffer & 0xff; - state_ptr->io_buffer = 0; - } - state_ptr->io_shift ^= 4; - } - if (!(state_ptr->io_shift)) { - *dst_ptr = state_ptr->io_buffer & 0xf0; - } -} - -static void adpcm_conv_s16bit_swap_adpcm(adpcm_state_t * state_ptr, unsigned short *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) { - state_ptr->io_buffer |= adpcm_encoder((signed short)(bswap_16(*src_ptr++)), state_ptr) << state_ptr->io_shift; - if (!(state_ptr->io_shift)) { - *dst_ptr++ = state_ptr->io_buffer & 0xff; - state_ptr->io_buffer = 0; - } - state_ptr->io_shift ^= 4; - } - if (!(state_ptr->io_shift)) { - *dst_ptr = state_ptr->io_buffer & 0xf0; - } -} - -static void adpcm_conv_u16bit_adpcm(adpcm_state_t * state_ptr, unsigned short *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) { - state_ptr->io_buffer |= adpcm_encoder((signed short)((*src_ptr++) ^ 0x8000), state_ptr) << state_ptr->io_shift; - if (!(state_ptr->io_shift)) { - *dst_ptr++ = state_ptr->io_buffer & 0xff; - state_ptr->io_buffer = 0; - } - state_ptr->io_shift ^= 4; - } - if (!(state_ptr->io_shift)) { - *dst_ptr = state_ptr->io_buffer & 0xf0; - } -} +typedef void (*adpcm_f)(adpcm_state_t *state, void *src_ptr, void *dst_ptr, int samples); -static void adpcm_conv_u16bit_swap_adpcm(adpcm_state_t * state_ptr, unsigned short *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) { - state_ptr->io_buffer |= adpcm_encoder((signed short)(bswap_16(*src_ptr++) ^ 0x8000), state_ptr) << state_ptr->io_shift; - if (!(state_ptr->io_shift)) { - *dst_ptr++ = state_ptr->io_buffer & 0xff; - state_ptr->io_buffer = 0; - } - state_ptr->io_shift ^= 4; - } - if (!(state_ptr->io_shift)) { - *dst_ptr = state_ptr->io_buffer & 0xf0; - } -} - -static void adpcm_conv_adpcm_u8bit(adpcm_state_t * state_ptr, unsigned char *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) { - if (state_ptr->io_shift) { - state_ptr->io_buffer = *src_ptr++; - } - *dst_ptr++ = (adpcm_decoder((state_ptr->io_buffer >> state_ptr->io_shift) & 0xf, state_ptr) >> 8) ^ 0x80; - state_ptr->io_shift ^= 4; - } -} - -static void adpcm_conv_adpcm_s8bit(adpcm_state_t * state_ptr, unsigned char *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) { - if (state_ptr->io_shift) { - state_ptr->io_buffer = *src_ptr++; - } - *dst_ptr++ = adpcm_decoder((state_ptr->io_buffer >> state_ptr->io_shift) & 0xf, state_ptr) >> 8; - state_ptr->io_shift ^= 4; - } -} - -static void adpcm_conv_adpcm_s16bit(adpcm_state_t * state_ptr, unsigned char *src_ptr, unsigned short *dst_ptr, size_t size) -{ - while (size-- > 0) { - if (state_ptr->io_shift) { - state_ptr->io_buffer = *src_ptr++; - } - *dst_ptr++ = adpcm_decoder((state_ptr->io_buffer >> state_ptr->io_shift) & 0xf, state_ptr); - state_ptr->io_shift ^= 4; - } +typedef struct adpcm_private_data { + adpcm_f func; + adpcm_state_t state; +} adpcm_t; + +#define ADPCM_FUNC_DECODE(name, dsttype, val) \ +static void adpcm_decode_##name(adpcm_state_t *state, \ + void *src_ptr, void *dst_ptr, int samples) \ +{ \ + unsigned char *src = src_ptr; \ + dsttype *dst = dst_ptr; \ + unsigned int s; \ + samples <<= 1; \ + while (samples--) { \ + if (state->io_shift) \ + state->io_buffer = *src++; \ + s = adpcm_decoder((state->io_buffer >> state->io_shift) & 0x0f, state); \ + *dst++ = val; \ + state->io_shift ^= 4; \ + } \ } -static void adpcm_conv_adpcm_swap_s16bit(adpcm_state_t * state_ptr, unsigned char *src_ptr, unsigned short *dst_ptr, size_t size) -{ - while (size-- > 0) { - if (state_ptr->io_shift) { - state_ptr->io_buffer = *src_ptr++; - } - *dst_ptr++ = bswap_16(adpcm_decoder((state_ptr->io_buffer >> state_ptr->io_shift) & 0xf, state_ptr)); - state_ptr->io_shift ^= 4; - } +#define ADPCM_FUNC_ENCODE(name, srctype, val) \ +static void adpcm_encode_##name(adpcm_state_t *state, \ + void *src_ptr, void *dst_ptr, int samples) \ +{ \ + srctype *src = src_ptr; \ + unsigned char *dst = dst_ptr; \ + unsigned int s; \ + samples <<= 1; \ + while (samples--) { \ + s = *src++; \ + state->io_buffer |= adpcm_encoder((signed short)(val), state) << state->io_shift; \ + if (state->io_shift == 0) { \ + *dst++ = state->io_buffer & 0xff; \ + state->io_buffer = 0; \ + } \ + state->io_shift ^= 4; \ + } \ } -static void adpcm_conv_adpcm_u16bit(adpcm_state_t * state_ptr, unsigned char *src_ptr, unsigned short *dst_ptr, size_t size) -{ - while (size-- > 0) { - if (state_ptr->io_shift) { - state_ptr->io_buffer = *src_ptr++; - } - *dst_ptr++ = adpcm_decoder((state_ptr->io_buffer >> state_ptr->io_shift) & 0xf, state_ptr) ^ 0x8000; - state_ptr->io_shift ^= 4; - } -} +ADPCM_FUNC_DECODE(u8, u_int8_t, (s >> 8) ^ 0x80) +ADPCM_FUNC_DECODE(s8, u_int8_t, s >> 8) +ADPCM_FUNC_DECODE(u16n, u_int16_t, s ^ 0x8000) +ADPCM_FUNC_DECODE(u16s, u_int16_t, bswap_16(s ^ 0x8000)) +ADPCM_FUNC_DECODE(s16n, u_int16_t, s) +ADPCM_FUNC_DECODE(s16s, u_int16_t, bswap_16(s)) +ADPCM_FUNC_DECODE(u24n, u_int32_t, (s << 8) ^ 0x800000) +ADPCM_FUNC_DECODE(u24s, u_int32_t, bswap_32((s << 8) ^ 0x800000)) +ADPCM_FUNC_DECODE(s24n, u_int32_t, s << 8) +ADPCM_FUNC_DECODE(s24s, u_int32_t, bswap_32(s << 8)) +ADPCM_FUNC_DECODE(u32n, u_int32_t, (s << 16) ^ 0x80000000) +ADPCM_FUNC_DECODE(u32s, u_int32_t, bswap_32((s << 16) ^ 0x80000000)) +ADPCM_FUNC_DECODE(s32n, u_int32_t, s << 16) +ADPCM_FUNC_DECODE(s32s, u_int32_t, bswap_32(s << 16)) + +ADPCM_FUNC_ENCODE(u8, u_int8_t, s << 8) +ADPCM_FUNC_ENCODE(s8, u_int8_t, (s << 8) ^ 0x8000) +ADPCM_FUNC_ENCODE(u16n, u_int16_t, s ^ 0x8000) +ADPCM_FUNC_ENCODE(u16s, u_int16_t, bswap_16(s ^ 0x8000)) +ADPCM_FUNC_ENCODE(s16n, u_int16_t, s) +ADPCM_FUNC_ENCODE(s16s, u_int16_t, bswap_16(s)) +ADPCM_FUNC_ENCODE(u24n, u_int32_t, (s ^ 0x800000) >> 8) +ADPCM_FUNC_ENCODE(u24s, u_int32_t, bswap_32((s ^ 0x800000) >> 8)) +ADPCM_FUNC_ENCODE(s24n, u_int32_t, s >> 8) +ADPCM_FUNC_ENCODE(s24s, u_int32_t, bswap_32(s >> 8)) +ADPCM_FUNC_ENCODE(u32n, u_int32_t, (s ^ 0x80000000) >> 16) +ADPCM_FUNC_ENCODE(u32s, u_int32_t, bswap_32((s ^ 0x80000000) >> 16)) +ADPCM_FUNC_ENCODE(s32n, u_int32_t, s >> 16) +ADPCM_FUNC_ENCODE(s32s, u_int32_t, bswap_32(s >> 16)) + +/* wide, sign, swap endian */ +static adpcm_f adpcm_functions_decode[4 * 4 * 2 * 2] = { + adpcm_decode_u8, /* decode:8-bit:unsigned:none */ + adpcm_decode_u8, /* decode:8-bit:unsigned:swap */ + adpcm_decode_s8, /* decode:8-bit:signed:none */ + adpcm_decode_s8, /* decode:8-bit:signed:swap */ + adpcm_decode_u16n, /* decode:16-bit:unsigned:none */ + adpcm_decode_u16s, /* decode:16-bit:unsigned:swap */ + adpcm_decode_s16n, /* decode:16-bit:signed:none */ + adpcm_decode_s16s, /* decode:16-bit:signed:swap */ + adpcm_decode_u24n, /* decode:24-bit:unsigned:none */ + adpcm_decode_u24s, /* decode:24-bit:unsigned:swap */ + adpcm_decode_s24n, /* decode:24-bit:signed:none */ + adpcm_decode_s24s, /* decode:24-bit:signed:swap */ + adpcm_decode_u32n, /* decode:32-bit:unsigned:none */ + adpcm_decode_u32s, /* decode:32-bit:unsigned:swap */ + adpcm_decode_s32n, /* decode:32-bit:signed:none */ + adpcm_decode_s32s, /* decode:32-bit:signed:swap */ +}; -static void adpcm_conv_adpcm_swap_u16bit(adpcm_state_t * state_ptr, unsigned char *src_ptr, unsigned short *dst_ptr, size_t size) -{ - while (size-- > 0) { - if (state_ptr->io_shift) { - state_ptr->io_buffer = *src_ptr++; - } - *dst_ptr++ = bswap_16(adpcm_decoder((state_ptr->io_buffer >> state_ptr->io_shift) & 0xf, state_ptr) ^ 0x8000); - state_ptr->io_shift ^= 4; - } -} +/* wide, sign, swap endian */ +static adpcm_f adpcm_functions_encode[4 * 2 * 2] = { + adpcm_encode_u8, /* encode:8-bit:unsigned:none */ + adpcm_encode_u8, /* encode:8-bit:unsigned:swap */ + adpcm_encode_s8, /* encode:8-bit:signed:none */ + adpcm_encode_s8, /* encode:8-bit:signed:swap */ + adpcm_encode_u16n, /* encode:16-bit:unsigned:none */ + adpcm_encode_u16s, /* encode:16-bit:unsigned:swap */ + adpcm_encode_s16n, /* encode:16-bit:signed:none */ + adpcm_encode_s16s, /* encode:16-bit:signed:swap */ + adpcm_encode_u24n, /* encode:24-bit:unsigned:none */ + adpcm_encode_u24s, /* encode:24-bit:unsigned:swap */ + adpcm_encode_s24n, /* encode:24-bit:signed:none */ + adpcm_encode_s24s, /* encode:24-bit:signed:swap */ + adpcm_encode_u32n, /* encode:32-bit:unsigned:none */ + adpcm_encode_u32s, /* encode:32-bit:unsigned:swap */ + adpcm_encode_s32n, /* encode:32-bit:signed:none */ + adpcm_encode_s32s, /* encode:32-bit:signed:swap */ +}; -static ssize_t adpcm_transfer(snd_pcm_plugin_t * plugin, - char *src_ptr, size_t src_size, - char *dst_ptr, size_t dst_size) +static ssize_t adpcm_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_voice_t *src_voices, + const snd_pcm_plugin_voice_t *dst_voices, + size_t samples) { - struct adpcm_private_data *data; + adpcm_t *data; + int voice; - if (plugin == NULL || src_ptr == NULL || src_size < 0 || - dst_ptr == NULL || dst_size < 0) + if (plugin == NULL || src_voices == NULL || dst_voices == NULL || samples < 0) return -EINVAL; - if (src_size == 0) + if (samples == 0) return 0; - data = (struct adpcm_private_data *)snd_pcm_plugin_extra_data(plugin); - if (data == NULL) - return -EINVAL; - switch (data->cmd) { - case _U8_ADPCM: - if ((dst_size << 1) < src_size) - return -EINVAL; - adpcm_conv_u8bit_adpcm(&data->state, src_ptr, dst_ptr, src_size); - return src_size >> 1; - case _S8_ADPCM: - if ((dst_size << 1) < src_size) - return -EINVAL; - adpcm_conv_s8bit_adpcm(&data->state, src_ptr, dst_ptr, src_size); - return src_size >> 1; - case _S16LE_ADPCM: - if ((dst_size << 2) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - adpcm_conv_s16bit_adpcm(&data->state, (short *) src_ptr, dst_ptr, src_size >> 1); -#elif __BYTE_ORDER == __BIG_ENDIAN - adpcm_conv_s16bit_swap_adpcm(&data->state, (short *) src_ptr, dst_ptr, src_size >> 1); -#else -#error "Have to be coded..." -#endif - return src_size >> 2; - case _U16LE_ADPCM: - if ((dst_size << 2) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - adpcm_conv_u16bit_adpcm(&data->state, (short *) src_ptr, dst_ptr, src_size >> 1); -#elif __BYTE_ORDER == __BIG_ENDIAN - adpcm_conv_u16bit_swap_adpcm(&data->state, (short *) src_ptr, dst_ptr, src_size >> 1); -#else -#error "Have to be coded..." -#endif - return src_size >> 2; - case _S16BE_ADPCM: - if ((dst_size << 2) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - adpcm_conv_s16bit_swap_adpcm(&data->state, (short *) src_ptr, dst_ptr, src_size >> 1); -#elif __BYTE_ORDER == __BIG_ENDIAN - adpcm_conv_s16bit_adpcm(&data->state, (short *) src_ptr, dst_ptr, src_size >> 1); -#else -#error "Have to be coded..." -#endif - return src_size >> 2; - case _U16BE_ADPCM: - if ((dst_size << 2) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - adpcm_conv_u16bit_swap_adpcm(&data->state, (short *) src_ptr, dst_ptr, src_size >> 1); -#elif __BYTE_ORDER == __BIG_ENDIAN - adpcm_conv_u16bit_adpcm(&data->state, (short *) src_ptr, dst_ptr, src_size >> 1); -#else -#error "Have to be coded..." -#endif - return src_size >> 2; - case _ADPCM_U8: - if ((dst_size >> 1) < src_size) - return -EINVAL; - adpcm_conv_adpcm_u8bit(&data->state, src_ptr, dst_ptr, src_size << 1); - return src_size << 1; - case _ADPCM_S8: - if ((dst_size >> 1) < src_size) - return -EINVAL; - adpcm_conv_adpcm_s8bit(&data->state, src_ptr, dst_ptr, src_size << 1); - return src_size << 1; - case _ADPCM_S16LE: - if ((dst_size >> 2) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - adpcm_conv_adpcm_s16bit(&data->state, src_ptr, (short *) dst_ptr, src_size << 1); -#elif __BYTE_ORDER == __BIG_ENDIAN - adpcm_conv_adpcm_swap_s16bit(&data->state, src_ptr, (short *) dst_ptr, src_size << 1); -#else -#error "Have to be coded..." -#endif - return src_size << 2; - case _ADPCM_U16LE: - if ((dst_size >> 2) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - adpcm_conv_adpcm_u16bit(&data->state, src_ptr, (short *) dst_ptr, src_size << 1); -#elif __BYTE_ORDER == __BIG_ENDIAN - adpcm_conv_adpcm_swap_u16bit(&data->state, src_ptr, (short *) dst_ptr, src_size << 1); -#else -#error "Have to be coded..." -#endif - return src_size << 2; - case _ADPCM_S16BE: - if ((dst_size >> 2) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - adpcm_conv_adpcm_swap_s16bit(&data->state, src_ptr, (short *) dst_ptr, src_size << 1); -#elif __BYTE_ORDER == __BIG_ENDIAN - adpcm_conv_adpcm_s16bit(&data->state, src_ptr, (short *) dst_ptr, src_size << 1); -#else -#error "Have to be coded..." -#endif - return src_size << 2; - case _ADPCM_U16BE: - if ((dst_size << 2) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - adpcm_conv_adpcm_swap_u16bit(&data->state, src_ptr, (short *) dst_ptr, src_size << 1); -#elif __BYTE_ORDER == __BIG_ENDIAN - adpcm_conv_adpcm_u16bit(&data->state, src_ptr, (short *) dst_ptr, src_size << 1); -#else -#error "Have to be coded..." -#endif - return src_size << 2; - default: - return -EIO; + data = (adpcm_t *)plugin->extra_data; + /* FIXME */ + if (plugin->src_format.interleave) { + data->func(&data->state, + src_voices[0].addr, + dst_voices[0].addr, + samples * plugin->src_format.voices); + } else { + for (voice = 0; voice < plugin->src_format.voices; voice++) { + if (src_voices[voice].addr == NULL) + continue; + data->func(&data->state, + src_voices[voice].addr, + dst_voices[voice].addr, + samples); + } } + return samples; } static int adpcm_action(snd_pcm_plugin_t * plugin, snd_pcm_plugin_action_t action, unsigned long udata) { - struct adpcm_private_data *data; + adpcm_t *data; if (plugin == NULL) return -EINVAL; - data = (struct adpcm_private_data *)snd_pcm_plugin_extra_data(plugin); + data = (adpcm_t *)plugin->extra_data; if (action == PREPARE) adpcm_init_state(&data->state); return 0; /* silenty ignore other actions */ } -static ssize_t adpcm_src_size(snd_pcm_plugin_t * plugin, size_t size) -{ - struct adpcm_private_data *data; - - if (!plugin || size <= 0) - return -EINVAL; - data = (struct adpcm_private_data *)snd_pcm_plugin_extra_data(plugin); - switch (data->cmd) { - case _U8_ADPCM: - case _S8_ADPCM: - return size * 2; - case _ADPCM_U8: - case _ADPCM_S8: - return size / 2; - case _U16LE_ADPCM: - case _S16LE_ADPCM: - case _U16BE_ADPCM: - case _S16BE_ADPCM: - return size * 4; - case _ADPCM_U16LE: - case _ADPCM_S16LE: - case _ADPCM_U16BE: - case _ADPCM_S16BE: - return size / 4; - default: - return -EIO; - } -} - -static ssize_t adpcm_dst_size(snd_pcm_plugin_t * plugin, size_t size) -{ - struct adpcm_private_data *data; - - if (!plugin || size <= 0) - return -EINVAL; - data = (struct adpcm_private_data *)snd_pcm_plugin_extra_data(plugin); - switch (data->cmd) { - case _U8_ADPCM: - case _S8_ADPCM: - return size / 2; - case _ADPCM_U8: - case _ADPCM_S8: - return size * 2; - case _U16LE_ADPCM: - case _S16LE_ADPCM: - case _U16BE_ADPCM: - case _S16BE_ADPCM: - return size / 4; - case _ADPCM_U16LE: - case _ADPCM_S16LE: - case _ADPCM_U16BE: - case _ADPCM_S16BE: - return size * 4; - default: - return -EIO; - } -} - -int snd_pcm_plugin_build_adpcm(snd_pcm_format_t * src_format, - snd_pcm_format_t * dst_format, - snd_pcm_plugin_t ** r_plugin) +int snd_pcm_plugin_build_adpcm(snd_pcm_plugin_handle_t *handle, + snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) { struct adpcm_private_data *data; snd_pcm_plugin_t *plugin; - combination_t cmd; + int endian, src_width, dst_width, sign; + adpcm_f func; if (!r_plugin || !src_format || !dst_format) return -EINVAL; @@ -602,39 +379,49 @@ int snd_pcm_plugin_build_adpcm(snd_pcm_format_t * src_format, return -EINVAL; if (dst_format->format == SND_PCM_SFMT_IMA_ADPCM) { - switch (src_format->format) { - case SND_PCM_SFMT_U8: cmd = _U8_ADPCM; break; - case SND_PCM_SFMT_S8: cmd = _S8_ADPCM; break; - case SND_PCM_SFMT_U16_LE: cmd = _U16LE_ADPCM; break; - case SND_PCM_SFMT_S16_LE: cmd = _S16LE_ADPCM; break; - case SND_PCM_SFMT_U16_BE: cmd = _U16BE_ADPCM; break; - case SND_PCM_SFMT_S16_BE: cmd = _S16BE_ADPCM; break; - default: + if (!snd_pcm_format_linear(src_format->format)) return -EINVAL; - } + sign = snd_pcm_format_signed(src_format->format); + src_width = snd_pcm_format_width(src_format->format); + if ((src_width % 8) != 0 || src_width < 8 || src_width > 32) + return -EINVAL; + dst_width = 8; +#if __BYTE_ORDER == __LITTLE_ENDIAN + endian = snd_pcm_format_big_endian(src_format->format); +#elif __BYTE_ORDER == __BIG_ENDIAN + endian = snd_pcm_format_little_endian(src_format->format); +#else +#error "Unsupported endian..." +#endif + func = ((adpcm_f(*)[2][2])adpcm_functions_encode)[src_width/8][sign][endian]; } else if (src_format->format == SND_PCM_SFMT_IMA_ADPCM) { - switch (dst_format->format) { - case SND_PCM_SFMT_U8: cmd = _ADPCM_U8; break; - case SND_PCM_SFMT_S8: cmd = _ADPCM_S8; break; - case SND_PCM_SFMT_U16_LE: cmd = _ADPCM_U16LE; break; - case SND_PCM_SFMT_S16_LE: cmd = _ADPCM_S16LE; break; - case SND_PCM_SFMT_U16_BE: cmd = _ADPCM_U16BE; break; - case SND_PCM_SFMT_S16_BE: cmd = _ADPCM_S16BE; break; - default: + if (!snd_pcm_format_linear(dst_format->format)) return -EINVAL; - } + sign = snd_pcm_format_signed(dst_format->format); + dst_width = snd_pcm_format_width(dst_format->format); + if ((dst_width % 8) != 0 || dst_width < 8 || dst_width > 32) + return -EINVAL; + src_width = 8; +#if __BYTE_ORDER == __LITTLE_ENDIAN + endian = snd_pcm_format_big_endian(dst_format->format); +#elif __BYTE_ORDER == __BIG_ENDIAN + endian = snd_pcm_format_little_endian(dst_format->format); +#else +#error "Unsupported endian..." +#endif + func = ((adpcm_f(*)[2][2])adpcm_functions_decode)[dst_width/8][sign][endian]; } else { return -EINVAL; } - plugin = snd_pcm_plugin_build("Ima-ADPCM<->linear conversion", + plugin = snd_pcm_plugin_build(handle, + "Ima-ADPCM<->linear conversion", + src_format, + dst_format, sizeof(struct adpcm_private_data)); if (plugin == NULL) return -ENOMEM; - data = (struct adpcm_private_data *)snd_pcm_plugin_extra_data(plugin); - data->cmd = cmd; + data = (adpcm_t *)plugin->extra_data; plugin->transfer = adpcm_transfer; - plugin->src_size = adpcm_src_size; - plugin->dst_size = adpcm_dst_size; plugin->action = adpcm_action; *r_plugin = plugin; return 0; diff --git a/src/pcm/plugin/alaw.c b/src/pcm/plugin/alaw.c index 86d73637..5cfcf2c4 100644 --- a/src/pcm/plugin/alaw.c +++ b/src/pcm/plugin/alaw.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "../pcm_local.h" #define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ @@ -69,7 +70,7 @@ static inline int search(int val, short *table, int size) * For further information see John C. Bellamy's Digital Telephony, 1982, * John Wiley & Sons, pps 98-111 and 472-476. */ -static inline unsigned char linear2alaw(int pcm_val) /* 2's complement (16-bit range) */ +static unsigned char linear2alaw(int pcm_val) /* 2's complement (16-bit range) */ { int mask; int seg; @@ -103,7 +104,7 @@ static inline unsigned char linear2alaw(int pcm_val) /* 2's complement (16-bit r * alaw2linear() - Convert an A-law value to 16-bit linear PCM * */ -static inline int alaw2linear(unsigned char a_val) +static int alaw2linear(unsigned char a_val) { int t; int seg; @@ -131,298 +132,148 @@ static inline int alaw2linear(unsigned char a_val) * Basic A-Law plugin */ -typedef enum { - _S8_ALAW, - _U8_ALAW, - _S16LE_ALAW, - _U16LE_ALAW, - _S16BE_ALAW, - _U16BE_ALAW, - _ALAW_S8, - _ALAW_U8, - _ALAW_S16LE, - _ALAW_U16LE, - _ALAW_S16BE, - _ALAW_U16BE -} combination_t; - -struct alaw_private_data { - combination_t cmd; -}; - -static void alaw_conv_u8bit_alaw(unsigned char *src_ptr, unsigned char *dst_ptr, size_t size) -{ - unsigned int pcm; - - while (size-- > 0) { - pcm = ((*src_ptr++) ^ 0x80) << 8; - *dst_ptr++ = linear2alaw((signed short)(pcm)); - } +typedef void (*alaw_f)(void *src_ptr, void *dst_ptr, int samples); + +typedef struct alaw_private_data { + int src_byte_width; + int dst_byte_width; + alaw_f func; +} alaw_t; + +#define ALAW_FUNC_DECODE(name, dsttype, val) \ +static void alaw_decode_##name(void *src_ptr, void *dst_ptr, int samples) \ +{ \ + unsigned char *src = src_ptr; \ + dsttype *dst = dst_ptr; \ + unsigned int s; \ + while (samples--) { \ + s = alaw2linear(*src++); \ + *dst++ = val; \ + } \ } -static void alaw_conv_s8bit_alaw(unsigned char *src_ptr, unsigned char *dst_ptr, size_t size) -{ - unsigned int pcm; - - while (size-- > 0) { - pcm = *src_ptr++ << 8; - *dst_ptr++ = linear2alaw((signed short)(pcm)); - } -} - -static void alaw_conv_s16bit_alaw(unsigned short *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = linear2alaw((signed short)(*src_ptr++)); -} - -static void alaw_conv_s16bit_swap_alaw(unsigned short *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = linear2alaw((signed short)(bswap_16(*src_ptr++))); -} - -static void alaw_conv_u16bit_alaw(unsigned short *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = linear2alaw((signed short)((*src_ptr++) ^ 0x8000)); -} - -static void alaw_conv_u16bit_swap_alaw(unsigned short *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = linear2alaw((signed short)(bswap_16(*src_ptr++) ^ 0x8000)); -} - -static void alaw_conv_alaw_u8bit(unsigned char *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = (alaw2linear(*src_ptr++) >> 8) ^ 0x80; -} - -static void alaw_conv_alaw_s8bit(unsigned char *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = alaw2linear(*src_ptr++) >> 8; -} - -static void alaw_conv_alaw_s16bit(unsigned char *src_ptr, unsigned short *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = alaw2linear(*src_ptr++); +#define ALAW_FUNC_ENCODE(name, srctype, val) \ +static void alaw_encode_##name(void *src_ptr, void *dst_ptr, int samples) \ +{ \ + srctype *src = src_ptr; \ + unsigned char *dst = dst_ptr; \ + unsigned int s; \ + while (samples--) { \ + s = *src++; \ + *dst++ = linear2alaw(val); \ + } \ } -static void alaw_conv_alaw_swap_s16bit(unsigned char *src_ptr, unsigned short *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = bswap_16(alaw2linear(*src_ptr++)); -} - -static void alaw_conv_alaw_u16bit(unsigned char *src_ptr, unsigned short *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = alaw2linear(*src_ptr++) ^ 0x8000; -} +ALAW_FUNC_DECODE(u8, u_int8_t, (s >> 8) ^ 0x80) +ALAW_FUNC_DECODE(s8, u_int8_t, s >> 8) +ALAW_FUNC_DECODE(u16n, u_int16_t, s ^ 0x8000) +ALAW_FUNC_DECODE(u16s, u_int16_t, bswap_16(s ^ 0x8000)) +ALAW_FUNC_DECODE(s16n, u_int16_t, s) +ALAW_FUNC_DECODE(s16s, u_int16_t, bswap_16(s)) +ALAW_FUNC_DECODE(u24n, u_int32_t, (s << 8) ^ 0x800000) +ALAW_FUNC_DECODE(u24s, u_int32_t, bswap_32((s << 8) ^ 0x800000)) +ALAW_FUNC_DECODE(s24n, u_int32_t, s << 8) +ALAW_FUNC_DECODE(s24s, u_int32_t, bswap_32(s << 8)) +ALAW_FUNC_DECODE(u32n, u_int32_t, (s << 16) ^ 0x80000000) +ALAW_FUNC_DECODE(u32s, u_int32_t, bswap_32((s << 16) ^ 0x80000000)) +ALAW_FUNC_DECODE(s32n, u_int32_t, s << 16) +ALAW_FUNC_DECODE(s32s, u_int32_t, bswap_32(s << 16)) + +ALAW_FUNC_ENCODE(u8, u_int8_t, s << 8) +ALAW_FUNC_ENCODE(s8, u_int8_t, (s << 8) ^ 0x8000) +ALAW_FUNC_ENCODE(u16n, u_int16_t, s ^ 0x8000) +ALAW_FUNC_ENCODE(u16s, u_int16_t, bswap_16(s ^ 0x8000)) +ALAW_FUNC_ENCODE(s16n, u_int16_t, s) +ALAW_FUNC_ENCODE(s16s, u_int16_t, bswap_16(s)) +ALAW_FUNC_ENCODE(u24n, u_int32_t, (s ^ 0x800000) >> 8) +ALAW_FUNC_ENCODE(u24s, u_int32_t, bswap_32((s ^ 0x800000) >> 8)) +ALAW_FUNC_ENCODE(s24n, u_int32_t, s >> 8) +ALAW_FUNC_ENCODE(s24s, u_int32_t, bswap_32(s >> 8)) +ALAW_FUNC_ENCODE(u32n, u_int32_t, (s ^ 0x80000000) >> 16) +ALAW_FUNC_ENCODE(u32s, u_int32_t, bswap_32((s ^ 0x80000000) >> 16)) +ALAW_FUNC_ENCODE(s32n, u_int32_t, s >> 16) +ALAW_FUNC_ENCODE(s32s, u_int32_t, bswap_32(s >> 16)) + +/* wide, sign, swap endian */ +static alaw_f alaw_functions_decode[4 * 2 * 2] = { + alaw_decode_u8, /* decode:8-bit:unsigned:none */ + alaw_decode_u8, /* decode:8-bit:unsigned:swap */ + alaw_decode_s8, /* decode:8-bit:signed:none */ + alaw_decode_s8, /* decode:8-bit:signed:swap */ + alaw_decode_u16n, /* decode:16-bit:unsigned:none */ + alaw_decode_u16s, /* decode:16-bit:unsigned:swap */ + alaw_decode_s16n, /* decode:16-bit:signed:none */ + alaw_decode_s16s, /* decode:16-bit:signed:swap */ + alaw_decode_u24n, /* decode:24-bit:unsigned:none */ + alaw_decode_u24s, /* decode:24-bit:unsigned:swap */ + alaw_decode_s24n, /* decode:24-bit:signed:none */ + alaw_decode_s24s, /* decode:24-bit:signed:swap */ + alaw_decode_u32n, /* decode:32-bit:unsigned:none */ + alaw_decode_u32s, /* decode:32-bit:unsigned:swap */ + alaw_decode_s32n, /* decode:32-bit:signed:none */ + alaw_decode_s32s, /* decode:32-bit:signed:swap */ +}; -static void alaw_conv_alaw_swap_u16bit(unsigned char *src_ptr, unsigned short *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = bswap_16(alaw2linear(*src_ptr++) ^ 0x8000); -} +/* wide, sign, swap endian */ +static alaw_f alaw_functions_encode[4 * 2 * 2] = { + alaw_encode_u8, /* encode:8-bit:unsigned:none */ + alaw_encode_u8, /* encode:8-bit:unsigned:swap */ + alaw_encode_s8, /* encode:8-bit:signed:none */ + alaw_encode_s8, /* encode:8-bit:signed:swap */ + alaw_encode_u16n, /* encode:16-bit:unsigned:none */ + alaw_encode_u16s, /* encode:16-bit:unsigned:swap */ + alaw_encode_s16n, /* encode:16-bit:signed:none */ + alaw_encode_s16s, /* encode:16-bit:signed:swap */ + alaw_encode_u24n, /* encode:24-bit:unsigned:none */ + alaw_encode_u24s, /* encode:24-bit:unsigned:swap */ + alaw_encode_s24n, /* encode:24-bit:signed:none */ + alaw_encode_s24s, /* encode:24-bit:signed:swap */ + alaw_encode_u32n, /* encode:32-bit:unsigned:none */ + alaw_encode_u32s, /* encode:32-bit:unsigned:swap */ + alaw_encode_s32n, /* encode:32-bit:signed:none */ + alaw_encode_s32s, /* encode:32-bit:signed:swap */ +}; static ssize_t alaw_transfer(snd_pcm_plugin_t *plugin, - char *src_ptr, size_t src_size, - char *dst_ptr, size_t dst_size) + const snd_pcm_plugin_voice_t *src_voices, + const snd_pcm_plugin_voice_t *dst_voices, + size_t samples) { - struct alaw_private_data *data; + alaw_t *data; + int voice; - if (plugin == NULL || src_ptr == NULL || src_size < 0 || - dst_ptr == NULL || dst_size < 0) + if (plugin == NULL || src_voices == NULL || dst_voices == NULL || samples < 0) return -EINVAL; - if (src_size == 0) + if (samples == 0) return 0; - data = (struct alaw_private_data *)snd_pcm_plugin_extra_data(plugin); - if (data == NULL) - return -EINVAL; - switch (data->cmd) { - case _U8_ALAW: - if (dst_size < src_size) - return -EINVAL; - alaw_conv_u8bit_alaw(src_ptr, dst_ptr, src_size); - return src_size; - case _S8_ALAW: - if (dst_size < src_size) - return -EINVAL; - alaw_conv_s8bit_alaw(src_ptr, dst_ptr, src_size); - return src_size; - case _S16LE_ALAW: - if ((dst_size << 1) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - alaw_conv_s16bit_alaw((short *)src_ptr, dst_ptr, src_size >> 1); -#elif __BYTE_ORDER == __BIG_ENDIAN - alaw_conv_s16bit_swap_alaw((short *)src_ptr, dst_ptr, src_size >> 1); -#else -#error "Have to be coded..." -#endif - return src_size >> 1; - case _U16LE_ALAW: - if ((dst_size << 1) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - alaw_conv_u16bit_alaw((short *)src_ptr, dst_ptr, src_size >> 1); -#elif __BYTE_ORDER == __BIG_ENDIAN - alaw_conv_u16bit_swap_alaw((short *)src_ptr, dst_ptr, src_size >> 1); -#else -#error "Have to be coded..." -#endif - return src_size >> 1; - case _S16BE_ALAW: - if ((dst_size << 1) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - alaw_conv_s16bit_swap_alaw((short *)src_ptr, dst_ptr, src_size >> 1); -#elif __BYTE_ORDER == __BIG_ENDIAN - alaw_conv_s16bit_alaw((short *)src_ptr, dst_ptr, src_size >> 1); -#else -#error "Have to be coded..." -#endif - return src_size >> 1; - case _U16BE_ALAW: - if ((dst_size << 1) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - alaw_conv_u16bit_swap_alaw((short *)src_ptr, dst_ptr, src_size >> 1); -#elif __BYTE_ORDER == __BIG_ENDIAN - alaw_conv_u16bit_alaw((short *)src_ptr, dst_ptr, src_size >> 1); -#else -#error "Have to be coded..." -#endif - return src_size >> 1; - case _ALAW_U8: - if (dst_size < src_size) - return -EINVAL; - alaw_conv_alaw_u8bit(src_ptr, dst_ptr, src_size); - return src_size; - case _ALAW_S8: - if (dst_size < src_size) - return -EINVAL; - alaw_conv_alaw_s8bit(src_ptr, dst_ptr, src_size); - return src_size; - case _ALAW_S16LE: - if ((dst_size >> 1) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - alaw_conv_alaw_s16bit(src_ptr, (short *)dst_ptr, src_size); -#elif __BYTE_ORDER == __BIG_ENDIAN - alaw_conv_alaw_swap_s16bit(src_ptr, (short *)dst_ptr, src_size); -#else -#error "Have to be coded..." -#endif - return src_size << 1; - case _ALAW_U16LE: - if ((dst_size >> 1) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - alaw_conv_alaw_u16bit(src_ptr, (short *)dst_ptr, src_size); -#elif __BYTE_ORDER == __BIG_ENDIAN - alaw_conv_alaw_swap_u16bit(src_ptr, (short *)dst_ptr, src_size); -#else -#error "Have to be coded..." -#endif - return src_size << 1; - case _ALAW_S16BE: - if ((dst_size >> 1) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - alaw_conv_alaw_swap_s16bit(src_ptr, (short *)dst_ptr, src_size); -#elif __BYTE_ORDER == __BIG_ENDIAN - alaw_conv_alaw_s16bit(src_ptr, (short *)dst_ptr, src_size); -#else -#error "Have to be coded..." -#endif - return src_size << 1; - case _ALAW_U16BE: - if ((dst_size >> 1) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - alaw_conv_alaw_swap_u16bit(src_ptr, (short *)dst_ptr, src_size); -#elif __BYTE_ORDER == __BIG_ENDIAN - alaw_conv_alaw_u16bit(src_ptr, (short *)dst_ptr, src_size); -#else -#error "Have to be coded..." -#endif - return src_size << 1; - default: - return -EIO; - } -} - -static ssize_t alaw_src_size(snd_pcm_plugin_t *plugin, size_t size) -{ - struct alaw_private_data *data; - - if (!plugin || size <= 0) - return -EINVAL; - data = (struct alaw_private_data *)snd_pcm_plugin_extra_data(plugin); - switch (data->cmd) { - case _U8_ALAW: - case _S8_ALAW: - case _ALAW_U8: - case _ALAW_S8: - return size; - case _U16LE_ALAW: - case _S16LE_ALAW: - case _U16BE_ALAW: - case _S16BE_ALAW: - return size * 2; - case _ALAW_U16LE: - case _ALAW_S16LE: - case _ALAW_U16BE: - case _ALAW_S16BE: - return size / 2; - default: - return -EIO; - } + data = (alaw_t *)plugin->extra_data; + if (plugin->src_format.interleave) { + data->func(src_voices[0].addr, + dst_voices[0].addr, + samples * plugin->src_format.voices); + } else { + for (voice = 0; voice < plugin->src_format.voices; voice++) { + if (src_voices[voice].addr == NULL) + continue; + data->func(src_voices[voice].addr, + dst_voices[voice].addr, + samples); + } + } + return samples; } -static ssize_t alaw_dst_size(snd_pcm_plugin_t *plugin, size_t size) -{ - struct alaw_private_data *data; - - if (!plugin || size <= 0) - return -EINVAL; - data = (struct alaw_private_data *)snd_pcm_plugin_extra_data(plugin); - switch (data->cmd) { - case _U8_ALAW: - case _S8_ALAW: - case _ALAW_U8: - case _ALAW_S8: - return size; - case _U16LE_ALAW: - case _S16LE_ALAW: - case _U16BE_ALAW: - case _S16BE_ALAW: - return size / 2; - case _ALAW_U16LE: - case _ALAW_S16LE: - case _ALAW_U16BE: - case _ALAW_S16BE: - return size * 2; - default: - return -EIO; - } -} - -int snd_pcm_plugin_build_alaw(snd_pcm_format_t *src_format, +int snd_pcm_plugin_build_alaw(snd_pcm_plugin_handle_t *handle, + snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin) { struct alaw_private_data *data; snd_pcm_plugin_t *plugin; - combination_t cmd; + int endian, src_width, dst_width, sign; + alaw_f func; - if (!r_plugin) + if (r_plugin == NULL) return -EINVAL; *r_plugin = NULL; @@ -434,40 +285,53 @@ int snd_pcm_plugin_build_alaw(snd_pcm_format_t *src_format, if (src_format->voices != dst_format->voices) return -EINVAL; - if (dst_format->format == SND_PCM_SFMT_A_LAW) { - switch (src_format->format) { - case SND_PCM_SFMT_U8: cmd = _U8_ALAW; break; - case SND_PCM_SFMT_S8: cmd = _S8_ALAW; break; - case SND_PCM_SFMT_U16_LE: cmd = _U16LE_ALAW; break; - case SND_PCM_SFMT_S16_LE: cmd = _S16LE_ALAW; break; - case SND_PCM_SFMT_U16_BE: cmd = _U16BE_ALAW; break; - case SND_PCM_SFMT_S16_BE: cmd = _S16BE_ALAW; break; - default: + if (dst_format->format == SND_PCM_SFMT_MU_LAW) { + if (!snd_pcm_format_linear(src_format->format)) return -EINVAL; - } - } else if (src_format->format == SND_PCM_SFMT_A_LAW) { - switch (dst_format->format) { - case SND_PCM_SFMT_U8: cmd = _ALAW_U8; break; - case SND_PCM_SFMT_S8: cmd = _ALAW_S8; break; - case SND_PCM_SFMT_U16_LE: cmd = _ALAW_U16LE; break; - case SND_PCM_SFMT_S16_LE: cmd = _ALAW_S16LE; break; - case SND_PCM_SFMT_U16_BE: cmd = _ALAW_U16BE; break; - case SND_PCM_SFMT_S16_BE: cmd = _ALAW_S16BE; break; - default: + sign = snd_pcm_format_signed(src_format->format); + src_width = snd_pcm_format_width(src_format->format); + if ((src_width % 8) != 0 || src_width < 8 || src_width > 32) return -EINVAL; - } + dst_width = 8; +#if __BYTE_ORDER == __LITTLE_ENDIAN + endian = snd_pcm_format_big_endian(src_format->format); +#elif __BYTE_ORDER == __BIG_ENDIAN + endian = snd_pcm_format_little_endian(src_format->format); +#else +#error "Unsupported endian..." +#endif + func = ((alaw_f(*)[2][2])alaw_functions_encode)[(src_width/8)-1][sign][endian]; + } else if (src_format->format == SND_PCM_SFMT_MU_LAW) { + if (!snd_pcm_format_linear(dst_format->format)) + return -EINVAL; + sign = snd_pcm_format_signed(dst_format->format); + dst_width = snd_pcm_format_width(dst_format->format); + if ((dst_width % 8) != 0 || dst_width < 8 || dst_width > 32) + return -EINVAL; + src_width = 8; +#if __BYTE_ORDER == __LITTLE_ENDIAN + endian = snd_pcm_format_big_endian(dst_format->format); +#elif __BYTE_ORDER == __BIG_ENDIAN + endian = snd_pcm_format_little_endian(dst_format->format); +#else +#error "Unsupported endian..." +#endif + func = ((alaw_f(*)[2][2])alaw_functions_decode)[(dst_width/8)-1][sign][endian]; } else { return -EINVAL; } - plugin = snd_pcm_plugin_build("A-Law<->linear conversion", + plugin = snd_pcm_plugin_build(handle, + "A-Law<->linear conversion", + src_format, + dst_format, sizeof(struct alaw_private_data)); if (plugin == NULL) return -ENOMEM; - data = (struct alaw_private_data *)snd_pcm_plugin_extra_data(plugin); - data->cmd = cmd; + data = (struct alaw_private_data *)plugin->extra_data; + data->src_byte_width = src_width / 8; + data->dst_byte_width = dst_width / 8; + data->func = func; plugin->transfer = alaw_transfer; - plugin->src_size = alaw_src_size; - plugin->dst_size = alaw_dst_size; *r_plugin = plugin; return 0; } diff --git a/src/pcm/plugin/block.c b/src/pcm/plugin/block.c index 195b4683..6c0e609e 100644 --- a/src/pcm/plugin/block.c +++ b/src/pcm/plugin/block.c @@ -24,55 +24,99 @@ #include #include #include +#include #include "../pcm_local.h" /* * Basic block plugin */ -struct block_private_data { - snd_pcm_t *pcm; +typedef struct block_private_data { int channel; -}; +} block_t; static ssize_t block_transfer(snd_pcm_plugin_t *plugin, - char *src_ptr, size_t src_size, - char *dst_ptr, size_t dst_size) + const snd_pcm_plugin_voice_t *src_voices, + const snd_pcm_plugin_voice_t *dst_voices, + size_t samples) { - struct block_private_data *data; + block_t *data; + ssize_t result; + struct iovec *vec; + int count, voice; - if (plugin == NULL || dst_ptr == NULL || dst_size <= 0) - return -EINVAL; - data = (struct block_private_data *)snd_pcm_plugin_extra_data(plugin); + if (plugin == NULL) + return -EINVAL; + data = (block_t *)plugin->extra_data; if (data == NULL) return -EINVAL; + vec = (struct iovec *)((char *)data + sizeof(*data)); if (data->channel == SND_PCM_CHANNEL_PLAYBACK) { - return snd_pcm_write(data->pcm, dst_ptr, dst_size); + if (src_voices == NULL) + return -EINVAL; + if ((result = snd_pcm_plugin_src_samples_to_size(plugin, samples)) < 0) + return result; + if (plugin->src_format.interleave) { + vec->iov_base = src_voices->addr; + vec->iov_len = result; + count = 1; + } else { + count = plugin->src_format.voices; + result /= count; + for (voice = 0; voice < count; voice++) { + vec[voice].iov_base = src_voices[voice].addr; + vec[voice].iov_len = result; + } + } + result = snd_pcm_writev(plugin->handle, vec, count); + if (result < 0) + return result; + return snd_pcm_plugin_src_size_to_samples(plugin, result); } else if (data->channel == SND_PCM_CHANNEL_CAPTURE) { - return snd_pcm_read(data->pcm, dst_ptr, dst_size); + if (dst_voices == NULL) + return -EINVAL; + if ((result = snd_pcm_plugin_dst_samples_to_size(plugin, samples)) < 0) + return result; + if (plugin->dst_format.interleave) { + vec->iov_base = dst_voices->addr; + vec->iov_len = result; + count = 1; + } else { + count = plugin->dst_format.voices; + result /= count; + for (voice = 0; voice < count; voice++) { + vec[voice].iov_base = dst_voices[voice].addr; + vec[voice].iov_len = result; + } + } + result = snd_pcm_readv(plugin->handle, vec, count); + return snd_pcm_plugin_dst_size_to_samples(plugin, result); } else { return -EINVAL; } } -int snd_pcm_plugin_build_block(snd_pcm_t *pcm, int channel, snd_pcm_plugin_t **r_plugin) +int snd_pcm_plugin_build_block(snd_pcm_t *pcm, int channel, + snd_pcm_format_t *format, + snd_pcm_plugin_t **r_plugin) { - struct block_private_data *data; + block_t *data; snd_pcm_plugin_t *plugin; if (r_plugin == NULL) return -EINVAL; *r_plugin = NULL; - if (!pcm || channel < 0 || channel > 1) + if (pcm == NULL || channel < 0 || channel > 1 || format == NULL) return -EINVAL; - plugin = snd_pcm_plugin_build(channel == SND_PCM_CHANNEL_PLAYBACK ? + plugin = snd_pcm_plugin_build(pcm, + channel == SND_PCM_CHANNEL_PLAYBACK ? "I/O block playback" : "I/O block capture", - sizeof(struct block_private_data)); + format, format, + sizeof(block_t) + sizeof(struct iovec) * format->voices); if (plugin == NULL) return -ENOMEM; - data = (struct block_private_data *)snd_pcm_plugin_extra_data(plugin); - data->pcm = pcm; + data = (block_t *)plugin->extra_data; data->channel = channel; plugin->transfer = block_transfer; *r_plugin = plugin; diff --git a/src/pcm/plugin/interleave.c b/src/pcm/plugin/interleave.c index 5dedda36..d13e39cf 100644 --- a/src/pcm/plugin/interleave.c +++ b/src/pcm/plugin/interleave.c @@ -1,6 +1,7 @@ /* * Interleave / non-interleave conversion Plug-In - * Copyright (c) 2000 by Abramo Bagnara + * Copyright (c) 2000 by Abramo Bagnara , + * Jaroslav Kysela * * * This library is free software; you can redistribute it and/or modify @@ -31,6 +32,7 @@ #include #include #include +#include #include "../pcm_local.h" #endif @@ -38,45 +40,44 @@ * Basic interleave / non-interleave conversion plugin */ -typedef void (*interleave_f)(void* src_ptr, void* dst_ptr, +typedef void (*interleave_f)(const snd_pcm_plugin_voice_t *src_voices, + const snd_pcm_plugin_voice_t *dst_voices, int voices, size_t samples); -struct interleave_private_data { - int sample_size; - int voices; +typedef struct interleave_private_data { interleave_f func; -}; +} interleave_t; #define INTERLEAVE_FUNC(name, type) \ -static void name(void* src_ptr, void* dst_ptr, \ - int voices, size_t samples) \ +static void name(const snd_pcm_plugin_voice_t *src_voices, \ + const snd_pcm_plugin_voice_t *dst_voices, \ + int voices, size_t samples) \ { \ - type* src = src_ptr; \ - int voice; \ - for (voice = 0; voice < voices; ++voice) { \ - type *dst = (type*)dst_ptr + voice; \ - int s; \ - for (s = 0; s < samples; ++s) { \ - *dst = *src; \ - src++; \ + type *src, *dst; \ + int voice, sample; \ + for (voice = 0; voice < voices; voice++) { \ + src = (type *)src_voices[voice].addr; \ + dst = (type *)dst_voices[voice].addr + voice; \ + for (sample = 0; sample < samples; sample++) { \ + *dst = *src++; \ dst += voices; \ } \ } \ } \ #define DEINTERLEAVE_FUNC(name, type) \ -static void name(void* src_ptr, void* dst_ptr, \ - int voices, size_t samples) \ +static void name(const snd_pcm_plugin_voice_t *src_voices, \ + const snd_pcm_plugin_voice_t *dst_voices, \ + int voices, size_t samples) \ { \ - type* dst = dst_ptr; \ - int voice; \ - for (voice = 0; voice < voices; ++voice) { \ - type *src = (type*)src_ptr + voice; \ - int s; \ - for (s = 0; s < samples; ++s) { \ - *dst = *src; \ - dst++; \ + type *src, *dst; \ + int voice, sample; \ + for (voice = 0; voice < voices; voice++) { \ + src = (type *)src_voices[voice].addr + voice; \ + dst = (type *)dst_voices[voice].addr; \ + for (sample = 0; sample < samples; sample++) { \ + *dst++ = *src; \ src += voices; \ } \ } \ @@ -92,36 +93,33 @@ FUNCS(4, int32_t); FUNCS(8, int64_t); static ssize_t interleave_transfer(snd_pcm_plugin_t *plugin, - char *src_ptr, size_t src_size, - char *dst_ptr, size_t dst_size) + const snd_pcm_plugin_voice_t *src_voices, + const snd_pcm_plugin_voice_t *dst_voices, + size_t samples) { - struct interleave_private_data *data; + interleave_t *data; - if (plugin == NULL || src_ptr == NULL || src_size < 0 || - dst_ptr == NULL || dst_size < 0) + if (plugin == NULL || src_voices == NULL || src_voices == NULL || samples < 0) return -EINVAL; - if (src_size == 0) + if (samples == 0) return 0; - if (src_size != dst_size) - return -EINVAL; - data = (struct interleave_private_data *)snd_pcm_plugin_extra_data(plugin); + data = (interleave_t *)plugin->extra_data; if (data == NULL) return -EINVAL; - data->func(src_ptr, dst_ptr, data->voices, - src_size / (data->voices * data->sample_size)); - return src_size; + data->func(src_voices, dst_voices, plugin->src_format.voices, samples); + return samples; } -int snd_pcm_plugin_build_interleave(snd_pcm_format_t *src_format, +int snd_pcm_plugin_build_interleave(snd_pcm_plugin_handle_t *handle, + snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin) { struct interleave_private_data *data; snd_pcm_plugin_t *plugin; interleave_f func; - int size; - if (r_plugin == NULL) + if (r_plugin == NULL || src_format == NULL || dst_format == NULL) return -EINVAL; *r_plugin = NULL; @@ -133,36 +131,35 @@ int snd_pcm_plugin_build_interleave(snd_pcm_format_t *src_format, return -EINVAL; if (src_format->voices != dst_format->voices) return -EINVAL; - size = snd_pcm_format_size(dst_format->format, 1); - if (dst_format->interleave) { - switch (size) { - case 1: + if (!src_format->interleave) { + switch (snd_pcm_format_width(src_format->format)) { + case 8: func = int_1; break; - case 2: + case 16: func = int_2; break; - case 4: + case 32: func = int_4; break; - case 8: + case 64: func = int_8; break; default: return -EINVAL; } } else { - switch (size) { - case 1: + switch (snd_pcm_format_width(src_format->format)) { + case 8: func = deint_1; break; - case 2: + case 16: func = deint_2; break; - case 4: + case 32: func = deint_4; break; - case 8: + case 64: func = deint_8; break; default: @@ -170,13 +167,13 @@ int snd_pcm_plugin_build_interleave(snd_pcm_format_t *src_format, } } - plugin = snd_pcm_plugin_build("interleave conversion", - sizeof(struct interleave_private_data)); + plugin = snd_pcm_plugin_build(handle, + "interleave conversion", + src_format, dst_format, + sizeof(interleave_t)); if (plugin == NULL) return -ENOMEM; - data = (struct interleave_private_data *)snd_pcm_plugin_extra_data(plugin); - data->sample_size = size; - data->voices = src_format->voices; + data = (interleave_t *)plugin->extra_data; data->func = func; plugin->transfer = interleave_transfer; *r_plugin = plugin; diff --git a/src/pcm/plugin/linear.c b/src/pcm/plugin/linear.c index 7bc5bb51..4f190212 100644 --- a/src/pcm/plugin/linear.c +++ b/src/pcm/plugin/linear.c @@ -1,6 +1,7 @@ /* * Linear conversion Plug-In - * Copyright (c) 1999 by Jaroslav Kysela + * Copyright (c) 1999 by Jaroslav Kysela , + * Abramo Bagnara * * * This library is free software; you can redistribute it and/or modify @@ -33,6 +34,7 @@ #include #include #include +#include #include "../pcm_local.h" #endif @@ -42,10 +44,10 @@ typedef void (*linear_f)(void *src, void *dst, size_t size); -struct linear_private_data { +typedef struct linear_private_data { int src_sample_size, dst_sample_size; linear_f func; -}; +} linear_t; #define LIN_FUNC(name, srctype, dsttype, val) \ static void lin_##name(void *src_ptr, void *dst_ptr, size_t size) \ @@ -163,7 +165,7 @@ LIN_FUNC(32_sign_end, u_int32_t, u_int32_t, bswap_32(src) ^ 0x80) LIN_FUNC(32_end_sign_end, u_int32_t, u_int32_t, src ^ 0x80) /* src_wid dst_wid src_endswap, dst_endswap, sign_swap */ -linear_f linear_functions[4 * 4 * 2 * 2 * 2] = { +static linear_f linear_functions[4 * 4 * 2 * 2 * 2] = { NULL, /* 8->8: Nothing to do */ lin_8_sign, /* 8->8 sign: lin_8_sign */ NULL, /* 8->8 dst_end: Nothing to do */ @@ -296,52 +298,33 @@ linear_f linear_functions[4 * 4 * 2 * 2 * 2] = { static ssize_t linear_transfer(snd_pcm_plugin_t *plugin, - char *src_ptr, size_t src_size, - char *dst_ptr, size_t dst_size) + const snd_pcm_plugin_voice_t *src_voices, + const snd_pcm_plugin_voice_t *dst_voices, + size_t samples) { - struct linear_private_data *data; + linear_t *data; + int voice; + ssize_t result; - if (plugin == NULL || src_ptr == NULL || src_size < 0 || - dst_ptr == NULL || dst_size < 0) + if (plugin == NULL || src_voices == NULL || dst_voices == NULL || samples < 0) return -EINVAL; - if (src_size == 0) + if (samples == 0) return 0; - data = (struct linear_private_data *)snd_pcm_plugin_extra_data(plugin); - if (data == NULL) - return -EINVAL; - if (src_size % data->src_sample_size != 0) - return -EINVAL; - if (dst_size < src_size*data->dst_sample_size/data->src_sample_size) - return -EINVAL; - data->func(src_ptr, dst_ptr, src_size / data->src_sample_size); - return src_size*data->dst_sample_size/data->src_sample_size; -} - -static ssize_t linear_src_size(snd_pcm_plugin_t *plugin, size_t size) -{ - struct linear_private_data *data; - - if (plugin == NULL || size <= 0) - return -EINVAL; - data = (struct linear_private_data *)snd_pcm_plugin_extra_data(plugin); - if (data == NULL) - return -EINVAL; - return size*data->src_sample_size/data->dst_sample_size; -} - -static ssize_t linear_dst_size(snd_pcm_plugin_t *plugin, size_t size) -{ - struct linear_private_data *data; - - if (plugin == NULL || size <= 0) - return -EINVAL; - data = (struct linear_private_data *)snd_pcm_plugin_extra_data(plugin); - if (data == NULL) - return -EINVAL; - return size*data->dst_sample_size/data->src_sample_size; + data = (linear_t *)plugin->extra_data; + for (voice = 0, result = 0; voice < plugin->src_format.voices; voice++) { + if (src_voices[voice].addr == NULL) + continue; + if (dst_voices[voice].addr == NULL) + return -EINVAL; + data->func(src_voices[voice].addr, + dst_voices[voice].addr, + samples); + } + return samples; } -int snd_pcm_plugin_build_linear(snd_pcm_format_t *src_format, +int snd_pcm_plugin_build_linear(snd_pcm_plugin_handle_t *handle, + snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin) { @@ -349,7 +332,6 @@ int snd_pcm_plugin_build_linear(snd_pcm_format_t *src_format, snd_pcm_plugin_t *plugin; linear_f func; int src_endian, dst_endian, sign, src_width, dst_width; - int src_sample_size, dst_sample_size; if (r_plugin == NULL) return -EINVAL; @@ -380,46 +362,17 @@ int snd_pcm_plugin_build_linear(snd_pcm_format_t *src_format, #error "Unsupported endian..." #endif - switch (src_width) { - case 8: - src_width = 0; - src_sample_size = 1; - break; - case 16: - src_width = 1; - src_sample_size = 2; - break; - case 24: - src_width = 2; - src_sample_size = 4; - break; - case 32: - src_width = 3; - src_sample_size = 4; - break; - default: - return -EINVAL; - } - switch (dst_width) { - case 8: - dst_width = 0; - dst_sample_size = 1; - break; - case 16: - dst_width = 1; - dst_sample_size = 2; - break; - case 24: - dst_width = 2; - dst_sample_size = 4; - break; - case 32: - dst_width = 3; - dst_sample_size = 4; - break; - default: - return -EINVAL; - } + src_width = snd_pcm_format_width(src_format->format); + if (src_width < 0) + return src_width; + src_width /= 8; + src_width--; + + dst_width = snd_pcm_format_width(dst_format->format); + if (dst_width < 0) + return dst_width; + dst_width /= 8; + dst_width--; if (src_endian < 0) src_endian = 0; @@ -431,18 +384,18 @@ int snd_pcm_plugin_build_linear(snd_pcm_format_t *src_format, if (func == NULL) return -EINVAL; - plugin = snd_pcm_plugin_build("linear format conversion", - sizeof(struct linear_private_data)); + plugin = snd_pcm_plugin_build(handle, + "linear format conversion", + src_format, + dst_format, + sizeof(linear_t)); if (plugin == NULL) return -ENOMEM; - data = (struct linear_private_data *)snd_pcm_plugin_extra_data(plugin); + data = (linear_t *)plugin->extra_data; data->func = func; - data->src_sample_size = src_sample_size; - data->dst_sample_size = dst_sample_size; - + data->src_sample_size = plugin->src_width / 8; + data->dst_sample_size = plugin->dst_width / 8; plugin->transfer = linear_transfer; - plugin->src_size = linear_src_size; - plugin->dst_size = linear_dst_size; *r_plugin = plugin; return 0; } diff --git a/src/pcm/plugin/mmap.c b/src/pcm/plugin/mmap.c index 91f56c4a..97157ff6 100644 --- a/src/pcm/plugin/mmap.c +++ b/src/pcm/plugin/mmap.c @@ -25,14 +25,14 @@ #include #include #include +#include #include "../pcm_local.h" /* * Basic mmap plugin */ -struct mmap_private_data { - snd_pcm_t *pcm; +typedef struct mmap_private_data { int channel; snd_pcm_mmap_control_t *control; char *buffer; @@ -41,10 +41,13 @@ struct mmap_private_data { int frags, frags_used; int frags_min, frags_max; unsigned int lastblock; -}; + struct iovec *vector; + int vector_count; +} mmap_t; -static int playback_ok(struct mmap_private_data *data) +static int playback_ok(snd_pcm_plugin_t *plugin) { + mmap_t *data = (mmap_t *)plugin->extra_data; snd_pcm_mmap_control_t *control = data->control; int delta = control->status.block; @@ -79,8 +82,9 @@ static int poll_playback(snd_pcm_t *pcm) return err < 0 ? err : 0; } -static int query_playback(struct mmap_private_data *data, int not_use_poll) +static int query_playback(snd_pcm_plugin_t *plugin, int not_use_poll) { + mmap_t *data = (mmap_t *)plugin->extra_data; snd_pcm_mmap_control_t *control = data->control; int err; @@ -89,10 +93,10 @@ static int query_playback(struct mmap_private_data *data, int not_use_poll) if (data->start_mode == SND_PCM_START_GO) return -EAGAIN; if ((data->start_mode == SND_PCM_START_DATA && - playback_ok(data)) || + playback_ok(plugin)) || (data->start_mode == SND_PCM_START_FULL && data->frags_used == data->frags)) { - err = snd_pcm_channel_go(data->pcm, data->channel); + err = snd_pcm_channel_go(plugin->handle, data->channel); if (err < 0) return err; } @@ -100,7 +104,7 @@ static int query_playback(struct mmap_private_data *data, int not_use_poll) case SND_PCM_STATUS_RUNNING: if (!not_use_poll) { control->status.expblock = control->status.block + 1; - err = poll_playback(data->pcm); + err = poll_playback(plugin->handle); if (err < 0) return err; } @@ -113,8 +117,9 @@ static int query_playback(struct mmap_private_data *data, int not_use_poll) return 0; } -static int capture_ok(struct mmap_private_data *data) +static int capture_ok(snd_pcm_plugin_t *plugin) { + mmap_t *data = (mmap_t *)plugin->extra_data; snd_pcm_mmap_control_t *control = data->control; int delta = control->status.block; @@ -148,8 +153,9 @@ static int poll_capture(snd_pcm_t *pcm) return err < 0 ? err : 0; } -static int query_capture(struct mmap_private_data *data, int not_use_poll) +static int query_capture(snd_pcm_plugin_t *plugin, int not_use_poll) { + mmap_t *data = (mmap_t *)plugin->extra_data; snd_pcm_mmap_control_t *control = data->control; int err; @@ -157,14 +163,14 @@ static int query_capture(struct mmap_private_data *data, int not_use_poll) case SND_PCM_STATUS_PREPARED: if (data->start_mode != SND_PCM_START_DATA) return -EAGAIN; - err = snd_pcm_channel_go(data->pcm, data->channel); + err = snd_pcm_channel_go(plugin->handle, data->channel); if (err < 0) return err; break; case SND_PCM_STATUS_RUNNING: if (!not_use_poll) { control->status.expblock = control->status.block + data->frags_min; - err = poll_capture(data->pcm); + err = poll_capture(plugin->handle); if (err < 0) return err; } @@ -177,131 +183,206 @@ static int query_capture(struct mmap_private_data *data, int not_use_poll) return 0; } -static int mmap_transfer_src_ptr(snd_pcm_plugin_t *plugin, char **buffer, size_t *size) +static int mmap_src_voices(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_voice_t **voices, + size_t samples, + void *(*plugin_alloc)(snd_pcm_plugin_handle_t *handle, size_t size)) { - struct mmap_private_data *data; + mmap_t *data; snd_pcm_mmap_control_t *control; - int interleave, err; + snd_pcm_plugin_voice_t *v; + int err; - if (plugin == NULL || buffer == NULL || size == NULL) + if (plugin == NULL || voices == NULL) + return -EINVAL; + *voices = NULL; + data = (mmap_t *)plugin->extra_data; + if (data->channel != SND_PCM_CHANNEL_PLAYBACK) + return -EINVAL; + if ((control = data->control) == NULL) + return -EBADFD; + /* wait until the block is not free */ + while (!playback_ok(plugin)) { + err = query_playback(plugin, 0); + if (err < 0) + return err; + } + v = plugin->voices; + if (plugin->src_format.interleave) { + void *addr; + int voice; + if (control->status.frag_size != snd_pcm_plugin_dst_samples_to_size(plugin, samples)) + return -EINVAL; + addr = data->buffer + control->fragments[data->frag].addr; + for (voice = 0; voice < plugin->src_format.voices; voice++) { + v->aptr = NULL; + v->addr = addr; + v->offset = voice * plugin->src_width; + v->next = plugin->src_format.voices * plugin->src_width; + } + } else { + int frag, voice; + if (control->status.frag_size != snd_pcm_plugin_src_samples_to_size(plugin, samples) / plugin->src_format.voices) return -EINVAL; - data = (struct mmap_private_data *)snd_pcm_plugin_extra_data(plugin); - if (data == NULL) + for (voice = 0; voice < plugin->src_format.voices; voice++) { + frag = data->frag + (voice * data->frags); + v->aptr = NULL; + v->addr = data->buffer + control->fragments[frag].addr; + v->offset = 0; + v->next = plugin->src_width; + } + } + *voices = plugin->voices; + return 0; +} + +static int mmap_dst_voices(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_voice_t **voices, + size_t samples, + void *(*plugin_alloc)(snd_pcm_plugin_handle_t *handle, size_t size)) +{ + mmap_t *data; + snd_pcm_mmap_control_t *control; + snd_pcm_plugin_voice_t *v; + + if (plugin == NULL || voices == NULL) return -EINVAL; - control = data->control; - if (control == NULL) + *voices = NULL; + data = (mmap_t *)plugin->extra_data; + if (data->channel != SND_PCM_CHANNEL_CAPTURE) return -EINVAL; - interleave = control->status.voices < 0; - if (interleave) { - *buffer = data->buffer + control->fragments[data->frag].addr; - if (data->channel == SND_PCM_CHANNEL_PLAYBACK) { - /* wait until the block is not free */ - while (!playback_ok(data)) { - err = query_playback(data, 0); - if (err < 0) - return err; - } + if ((control = data->control) == NULL) + return -EBADFD; + v = plugin->voices; + if (plugin->dst_format.interleave) { + void *addr; + int voice; + if (control->status.frag_size != snd_pcm_plugin_dst_samples_to_size(plugin, samples)) + return -EINVAL; + addr = data->buffer + control->fragments[data->frag].addr; + for (voice = 0; voice < plugin->dst_format.voices; voice++) { + v->addr = addr; + v->offset = voice * plugin->src_width; + v->next = plugin->dst_format.voices * plugin->dst_width; } - *size = control->status.frag_size; } else { - *buffer = NULL; /* use another buffer */ + int frag, voice; + if (control->status.frag_size != snd_pcm_plugin_dst_samples_to_size(plugin, samples) / plugin->dst_format.voices) + return -EINVAL; + for (voice = 0; voice < plugin->dst_format.voices; voice++) { + frag = data->frag + (voice * data->frags); + v->addr = data->buffer + control->fragments[frag].addr; + v->offset = 0; + v->next = plugin->dst_width; + } } + *voices = plugin->voices; return 0; } static ssize_t mmap_transfer(snd_pcm_plugin_t *plugin, - char *src_ptr, size_t src_size, - char *dst_ptr, size_t dst_size) + const snd_pcm_plugin_voice_t *src_voices, + const snd_pcm_plugin_voice_t *dst_voices, + size_t samples) { - struct mmap_private_data *data; + mmap_t *data; snd_pcm_mmap_control_t *control; - int interleave, voice, err; + ssize_t size; + int voice, err; char *addr; - if (plugin == NULL || dst_ptr == NULL || dst_size <= 0) - return -EINVAL; - data = (struct mmap_private_data *)snd_pcm_plugin_extra_data(plugin); - if (data == NULL) + if (plugin == NULL) return -EINVAL; + data = (mmap_t *)plugin->extra_data; control = data->control; if (control == NULL) return -EINVAL; - interleave = control->status.voices < 0; - if (interleave) { - if (dst_size != control->status.frag_size) - return -EINVAL; - } else { - if (dst_size != control->status.frag_size * control->status.voices) - return -EINVAL; - } if (data->channel == SND_PCM_CHANNEL_PLAYBACK) { - while (!playback_ok(data)) { - err = query_playback(data, 0); + if (src_voices == NULL) + return -EINVAL; + while (!playback_ok(plugin)) { + err = query_playback(plugin, 0); if (err < 0) return err; } - if (interleave) { + size = snd_pcm_plugin_src_samples_to_size(plugin, samples); + if (size < 0) + return size; + if (plugin->src_format.interleave) { + if (size != control->status.frag_size) + return -EINVAL; addr = data->buffer + control->fragments[data->frag].addr; - if (dst_ptr != addr) - memcpy(addr, dst_ptr, dst_size); + if (src_voices->addr != addr) + memcpy(addr, src_voices->addr, size); control->fragments[data->frag++].data = 1; data->frag %= control->status.frags; data->frags_used++; + return samples; } else { int frag; - for (voice = 0; voice < control->status.voices; voice++) { + if ((size / plugin->src_format.voices) != control->status.frag_size) + return -EINVAL; + for (voice = 0; voice < plugin->src_format.voices; voice++) { frag = data->frag + (voice * data->frags); while (control->fragments[frag].data) { - err = query_playback(data, 1); + err = query_playback(plugin, 1); if (err < 0) return err; } addr = data->buffer + control->fragments[frag].addr; - if (dst_ptr != addr) - memcpy(addr, dst_ptr, control->status.frag_size); + if (src_voices[voice].addr != addr) + memcpy(addr, src_voices[voice].addr, control->status.frag_size); control->fragments[frag].data = 1; - dst_ptr += control->status.frag_size; } data->frag++; data->frag %= data->frags; data->frags_used++; + return samples; } - return dst_size; } else if (data->channel == SND_PCM_CHANNEL_CAPTURE) { - while (!capture_ok(data)) { - err = query_capture(data, 0); + if (dst_voices == NULL) + return -EINVAL; + while (!capture_ok(plugin)) { + err = query_capture(plugin, 0); if (err < 0) return err; } - if (interleave) { + size = snd_pcm_plugin_dst_samples_to_size(plugin, samples); + if (size < 0) + return size; + if (plugin->dst_format.interleave) { + if (size != control->status.frag_size) + return -EINVAL; addr = data->buffer + control->fragments[data->frag].addr; - if (dst_ptr != addr) - memcpy(dst_ptr, addr, dst_size); + if (dst_voices->addr != addr) + memcpy(dst_voices->addr, addr, size); control->fragments[data->frag++].data = 0; data->frag %= control->status.frags; data->frags_used--; + return samples; } else { int frag; - for (voice = 0; voice < control->status.voices; voice++) { + if ((size / plugin->dst_format.voices) != control->status.frag_size) + return -EINVAL; + for (voice = 0; voice < plugin->dst_format.voices; voice++) { frag = data->frag + (voice * data->frags); while (!control->fragments[data->frag].data) { - err = query_capture(data, 1); + err = query_capture(plugin, 1); if (err < 0) return err; } addr = data->buffer + control->fragments[frag].addr; - if (dst_ptr != addr) - memcpy(dst_ptr, addr, control->status.frag_size); + if (dst_voices[voice].addr != addr) + memcpy(dst_voices[voice].addr, addr, control->status.frag_size); control->fragments[frag].data = 0; - dst_ptr += control->status.frag_size; } data->frag++; data->frag %= data->frags; data->frags_used--; + return samples; } - return dst_size; } else { return -EINVAL; } @@ -315,15 +396,15 @@ static int mmap_action(snd_pcm_plugin_t *plugin, if (plugin == NULL) return -EINVAL; - data = (struct mmap_private_data *)snd_pcm_plugin_extra_data(plugin); + data = (mmap_t *)plugin->extra_data; if (action == INIT) { snd_pcm_channel_params_t *params; snd_pcm_channel_setup_t setup; - int result, frags; + int result; if (data->control) - snd_pcm_munmap(data->pcm, data->channel); - result = snd_pcm_mmap(data->pcm, data->channel, &data->control, (void **)&data->buffer); + snd_pcm_munmap(plugin->handle, data->channel); + result = snd_pcm_mmap(plugin->handle, data->channel, &data->control, (void **)&data->buffer); if (result < 0) return result; params = (snd_pcm_channel_params_t *)udata; @@ -331,7 +412,7 @@ static int mmap_action(snd_pcm_plugin_t *plugin, data->stop_mode = params->stop_mode; memset(&setup, 0, sizeof(setup)); setup.channel = data->channel; - if ((result = snd_pcm_channel_setup(data->pcm, &setup)) < 0) + if ((result = snd_pcm_channel_setup(plugin->handle, &setup)) < 0) return result; data->frags = setup.buf.block.frags; data->frags_min = setup.buf.block.frags_min; @@ -368,14 +449,16 @@ static void mmap_free(snd_pcm_plugin_t *plugin, void *private_data) if (plugin == NULL) return; - data = (struct mmap_private_data *)snd_pcm_plugin_extra_data(plugin); + data = (mmap_t *)plugin->extra_data; if (data->control) - snd_pcm_munmap(data->pcm, data->channel); + snd_pcm_munmap(plugin->handle, data->channel); } -int snd_pcm_plugin_build_mmap(snd_pcm_t *pcm, int channel, snd_pcm_plugin_t **r_plugin) +int snd_pcm_plugin_build_mmap(snd_pcm_t *pcm, int channel, + snd_pcm_format_t *format, + snd_pcm_plugin_t **r_plugin) { - struct mmap_private_data *data; + mmap_t *data; snd_pcm_plugin_t *plugin; if (r_plugin == NULL) @@ -383,16 +466,18 @@ int snd_pcm_plugin_build_mmap(snd_pcm_t *pcm, int channel, snd_pcm_plugin_t **r_ *r_plugin = NULL; if (!pcm || channel < 0 || channel > 1) return -EINVAL; - plugin = snd_pcm_plugin_build(channel == SND_PCM_CHANNEL_PLAYBACK ? + plugin = snd_pcm_plugin_build(pcm, + channel == SND_PCM_CHANNEL_PLAYBACK ? "I/O mmap playback" : "I/O mmap capture", - sizeof(struct mmap_private_data)); + format, format, + sizeof(mmap_t)); if (plugin == NULL) return -ENOMEM; - data = (struct mmap_private_data *)snd_pcm_plugin_extra_data(plugin); - data->pcm = pcm; + data = (mmap_t *)plugin->extra_data; data->channel = channel; - plugin->transfer_src_ptr = mmap_transfer_src_ptr; + plugin->src_voices = mmap_src_voices; + plugin->dst_voices = mmap_dst_voices; plugin->transfer = mmap_transfer; plugin->action = mmap_action; plugin->private_free = mmap_free; diff --git a/src/pcm/plugin/mulaw.c b/src/pcm/plugin/mulaw.c index 337c1787..6dd7ff24 100644 --- a/src/pcm/plugin/mulaw.c +++ b/src/pcm/plugin/mulaw.c @@ -23,8 +23,8 @@ #ifdef __KERNEL__ #include "../../include/driver.h" +#include "../../include/pcm.h" #include "../../include/pcm_plugin.h" -#define bswap_16(x) __swab16((x)) #else #include #include @@ -33,6 +33,7 @@ #include #include #include +#include #include "../pcm_local.h" #endif @@ -87,7 +88,7 @@ static inline int search(int val, short *table, int size) * For further information see John C. Bellamy's Digital Telephony, 1982, * John Wiley & Sons, pps 98-111 and 472-476. */ -static inline unsigned char linear2ulaw(int pcm_val) /* 2's complement (16-bit range) */ +static unsigned char linear2ulaw(int pcm_val) /* 2's complement (16-bit range) */ { int mask; int seg; @@ -126,7 +127,7 @@ static inline unsigned char linear2ulaw(int pcm_val) /* 2's complement (16-bit r * Note that this function expects to be passed the complement of the * original code word. This is in keeping with ISDN conventions. */ -static inline int ulaw2linear(unsigned char u_val) +static int ulaw2linear(unsigned char u_val) { int t; @@ -147,296 +148,146 @@ static inline int ulaw2linear(unsigned char u_val) * Basic Mu-Law plugin */ -typedef enum { - _S8_MULAW, - _U8_MULAW, - _S16LE_MULAW, - _U16LE_MULAW, - _S16BE_MULAW, - _U16BE_MULAW, - _MULAW_S8, - _MULAW_U8, - _MULAW_S16LE, - _MULAW_U16LE, - _MULAW_S16BE, - _MULAW_U16BE -} combination_t; - -struct mulaw_private_data { - combination_t cmd; -}; - -static void mulaw_conv_u8bit_mulaw(unsigned char *src_ptr, unsigned char *dst_ptr, size_t size) -{ - unsigned int pcm; - - while (size-- > 0) { - pcm = ((*src_ptr++) ^ 0x80) << 8; - *dst_ptr++ = linear2ulaw((signed short)(pcm)); - } -} - -static void mulaw_conv_s8bit_mulaw(unsigned char *src_ptr, unsigned char *dst_ptr, size_t size) -{ - unsigned int pcm; - - while (size-- > 0) { - pcm = *src_ptr++ << 8; - *dst_ptr++ = linear2ulaw((signed short)(pcm)); - } -} - -static void mulaw_conv_s16bit_mulaw(unsigned short *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = linear2ulaw((signed short)(*src_ptr++)); -} - -static void mulaw_conv_s16bit_swap_mulaw(unsigned short *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = linear2ulaw((signed short)(bswap_16(*src_ptr++))); -} - -static void mulaw_conv_u16bit_mulaw(unsigned short *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = linear2ulaw((signed short)((*src_ptr++) ^ 0x8000)); -} - -static void mulaw_conv_u16bit_swap_mulaw(unsigned short *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = linear2ulaw((signed short)(bswap_16(*src_ptr++) ^ 0x8000)); -} - -static void mulaw_conv_mulaw_u8bit(unsigned char *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = (ulaw2linear(*src_ptr++) >> 8) ^ 0x80; -} - -static void mulaw_conv_mulaw_s8bit(unsigned char *src_ptr, unsigned char *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = ulaw2linear(*src_ptr++) >> 8; +typedef void (*mulaw_f)(void *src_ptr, void *dst_ptr, int samples); + +typedef struct mulaw_private_data { + int src_byte_width; + int dst_byte_width; + mulaw_f func; +} mulaw_t; + +#define MULAW_FUNC_DECODE(name, dsttype, val) \ +static void mulaw_decode_##name(void *src_ptr, void *dst_ptr, int samples) \ +{ \ + unsigned char *src = src_ptr; \ + dsttype *dst = dst_ptr; \ + unsigned int s; \ + while (samples--) { \ + s = ulaw2linear(*src++); \ + *dst++ = val; \ + } \ } -static void mulaw_conv_mulaw_s16bit(unsigned char *src_ptr, unsigned short *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = ulaw2linear(*src_ptr++); -} - -static void mulaw_conv_mulaw_swap_s16bit(unsigned char *src_ptr, unsigned short *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = bswap_16(ulaw2linear(*src_ptr++)); +#define MULAW_FUNC_ENCODE(name, srctype, val) \ +static void mulaw_encode_##name(void *src_ptr, void *dst_ptr, int samples) \ +{ \ + srctype *src = src_ptr; \ + unsigned char *dst = dst_ptr; \ + unsigned int s; \ + while (samples--) { \ + s = *src++; \ + *dst++ = linear2ulaw(val); \ + } \ } -static void mulaw_conv_mulaw_u16bit(unsigned char *src_ptr, unsigned short *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = ulaw2linear(*src_ptr++) ^ 0x8000; -} +MULAW_FUNC_DECODE(u8, u_int8_t, (s >> 8) ^ 0x80) +MULAW_FUNC_DECODE(s8, u_int8_t, s >> 8) +MULAW_FUNC_DECODE(u16n, u_int16_t, s ^ 0x8000) +MULAW_FUNC_DECODE(u16s, u_int16_t, bswap_16(s ^ 0x8000)) +MULAW_FUNC_DECODE(s16n, u_int16_t, s) +MULAW_FUNC_DECODE(s16s, u_int16_t, bswap_16(s)) +MULAW_FUNC_DECODE(u24n, u_int32_t, (s << 8) ^ 0x800000) +MULAW_FUNC_DECODE(u24s, u_int32_t, bswap_32((s << 8) ^ 0x800000)) +MULAW_FUNC_DECODE(s24n, u_int32_t, s << 8) +MULAW_FUNC_DECODE(s24s, u_int32_t, bswap_32(s << 8)) +MULAW_FUNC_DECODE(u32n, u_int32_t, (s << 16) ^ 0x80000000) +MULAW_FUNC_DECODE(u32s, u_int32_t, bswap_32((s << 16) ^ 0x80000000)) +MULAW_FUNC_DECODE(s32n, u_int32_t, s << 16) +MULAW_FUNC_DECODE(s32s, u_int32_t, bswap_32(s << 16)) + +MULAW_FUNC_ENCODE(u8, u_int8_t, s << 8) +MULAW_FUNC_ENCODE(s8, u_int8_t, (s << 8) ^ 0x8000) +MULAW_FUNC_ENCODE(u16n, u_int16_t, s ^ 0x8000) +MULAW_FUNC_ENCODE(u16s, u_int16_t, bswap_16(s ^ 0x8000)) +MULAW_FUNC_ENCODE(s16n, u_int16_t, s) +MULAW_FUNC_ENCODE(s16s, u_int16_t, bswap_16(s)) +MULAW_FUNC_ENCODE(u24n, u_int32_t, (s ^ 0x800000) >> 8) +MULAW_FUNC_ENCODE(u24s, u_int32_t, bswap_32((s ^ 0x800000) >> 8)) +MULAW_FUNC_ENCODE(s24n, u_int32_t, s >> 8) +MULAW_FUNC_ENCODE(s24s, u_int32_t, bswap_32(s >> 8)) +MULAW_FUNC_ENCODE(u32n, u_int32_t, (s ^ 0x80000000) >> 16) +MULAW_FUNC_ENCODE(u32s, u_int32_t, bswap_32((s ^ 0x80000000) >> 16)) +MULAW_FUNC_ENCODE(s32n, u_int32_t, s >> 16) +MULAW_FUNC_ENCODE(s32s, u_int32_t, bswap_32(s >> 16)) + +/* wide, sign, swap endian */ +static mulaw_f mulaw_functions_decode[4 * 4 * 2 * 2] = { + mulaw_decode_u8, /* decode:8-bit:unsigned:none */ + mulaw_decode_u8, /* decode:8-bit:unsigned:swap */ + mulaw_decode_s8, /* decode:8-bit:signed:none */ + mulaw_decode_s8, /* decode:8-bit:signed:swap */ + mulaw_decode_u16n, /* decode:16-bit:unsigned:none */ + mulaw_decode_u16s, /* decode:16-bit:unsigned:swap */ + mulaw_decode_s16n, /* decode:16-bit:signed:none */ + mulaw_decode_s16s, /* decode:16-bit:signed:swap */ + mulaw_decode_u24n, /* decode:24-bit:unsigned:none */ + mulaw_decode_u24s, /* decode:24-bit:unsigned:swap */ + mulaw_decode_s24n, /* decode:24-bit:signed:none */ + mulaw_decode_s24s, /* decode:24-bit:signed:swap */ + mulaw_decode_u32n, /* decode:32-bit:unsigned:none */ + mulaw_decode_u32s, /* decode:32-bit:unsigned:swap */ + mulaw_decode_s32n, /* decode:32-bit:signed:none */ + mulaw_decode_s32s, /* decode:32-bit:signed:swap */ +}; -static void mulaw_conv_mulaw_swap_u16bit(unsigned char *src_ptr, unsigned short *dst_ptr, size_t size) -{ - while (size-- > 0) - *dst_ptr++ = bswap_16(ulaw2linear(*src_ptr++) ^ 0x8000); -} +/* wide, sign, swap endian */ +static mulaw_f mulaw_functions_encode[4 * 2 * 2] = { + mulaw_encode_u8, /* from:8-bit:unsigned:none */ + mulaw_encode_u8, /* from:8-bit:unsigned:swap */ + mulaw_encode_s8, /* from:8-bit:signed:none */ + mulaw_encode_s8, /* from:8-bit:signed:swap */ + mulaw_encode_u16n, /* from:16-bit:unsigned:none */ + mulaw_encode_u16s, /* from:16-bit:unsigned:swap */ + mulaw_encode_s16n, /* from:16-bit:signed:none */ + mulaw_encode_s16s, /* from:16-bit:signed:swap */ + mulaw_encode_u24n, /* from:24-bit:unsigned:none */ + mulaw_encode_u24s, /* from:24-bit:unsigned:swap */ + mulaw_encode_s24n, /* from:24-bit:signed:none */ + mulaw_encode_s24s, /* from:24-bit:signed:swap */ + mulaw_encode_u32n, /* from:32-bit:unsigned:none */ + mulaw_encode_u32s, /* from:32-bit:unsigned:swap */ + mulaw_encode_s32n, /* from:32-bit:signed:none */ + mulaw_encode_s32s, /* from:32-bit:signed:swap */ +}; static ssize_t mulaw_transfer(snd_pcm_plugin_t *plugin, - char *src_ptr, size_t src_size, - char *dst_ptr, size_t dst_size) + const snd_pcm_plugin_voice_t *src_voices, + const snd_pcm_plugin_voice_t *dst_voices, + size_t samples) { - struct mulaw_private_data *data; + mulaw_t *data; + int voice; - if (plugin == NULL || src_ptr == NULL || src_size < 0 || - dst_ptr == NULL || dst_size < 0) + if (plugin == NULL || src_voices == NULL || dst_voices == NULL || samples < 0) return -EINVAL; - if (src_size == 0) + if (samples == 0) return 0; - data = (struct mulaw_private_data *)snd_pcm_plugin_extra_data(plugin); - if (data == NULL) - return -EINVAL; - switch (data->cmd) { - case _U8_MULAW: - if (dst_size < src_size) - return -EINVAL; - mulaw_conv_u8bit_mulaw(src_ptr, dst_ptr, src_size); - return src_size; - case _S8_MULAW: - if (dst_size < src_size) - return -EINVAL; - mulaw_conv_s8bit_mulaw(src_ptr, dst_ptr, src_size); - return src_size; - case _S16LE_MULAW: - if ((dst_size << 1) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - mulaw_conv_s16bit_mulaw((short *)src_ptr, dst_ptr, src_size >> 1); -#elif __BYTE_ORDER == __BIG_ENDIAN - mulaw_conv_s16bit_swap_mulaw((short *)src_ptr, dst_ptr, src_size >> 1); -#else -#error "Have to be coded..." -#endif - return src_size >> 1; - case _U16LE_MULAW: - if ((dst_size << 1) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - mulaw_conv_u16bit_mulaw((short *)src_ptr, dst_ptr, src_size >> 1); -#elif __BYTE_ORDER == __BIG_ENDIAN - mulaw_conv_u16bit_swap_mulaw((short *)src_ptr, dst_ptr, src_size >> 1); -#else -#error "Have to be coded..." -#endif - return src_size >> 1; - case _S16BE_MULAW: - if ((dst_size << 1) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - mulaw_conv_s16bit_swap_mulaw((short *)src_ptr, dst_ptr, src_size >> 1); -#elif __BYTE_ORDER == __BIG_ENDIAN - mulaw_conv_s16bit_mulaw((short *)src_ptr, dst_ptr, src_size >> 1); -#else -#error "Have to be coded..." -#endif - return src_size >> 1; - case _U16BE_MULAW: - if ((dst_size << 1) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - mulaw_conv_u16bit_swap_mulaw((short *)src_ptr, dst_ptr, src_size >> 1); -#elif __BYTE_ORDER == __BIG_ENDIAN - mulaw_conv_u16bit_mulaw((short *)src_ptr, dst_ptr, src_size >> 1); -#else -#error "Have to be coded..." -#endif - return src_size >> 1; - case _MULAW_U8: - if (dst_size < src_size) - return -EINVAL; - mulaw_conv_mulaw_u8bit(src_ptr, dst_ptr, src_size); - return src_size; - case _MULAW_S8: - if (dst_size < src_size) - return -EINVAL; - mulaw_conv_mulaw_s8bit(src_ptr, dst_ptr, src_size); - return src_size; - case _MULAW_S16LE: - if ((dst_size >> 1) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - mulaw_conv_mulaw_s16bit(src_ptr, (short *)dst_ptr, src_size); -#elif __BYTE_ORDER == __BIG_ENDIAN - mulaw_conv_mulaw_swap_s16bit(src_ptr, (short *)dst_ptr, src_size); -#else -#error "Have to be coded..." -#endif - return src_size << 1; - case _MULAW_U16LE: - if ((dst_size >> 1) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - mulaw_conv_mulaw_u16bit(src_ptr, (short *)dst_ptr, src_size); -#elif __BYTE_ORDER == __BIG_ENDIAN - mulaw_conv_mulaw_swap_u16bit(src_ptr, (short *)dst_ptr, src_size); -#else -#error "Have to be coded..." -#endif - return src_size << 1; - case _MULAW_S16BE: - if ((dst_size >> 1) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - mulaw_conv_mulaw_swap_s16bit(src_ptr, (short *)dst_ptr, src_size); -#elif __BYTE_ORDER == __BIG_ENDIAN - mulaw_conv_mulaw_s16bit(src_ptr, (short *)dst_ptr, src_size); -#else -#error "Have to be coded..." -#endif - return src_size << 1; - case _MULAW_U16BE: - if ((dst_size >> 1) < src_size) - return -EINVAL; -#if __BYTE_ORDER == __LITTLE_ENDIAN - mulaw_conv_mulaw_swap_u16bit(src_ptr, (short *)dst_ptr, src_size); -#elif __BYTE_ORDER == __BIG_ENDIAN - mulaw_conv_mulaw_u16bit(src_ptr, (short *)dst_ptr, src_size); -#else -#error "Have to be coded..." -#endif - return src_size << 1; - default: - return -EIO; - } -} - -static ssize_t mulaw_src_size(snd_pcm_plugin_t *plugin, size_t size) -{ - struct mulaw_private_data *data; - - if (plugin == NULL || size <= 0) - return -EINVAL; - data = (struct mulaw_private_data *)snd_pcm_plugin_extra_data(plugin); - switch (data->cmd) { - case _U8_MULAW: - case _S8_MULAW: - case _MULAW_U8: - case _MULAW_S8: - return size; - case _U16LE_MULAW: - case _S16LE_MULAW: - case _U16BE_MULAW: - case _S16BE_MULAW: - return size * 2; - case _MULAW_U16LE: - case _MULAW_S16LE: - case _MULAW_U16BE: - case _MULAW_S16BE: - return size / 2; - default: - return -EIO; + data = (mulaw_t *)plugin->extra_data; + if (plugin->src_format.interleave) { + data->func(src_voices[0].addr, + dst_voices[0].addr, + samples * plugin->src_format.voices); + } else { + for (voice = 0; voice < plugin->src_format.voices; voice++) { + if (src_voices[voice].addr == NULL) + continue; + data->func(src_voices[voice].addr, + dst_voices[voice].addr, + samples); + } } + return samples; } -static ssize_t mulaw_dst_size(snd_pcm_plugin_t *plugin, size_t size) -{ - struct mulaw_private_data *data; - - if (plugin == NULL || size <= 0) - return -EINVAL; - data = (struct mulaw_private_data *)snd_pcm_plugin_extra_data(plugin); - switch (data->cmd) { - case _U8_MULAW: - case _S8_MULAW: - case _MULAW_U8: - case _MULAW_S8: - return size; - case _U16LE_MULAW: - case _S16LE_MULAW: - case _U16BE_MULAW: - case _S16BE_MULAW: - return size / 2; - case _MULAW_U16LE: - case _MULAW_S16LE: - case _MULAW_U16BE: - case _MULAW_S16BE: - return size * 2; - default: - return -EIO; - } -} - -int snd_pcm_plugin_build_mulaw(snd_pcm_format_t *src_format, +int snd_pcm_plugin_build_mulaw(snd_pcm_plugin_handle_t *handle, + snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin) { struct mulaw_private_data *data; snd_pcm_plugin_t *plugin; - combination_t cmd; + int endian, src_width, dst_width, sign; + mulaw_f func; if (r_plugin == NULL) return -EINVAL; @@ -450,40 +301,54 @@ int snd_pcm_plugin_build_mulaw(snd_pcm_format_t *src_format, if (src_format->voices != dst_format->voices) return -EINVAL; + if (dst_format->format == SND_PCM_SFMT_MU_LAW) { - switch (src_format->format) { - case SND_PCM_SFMT_U8: cmd = _U8_MULAW; break; - case SND_PCM_SFMT_S8: cmd = _S8_MULAW; break; - case SND_PCM_SFMT_U16_LE: cmd = _U16LE_MULAW; break; - case SND_PCM_SFMT_S16_LE: cmd = _S16LE_MULAW; break; - case SND_PCM_SFMT_U16_BE: cmd = _U16BE_MULAW; break; - case SND_PCM_SFMT_S16_BE: cmd = _S16BE_MULAW; break; - default: + if (!snd_pcm_format_linear(src_format->format)) return -EINVAL; - } + sign = snd_pcm_format_signed(src_format->format); + src_width = snd_pcm_format_width(src_format->format); + if ((src_width % 8) != 0 || src_width < 8 || src_width > 32) + return -EINVAL; + dst_width = 8; +#if __BYTE_ORDER == __LITTLE_ENDIAN + endian = snd_pcm_format_big_endian(src_format->format); +#elif __BYTE_ORDER == __BIG_ENDIAN + endian = snd_pcm_format_little_endian(src_format->format); +#else +#error "Unsupported endian..." +#endif + func = ((mulaw_f(*)[2][2])mulaw_functions_encode)[(src_width/8)-1][sign][endian]; } else if (src_format->format == SND_PCM_SFMT_MU_LAW) { - switch (dst_format->format) { - case SND_PCM_SFMT_U8: cmd = _MULAW_U8; break; - case SND_PCM_SFMT_S8: cmd = _MULAW_S8; break; - case SND_PCM_SFMT_U16_LE: cmd = _MULAW_U16LE; break; - case SND_PCM_SFMT_S16_LE: cmd = _MULAW_S16LE; break; - case SND_PCM_SFMT_U16_BE: cmd = _MULAW_U16BE; break; - case SND_PCM_SFMT_S16_BE: cmd = _MULAW_S16BE; break; - default: + if (!snd_pcm_format_linear(dst_format->format)) return -EINVAL; - } + sign = snd_pcm_format_signed(dst_format->format); + dst_width = snd_pcm_format_width(dst_format->format); + if ((dst_width % 8) != 0 || dst_width < 8 || dst_width > 32) + return -EINVAL; + src_width = 8; +#if __BYTE_ORDER == __LITTLE_ENDIAN + endian = snd_pcm_format_big_endian(dst_format->format); +#elif __BYTE_ORDER == __BIG_ENDIAN + endian = snd_pcm_format_little_endian(dst_format->format); +#else +#error "Unsupported endian..." +#endif + func = ((mulaw_f(*)[2][2])mulaw_functions_decode)[(dst_width/8)-1][sign][endian]; } else { return -EINVAL; } - plugin = snd_pcm_plugin_build("Mu-Law<->linear conversion", + plugin = snd_pcm_plugin_build(handle, + "Mu-Law<->linear conversion", + src_format, + dst_format, sizeof(struct mulaw_private_data)); if (plugin == NULL) return -ENOMEM; - data = (struct mulaw_private_data *)snd_pcm_plugin_extra_data(plugin); - data->cmd = cmd; + data = (struct mulaw_private_data *)plugin->extra_data; + data->src_byte_width = src_width / 8; + data->dst_byte_width = dst_width / 8; + data->func = func; plugin->transfer = mulaw_transfer; - plugin->src_size = mulaw_src_size; - plugin->dst_size = mulaw_dst_size; *r_plugin = plugin; return 0; } diff --git a/src/pcm/plugin/rate.c b/src/pcm/plugin/rate.c index 6cfe29b3..30d6dfa7 100644 --- a/src/pcm/plugin/rate.c +++ b/src/pcm/plugin/rate.c @@ -21,6 +21,7 @@ #ifdef __KERNEL__ #include "../../include/driver.h" +#include "../../include/pcm.h" #include "../../include/pcm_plugin.h" #else #include @@ -36,337 +37,350 @@ #define SHIFT 11 #define BITS (1<pos; - S1 = data->last_S1[voice]; - S2 = data->last_S2[voice]; - src = src_ptr + voice; - dst = dst_ptr + voice; - size = dst_size; - if (pos >> SHIFT) { - pos &= MASK; - S1 = S2; - S2 = *src; - } - while (size-- > 0) { - if (pos >> SHIFT) { - src += voices; - pos &= MASK; - S1 = S2; - if ((src - src_ptr) < src_size * voices) - S2 = *src; - } - val = S1 + ((S2 - S1) * (signed int)pos) / BITS; - if (val < -32768) - val = -32768; - else if (val > 32767) - val = 32767; - *dst = val; - dst += voices; - pos += data->pitch; - } - data->last_S1[voice] = S1; - data->last_S2[voice] = S2; - data->pos = pos; + rate_voice_t *rvoices = rate_voices(data); + + data->pos = 0; + for (voice = 0; plugin->src_format.voices; voice++) { + rvoices[voice].last_S1 = 0; + rvoices[voice].last_S2 = 0; } } -static void resample16_shrink(struct rate_private_data *data, int voices, - signed short *src_ptr, int src_size, - signed short *dst_ptr, int dst_size) -{ - unsigned int pos; - signed int val; - signed short S1, S2; - int voice; - signed short *src, *dst; - int size; - - for (voice = 0; voice < voices; ++voice) { - pos = data->pos; - S1 = data->last_S1[voice]; - S2 = data->last_S2[voice]; - src = src_ptr + voice; - dst = dst_ptr + voice; - size = dst_size; - while (size > 0) { - S1 = S2; - if ((src - src_ptr) < (src_size * voices)) { - S2 = *src; - src += voices; - } - if (pos >> SHIFT) { - pos &= MASK; - val = S1 + ((S2 - S1) * (signed int)pos) / BITS; - if (val < -32768) - val = -32768; - else if (val > 32767) - val = 32767; - *dst = val; - dst += voices; - size--; - } - pos += data->pitch; - } - data->last_S1[voice] = S1; - data->last_S2[voice] = S2; - data->pos = pos; - } +#define RATE_TAKE_SAMPLE(name, type, val) \ +static signed short rate_take_sample_##name(void *ptr) \ +{ \ + signed int smp = *(type *)ptr; \ + return val; \ +} + +#define RATE_PUT_SAMPLE(name, type, val) \ +static void rate_put_sample_##name(void *ptr, signed int smp) \ +{ \ + *(type *)ptr = val; \ } -static void resample8_expand(struct rate_private_data *data, int voices, - unsigned char *src_ptr, int src_size, - unsigned char *dst_ptr, int dst_size) +static void resample_expand(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_voice_t *src_voices, + const snd_pcm_plugin_voice_t *dst_voices, + int src_samples, int dst_samples) { unsigned int pos; signed int val; signed short S1, S2; + char *src, *dst; int voice; - unsigned char *src, *dst; - int size; + int src_step, dst_step; + int src_samples1, dst_samples1; + rate_t *data = (rate_t *)plugin->extra_data; + rate_voice_t *rvoices = rate_voices(data); - for (voice = 0; voice < voices; ++voice) { + for (voice = 0; voice < plugin->src_format.voices; voice++, rvoices++) { pos = data->pos; - S1 = data->last_S1[voice]; - S2 = data->last_S2[voice]; - src = src_ptr + voice; - dst = dst_ptr + voice; - size = dst_size; - if (pos >> SHIFT) { + S1 = rvoices->last_S1; + S2 = rvoices->last_S2; + if (src_voices[voice].addr == NULL) + continue; + src = (char *)src_voices[voice].addr + src_voices[voice].offset / 8; + dst = (char *)dst_voices[voice].addr + src_voices[voice].offset / 8; + src_step = src_voices[voice].next / 8; + dst_step = dst_voices[voice].next / 8; + src_samples1 = src_samples; + dst_samples1 = dst_samples; + if (pos & ~MASK) { pos &= MASK; S1 = S2; - S2 = (*src << 8) ^ 0x8000; + S2 = data->take(src); + src += src_step; + src_samples--; } - while (size-- > 0) { - if (pos >> SHIFT) { - src += voices; + while (dst_samples1-- > 0) { + if (pos & ~MASK) { pos &= MASK; S1 = S2; - if ((src - src_ptr) < src_size * voices) - S2 = (*src << 8) ^ 0x8000; + if (src_samples1-- > 0) { + S2 = data->take(src); + src += src_step; + } } val = S1 + ((S2 - S1) * (signed int)pos) / BITS; if (val < -32768) val = -32768; else if (val > 32767) val = 32767; - *dst = (val >> 8) ^ 0x0080; - dst += voices; + data->put(dst, val); + dst += dst_step; pos += data->pitch; } - data->last_S1[voice] = S1; - data->last_S2[voice] = S2; + rvoices->last_S1 = S1; + rvoices->last_S2 = S2; data->pos = pos; } } -static void resample8_shrink(struct rate_private_data *data, int voices, - unsigned char *src_ptr, int src_size, - unsigned char *dst_ptr, int dst_size) +static void resample_shrink(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_voice_t *src_voices, + const snd_pcm_plugin_voice_t *dst_voices, + int src_samples, int dst_samples) { unsigned int pos; signed int val; signed short S1, S2; + char *src, *dst; int voice; - unsigned char *src, *dst; - int size; + int src_step, dst_step; + int src_samples1, dst_samples1; + rate_t *data = (rate_t *)plugin->extra_data; + rate_voice_t *rvoices = rate_voices(data); - for (voice = 0; voice < voices; ++voice) { + for (voice = 0; voice < plugin->src_format.voices; ++voice) { pos = data->pos; - S1 = data->last_S1[voice]; - S2 = data->last_S2[voice]; - src = src_ptr + voice; - dst = dst_ptr + voice; - size = dst_size; - while (size > 0) { + S1 = rvoices->last_S1; + S2 = rvoices->last_S2; + if (src_voices[voice].addr == NULL) + continue; + src = (char *)src_voices[voice].addr + src_voices[voice].offset / 8; + dst = (char *)dst_voices[voice].addr + src_voices[voice].offset / 8; + src_step = src_voices[voice].next / 8; + dst_step = dst_voices[voice].next / 8; + src_samples1 = src_samples; + dst_samples1 = dst_samples; + while (dst_samples1 > 0) { S1 = S2; - if ((src - src_ptr) < (src_size * voices)) { - S2 = (*src << 8) ^ 0x8000; - src += voices; + if (src_samples1-- > 0) { + S2 = data->take(src); + src += src_step; } - if (pos >> SHIFT) { + if (pos & ~MASK) { pos &= MASK; val = S1 + ((S2 - S1) * (signed int)pos) / BITS; if (val < -32768) val = -32768; else if (val > 32767) val = 32767; - *dst = (val >> 8) ^ 0x0080; - dst += voices; - size--; + data->put(dst, val); + dst += dst_step; + dst_samples1--; } pos += data->pitch; } - data->last_S1[voice] = S1; - data->last_S2[voice] = S2; + rvoices->last_S1 = S1; + rvoices->last_S2 = S2; data->pos = pos; } } -static ssize_t rate_src_size(snd_pcm_plugin_t *plugin, size_t size) +static ssize_t rate_src_samples(snd_pcm_plugin_t *plugin, size_t samples) { - struct rate_private_data *data; + rate_t *data; ssize_t res; - if (plugin == NULL || size <= 0) + if (plugin == NULL || samples <= 0) return -EINVAL; - data = (struct rate_private_data *)snd_pcm_plugin_extra_data(plugin); - if (data->expand) { - res = (((size * data->pitch) + (BITS/2)) >> SHIFT); + data = (rate_t *)plugin->extra_data; + if (plugin->src_format.rate < plugin->dst_format.rate) { + res = (((samples * data->pitch) + (BITS/2)) >> SHIFT); } else { - res = (((size << SHIFT) + (data->pitch / 2)) / data->pitch); + res = (((samples << SHIFT) + (data->pitch / 2)) / data->pitch); } - res = res / (data->src_voices*data->sample_size) * (data->src_voices*data->sample_size); - if (data->old_src_size > 0) { - ssize_t size1 = size, res1 = data->old_dst_size; - while (data->old_src_size < size1) { - size1 >>= 1; + if (data->old_src_samples > 0) { + ssize_t samples1 = samples, res1 = data->old_dst_samples; + while (data->old_src_samples < samples1) { + samples1 >>= 1; res1 <<= 1; } - while (data->old_src_size > size1) { - size1 <<= 1; + while (data->old_src_samples > samples1) { + samples1 <<= 1; res1 >>= 1; } - if (data->old_src_size == size1) + if (data->old_src_samples == samples1) return res1; } - data->old_src_size = size; - data->old_dst_size = res; + data->old_src_samples = samples; + data->old_dst_samples = res; return res; } -static ssize_t rate_dst_size(snd_pcm_plugin_t *plugin, size_t size) +static ssize_t rate_dst_samples(snd_pcm_plugin_t *plugin, size_t samples) { - struct rate_private_data *data; + rate_t *data; ssize_t res; - if (plugin == NULL || size <= 0) + if (plugin == NULL || samples <= 0) return -EINVAL; - data = (struct rate_private_data *)snd_pcm_plugin_extra_data(plugin); - if (data->expand) { - res = (((size << SHIFT) + (data->pitch / 2)) / data->pitch); + data = (rate_t *)plugin->extra_data; + if (plugin->src_format.rate < plugin->dst_format.rate) { + res = (((samples << SHIFT) + (data->pitch / 2)) / data->pitch); } else { - res = (((size * data->pitch) + (BITS/2)) >> SHIFT); + res = (((samples * data->pitch) + (BITS/2)) >> SHIFT); } - res = res / (data->dst_voices*data->sample_size) * (data->dst_voices*data->sample_size); - if (data->old_dst_size > 0) { - ssize_t size1 = size, res1 = data->old_src_size; - while (data->old_dst_size < size1) { - size1 >>= 1; + if (data->old_dst_samples > 0) { + ssize_t samples1 = samples, res1 = data->old_src_samples; + while (data->old_dst_samples < samples1) { + samples1 >>= 1; res1 <<= 1; } - while (data->old_dst_size > size1) { - size1 <<= 1; + while (data->old_dst_samples > samples1) { + samples1 <<= 1; res1 >>= 1; } - if (data->old_dst_size == size1) + if (data->old_dst_samples == samples1) return res1; } - data->old_dst_size = size; - data->old_src_size = res; + data->old_dst_samples = samples; + data->old_src_samples = res; return res; } static ssize_t rate_transfer(snd_pcm_plugin_t *plugin, - char *src_ptr, size_t src_size, - char *dst_ptr, size_t dst_size) + const snd_pcm_plugin_voice_t *src_voices, + const snd_pcm_plugin_voice_t *dst_voices, + size_t samples) { - struct rate_private_data *data; + size_t dst_samples; - if (plugin == NULL || src_ptr == NULL || src_size < 0 || - dst_ptr == NULL || dst_size < 0) + if (plugin == NULL || src_voices == NULL || src_voices == NULL || samples < 0) return -EINVAL; - if (src_size == 0) + if (samples == 0) return 0; - data = (struct rate_private_data *)snd_pcm_plugin_extra_data(plugin); - if (data == NULL) - return -EINVAL; - if (data->sample_size == 2) { - if (data->src_rate < data->dst_rate) { - resample16_expand(data, data->src_voices, - (signed short *)src_ptr, src_size / (data->src_voices * 2), - (signed short *)dst_ptr, dst_size / (data->dst_voices * 2)); - } else { - resample16_shrink(data, data->src_voices, - (signed short *)src_ptr, src_size / (data->src_voices * 2), - (signed short *)dst_ptr, dst_size / (data->dst_voices * 2)); - } + dst_samples = rate_dst_samples(plugin, samples); + if (plugin->src_format.rate < plugin->dst_format.rate) { + resample_expand(plugin, src_voices, dst_voices, samples, dst_samples); } else { - if (data->src_rate < data->dst_rate) { - resample8_expand(data, data->src_voices, - src_ptr, src_size / data->src_voices, - dst_ptr, dst_size / data->dst_voices); - } else { - resample8_shrink(data, data->src_voices, - src_ptr, src_size / data->src_voices, - dst_ptr, dst_size / data->dst_voices); - } + resample_shrink(plugin, src_voices, dst_voices, samples, dst_samples); } - return rate_dst_size(plugin, src_size); + return dst_samples; } static int rate_action(snd_pcm_plugin_t *plugin, snd_pcm_plugin_action_t action, unsigned long udata) { - struct rate_private_data *data; - int voice; + rate_t *data; if (plugin == NULL) return -EINVAL; - data = (struct rate_private_data *)snd_pcm_plugin_extra_data(plugin); + data = (rate_t *)plugin->extra_data; switch (action) { case INIT: case PREPARE: case DRAIN: case FLUSH: - data->pos = 0; - for (voice = 0; voice < data->src_voices; ++voice) { - data->last_S1[voice] = data->last_S2[voice] = 0; - } + rate_init(plugin, data); break; } return 0; /* silenty ignore other actions */ } -int snd_pcm_plugin_build_rate(snd_pcm_format_t *src_format, +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define my_little_swap16(x) (x) +#define my_little_swap32(x) (x) +#define my_big_swap16(x) bswap_16(x) +#define my_big_swap32(x) bswap_32(x) +#else +#define my_little_swap16(x) bswap_16(x) +#define my_little_swap32(x) bswap_32(x) +#define my_big_swap16(x) (x) +#define my_big_swap32(x) (x) +#endif + +RATE_TAKE_SAMPLE(s8, int8_t, smp << 8) +RATE_TAKE_SAMPLE(u8, int8_t, (smp << 8) ^ 0x8000) +RATE_TAKE_SAMPLE(s16_le, int16_t, my_little_swap16(smp)) +RATE_TAKE_SAMPLE(s16_be, int16_t, my_big_swap16(smp)) +RATE_TAKE_SAMPLE(u16_le, int16_t, my_little_swap16(smp) ^ 0x8000) +RATE_TAKE_SAMPLE(u16_be, int16_t, my_big_swap16(smp) ^ 0x8000) +RATE_TAKE_SAMPLE(s24_le, int32_t, my_little_swap32(smp) >> 8) +RATE_TAKE_SAMPLE(s24_be, int32_t, my_big_swap32(smp) >> 8) +RATE_TAKE_SAMPLE(u24_le, int32_t, (my_little_swap32(smp) >> 8) ^ 0x8000) +RATE_TAKE_SAMPLE(u24_be, int32_t, (my_big_swap32(smp) >> 8) ^ 0x8000) +RATE_TAKE_SAMPLE(s32_le, int32_t, my_little_swap32(smp) >> 16) +RATE_TAKE_SAMPLE(s32_be, int32_t, my_big_swap32(smp) >> 16) +RATE_TAKE_SAMPLE(u32_le, int32_t, (my_little_swap32(smp) >> 16) ^ 0x8000) +RATE_TAKE_SAMPLE(u32_be, int32_t, (my_big_swap32(smp) >> 16) ^ 0x8000) + +static take_sample_f rate_take_sample[] = { + [SND_PCM_SFMT_S8] rate_take_sample_s8, + [SND_PCM_SFMT_U8] rate_take_sample_u8, + [SND_PCM_SFMT_S16_LE] rate_take_sample_s16_le, + [SND_PCM_SFMT_S16_BE] rate_take_sample_s16_be, + [SND_PCM_SFMT_U16_LE] rate_take_sample_u16_le, + [SND_PCM_SFMT_U16_BE] rate_take_sample_u16_be, + [SND_PCM_SFMT_S24_LE] rate_take_sample_s24_le, + [SND_PCM_SFMT_S24_BE] rate_take_sample_s24_be, + [SND_PCM_SFMT_U24_LE] rate_take_sample_u24_le, + [SND_PCM_SFMT_U24_BE] rate_take_sample_u24_be, + [SND_PCM_SFMT_S32_LE] rate_take_sample_s32_le, + [SND_PCM_SFMT_S32_BE] rate_take_sample_s32_be, + [SND_PCM_SFMT_U32_LE] rate_take_sample_u32_le, + [SND_PCM_SFMT_U32_BE] rate_take_sample_u32_be +}; + +RATE_PUT_SAMPLE(s8, int8_t, smp >> 8) +RATE_PUT_SAMPLE(u8, int8_t, (smp >> 8) ^ 0x80) +RATE_PUT_SAMPLE(s16_le, int16_t, my_little_swap16(smp)) +RATE_PUT_SAMPLE(s16_be, int16_t, my_big_swap16(smp)) +RATE_PUT_SAMPLE(u16_le, int16_t, my_little_swap16(smp ^ 0x8000)) +RATE_PUT_SAMPLE(u16_be, int16_t, my_big_swap16(smp ^ 0x8000)) +RATE_PUT_SAMPLE(s24_le, int32_t, my_little_swap32(smp << 8)) +RATE_PUT_SAMPLE(s24_be, int32_t, my_big_swap32(smp << 8)) +RATE_PUT_SAMPLE(u24_le, int32_t, my_little_swap32((smp ^ 0x8000) >> 8)) +RATE_PUT_SAMPLE(u24_be, int32_t, my_big_swap32((smp ^ 0x8000) >> 8)) +RATE_PUT_SAMPLE(s32_le, int32_t, my_little_swap32(smp >> 16)) +RATE_PUT_SAMPLE(s32_be, int32_t, my_big_swap32(smp >> 16)) +RATE_PUT_SAMPLE(u32_le, int32_t, my_little_swap32((smp ^ 0x8000) >> 16)) +RATE_PUT_SAMPLE(u32_be, int32_t, my_big_swap32((smp ^ 0x8000) >> 16)) + +static put_sample_f rate_put_sample[] = { + [SND_PCM_SFMT_S8] rate_put_sample_s8, + [SND_PCM_SFMT_U8] rate_put_sample_u8, + [SND_PCM_SFMT_S16_LE] rate_put_sample_s16_le, + [SND_PCM_SFMT_S16_BE] rate_put_sample_s16_be, + [SND_PCM_SFMT_U16_LE] rate_put_sample_u16_le, + [SND_PCM_SFMT_U16_BE] rate_put_sample_u16_be, + [SND_PCM_SFMT_S24_LE] rate_put_sample_s24_le, + [SND_PCM_SFMT_S24_BE] rate_put_sample_s24_be, + [SND_PCM_SFMT_U24_LE] rate_put_sample_u24_le, + [SND_PCM_SFMT_U24_BE] rate_put_sample_u24_be, + [SND_PCM_SFMT_S32_LE] rate_put_sample_s32_le, + [SND_PCM_SFMT_S32_BE] rate_put_sample_s32_be, + [SND_PCM_SFMT_U32_LE] rate_put_sample_u32_le, + [SND_PCM_SFMT_U32_BE] rate_put_sample_u32_be +}; + +int snd_pcm_plugin_build_rate(snd_pcm_plugin_handle_t *handle, + snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin) { struct rate_private_data *data; snd_pcm_plugin_t *plugin; - int voice; if (r_plugin == NULL) return -EINVAL; @@ -375,45 +389,41 @@ int snd_pcm_plugin_build_rate(snd_pcm_format_t *src_format, if (src_format->interleave != dst_format->interleave && src_format->voices > 1) return -EINVAL; - if (src_format->format != dst_format->format) - return -EINVAL; if (!dst_format->interleave) return -EINVAL; if (src_format->voices != dst_format->voices) return -EINVAL; - if (dst_format->voices < 1 || dst_format->voices > MAX_VOICES) + if (dst_format->voices < 1) return -EINVAL; - - if (src_format->format != SND_PCM_SFMT_S16_LE && - src_format->format != SND_PCM_SFMT_U8) + if (snd_pcm_format_linear(src_format->format) <= 0) + return -EINVAL; + if (snd_pcm_format_linear(dst_format->format) <= 0) return -EINVAL; if (src_format->rate == dst_format->rate) return -EINVAL; - plugin = snd_pcm_plugin_build("rate conversion", - sizeof(struct rate_private_data)); + plugin = snd_pcm_plugin_build(handle, + "rate conversion", + src_format, + dst_format, + sizeof(rate_t) + + src_format->voices * sizeof(rate_voice_t)); if (plugin == NULL) return -ENOMEM; - data = (struct rate_private_data *)snd_pcm_plugin_extra_data(plugin); - data->sample_size = src_format->format == SND_PCM_SFMT_S16_LE ? 2 : 1; - data->src_voices = src_format->voices; - data->dst_voices = dst_format->voices; - data->src_rate = src_format->rate; - data->dst_rate = dst_format->rate; + data = (rate_t *)plugin->extra_data; + data->plugin = plugin; + data->take = rate_take_sample[src_format->format]; + data->put = rate_put_sample[dst_format->format]; if (src_format->rate < dst_format->rate) { - data->expand = 1; data->pitch = ((src_format->rate << SHIFT) + (dst_format->rate >> 1)) / dst_format->rate; } else { - data->expand = 0; data->pitch = ((dst_format->rate << SHIFT) + (src_format->rate >> 1)) / src_format->rate; } data->pos = 0; - for (voice = 0; voice < data->src_voices; ++voice) { - data->last_S1[voice] = data->last_S2[voice] = 0; - } - data->old_src_size = data->old_dst_size = 0; + rate_init(plugin, data); + data->old_src_samples = data->old_dst_samples = 0; plugin->transfer = rate_transfer; - plugin->src_size = rate_src_size; - plugin->dst_size = rate_dst_size; + plugin->src_samples = rate_src_samples; + plugin->dst_samples = rate_dst_samples; plugin->action = rate_action; *r_plugin = plugin; return 0; diff --git a/src/pcm/plugin/route.c b/src/pcm/plugin/route.c index ca818b7c..f5787dbf 100644 --- a/src/pcm/plugin/route.c +++ b/src/pcm/plugin/route.c @@ -352,7 +352,7 @@ static ssize_t route_transfer(snd_pcm_plugin_t *plugin, return -EINVAL; if (src_size == 0) return 0; - data = (struct route_private_data *)snd_pcm_plugin_extra_data(plugin); + data = (struct route_private_data *)plugin->extra_data; data->func(data, src_ptr, dst_ptr, src_size, dst_size); return dst_size; } @@ -414,7 +414,7 @@ int snd_pcm_plugin_build_route(snd_pcm_format_t *src_format, sizeof(data->ttable[0]) * src_format->voices * dst_format->voices); if (plugin == NULL) return -ENOMEM; - data = (struct route_private_data *)snd_pcm_plugin_extra_data(plugin); + data = (struct route_private_data *)plugin->extra_data; data->src_voices = src_format->voices; data->dst_voices = dst_format->voices; diff --git a/src/pcm/plugin/stream.c b/src/pcm/plugin/stream.c index 3324221c..963a6973 100644 --- a/src/pcm/plugin/stream.c +++ b/src/pcm/plugin/stream.c @@ -24,40 +24,83 @@ #include #include #include +#include #include "../pcm_local.h" /* * Basic stream plugin */ -struct stream_private_data { - snd_pcm_t *pcm; +typedef struct stream_private_data { int channel; -}; +} stream_t; static ssize_t stream_transfer(snd_pcm_plugin_t *plugin, - char *src_ptr, size_t src_size, - char *dst_ptr, size_t dst_size) + const snd_pcm_plugin_voice_t *src_voices, + const snd_pcm_plugin_voice_t *dst_voices, + size_t samples) { - struct stream_private_data *data; + stream_t *data; + ssize_t result; + struct iovec *vec; + int count, voice; - if (plugin == NULL || dst_ptr == NULL || dst_size <= 0) + if (plugin == NULL) return -EINVAL; - data = (struct stream_private_data *)snd_pcm_plugin_extra_data(plugin); + data = (stream_t *)plugin->extra_data; if (data == NULL) return -EINVAL; + vec = (struct iovec *)((char *)data + sizeof(*data)); if (data->channel == SND_PCM_CHANNEL_PLAYBACK) { - return snd_pcm_write(data->pcm, dst_ptr, dst_size); + if (src_voices == NULL) + return -EINVAL; + if ((result = snd_pcm_plugin_src_samples_to_size(plugin, samples)) < 0) + return result; + if (plugin->src_format.interleave) { + vec->iov_base = src_voices->addr; + vec->iov_len = result; + count = 1; + } else { + count = plugin->src_format.voices; + result /= count; + for (voice = 0; voice < count; voice++) { + vec[voice].iov_base = src_voices[voice].addr; + vec[voice].iov_len = result; + } + } + result = snd_pcm_writev(plugin->handle, vec, count); + if (result < 0) + return result; + return snd_pcm_plugin_src_size_to_samples(plugin, result); } else if (data->channel == SND_PCM_CHANNEL_CAPTURE) { - return snd_pcm_read(data->pcm, dst_ptr, dst_size); + if (dst_voices == NULL) + return -EINVAL; + if ((result = snd_pcm_plugin_dst_samples_to_size(plugin, samples)) < 0) + return result; + if (plugin->dst_format.interleave) { + vec->iov_base = dst_voices->addr; + vec->iov_len = result; + count = 1; + } else { + count = plugin->dst_format.voices; + result /= count; + for (voice = 0; voice < count; voice++) { + vec[voice].iov_base = dst_voices[voice].addr; + vec[voice].iov_len = result; + } + } + result = snd_pcm_readv(plugin->handle, vec, count); + return snd_pcm_plugin_dst_size_to_samples(plugin, result); } else { return -EINVAL; } } -int snd_pcm_plugin_build_stream(snd_pcm_t *pcm, int channel, snd_pcm_plugin_t **r_plugin) +int snd_pcm_plugin_build_stream(snd_pcm_t *pcm, int channel, + snd_pcm_format_t *format, + snd_pcm_plugin_t **r_plugin) { - struct stream_private_data *data; + stream_t *data; snd_pcm_plugin_t *plugin; if (!r_plugin) @@ -65,14 +108,15 @@ int snd_pcm_plugin_build_stream(snd_pcm_t *pcm, int channel, snd_pcm_plugin_t ** *r_plugin = NULL; if (!pcm || channel < 0 || channel > 1) return -EINVAL; - plugin = snd_pcm_plugin_build(channel == SND_PCM_CHANNEL_PLAYBACK ? + plugin = snd_pcm_plugin_build(pcm, + channel == SND_PCM_CHANNEL_PLAYBACK ? "I/O stream playback" : "I/O stream capture", - sizeof(struct stream_private_data)); + format, format, + sizeof(stream_t) + sizeof(struct iovec) * format->voices); if (plugin == NULL) return -ENOMEM; - data = (struct stream_private_data *)snd_pcm_plugin_extra_data(plugin); - data->pcm = pcm; + data = (stream_t *)plugin->extra_data; data->channel = channel; plugin->transfer = stream_transfer; *r_plugin = plugin; -- 2.47.1