mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-29 05:40:25 -04:00
New plugin interface with readv/writev support.
Initial code.
This commit is contained in:
parent
e7d099089c
commit
098b4b6a96
15 changed files with 1820 additions and 1742 deletions
116
include/pcm.h
116
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -25,18 +25,45 @@
|
|||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <sys/uio.h>
|
||||
#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;
|
||||
}
|
||||
*ptr = (char *)realloc(pcm->plugin_alloc_xptr[channel], *size);
|
||||
} else {
|
||||
*ptr = (char *)malloc(*size);
|
||||
if (*ptr != NULL)
|
||||
pcm->plugin_alloc_xsize[channel] = *size;
|
||||
}
|
||||
if (*ptr == NULL)
|
||||
return -ENOMEM;
|
||||
pcm->plugin_alloc_xptr[channel] = *ptr;
|
||||
__skip:
|
||||
*ptr = snd_pcm_plugin_ptr_alloc(pcm, *size);
|
||||
if (*ptr == NULL)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *snd_pcm_plugin_malloc(snd_pcm_t *pcm, long size)
|
||||
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;
|
||||
}
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 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;
|
||||
v->addr = ptr + (voice * size);
|
||||
v->offset = 0;
|
||||
v->next = width;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <malloc.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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;
|
||||
|
||||
ssize_t snd_pcm_plugin_transfer_size(PLUGIN_BASE *pb, int channel, size_t drv_size)
|
||||
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_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_samples == 0)
|
||||
return 0;
|
||||
if (drv_samples < 0)
|
||||
return -EINVAL;
|
||||
if (channel == SND_PCM_CHANNEL_PLAYBACK) {
|
||||
plugin = snd_pcm_plugin_last(pb, SND_PCM_CHANNEL_PLAYBACK);
|
||||
while (plugin && drv_samples > 0) {
|
||||
plugin_prev = plugin->prev;
|
||||
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, SND_PCM_CHANNEL_CAPTURE);
|
||||
while (plugin && drv_samples > 0) {
|
||||
plugin_next = plugin->next;
|
||||
if (plugin->dst_samples)
|
||||
drv_samples = plugin->dst_samples(plugin, drv_samples);
|
||||
plugin = plugin_next;
|
||||
}
|
||||
}
|
||||
return drv_samples;
|
||||
}
|
||||
|
||||
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))
|
||||
return -EINVAL;
|
||||
if (clt_samples == 0)
|
||||
return 0;
|
||||
if (clt_samples < 0)
|
||||
return -EINVAL;
|
||||
if (channel == SND_PCM_CHANNEL_PLAYBACK) {
|
||||
plugin = snd_pcm_plugin_first(pb, SND_PCM_CHANNEL_PLAYBACK);
|
||||
while (plugin && clt_samples > 0) {
|
||||
plugin_next = plugin->next;
|
||||
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, SND_PCM_CHANNEL_CAPTURE);
|
||||
while (plugin) {
|
||||
plugin_prev = plugin->prev;
|
||||
if (plugin->src_samples)
|
||||
clt_samples = plugin->src_samples(plugin, clt_samples);
|
||||
plugin = plugin_prev;
|
||||
}
|
||||
}
|
||||
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, channel);
|
||||
while (plugin) {
|
||||
plugin_prev = plugin->prev;
|
||||
if (plugin->src_size)
|
||||
drv_size = plugin->src_size(plugin, drv_size);
|
||||
plugin = plugin_prev;
|
||||
}
|
||||
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, channel);
|
||||
while (plugin) {
|
||||
plugin_next = plugin->next;
|
||||
if (plugin->dst_size)
|
||||
drv_size = plugin->dst_size(plugin, drv_size);
|
||||
plugin = plugin_next;
|
||||
}
|
||||
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 drv_size;
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_plugin_hardware_size(PLUGIN_BASE *pb, int channel, size_t trf_size)
|
||||
ssize_t snd_pcm_plugin_hardware_size(snd_pcm_plugin_handle_t *pb, int channel, size_t clt_size)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next;
|
||||
snd_pcm_plugin_t *plugin;
|
||||
ssize_t result = 0;
|
||||
|
||||
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_size == 0)
|
||||
return 0;
|
||||
if (trf_size < 0)
|
||||
if (clt_size < 0)
|
||||
return -EINVAL;
|
||||
if (channel == SND_PCM_CHANNEL_PLAYBACK) {
|
||||
plugin = snd_pcm_plugin_first(pb, channel);
|
||||
while (plugin) {
|
||||
plugin_next = plugin->next;
|
||||
if (plugin->dst_size)
|
||||
trf_size = plugin->dst_size(plugin, trf_size);
|
||||
plugin = plugin_next;
|
||||
}
|
||||
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, channel);
|
||||
while (plugin) {
|
||||
plugin_prev = plugin->prev;
|
||||
if (plugin->src_size)
|
||||
trf_size = plugin->src_size(plugin, trf_size);
|
||||
plugin = plugin_prev;
|
||||
}
|
||||
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 trf_size;
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
typedef void (*adpcm_f)(adpcm_state_t *state, void *src_ptr, void *dst_ptr, int samples);
|
||||
|
||||
struct adpcm_private_data {
|
||||
combination_t cmd;
|
||||
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; \
|
||||
} \
|
||||
}
|
||||
|
||||
#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; \
|
||||
} \
|
||||
}
|
||||
|
||||
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_u8bit_adpcm(adpcm_state_t * state_ptr, unsigned char *src_ptr, unsigned char *dst_ptr, size_t size)
|
||||
/* 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,
|
||||
const snd_pcm_plugin_voice_t *src_voices,
|
||||
const snd_pcm_plugin_voice_t *dst_voices,
|
||||
size_t samples)
|
||||
{
|
||||
unsigned int pcm;
|
||||
adpcm_t *data;
|
||||
int voice;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t adpcm_transfer(snd_pcm_plugin_t * plugin,
|
||||
char *src_ptr, size_t src_size,
|
||||
char *dst_ptr, size_t dst_size)
|
||||
{
|
||||
struct adpcm_private_data *data;
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <errno.h>
|
||||
#include <endian.h>
|
||||
#include <byteswap.h>
|
||||
#include <sys/uio.h>
|
||||
#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;
|
||||
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; \
|
||||
} \
|
||||
}
|
||||
|
||||
#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); \
|
||||
} \
|
||||
}
|
||||
|
||||
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_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));
|
||||
}
|
||||
}
|
||||
|
||||
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++);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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_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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,55 +24,99 @@
|
|||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/uio.h>
|
||||
#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;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Interleave / non-interleave conversion Plug-In
|
||||
* Copyright (c) 2000 by Abramo Bagnara <abbagnara@racine.ra.it>
|
||||
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>,
|
||||
* Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
|
|
@ -31,6 +32,7 @@
|
|||
#include <errno.h>
|
||||
#include <endian.h>
|
||||
#include <byteswap.h>
|
||||
#include <sys/uio.h>
|
||||
#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;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Linear conversion Plug-In
|
||||
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
|
||||
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>,
|
||||
* Abramo Bagnara <abramo@alsa-project.org>
|
||||
*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
|
|
@ -33,6 +34,7 @@
|
|||
#include <errno.h>
|
||||
#include <endian.h>
|
||||
#include <byteswap.h>
|
||||
#include <sys/uio.h>
|
||||
#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;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,14 +25,14 @@
|
|||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/uio.h>
|
||||
#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;
|
||||
data = (struct mmap_private_data *)snd_pcm_plugin_extra_data(plugin);
|
||||
if (data == NULL)
|
||||
return -EINVAL;
|
||||
control = data->control;
|
||||
if (control == NULL)
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
*size = control->status.frag_size;
|
||||
} else {
|
||||
*buffer = NULL; /* use another buffer */
|
||||
int frag, voice;
|
||||
if (control->status.frag_size != snd_pcm_plugin_src_samples_to_size(plugin, samples) / plugin->src_format.voices)
|
||||
return -EINVAL;
|
||||
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;
|
||||
*voices = NULL;
|
||||
data = (mmap_t *)plugin->extra_data;
|
||||
if (data->channel != SND_PCM_CHANNEL_CAPTURE)
|
||||
return -EINVAL;
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -33,6 +33,7 @@
|
|||
#include <errno.h>
|
||||
#include <endian.h>
|
||||
#include <byteswap.h>
|
||||
#include <sys/uio.h>
|
||||
#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;
|
||||
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; \
|
||||
} \
|
||||
}
|
||||
|
||||
#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); \
|
||||
} \
|
||||
}
|
||||
|
||||
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_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;
|
||||
}
|
||||
|
||||
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++));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
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_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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#ifdef __KERNEL__
|
||||
#include "../../include/driver.h"
|
||||
#include "../../include/pcm.h"
|
||||
#include "../../include/pcm_plugin.h"
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
|
@ -36,337 +37,350 @@
|
|||
#define SHIFT 11
|
||||
#define BITS (1<<SHIFT)
|
||||
#define MASK (BITS-1)
|
||||
#define MAX_VOICES 6
|
||||
|
||||
/*
|
||||
* Basic rate conversion plugin
|
||||
*/
|
||||
|
||||
#define rate_voices(data) ((rate_voice_t *)((char *)data + sizeof(*data)))
|
||||
|
||||
typedef signed short (*take_sample_f)(void *ptr);
|
||||
typedef void (*put_sample_f)(void *ptr, signed int val);
|
||||
|
||||
struct rate_private_data {
|
||||
int src_voices;
|
||||
int dst_voices;
|
||||
int src_rate;
|
||||
int dst_rate;
|
||||
int sample_size;
|
||||
int expand: 1;
|
||||
typedef struct {
|
||||
signed short last_S1;
|
||||
signed short last_S2;
|
||||
} rate_voice_t;
|
||||
|
||||
typedef struct rate_private_data {
|
||||
snd_pcm_plugin_t *plugin;
|
||||
take_sample_f take;
|
||||
put_sample_f put;
|
||||
unsigned int pitch;
|
||||
unsigned int pos;
|
||||
signed short last_S1[MAX_VOICES];
|
||||
signed short last_S2[MAX_VOICES];
|
||||
ssize_t old_src_size, old_dst_size;
|
||||
};
|
||||
ssize_t old_src_samples, old_dst_samples;
|
||||
} rate_t;
|
||||
|
||||
static void resample16_expand(struct rate_private_data *data, int voices,
|
||||
signed short *src_ptr, int src_size,
|
||||
signed short *dst_ptr, int dst_size)
|
||||
static void rate_init(snd_pcm_plugin_t *plugin, rate_t *data)
|
||||
{
|
||||
int voice;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
#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 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;
|
||||
signed short *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;
|
||||
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;
|
||||
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;
|
||||
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 resample16_shrink(struct rate_private_data *data, int voices,
|
||||
signed short *src_ptr, int src_size,
|
||||
signed short *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;
|
||||
signed short *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;
|
||||
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;
|
||||
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 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 ssize_t rate_src_samples(snd_pcm_plugin_t *plugin, size_t samples)
|
||||
{
|
||||
unsigned int pos;
|
||||
signed int val;
|
||||
signed short S1, S2;
|
||||
int voice;
|
||||
unsigned char *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;
|
||||
if (pos >> SHIFT) {
|
||||
pos &= MASK;
|
||||
S1 = S2;
|
||||
S2 = (*src << 8) ^ 0x8000;
|
||||
}
|
||||
while (size-- > 0) {
|
||||
if (pos >> SHIFT) {
|
||||
src += voices;
|
||||
pos &= MASK;
|
||||
S1 = S2;
|
||||
if ((src - src_ptr) < src_size * voices)
|
||||
S2 = (*src << 8) ^ 0x8000;
|
||||
}
|
||||
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;
|
||||
pos += data->pitch;
|
||||
}
|
||||
data->last_S1[voice] = S1;
|
||||
data->last_S2[voice] = 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)
|
||||
{
|
||||
unsigned int pos;
|
||||
signed int val;
|
||||
signed short S1, S2;
|
||||
int voice;
|
||||
unsigned char *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 << 8) ^ 0x8000;
|
||||
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 >> 8) ^ 0x0080;
|
||||
dst += voices;
|
||||
size--;
|
||||
}
|
||||
pos += data->pitch;
|
||||
}
|
||||
data->last_S1[voice] = S1;
|
||||
data->last_S2[voice] = S2;
|
||||
data->pos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t rate_src_size(snd_pcm_plugin_t *plugin, size_t size)
|
||||
{
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -24,40 +24,83 @@
|
|||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/uio.h>
|
||||
#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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue